/* khdoc.cc Hypertext Document Classes Copyright (c) 1996-9,2000-2,2003,2007 Kriang Lerdsuwanakij email: lerdsuwa@users.sourceforge.net This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Current supported HTML tags: ... ... ... ... ( optional but recommended) ...
...
...
......
", buffer, from-1, length); } my_wattr_set(pad, oldAttr, oldPair); } curColumn = indentPos[numIndent-1]; if (pad) wmove(pad, curRow, curColumn); } } #endif /* TRIM_NO_INDENT */ /* pad == NULL indicates that it is called from FindLink(...), curRow & curColumn are ignored in this case */ void HyperDocument::ProcessA(char *buffer, size_t length, WINDOW *pad, size_t from, int curRow, int curColumn) { size_t i = SeekNonSpace(buffer, from, length); size_t j = SeekTokenEnd(buffer, i, length); if (isInA == 1 && procA) { // Must have
",
buffer, i-1, length);
}
isInPre++;
}
else if (j-i == 4 && CompareStringCase(buffer+i, "/PRE", j-i) == 0) {
if (!isInPre) {
ThrowErrorTagWithout("", "",
buffer, i-1, length);
}
isInPre--;
}
#ifndef TRIM_NO_STD_ATTRIB
else if (j-i == 1 && CompareStringCase(buffer+i, "B", j-i) == 0) {
numNestB++;
numNestBTag++;
if (pad)
wattrset(pad, GetNestedAttr());
}
else if (j-i == 2 && CompareStringCase(buffer+i, "/B", j-i) == 0) {
if (numNestBTag == 0) {
ThrowErrorTagWithout("", "",
buffer, i-1, length);
}
numNestB--;
numNestBTag--;
if (pad)
wattrset(pad, GetNestedAttr());
}
else if (j-i == 1 && CompareStringCase(buffer+i, "I", j-i) == 0) {
numNestI++;
numNestITag++;
if (pad)
wattrset(pad, GetNestedAttr());
}
else if (j-i == 2 && CompareStringCase(buffer+i, "/I", j-i) == 0) {
if (numNestITag == 0) {
ThrowErrorTagWithout("", "",
buffer, i-1, length);
}
numNestI--;
numNestITag--;
if (pad)
wattrset(pad, GetNestedAttr());
}
#endif /* TRIM_NO_STD_ATTRIB */
#ifndef TRIM_NO_EXTRA_ATTRIB
else if (j-i == 6 && CompareStringCase(buffer+i, "STRONG", j-i) == 0) {
numNestB++;
numNestStrongTag++;
if (pad)
wattrset(pad, GetNestedAttr());
}
else if (j-i == 7 && CompareStringCase(buffer+i, "/STRONG", j-i) == 0) {
if (numNestStrongTag == 0) {
ThrowErrorTagWithout("", "",
buffer, i-1, length);
}
numNestB--;
numNestStrongTag--;
if (pad)
wattrset(pad, GetNestedAttr());
}
else if (j-i == 2 && CompareStringCase(buffer+i, "EM", j-i) == 0) {
numNestI++;
numNestEmTag++;
if (pad)
wattrset(pad, GetNestedAttr());
}
else if (j-i == 3 && CompareStringCase(buffer+i, "/EM", j-i) == 0) {
if (numNestEmTag == 0) {
ThrowErrorTagWithout("", "",
buffer, i-1, length);
}
numNestI--;
numNestEmTag--;
if (pad)
wattrset(pad, GetNestedAttr());
}
else if (j-i == 4 && CompareStringCase(buffer+i, "CITE", j-i) == 0) {
numNestI++;
numNestCiteTag++;
if (pad)
wattrset(pad, GetNestedAttr());
}
else if (j-i == 5 && CompareStringCase(buffer+i, "/CITE", j-i) == 0) {
if (numNestCiteTag == 0) {
ThrowErrorTagWithout("", "",
buffer, i-1, length);
}
numNestI--;
numNestCiteTag--;
if (pad)
wattrset(pad, GetNestedAttr());
}
#endif /* TRIM_EXTRA_ATTRIB */
#ifndef TRIM_NO_INDENT
else if (j-i == 2 && CompareStringCase(buffer+i, "UL", j-i) == 0) {
if (numIndent < NUM_INDENT) {
indentType[numIndent] = IND_UL;
if (numIndent)
indentPos[numIndent] =
indentPos[numIndent-1]+4;
else
indentPos[numIndent] = 4;
}
numIndent++;
}
else if (j-i == 2 && CompareStringCase(buffer+i, "OL", j-i) == 0) {
if (numIndent < NUM_INDENT) {
indentType[numIndent] = IND_OL;
if (numIndent)
indentPos[numIndent] =
indentPos[numIndent-1]+4;
else
indentPos[numIndent] = 4;
indentVal[numIndent] = 1;
}
numIndent++;
}
else if (j-i == 2 && CompareStringCase(buffer+i, "DL", j-i) == 0) {
if (numIndent < NUM_INDENT) {
indentType[numIndent] = IND_DL;
if (numIndent)
indentPos[numIndent] =
indentPos[numIndent-1]+4;
else
indentPos[numIndent] = 4;
}
numIndent++;
}
else if (j-i == 10 && CompareStringCase(buffer+i, "BLOCKQUOTE", j-i) == 0) {
/* New line */
if (curColumn != IndentColumn())
NextLine(pad);
if (numIndent < NUM_INDENT) {
indentType[numIndent] = IND_BLOCK;
if (numIndent)
indentPos[numIndent] =
indentPos[numIndent-1]+4;
else
indentPos[numIndent] = 4;
}
numIndent++;
IndentLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
else if (j-i == 3 && CompareStringCase(buffer+i, "/UL", j-i) == 0) {
if (curColumn != IndentColumn()) {
NextLine();
}
if (numIndent > 0)
numIndent--;
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
else if (j-i == 3 && CompareStringCase(buffer+i, "/OL", j-i) == 0) {
if (curColumn != IndentColumn())
NextLine(pad);
if (numIndent > 0)
numIndent--;
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
else if (j-i == 3 && CompareStringCase(buffer+i, "/DL", j-i) == 0) {
if (curColumn != IndentColumn())
NextLine(pad);
if (numIndent > 0)
numIndent--;
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
else if (j-i == 11 && CompareStringCase(buffer+i, "/BLOCKQUOTE", j-i) == 0) {
if (curColumn != IndentColumn())
NextLine(pad);
if (numIndent > 0)
numIndent--;
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
else if (j-i == 2 && CompareStringCase(buffer+i, "LI", j-i) == 0) {
if (curColumn != IndentColumn())
NextLine(pad);
ProcessLI(buffer, length, pad, i);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
else if (j-i == 2 && CompareStringCase(buffer+i, "DT", j-i) == 0) {
if (curColumn != IndentColumn())
NextLine(pad);
NextLine(pad);
ProcessLI(buffer, length, pad, i);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
#endif /* TRIM_NO_INDENT */
#ifndef TRIM_NO_RULE
else if (j-i == 2 && CompareStringCase(buffer+i, "HR", j-i) == 0) {
if (curColumn != IndentColumn())
NextLine(pad);
NextLine(pad);
if (pad) {
for (int curColumn = numColumn/4; curColumn < numColumn*3/4;
curColumn++) {
wmove(pad, curRow, curColumn);
draw_entity(pad, EID_HLINE);
}
}
NextLine(pad);
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
#endif /* TRIM_NO_RULE */
else if (j-i == 5 && CompareStringCase(buffer+i, "TITLE", j-i) == 0) {
try {
j = SeekChar('>', buffer, i+5, length);
}
catch (...) {
ThrowErrorCannotFind(_("`>\'"),
buffer, i-1, length);
}
j++; /* Skip '>' */
k = j;
try {
j = SeekCaseString("", buffer, j, length);
}
catch (...) {
ThrowErrorCannotFind("",
buffer, i-1, length);
}
if (titleText.size()) {
ThrowErrorMessage(_("cannot "
"specify title more "
"than once"),
buffer, i-1, length);
}
/* Empty allowed */
if (j-k)
titleText = string(buffer+k, j-k);
}
else if (CompareStringCase(buffer+i, "!--", 3) == 0 && pad) {
/* Seek for "-->" */
try {
j = SeekString("-->", buffer, i, length);
}
catch (...) {
ThrowErrorCannotFind(_("`-->\'"),
buffer, i-1, length);
}
}
/* else - ignore unregcognized tags */
/* Seek for '>' */
/* Can be quoted */
try {
i = SeekChar('>', buffer, j, length, 2);
/* nest support */
}
catch (...) {
ThrowErrorCannotFind(_("`>\'"),
buffer, i-1, length);
}
i++;
}
else {
chtype bufChar = '?'; // Enough to cover 0..255
#ifdef USE_UTF8_MODE
bool use_wide = false;
wchar_t bufWChar;
int charWidth = 1;
#endif
switch(buffer[i]) {
case '\t':
case '\n':
case '\r':
case '\b':
case ' ':
case '\0':
bufChar = ' ';
break;
case '&':
i++;
/* Seek for ';' */
try {
j = SeekChar(';', buffer, i, length);
}
catch (...) {
ThrowErrorCannotFind(_("`;\'"),
buffer, i-1, length);
}
if (buffer[i] == '#') {
unsigned bufChar_ = 0;
try {
bufChar_ = StringToUnsigned(buffer+i+1, ';');
}
catch (ErrorRange) {
ThrowErrorMessage(_("error in escape"),
buffer, i-1, length);
}
if (bufChar_ > 255) {
ThrowErrorMessage(_("character code too large"),
buffer, i-1, length);
}
bufChar = static_cast(bufChar_);
}
else {
// For unknown escape
bufChar = '?';
for (size_t k = 0; entity_table[k].name; ++k) {
if (static_cast(j-i) == entity_table[k].length
&& strncmp(buffer+i, entity_table[k].name, j-i) == 0) {
// FIXME: Handle wide/combining entity
#ifdef USE_UTF8_MODE
if (IsUTF8Mode()) {
use_wide = true;
bufWChar = entity_table[k].wc;
charWidth = wcwidth(bufWChar);
}
else
#endif
bufChar = entity_table[k].code;
break;
}
}
}
i = j; /* i++ appears later... */
break;
default: // ncurses only works with unsigned char
// Assume no combining char
#ifdef USE_UTF8_MODE
if (IsUTF8Mode()) {
static mbstate_t state;
memset(&state, 0, sizeof(mbstate_t));
int ret = mbrtowc(&bufWChar, buffer+i, length-i, &state);
if (ret <= 0)
throw ErrorBadSequence();
// FIXME: Check if printable logic correct?
if (iswprint(bufWChar) || bufWChar == ' ') {
charWidth = wcwidth(bufWChar);
use_wide = true;
i += ret-1;
}
else {
bufChar = '?';
}
}
else
#endif
if (isprint(buffer[i]) || buffer[i] == ' ') {
bufChar = static_cast(buffer[i]);
}
else {
bufChar = static_cast('?');
}
}
#ifdef USE_UTF8_MODE
if (charWidth == 0) {
if (pad)
DrawWCharSub(pad, bufWChar);
i++;
continue;
}
else if (charWidth > 1) {
if (curColumn > scrColumn-charWidth && isInPre == 0)
NextLine(pad);
if (pad)
DrawWCharSub(pad, bufWChar);
else // Keep track of char for wrap
for (int i = 0; i < charWidth; ++i)
lineBuffer[curColumn+i] = '*';
curColumn += charWidth;
if (!isInPre) { // this char pos
lastCharRow = curRow;
lastCharColumn = curColumn-1;
lastChar = bufChar;
}
else { // this preformatted char pos
lastPreColumn = curColumn-1;
lastPreRow = curRow;
}
i++;
continue;
}
#endif
// Handle whitespaces
// or line too long outside tag
if (bufChar == ' ' ||
(curColumn > scrColumn-1 && isInPre == 0)) {
if (isInPre == 0) {
// Skip space at the beginning of line
if (curColumn == IndentColumn() &&
bufChar == ' ') {
i++; // Skip
}
// Ignore double spaces
else if (curColumn > IndentColumn() &&
lastCharRow == curRow &&
lastCharColumn == curColumn-1 &&
lastChar == ' ' && bufChar == ' ') {
lastCharColumn = curColumn;
i++; // Skip
}
// Do wrap
// FIXME: Update for Unicode
else if (curColumn > scrColumn-1) {
chtype ch;
int x = curColumn-1;
if (pad)
wmove(pad, curRow, x);
// Search for the last space in the line
while ((pad ? (ch = winch(pad) &
(A_CHARTEXT|A_ALTCHARSET)):
lineBuffer[x])
!= ' ') {
x--;
if (x >= 0) { // Move cursor back
if (pad)
wmove(pad, curRow, x);
if (curRow == lastPreRow &&
x == lastPreColumn)
// Can no longer wrapped
// since it's preformatted
break;
}
else // x < 0, wrap fails
break;
}
// Cannot break line
if (x < 0 || (curRow == lastPreRow
&& x == lastPreColumn)) {
// We are printing a space
// so break the line here
if (bufChar == ' ') {
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
// Print character beyond screen width
else {
if (pad) {
wmove(pad, curRow, curColumn);
waddch(pad, bufChar);
}
else { // This is OK
lineBuffer[curColumn] = '*';
}
curColumn++;
}
i++; // Skip current char
}
// Can break line
else {
x = curColumn; /* Leave the space as-is
as it may be inside
some link text */
int prevRow = curRow;
int prevCol = curColumn-1;
/* Init it to avoid
compiler warning */
attr_t oldAttr = normalA;
short oldPair = 0;
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
if (pad) {
/* Update links position */
for (sptr_list::reverse_iterator iter = anchorList.rbegin();
iter != anchorList.rend()
&& ((*iter)->rowFrom == prevRow || (*iter)->rowTo == prevRow);
++iter) {
if ((*iter)->rowFrom == prevRow && (*iter)->colFrom >= x) {
(*iter)->rowFrom = curRow;
(*iter)->colFrom -= x;
}
if ((*iter)->rowTo == prevRow && (*iter)->colTo >= x) {
(*iter)->rowTo = curRow;
(*iter)->colTo -= x;
}
}
/* Update section position */
for (sptr_list::reverse_iterator iter = sectionList.rbegin();
iter != sectionList.rend()
&& ((*iter)->rowFrom == prevRow || (*iter)->rowTo == prevRow);
++iter) {
if ((*iter)->rowFrom == prevRow && (*iter)->colFrom >= x) {
(*iter)->rowFrom = curRow;
(*iter)->colFrom -= x;
}
if ((*iter)->rowTo == prevRow && (*iter)->colTo >= x) {
(*iter)->rowTo = curRow;
(*iter)->colTo -= x;
}
}
my_wattr_get(pad, &oldAttr, &oldPair);
wattrset(pad, A_NORMAL);
}
for (k = x; k <= static_cast(prevCol); k++) {
if (pad) {
wmove(pad, prevRow, k);
ch = winch(pad);
waddch(pad, ' ');
wmove(pad, curRow, curColumn);
waddch(pad, ch);
#ifdef DEBUG_WRAP
pnoutrefresh(pad, 0, 0, 0, 0, scrRow-1, scrColumn-1);
wrefresh(stdscr);
getch();
#endif
}
else {
lineBuffer[curColumn] = lineBuffer[k];
}
curColumn++;
}
/* Display another space */
if (pad) {
my_wattr_set(pad, oldAttr, oldPair);
wmove(pad, curRow, curColumn);
waddch(pad, bufChar);
}
else
lineBuffer[curColumn] = bufChar;
/* Track & remove contiguous space */
lastCharRow = curRow;
lastCharColumn = curColumn;
lastChar = bufChar;
curColumn++;
if (curColumn == NUM_COLUMN) {
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
i++;
}
}
// Not reach screen width yet
else {
// Display it
if (pad) {
// No need to deal with Unicode
// for space char.
waddch(pad, bufChar);
}
else
lineBuffer[curColumn] = bufChar;
// Track & remove contiguous space
lastCharRow = curRow;
lastCharColumn = curColumn;
lastChar = bufChar;
curColumn++;
if (curColumn == NUM_COLUMN) {
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
i++;
}
}
else { // For whitespaces
// inside tag
lastPreColumn = curColumn;
lastPreRow = curRow;
switch(buffer[i]) {
case ' ':
case '\0':
if (pad)
waddch(pad, ' ');
else // No wrap char
lineBuffer[curColumn] = '*';
curColumn++;
break;
case '\t':
do { // Add space to pad
if (pad)
waddch(pad, ' ');
else // No wrap char
lineBuffer[curColumn] = '*';
curColumn++;
} while(curColumn % 8);
break;
case '\n':
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
break;
}
if (curColumn == NUM_COLUMN) {
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
i++;
}
}
else { // Can be displayed directly
if (pad) { // Add char. to pad
#ifdef USE_UTF8_MODE
if (use_wide)
DrawWCharSub(pad, bufWChar);
else
#endif
waddch(pad, bufChar);
}
else // Keep track of char for wrap
lineBuffer[curColumn] = '*';
if (!isInPre) { // this char pos
lastCharRow = curRow;
lastCharColumn = curColumn;
lastChar = bufChar;
}
else { // this preformatted char pos
lastPreColumn = curColumn;
lastPreRow = curRow;
}
if (curColumn == NUM_COLUMN) {
NextLine(pad);
lastPreColumn = curColumn;
lastPreRow = curRow;
}
curColumn++;
i++;
}
}
}
if (isInA == 1) {
throw ErrorGenericSyntax(_("unexpected end of file, missing "));
}
if (isInPre == 1) {
throw ErrorGenericSyntax(_("unexpected end of file, missing "));
}
#ifdef DEBUG_FORMAT_TEXT
fclose(f);
#endif
if (pad == NULL) { // Update document size
if (curColumn > numColumn)
numColumn = curColumn;
numRow = curRow+1; // Off-by-1 different
}
else { // *** Move check to loop above ??
if (curRow != numRow-1 && curRow > scrRow) {
throw ErrorGenericSyntax(_("hypertext size calculation not match actual hypertext parse\n"
"Number of row: %$ (calculatation) %$ (parse)"),
numRow, curRow+1);
}
for (i = curRow; i < static_cast