nyctergatis.com

Contact

Projects
Sysquake Remote Live
NME
PDF
Hike
Sudoku
GifBuilder
jpeglib for Palm OS
MySQL Client
Cross-GCC for Mac OS
NME.c
Go to the documentation of this file.
00001 
00008 /* License: new BSD license (see header file) */
00009 
00010 #include "NME.h"
00011 
00012 #define kMaxNesting 8   ///< maximum nesting of lists
00013 
00014 // #define useHTMLEmphasisTags  ///< if defined, use em/strong instead of i/b
00015 
00017 enum
00018 {
00019     kNMEListNumUL = -100,   
00020     kNMEListNumDT,  
00021     kNMEListNumDD,  
00022     kNMEListIndented,   
00023     kNMEListNumTableCell,   
00024     kNMEListNumTableHCell   
00025 };
00026 
00028 #define isBlank(c) ((c) == ' ' || (c) == '\t')
00029 
00031 #define isEol(c) ((c) == '\r' || (c) == '\n')
00032 
00034 #define isDigit(c) ((c) >= '0' && (c) <= '9')
00035 
00037 #define isAlphaNum(c) \
00038     (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || isDigit(c))
00039 
00041 #define isFirstUTF8Byte(c) \
00042     (((c) & 0x80) == 0 || ((c) & 0xe0) == 0xc0 || ((c) & 0xf0) == 0xe0)
00043 
00045 #define kMaxNumberedHeadingLevels 2
00046 
00048 #define kMaxSectionLevels 4
00049 
00051 #define kTabWidth 4
00052 
00054 #define CheckError(c) \
00055     do { err = (c); if (err != kNMEErrOk) return err; } while (0)
00056 
00060 typedef enum NMEState
00061 {
00062     kNMEStatePar,   // or UL/OL/DL depending on nesting
00063     kNMEStatePre,
00064     kNMEStateHeading,
00065     kNMEStateParAfterEol,
00066     kNMEStatePreAfterEol,
00067     kNMEStateBetweenPar
00068 } NMEState;
00069 
00071 typedef enum NMEToken
00072 {
00073     kNMETokenChar,  
00074     kNMETokenSpace, 
00075     kNMETokenTab,   
00076     kNMETokenEOL,   
00077     kNMETokenHeading,   
00078     kNMETokenLineBreak, 
00079     kNMETokenLI,    
00080     kNMETokenDD,    
00081     kNMETokenTableCell, 
00082     kNMETokenTableHCell,    
00083     kNMETokenHR,    
00084     kNMETokenPre,   
00085     kNMETokenStyle, 
00086     kNMETokenLinkBegin, 
00087     kNMETokenLinkEnd,   
00088     kNMETokenImageBegin,    
00089     kNMETokenImageEnd,  
00090     kNMETokenPlugin,    
00091     kNMETokenPluginBlock,   
00092     kNMETokenPlaceholder,   
00093     kNMETokenPlaceholderBlock   
00094 } NMEToken;
00095 
00097 typedef enum NMEStyle
00098 {
00099     kNMEStyleBold = 0,  
00100         // must begin at 0 so that kNMEStylesCount is correct
00101     kNMEStyleItalic,    
00102     kNMEStyleUnderline, 
00103     kNMEStyleSuperscript,   
00104     kNMEStyleSubscript, 
00105     kNMEStyleMonospace, 
00106     kNMEStyleVerbatim,  
00107     kNMEStyleLink,  
00108     kNMEStyleImage, 
00109     kNMEStylesCount 
00110 } NMEStyle;
00111 
00113 struct NMEContextStruct
00114 {
00115     NMEText dest;   
00116     NMEInt destLen; 
00117     
00118     NMEInt destLenUCS16;    
00119     
00120     NMEText src;    
00121     NMEInt srcIndex;    
00122     NMEInt srcIndexOffset;  
00123     NMEInt srcLen;  
00124     NMEInt srcLineNum;  
00125     NMEInt srcIndexForLineNum;  
00126     
00127     NMEInt bufSize; 
00128     
00129     NMEInt currentIndent;   
00130     NMEInt col; 
00131     
00132     NMEInt listNum[kMaxNesting];    
00133     NMEInt nesting; 
00134     
00135     NMEOutputFormat const *outputFormat;    
00136     NMEConstText eol;   
00137     NMEChar ctrlChar;   
00138     NMEInt options; 
00139     
00140     NMEInt fontSize;    
00141     
00142     NMEInt level;   
00143     NMEInt item;    
00144     NMEInt linkOffset;  
00145     NMEInt linkLength;
00146     
00147     NMEBoolean xref;    
00148 };
00149 
00151 #define setContext(c, l, i) do { (c).level = l; (c).item = (i) < 0 ? 0 : (i); } while (0)
00152 
00158 static void skipBlanks(NMEConstText src, NMEInt srcLen, NMEInt *i)
00159 {
00160     while (*i < srcLen && isBlank(src[*i]))
00161         (*i)++;
00162 }
00163 
00169 static void execOperator(NMEInt stack[], NMEInt *stackDepth, NMEChar op)
00170 {
00171     switch (op)
00172     {
00173         case '+':
00174             stack[*stackDepth-2] += stack[*stackDepth-1];
00175             break;
00176         case '-':
00177             stack[*stackDepth-2] -= stack[*stackDepth-1];
00178             break;
00179         case '*':
00180             stack[*stackDepth-2] *= stack[*stackDepth-1];
00181             break;
00182         case '/':
00183             stack[*stackDepth-2] /= stack[*stackDepth-1];
00184             break;
00185         case '=':
00186             stack[*stackDepth-2] = stack[*stackDepth-2] == stack[*stackDepth-1];
00187             break;
00188         case '!':
00189             stack[*stackDepth-2] = stack[*stackDepth-2] != stack[*stackDepth-1];
00190             break;
00191         case '<':
00192             stack[*stackDepth-2] = stack[*stackDepth-2] < stack[*stackDepth-1];
00193             break;
00194         case '>':
00195             stack[*stackDepth-2] = stack[*stackDepth-2] > stack[*stackDepth-1];
00196             break;
00197         case '&':
00198         case '?':   // synonym with lower priority used in conditional expressions
00199             if (stack[*stackDepth-2] != 0)
00200                 stack[*stackDepth-2] = stack[*stackDepth-1];
00201             break;
00202         case '|':
00203         case ':':   // synonym with lower priority used in conditional expressions
00204             if (stack[*stackDepth-2] == 0)
00205                 stack[*stackDepth-2] = stack[*stackDepth-1];
00206             break;
00207     }
00208     (*stackDepth)--;
00209 }
00210 
00215 static void updateLineNum(NMEContext *context)
00216 {
00217     for ( ;
00218             context->srcIndexForLineNum < context->srcIndex;
00219             context->srcIndexForLineNum++)
00220         if (context->src[context->srcIndexForLineNum] == '\n'
00221                 || (context->src[context->srcIndexForLineNum] == '\r'
00222                     && (context->srcIndexForLineNum + 1 == context->srcIndex
00223                         || context->src[context->srcIndexForLineNum + 1] != '\n')))
00224             context->srcLineNum++;
00225 }
00226 
00234 static NMEInt evalExpression(NMEConstText src, NMEInt srcLen,
00235         NMEContext *context)
00236 {
00237 #define kExprStackSize 16   ///< size of operand and operator stacks
00238 #define kExprErrorValue 1   ///< default value if error
00239     static struct
00240     {
00241         NMEChar op; 
00242         NMEInt priority;    
00243     } const opList[] =
00244     {
00245         {'?', 1}, {':', 1},
00246         {'|', 2},
00247         {'&', 3},
00248         {'=', 4}, {'!', 4}, {'<', 4}, {'>', 4},
00249         {'+', 5}, {'-', 5},
00250         {'*', 6}, {'/', 6},
00251         {'\0', 0}
00252     };
00253     NMEInt stack[kExprStackSize];   // stack of operands (intermediate results)
00254     NMEInt stackDepth;
00255     NMEInt opStack[kExprStackSize];
00256         // stack of waiting operators (index in opList, -1=parenthesis)
00257     NMEInt opStackDepth;
00258     NMEInt i, j;
00259     
00260     for (i = stackDepth = opStackDepth = 0; ; )
00261     {
00262         skipBlanks(src, srcLen, &i);
00263         
00264         if (i >= srcLen)
00265             return 1;   // unexpected end of expression
00266         
00267         // decode opening parenthesis
00268         while (i < srcLen && src[i] == '(' && opStackDepth < kExprStackSize)
00269         {
00270             opStack[opStackDepth++] = -1;
00271             i++;
00272             skipBlanks(src, srcLen, &i);
00273         }
00274         
00275         // decode number or variable
00276         if (isDigit(src[i]))
00277             for (stack[stackDepth] = 0; i < srcLen && isDigit(src[i]); i++)
00278                 stack[stackDepth] = 10 * stack[stackDepth] + src[i] - '0';
00279         else
00280             switch (src[i++])
00281             {
00282                 case 'l':
00283                     stack[stackDepth] = context->level;
00284                     break;
00285                 case 'i':
00286                     stack[stackDepth] = context->item;
00287                     break;
00288                 case 's':
00289                     stack[stackDepth] = context->fontSize;
00290                     break;
00291                 case 'o':
00292                     stack[stackDepth] = context->srcIndex;
00293                     break;
00294                 case 'L':
00295                     updateLineNum(context);
00296                     stack[stackDepth] = context->srcLineNum;
00297                     break;
00298                 case 'p':
00299                     stack[stackDepth] = context->destLen;
00300                     break;
00301                 case 'x':
00302                     stack[stackDepth] = context->xref;
00303                     break;
00304                 default:
00305                     if (src[i - 1] >= 'A' && src[i - 1] <= 'Z')
00306                     {
00307                         // custom variable
00308                         if (context->outputFormat->getVarFun)
00309                             stack[stackDepth]
00310                                     = context->outputFormat->getVarFun(src[i - 1],
00311                                             context->outputFormat->getVarData);
00312                         else
00313                             stack[stackDepth] = 0;
00314                     }
00315                     else
00316                         return kExprErrorValue; // unknown variable, or garbage
00317             }
00318         stackDepth++;
00319         
00320         skipBlanks(src, srcLen, &i);
00321         
00322         // stop if end of expression
00323         if (i >= srcLen)
00324         {
00325             // flush all waiting operators, including parenthesis not closed
00326             for ( ; opStackDepth > 0; opStackDepth--)
00327                 if (opStack[opStackDepth - 1] >= 0)
00328                     execOperator(stack, &stackDepth,
00329                             opList[opStack[opStackDepth - 1]].op);
00330             return stack[0];
00331         }
00332         
00333         // decode closing parenthesis (those without matching opening parenthesis are ignored)
00334         while (i < srcLen && src[i] == ')')
00335         {
00336             // flush waiting operators until matching opening parenthesis
00337             for ( ; opStackDepth > 0 && opStack[opStackDepth - 1] >= 0; opStackDepth--)
00338                 execOperator(stack, &stackDepth,
00339                         opList[opStack[opStackDepth - 1]].op);
00340             if (opStackDepth > 0)
00341                 opStackDepth--; // opening parenthesis
00342             
00343             // skip parenthesis and spaces in source
00344             i++;
00345             skipBlanks(src, srcLen, &i);
00346         }
00347         
00348         // decode operator
00349         for (j = 0; opList[j].op && src[i] != opList[j].op; j++)
00350             ;
00351         if (!opList[j].op)
00352             return kExprErrorValue; // unknown operator, or garbage
00353         
00354         // execute operators with higher priority
00355         for ( ; opStackDepth > 0
00356                     && opStack[opStackDepth - 1] >= 0
00357                     && opList[j].priority <= opList[opStack[opStackDepth - 1]].priority;
00358                 opStackDepth--)
00359             execOperator(stack, &stackDepth, opList[opStack[opStackDepth - 1]].op);
00360         
00361         // give up if op stack is full
00362         if (opStackDepth >= kExprStackSize)
00363             return kExprErrorValue;
00364         
00365         // store operator in opStack
00366         opStack[opStackDepth++] = j;
00367         
00368         // skip operator in source
00369         i++;
00370     }
00371 }
00372 
00373 NMEBoolean NMEAddString(NMEConstText str,
00374         NMEInt strLen,
00375         NMEChar ctrlChar,
00376         NMEContext *context)
00377 {
00378     NMEInt k;
00379     
00380     if (!str)
00381         return TRUE;    // no op if str is NULL
00382     
00383     if (strLen < 0)
00384         for (strLen = 0; str[strLen]; strLen++)
00385             ;
00386     
00387     for (k = 0; k < strLen; )
00388         if (context->destLen
00389                     + (str[k] == '\n' ? (context->eol[1] ? 1 : 0)
00390                         + context->currentIndent : 0)
00391                 >= context->bufSize)
00392             return FALSE;
00393         else if (str[k] == '\n')
00394         {
00395             context->dest[context->destLen++] = context->eol[0];
00396             context->destLenUCS16++;
00397             if (context->eol[1])
00398             {
00399                 context->dest[context->destLen++] = context->eol[1];
00400                 context->destLenUCS16++;
00401             }
00402             k++;
00403             context->col = context->currentIndent;
00404         }
00405         else if (k + 2 < strLen
00406                 && str[k] == ctrlChar
00407                 && (str[k + 1] == '{' || (str[k + 1] == ctrlChar && str[k + 2] == '{')))
00408         {
00409             NMEBoolean replicate;
00410             NMEText repStr;
00411             NMEInt len, result, i, repStrLen, col0, destLenUCS160;
00412             
00413             replicate = str[k + 1] == ctrlChar;
00414             if (replicate)
00415                 k++;
00416             for (k += 2, len = 0; k + len < strLen && str[k + len] != '}'; len++)
00417                 ;
00418             if (k + len >= strLen)  // unexpected end of string
00419                 return TRUE;    // don't report error (enough space)
00420             result = evalExpression(str + k, len, context);
00421             k += len + 1;   // skip after }
00422             if (replicate)
00423             {
00424                 // find string, until double-ctrlChar
00425                 for (len = 0;
00426                         k + len + 1 < strLen
00427                             && (str[k + len] != ctrlChar
00428                                     || str[k + len + 1] != ctrlChar);
00429                         len++)
00430                     ;
00431                 // copy once, evaluating expressions
00432                 // (cannot have more recursive calls, because they occur only when
00433                 // the string contains double-ctrlChar which cannot happen here)
00434                 repStr = context->dest + context->destLen;  // repl. string after expr substitutions
00435                 destLenUCS160 = context->destLenUCS16;
00436                 col0 = context->col;
00437                 if (!NMEAddString(str + k, len, context->ctrlChar, context))
00438                     return FALSE;
00439                 repStrLen = context->dest + context->destLen - repStr;
00440                 context->destLen = repStr - context->dest;
00441                 context->destLenUCS16 = destLenUCS160;
00442                 context->col = col0;
00443                 
00444                 // copy result times dest[*destLen..*destLen+len-1]
00445                 if (result > 100)
00446                     result = 100;   // avoid overflows
00447                 if (result > 0 && context->destLen + result * repStrLen > context->bufSize)
00448                     return FALSE;
00449                 for (; result > 0; result--)
00450                 {
00451                     for (i = 0; i < repStrLen; i++)
00452                     {
00453                         context->dest[context->destLen++] = repStr[i];
00454                         if (isFirstUTF8Byte(repStr[i]))
00455                             context->destLenUCS16++;
00456                     }
00457                     context->col += repStrLen;
00458                 }
00459                 // skip rep string and double ctrlChar
00460                 k += len + 2;
00461             }
00462             else
00463             {
00464                 // write result
00465                 if (context->destLen + 12 > context->bufSize)
00466                     return FALSE;
00467                 if (result < 0)
00468                 {
00469                     context->dest[context->destLen++] = '-';
00470                     context->destLenUCS16++;
00471                     result = -result;
00472                     context->col++;
00473                 }
00474                 for (i = 1000000000; i >= 1; i /= 10)
00475                     if (result >= i || i == 1)
00476                     {
00477                         context->dest[context->destLen++] = '0' + (result / i) % 10;
00478                         context->destLenUCS16++;
00479                         context->col++;
00480                     }
00481             }
00482         }
00483         else if (k + 2 < strLen && str[k] == ctrlChar && str[k + 1] == 'L')
00484         {
00485             NMEConstText str = NMECurrentListNesting(context);
00486             NMEInt i;
00487             
00488             if (context->destLen + kMaxNesting > context->bufSize)
00489                 return FALSE;
00490             for (i = 0; str[i]; i++)
00491             {
00492                 context->dest[context->destLen++] = str[i];
00493                 context->destLenUCS16++;
00494                 context->col++;
00495             }
00496             k += 2;
00497         }
00498         else
00499         {
00500             if (isFirstUTF8Byte(str[k]))
00501                 context->destLenUCS16++;
00502             context->dest[context->destLen++] = str[k++];
00503             context->col++;
00504         }
00505     
00506     return TRUE;
00507 }
00508 
00509 NMEErr NMEAddRawString(NMEConstText str,
00510         NMEInt strLen,
00511         NMEContext *context)
00512 {
00513     NMEInt i;
00514     NMEText d = context->dest + context->destLen;
00515     
00516     if (!str)
00517         return kNMEErrOk;   // no op if str is NULL
00518     
00519     if (strLen < 0)
00520         for (strLen = 0; str[strLen]; strLen++)
00521             ;
00522     
00523     if (context->destLen + strLen > context->bufSize)
00524         return kNMEErrNotEnoughMemory;
00525     
00526     for (i = 0; i < strLen; i++)
00527     {
00528         d[i] = str[i];
00529         if (isFirstUTF8Byte(str[i]))
00530             context->destLenUCS16++;
00531     }
00532     context->destLen += strLen;
00533     context->col = 0;
00534     
00535     return kNMEErrOk;
00536 }
00537 
00538 NMEErr NMECopySource(NMEInt length,
00539         NMEBoolean copy,
00540         NMEBoolean encodeChar,
00541         NMEContext *context)
00542 {
00543     if (context->srcIndexOffset + context->srcIndex + length > context->srcLen)
00544         length = context->srcLen - (context->srcIndexOffset + context->srcIndex);
00545     if (copy)
00546     {
00547         NMEInt i;
00548         NMEErr err;
00549         
00550         if (encodeChar && context->outputFormat->encodeCharFun)
00551         {
00552             NMEEncodeCharFun fun = context->outputFormat->encodeCharFun;
00553             void *data = context->outputFormat->encodeCharData;
00554             
00555             for (i = context->srcIndex; i < context->srcIndex + length; )
00556                 CheckError(fun(context->src, context->srcLen, &i, context, data));
00557         }
00558         else
00559         {
00560             NMEConstText s = context->src + context->srcIndex;
00561             NMEText d = context->dest + context->destLen;
00562             
00563             if (context->destLen + length > context->bufSize)
00564                 return kNMEErrNotEnoughMemory;
00565             
00566             for (i = 0; i < length; i++)
00567             {
00568                 d[i] = s[i];
00569                 if (isFirstUTF8Byte(s[i]))
00570                     context->destLenUCS16++;
00571             }
00572             context->srcIndex += length;
00573             context->destLen += length;
00574         }
00575     }
00576     
00577     context->srcIndex += length;
00578     context->col = 0;
00579     return kNMEErrOk;
00580 }
00581 
00582 void NMEResetOutput(NMEContext *context)
00583 {
00584     context->destLen = 0;
00585 }
00586 
00593 static NMEErr checkWordwrap(NMEContext *context,
00594         NMEOutputFormat const *outputFormat)
00595 {
00596     if (outputFormat && outputFormat->textWidth > 0
00597             && context->col >= outputFormat->textWidth)
00598     {
00599         // current line longer that textWidth: do wordwrap
00600         
00601         NMEInt i, j, dist;
00602         NMEWordwrapPermission perm;
00603         
00604         perm = kNMEWordwrapNo;
00605         if (outputFormat->wordwrapPermFun)
00606         {
00607             // find last wordwrap point on current line
00608             for (i = context->destLen - 1;
00609                     i >= 0 && !isEol(context->dest[i]);
00610                     i--)
00611             {
00612                 perm = outputFormat->wordwrapPermFun(context->dest, context->destLen, i,
00613                         context, outputFormat->wordwrapPermData);
00614                 if (perm != kNMEWordwrapNo)
00615                     break;
00616             }
00617         }
00618         else
00619         {
00620             // find last space on current line
00621             for (i = context->destLen - 1;
00622                     i >= 0 && !isEol(context->dest[i]);
00623                     i--)
00624                 if (isBlank(context->dest[i]))
00625                 {
00626                     perm = kNMEWordwrapReplaceChar;
00627                     break;
00628                 }
00629         }
00630         
00631         // ret. if none
00632         if (perm == kNMEWordwrapNo)
00633             return kNMEErrOk;
00634         
00635         // if eol has two char or spaceBeforeWordWrap, insert enough space
00636         dist = (context->eol[1] ? 2 : 1)
00637                 + (perm == kNMEWordwrapReplaceChar ? -1 : 0)
00638                 + context->currentIndent;
00639         if (dist > 0)
00640         {
00641             if (context->destLen + dist > context->bufSize)
00642                 return kNMEErrNotEnoughMemory;
00643             for (j = context->destLen - 1; j > i; j--)
00644                 context->dest[j + dist] = context->dest[j];
00645             context->destLen += dist;
00646             context->destLenUCS16 += dist;
00647         }
00648         
00649         // insert eol
00650         if (perm == kNMEWordwrapInsert)
00651             i++;    // keep character
00652         context->dest[i++] = context->eol[0];
00653         if (context->eol[1])
00654             context->dest[i++] = context->eol[1];
00655         for (j = 0; j < context->currentIndent; j++)
00656             context->dest[i++] = ' ';
00657         context->col = context->destLen - i + context->currentIndent;
00658     }
00659     
00660     return kNMEErrOk;
00661 }
00662 
00664 NMEEncodeCharDict const NMEXMLCharDict[] =
00665 {
00666     {'<', "&lt;"},
00667     {'>', "&gt;"},
00668     {'"', "&quot;"},
00669     {'&', "&amp;"},
00670     {0, NULL}
00671 };
00672 
00674 static NMEEncodeCharDict const latexCharDict[] =
00675 {
00676     {'#', "\\#"},
00677     {'^', "$\\,\\hat{}\\,$"},
00678     {'~', "$\\,\\tilde{}\\,$"},
00679     {'\\', "$\\backslash$"},
00680     {'|', "$|$"},
00681     {'\'', "\'{}"},
00682     {'`', "`{}"},
00683     {'<', "$<$"},
00684     {'>', "$>$"},
00685     {'{', "\\{"},
00686     {'}', "\\}"},
00687     {0, NULL}
00688 };
00689 
00690 NMEErr NMEEncodeCharFunDict(NMEConstText src, NMEInt srcLen, NMEInt *srcIx,
00691         NMEContext *context, void *data)
00692 {
00693     NMEInt i;
00694     (void)srcLen;
00695     
00696     for (i = 0; ((NMEEncodeCharDict const *)data)[i].str; i++)
00697         if (src[*srcIx] == ((NMEEncodeCharDict const *)data)[i].ch)
00698         {
00699             if (!NMEAddString(((NMEEncodeCharDict const *)data)[i].str, -1,
00700                     context->ctrlChar, context))
00701                 return kNMEErrNotEnoughMemory;
00702             (*srcIx)++;
00703             return kNMEErrOk;
00704         }
00705     if (!NMEAddString(&src[(*srcIx)++], 1, context->ctrlChar, context))
00706         return kNMEErrNotEnoughMemory;
00707     return kNMEErrOk;
00708 }
00709 
00720 static NMEErr encodeCharFunNME(NMEConstText src, NMEInt srcLen, NMEInt *srcIx,
00721         NMEContext *context, void *data)
00722 {
00723     NMEInt i;
00724     NMEConstText output;
00725     NMEInt outputLength;
00726     NMEChar prev, str[2];
00727     static const struct
00728     {
00729         NMEChar ch; 
00730         NMEChar prev;   
00732     } dict[] =
00733     {
00734         {'*', '\n'},    // first nonblank character of the line
00735         {'#', '\n'},
00736         {';', '\n'},
00737         {':', '\n'},
00738         {'=', '\n'},
00739         {'-', '\n'},
00740         {'*', '*'}, // double characters
00741         {'/', '/'},
00742         {'#', '#'},
00743         {'_', '_'},
00744         {'^', '^'},
00745         {',', ','},
00746         {'[', '['},
00747         {']', ']'},
00748         {'{', '{'},
00749         {'}', '}'},
00750         {'<', '<'},
00751         {'>', '>'},
00752         {'\\', '\\'},
00753         {'~', '\0'},    // other
00754         {'|', '\0'},    // other
00755         {0, 0}
00756     };
00757     (void)srcLen;
00758     (void)data;
00759     
00760     // get preceding character
00761     NMECurrentOutput(context, &output, &outputLength);
00762     prev = outputLength > 0 ? output[outputLength - 1] : '\n';
00763     for (i = outputLength - 1; i >= 0 && isBlank(output[i] == ' '); i--)
00764         ;
00765     if (i < 0 || isEol(output[i]))
00766         prev = '\n';
00767     
00768     // look for a matching entry in dict[]
00769     for (i = 0; dict[i].ch; i++)
00770         if (src[*srcIx] == dict[i].ch && (!dict[i].prev || prev == dict[i].prev))
00771         {
00772             str[0] = '~';
00773             str[1] = dict[i].ch;
00774             if (!NMEAddString(str, 2, '\0', context))
00775                 return kNMEErrNotEnoughMemory;
00776             (*srcIx)++;
00777             return kNMEErrOk;
00778         }
00779     if (!NMEAddString(&src[(*srcIx)++], 1, '\0', context))
00780         return kNMEErrNotEnoughMemory;
00781     return kNMEErrOk;
00782 }
00783 
00784 NMEOutputFormat const NMEOutputFormatText =
00785 {
00786     " ",    // space
00787     3,  // indentSpaces
00788     10, // defFontSize
00789     '%',    // ctrlChar
00790     "", "", // doc
00791     4,  // highest heading level
00792     "%%{4-l} %%%%{i>0}%{i}. %%", "\n\n",    // heading
00793     "", "\n\n", // par
00794     "\n",   // line break
00795     "", "\n",   // pre
00796     "", "\n",   // pre line
00797     FALSE,  // sublistInListItem
00798     "", "%%{l=1}\n%%",  // UL
00799     "%%{3*l-2} %%- ", "\n", // UL line
00800     "", "%%{l=1}\n%%",  // OL
00801     "%%{3*l-3} %%%{i}. ", "\n", // OL line
00802     "", "%%{l=1}\n%%",  // DL
00803     "%%{3*l-3} %%", "\n",   // DT
00804     NULL,   // emptyDT
00805     "%%{3*l-1} %%", "\n",   // DD
00806     "", "%%{l=1}\n%%",  // indented section
00807     "%%{3*l} %%", "\n", // indented par
00808     "", "\n",   // table
00809     "", "\n",   // table row
00810     "", "\t",   // table header cell
00811     "", "\t",   // table normal cell
00812     "%%{10}-%%\n\n",    // hr
00813     "", "", // bold
00814     "", "", // italic
00815     "", "", // underline
00816     "", "", // superscript
00817     "", "", // subscript
00818     "", "", // monospace
00819     "", "", NULL, FALSE,    // link
00820     "", "", NULL, FALSE, FALSE, // image
00821     NULL,   // interwikis
00822     NULL, NULL, // encodeURLFun
00823     NULL, NULL, // char encoder
00824     NULL, NULL, // char pre encoder
00825     70, NULL, NULL, // wordwrap
00826     NULL, NULL, // char hook
00827     NULL, NULL, NULL, NULL, // process hooks
00828     NULL,   // plugins
00829     NULL,   // autoconverts
00830     NULL, NULL  // getVar
00831 };
00832 
00833 NMEOutputFormat const NMEOutputFormatTextCompact =
00834 {
00835     " ",    // space
00836     3,  // indentSpaces
00837     10, // defFontSize
00838     '%',    // ctrlChar
00839     "", "", // doc
00840     4,  // highest heading level
00841     "%%{p>0}\n%%%%{4-l} %%%%{i>0}%{i}. %%", "\n",   // heading
00842     "", "\n",   // par
00843     "\n",   // line break
00844     "", "", // pre
00845     "", "\n",   // pre line
00846     FALSE,  // sublistInListItem
00847     "", "%%{l=1}%%",    // UL
00848     "%%{3*l-2} %%- ", "\n", // UL line
00849     "", "%%{l=1}%%",    // OL
00850     "%%{3*l-3} %%%{i}. ", "\n", // OL line
00851     "", "%%{l=1}%%",    // DL
00852     "%%{3*l-3} %%", "\n",   // DT
00853     NULL,   // emptyDT
00854     "%%{3*l-1} %%", "\n",   // DD
00855     "", "%%{l=1}%%",    // indented section
00856     "%%{3*l} %%", "\n", // indented par
00857     "", "", // table
00858     "", "\n",   // table row
00859     "", "\t",   // table header cell
00860     "", "\t",   // table normal cell
00861     "%%{10}-%%\n",  // hr
00862     "", "", // bold
00863     "", "", // italic
00864     "", "", // underline
00865     "", "", // superscript
00866     "", "", // subscript
00867     "", "", // monospace
00868     "", "", NULL, FALSE,    // link
00869     "", "", NULL, FALSE, FALSE, // image
00870     NULL,   // interwikis
00871     NULL, NULL, // encodeURLFun
00872     NULL, NULL, // char encoder
00873     NULL, NULL, // char pre encoder
00874     70, NULL, NULL, // wordwrap
00875     NULL, NULL, // char hook
00876     NULL, NULL, NULL, NULL, // process hooks
00877     NULL,   // plugins
00878     NULL,   // autoconverts
00879     NULL, NULL  // getVar
00880 };
00881 
00882 NMEOutputFormat const NMEOutputFormatDebug =
00883 {
00884     "[s]",  // space
00885     3,  // indentSpaces
00886     10, // defFontSize
00887     '%',    // ctrlChar
00888     "<doc>\n", "</doc>\n",  // doc
00889     4,  // highest heading level
00890     "<h l=\"%{l}\" i=\"%{i}\">", "</h>\n",  // heading
00891     "<p>", "</p>\n",    // par
00892     "<br/>\n",  // line break
00893     "<pre>", "</pre>\n",    // pre
00894     "<preline>", "</preline>\n",    // pre line
00895     FALSE,  // sublistInListItem
00896     "%%{l-1} %%<ul l=\"%{l}\">\n", "%%{l-1} %%</ul l=\"%{l}\">\n",  // UL
00897     "%%{l} %%<uli l=\"%{l}\" i=\"%{i}\">", "</uli l=\"%{l}\" i=\"%{i}\">\n",    // UL line
00898     "%%{l-1} %%<ol l=\"%{l}\">\n", "%%{l-1} %%</ol l=\"%{l}\">\n",  // OL
00899     "%%{l} %%<oli l=\"%{l}\" i=\"%{i}\">", "</oli l=\"%{l}\" i=\"%{i}\">\n",    // OL line
00900     "%%{l-1} %%<dl l=\"%{l}\">\n", "%%{l-1} %%</dl l=\"%{l}\">\n",  // DL
00901     "%%{l} %%<dlt l=\"%{l}\" i=\"%{i}>", "%%{l} %%</dlt l=\"%{l}\" i=\"%{i}>\n",    // DT
00902     "<dlt/>",   // emptyDT
00903     "%%{l} %%<dld l=\"%{l}\" i=\"%{i}>", "%%{l} %%</dld l=\"%{l}\" i=\"%{i}>\n",    // DD
00904     "%%{l-1} %%<indentsec l=\"%{l}\">\n", "%%{l-1} %%</indentsec l=\"%{l}\">\n",    // indented section
00905     "%%{l-1} %%<indentpar l=\"%{l}\">", "%%{l-1} %%</indentpar l=\"%{l}\">\n",  // indented par
00906     "<table>\n", "</table>\n",  // table
00907     " <tr>\n", " </tr>\n",  // table row
00908     "  <th>", "</th>\t",    // table header cell
00909     "  <td>", "</td>\t",    // table normal cell
00910     "<hr/>\n",  // hr
00911     "<b>", "</b>",  // bold
00912     "<i>", "</i>",  // italic
00913     "<u>", "</u>",  // underline
00914     "<sub>", "</sub>",  // superscript
00915     "<super>", "</super>",  // subscript
00916     "<code>", "</code>",    // monospace
00917     "<a href=\"", "</a>", "\">", FALSE, // link
00918     "<img src=\"", "\" />", "\" alt=\"", FALSE, TRUE,   // image
00919     NULL,   // interwikis
00920     NULL, NULL, // encodeURLFun
00921     NULL, NULL, // char encoder
00922     NULL, NULL, // char pre encoder
00923     70, NULL, NULL, // wordwrap
00924     NULL, NULL, // char hook
00925     NULL, NULL, NULL, NULL, // process hooks
00926     NULL,   // plugins
00927     NULL,   // autoconverts
00928     NULL, NULL  // getVar
00929 };
00930 
00939 static NMEErr encodeURLFunNull(NMEConstText link, NMEInt linkLen,
00940         NMEContext *context, void *data)
00941 {
00942     return kNMEErrOk;
00943 }
00944 
00955 static NMEErr encodeCharFunNull(NMEConstText src, NMEInt srcLen, NMEInt *srcIx,
00956         NMEContext *context,
00957         void *data)
00958 {
00959     (*srcIx)++;
00960     return kNMEErrOk;
00961 }
00962 
00963 NMEOutputFormat const NMEOutputFormatNull =
00964 {
00965     "", // space
00966     0,  // indentSpaces
00967     10, // defFontSize
00968     '%',    // ctrlChar
00969     "", "", // doc
00970     4,  // highest heading level
00971     "", "", // heading
00972     "", "", // par
00973     "", // line break
00974     "", "", // pre
00975     "", "", // pre line
00976     FALSE,  // sublistInListItem
00977     "", "", // UL
00978     "", "", // UL line
00979     "", "", // OL
00980     "", "", // OL line
00981     "", "", // DL
00982     "", "", // DT
00983     NULL,   // emptyDT
00984     "", "", // DD
00985     "", "", // indented section
00986     "", "", // indented par
00987     "", "", // table
00988     "", "", // table row
00989     "", "", // table header cell
00990     "", "", // table normal cell
00991     "", // hr
00992     "", "", // bold
00993     "", "", // italic
00994     "", "", // underline
00995     "", "", // superscript
00996     "", "", // subscript
00997     "", "", // monospace
00998     "", "", NULL, FALSE,    // link
00999     "", "", NULL, FALSE, FALSE, // image
01000     NULL,   // interwikis
01001     encodeURLFunNull, NULL, // encodeURLFun
01002     encodeCharFunNull, NULL,    // char encoder
01003     encodeCharFunNull, NULL,    // char pre encoder
01004     -1, NULL, NULL, // no wordwrap
01005     NULL, NULL, // char hook
01006     NULL, NULL, NULL, NULL, // process hooks
01007     NULL,   // plugins
01008     NULL,   // autoconverts
01009     NULL, NULL  // getVar
01010 };
01011 
01021 static NMEWordwrapPermission wordwrapCheckNMEFun(NMEConstText txt,
01022         NMEInt len, NMEInt i,
01023         NMEContext *context,
01024         void *data)
01025 {
01026     NMEConstText listNesting;
01027     NMEInt j;
01028     (void)data;
01029     
01030     // check we aren't in a table
01031     listNesting = NMECurrentListNesting(context);
01032     for (j = 0; listNesting[j]; j++)
01033         if (listNesting[j] == '|')
01034             return kNMEWordwrapNo;
01035     
01036     // check the next character doesn't get a special meaning if moved to the beginning of a line
01037     return txt[i] == ' ' && i + 1 < len
01038                 && txt[i + 1] != '*'
01039                 && txt[i + 1] != '#'
01040                 && txt[i + 1] != '|'
01041                 && txt[i + 1] != '='
01042             ? kNMEWordwrapReplaceChar
01043             : kNMEWordwrapNo;
01044 }
01045 
01046 NMEOutputFormat const NMEOutputFormatNME =
01047 {
01048     " ",    // space
01049     0,  // indentSpaces
01050     10, // defFontSize
01051     '%',    // ctrlChar
01052     "", "", // doc
01053     6,  // highest heading level
01054     "%%{l}=%%", "%%{l}=%%\n",   // heading
01055     "", "\n\n", // par
01056     "\\\\", // line break
01057     "{{{\n", "}}}\n\n", // pre
01058     "", "\n",   // pre line
01059     FALSE,  // sublistInListItem
01060     "", "%%{l=1}\n%%",  // UL
01061     "%L ", "\n",    // UL line
01062     "", "%%{l=1}\n%%",  // OL
01063     "%L ", "\n",    // OL line
01064     "", "%%{l=1}\n%%",  // DL
01065     "%L ", "\n",    // DT
01066     NULL,   // emptyDT
01067     "%L ", "\n",    // DD
01068     "", "%%{l=1}\n%%",  // indented section
01069     "%%{l}:%% ", "\n",  // indented par
01070     "", "\n",   // table
01071     "", "\n",   // table row
01072     "|=", "",   // table header cell
01073     "|", "",    // table normal cell
01074     "----\n\n", // hr
01075     "**", "**", // bold
01076     "//", "//", // italic
01077     "__", "__", // underline
01078     "^^", "^^", // superscript
01079     ",,", ",,", // subscript
01080     "##", "##", // monospace
01081     "[[", "]]", "|", FALSE, // link
01082     "{{", "}}", "|", FALSE, FALSE,  // image
01083     NULL,   // interwiki
01084     NULL, NULL, // encodeURLFun
01085     encodeCharFunNME, NULL, // char encoder
01086     NULL, NULL, // char pre encoder
01087     70, wordwrapCheckNMEFun, NULL,  // wordwrap
01088     NULL, NULL, // char hook
01089     NULL, NULL, NULL, NULL, // process hooks
01090     NULL,   // plugins
01091     NULL,   // autoconverts
01092     NULL, NULL  // getVar
01093 };
01094 
01095 NMEOutputFormat const NMEOutputFormatHTML =
01096 {
01097     " ",    // space
01098     2,  // indentSpaces
01099     0,  // defFontSize
01100     '%',    // ctrlChar
01101     "<!-- Generated by Nyctergatis Markup Engine, "
01102             __DATE__ " " __TIME__ " -->\n"
01103     "<html>\n<body>\n", "</body>\n</html>\n",   // doc
01104     4,  // highest heading level
01105     "<h%{l}"
01106             "%%{x} id=\"h%{o}\"%%"
01107             "%%{s>0} style=\"font-size:%{l=1?3*s:l=2?2*s:l=3?3*s/2|5*s/4}pt\"%%>"
01108             "%%{i>0}%{i}. %%",
01109         "</h%{l}>\n",   // heading
01110     "<p%%{s>0} style=\"font-size:%{s}pt\"%%>", "</p>\n",    // par
01111     "<br />",   // line break
01112     "<pre%%{s>0} style=\"font-size:%{s}pt\"%%>\n", "</pre>\n",  // pre
01113     "", "\n",   // pre line
01114     TRUE,   // sublistInListItem
01115     "\n<ul>\n", "</ul>\n",  // UL
01116     "%%{2*l} %%<li%%{s>0} style=\"font-size:%{s}pt\"%%>", "</li>\n",    // UL line
01117     "\n<ol>\n", "</ol>\n",  // OL
01118     "%%{2*l} %%<li%%{s>0} style=\"font-size:%{s}pt\"%%>", "</li>\n",    // OL line
01119     "\n<dl>\n", "</dl>\n",  // DL
01120     "%%{2*l} %%<dt%%{s>0} style=\"font-size:%{s}pt\"%%>", "</dt>\n",    // DT
01121     NULL,   // emptyDT
01122     "%%{2*l} %%<dd%%{s>0} style=\"font-size:%{s}pt\"%%>", "</dd>\n",    // DD
01123     "<div style=\"margin-left:2em%%{s>0}; font-size:%{s}pt%%\">\n",
01124         "</div>\n", // indented section
01125     "%%{2*l} %%<p%%{s>0} style=\"font-size:%{s}pt\"%%>", "</p>\n",  // indented par
01126     "<table>\n", "</table>\n",  // table
01127     "<tr>", "</tr>\n",  // table row
01128     "<th%%{s>0} style=\"font-size:%{s}pt\"%%>", "</th>\n",  // table header cell
01129     "<td%%{s>0} style=\"font-size:%{s}pt\"%%>", "</td>\n",  // table normal cell
01130     "<hr />\n", // hr
01131 #if defined(useHTMLEmphasisTags)
01132     "<strong>", "</strong>",    // strong emphasis for **...**
01133     "<em>", "</em>",    // emphasis for //...//
01134 #else
01135     "<b>", "</b>",  // bold
01136     "<i>", "</i>",  // italic
01137 #endif
01138     "<u>", "</u>",  // underline
01139     "<sup>", "</sup>",  // superscript
01140     "<sub>", "</sub>",  // subscript
01141     "<tt>", "</tt>",    // monospace
01142     "<a href=\"", "</a>", "\">", FALSE, // link
01143     "<img src=\"", "\" />", "\" alt=\"", FALSE, TRUE,   // image
01144     NULL,   // interwiki
01145     NULL, NULL, // encodeURLFun
01146     NMEEncodeCharFunDict, (void *)NMEXMLCharDict,   // char encoder
01147     NMEEncodeCharFunDict, (void *)NMEXMLCharDict,   // char pre encoder
01148     70, NULL, NULL, // wordwrap
01149     NULL, NULL, // char hook
01150     NULL, NULL, NULL, NULL, // process hooks
01151     NULL,   // plugins
01152     NULL,   // autoconverts
01153     NULL, NULL  // getVar
01154 };
01155 
01169 static NMEErr encodeCharRTFFun(NMEConstText src, NMEInt srcLen, NMEInt *srcIx,
01170         NMEContext *context, void *data)
01171 {
01172     NMEInt ch, i;
01173     (void)data;
01174     
01175     if ((src[*srcIx] & 0x80) == 0)  // one byte, ASCII
01176     {
01177         if (context->destLen + 1 >= context->bufSize)
01178             return kNMEErrNotEnoughMemory;
01179         if (src[*srcIx] == '\\' || src[*srcIx] == '{' || src[*srcIx] == '}')
01180         {
01181             context->dest[context->destLen++] = '\\';   // \, { and } must be escaped
01182             context->destLenUCS16++;
01183         }
01184         context->dest[context->destLen++] = src[(*srcIx)++];
01185         context->destLenUCS16++;
01186         return kNMEErrOk;
01187     }
01188     else if (*srcIx + 1 < srcLen && (src[*srcIx] & 0xe0) == 0xc0    // two bytes
01189             && (src[*srcIx + 1] & 0xc0) == 0x80)
01190     {
01191         ch = (((NMEInt)src[*srcIx] & 0x1f) << 6) | (src[*srcIx + 1] & 0x3f);
01192         *srcIx += 2;
01193     }
01194     else if (*srcIx + 2 < srcLen && (src[*srcIx] & 0xf0) == 0xe0    // three bytes
01195             && (src[*srcIx + 1] & 0xc0) == 0x80 && (src[*srcIx + 2] & 0xc0) == 0x80)
01196     {
01197         ch = ((NMEInt)src[*srcIx] << 12) | (((NMEInt)src[*srcIx + 1] & 0x3f) << 6)
01198                 | (src[*srcIx + 2] & 0x3f);
01199         *srcIx += 3;
01200     }
01201     else
01202     {
01203         *srcIx += 1;    // ignore byte
01204         return kNMEErrOk;
01205     }
01206     
01207     if (context->destLen + 9 >= context->bufSize)   // worst case (\u-30000?)
01208         return kNMEErrNotEnoughMemory;
01209     
01210     // unsigned -> signed
01211     if (ch >= 32768)
01212         ch -= 65536;
01213     
01214     context->dest[context->destLen++] = '\\';
01215     context->dest[context->destLen++] = 'u';
01216     if (ch < 0)
01217     {
01218         context->dest[context->destLen++] = '-';
01219         context->destLenUCS16++;
01220         ch = -ch;
01221     }
01222     for (i = 1; i < ch; i *= 10)
01223         ;
01224     for (i /= 10; i > 0; i /= 10)
01225     {
01226         context->dest[context->destLen++] = '0' + (ch / i) % 10;
01227         context->destLenUCS16++;
01228     }
01229     context->dest[context->destLen++] = '?';    // ANSI representation
01230     context->destLenUCS16 += 3; // \u?
01231     
01232     return kNMEErrOk;
01233 }
01234 
01242 static NMEErr encodeURLFunRTF(NMEConstText link, NMEInt linkLen,
01243         NMEContext *context, void *data)
01244 {
01245     NMEInt i;
01246     NMEErr err;
01247     (void)data;
01248     
01249     // encode each character with encodeCharRTFFun 
01250     for (i = 0; i < linkLen; )
01251         CheckError(encodeCharRTFFun(link, linkLen, &i, context, NULL));
01252     return kNMEErrOk;
01253 }
01254 
01264 static NMEWordwrapPermission wordwrapCheckRTFFun(NMEConstText txt,
01265         NMEInt len, NMEInt i,
01266         NMEContext *context,
01267         void *data)
01268 {
01269     (void)data;
01270     
01271     return txt[i] == ' ' ? kNMEWordwrapInsert : kNMEWordwrapNo;
01272 }
01273 
01274 NMEOutputFormat const NMEOutputFormatRTF =
01275 {
01276 #define SIZE "%{2*s}"   ///< size of text
01277 #define SIZEH "%{l=1?3*s:l=2?5*s/2:l=3?2*s:3*s/2}"  ///< size of heading
01278     " ",    // space
01279     0,  // indentSpaces
01280     10, // defFontSize
01281     '%',    // ctrlChar
01282     "{\\rtf1\\ansi\\deff0"
01283             "{\\fonttbl"
01284                 "{\\f0\\froman Times;}"
01285                 "{\\f1\\fswiss Helvetica;}"
01286                 "{\\f2\\fmodern Courier;}"
01287             "}\n",  // beginDoc
01288         "\n}\n",    // endDoc
01289     4,  // highest heading level
01290     "{\\pard\\sb%{500-100*l}\\li60\\sa40%%{l=1}\\qc%%\\f1"
01291             "\\fs" SIZEH "%%{l!2}\\b%% %%{i>0}%{i}. %%",    // beginHeading
01292         "\\par}\n", // endHeading
01293     "{\\pard\\sb80\\li60\\qj\\fi160\\f0\\fs" SIZE " ",  // beginPar
01294         "\\par}\n", // endPar
01295     "\\line ",  // line break
01296     "{\\pard\\sb80\\li160\\f2\\fs" SIZE " ",    // beginPre
01297         "}\n",  // endPre
01298     "", // beginPreLine
01299         "\\par\n",  // endPreLine
01300     FALSE,  // sublistInListItem
01301     "",
01302         "", // UL
01303     "{\\pard\\sb80\\li%{60+100*l}\\qj\\fi160\\f0\\fs" SIZE " * ",
01304         "\\par}\n", // UL line
01305     "",
01306         "", // OL
01307     "{\\pard\\sb80\\li%{60+100*l}\\qj\\fi160\\f0\\fs" SIZE " %{i}",
01308         "\\par}\n", // OL line
01309     "", "", // DL
01310     "{\\pard\\sb80\\li%{60+100*l}\\qj\\f0\\fs" SIZE "\\i ",
01311         "\\par}\n", // DT
01312     NULL,   // emptyDT
01313     "{\\pard\\sb80\\qj\\fi160\\f0\\fs" SIZE "\\li320 ",
01314         "\\par}\n", // DD
01315     "", "", // indented section
01316     "{\\pard\\sb80\\li%{60+100*l}\\qj\\fi160\\f0\\fs" SIZE " ",
01317         "\\par}\n", // indented par
01318     "{\\par\\li60 ", "\\pard}\n",   // table
01319     "\\trowd\\trautofit1 ", "\\row\n",  // table row
01320     "\\pard\\intbl\\sb80\\qc\\fi160\\f0\\fs" SIZE " {\\b ",
01321         "}\\cell\n",    // table header cell
01322     "\\pard\\intbl\\sb80\\qj\\fi160\\f0\\fs" SIZE " ",
01323         "\\cell\n", // table normal cell
01324     "\\hrule\n",    // hr
01325     "{\\b ",
01326         "}",    // bold
01327     "{\\i ",
01328         "}",    // italic
01329     "{\\ul ",
01330         "}",    // underline
01331     "{\\super ",
01332         "}",    // superscript
01333     "{\\sub ",
01334         "}",    // subscript
01335     "{\\f2 ",
01336         "}",    // monospace
01337     "{\\field{\\*\\fldinst{HYPERLINK \"", "}}", "\"}}{\\fldrslt ", FALSE,   // link
01338     "", "", NULL, FALSE, FALSE, // image
01339     NULL,   // interwiki
01340     encodeURLFunRTF, NULL,  // encodeURLFun
01341     encodeCharRTFFun, NULL, // char encoder
01342     encodeCharRTFFun, NULL, // char pre encoder
01343     70, wordwrapCheckRTFFun, NULL,  // wordwrap (keep space)
01344     NULL, NULL, // char hook
01345     NULL, NULL, NULL, NULL, // process hooks
01346     NULL,   // plugins
01347     NULL,   // autoconverts
01348     NULL, NULL  // getVar
01349 #undef SIZE
01350 #undef SIZEH
01351 };
01352 
01353 NMEOutputFormat const NMEOutputFormatLaTeX =
01354 {
01355     " ",    // space
01356     2,  // indentSpaces
01357     10, // defFontSize
01358     '%',    // ctrlChar
01359     "\\documentclass[%{s}pt]{article}\n"
01360     "\\usepackage{hyperref}\n"
01361     "\\begin{document}\n",  // beginDoc
01362         "\n\\end{document}\n",  // endDoc
01363     4,  // highest heading level
01364     "\n\\%%{l>3?2:l-1}sub%%section%%{l>3|i<1}*%%{", // beginHeading
01365         "}\n",  // endHeading
01366     "\n",   // beginPar
01367         "\n",   // endPar
01368     "\\\\", // line break
01369     "\n\\begin{verbatim}\n",    // beginPre
01370         "\\end{verbatim}\n",    // endPre
01371     "", // beginPreLine
01372         "\n",   // endPreLine
01373     FALSE,  // sublistInListItem
01374     "\\begin{itemize}\n",
01375         "\\end{itemize}\n", // UL
01376     "%%{2*l} %%\\item ",
01377         "\n",   // UL line
01378     "\\begin{enumerate}\n",
01379         "\\end{enumerate}\n",   // OL
01380     "%%{2*l} %%\\item ",
01381         "\n",   // OL line
01382     "\\begin{description}\n",
01383         "\\end{description}\n", // DL
01384     "%%{2*l} %%\\item[", "] ",  // DT
01385     NULL,   // emptyDT
01386     "\n", "\n", // DD
01387     "\\begin{itemize}\n", "\\end{itemize}\n",   // indented section
01388     "\\item[] ", "\n",  // indented par
01389     "\\begin{tabular}{llllllllllllllll}\n", "\\end{tabular}\n", // table
01390     "", "\\\\\n",   // table row
01391     "{\\bf ", "} & ",   // table header cell
01392     "", " & ",  // table normal cell
01393     "", // hr
01394     "{\\bfseries ",
01395         "}",    // bold
01396     "{\\itshape ",
01397         "}",    // italic
01398     "\\underline{",
01399         "}",    // underline
01400     "\\textsuperscript{",
01401         "}",    // superscript
01402     "\\ensuremath{_{\\mbox{",
01403         "}}}",  // subscript
01404     "{\\ttfamily ",
01405         "}",    // monospace
01406     "\\href{", "}", "}{", FALSE,    // link
01407     "", "", NULL, FALSE, FALSE, // image
01408     NULL,   // interwiki
01409     NULL, NULL, // encodeURLFun
01410     NMEEncodeCharFunDict, (void *)latexCharDict,    // char encoder
01411     NULL, NULL, // char pre encoder
01412     70, NULL, NULL, // wordwrap
01413     NULL, NULL, // char hook
01414     NULL, NULL, NULL, NULL, // process hooks
01415     NULL,   // plugins
01416     NULL,   // autoconverts
01417     NULL, NULL  // getVar
01418 };
01419 
01420 NMEOutputFormat const NMEOutputFormatMan =
01421 {
01422     " ",    // space
01423     0,  // indentSpaces
01424     10, // defFontSize
01425     '%',    // ctrlChar
01426     ".TH title 1\n", "",    // doc
01427     2,  // highest heading level
01428     "%%{l=1}.SH%%%%{l>1}.SS%% ", "\n",  // heading
01429     ".P\n", "\n",   // par
01430     "", // line break
01431     "", "", // pre
01432     " ", "\n",  // pre line
01433     FALSE,  // sublistInListItem
01434     "", "", // UL
01435     ".IP *\n", "\n",    // UL line
01436     "", "", // OL
01437     ".IP %{i}\n", "\n", // OL line
01438     "", "", // DL
01439     ".IP ", "\n",   // DT
01440     NULL,   // emptyDT
01441     "", "\n",   // DD
01442     "", "", // indented section
01443     "\n.P\n", "\n", // indented par
01444     "", "", // table
01445     "", "\n",   // table row
01446     "", " ",    // table header cell
01447     "", " ",    // table normal cell
01448     "\n",   // hr
01449     "\n.B ", "\n",  // bold
01450     "\n.I ", "\n",  // italic
01451     "", "", // underline
01452     "", "", // superscript
01453     "", "", // subscript
01454     "", "", // monospace
01455     "", "", NULL, FALSE,    // link
01456     "", "", NULL, FALSE, FALSE, // image
01457     NULL,   // interwiki
01458     NULL, NULL, // encodeURLFun
01459     NULL, NULL, // char encoder
01460     NULL, NULL, // char pre encoder
01461     70, NULL, NULL, // wordwrap
01462     NULL, NULL, // char hook
01463     NULL, NULL, NULL, NULL, // process hooks
01464     NULL,   // plugins
01465     NULL,   // autoconverts
01466     NULL, NULL  // getVar
01467 };
01468 
01474 static NMEErr addLink(NMEContext *context,
01475         NMEOutputFormat const *outputFormat)
01476 {
01477     NMEInt i;
01478     NMEConstText link = context->src + context->linkOffset;
01479     NMEInt linkLen = context->linkLength;
01480     NMEErr err;
01481     
01482     if (outputFormat->interwikis)
01483     {
01484         NMEInt iw;
01485         
01486         // find interwiki whose alias matches the beginning of link
01487         for (iw = 0; outputFormat->interwikis[iw].alias; iw++)
01488         {
01489             for (i = 0;
01490                     i < linkLen
01491                         && outputFormat->interwikis[iw].alias[i] != '\0'
01492                         && link[i] == outputFormat->interwikis[iw].alias[i];
01493                     i++)
01494                 ;
01495             if (outputFormat->interwikis[iw].alias[i] == '\0')
01496             {
01497                 // found
01498                 if (!NMEAddString(outputFormat->interwikis[iw].urlPrefix, -1,
01499                             context->ctrlChar, context))
01500                     return kNMEErrNotEnoughMemory;
01501                 CheckError(checkWordwrap(context, outputFormat));
01502                 link += i;
01503                 linkLen -= i;
01504                 break;
01505             }
01506         }
01507     }
01508     
01509     // write (remaining) part of link using encodeURLFun if defined
01510     if (outputFormat->encodeURLFun)
01511         CheckError(outputFormat->encodeURLFun(link, linkLen, context,
01512                 outputFormat->encodeURLData));
01513     else
01514     {
01515         // copy link as is
01516         if (context->destLen + linkLen > context->bufSize)
01517             return kNMEErrNotEnoughMemory;
01518         for (i = 0; i < linkLen; i++)
01519         {
01520             context->dest[context->destLen++] = link[i];
01521             context->destLenUCS16++;
01522         }
01523     }
01524     
01525     return kNMEErrOk;
01526 }
01527 
01535 static NMEBoolean findStyleInStyleStack(
01536         NMEStyle const styleStack[],
01537         NMEInt styleNesting,
01538         NMEStyle style,
01539         NMEInt *i)
01540 {
01541     NMEInt j;
01542     
01543     for (j = 0; j < styleNesting; j++)
01544         if (styleStack[j] == style)
01545         {
01546             if (i)
01547                 *i = j;
01548             return TRUE;
01549         }
01550     return FALSE;
01551 }
01552 
01557 static NMEConstText styleMarkerFromStyleID(NMEStyle style)
01558 {
01559     switch (style)
01560     {
01561         case kNMEStyleBold:
01562             return "**";
01563         case kNMEStyleItalic:
01564             return "//";
01565         case kNMEStyleUnderline:
01566             return "__";
01567         case kNMEStyleSuperscript:
01568             return "^^";
01569         case kNMEStyleSubscript:
01570             return ",,";
01571         case kNMEStyleMonospace:
01572             return "##";
01573         case kNMEStyleLink:
01574             return "[[";
01575         case kNMEStyleImage:
01576             return "{{";
01577         default:
01578             return NULL;
01579     }
01580 }
01581 
01591 static NMEErr processStyleTag(
01592         NMEStyle styleStack[/* kNMEStylesCount */],
01593         NMEInt *styleNesting,
01594         NMEStyle style,
01595         NMEInt i0,
01596         NMEOutputFormat const *outputFormat,
01597         NMEContext *context)
01598 {
01599     NMEInt i, j;
01600     NMEErr err;
01601     
01602 #define HOOK(e, st) \
01603     do { \
01604         if (outputFormat->spanHookFun) \
01605         { \
01606             NMEConstText styleStr = styleMarkerFromStyleID(st); \
01607             if (styleStr) \
01608             { \
01609                 updateLineNum(context); \
01610                 CheckError(outputFormat->spanHookFun(kNMEHookLevelSpan, 0, e, styleStr, \
01611                         i0 + context->srcIndexOffset, \
01612                         context->srcLineNum, \
01613                         context, \
01614                         outputFormat->hookData)); \
01615             } \
01616         } \
01617     } while (0)
01618     
01619     // check if s is in styleStack
01620     if (findStyleInStyleStack(styleStack, *styleNesting, style, &i))
01621     {
01622         // remove it as well as all badly nested styles
01623         for (j = *styleNesting - 1; j >= i; j--)
01624             if (styleStack[j] != kNMEStyleVerbatim
01625                     || (context->options & kNMEProcessOptVerbatimMono
01626                         && !findStyleInStyleStack(styleStack, *styleNesting,
01627                                 kNMEStyleMonospace, NULL)))
01628             {
01629                 // write separator and link if they must be after the text
01630                 if (styleStack[j] == kNMEStyleLink
01631                         && outputFormat->sepLink && outputFormat->linkAfterSep)
01632                 {
01633                     if (!NMEAddString(outputFormat->sepLink, -1,
01634                                 context->ctrlChar, context))
01635                         return kNMEErrNotEnoughMemory;
01636                     CheckError(addLink(context, outputFormat));
01637                     CheckError(checkWordwrap(context, outputFormat));
01638                 }
01639                 else if (styleStack[j] == kNMEStyleImage
01640                         && outputFormat->sepImage && outputFormat->imageAfterSep)
01641                 {
01642                     if (!NMEAddString(outputFormat->sepImage, -1,
01643                                 context->ctrlChar, context))
01644                         return kNMEErrNotEnoughMemory;
01645                     CheckError(addLink(context, outputFormat));
01646                     CheckError(checkWordwrap(context, outputFormat));
01647                 }
01648                 
01649                 // write end tag
01650                 if (!NMEAddString(styleStack[j] == kNMEStyleBold
01651                                 ? outputFormat->endBold
01652                             : styleStack[j] == kNMEStyleItalic
01653                                 ? outputFormat->endItalic
01654                             : styleStack[j] == kNMEStyleUnderline
01655                                 ? outputFormat->endUnderline
01656                             : styleStack[j] == kNMEStyleSuperscript
01657                                 ? outputFormat->endSuperscript
01658                             : styleStack[j] == kNMEStyleSubscript
01659                                 ? outputFormat->endSubscript
01660                             : styleStack[j] == kNMEStyleLink
01661                                 ? outputFormat->endLink
01662                             : styleStack[j] == kNMEStyleImage
01663                                 ? outputFormat->endImage
01664                                 : outputFormat->endCode,
01665                             -1,
01666                             context->ctrlChar, context))
01667                     return kNMEErrNotEnoughMemory;
01668                 CheckError(checkWordwrap(context, outputFormat));
01669                 
01670                 // call hook
01671                 HOOK(FALSE, styleStack[j]);
01672             }
01673         // add back badly nested styles, except for kNMEStyleLink or kNMEStyleImage
01674         for (j = i + 1; j < *styleNesting; j++)
01675             if (styleStack[j] != kNMEStyleLink && styleStack[j] != kNMEStyleImage)
01676             {
01677                 HOOK(TRUE, styleStack[j]);
01678                 
01679                 styleStack[i] = styleStack[j];
01680                 if (styleStack[i] != kNMEStyleVerbatim)
01681                 {
01682                     if (!NMEAddString(styleStack[i] == kNMEStyleBold
01683                                 ? outputFormat->beginBold
01684                             : styleStack[i] == kNMEStyleItalic
01685                                 ? outputFormat->beginItalic
01686                             : styleStack[i] == kNMEStyleUnderline
01687                                 ? outputFormat->beginUnderline
01688                             : styleStack[i] == kNMEStyleSuperscript
01689                                 ? outputFormat->beginSuperscript
01690                             : styleStack[i] == kNMEStyleSubscript
01691                                 ? outputFormat->beginSubscript
01692                                 : outputFormat->beginCode,
01693                             -1,
01694                             context->ctrlChar, context))
01695                         return kNMEErrNotEnoughMemory;
01696                     CheckError(checkWordwrap(context, outputFormat));
01697                 }
01698                 i++;
01699             }
01700         *styleNesting -= j - i;
01701         return kNMEErrOk;
01702     }
01703     
01704     // not found; add it unless it's kNMEStyleLink or kNMEStyleImage
01705     //  or any style in image alt text if noStyleInAlt
01706     if (style == kNMEStyleLink || style == kNMEStyleImage
01707             || (outputFormat->noStyleInAlt
01708                 && findStyleInStyleStack(styleStack, *styleNesting,
01709                         kNMEStyleImage, NULL)))
01710         return kNMEErrOk;   // ignore
01711     styleStack[(*styleNesting)++] = style;
01712     
01713     // add beginning tag
01714     if (style == kNMEStyleVerbatim
01715             && (!(context->options & kNMEProcessOptVerbatimMono)
01716                 || findStyleInStyleStack(styleStack, *styleNesting,
01717                                 kNMEStyleMonospace, NULL)))
01718         return kNMEErrOk;
01719     HOOK(TRUE, style);
01720     if (!NMEAddString(style == kNMEStyleBold ? outputFormat->beginBold
01721                     : style == kNMEStyleItalic ? outputFormat->beginItalic
01722                     : style == kNMEStyleUnderline ? outputFormat->beginUnderline
01723                     : style == kNMEStyleSuperscript ? outputFormat->beginSuperscript
01724                     : style == kNMEStyleSubscript ? outputFormat->beginSubscript
01725                     : outputFormat->beginCode,
01726                 -1,
01727                 context->ctrlChar, context))
01728         return kNMEErrNotEnoughMemory;
01729     CheckError(checkWordwrap(context, outputFormat));
01730     return kNMEErrOk;
01731 
01732 #undef HOOK
01733 }
01734 
01743 static NMEErr flushStyleTags(
01744         NMEStyle styleStack[kNMEStylesCount],
01745         NMEInt *styleNesting,
01746         NMEInt i0,
01747         NMEOutputFormat const *outputFormat,
01748         NMEContext *context)
01749 {
01750     NMEInt i;
01751     NMEErr err;
01752     
01753     for (i = *styleNesting - 1; i >= 0; i--)
01754         if (styleStack[i] != kNMEStyleVerbatim
01755                 || (context->options & kNMEProcessOptVerbatimMono
01756                         && !findStyleInStyleStack(styleStack, *styleNesting,
01757                                 kNMEStyleMonospace, NULL)))
01758         {
01759             if (styleStack[i] == kNMEStyleLink
01760                     && outputFormat->sepLink && outputFormat->linkAfterSep)
01761             {
01762                 if (!NMEAddString(outputFormat->sepLink, -1,
01763                             context->ctrlChar, context))
01764                     return kNMEErrNotEnoughMemory;
01765                 CheckError(addLink(context, outputFormat));
01766                 CheckError(checkWordwrap(context, outputFormat));
01767             }
01768             else if (styleStack[i] == kNMEStyleImage
01769                     && outputFormat->sepImage && outputFormat->imageAfterSep)
01770             {
01771                 if (!NMEAddString(outputFormat->sepImage, -1,
01772                             context->ctrlChar, context))
01773                     return kNMEErrNotEnoughMemory;
01774                 CheckError(addLink(context, outputFormat));
01775                 CheckError(checkWordwrap(context, outputFormat));
01776             }
01777             
01778             if (!NMEAddString(styleStack[i] == kNMEStyleBold
01779                                     ? outputFormat->endBold
01780                                 : styleStack[i] == kNMEStyleItalic
01781                                     ? outputFormat->endItalic
01782                                 : styleStack[i] == kNMEStyleUnderline
01783                                     ? outputFormat->endUnderline
01784                                 : styleStack[i] == kNMEStyleSuperscript
01785                                     ? outputFormat->endSuperscript
01786                                 : styleStack[i] == kNMEStyleSubscript
01787                                     ? outputFormat->endSubscript
01788                                 : styleStack[i] == kNMEStyleLink
01789                                     ? outputFormat->endLink
01790                                 : styleStack[i] == kNMEStyleImage
01791                                     ? outputFormat->endImage
01792                                 : outputFormat->endCode,
01793                             -1,
01794                             context->ctrlChar, context))
01795                 return kNMEErrNotEnoughMemory;
01796             CheckError(checkWordwrap(context, outputFormat));
01797                 
01798             // call hook
01799             if (outputFormat->spanHookFun)
01800             {
01801                 NMEConstText styleStr = styleMarkerFromStyleID(styleStack[i]);
01802                 if (styleStr)
01803                 {
01804                     updateLineNum(context);
01805                     CheckError(outputFormat->spanHookFun(kNMEHookLevelSpan, 0,
01806                             FALSE, styleStr,
01807                             i0 + context->srcIndexOffset,
01808                             context->srcLineNum,
01809                             context,
01810                             outputFormat->hookData));
01811                 }
01812             }
01813         }
01814     *styleNesting = 0;
01815     return kNMEErrOk;
01816 }
01817 
01825 static NMEErr addEndPar(NMEBoolean forceEndOfList,
01826         NMEOutputFormat const *outputFormat,
01827         NMEContext *context,
01828         NMEInt srcIndexEndPar)
01829 {
01830     NMEErr err;
01831     
01832 #define HOOK(cb, l, it, e, m) \
01833     do { \
01834         if (outputFormat->cb) \
01835         { \
01836             updateLineNum(context); \
01837             CheckError(outputFormat->cb(l, it, e, m, \
01838                     context->srcIndexOffset + srcIndexEndPar, \
01839                     context->srcLineNum, \
01840                     context, \
01841                     outputFormat->hookData)); \
01842         } \
01843     } while (0)
01844     
01845     if (context->nesting > 0)
01846     {
01847         NMEInt level0, item0;
01848         
01849         level0 = context->level;
01850         item0 = context->item;
01851         setContext(*context, context->nesting, context->listNum[context->nesting - 1]);
01852         
01853         // end of item
01854         if (context->listNum[context->nesting - 1] >= 0)    // ordered list
01855         {
01856             context->listNum[context->nesting - 1]++;
01857             if (!NMEAddString(outputFormat->endOLItem, -1,
01858                         context->ctrlChar, context))
01859                 return kNMEErrNotEnoughMemory;
01860             CheckError(checkWordwrap(context, outputFormat));
01861             HOOK(parHookFun, context->level, context->item, FALSE, "#");
01862         }
01863         else if (context->listNum[context->nesting - 1] == kNMEListNumTableCell
01864                 || context->listNum[context->nesting - 1] == kNMEListNumTableHCell)
01865         {
01866             if (!NMEAddString(context->listNum[context->nesting - 1] == kNMEListNumTableCell
01867                             ? outputFormat->endTableCell
01868                             : outputFormat->endTableHCell,
01869                         -1,
01870                         context->ctrlChar, context))
01871                 return kNMEErrNotEnoughMemory;
01872             CheckError(checkWordwrap(context, outputFormat));
01873             HOOK(parHookFun, kNMEHookLevelPar, 0, FALSE,
01874                     context->listNum[context->nesting - 1] == kNMEListNumTableCell ? "|" : "|=");
01875             if (!NMEAddString(outputFormat->endTableRow, -1,
01876                             context->ctrlChar, context))
01877                 return kNMEErrNotEnoughMemory;
01878             CheckError(checkWordwrap(context, outputFormat));
01879         }
01880         else
01881             switch (context->listNum[context->nesting - 1])
01882             {
01883                 case kNMEListNumUL:
01884                     if (!NMEAddString(outputFormat->endULItem, -1, context->ctrlChar, context))
01885                         return kNMEErrNotEnoughMemory;
01886                     CheckError(checkWordwrap(context, outputFormat));
01887                     HOOK(parHookFun, context->level, context->item, FALSE, "*");
01888                     break;
01889                 case kNMEListNumDT:
01890                     if (!NMEAddString(outputFormat->endDT, -1, context->ctrlChar, context))
01891                         return kNMEErrNotEnoughMemory;
01892                     CheckError(checkWordwrap(context, outputFormat));
01893                     HOOK(parHookFun, context->level, context->item, FALSE, ";");
01894                     break;
01895                 case kNMEListNumDD:
01896                     if (!NMEAddString(outputFormat->endDD, -1, context->ctrlChar, context))
01897                         return kNMEErrNotEnoughMemory;
01898                     CheckError(checkWordwrap(context, outputFormat));
01899                     HOOK(parHookFun, context->level, context->item, FALSE, ";:");
01900                     break;
01901                 case kNMEListIndented:
01902                     if (!NMEAddString(outputFormat->endIndentedPar, -1, context->ctrlChar, context))
01903                         return kNMEErrNotEnoughMemory;
01904                     CheckError(checkWordwrap(context, outputFormat));
01905                     HOOK(parHookFun, context->level, context->item, FALSE, ":");
01906                     break;
01907                 // ordered list: handled above
01908             }
01909         // end of list or table
01910         if (forceEndOfList)
01911             while (context->nesting > 0)
01912             {
01913                 setContext(*context, context->nesting, context->listNum[context->nesting - 1]);
01914                 switch (context->listNum[context->nesting - 1])
01915                 {
01916                     case kNMEListNumUL:
01917                         if (!NMEAddString(outputFormat->endUL, -1, context->ctrlChar, context))
01918                             return kNMEErrNotEnoughMemory;
01919                         CheckError(checkWordwrap(context, outputFormat));
01920                         HOOK(divHookFun, context->level, 0, FALSE, "*");
01921                         break;
01922                     case kNMEListNumDT:
01923                     case kNMEListNumDD:
01924                         if (!NMEAddString(outputFormat->endDL, -1, context->ctrlChar, context))
01925                             return kNMEErrNotEnoughMemory;
01926                         CheckError(checkWordwrap(context, outputFormat));
01927                         HOOK(divHookFun, context->level, 0, FALSE, ";");
01928                         break;
01929                     case kNMEListIndented:
01930                         if (!NMEAddString(outputFormat->endIndented, -1, context->ctrlChar, context))
01931                             return kNMEErrNotEnoughMemory;
01932                         CheckError(checkWordwrap(context, outputFormat));
01933                         HOOK(divHookFun, context->level, 0, FALSE, ":");
01934                         break;
01935                     case kNMEListNumTableCell:
01936                     case kNMEListNumTableHCell:
01937                         if (!NMEAddString(outputFormat->endTable, -1, context->ctrlChar, context))
01938                             return kNMEErrNotEnoughMemory;
01939                         CheckError(checkWordwrap(context, outputFormat));
01940                         HOOK(divHookFun, kNMEHookLevelPar, 0, FALSE, "|");
01941                         break;
01942                     default:
01943                         if (!NMEAddString(outputFormat->endOL, -1, context->ctrlChar, context))
01944                             return kNMEErrNotEnoughMemory;
01945                         CheckError(checkWordwrap(context, outputFormat));
01946                         HOOK(divHookFun, context->level, 0, FALSE, "#");
01947                         break;
01948                 }
01949                 context->nesting--;
01950                 setContext(*context, context->nesting, context->listNum[context->nesting - 1]);
01951                 if (outputFormat->sublistInListItem && context->nesting > 0)
01952                     switch (context->listNum[context->nesting - 1])
01953                     {
01954                         case kNMEListNumUL:
01955                             if (!NMEAddString(outputFormat->endULItem, -1, context->ctrlChar, context))
01956                                 return kNMEErrNotEnoughMemory;
01957                             break;
01958                         case kNMEListNumDT:
01959                         case kNMEListNumDD:
01960                             if (!NMEAddString(outputFormat->endDD, -1, context->ctrlChar, context))
01961                                 return kNMEErrNotEnoughMemory;
01962                             break;
01963                         case kNMEListIndented:
01964                             // straight indenting, no nesting
01965                             break;
01966                         default:    // ordered list
01967                             if (!NMEAddString(outputFormat->endOLItem, -1, context->ctrlChar, context))
01968                                 return kNMEErrNotEnoughMemory;
01969                             break;
01970                     }
01971             }
01972         
01973         setContext(*context, level0, item0);
01974         return kNMEErrOk;
01975     }
01976     else
01977     {
01978         if (!NMEAddString(outputFormat->endPar, -1, context->ctrlChar, context))
01979             return kNMEErrNotEnoughMemory;
01980         CheckError(checkWordwrap(context, outputFormat));
01981         HOOK(parHookFun, kNMEHookLevelPar, 0, FALSE, "p");
01982         return kNMEErrOk;
01983     }
01984 #undef HOOK
01985 }
01986 
01997 static NMEErr addLinkBegin(NMEBoolean isImage,
01998         NMEStyle styleStack[kNMEStylesCount],
01999         NMEInt *styleNesting,
02000         NMEInt i0,
02001         NMEOutputFormat const *outputFormat,
02002         NMEContext *context)
02003 {
02004     NMEInt j, k;
02005     NMEErr err;
02006     
02007     // check if already parsing a link or image
02008     for (j = 0; j < *styleNesting; j++)
02009         if ((styleStack[j] == kNMEStyleLink && !isImage)    // image in links are ok
02010                 || styleStack[j] == kNMEStyleImage)
02011             return kNMEErrOk;
02012     
02013     // skip spaces
02014     skipBlanks(context->src, context->srcLen, &context->srcIndex);
02015     
02016     // find | or ]]/}}, stopping at double end of line
02017     for (j = context->srcIndex; j < context->srcLen
02018                 && (!isEol(context->src[j])
02019                         || (j + 1 < context->srcLen
02020                             && (!isEol(context->src[j + 1])
02021                                 || (context->src[j] == '\r' && context->src[j + 1] =='\n'))))
02022                 && (j + 1 >= context->srcLen
02023                     || context->src[j] != (isImage ? '}' : ']')
02024                     || context->src[j + 1] != context->src[j])
02025                 && context->src[j] != '|';
02026             j++)
02027         ;
02028     
02029     // gobble back trailing spaces and eol
02030     for (k = j;
02031             k > context->srcIndex
02032                 && (isBlank(context->src[k - 1]) || isEol(context->src[k - 1]));
02033             k--)
02034         ;
02035     
02036     // give up if no link
02037     if (k <= context->srcIndex)
02038     {
02039         context->srcIndex = j;
02040         return kNMEErrOk;
02041     }
02042     
02043     // set link location
02044     context->linkOffset = context->srcIndex;
02045     context->linkLength = k - context->srcIndex;
02046     
02047     // call hook, if any
02048     if (outputFormat->spanHookFun)
02049     {
02050         updateLineNum(context);
02051         CheckError(outputFormat->spanHookFun(kNMEHookLevelSpan, 0, TRUE,
02052                 isImage ? "{{" : "[[",
02053                 i0 + context->srcIndexOffset,
02054                 context->srcLineNum,
02055                 context,
02056                 outputFormat->hookData));
02057     }
02058     
02059     if (isImage ? outputFormat->sepImage : outputFormat->sepLink)
02060     {
02061         // write beginning of link
02062         if (!NMEAddString(isImage ? outputFormat->beginImage : outputFormat->beginLink, -1,
02063                     context->ctrlChar, context))
02064             return kNMEErrNotEnoughMemory;
02065         CheckError(checkWordwrap(context, outputFormat));
02066         // write link unless linkAfterSep or imageAfterSep
02067         if (!(isImage ? outputFormat->imageAfterSep : outputFormat->linkAfterSep))
02068         {
02069             CheckError(addLink(context, outputFormat));
02070             if (!NMEAddString(isImage ? outputFormat->sepImage : outputFormat->sepLink, -1,
02071                     context->ctrlChar, context))
02072                 return kNMEErrNotEnoughMemory;
02073             CheckError(checkWordwrap(context, outputFormat));
02074         }
02075     }
02076     
02077     // continue with link text or image alt text
02078     if (j < context->srcLen && context->src[j] == '|')
02079     {
02080         context->srcIndex = j + 1;
02081         skipBlanks(context->src, context->srcLen, &context->srcIndex);
02082     }
02083     else
02084     {
02085         // no separate link text or image alt text: write link verbatim
02086         for (k = 0; k < context->linkLength; )
02087         {
02088             if (outputFormat->charHookFun)
02089                 CheckError(outputFormat->charHookFun(context->linkOffset + k,
02090                         context,
02091                         outputFormat->charHookData));
02092             if (outputFormat->encodeCharFun)
02093                 CheckError(outputFormat->encodeCharFun(context->src + context->linkOffset,
02094                         context->linkLength, &k,
02095                         context,
02096                         outputFormat->encodeCharData));
02097             else
02098             {
02099                 if (context->destLen + 1 > context->bufSize)
02100                     return kNMEErrNotEnoughMemory;
02101                 context->dest[context->destLen++] = context->src[context->linkOffset + k];
02102                 if (isFirstUTF8Byte(context->src[context->linkOffset + k]))
02103                     context->destLenUCS16++;
02104                 k++;
02105                 context->col++;
02106             }
02107             CheckError(checkWordwrap(context, outputFormat));
02108         }
02109         // skip to end of link, before the end markup
02110         context->srcIndex = j;
02111     }
02112     
02113     // add link or image "style", which indicates we're expecting an
02114     //  end-of-link marker
02115     styleStack[(*styleNesting)++] = isImage ? kNMEStyleImage : kNMEStyleLink;
02116     
02117     return kNMEErrOk;
02118 }
02119 
02128 static NMEInt findPlugin(NMEConstText src, NMEInt srcLen, NMEInt i,
02129         NMEBoolean isPlaceholder,
02130         NMEOutputFormat const *outputFormat)
02131 {
02132     NMEConstText name;
02133     NMEInt nameLen, j, k;
02134     
02135     // find name
02136     skipBlanks(src, srcLen, &i);
02137     name = src + i;
02138     for (nameLen = 0;
02139             i + nameLen < srcLen
02140                     && !isBlank(src[i + nameLen])
02141                     && !isEol(src[i + nameLen])
02142                     && src[i + nameLen] != '>';
02143             nameLen++)
02144         ;
02145     
02146     // find plugin
02147     if (outputFormat->plugins && nameLen > 0)
02148         for (j = 0; outputFormat->plugins[j].name; j++)
02149             if (isPlaceholder
02150                     ^ ((outputFormat->plugins[j].options & kNMEPluginOptTripleAngleBrackets) == 0))
02151             {
02152                 // compare plugin name
02153                 for (k = 0; k < nameLen && outputFormat->plugins[j].name[k]; k++)
02154                     if (name[k] != outputFormat->plugins[j].name[k])
02155                         goto continueMainLoop;
02156                 if (outputFormat->plugins[j].name[k] != '\0'
02157                         || (k < nameLen
02158                             && !(outputFormat->plugins[j].options & kNMEPluginOptPartialName)))
02159                     goto continueMainLoop;
02160                 
02161                 return j;
02162 continueMainLoop:
02163                 ;
02164             }
02165     
02166     // not found
02167     return -1;
02168 }
02169 
02179 static NMEErr addPlugin(NMEBoolean isBlock,
02180         NMEBoolean isPlaceholder,
02181         NMEInt options,
02182         NMEOutputFormat const *outputFormat,
02183         NMEContext *context,
02184         NMEBoolean *reparseOutput)
02185 {
02186     NMEConstText name, data;
02187     NMEInt nameLen, dataLen;
02188     NMEInt j, k;
02189     NMEErr err;
02190     
02191     // find name
02192     skipBlanks(context->src, context->srcLen, &context->srcIndex);
02193     name = context->src + context->srcIndex;
02194     for (nameLen = 0;
02195             context->srcIndex + nameLen < context->srcLen
02196                     && !isBlank(context->src[context->srcIndex + nameLen])
02197                     && !isEol(context->src[context->srcIndex + nameLen])
02198                     && context->src[context->srcIndex + nameLen] != '>';
02199             nameLen++)
02200         ;
02201     context->srcIndex += nameLen;
02202     skipBlanks(context->src, context->srcLen, &context->srcIndex);
02203     if (context->srcIndex < context->srcLen && context->src[context->srcIndex] == '\r')
02204         context->srcIndex++;
02205     if (context->srcIndex < context->srcLen && context->src[context->srcIndex] == '\n')
02206         context->srcIndex++;
02207     
02208     // find data
02209     data = context->src + context->srcIndex;
02210     if (isBlock)
02211         for (dataLen = 0;
02212                 context->srcIndex + dataLen + (isPlaceholder ? 2 : 1) < context->srcLen;
02213                 dataLen++)
02214         {
02215             if (isEol(context->src[context->srcIndex + dataLen - 1])
02216                     && context->src[context->srcIndex + dataLen] == '>'
02217                     && context->src[context->srcIndex + dataLen + 1] == '>'
02218                     && (!isPlaceholder
02219                         || context->src[context->srcIndex + dataLen + 2] == '>'))
02220             {
02221                 // >> or >>> at the beginning of a line; check what follows
02222                 j = context->srcIndex + dataLen + (isPlaceholder ? 3 : 2);
02223                 skipBlanks(context->src, context->srcLen, &j);
02224                 if (j >= context->srcLen || isEol(context->src[j]))
02225                     break;  // nothing but blanks: end marker
02226             }
02227         }
02228     else    // inline plugin
02229         for (dataLen = 0;
02230                 context->srcIndex + dataLen + (isPlaceholder ? 2 : 1) < context->srcLen
02231                         && (context->src[context->srcIndex + dataLen] != '>'
02232                             || context->src[context->srcIndex + dataLen + 1] != '>'
02233                             || (isPlaceholder
02234                                 && context->src[context->srcIndex + dataLen + 2] != '>'));
02235                 dataLen++)
02236             ;
02237     context->srcIndex += dataLen;
02238     if (context->srcIndex + (isPlaceholder ? 3 : 2) <= context->srcLen)
02239         context->srcIndex += isPlaceholder ? 3 : 2; // skip >>> or >>
02240     
02241     // remove trailing eol
02242     if (dataLen > 0 && data[dataLen - 1] == '\n')
02243         dataLen--;
02244     if (dataLen > 0 && data[dataLen - 1] == '\r')
02245         dataLen--;
02246     
02247     // find plugin
02248     if (outputFormat->plugins && nameLen > 0)
02249         for (j = 0; outputFormat->plugins[j].name; j++)
02250             if (isPlaceholder
02251                     ^ ((outputFormat->plugins[j].options & kNMEPluginOptTripleAngleBrackets) == 0))
02252             {
02253                 // compare plugin name
02254                 for (k = 0; k < nameLen && outputFormat->plugins[j].name[k]; k++)
02255                     if (name[k] != outputFormat->plugins[j].name[k])
02256                         goto continueMainLoop;
02257                 if (outputFormat->plugins[j].name[k] != '\0'
02258                         || (k < nameLen
02259                             && !(outputFormat->plugins[j].options & kNMEPluginOptPartialName)))
02260                     goto continueMainLoop;
02261                 
02262                 // execute plugin
02263                 CheckError(outputFormat->plugins[j].cb(name, nameLen,
02264                         data, dataLen,
02265                         context,
02266                         outputFormat->plugins[j].userData));
02267                 
02268                 *reparseOutput
02269                         = (outputFormat->plugins[j].options & kNMEPluginOptReparseOutput) != 0;
02270                 
02271                 return kNMEErrOk;
02272 continueMainLoop:
02273                 ;
02274             }
02275     
02276     // not found: ignore
02277     return kNMEErrOk;
02278 }
02279 
02287 static void nextHeading(NMEInt *headingFlags,
02288         NMEInt headingNum[/* kMaxNumberedHeadingLevels */],
02289         NMEInt headingLevel)
02290 {
02291     NMEInt i;
02292     
02293     if (headingLevel <= sizeof(*headingFlags))
02294     {
02295         // set flag corresponding to current level (headingLevel-1)
02296         *headingFlags |= 1 << headingLevel - 1;
02297         // clear flags for i >= headingLevel
02298         *headingFlags &= ~((~(NMEInt)0) << headingLevel);
02299     }
02300     
02301     if (headingLevel <= kMaxNumberedHeadingLevels)
02302     {
02303         headingNum[headingLevel - 1]++;
02304         for (i = headingLevel; i < kMaxNumberedHeadingLevels; i++)
02305             headingNum[i] = 0;
02306     }
02307 }
02308 
02328 static NMEBoolean parseNextToken(NMEConstText src, NMEInt srcLen,
02329         NMEInt *i,
02330         NMEState state,
02331         NMEBoolean verbatim,
02332         NMEInt nesting,
02333         NMEInt const listNum[],
02334         NMEStyle const styleStack[],
02335         NMEInt styleNesting,
02336         NMEOutputFormat const *outputFormat,
02337         NMEToken *token,
02338         NMEInt *headingLevel,
02339         NMEInt *itemNesting,
02340         NMEStyle *style,
02341         NMEInt options)
02342 {
02343     NMEInt k;   // temp. index in src
02344     
02345     // skip blanks before trailing = in headings (must do it here)
02346     if (state == kNMEStateHeading && isBlank(src[*i]))
02347     {
02348         k = *i;
02349         skipBlanks(src, srcLen, &k);
02350         if (k < srcLen && src[k] == '=')
02351         {
02352             NMEInt p;
02353             
02354             // trailing '='?
02355             for (p = k + 1; p < srcLen && src[p] == '='; p++)
02356                 ;
02357             skipBlanks(src, srcLen, &p);
02358             if (p >= srcLen || isEol(src[p]))   // yes
02359                 *i = k; // skip blanks
02360         }
02361     }
02362     
02363     // parse token (sensitive to context)
02364     switch (src[*i])
02365     {
02366         case ' ':
02367             *token = kNMETokenSpace;
02368             (*i)++;
02369             return TRUE;
02370         case '\t':
02371             *token = kNMETokenTab;
02372             (*i)++;
02373             return TRUE;
02374         case '\r':
02375             *token = kNMETokenEOL;
02376             (*i)++;
02377             if (*i < srcLen && src[*i] == '\n')
02378                 (*i)++;
02379             return TRUE;
02380         case '\n':
02381             *token = kNMETokenEOL;
02382             (*i)++;
02383             return TRUE;
02384         case '\\':
02385             if (verbatim)
02386                 break;
02387             switch (state)
02388             {
02389                 case kNMEStatePar:
02390                 case kNMEStateHeading:
02391                 case kNMEStateBetweenPar:
02392                 case kNMEStateParAfterEol:
02393                     if (*i + 1 >= srcLen || src[*i + 1] != '\\')
02394                         break;  // not double backslash -> plain character
02395                     *token = kNMETokenLineBreak;
02396                     *i += 2;
02397                     skipBlanks(src, srcLen, i);
02398                     return TRUE;
02399                 default:
02400                     // plain character
02401                     break;
02402             }
02403             break;
02404         case '=':
02405             if (state == kNMEStatePar || state == kNMEStatePre
02406                     || state == kNMEStatePreAfterEol || verbatim)
02407                 break;  // plain character
02408             if (state == kNMEStateHeading)  // ignore number of ending =
02409             {
02410                 // trailing?
02411                 for (k = *i; k < srcLen && src[k] == '='; k++)
02412                     ;
02413                 skipBlanks(src, srcLen, &k);
02414                 if (k < srcLen && !isEol(src[k]))
02415                     break;  // no, plain character
02416                 while (*i < srcLen && src[*i] == '=')
02417                     (*i)++;
02418             }
02419             else
02420             {
02421                 for (*headingLevel = 0;
02422                         *i < srcLen && src[*i] == '=';
02423                         (*i)++, (*headingLevel)++)
02424                     ;
02425                 if (options & kNMEProcessOptNoH1 && *headingLevel == 1)
02426                     *headingLevel = 2;
02427                 if (*headingLevel > outputFormat->maxHeadingLevel)
02428                     *headingLevel = outputFormat->maxHeadingLevel;
02429             }
02430             *token = kNMETokenHeading;
02431             return TRUE;
02432         case '*':
02433         case '#':
02434             if (verbatim)
02435                 break;  // plain character
02436             switch (state)
02437             {
02438                 case kNMEStatePar:
02439                 case kNMEStateHeading:
02440                     if (*i + 1 >= srcLen || src[*i + 1] != src[*i])
02441                         break;  // not bold/monospace
02442                     if (options & (src[*i] == '*'
02443                             ? kNMEProcessOptNoBold : kNMEProcessOptNoMonospace))
02444                         break;
02445                     *token = kNMETokenStyle;
02446                     *style = src[*i] == '*' ? kNMEStyleBold : kNMEStyleMonospace;
02447                     *i += 2;
02448                     return TRUE;
02449                 case kNMEStateBetweenPar:
02450                 case kNMEStateParAfterEol:
02451                     if (nesting == 0
02452                             || (src[*i] == (listNum[0] == kNMEListNumUL ? '*' : listNum[0] > 0 ? '#' : 'x')
02453                                 && (nesting == 1 || *i + 1 >= srcLen || src[*i] != src[*i + 1]
02454                                     || (src[*i] == '*'
02455                                         ? options & kNMEProcessOptNoBold || listNum[1] == kNMEListNumUL
02456                                         : src[*i] == '#'
02457                                         ? options & kNMEProcessOptNoMonospace || listNum[1] > 0
02458                                         : TRUE))))
02459                     {
02460                         // no list mismatch at top or second level
02461                         if (*i + 1 >= srcLen    // eof after one star or sharp
02462                                 || src[*i + 1] != src[*i]   // not ** or ##
02463                                 || (nesting > 0 && *i + 2 < srcLen) // already in lists
02464                                 || options & (src[*i] == '*'
02465                                         ? kNMEProcessOptNoBold
02466                                         : kNMEProcessOptNoMonospace))
02467                             goto tokenLI;
02468                     }
02469                     
02470                     if (*i + 1 < srcLen && src[*i + 1] == src[*i])
02471                     {
02472                         *token = kNMETokenStyle;
02473                         *style = src[*i] == '*' ? kNMEStyleBold : kNMEStyleMonospace;
02474                         *i += 2;
02475                         return TRUE;
02476                     }
02477                     // plain character
02478                 default:
02479                     // plain character
02480                     break;
02481             }
02482             break;
02483         case '/':
02484             if (*i >= 2 && src[*i - 1] == ':'
02485                     && (isAlphaNum(src[*i - 2])
02486                         || src[*i - 2] == '+' || src[*i - 2] == '-' || src[*i - 2] == '.')
02487                     && *i + 2 < srcLen
02488                     && !isBlank(src[*i + 2]) && !isEol(src[*i + 2]))
02489                 break;  // plain character in URL (see RFC 1738)
02490             // continue
02491         case '_':
02492         case '^':
02493         case ',':
02494             if (verbatim
02495                     || (options & kNMEProcessOptNoItalic && src[*i] == '/')
02496                     || (options & kNMEProcessOptNoUnderline && src[*i] == '_')
02497                     || (options & kNMEProcessOptNoSubSuperscript && src[*i] == '^')
02498                     || (options & kNMEProcessOptNoSubSuperscript && src[*i] == ','))
02499                 break;  // plain character
02500             switch (state)
02501             {
02502                 case kNMEStatePar:
02503                 case kNMEStateHeading:
02504                 case kNMEStateBetweenPar:
02505                 case kNMEStateParAfterEol:
02506                     if (*i + 1 >= srcLen || src[*i + 1] != src[*i])
02507                         break;  // not double character -> plain character
02508                     *token = kNMETokenStyle;
02509                     *style = src[*i] == '/' ? kNMEStyleItalic
02510                             : src[*i] == '^' ? kNMEStyleSuperscript
02511                             : src[*i] == ',' ? kNMEStyleSubscript
02512                             : kNMEStyleUnderline;
02513                     *i += 2;
02514                     return TRUE;
02515                 default:
02516                     // plain character
02517                     break;
02518             }
02519             break;
02520         case ';':
02521             if (verbatim
02522                     || options & kNMEProcessOptNoDL
02523                     || (state != kNMEStateBetweenPar && state != kNMEStateParAfterEol))
02524                 break;  // not at beginning of line -> plain character
02525             // check that markup matches list kinds
02526             if (nesting == 0 || listNum[0] == kNMEListNumDT || listNum[0] == kNMEListNumDD)
02527                 goto tokenLI;
02528             // doesn't match, plain character
02529             break;
02530         case ':':
02531             switch (state)
02532             {
02533                 case kNMEStatePar:
02534                     if (verbatim || nesting == 0 || listNum[nesting - 1] != kNMEListNumDT)
02535                         break;  // not the end of a definition title: plain character
02536                     // in a definition title: dd
02537                     *token = kNMETokenDD;
02538                     (*i)++;
02539                     skipBlanks(src, srcLen, i);
02540                     return TRUE;
02541                 case kNMEStateParAfterEol:
02542                     if (verbatim
02543                             || (nesting > 0
02544                                 && listNum[nesting - 1] != kNMEListNumDT
02545                                 && listNum[nesting - 1] != kNMEListNumDD
02546                                 && listNum[0] != kNMEListIndented))
02547                         break;  // plain character
02548                     if (nesting > 0)
02549                     {
02550                         if (listNum[nesting - 1] == kNMEListNumDT
02551                                 || listNum[nesting - 1] == kNMEListNumDD)
02552                         {
02553                             // dd: pick a single colon
02554                             *token = kNMETokenDD;
02555                             (*i)++;
02556                             skipBlanks(src, srcLen, i);
02557                             return TRUE;
02558                         }
02559                         else if (listNum[0] != kNMEListIndented)
02560                             goto tokenLI;
02561                     }
02562                     // indented par
02563                 case kNMEStateBetweenPar:
02564                     if (options & kNMEProcessOptNoIndentedPar)
02565                         break;
02566                     // count colons
02567                     for (*itemNesting = 0;
02568                             *i < srcLen && src[*i] == ':';
02569                             (*i)++, (*itemNesting)++)
02570                         ;
02571                     // limit
02572                     if (*itemNesting > kMaxNesting)
02573                         *itemNesting = kMaxNesting;
02574                     *token = kNMETokenLI;
02575                     return TRUE;
02576                 default:
02577                     // plain character
02578                     break;
02579             }
02580             break;
02581         case '|':
02582             if (verbatim
02583                     || options & kNMEProcessOptNoTable)
02584                 break;  // plain character
02585             switch (state)
02586             {
02587                 case kNMEStatePar:
02588                     if (nesting == 0
02589                             || (listNum[nesting - 1] != kNMEListNumTableCell
02590                                 && listNum[nesting - 1] != kNMEListNumTableHCell))
02591                         break;  // not in a table -> plain character
02592                     // continue
02593                 case kNMEStateParAfterEol:
02594                 case kNMEStateBetweenPar:
02595                     if (*i + 1 < srcLen && src[*i + 1] == '=')
02596                     {
02597                         *token = kNMETokenTableHCell;
02598                         *i += 2;
02599                     }
02600                     else
02601                     {
02602                         *token = kNMETokenTableCell;
02603                         (*i)++;
02604                     }
02605                     skipBlanks(src, srcLen, i);
02606                     if (state == kNMEStatePar)
02607                     {
02608                         if (*i >= srcLen)
02609                             return FALSE;   // nothing more on line: ignore
02610                         else if (isEol(src[*i]))
02611                         {
02612                             // nothing more on line: ignore
02613                             (*i)++;
02614                             if (src[*i - 1] == '\r' && *i < srcLen && src[*i] == '\n')
02615                                 (*i)++; // skip CRLF
02616                             *token = kNMETokenEOL;
02617                             return TRUE;
02618                         }
02619                     }
02620                     return TRUE;
02621                 default:
02622                     // plain character
02623                     break;
02624             }
02625             break;
02626         case '}':
02627             if (!(options & kNMEProcessOptNoImage)
02628                     && !verbatim && *i + 1 < srcLen && src[*i + 1] == '}'
02629                     && findStyleInStyleStack(styleStack, styleNesting,
02630                             kNMEStyleImage, NULL)
02631                     && state != kNMEStatePre && state != kNMEStatePreAfterEol)
02632             {
02633                 *token = kNMETokenImageEnd;
02634                 *i += 2;
02635                 return TRUE;
02636             }
02637             // continue
02638         case '{':
02639             // matches "{{{" or "}}}", or "{{" for images
02640             if (*i + 2 >= srcLen || src[*i + 1] != src[*i]
02641                     || src[*i + 2] != src[*i])
02642             {
02643                 if (!(options & kNMEProcessOptNoImage)
02644                         && !verbatim
02645                         && *i + 1 < srcLen && src[*i] == '{' && src[*i + 1] == '{'
02646                         && !findStyleInStyleStack(styleStack, styleNesting,
02647                                 kNMEStyleImage, NULL)
02648                         && state != kNMEStatePre && state != kNMEStatePreAfterEol)
02649                 {
02650                     *token = kNMETokenImageBegin;
02651                     *i += 2;
02652                     return TRUE;
02653                 }
02654                 break;  // plain character
02655             }
02656             if (src[*i] == '}' && *i + 3 < srcLen && src[*i + 3] == '}')
02657                 break;  // '}' before "}}}" -> still inside verbatim
02658             switch (state)
02659             {
02660                 case kNMEStatePar:
02661                 case kNMEStateHeading:
02662                     if (verbatim ? src[*i] == '{' : src[*i] == '}')
02663                         break;  // not the expected one
02664                     *token = kNMETokenStyle;
02665                     *style = kNMEStyleVerbatim;
02666                     *i += 3;
02667                     return TRUE;
02668                 case kNMEStateBetweenPar:
02669                 case kNMEStateParAfterEol:
02670                     if (src[*i] == '}') // outside pre: '}' is a plain character
02671                         break;
02672                     // check if there is something else on the line
02673                     k = *i + 3;
02674                     skipBlanks(src, srcLen, &k);
02675                     if (k < srcLen && !isEol(src[k]))
02676                     {
02677                         *token = kNMETokenStyle;    // yes
02678                         *style = kNMEStyleVerbatim;
02679                     }
02680                     else
02681                         *token = kNMETokenPre;  // no
02682                     *i += 3;
02683                     return TRUE;
02684                 case kNMEStatePreAfterEol:
02685                     if (src[*i] == '}')
02686                     {
02687                         *token = kNMETokenPre;
02688                         *i += 3;
02689                         return TRUE;
02690                     }
02691                     break;
02692                 default:
02693                     // plain character
02694                     break;
02695             }
02696             break;
02697         case ']':
02698             if (!findStyleInStyleStack(styleStack, styleNesting,
02699                     kNMEStyleLink, NULL))
02700                 break;  // not parsing a link -> plain character
02701             // continue
02702         case '[':
02703             // matches "[[" or "]]"
02704             if (verbatim || options & kNMEProcessOptNoLink)
02705                 break;  // plain character
02706             switch (state)
02707             {
02708                 case kNMEStatePar:
02709                 case kNMEStateHeading:
02710                 case kNMEStateBetweenPar:
02711                 case kNMEStateParAfterEol:
02712                     if (*i + 1 >= srcLen || src[*i + 1] != src[*i])
02713                         break;  // not double char -> plain character
02714                     *token = src[*i] == '[' ? kNMETokenLinkBegin : kNMETokenLinkEnd;
02715                     *i += 2;
02716                     return TRUE;
02717                 default:
02718                     // plain character
02719                     break;
02720             }
02721             break;
02722         case '<':
02723             // matches "<<" or "<<<"
02724             if (verbatim
02725                     || options & kNMEProcessOptNoPlugin
02726                     || state == kNMEStatePre || state == kNMEStatePreAfterEol
02727                     || *i + 2 >= srcLen
02728                     || src[*i + 1] != '<')
02729                 break;  // in verbatim or not double char -> plain character
02730             if (*i + 2 < srcLen && src[*i + 2] == '<')
02731             {
02732                 *token = kNMETokenPlaceholder;
02733                 *i += 3;
02734             }
02735             else
02736             {
02737                 *token = kNMETokenPlugin;
02738                 *i += 2;
02739             }
02740             if (state == kNMEStateBetweenPar || state == kNMEStateParAfterEol)
02741             {
02742                 // check if there is something else on the line
02743                 k = *i;
02744                 skipBlanks(src, srcLen, &k);
02745                 if (k >= srcLen || isEol(src[k]))   // no -> block syntax
02746                 {
02747                     if (*token == kNMETokenPlaceholder)
02748                         *token = kNMETokenPlaceholderBlock;
02749                     else
02750                         *token = kNMETokenPluginBlock;
02751                 }
02752             }
02753             return TRUE;
02754         case '-':
02755             // matches regex "^----"
02756             if (options & kNMEProcessOptNoHRule
02757                     || (state != kNMEStateBetweenPar && state != kNMEStateParAfterEol)
02758                     || *i + 3 >= srcLen
02759                     || src[*i + 1] != '-' || src[*i + 2] != '-'
02760                     || src[*i + 3] != '-')
02761                 break;  // doesn't match -> plain character
02762             // skip all '-'
02763             while (*i < srcLen && src[*i] == '-')
02764                 (*i)++;
02765             *token = kNMETokenHR;
02766             return TRUE;
02767         case '~':
02768             // escape character, before any non-blank character
02769             if (verbatim
02770                     || options & kNMEProcessOptNoEscape
02771                     || state == kNMEStatePre || state == kNMEStatePreAfterEol
02772                     || *i + 1 >= srcLen
02773                     || isEol(src[*i + 1]) || isBlank(src[*i + 1]))
02774                 break;  // no -> plain character
02775             *i += 2;
02776             *token = kNMETokenChar;
02777             return TRUE;
02778     }
02779     
02780     *token = kNMETokenChar;
02781     (*i)++;
02782     return TRUE;
02783     
02784 tokenLI:
02785     // count *, #, ;, : which match current list
02786     for (*itemNesting = 0;
02787             *i < srcLen
02788                 && (src[*i] == '*' || src[*i] == '#' || src[*i] == ';'
02789                     || (src[*i] == ':' && *itemNesting < nesting))
02790                 && (*itemNesting >= nesting
02791                     || (listNum[*itemNesting] == kNMEListNumUL ? src[*i] == '*'
02792                         : listNum[*itemNesting] == kNMEListNumDT
02793                                 || listNum[*itemNesting] == kNMEListNumDD
02794                             ? src[*i] == ';' || src[*i] == ':'
02795                         : src[*i] == '#'));
02796             (*i)++, (*itemNesting)++)
02797         ;
02798     // limit
02799     if (*itemNesting > kMaxNesting)
02800         *itemNesting = kMaxNesting;
02801     *token = src[*i - 1] == ':' ? kNMETokenDD : kNMETokenLI;
02802     return TRUE;
02803 }
02804 
02815 static NMEErr swapBuffers(NMEText *src, NMEInt *srcLen,
02816         NMEContext *context,
02817         NMEInt *commonLen,
02818         NMEInt destLen0)
02819 {
02820     NMEInt k;
02821     NMEText tmp;
02822     
02823     // check size
02824     if (*srcLen + context->destLen - context->srcIndex > context->bufSize
02825             || destLen0 > context->bufSize)
02826         return kNMEErrNotEnoughMemory;
02827     
02828     // update line number while we still have past src
02829     updateLineNum(context);
02830     
02831     // see comment at beginning of NMEProcess
02832     for (k = 0; k < *srcLen - context->srcIndex; k++)
02833         context->dest[context->destLen + k] = (*src)[context->srcIndex + k];
02834     for (k = 0; k < destLen0 - *commonLen; k++)
02835         (*src)[*commonLen + k] = context->dest[*commonLen + k];
02836     *commonLen = destLen0;
02837     *srcLen += context->destLen - context->srcIndex;
02838     context->srcIndexOffset -= context->destLen - context->srcIndex;
02839     context->srcIndex = context->srcIndexForLineNum = context->destLen = destLen0;
02840     tmp = *src; *src = context->dest; context->dest = tmp;
02841     
02842     return kNMEErrOk;
02843 }
02844 
02845 NMEErr NMEProcess(NMEConstText nmeText, NMEInt nmeTextLen,
02846         NMEText buf, NMEInt bufSize,
02847         NMEInt options,
02848         NMEConstText eol,
02849         NMEOutputFormat const *outputFormat,
02850         NMEInt fontSize,
02851         NMEText *output,
02852         NMEInt *outputLen,
02853         NMEInt *outputUCS16Len)
02854 {
02855     /*
02856     NMEProcess uses two buffers for input and output and swaps them when
02857     it must reprocess output, be it for plugins or autoconvert.
02858     src[0..commonLen-1]: partial copy of processed output, same as dest[0..commonLen-1]
02859     src[commonLen..i0-1]: processed input, can be discarded
02860     src[i0..srcLen-1]: input still to be processed
02861     dest[0..destLen0-1]: processed output
02862     When having to consume src[i0..i-1] and replace it with dest[destLen0..destLen-1] which must
02863     be processed again, the following steps occur:
02864     - copy src[i..srcLen-1] to dest[destLen..destLen+srcLen-i-1]
02865     - copy dest[commonLen..destLen0-1] to src[commonLen..destLen0-1]
02866     - set commonLen to destLen0
02867     - set srcLen to srcLen + destLen - i
02868     - set i to destLen0
02869     - set destLen to destLen0
02870     - swap src and dest
02871     */
02872     NMEInt destLenTmp;  // temp. destLen used with plugins and autoconvert
02873     NMEInt commonLen;   // length of processed text shared in src and dest
02874     NMEInt i0;  // value of srcIndex before parsing current token
02875     NMEInt noAutoOrPluginLen;   // initial span of src protected against autoconvert and plugins
02876     NMEBoolean reparseOutput;   // TRUE if plugin's output must be parsed again
02877     NMEState state; // current state
02878     NMEToken token; // next token
02879     NMEInt headingNum[kMaxNumberedHeadingLevels];   // last heading number
02880     NMEInt headingFlags;    // bit i-1 is 1 if currently in a section at level i
02881     NMEStyle styleStack[kNMEStylesCount];   // stack of styles
02882     NMEInt styleNesting;    // number of styles in styleStack
02883     NMEStyle newStyle = (NMEStyle)0;
02884     NMEInt itemNesting = 0;
02885     NMEInt headingLevel = 0;    // current heading level (1=top-level heading)
02886     NMEInt headingLevel0 = 0;   // heading level before current token
02887     NMEBoolean firstIteration;  // TRUE during first loop iteration, used with sublistInListItem
02888     NMEContext context; // context used for expressions in output strings
02889     NMEErr err;
02890     
02891 #define HOOK(cb, l, it, e, m) \
02892     do { \
02893         if (outputFormat->cb) \
02894         { \
02895             updateLineNum(&context); \
02896             err = outputFormat->cb(l, it, e, m, \
02897                     i0 + context.srcIndexOffset, \
02898                     context.srcLineNum, \
02899                     &context, \
02900                     outputFormat->hookData); \
02901             if (err != kNMEErrOk) \
02902                 return err; \
02903         } \
02904     } while (0)
02905     
02906     // set up format
02907     if (!outputFormat)
02908         outputFormat = &NMEOutputFormatText;
02909     context.fontSize = fontSize > 0 ? fontSize : outputFormat->defFontSize;
02910     context.options = options;
02911     context.eol = eol;
02912     context.ctrlChar = outputFormat->ctrlChar;
02913     context.xref = (options & kNMEProcessOptXRef) != 0;
02914     setContext(context, 0, 0);
02915     
02916     // set up buffers
02917     if (nmeTextLen > bufSize / 2)
02918         return kNMEErrNotEnoughMemory;
02919     context.src = buf;
02920     context.srcLen = nmeTextLen;
02921     for (i0 = 0; i0 < nmeTextLen; i0++)
02922         buf[i0] = nmeText[i0];
02923     context.dest = buf + bufSize / 2;
02924     context.bufSize = bufSize / 2;
02925     
02926     // set up parser state
02927     context.outputFormat = outputFormat;
02928     context.destLen = context.col = 0;
02929     context.destLenUCS16 = 0;
02930     commonLen = noAutoOrPluginLen = 0;
02931     context.currentIndent = 0;
02932     state = kNMEStateBetweenPar;
02933     context.nesting = 0;
02934     styleNesting = 0;
02935     headingNum[0] = -1;
02936     nextHeading(&headingFlags, headingNum, 1);
02937     headingFlags = 0;
02938     context.srcIndex = 0;
02939     context.srcIndexOffset = 0;
02940     context.srcLineNum = 1;
02941     context.srcIndexForLineNum = 0;
02942     
02943     // beginning of doc
02944     if (!(options & kNMEProcessOptNoPreAndPost)
02945             && !NMEAddString(outputFormat->beginDoc, -1, context.ctrlChar, &context))
02946         return kNMEErrNotEnoughMemory;
02947     
02948     // single pass main loop
02949     while (context.srcIndex < context.srcLen)
02950     {
02951         // check enough memory for worst case
02952         if (context.destLen + kNMETokenTab >= context.bufSize)
02953             return kNMEErrNotEnoughMemory;
02954         
02955         // autoconvert
02956         if (state != kNMEStatePre && state != kNMEStatePreAfterEol
02957                 && context.srcIndex >= noAutoOrPluginLen
02958                 && !(options & kNMEProcessOptNoPlugin)
02959                 && outputFormat->autoconverts)
02960         {
02961             NMEInt k;
02962             
02963             for (k = 0; outputFormat->autoconverts[k].cb; k++)
02964             {
02965                 destLenTmp = context.destLen;
02966                 if (outputFormat->autoconverts[k].cb(context.src, context.srcLen, &context.srcIndex,
02967                         &context,
02968                         outputFormat->autoconverts[k].userData))
02969                 {
02970                     noAutoOrPluginLen = context.destLen;
02971                     CheckError(swapBuffers(&context.src, &context.srcLen,
02972                             &context,
02973                             &commonLen,
02974                             destLenTmp));
02975                     break;
02976                 }
02977             }
02978         }
02979         
02980         // parse token (sensitive to context)
02981         i0 = context.srcIndex;
02982         headingLevel0 = headingLevel;
02983         if (!parseNextToken(context.src, context.srcLen, &context.srcIndex,
02984                 state,
02985                 styleNesting > 0 && styleStack[styleNesting - 1] == kNMEStyleVerbatim,
02986                 context.nesting, context.listNum,
02987                 styleStack, styleNesting,
02988                 outputFormat,
02989                 &token,
02990                 &headingLevel,
02991                 &itemNesting,
02992                 &newStyle,
02993                 options))
02994             break;  // nothing more on line: ignore
02995         
02996         // state machine
02997         switch (state)
02998         {
02999             case kNMEStateBetweenPar:
03000                 switch (token)
03001                 {
03002                     case kNMETokenChar:
03003                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "p");
03004                         if (!NMEAddString(outputFormat->beginPar, -1,
03005                                 context.ctrlChar, &context))
03006                             return kNMEErrNotEnoughMemory;
03007                         if (outputFormat->charHookFun)
03008                             CheckError(outputFormat->charHookFun(i0 + context.srcIndexOffset,
03009                                     &context,
03010                                     outputFormat->charHookData));
03011                         if (outputFormat->encodeCharFun)
03012                         {
03013                             context.srcIndex--;
03014                             CheckError(outputFormat->encodeCharFun(context.src,
03015                                     context.srcLen, &context.srcIndex,
03016                                     &context,
03017                                     outputFormat->encodeCharData));
03018                         }
03019                         else
03020                         {
03021                             context.dest[context.destLen++] = context.src[context.srcIndex - 1];
03022                             if (isFirstUTF8Byte(context.src[context.srcIndex - 1]))
03023                                 context.destLenUCS16++;
03024                             context.col++;
03025                         }
03026                         CheckError(checkWordwrap(&context, outputFormat));
03027                         state = kNMEStatePar;
03028                         break;
03029                     case kNMETokenSpace:
03030                     case kNMETokenTab:
03031                         // ignore
03032                         break;
03033                     case kNMETokenEOL:
03034                         // ignore
03035                         break;
03036                     case kNMETokenHeading:
03037                         for (; headingLevel0 >= headingLevel; headingLevel0--)
03038                             if ((headingFlags >> headingLevel0 - 1) & 1)
03039                                 HOOK(divHookFun, headingLevel0, 0, FALSE, "=");
03040                         nextHeading(&headingFlags, headingNum, headingLevel);
03041                         context.level = headingLevel;
03042                         context.item = headingLevel <= kMaxNumberedHeadingLevels
03043                                     && options & (headingLevel == 1
03044                                             ? kNMEProcessOptH1Num : kNMEProcessOptH2Num)
03045                                 ? headingNum[headingLevel - 1]
03046                                 : 0;
03047                         HOOK(divHookFun, context.level, 0, TRUE, "=");
03048                         HOOK(parHookFun, context.level, context.item, TRUE, "=");
03049                         if (!NMEAddString(outputFormat->beginHeading, -1,
03050                                 context.ctrlChar, &context))
03051                             return kNMEErrNotEnoughMemory;
03052                         context.level = 0;
03053                         state = kNMEStateHeading;
03054                         skipBlanks(context.src, context.srcLen, &context.srcIndex);
03055                         break;
03056                     case kNMETokenLineBreak:
03057                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "p");
03058                         if (!NMEAddString(outputFormat->beginPar, -1,
03059                                     context.ctrlChar, &context)
03060                                 || !NMEAddString(outputFormat->lineBreak, -1,
03061                                         context.ctrlChar, &context))
03062                             return kNMEErrNotEnoughMemory;
03063                         state = kNMEStatePar;
03064                         break;
03065                     case kNMETokenPre:
03066                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "{{{");
03067                         if (!NMEAddString(outputFormat->beginPre, -1,
03068                                     context.ctrlChar, &context)
03069                                 /*|| !NMEAddString(outputFormat->beginPreLine, -1,
03070                                         context.ctrlChar, &context)*/   )
03071                             return kNMEErrNotEnoughMemory;
03072                         state = kNMEStatePreAfterEol;
03073                         // skip next eol
03074                         if (context.srcIndex < context.srcLen && context.src[context.srcIndex] == '\r')
03075                             context.srcIndex++;
03076                         if (context.srcIndex < context.srcLen && context.src[context.srcIndex] == '\n')
03077                             context.srcIndex++;
03078                         break;
03079                     case kNMETokenLI:
03080                         // begin list(s)
03081                         for (context.nesting = 0; context.nesting < itemNesting; context.nesting++)
03082                         {
03083                             context.listNum[context.nesting]
03084                                     = context.src[context.srcIndex - itemNesting + context.nesting] == '*'
03085                                         ? kNMEListNumUL
03086                                         : context.src[context.srcIndex - itemNesting + context.nesting] == ';'
03087                                             ? kNMEListNumDT
03088                                             : context.src[context.srcIndex - itemNesting + context.nesting] == ':'
03089                                                 ? kNMEListIndented
03090                                                 : 1;
03091                             setContext(context, context.nesting + 1, context.listNum[context.nesting]);
03092                             switch (context.listNum[context.nesting])
03093                             {
03094                                 case kNMEListNumUL:
03095                                     HOOK(divHookFun, context.level, 0, TRUE, "*");
03096                                     if (!NMEAddString(outputFormat->beginUL, -1, context.ctrlChar, &context))
03097                                         return kNMEErrNotEnoughMemory;
03098                                     if (outputFormat->sublistInListItem && context.nesting + 1 < itemNesting)
03099                                         if (!NMEAddString(outputFormat->beginULItem, -1, context.ctrlChar, &context))
03100                                             return kNMEErrNotEnoughMemory;
03101                                     break;
03102                                 case kNMEListNumDT:
03103                                 case kNMEListNumDD:
03104                                     HOOK(divHookFun, context.level, 0, TRUE, ";");
03105                                     if (!NMEAddString(outputFormat->beginDL, -1, context.ctrlChar, &context))
03106                                         return kNMEErrNotEnoughMemory;
03107                                     if (outputFormat->sublistInListItem && context.nesting + 1 < itemNesting)
03108                                         if (!NMEAddString(outputFormat->beginDD, -1, context.ctrlChar, &context))
03109                                             return kNMEErrNotEnoughMemory;
03110                                     break;
03111                                 case kNMEListIndented:
03112                                     HOOK(divHookFun, context.level, 0, TRUE, ":");
03113                                     if (!NMEAddString(outputFormat->beginIndented, -1, context.ctrlChar, &context))
03114                                         return kNMEErrNotEnoughMemory;
03115                                     // straight indenting, no nesting even if sublistInListItem
03116                                     break;
03117                                 default:    // ordered list
03118                                     HOOK(divHookFun, context.level, 0, TRUE, "#");
03119                                     if (!NMEAddString(outputFormat->beginOL, -1, context.ctrlChar, &context))
03120                                         return kNMEErrNotEnoughMemory;
03121                                     if (outputFormat->sublistInListItem && context.nesting + 1 < itemNesting)
03122                                         if (!NMEAddString(outputFormat->beginOLItem, -1, context.ctrlChar, &context))
03123                                             return kNMEErrNotEnoughMemory;
03124                                     break;
03125                             }
03126                             context.level = 0;
03127                         }
03128                         // skip spaces
03129                         skipBlanks(context.src, context.srcLen, &context.srcIndex);
03130                         // begin item
03131                         context.currentIndent = context.nesting * outputFormat->indentSpaces;
03132                         setContext(context, context.nesting, context.listNum[context.nesting - 1]);
03133                         HOOK(parHookFun, context.level, context.item, TRUE,
03134                                 context.listNum[context.nesting - 1] == kNMEListNumUL ? "*"
03135                                     : context.listNum[context.nesting - 1] == kNMEListNumDT ? ";"
03136                                     : context.listNum[context.nesting - 1] == kNMEListIndented ? ":"
03137                                     : "#");
03138                         if (!NMEAddString(context.listNum[context.nesting - 1] == kNMEListNumUL
03139                                     ? outputFormat->beginULItem
03140                                 : context.listNum[context.nesting - 1] == kNMEListNumDT
03141                                     ? outputFormat->beginDT
03142                                 : context.listNum[context.nesting - 1] == kNMEListIndented
03143                                     ? outputFormat->beginIndentedPar
03144                                     : outputFormat->beginOLItem,
03145                                 -1,
03146                                 context.ctrlChar, &context))
03147                             return kNMEErrNotEnoughMemory;
03148                         setContext(context, 0, 0);
03149                         state = kNMEStatePar;
03150                         break;
03151                     case kNMETokenTableCell:
03152                     case kNMETokenTableHCell:
03153                         // start new table
03154                         if (context.nesting > 0)
03155                             switch (context.listNum[context.nesting - 1])
03156                             {
03157                                 case kNMEListNumUL:
03158                                     if (!NMEAddString(outputFormat->beginULItem, -1, context.ctrlChar, &context))
03159                                         return kNMEErrNotEnoughMemory;
03160                                     break;
03161                                 case kNMEListNumDT:
03162                                 case kNMEListNumDD:
03163                                     if (!NMEAddString(outputFormat->beginDD, -1, context.ctrlChar, &context))
03164                                         return kNMEErrNotEnoughMemory;
03165                                     break;
03166                                 case kNMEListIndented:
03167                                     if (!NMEAddString(outputFormat->beginIndentedPar, -1, context.ctrlChar, &context))
03168                                         return kNMEErrNotEnoughMemory;
03169                                     break;
03170                                 default:    // ordered list
03171                                     if (!NMEAddString(outputFormat->beginOLItem, -1, context.ctrlChar, &context))
03172                                         return kNMEErrNotEnoughMemory;
03173                                     break;
03174                             }
03175                         context.listNum[context.nesting++]
03176                                 = token == kNMETokenTableCell
03177                                     ? kNMEListNumTableCell
03178                                     : kNMEListNumTableHCell;
03179                         context.currentIndent = context.nesting * outputFormat->indentSpaces;
03180                         state = kNMEStatePar;
03181                         context.level = context.nesting - 1;
03182                         HOOK(divHookFun, kNMEHookLevelPar, 0, TRUE, "|");
03183                         if (!NMEAddString(outputFormat->beginTable, -1,
03184                                     context.ctrlChar, &context)
03185                                 || !NMEAddString(outputFormat->beginTableRow, -1,
03186                                         context.ctrlChar, &context))
03187                             return kNMEErrNotEnoughMemory;
03188                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE,
03189                                 token == kNMETokenTableCell ? "|" : "|=");
03190                         if (!NMEAddString(token == kNMETokenTableCell
03191                                     ? outputFormat->beginTableCell
03192                                     : outputFormat->beginTableHCell,
03193                                 -1,
03194                                 context.ctrlChar, &context))
03195                             return kNMEErrNotEnoughMemory;
03196                         context.level = 0;
03197                         break;
03198                     case kNMETokenHR:
03199                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "----");
03200                         if (!NMEAddString(outputFormat->horRule, -1,
03201                                 context.ctrlChar, &context))
03202                             return kNMEErrNotEnoughMemory;
03203                         HOOK(parHookFun, kNMEHookLevelPar, 0, FALSE, "----");
03204                         break;
03205                     case kNMETokenStyle:
03206                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "p");
03207                         if (!NMEAddString(outputFormat->beginPar, -1,
03208                                     context.ctrlChar, &context))
03209                             return kNMEErrNotEnoughMemory;
03210                         CheckError(processStyleTag(styleStack, &styleNesting,
03211                                 newStyle,
03212                                 i0,
03213                                 outputFormat, &context));
03214                         state = kNMEStatePar;
03215                         break;
03216                     case kNMETokenLinkBegin:
03217                     case kNMETokenImageBegin:
03218                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "p");
03219                         if (!NMEAddString(outputFormat->beginPar, -1,
03220                                     context.ctrlChar, &context))
03221                             return kNMEErrNotEnoughMemory;
03222                         CheckError(addLinkBegin(token == kNMETokenImageBegin,
03223                                 styleStack, &styleNesting,
03224                                 i0, outputFormat, &context));
03225                         state = kNMEStatePar;
03226                         break;
03227                     case kNMETokenPlugin:
03228                     case kNMETokenPluginBlock:
03229                     case kNMETokenPlaceholder:
03230                     case kNMETokenPlaceholderBlock:
03231                         {
03232                             NMEInt pluginIndex;
03233                             
03234                             pluginIndex = findPlugin(context.src, context.srcLen, context.srcIndex,
03235                                     token == kNMETokenPlaceholder
03236                                             || token == kNMETokenPlaceholderBlock,
03237                                     outputFormat);
03238                             if (pluginIndex >= 0
03239                                     && !(outputFormat->plugins[pluginIndex].options
03240                                             & kNMEPluginOptBetweenPar))
03241                             {
03242                                 // start a new paragraph
03243                                 HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "p");
03244                                 if (!NMEAddString(outputFormat->beginPar, -1,
03245                                         context.ctrlChar, &context))
03246                                     return kNMEErrNotEnoughMemory;
03247                                 state = kNMEStatePar;
03248                             }
03249                             destLenTmp = context.destLen;
03250                             CheckError(addPlugin(token == kNMETokenPluginBlock
03251                                         || token == kNMETokenPlaceholderBlock,
03252                                     token == kNMETokenPlaceholder
03253                                         || token == kNMETokenPlaceholderBlock,
03254                                     options, outputFormat, &context,
03255                                     &reparseOutput));
03256                             if (reparseOutput)
03257                             {
03258                                 CheckError(swapBuffers(&context.src, &context.srcLen,
03259                                         &context,
03260                                         &commonLen,
03261                                         destLenTmp));
03262                                 // noAutoOrPluginLen = context.destLen;
03263                             }
03264                         }
03265                         break;
03266                     case kNMETokenDD:
03267                     case kNMETokenLinkEnd:
03268                     case kNMETokenImageEnd:
03269                         // should never occur between paragraphs
03270                         return kNMEErrInternal;
03271                 }
03272                 break;
03273             case kNMEStatePar:
03274                 switch (token)
03275                 {
03276                     case kNMETokenChar:
03277                         if (outputFormat->charHookFun)
03278                             CheckError(outputFormat->charHookFun(i0 + context.srcIndexOffset,
03279                                     &context,
03280                                     outputFormat->charHookData));
03281                         if (outputFormat->encodeCharFun)
03282                         {
03283                             context.srcIndex--;
03284                             CheckError(outputFormat->encodeCharFun(context.src, context.srcLen, &context.srcIndex,
03285                                     &context,
03286                                     outputFormat->encodeCharData));
03287                         }
03288                         else
03289                         {
03290                             context.dest[context.destLen++] = context.src[context.srcIndex - 1];
03291                             if (isFirstUTF8Byte(context.src[context.srcIndex - 1]))
03292                                 context.destLenUCS16++;
03293                             context.col++;
03294                         }
03295                         CheckError(checkWordwrap(&context, outputFormat));
03296                         break;
03297                     case kNMETokenSpace:
03298                     case kNMETokenTab:
03299                         skipBlanks(context.src, context.srcLen, &context.srcIndex);
03300                         // single space provided not last of line
03301                         if (context.srcIndex < context.srcLen && !isEol(context.src[context.srcIndex]))
03302                         {
03303                             if (!NMEAddString(outputFormat->space, -1,
03304                                         context.ctrlChar, &context))
03305                                 return kNMEErrNotEnoughMemory;
03306                             CheckError(checkWordwrap(&context, outputFormat));
03307                         }
03308                         break;
03309                     case kNMETokenLineBreak:
03310                         if (!NMEAddString(outputFormat->lineBreak, -1,
03311                                     context.ctrlChar, &context))
03312                             return kNMEErrNotEnoughMemory;
03313                         CheckError(checkWordwrap(&context, outputFormat));
03314                         break;
03315                     case kNMETokenEOL:
03316                         state = kNMEStateParAfterEol;
03317                         break;
03318                     case kNMETokenDD:
03319                         context.level = context.nesting;
03320                         CheckError(flushStyleTags(styleStack, &styleNesting,
03321                                 i0,
03322                                 outputFormat, &context));
03323                         CheckError(addEndPar(FALSE, outputFormat, &context, i0));
03324                         context.listNum[context.nesting - 1] = kNMEListNumDD;
03325                         HOOK(parHookFun, context.level, context.item, TRUE, ";:");
03326                         if (!NMEAddString(outputFormat->beginDD, -1,
03327                                 context.ctrlChar, &context))
03328                             return kNMEErrNotEnoughMemory;
03329                         CheckError(checkWordwrap(&context, outputFormat));
03330                         context.level = 0;
03331                         break;
03332                     case kNMETokenTableCell:
03333                     case kNMETokenTableHCell:
03334                         // gobble back spaces (keep tabs)
03335                         while (context.destLen > 0 && context.dest[context.destLen - 1] == ' ')
03336                         {
03337                             context.destLen--;
03338                             context.destLenUCS16--;
03339                         }
03340                         // end last cell and begin new one
03341                         context.level = context.nesting;
03342                         CheckError(flushStyleTags(styleStack, &styleNesting,
03343                                 i0,
03344                                 outputFormat, &context));
03345                         if (!NMEAddString(context.listNum[context.nesting - 1] == kNMEListNumTableCell
03346                                     ? outputFormat->endTableCell
03347                                     : outputFormat->endTableHCell,
03348                                 -1,
03349                                 context.ctrlChar, &context))
03350                             return kNMEErrNotEnoughMemory;
03351                         CheckError(checkWordwrap(&context, outputFormat));
03352                         HOOK(parHookFun, kNMEHookLevelPar, 0, FALSE,
03353                                 context.listNum[context.nesting - 1] == kNMEListNumTableCell
03354                                     ? "|" : "|=");
03355                         CheckError(checkWordwrap(&context, outputFormat));
03356                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE,
03357                                 token == kNMETokenTableCell ? "|" : "|=");
03358                         if (!NMEAddString(token == kNMETokenTableCell
03359                                     ? outputFormat->beginTableCell
03360                                     : outputFormat->beginTableHCell,
03361                                 -1,
03362                                 context.ctrlChar, &context))
03363                             return kNMEErrNotEnoughMemory;
03364                         CheckError(checkWordwrap(&context, outputFormat));
03365                         context.level = 0;
03366                         context.listNum[context.nesting - 1] = token == kNMETokenTableCell
03367                                 ? kNMEListNumTableCell
03368                                 : kNMEListNumTableHCell;
03369                         break;
03370                     case kNMETokenStyle:
03371                     case kNMETokenLinkEnd:
03372                     case kNMETokenImageEnd:
03373                         CheckError(processStyleTag(styleStack, &styleNesting,
03374                                 token == kNMETokenLinkEnd ? kNMEStyleLink
03375                                     : token == kNMETokenImageEnd ? kNMEStyleImage : newStyle,
03376                                 i0,
03377                                 outputFormat, &context));
03378                         break;
03379                     case kNMETokenLinkBegin:
03380                     case kNMETokenImageBegin:
03381                         CheckError(addLinkBegin(token == kNMETokenImageBegin,
03382                                 styleStack, &styleNesting,
03383                                 i0, outputFormat, &context));
03384                         break;
03385                     case kNMETokenPlugin:
03386                     case kNMETokenPluginBlock:
03387                     case kNMETokenPlaceholder:
03388                     case kNMETokenPlaceholderBlock:
03389                         {
03390                             NMEInt pluginIndex;
03391                             
03392                             pluginIndex = findPlugin(context.src, context.srcLen, context.srcIndex,
03393                                     token == kNMETokenPlaceholder
03394                                             || token == kNMETokenPlaceholderBlock,
03395                                     outputFormat);
03396                             if (pluginIndex >= 0
03397                                     && outputFormat->plugins[pluginIndex].options
03398                                             & kNMEPluginOptBetweenPar)
03399                             {
03400                                 // end paragraph
03401                                 CheckError(flushStyleTags(styleStack, &styleNesting,
03402                                             i0,
03403                                             outputFormat, &context));
03404                                 CheckError(addEndPar(TRUE, outputFormat, &context, i0));
03405                                 state = kNMEStateBetweenPar;
03406                             }
03407                             destLenTmp = context.destLen;
03408                             CheckError(addPlugin(token == kNMETokenPluginBlock
03409                                         || token == kNMETokenPlaceholderBlock,
03410                                     token == kNMETokenPlaceholder
03411                                         || token == kNMETokenPlaceholderBlock,
03412                                     options, outputFormat, &context,
03413                                     &reparseOutput));
03414                             if (reparseOutput)
03415                             {
03416                                 CheckError(swapBuffers(&context.src, &context.srcLen,
03417                                         &context,
03418                                         &commonLen,
03419                                         destLenTmp));
03420                                 // noAutoOrPluginLen = context.destLen;
03421                             }
03422                         }
03423                         break;
03424                     case kNMETokenHeading:
03425                     case kNMETokenLI:
03426                     case kNMETokenHR:
03427                     case kNMETokenPre:
03428                         // should never occur inside a paragraph
03429                         return kNMEErrInternal;
03430                 }
03431                 break;
03432             case kNMEStateParAfterEol:
03433                 switch (token)
03434                 {
03435                     case kNMETokenChar:
03436                         if (options & kNMEProcessOptNoMultilinePar
03437                                 || (context.nesting > 0
03438                                     && (context.listNum[context.nesting - 1] == kNMEListNumTableCell
03439                                         || context.listNum[context.nesting - 1] == kNMEListNumTableHCell)))
03440                         {
03441                             // new paragraph
03442                             CheckError(flushStyleTags(styleStack, &styleNesting,
03443                                     i0,
03444                                     outputFormat, &context));
03445                             CheckError(addEndPar(TRUE, outputFormat, &context, i0));
03446                             HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "p");
03447                             if (!NMEAddString(outputFormat->beginPar, -1,
03448                                     context.ctrlChar, &context))
03449                                 return kNMEErrNotEnoughMemory;
03450                             context.currentIndent = 0;
03451                         }
03452                         else
03453                             if (!NMEAddString(outputFormat->space, -1,
03454                                         context.ctrlChar, &context))
03455                                 return kNMEErrNotEnoughMemory;
03456                         CheckError(checkWordwrap(&context, outputFormat));
03457                         if (outputFormat->charHookFun)
03458                             CheckError(outputFormat->charHookFun(i0 + context.srcIndexOffset,
03459                                     &context,
03460                                     outputFormat->charHookData));
03461                         if (outputFormat->encodeCharFun)
03462                         {
03463                             context.srcIndex--;
03464                             CheckError(outputFormat->encodeCharFun(context.src, context.srcLen, &context.srcIndex,
03465                                     &context,
03466                                     outputFormat->encodeCharData));
03467                         }
03468                         else
03469                         {
03470                             context.dest[context.destLen++] = context.src[context.srcIndex - 1];
03471                             if (isFirstUTF8Byte(context.src[context.srcIndex - 1]))
03472                                 context.destLenUCS16++;
03473                             context.col++;
03474                         }
03475                         state = kNMEStatePar;
03476                         break;
03477                     case kNMETokenSpace:
03478                     case kNMETokenTab:
03479                         // ignore
03480                         break;
03481                     case kNMETokenEOL:
03482                         CheckError(flushStyleTags(styleStack, &styleNesting,
03483                                 i0,
03484                                 outputFormat, &context));
03485                         CheckError(addEndPar(TRUE, outputFormat, &context, i0));
03486                         state = kNMEStateBetweenPar;
03487                         context.currentIndent = context.nesting * outputFormat->indentSpaces;
03488                         break;
03489                     case kNMETokenDD:
03490                         context.level = context.nesting;
03491                         CheckError(flushStyleTags(styleStack, &styleNesting,
03492                                 i0,
03493                                 outputFormat, &context));
03494                         CheckError(addEndPar(FALSE, outputFormat, &context, i0));
03495                         // if not in innermost dl, end lists(s)
03496                         while (context.nesting > itemNesting)
03497                         {
03498                             setContext(context, context.nesting, context.listNum[context.nesting - 1]);
03499                             context.nesting--;
03500                             switch (context.listNum[context.nesting])
03501                             {
03502                                 case kNMEListNumUL:
03503                                     if (!NMEAddString(outputFormat->endUL, -1, context.ctrlChar, &context))
03504                                         return kNMEErrNotEnoughMemory;
03505                                     HOOK(divHookFun, context.level, 0, FALSE, "*");
03506                                     break;
03507                                 case kNMEListNumDT:
03508                                 case kNMEListNumDD:
03509                                     if (!NMEAddString(outputFormat->endDL, -1, context.ctrlChar, &context))
03510                                         return kNMEErrNotEnoughMemory;
03511                                     HOOK(divHookFun, context.level, 0, FALSE, ";");
03512                                     break;
03513                                 case kNMEListIndented:
03514                                     if (!NMEAddString(outputFormat->endIndented, -1, context.ctrlChar, &context))
03515                                         return kNMEErrNotEnoughMemory;
03516                                     HOOK(divHookFun, context.level, 0, FALSE, ":");
03517                                     break;
03518                                 default:
03519                                     if (!NMEAddString(outputFormat->endOL, -1, context.ctrlChar, &context))
03520                                         return kNMEErrNotEnoughMemory;
03521                                     HOOK(divHookFun, context.level, 0, FALSE, "#");
03522                                     break;
03523                             }
03524                             if (outputFormat->sublistInListItem && context.nesting > 0)
03525                             {
03526                                 setContext(context, context.nesting, context.listNum[context.nesting - 1]);
03527                                 switch (context.listNum[context.nesting - 1])
03528                                 {
03529                                     case kNMEListNumUL:
03530                                         if (!NMEAddString(outputFormat->endULItem, -1, context.ctrlChar, &context))
03531                                             return kNMEErrNotEnoughMemory;
03532                                         break;
03533                                     case kNMEListNumDT:
03534                                     case kNMEListNumDD:
03535                                         if (!NMEAddString(outputFormat->endDD, -1, context.ctrlChar, &context))
03536                                             return kNMEErrNotEnoughMemory;
03537                                         break;
03538                                     case kNMEListIndented:
03539                                         // straight indenting, no nesting
03540                                         break;
03541                                     default:    // ordered list
03542                                         if (!NMEAddString(outputFormat->endOLItem, -1, context.ctrlChar, &context))
03543                                             return kNMEErrNotEnoughMemory;
03544                                         break;
03545                                 }
03546                             }
03547                         }
03548                         setContext(context, context.nesting, 0);
03549                         if (context.listNum[context.nesting - 1] != kNMEListNumDT   // prev par wasn't DT
03550                                 && outputFormat->emptyDT)
03551                         {
03552                             if (!NMEAddString(outputFormat->emptyDT, -1,
03553                                     context.ctrlChar, &context))
03554                                 return kNMEErrNotEnoughMemory;
03555                             CheckError(checkWordwrap(&context, outputFormat));
03556                         }
03557                         // skip spaces
03558                         skipBlanks(context.src, context.srcLen, &context.srcIndex);
03559                         // begin DD
03560                         context.listNum[context.nesting - 1] = kNMEListNumDD;
03561                         HOOK(parHookFun, context.level, context.item, TRUE, ";:");
03562                         if (!NMEAddString(outputFormat->beginDD, -1, context.ctrlChar, &context))
03563                             return kNMEErrNotEnoughMemory;
03564                         CheckError(checkWordwrap(&context, outputFormat));
03565                         context.level = 0;
03566                         state = kNMEStatePar;
03567                         break;
03568                     case kNMETokenLineBreak:
03569                         if (!NMEAddString(outputFormat->lineBreak, -1,
03570                                     context.ctrlChar, &context))
03571                             return kNMEErrNotEnoughMemory;
03572                         CheckError(checkWordwrap(&context, outputFormat));
03573                         break;
03574                     case kNMETokenPre:
03575                         CheckError(flushStyleTags(styleStack, &styleNesting,
03576                                 i0,
03577                                 outputFormat, &context));
03578                         CheckError(addEndPar(TRUE, outputFormat, &context, i0));
03579                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "{{{");
03580                         if (!NMEAddString(outputFormat->beginPre, -1,
03581                                         context.ctrlChar, &context)
03582                                 || !NMEAddString(outputFormat->beginPreLine, -1,
03583                                         context.ctrlChar, &context))
03584                             return kNMEErrNotEnoughMemory;
03585                         CheckError(checkWordwrap(&context, outputFormat));
03586                         state = kNMEStatePreAfterEol;
03587                         context.currentIndent = 0;
03588                         // skip next eol
03589                         if (context.srcIndex < context.srcLen && context.src[context.srcIndex] == '\r')
03590                             context.srcIndex++;
03591                         if (context.srcIndex < context.srcLen && context.src[context.srcIndex] == '\n')
03592                             context.srcIndex++;
03593                         break;
03594                     case kNMETokenHeading:
03595                         CheckError(flushStyleTags(styleStack, &styleNesting,
03596                                 i0,
03597                                 outputFormat, &context));
03598                         CheckError(addEndPar(TRUE, outputFormat, &context, i0));
03599                         for (; headingLevel0 >= headingLevel; headingLevel0--)
03600                             if ((headingFlags >> headingLevel0 - 1) & 1)
03601                                 HOOK(divHookFun, headingLevel0, 0, FALSE, "=");
03602                         nextHeading(&headingFlags, headingNum, headingLevel);
03603                         context.level = headingLevel;
03604                         context.item = headingLevel <= kMaxNumberedHeadingLevels
03605                                     && options & (headingLevel == 1
03606                                             ? kNMEProcessOptH1Num : kNMEProcessOptH2Num)
03607                                 ? headingNum[headingLevel - 1]
03608                                 : 0;
03609                         HOOK(divHookFun, context.level, 0, TRUE, "=");
03610                         HOOK(parHookFun, context.level, context.item, TRUE, "=");
03611                         if (!NMEAddString(outputFormat->beginHeading, -1,
03612                                 context.ctrlChar, &context))
03613                             return kNMEErrNotEnoughMemory;
03614                         context.currentIndent = 0;
03615                         context.level = 0;
03616                         state = kNMEStateHeading;
03617                         skipBlanks(context.src, context.srcLen, &context.srcIndex);
03618                         break;
03619                     case kNMETokenLI:
03620                         // end last paragraph or list item
03621                         CheckError(flushStyleTags(styleStack, &styleNesting,
03622                                 i0,
03623                                 outputFormat, &context));
03624                         if (!outputFormat->sublistInListItem
03625                                 || (context.nesting > 0
03626                                     && context.listNum[context.nesting - 1] == kNMEListIndented)
03627                                 || itemNesting <= context.nesting
03628                                 || context.nesting == 0)
03629                             CheckError(addEndPar(FALSE, outputFormat, &context, i0));
03630                         // if new item less nested than current level, end lists(s)
03631                         while (context.nesting > itemNesting)
03632                         {
03633                             setContext(context, context.nesting, context.listNum[context.nesting - 1]);
03634                             context.nesting--;
03635                             switch (context.listNum[context.nesting])
03636                             {
03637                                 case kNMEListNumUL:
03638                                     if (!NMEAddString(outputFormat->endUL, -1, context.ctrlChar, &context))
03639                                         return kNMEErrNotEnoughMemory;
03640                                     HOOK(divHookFun, context.level, 0, FALSE, "*");
03641                                     break;
03642                                 case kNMEListNumDT:
03643                                 case kNMEListNumDD:
03644                                     if (!NMEAddString(outputFormat->endDL, -1, context.ctrlChar, &context))
03645                                         return kNMEErrNotEnoughMemory;
03646                                     HOOK(divHookFun, context.level, 0, FALSE, ";");
03647                                     break;
03648                                 case kNMEListIndented:
03649                                     if (!NMEAddString(outputFormat->endIndented, -1, context.ctrlChar, &context))
03650                                         return kNMEErrNotEnoughMemory;
03651                                     HOOK(divHookFun, context.level, 0, FALSE, ":");
03652                                     break;
03653                                 default:
03654                                     if (!NMEAddString(outputFormat->endOL, -1, context.ctrlChar, &context))
03655                                         return kNMEErrNotEnoughMemory;
03656                                     HOOK(divHookFun, context.level, 0, FALSE, "#");
03657                                     break;
03658                             }
03659                             if (outputFormat->sublistInListItem && context.nesting > 0)
03660                             {
03661                                 setContext(context, context.nesting, context.listNum[context.nesting - 1]);
03662                                 switch (context.listNum[context.nesting - 1])
03663                                 {
03664                                     case kNMEListNumUL:
03665                                         if (!NMEAddString(outputFormat->endULItem, -1, context.ctrlChar, &context))
03666                                             return kNMEErrNotEnoughMemory;
03667                                         break;
03668                                     case kNMEListNumDT:
03669                                     case kNMEListNumDD:
03670                                         if (!NMEAddString(outputFormat->endDD, -1, context.ctrlChar, &context))
03671                                             return kNMEErrNotEnoughMemory;
03672                                         break;
03673                                     case kNMEListIndented:
03674                                         // straight indenting, no nesting
03675                                         break;
03676                                     default:    // ordered list
03677                                         if (!NMEAddString(outputFormat->endOLItem, -1, context.ctrlChar, &context))
03678                                             return kNMEErrNotEnoughMemory;
03679                                         break;
03680                                 }
03681                             }
03682                         }
03683                         setContext(context, 0, 0);
03684                         // if new item more nested than current level, begin list(s)
03685                         for (firstIteration = TRUE;
03686                                 context.nesting < itemNesting;
03687                                 context.nesting++, firstIteration = FALSE)
03688                         {
03689                             context.listNum[context.nesting]
03690                                     = context.src[context.srcIndex - itemNesting + context.nesting] == '*'
03691                                         ? kNMEListNumUL
03692                                         : context.src[context.srcIndex - itemNesting + context.nesting] == ';'
03693                                             ? kNMEListNumDT
03694                                             : context.src[context.srcIndex - itemNesting + context.nesting] == ':'
03695                                                 ? kNMEListIndented
03696                                                 : 1;
03697                             context.level = context.nesting + 1;
03698                             HOOK(divHookFun, context.level, 0, TRUE,
03699                                     context.listNum[context.nesting] == kNMEListNumUL ? "*"
03700                                     : context.listNum[context.nesting] == kNMEListNumDT ? ";"
03701                                     : context.listNum[context.nesting] == kNMEListIndented ? ":"
03702                                     : "#");
03703                             if (outputFormat->sublistInListItem && context.nesting > 0 && !firstIteration)
03704                             {
03705                                 setContext(context, context.nesting, context.listNum[context.nesting - 1]);
03706                                 switch (context.listNum[context.nesting - 1])
03707                                 {
03708                                     case kNMEListNumUL:
03709                                         if (!NMEAddString(outputFormat->beginULItem, -1, context.ctrlChar, &context))
03710                                             return kNMEErrNotEnoughMemory;
03711                                         break;
03712                                     case kNMEListNumDT:
03713                                         context.listNum[context.nesting - 1] = kNMEListNumDD;
03714                                         // fall through
03715                                     case kNMEListNumDD:
03716                                         if (!NMEAddString(outputFormat->beginDD, -1, context.ctrlChar, &context))
03717                                             return kNMEErrNotEnoughMemory;
03718                                         break;
03719                                     case kNMEListIndented:
03720                                         // straight indenting, no nesting
03721                                         break;
03722                                     default:    // ordered list
03723                                         if (!NMEAddString(outputFormat->beginOLItem, -1, context.ctrlChar, &context))
03724                                             return kNMEErrNotEnoughMemory;
03725                                         break;
03726                                 }
03727                                 context.level = context.nesting + 1;
03728                             }
03729                             if (outputFormat->sublistInListItem
03730                                     && context.listNum[context.nesting - 1] == kNMEListNumDT)
03731                             {
03732                                 // sublist must go in DD, not in DT
03733                                 context.level--;
03734                                 if (!NMEAddString(outputFormat->endDT, -1, context.ctrlChar, &context)
03735                                         || !NMEAddString(outputFormat->beginDD, -1, context.ctrlChar, &context))
03736                                     return kNMEErrNotEnoughMemory;
03737                                 context.level++;
03738                                 context.listNum[context.nesting - 1] = kNMEListNumDD;
03739                             }
03740                             if (!NMEAddString(context.listNum[context.nesting] == kNMEListNumUL
03741                                         ? outputFormat->beginUL
03742                                     : context.listNum[context.nesting] == kNMEListNumDT
03743                                         ? outputFormat->beginDL
03744                                     : context.listNum[context.nesting] == kNMEListIndented
03745                                         ? outputFormat->beginIndented
03746                                         : outputFormat->beginOL,
03747                                     -1,
03748                                     context.ctrlChar, &context))
03749                                 return kNMEErrNotEnoughMemory;
03750                         }
03751                         context.currentIndent = context.nesting * outputFormat->indentSpaces;
03752                         // skip spaces
03753                         skipBlanks(context.src, context.srcLen, &context.srcIndex);
03754                         // replace DD with DT
03755                         if (context.listNum[context.nesting - 1] == kNMEListNumDD)
03756                             context.listNum[context.nesting - 1] = kNMEListNumDT;
03757                         // begin item
03758                         setContext(context, context.nesting, context.listNum[context.nesting - 1]);
03759                         HOOK(parHookFun, context.level, context.item, TRUE,
03760                                 context.listNum[context.nesting - 1] == kNMEListNumUL ? "*"
03761                                 : context.listNum[context.nesting - 1] == kNMEListNumDT ? ";"
03762                                 : context.listNum[context.nesting - 1] == kNMEListIndented ? ":"
03763                                 : "#");
03764                         if (!NMEAddString(context.listNum[context.nesting - 1] == kNMEListNumUL
03765                                     ? outputFormat->beginULItem
03766                                 : context.listNum[context.nesting - 1] == kNMEListNumDT
03767                                     ? outputFormat->beginDT
03768                                 : context.listNum[context.nesting - 1] == kNMEListIndented
03769                                     ? outputFormat->beginIndentedPar
03770                                     : outputFormat->beginOLItem,
03771                                 -1,
03772                                 context.ctrlChar, &context))
03773                             return kNMEErrNotEnoughMemory;
03774                         setContext(context, 0, 0);
03775                         state = kNMEStatePar;
03776                         break;
03777                     case kNMETokenTableCell:
03778                     case kNMETokenTableHCell:
03779                         // end last paragraph or list item
03780                         CheckError(flushStyleTags(styleStack, &styleNesting,
03781                                 i0,
03782                                 outputFormat, &context));
03783                         if (context.nesting == 0
03784                                 || (context.listNum[context.nesting - 1] != kNMEListNumTableCell
03785                                     && context.listNum[context.nesting - 1] != kNMEListNumTableHCell))
03786                         {
03787                             // not in a table: force end of previous list if any
03788                             CheckError(addEndPar(TRUE, outputFormat, &context, i0));
03789                             // start new table
03790                             if (context.nesting > 0)
03791                                 switch (context.listNum[context.nesting - 1])
03792                                 {
03793                                     case kNMEListNumUL:
03794                                         if (!NMEAddString(outputFormat->beginULItem, -1, context.ctrlChar, &context))
03795                                             return kNMEErrNotEnoughMemory;
03796                                         break;
03797                                     case kNMEListNumDT:
03798                                     case kNMEListNumDD:
03799                                         if (!NMEAddString(outputFormat->beginDD, -1, context.ctrlChar, &context))
03800                                             return kNMEErrNotEnoughMemory;
03801                                         break;
03802                                     case kNMEListIndented:
03803                                         if (!NMEAddString(outputFormat->beginIndentedPar, -1, context.ctrlChar, &context))
03804                                             return kNMEErrNotEnoughMemory;
03805                                         break;
03806                                     default:    // ordered list
03807                                         if (!NMEAddString(outputFormat->beginOLItem, -1, context.ctrlChar, &context))
03808                                             return kNMEErrNotEnoughMemory;
03809                                         break;
03810                                 }
03811                             context.nesting++;  // context.listNum set below
03812                             context.level = context.nesting - 1;
03813                             HOOK(divHookFun, kNMEHookLevelPar, 0, TRUE, "|");
03814                             if (!NMEAddString(outputFormat->beginTable, -1,
03815                                     context.ctrlChar, &context))
03816                                 return kNMEErrNotEnoughMemory;
03817                         }
03818                         else
03819                             CheckError(addEndPar(FALSE, outputFormat, &context, i0));   // alrdy in table
03820                         context.currentIndent = context.nesting * outputFormat->indentSpaces;
03821                         // set context.listNum type
03822                         context.listNum[context.nesting - 1] = token == kNMETokenTableCell
03823                                 ? kNMEListNumTableCell
03824                                 : kNMEListNumTableHCell;
03825                         // new row
03826                         context.level = context.nesting - 1;
03827                         if (!NMEAddString(outputFormat->beginTableRow, -1,
03828                                 context.ctrlChar, &context))
03829                             return kNMEErrNotEnoughMemory;
03830                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE,
03831                                 token == kNMETokenTableCell ? "|" : "|=");
03832                         if (!NMEAddString(token == kNMETokenTableCell
03833                                         ? outputFormat->beginTableCell
03834                                         : outputFormat->beginTableHCell,
03835                                     -1,
03836                                     context.ctrlChar, &context))
03837                             return kNMEErrNotEnoughMemory;
03838                         CheckError(checkWordwrap(&context, outputFormat));
03839                         context.level = 0;
03840                         state = kNMEStatePar;
03841                         break;
03842                     case kNMETokenHR:
03843                         CheckError(flushStyleTags(styleStack, &styleNesting,
03844                                 i0,
03845                                 outputFormat, &context));
03846                         CheckError(addEndPar(TRUE, outputFormat, &context, i0));
03847                         HOOK(parHookFun, kNMEHookLevelPar, 0, TRUE, "----");
03848                         if (!NMEAddString(outputFormat->horRule, -1,
03849                                     context.ctrlChar, &context))
03850                             return kNMEErrNotEnoughMemory;
03851                         CheckError(checkWordwrap(&context, outputFormat));
03852                         HOOK(parHookFun, kNMEHookLevelPar, 0, FALSE, "----");
03853                         context.currentIndent = 0;
03854                         state = kNMEStateBetweenPar;
03855                         break;
03856                     case kNMETokenStyle:
03857                     case kNMETokenLinkEnd:
03858                     case kNMETokenImageEnd:
03859                         if (!NMEAddString(outputFormat->space, -1,
03860                                 context.ctrlChar, &context))
03861                             return kNMEErrNotEnoughMemory;
03862                         CheckError(checkWordwrap(&context, outputFormat));
03863                         CheckError(processStyleTag(styleStack, &styleNesting,
03864                                 token == kNMETokenLinkEnd
03865                                         ? kNMEStyleLink
03866                                 : token == kNMETokenImageEnd
03867                                         ? kNMEStyleImage
03868                                         : newStyle,
03869                                 i0,
03870                                 outputFormat, &context));
03871                         state = kNMEStatePar;
03872                         break;
03873                     case kNMETokenLinkBegin:
03874                     case kNMETokenImageBegin:
03875                         if (!NMEAddString(outputFormat->space, -1,
03876                                     context.ctrlChar, &context))
03877                             return kNMEErrNotEnoughMemory;
03878                         CheckError(addLinkBegin(token == kNMETokenImageBegin,
03879                                 styleStack, &styleNesting,
03880                                 i0, outputFormat, &context));
03881                         state = kNMEStatePar;
03882                         break;
03883                     case kNMETokenPlugin:
03884                     case kNMETokenPluginBlock:
03885                     case kNMETokenPlaceholder:
03886                     case kNMETokenPlaceholderBlock:
03887                         {
03888                             NMEInt pluginIndex;
03889                             
03890                             pluginIndex = findPlugin(context.src, context.srcLen, context.srcIndex,
03891                                     token == kNMETokenPlaceholder
03892                                             || token == kNMETokenPlaceholderBlock,
03893                                     outputFormat);
03894                             if (pluginIndex >= 0
03895                                     && outputFormat->plugins[pluginIndex].options
03896                                             & kNMEPluginOptBetweenPar)
03897                             {
03898                                 // end paragraph
03899                                 CheckError(flushStyleTags(styleStack, &styleNesting,
03900                                         i0,
03901                                         outputFormat, &context));
03902                                 CheckError(addEndPar(TRUE, outputFormat, &context, i0));
03903                                 state = kNMEStateBetweenPar;
03904                             }
03905                             else
03906                             {
03907                                 // begin line (insert space)
03908                                 if (!NMEAddString(outputFormat->space, -1,
03909                                         context.ctrlChar, &context))
03910                                     return kNMEErrNotEnoughMemory;
03911                                 state = kNMEStatePar;
03912                             }
03913                             destLenTmp = context.destLen;
03914                             CheckError(addPlugin(token == kNMETokenPluginBlock
03915                                         || token == kNMETokenPlaceholderBlock,
03916                                     token == kNMETokenPlaceholder
03917                                         || token == kNMETokenPlaceholderBlock,
03918                                     options, outputFormat, &context,
03919                                     &reparseOutput));
03920                             if (reparseOutput)
03921                             {
03922                                 CheckError(swapBuffers(&context.src, &context.srcLen,
03923                                         &context,
03924                                         &commonLen,
03925                                         destLenTmp));
03926                                 // noAutoOrPluginLen = context.destLen;
03927                             }
03928                         }
03929                         break;
03930                 }
03931                 break;
03932             case kNMEStatePreAfterEol:
03933                 if (token == kNMETokenPre)  // end of pre
03934                 {
03935                     if (!NMEAddString(outputFormat->endPre, -1,
03936                             context.ctrlChar, &context))
03937                         return kNMEErrNotEnoughMemory;
03938                     HOOK(parHookFun, kNMEHookLevelPar, 0, FALSE, "{{{");
03939                     state = kNMEStateBetweenPar;
03940                     break;
03941                 }
03942                 // beginning of pre line
03943                 if (!NMEAddString(outputFormat->beginPreLine, -1,
03944                         context.ctrlChar, &context))
03945                     return kNMEErrNotEnoughMemory;
03946                 state = kNMEStatePre;
03947                 
03948                 if (token == kNMETokenSpace)
03949                 {
03950                     // followed by spaces and three closing braces?
03951                     NMEInt k;
03952                     
03953                     for (k = context.srcIndex; k < context.srcLen && context.src[k] == ' '; k++)
03954                         ;
03955                     if (k + 3 <= context.srcLen && context.src[k] == '}'
03956                             && context.src[k + 1] == '}' && context.src[k + 2] == '}')
03957                         break;  // yes: ignore first space
03958                 }               
03959                 // continue
03960             case kNMEStatePre:
03961                 switch (token)
03962                 {
03963                     case kNMETokenChar:
03964                         if (outputFormat->encodeCharPreFun)
03965                         {
03966                             context.srcIndex--;
03967                             CheckError(outputFormat->encodeCharPreFun(context.src, context.srcLen, &context.srcIndex,
03968                                     &context,
03969                                     outputFormat->encodeCharPreData));
03970                         }
03971                         else
03972                         {
03973                             context.dest[context.destLen++] = context.src[context.srcIndex - 1];
03974                             if (isFirstUTF8Byte(context.src[context.srcIndex - 1]))
03975                                 context.destLenUCS16++;
03976                             context.col++;
03977                         }
03978                         break;
03979                     case kNMETokenSpace:
03980                         if (outputFormat->encodeCharPreFun)
03981                         {
03982                             NMEInt tmp = 0;
03983                             
03984                             CheckError(outputFormat->encodeCharPreFun(" ", 1, &tmp,
03985                                     &context,
03986                                     outputFormat->encodeCharPreData));
03987                         }
03988                         else
03989                         {
03990                             context.dest[context.destLen++] = ' ';
03991                             context.destLenUCS16++;
03992                             context.col++;
03993                         }
03994                         break;
03995                     case kNMETokenTab:
03996                         do
03997                         {
03998                             if (outputFormat->encodeCharPreFun)
03999                             {
04000                                 NMEInt tmp = 0;
04001                                 
04002                                 CheckError(outputFormat->encodeCharPreFun(" ", 1, &tmp,
04003                                         &context,
04004                                         outputFormat->encodeCharPreData));
04005                             }
04006                             else
04007                             {
04008                                 context.dest[context.destLen++] = ' ';
04009                                 context.destLenUCS16++;
04010                                 context.col++;
04011                             }
04012                         } while (context.col % kTabWidth != 0);
04013                         break;
04014                     case kNMETokenEOL:
04015                         if (!NMEAddString(outputFormat->endPreLine, -1,
04016                                 context.ctrlChar, &context))
04017                             return kNMEErrNotEnoughMemory;
04018                         state = kNMEStatePreAfterEol;
04019                         break;
04020                     default:
04021                         // should never occur in preformatted blocks
04022                         return kNMEErrInternal;
04023                 }
04024                 break;
04025             case kNMEStateHeading:
04026                 switch (token)
04027                 {
04028                     case kNMETokenChar:
04029                         if (outputFormat->charHookFun)
04030                             CheckError(outputFormat->charHookFun(i0 + context.srcIndexOffset,
04031                                     &context,
04032                                     outputFormat->charHookData));
04033                         if (outputFormat->encodeCharFun)
04034                         {
04035                             context.srcIndex--;
04036                             CheckError(outputFormat->encodeCharFun(context.src, context.srcLen, &context.srcIndex,
04037                                     &context,
04038                                     outputFormat->encodeCharData));
04039                         }
04040                         else
04041                         {
04042                             context.dest[context.destLen++] = context.src[context.srcIndex - 1];
04043                             if (isFirstUTF8Byte(context.src[context.srcIndex - 1]))
04044                                 context.destLenUCS16++;
04045                             context.col++;
04046                         }
04047                         CheckError(checkWordwrap(&context, outputFormat));
04048                         break;
04049                     case kNMETokenSpace:
04050                     case kNMETokenTab:
04051                         if (state == kNMEStatePar)  // pack multiple spaces for par
04052                             skipBlanks(context.src, context.srcLen, &context.srcIndex);
04053                         // single space provided not last of line
04054                         if (context.srcIndex < context.srcLen && !isEol(context.src[context.srcIndex]))
04055                         {
04056                             if (!NMEAddString(outputFormat->space, -1,
04057                                     context.ctrlChar, &context))
04058                                 return kNMEErrNotEnoughMemory;
04059                             CheckError(checkWordwrap(&context, outputFormat));
04060                         }
04061                         break;
04062                     case kNMETokenHeading:
04063                         // end of heading
04064                         skipBlanks(context.src, context.srcLen, &context.srcIndex);
04065                     case kNMETokenEOL:
04066                         context.level = headingLevel;
04067                         CheckError(flushStyleTags(styleStack, &styleNesting,
04068                                 i0,
04069                                 outputFormat, &context));
04070                         if (!NMEAddString(outputFormat->endHeading, -1,
04071                                     context.ctrlChar, &context))
04072                             return kNMEErrNotEnoughMemory;
04073                         CheckError(checkWordwrap(&context, outputFormat));
04074                         HOOK(parHookFun, context.level, 0, FALSE, "=");
04075                         context.level = 0;
04076                         state = kNMEStateBetweenPar;
04077                         break;
04078                     case kNMETokenLineBreak:
04079                         if (!NMEAddString(outputFormat->lineBreak, -1,
04080                                 context.ctrlChar, &context))
04081                             return kNMEErrNotEnoughMemory;
04082                         CheckError(checkWordwrap(&context, outputFormat));
04083                         break;
04084                     case kNMETokenStyle:
04085                     case kNMETokenLinkEnd:
04086                     case kNMETokenImageEnd:
04087                         CheckError(processStyleTag(styleStack, &styleNesting,
04088                                 token == kNMETokenLinkEnd
04089                                     ? kNMEStyleLink
04090                                     : token == kNMETokenImageEnd
04091                                         ? kNMEStyleImage : newStyle,
04092                                 i0,
04093                                 outputFormat, &context));
04094                         break;
04095                     case kNMETokenLinkBegin:
04096                     case kNMETokenImageBegin:
04097                         CheckError(addLinkBegin(token == kNMETokenImageBegin,
04098                                 styleStack, &styleNesting,
04099                                 i0, outputFormat, &context));
04100                         break;
04101                     case kNMETokenPlugin:
04102                     case kNMETokenPluginBlock:
04103                     case kNMETokenPlaceholder:
04104                     case kNMETokenPlaceholderBlock:
04105                         destLenTmp = context.destLen;
04106                         CheckError(addPlugin(token == kNMETokenPluginBlock
04107                                         || token == kNMETokenPlaceholderBlock,
04108                                     token == kNMETokenPlaceholder
04109                                         || token == kNMETokenPlaceholderBlock,
04110                                 options, outputFormat, &context,
04111                                 &reparseOutput));
04112                         if (reparseOutput)
04113                         {
04114                             CheckError(swapBuffers(&context.src, &context.srcLen,
04115                                     &context,
04116                                     &commonLen,
04117                                     destLenTmp));
04118                             // noAutoOrPluginLen = context.destLen;
04119                         }
04120                         break;
04121                     case kNMETokenLI:
04122                     case kNMETokenDD:
04123                     case kNMETokenTableCell:
04124                     case kNMETokenTableHCell:
04125                     case kNMETokenHR:
04126                     case kNMETokenPre:
04127                         // should never occur in a heading
04128                         return kNMEErrInternal;
04129                 }
04130                 break;
04131         }
04132     }
04133     
04134     // end: flush pending constructs
04135     switch (state)
04136     {
04137         case kNMEStatePar:
04138         case kNMEStateParAfterEol:
04139             CheckError(flushStyleTags(styleStack, &styleNesting,
04140                     context.srcIndex,
04141                     outputFormat, &context));
04142             CheckError(addEndPar(TRUE, outputFormat, &context, context.srcIndex));
04143             break;
04144         case kNMEStatePre:
04145             if (!NMEAddString(outputFormat->endPreLine, -1,
04146                         context.ctrlChar, &context)
04147                     || !NMEAddString(outputFormat->endPre, -1,
04148                         context.ctrlChar, &context))
04149                 return kNMEErrNotEnoughMemory;
04150             HOOK(parHookFun, kNMEHookLevelPar, 0, FALSE, "{{{");
04151             break;
04152         case kNMEStatePreAfterEol:
04153             if (!NMEAddString(outputFormat->endPre, -1,
04154                     context.ctrlChar, &context))
04155                 return kNMEErrNotEnoughMemory;
04156             HOOK(parHookFun, kNMEHookLevelPar, 0, FALSE, "{{{");
04157             break;
04158         case kNMEStateHeading:
04159             context.level = headingLevel;
04160             CheckError(flushStyleTags(styleStack, &styleNesting,
04161                     context.srcIndex,
04162                     outputFormat, &context));
04163             if (!NMEAddString(outputFormat->endHeading, -1,
04164                             context.ctrlChar, &context))
04165                 return kNMEErrNotEnoughMemory;
04166             CheckError(checkWordwrap(&context, outputFormat));
04167             HOOK(parHookFun, context.level, 0, FALSE, "=");
04168             context.level = 0;
04169             break;
04170         default:
04171             break;
04172     }
04173     
04174     // end of doc
04175     if (!(options & kNMEProcessOptNoPreAndPost)
04176             && !NMEAddString(outputFormat->endDoc, -1,
04177                     context.ctrlChar, &context))
04178         return kNMEErrNotEnoughMemory;
04179     if (context.destLen + 1 >= context.bufSize)
04180         return kNMEErrNotEnoughMemory;
04181     context.dest[context.destLen] = '\0';
04182     
04183     // set result
04184     *output = context.dest;
04185     *outputLen = context.destLen;
04186     if (outputUCS16Len)
04187         *outputUCS16Len = context.destLenUCS16;
04188     return kNMEErrOk;
04189 }
04190 
04191 void NMEGetTempMemory(NMEContext const *context,
04192         NMEText *addr,
04193         NMEInt *len)
04194 {
04195     *addr = context->src + context->srcLen;
04196     *len = context->bufSize - context->srcLen;
04197 }
04198 
04199 void NMEGetFormat(NMEContext const *context,
04200         NMEOutputFormat const **outputFormat,
04201         NMEInt *options,
04202         NMEInt *fontSize)
04203 {
04204     if (outputFormat)
04205         *outputFormat = context->outputFormat;
04206     if (options)
04207         *options = context->options;
04208     if (fontSize)
04209         *fontSize = context->fontSize;
04210 }
04211 
04212 NMEInt NMECurrentInputIndex(NMEContext const *context)
04213 {
04214     return context->srcIndex;
04215 }
04216 
04217 NMEInt NMECurrentOutputIndex(NMEContext const *context)
04218 {
04219     return context->destLen;
04220 }
04221 
04222 NMEInt NMECurrentOutputIndexUCS16(NMEContext const *context)
04223 {
04224     return context->destLenUCS16;
04225 }
04226 
04227 void NMECurrentLink(NMEContext const *context,
04228         NMEInt *linkOffset, NMEInt *linkLength)
04229 {
04230     *linkOffset = context->srcIndexOffset + context->linkOffset;
04231     *linkLength = context->linkLength;
04232 }
04233 
04234 void NMECurrentOutput(NMEContext const *context,
04235         NMEConstText *output, NMEInt *outputLength)
04236 {
04237     if (output)
04238         *output = context->dest;
04239     if (outputLength)
04240         *outputLength = context->destLen;
04241 }
04242 
04243 NMEConstText NMECurrentListNesting(NMEContext const *context)
04244 {
04245     NMEInt i;
04246     static NMEChar str[kMaxNesting + 1];
04247     
04248     for (i = 0; i < context->nesting; i++)
04249         str[i] = context->listNum[i] == kNMEListNumUL ? '*'
04250                 : context->listNum[i] == kNMEListNumDT ? ';'
04251                 : context->listNum[i] == kNMEListNumDD ? ':'
04252                 : context->listNum[i] == kNMEListIndented ? ':'
04253                 : context->listNum[i] == kNMEListNumTableCell ? '|'
04254                 : context->listNum[i] == kNMEListNumTableHCell ? '|'
04255                 : '#';
04256     str[i] = '\0';
04257     return str;
04258 }
Generated by Doxygen.
Copyright 2007-2013, Yves Piguet.
All rights reserved.