home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 3 / goldfish_volume_3.bin / files / text / edit / warpmod / source / funcs.c < prev    next >
C/C++ Source or Header  |  1995-06-15  |  39KB  |  1,346 lines

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  Modula scanner ©1995 Dietmar Eilert
  4.  
  5.  GoldED syntax parser (uses a syntax cache). Dice:
  6.  
  7.  DMAKE
  8.  
  9.  -------------------------------------------------------------------------------
  10.  
  11. */
  12.  
  13. #include "defs.h"
  14.  
  15. /// "Header stuff"
  16.  
  17. // Buffer handles are allocated for each text buffer to keep track of ressources:
  18.  
  19. struct BufferHandle {
  20.  
  21.     struct EditConfig     *bh_EditConfig;            // pointer to text data
  22.     struct GlobalConfig   *bh_GlobalConfig;          // editor configuration
  23.     struct SyntaxInfo     *bh_SyntaxInfo;            // parser output
  24.     struct RefreshRequest  bh_RefreshRequest;        // display refresh request
  25. };
  26.  
  27. // known keywords are described by syntax structures:
  28.  
  29. struct Keyword {
  30.  
  31.     UBYTE *Pattern;                                  // known pattern (e.g. "PROCEDURE")
  32.     UBYTE *Next;                                     // valid range for next non-space char  (or NULL)
  33.     UBYTE *Required;                                 // this char has to be on the same line (or NULL)
  34.     UWORD  Level;                                    // syntax level
  35. };
  36.  
  37. #define EMPTY_STACK ((struct SyntaxInfo *)~0)        // empty stack flag
  38.  
  39. // recognized syntax levels
  40.  
  41. #define SYNTAX_COMMENT    1
  42. #define SYNTAX_MODULE     2
  43. #define SYNTAX_RESERVED   3
  44. #define SYNTAX_DECLARE    4
  45. #define SYNTAX_TYPES      5
  46. #define SYNTAX_NUMBER     6
  47. #define SYNTAX_STRING     7
  48. #define SYNTAX_ASSIGN     8
  49. #define SYNTAX_OPERATOR   9
  50.  
  51. #define UPPER(a)          ((a) & 95)                 // simple uppercase conversion
  52.  
  53. // syntax infos (or EMPTY_STACK) are attached to parsed lines
  54.  
  55. struct SyntaxInfo {
  56.  
  57.     UWORD              Flags;                        // comment flags (upper 8 bits = comment depth)
  58.     struct SyntaxChunk SyntaxChunk;                  // syntax chunks
  59. };
  60.  
  61. // these flag bits describe the line's comment properties
  62.  
  63. #define COMMENT_NONE      (0)                        // no comment in this line
  64. #define COMMENT_SIMPLE    (1L<<0)                    // there is a comment in this line
  65. #define COMMENT_START     (1L<<1)                    // line is start of multi-line comment
  66. #define COMMENT_BODY      (1L<<2)                    // line is body  of multi-line comment
  67. #define COMMENT_END       (1L<<3)                    // line is end   of multi-line comment
  68.  
  69. // macros to access SyntaxInfo members
  70.  
  71. #define GET_FLAGS(node)    (((struct SyntaxInfo *)(node)->UserData)->Flags)
  72. #define GET_CHUNK(node)   (&((struct SyntaxInfo *)(node)->UserData)->SyntaxChunk)
  73.  
  74. // macro to access comment depths (stored within upper eight bits of the flags)
  75.  
  76. #define COMMENT_DEPTH(flags) ((WORD)(flags>>8))
  77.  
  78. ///
  79. /// "Globals"
  80.  
  81. struct Keyword *Hash   [256];
  82. BOOL            IsAlNum[256];
  83. BOOL            IsDigit[256];
  84.  
  85. struct Keyword Reserved[] = {
  86.  
  87.     //  known_word next_char  on_same_line level
  88.  
  89.     { "ABS",        NULL,        NULL,      SYNTAX_OPERATOR },
  90.     { "AND",        NULL,        NULL,      SYNTAX_RESERVED },
  91.     { "ARRAY",      NULL,        NULL,      SYNTAX_TYPES    },
  92.     { "ASH",        NULL,        NULL,      SYNTAX_OPERATOR },
  93.     { "ASSERT",     NULL,        NULL,      SYNTAX_OPERATOR },
  94.     { "BEGIN",      NULL,        NULL,      SYNTAX_RESERVED },
  95.     { "BITSET",     NULL,        NULL,      SYNTAX_TYPES    },
  96.     { "BOOLEAN",    NULL,        NULL,      SYNTAX_TYPES    },
  97.     { "BPOINTER",   NULL,        NULL,      SYNTAX_TYPES    },
  98.     { "BY",         NULL,        NULL,      SYNTAX_RESERVED },
  99.     { "CAP",        NULL,        NULL,      SYNTAX_OPERATOR },
  100.     { "CARDINAL",   NULL,        NULL,      SYNTAX_TYPES    },
  101.     { "CASE",       NULL,        NULL,      SYNTAX_RESERVED },
  102.     { "CHAR",       NULL,        NULL,      SYNTAX_TYPES    },
  103.     { "CHR",        NULL,        NULL,      SYNTAX_OPERATOR },
  104.     { "CLOSE",      NULL,        NULL,      SYNTAX_RESERVED },
  105.     { "CONST",      NULL,        NULL,      SYNTAX_DECLARE  },
  106.     { "COPY",       NULL,        NULL,      SYNTAX_OPERATOR },
  107.     { "CPOINTER",   NULL,        NULL,      SYNTAX_TYPES    },
  108.     { "DEC",        NULL,        NULL,      SYNTAX_OPERATOR },
  109.     { "DIV",        NULL,        NULL,      SYNTAX_OPERATOR },
  110.     { "DO",         NULL,        NULL,      SYNTAX_RESERVED },
  111.     { "ELSE",       NULL,        NULL,      SYNTAX_RESERVED },
  112.     { "ELSIF",      NULL,        NULL,      SYNTAX_RESERVED },
  113.     { "END",        NULL,        NULL,      SYNTAX_RESERVED },
  114.     { "ENTIER",     NULL,        NULL,      SYNTAX_OPERATOR },
  115.     { "EXCL",       NULL,        NULL,      SYNTAX_OPERATOR },
  116.     { "EXIT",       NULL,        NULL,      SYNTAX_RESERVED },
  117.     { "FALSE",      NULL,        NULL,      SYNTAX_RESERVED },
  118.     { "FLOAT",      NULL,        NULL,      SYNTAX_RESERVED },
  119.     { "FOR",        NULL,        NULL,      SYNTAX_RESERVED },
  120.     { "FROM",       NULL,        NULL,      SYNTAX_MODULE   },
  121.     { "HALT",       NULL,        NULL,      SYNTAX_OPERATOR },
  122.     { "HIGH",       NULL,        NULL,      SYNTAX_RESERVED },
  123.     { "IF",         NULL,        NULL,      SYNTAX_RESERVED },
  124.     { "IMPORT",     NULL,        NULL,      SYNTAX_MODULE   },
  125.     { "IN",         NULL,        NULL,      SYNTAX_RESERVED },
  126.     { "INC",        NULL,        NULL,      SYNTAX_OPERATOR },
  127.     { "INCL",       NULL,        NULL,      SYNTAX_OPERATOR },
  128.     { "INTEGER",    NULL,        NULL,      SYNTAX_TYPES    },
  129.     { "IS",         NULL,        NULL,      SYNTAX_RESERVED },
  130.     { "LEN",        NULL,        NULL,      SYNTAX_OPERATOR },
  131.     { "LIBCALL",    NULL,        NULL,      SYNTAX_RESERVED },
  132.     { "LONG",       NULL,        NULL,      SYNTAX_RESERVED },
  133.     { "LONGCARD",   NULL,        NULL,      SYNTAX_TYPES    },
  134.     { "LONGINT",    NULL,        NULL,      SYNTAX_TYPES    },
  135.     { "LONGREAL",   NULL,        NULL,      SYNTAX_TYPES    },
  136.     { "LONGSET",    NULL,        NULL,      SYNTAX_TYPES    },
  137.     { "LOOP",       NULL,        NULL,      SYNTAX_RESERVED },
  138.     { "MAX",        NULL,        NULL,      SYNTAX_OPERATOR },
  139.     { "MIN",        NULL,        NULL,      SYNTAX_OPERATOR },
  140.     { "MOD",        NULL,        NULL,      SYNTAX_OPERATOR },
  141.     { "MODULE",     NULL,        NULL,      SYNTAX_MODULE   },
  142.     { "NEW",        NULL,        NULL,      SYNTAX_OPERATOR },
  143.     { "NIL",        NULL,        NULL,      SYNTAX_RESERVED },
  144.     { "NOT",        NULL,        NULL,      SYNTAX_RESERVED },
  145.     { "ODD",        NULL,        NULL,      SYNTAX_OPERATOR },
  146.     { "OF",         NULL,        NULL,      SYNTAX_RESERVED },
  147.     { "OR",         NULL,        NULL,      SYNTAX_RESERVED },
  148.     { "ORD",        NULL,        NULL,      SYNTAX_OPERATOR },
  149.     { "POINTER",    NULL,        NULL,      SYNTAX_TYPES    },
  150.     { "PROCEDURE",  NULL,        NULL,      SYNTAX_RESERVED },
  151.     { "REAL",       NULL,        NULL,      SYNTAX_TYPES    },
  152.     { "RECORD",     NULL,        NULL,      SYNTAX_TYPES    },
  153.     { "REPEAT",     NULL,        NULL,      SYNTAX_RESERVED },
  154.     { "RETURN",     NULL,        NULL,      SYNTAX_RESERVED },
  155.     { "SET",        NULL,        NULL,      SYNTAX_RESERVED },
  156.     { "SHORT",      NULL,        NULL,      SYNTAX_RESERVED },
  157.     { "SHORTINT",   NULL,        NULL,      SYNTAX_TYPES    },
  158.     { "SIZE",       NULL,        NULL,      SYNTAX_OPERATOR },
  159.     { "STRUCT",     NULL,        NULL,      SYNTAX_RESERVED },
  160.     { "THEN",       NULL,        NULL,      SYNTAX_RESERVED },
  161.     { "TO",         NULL,        NULL,      SYNTAX_TYPES    },
  162.     { "TRUE",       NULL,        NULL,      SYNTAX_RESERVED },
  163.     { "TRUNC",      NULL,        NULL,      SYNTAX_RESERVED },
  164.     { "TYPE",       NULL,        NULL,      SYNTAX_DECLARE  },
  165.     { "UNTIL",      NULL,        NULL,      SYNTAX_RESERVED },
  166.     { "UNTRACED",   NULL,        NULL       SYNTAX_RESERVED },
  167.     { "VAL",        NULL,        NULL,      SYNTAX_RESERVED },
  168.     { "VAR",        NULL,        NULL,      SYNTAX_DECLARE  },
  169.     { "WHILE",      NULL,        NULL,      SYNTAX_RESERVED },
  170.     { "WITH",       NULL,        NULL,      SYNTAX_RESERVED },
  171.  
  172.     {  NULL, NULL, NULL, 0}
  173. };
  174.  
  175. ///
  176. /// "Prototype"
  177.  
  178. // library functions
  179.  
  180. Prototype LibCall struct ParserData     *MountScanner(void);
  181. Prototype LibCall ULONG                  StartScanner(__A0 struct GlobalConfig *, __A1 struct EditConfig *, __D0 struct SyntaxChunk *);
  182. Prototype LibCall ULONG                  CloseScanner(__D0 ULONG);
  183. Prototype LibCall void                   FlushScanner(__D0 ULONG);
  184. Prototype LibCall void                   SetupScanner(__A0 struct GlobalConfig  *);
  185. Prototype LibCall struct RefreshRequest *BriefScanner(__D0 ULONG, __A0 struct ScannerNotify *);
  186. Prototype LibCall struct SyntaxChunk    *ParseLine   (__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  187. Prototype LibCall void                   UnparseLines(__A0 struct LineNode *, __D0 ULONG);
  188. Prototype LibCall void                   ParseSection(__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  189.  
  190. // private functions
  191.  
  192. Prototype void                           PrepareHash(void);
  193. Prototype struct SyntaxChunk            *ParseString(UBYTE *, UWORD, ULONG);
  194. Prototype struct SyntaxInfo             *DupInfo(struct SyntaxInfo *);
  195. Prototype struct Keyword                *IsAReservedWord(UBYTE *, UWORD);
  196. Prototype BOOL                           strnchr(UBYTE *, UWORD, UWORD);
  197. Prototype BOOL                           CheckEnvironment(UBYTE *, UWORD, UWORD, struct Keyword *);
  198. Prototype struct LineNode               *FindContextStart(ULONG, struct LineNode *);
  199. Prototype void                           ParseContext(struct LineNode *, ULONG, ULONG);
  200. Prototype struct SyntaxInfo             *ParseContextLine(UBYTE *, UWORD, ULONG, ULONG);
  201. Prototype BOOL                           BadSuccessor(UWORD, struct LineNode *);
  202. Prototype struct RefreshRequest         *VerifyContext(ULONG, ULONG, ULONG);
  203. Prototype WORD                           CommentDepth(UBYTE *, UWORD, ULONG);
  204. Prototype BOOL                           IsNumber(UBYTE *, UWORD);
  205.  
  206. ///
  207. /// "Library functions"
  208.  
  209. /* ------------------------------- MountScanner --------------------------------
  210.  
  211.  Called by the editor before first usage of a scanner. Return a description of
  212.  our abilities.
  213.  
  214. */
  215.  
  216. LibCall struct ParserData *
  217. MountScanner()
  218. {
  219.     static UBYTE version[] = "$VER: WarpModula 1.3 (" __COMMODORE_DATE__ ")";
  220.  
  221.     static struct ParserData parserData;
  222.  
  223.     // syntax elements understood by parser
  224.  
  225.     static UBYTE *levelNames[] = {
  226.  
  227.         "Standard text",
  228.         "Comment",
  229.         "Module level",
  230.         "Reserved words",
  231.         "Header sections",
  232.         "Types",
  233.         "Numbers",
  234.         "Strings",
  235.         "Assignments",
  236.         "Operators",
  237.  
  238.         NULL
  239.     };
  240.  
  241.     // color suggestions
  242.  
  243.     static ULONG *levelColors[] = {
  244.  
  245.         MAKE_RGB4( 0,  0,   0),                      // black
  246.         MAKE_RGB4( 5,  7,   9),                      // blue
  247.         MAKE_RGB4( 8,  8,   8),                      // grey
  248.         MAKE_RGB4(15, 15,  15),                      // white
  249.         MAKE_RGB4(15, 15,   9),                      // yellow
  250.         MAKE_RGB4( 0,  0,   0),                      // black
  251.         MAKE_RGB4( 0,  0,   0),                      // black
  252.         MAKE_RGB4( 0,  0,   0),                      // black
  253.         MAKE_RGB4( 0,  0,   0),                      // black
  254.         MAKE_RGB4( 0,  0,   0),                      // black
  255.     };
  256.  
  257.     parserData.pd_Release  = SCANLIBVERSION;
  258.     parserData.pd_Version  = 1;
  259.     parserData.pd_Serial   = 0;
  260.     parserData.pd_Info     = "Warp MODULA 1.3 ©'95 D.Eilert";
  261.     parserData.pd_Levels   = 10;
  262.     parserData.pd_Names    = levelNames;
  263.     parserData.pd_Colors   = levelColors;
  264.     parserData.pd_Flags    = SCPRF_SYNTAXCACHE | SCPRF_CONTEXT;
  265.     parserData.pd_Reserved = 0;
  266.  
  267.     PrepareHash();
  268.  
  269.     return(&parserData);
  270. }
  271.  
  272.  
  273. /* ------------------------------- StartScanner --------------------------------
  274.  
  275.  Called by the editor after a new text buffer has been created. We allocate a
  276.  buffer to hold text-specific data. The buffer address is returned as handle.
  277.  
  278. */
  279.  
  280. LibCall ULONG
  281. StartScanner(__A0 struct GlobalConfig *globalConfigPtr, __A1 struct EditConfig *editConfigPtr, __D0 struct SyntaxChunk *syntaxStack)
  282. {
  283.     struct BufferHandle *handle;
  284.  
  285.     if (handle = AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
  286.  
  287.         handle->bh_GlobalConfig = globalConfigPtr;
  288.         handle->bh_EditConfig   = editConfigPtr;
  289.         handle->bh_SyntaxInfo   = (struct SyntaxInfo *)syntaxStack;
  290.     }
  291.  
  292.     return((ULONG)handle);
  293. }
  294.  
  295.  
  296. /* ------------------------------- CloseScanner --------------------------------
  297.  
  298.  Called by the editor if a text buffer is about to be closed. Deallocate buffer
  299.  specific 'global' data.
  300.  
  301. */
  302.  
  303. LibCall ULONG
  304. CloseScanner(__D0 ULONG scanID)
  305. {
  306.     if (scanID) {
  307.  
  308.         struct BufferHandle *handle = (struct BufferHandle *)scanID;
  309.  
  310.         FreeVec(handle);
  311.     }
  312.  
  313.     return(0);
  314. }
  315.  
  316.  
  317. /* ------------------------------- FlushScanner --------------------------------
  318.  
  319.  Called by the editor in low memory situations: we are supposed to free as much
  320.  memory as possible.
  321.  
  322. */
  323.  
  324. LibCall void
  325. FlushScanner(__D0 ULONG scanID)
  326. {
  327.     struct BufferHandle *handle;
  328.     struct EditConfig   *config;
  329.     struct LineNode     *lineNode;
  330.  
  331.     handle = (struct BufferHandle *)scanID;
  332.     config = handle->bh_EditConfig;
  333.  
  334.     if (lineNode = config->TextNodes)
  335.         UnparseLines(lineNode, config->Lines);
  336. }
  337.  
  338.  
  339. /* ------------------------------- SetupScanner --------------------------------
  340.  
  341.  Called by the editor if the user wants to change the scanner's configuration.
  342.  We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag
  343.  unset).
  344.  
  345. */
  346.  
  347. LibCall void
  348. SetupScanner(__A0 globalConfigPtr)
  349. {
  350.     return();
  351. }
  352.  
  353.  
  354. /* ------------------------------- BriefScanner --------------------------------
  355.  
  356.  Called to notify a context scanner if lines have been added, deleted or
  357.  modified. We are supposed to return an additional display request or NULL
  358.  (the editor will refresh the damaged region only if we return NULL). Damaged
  359.  lines have already been unparsed by the editor. This is what we have to do:
  360.  
  361.  a) lines have been deleted or modified
  362.  
  363.     Check whether the syntax scheme of the remaining lines is still valid (it
  364.     will be invalid if say a comment start has been deleted but the comment
  365.     end has not been deleted). Reparse the lines and return a refresh request
  366.     if not.
  367.  
  368.  b) lines have been inserted
  369.  
  370.     Check whether the syntax sheme of the new lines affects the existing lines.
  371.     Reparse all lines starting at the insertion point and return a refresh
  372.     request if not.
  373.  
  374.  c) lines have been (un)folded
  375.  
  376.     This scanner assumes that folding doesn't affect syntax highlighting for the
  377.     sake of simplicity. This isn't necessarily a valid assumption: we will run
  378.     into touble if the user folds parts of a comment only (e.g. the first line).
  379.  
  380. */
  381.  
  382. LibCall struct RefreshRequest *
  383. BriefScanner(__D0 ULONG scanID, __A0 struct ScannerNotify *notify)
  384. {
  385.     struct EditConfig *config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  386.  
  387.     switch (notify->sn_Class) {
  388.  
  389.         case SCANNER_NOTIFY_MODIFIED:
  390.  
  391.             ULONG line = notify->sn_Line;
  392.  
  393.             if (notify->sn_Removed > notify->sn_Lines) {
  394.  
  395.                 // lines have been deleted
  396.  
  397.                 return(VerifyContext(scanID, line, 0));
  398.             }
  399.             else if (notify->sn_Lines)
  400.  
  401.                 // lines have been modified or inserted
  402.  
  403.                 return(VerifyContext(scanID, line, notify->sn_Lines));
  404.             else
  405.                 return(NULL);
  406.  
  407.             break;
  408.  
  409.         default:
  410.  
  411.             return(NULL);
  412.     }
  413. }
  414.  
  415.  
  416. /* --------------------------------- ParseLine ---------------------------------
  417.  
  418.  Parse a line, build a syntax description
  419.  
  420. */
  421.  
  422. LibCall struct SyntaxChunk *
  423. ParseLine(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG line)
  424. {
  425.     if (lineNode->Fold)
  426.  
  427.         return(NULL);
  428.  
  429.     else if (lineNode->Len) {
  430.  
  431.         // not yet preparsed ? preparse a couple of lines
  432.  
  433.         if (lineNode->UserData == EMPTY_STACK)
  434.  
  435.             return(NULL);
  436.  
  437.         else if (lineNode->UserData)
  438.  
  439.             return(GET_CHUNK(lineNode));
  440.  
  441.         else {
  442.  
  443.             ULONG lines = ((struct BufferHandle *)scanID)->bh_EditConfig->Lines - line + 1;
  444.  
  445.             if (lines > 50)
  446.                 lines = 50;
  447.  
  448.             ParseSection(scanID, lineNode, lines);
  449.  
  450.             if (lineNode->UserData == EMPTY_STACK)
  451.                 return(NULL);
  452.             else
  453.                 return(GET_CHUNK(lineNode));
  454.         }
  455.     }
  456.     else
  457.         return(NULL);
  458. }
  459.  
  460.  
  461. /* -------------------------------- UnparseLines -------------------------------
  462.  
  463.  Called by the editor if lines are to be deleted. We are supposed to free
  464.  private data attached to the lines.
  465.  
  466. */
  467.  
  468. LibCall void
  469. UnparseLines(__A0  struct LineNode *lineNode, __D0 ULONG lines)
  470. {
  471.     while (lines--) {
  472.  
  473.         // free syntax cache
  474.  
  475.         if (lineNode->UserData) {
  476.  
  477.             if (lineNode->UserData != (APTR)EMPTY_STACK)
  478.                 FreeVec(lineNode->UserData);
  479.  
  480.             lineNode->UserData = NULL;
  481.         }
  482.  
  483.         // free folded subblock
  484.  
  485.         if (lineNode->Fold)
  486.             UnparseLines(lineNode->Fold->TextNodes, lineNode->Fold->Lines);
  487.  
  488.         ++lineNode;
  489.     }
  490. }
  491.  
  492. /* -------------------------------- ParseSection -------------------------------
  493.  
  494.  Called by the editor if lines are to be displayed. The scanner is encouraged to
  495.  preparse the lines.
  496.  
  497. */
  498.  
  499. LibCall void
  500. ParseSection(__D0 ULONG scanID, __A0  struct LineNode *lineNode, __D1 ULONG lines)
  501. {
  502.     struct LineNode *firstNode;
  503.  
  504.     if (firstNode = FindContextStart(scanID, lineNode))
  505.         ParseContext(firstNode, lines + (lineNode - firstNode), scanID);
  506. }
  507.  
  508. ///
  509. /// "private"
  510.  
  511. /* -------------------------------- PrepareHash --------------------------------
  512.  
  513.  Prepare reserved keyword hashtable (used to find keyword description if ascii
  514.  value of first letter is known).
  515.  
  516. */
  517.  
  518. void
  519. PrepareHash()
  520. {
  521.     struct Keyword *keyword;
  522.     UWORD           ascii;
  523.  
  524.     memset(Hash, 0, sizeof(Hash));
  525.  
  526.     keyword = Reserved;
  527.  
  528.     while (keyword->Pattern) {
  529.  
  530.         UWORD ascii = *keyword->Pattern;
  531.  
  532.         Hash[ascii] = keyword;
  533.  
  534.         while (keyword->Pattern && (*keyword->Pattern == ascii))
  535.             ++keyword;
  536.     }
  537.  
  538.     memset(IsAlNum, FALSE, sizeof(IsAlNum));
  539.     memset(IsDigit, FALSE, sizeof(IsDigit));
  540.  
  541.     for (ascii = 0; ascii <= 255; ++ascii)
  542.         IsAlNum[ascii] = ((ascii >= 'A') && (ascii <= 'Z')) || ((ascii >= 'a') && (ascii <= 'z')) || ((ascii >= '0') && (ascii <= '9'));
  543.  
  544.     for (ascii = '0'; ascii <= '9'; ++ascii)
  545.         IsDigit[ascii] = TRUE;
  546. }
  547.  
  548.  
  549. /* ------------------------------ IsAReservedWord ------------------------------
  550.  
  551.  Decide whether word of length <len> is a reserved keyword
  552.  
  553. */
  554.  
  555. struct Keyword *
  556. IsAReservedWord(text, len)
  557.  
  558. UBYTE *text;
  559. UWORD  len;
  560. {
  561.     struct Keyword *keyword;
  562.  
  563.     if (keyword = Hash[*text]) {
  564.  
  565.         while (keyword->Pattern) {
  566.  
  567.             if (*keyword->Pattern > *text)
  568.  
  569.                 break;
  570.  
  571.             else if (keyword->Pattern[len] || (keyword->Pattern[1] != text[1]) || memcmp(text, keyword->Pattern, len))
  572.  
  573.                 ++keyword;
  574.             else
  575.                 return(keyword);
  576.         }
  577.  
  578.         return(NULL);
  579.     }
  580.     else
  581.         return(NULL);
  582. }
  583.  
  584. /* ----------------------------- CheckEnvironment ------------------------------
  585.  
  586.  Check whether keyword environment complies with keyword.
  587.  
  588. */
  589.  
  590. BOOL
  591. CheckEnvironment(text, len, wordLen, keyword)
  592.  
  593. struct Keyword *keyword;
  594. UBYTE          *text;
  595. UWORD           len, wordLen;
  596. {
  597.     // move to first non-space character after recognized keyword
  598.  
  599.     for (text += wordLen, len -= wordLen; len && (*text == 32); ++wordLen, --len)
  600.         ++text;
  601.  
  602.     // check whether first non-space character is valid
  603.  
  604.     if ((keyword->Next == NULL) || strchr(keyword->Next, *text)) {
  605.  
  606.         // check whether required character is used on the same line
  607.  
  608.         if ((keyword->Required == NULL) || strnchr(text, len, *keyword->Required)) {
  609.  
  610.             return(TRUE);
  611.         }
  612.         else
  613.             return(FALSE);
  614.     }
  615.     else
  616.         return(FALSE);
  617. }
  618.  
  619.  
  620. /* ---------------------------------- strnchr ----------------------------------
  621.  
  622.  strchr replacement (doesn't require 0-terminated string). Return TRUE if the
  623.  character is found within <text>.
  624.  
  625. */
  626.  
  627. BOOL
  628. strnchr(text, len, character)
  629.  
  630. UBYTE *text;
  631. UWORD  character, len;
  632. {
  633.     while (len--)
  634.         if (*text++ == character)
  635.             return(TRUE);
  636.  
  637.     return(FALSE);
  638. }
  639.  
  640.  
  641. /* --------------------------------- DupInfo -----------------------------------
  642.  
  643.  Duplicate syntax info (to be FreeVec'ed)
  644.  
  645. */
  646.  
  647. struct SyntaxInfo *
  648. DupInfo(syntaxInfo)
  649.  
  650. struct SyntaxInfo *syntaxInfo;
  651. {
  652.     struct SyntaxChunk *chunk;
  653.     struct SyntaxInfo  *info;
  654.     ULONG               size;
  655.     UWORD               elements;
  656.  
  657.     // determine stack size
  658.  
  659.     for (elements = 0, chunk = &syntaxInfo->SyntaxChunk; chunk->sc_Level; ++chunk)
  660.         ++elements;
  661.  
  662.     // create copy of syntax stack (to be attached to a text line by the caller)
  663.  
  664.     size = (++elements) * sizeof(struct SyntaxChunk) + sizeof(UWORD);
  665.  
  666.     if (info = AllocVec(size + sizeof(UWORD), MEMF_PUBLIC)) {
  667.  
  668.         movmem(syntaxInfo, info, size);
  669.  
  670.         return(info);
  671.     }
  672.     else
  673.         return(NULL);
  674. }
  675.  
  676.  
  677. /* ----------------------------- FindContextStart ------------------------------
  678.  
  679.  Search backwards until a parsed line or a context free line (fold or start of
  680.  file) is found. Return line node. This node may be used as starting point for
  681.  parsing lines.
  682.  
  683. */
  684.  
  685. struct LineNode *
  686. FindContextStart(scanID, lineNode)
  687.  
  688. ULONG            scanID;
  689. struct LineNode *lineNode;
  690. {
  691.     ULONG line = lineNode - ((struct BufferHandle *)scanID)->bh_EditConfig-> TextNodes;
  692.  
  693.     while (line--) {
  694.  
  695.         // found a fold or a parsed line ?
  696.  
  697.         if (lineNode->Fold || lineNode->UserData)
  698.             break;
  699.  
  700.         --lineNode;
  701.     }
  702.  
  703.     return(lineNode);
  704. }
  705.  
  706.  
  707. /* ------------------------------- ParseContext --------------------------------
  708.  
  709.  Preparse lines. The first line is expected to be either a context free line
  710.  (start of file or a fold header) or to be a parsed line.
  711.  
  712. */
  713.  
  714. void
  715. ParseContext(lineNode, lines, scanID)
  716.  
  717. struct LineNode *lineNode;
  718. ULONG            lines, scanID;
  719. {
  720.     ULONG mode;
  721.  
  722.     for (mode = COMMENT_NONE; lines--; ++lineNode) {
  723.  
  724.         if (lineNode->Fold)
  725.  
  726.             mode = COMMENT_NONE;
  727.  
  728.         else {
  729.  
  730.             if (lineNode->UserData == EMPTY_STACK)
  731.  
  732.                 mode = COMMENT_NONE;
  733.  
  734.             else if (lineNode->UserData)
  735.  
  736.                 mode = GET_FLAGS(lineNode);
  737.  
  738.             else {
  739.  
  740.                 struct SyntaxInfo *syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, mode, scanID);
  741.  
  742.                 if (syntaxInfo == EMPTY_STACK) {
  743.  
  744.                     lineNode->UserData = EMPTY_STACK;
  745.  
  746.                     mode = COMMENT_NONE;
  747.                 }
  748.                 else if (syntaxInfo) {
  749.  
  750.                     lineNode->UserData = DupInfo(syntaxInfo);
  751.  
  752.                     mode = syntaxInfo->Flags;
  753.                 }
  754.                 else
  755.                     mode = COMMENT_NONE;
  756.             }
  757.         }
  758.     }
  759. }
  760.  
  761.  
  762. /* ----------------------------- ParseContextLine ------------------------------
  763.  
  764.  Parse a string, build a syntax description. Return EMPTY_STACK in case there is
  765.  nothing to highlight. Check whether there are comments. The status of the last
  766.  line is passed in by the caller.
  767.  
  768. */
  769.  
  770. struct SyntaxInfo *
  771. ParseContextLine(text, len, last, scanID)
  772.  
  773. UBYTE *text;
  774. UWORD  len;
  775. ULONG  last, scanID;
  776. {
  777.     if (len) {
  778.  
  779.         struct SyntaxInfo  *syntaxInfo;
  780.         struct SyntaxChunk *syntaxStack;
  781.         struct Keyword     *keyword;
  782.  
  783.         BOOL   inComment, anyText, innerComment;
  784.         UWORD  indent, startString, startComment, element, lastChar, wordLen, inString;
  785.         ULONG  status;
  786.         WORD   depth;
  787.  
  788.         syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
  789.  
  790.         // leading spaces have to be ignored
  791.  
  792.         for (indent = 0; len && (*text == 32); ++indent, --len)
  793.             ++text;
  794.  
  795.         // trailing spaces have to be ignored
  796.  
  797.          while (len && (text[len - 1] == 32))
  798.             --len;
  799.  
  800.         // preset comment status based on status of previous line
  801.  
  802.         if (last & (COMMENT_START | COMMENT_BODY)) {
  803.  
  804.             status    = COMMENT_BODY;
  805.             depth     = COMMENT_DEPTH(last);
  806.             inComment = TRUE;
  807.         }
  808.         else {
  809.  
  810.             status    = COMMENT_NONE;
  811.             inComment = FALSE;
  812.             depth     = 0;
  813.         }
  814.  
  815.         startComment = 0;
  816.         innerComment = FALSE;
  817.  
  818.         inString = FALSE;
  819.         anyText  = FALSE;
  820.         lastChar = 32;
  821.  
  822.         syntaxStack = &syntaxInfo->SyntaxChunk;
  823.  
  824.         for (element = 0; len; ++text, ++indent, --len) {
  825.  
  826.             if (*text != 32) {
  827.  
  828.                 if (inComment) {
  829.  
  830.                     // end of comment detected ?
  831.  
  832.                     if ((*text == '*') && (text[1] == ')') && (len > 1)) {
  833.  
  834.                         --depth;
  835.  
  836.                         // end of a muli-line comment or end of inner line comment ?
  837.  
  838.                         if (depth == 0) {
  839.  
  840.                             if (innerComment)
  841.  
  842.                                 status |= COMMENT_SIMPLE;
  843.  
  844.                             else {
  845.  
  846.                                 status |=  COMMENT_END;
  847.                                 status &= ~COMMENT_BODY;
  848.                             }
  849.  
  850.                             syntaxStack[element].sc_Level = SYNTAX_COMMENT;
  851.                             syntaxStack[element].sc_Start = startComment;
  852.                             syntaxStack[element].sc_End   = indent + 1;
  853.  
  854.                             ++element;
  855.  
  856.                             inComment = FALSE;
  857.                         }
  858.  
  859.                         ++text;
  860.                         ++indent;
  861.                         --len;
  862.                     }
  863.                     else if ((*text == '(') && (text[1] == '*') && (len > 1)) {
  864.  
  865.                         // nested comment detected
  866.  
  867.                         ++depth;
  868.                         ++text;
  869.                         ++indent;
  870.                         --len;
  871.                     }
  872.                 }
  873.                 else if ((*text == 34) || (*text == 39)) {
  874.  
  875.                     if (inString) {
  876.  
  877.                         if ((*text == inString) && (*(text - 1) != 92)) {
  878.  
  879.                             // end of string detected
  880.  
  881.                             inString = FALSE;
  882.  
  883.                             syntaxStack[element].sc_Level = SYNTAX_STRING;
  884.                             syntaxStack[element].sc_Start = startString;
  885.                             syntaxStack[element].sc_End   = indent - 1;
  886.  
  887.                             ++element;
  888.                         }
  889.                     }
  890.                     else {
  891.  
  892.                         inString    = *text;
  893.                         startString = indent + 1;
  894.                     }
  895.                 }
  896.                 else if (inString == FALSE) {
  897.  
  898.                     if ((*text == ':') && (text[1] == '=') && (len > 1)) {
  899.  
  900.                         // assignment detected
  901.  
  902.                         syntaxStack[element].sc_Level = SYNTAX_ASSIGN;
  903.                         syntaxStack[element].sc_Start = indent;
  904.                         syntaxStack[element].sc_End   = indent + 1;
  905.  
  906.                         ++element;
  907.  
  908.                         // move to next section (consider end-of-loop action)
  909.  
  910.                         ++text;
  911.                         ++indent;
  912.                         --len;
  913.                     }
  914.                     else if ((*text == '(') && (text[1] == '*') && (len > 1)) {
  915.  
  916.                         // start of comment detected
  917.  
  918.                         inComment    = TRUE;
  919.                         innerComment = TRUE;
  920.                         startComment = indent;
  921.  
  922.                         ++depth;
  923.                         ++text;
  924.                         ++indent;
  925.                         --len;
  926.                     }
  927.                     else if (IsAlNum[*text]) {
  928.  
  929.                         wordLen = 1;
  930.  
  931.                         while (IsAlNum[text[wordLen]] && (wordLen < len))
  932.                             ++wordLen;
  933.  
  934.                         // reserved word detected ?
  935.  
  936.                         if (Hash[*text] && ((lastChar == ' ') || (lastChar == '(') || (lastChar == ':')) && (keyword = IsAReservedWord(text, wordLen))) {
  937.  
  938.                             // environment to be checked ?
  939.  
  940.                             if (keyword->Next || keyword->Required) {
  941.  
  942.                                 if (CheckEnvironment(text, len, wordLen, keyword)) {
  943.  
  944.                                     syntaxStack[element].sc_Level = keyword->Level;
  945.                                     syntaxStack[element].sc_Start = indent;
  946.                                     syntaxStack[element].sc_End   = indent + wordLen - 1;
  947.  
  948.                                     ++element;
  949.                                 }
  950.                             }
  951.                             else {
  952.  
  953.                                 syntaxStack[element].sc_Level = keyword->Level;
  954.                                 syntaxStack[element].sc_Start = indent;
  955.                                 syntaxStack[element].sc_End   = indent + wordLen - 1;
  956.  
  957.                                 ++element;
  958.                             }
  959.                         }
  960.                         else if (IsDigit[*text] && IsNumber(text, wordLen)) {
  961.  
  962.                             syntaxStack[element].sc_Level = SYNTAX_NUMBER;
  963.                             syntaxStack[element].sc_Start = indent;
  964.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  965.  
  966.                             ++element;
  967.                         }
  968.  
  969.                         // move to next section (consider end-of-loop action)
  970.  
  971.                         --wordLen;
  972.  
  973.                         text   += wordLen;
  974.                         indent += wordLen;
  975.                         len    -= wordLen;
  976.                     }
  977.                 }
  978.  
  979.                 anyText = TRUE;
  980.             }
  981.  
  982.             lastChar = *text;
  983.         }
  984.  
  985.         // comment not closed ?
  986.  
  987.         if (inComment) {
  988.  
  989.             // continuation of multi-line comment or new (inner line) comment ?
  990.  
  991.             if (innerComment)
  992.                 status |= COMMENT_START;
  993.             else
  994.                 status |= COMMENT_BODY;
  995.  
  996.             syntaxStack[element].sc_Level = SYNTAX_COMMENT;
  997.             syntaxStack[element].sc_Start = startComment;
  998.             syntaxStack[element].sc_End   = indent - 1;
  999.  
  1000.             ++element;
  1001.         }
  1002.  
  1003.         if (element) {
  1004.  
  1005.             // terminate syntax stack
  1006.  
  1007.             syntaxStack[element].sc_Start = FALSE;
  1008.             syntaxStack[element].sc_End   = FALSE;
  1009.             syntaxStack[element].sc_Level = FALSE;
  1010.  
  1011.             syntaxInfo->Flags = status | (depth<<8);
  1012.  
  1013.             return(syntaxInfo);
  1014.         }
  1015.         else
  1016.             return(EMPTY_STACK);
  1017.     }
  1018.     else if (last) {
  1019.  
  1020.         if (last & (COMMENT_START | COMMENT_BODY)) {
  1021.  
  1022.             struct SyntaxInfo *syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
  1023.  
  1024.             syntaxInfo->SyntaxChunk.sc_Start = FALSE;
  1025.             syntaxInfo->SyntaxChunk.sc_End   = FALSE;
  1026.             syntaxInfo->SyntaxChunk.sc_Level = FALSE;
  1027.  
  1028.             syntaxInfo->Flags = COMMENT_BODY | (last & 0xff00);
  1029.  
  1030.             return(syntaxInfo);
  1031.         }
  1032.         else
  1033.             return(EMPTY_STACK);
  1034.     }
  1035.     else
  1036.         return(EMPTY_STACK);
  1037. }
  1038.  
  1039.  
  1040. /* ------------------------------ CommentDepth ---------------------------------
  1041.  
  1042.  Determine comment depth of a line
  1043.  
  1044. */
  1045.  
  1046. WORD
  1047. CommentDepth(text, len, last)
  1048.  
  1049. UBYTE *text;
  1050. UWORD  len;
  1051. ULONG  last;
  1052. {
  1053.     if (len) {
  1054.  
  1055.         BOOL inString, inComment;
  1056.         WORD depth;
  1057.  
  1058.         // preset comment status based on status of previous line
  1059.  
  1060.         if (last & (COMMENT_START | COMMENT_BODY)) {
  1061.  
  1062.             depth     = COMMENT_DEPTH(last);
  1063.             inComment = TRUE;
  1064.         }
  1065.         else {
  1066.  
  1067.             depth     = 0;
  1068.             inComment = FALSE;
  1069.         }
  1070.  
  1071.         for (inString = FALSE; len; ++text, --len) {
  1072.  
  1073.             // (,),",*
  1074.  
  1075.             if (*text <= '*') {
  1076.  
  1077.                 if (inComment) {
  1078.  
  1079.                     // end of comment detected ?
  1080.  
  1081.                     if (len > 1) {
  1082.  
  1083.                         if ((*text == '*') && (text[1] == ')')) {
  1084.  
  1085.                             --depth;
  1086.                             ++text;
  1087.                             --len;
  1088.  
  1089.                             // end of a muli-line comment or end of inner line comment ?
  1090.  
  1091.                             if (depth == 0)
  1092.                                 inComment = FALSE;
  1093.                         }
  1094.                         else if ((*text == '(') && (text[1] == '*')) {
  1095.  
  1096.                             // nested comment detected
  1097.  
  1098.                             ++depth;
  1099.                             ++text;
  1100.                             --len;
  1101.                         }
  1102.                     }
  1103.                 }
  1104.                 else if ((*text == 34) && ((inString == FALSE) || (*(text - 1) != 92)))
  1105.  
  1106.                     inString = !inString;
  1107.  
  1108.                 else if (inString == FALSE) {
  1109.  
  1110.                     if ((*text == '(') && (text[1] == '*') && (len > 1)) {
  1111.  
  1112.                         // start of comment detected
  1113.  
  1114.                         inComment = TRUE;
  1115.  
  1116.                         ++depth;
  1117.                         ++text;
  1118.                         --len;
  1119.                     }
  1120.                 }
  1121.             }
  1122.         }
  1123.  
  1124.         return(depth);
  1125.     }
  1126.     else if (last & (COMMENT_START | COMMENT_BODY))
  1127.  
  1128.         return(COMMENT_DEPTH(last));
  1129.     else
  1130.         return(0);
  1131. }
  1132.  
  1133.  
  1134. /* ------------------------------- VerifyContext -------------------------------
  1135.  
  1136.  Ensure that syntax information of a specified range of lines ("damage region")
  1137.  is valid and consistent with the existing text. Return a refresh request if
  1138.  not. The damage area is expected to have been unparsed already. This is what we
  1139.  have to do: we preparse existing lines before the damage area if belonging to
  1140.  the syntax context of the damage area (ie. all lines affecting highlighting of
  1141.  the first line in the damage area). The damage area is parsed, too. Parsed
  1142.  lines after the damage area are reparsed if highlighting is found to be
  1143.  inconsistent with the line(s) before. Reparsing continues until the end of the
  1144.  file respectively until no more inconsistencies are found.
  1145.  
  1146. */
  1147.  
  1148. struct RefreshRequest *
  1149. VerifyContext(scanID, line, lines)
  1150.  
  1151. ULONG scanID, line, lines;
  1152. {
  1153.     struct EditConfig *config;
  1154.     struct LineNode   *lineNode, *lastNode;
  1155.     struct SyntaxInfo *syntaxInfo;
  1156.  
  1157.     ULONG last, refreshStart, refresh = FALSE;
  1158.  
  1159.     config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  1160.  
  1161.     lineNode = config->TextNodes + line;
  1162.     lastNode = config->TextNodes + line + lines - 1;
  1163.  
  1164.     // preparse from context start until end of damage area
  1165.  
  1166.     ParseSection(scanID, lineNode, lines);
  1167.  
  1168.     // get syntax flags of last line in damage area
  1169.  
  1170.     if (lastNode->Fold || (lastNode->UserData == EMPTY_STACK) || (lastNode->UserData == NULL))
  1171.         last = COMMENT_NONE;
  1172.     else
  1173.         last = GET_FLAGS(lastNode);
  1174.  
  1175.     // continue parsing until no more inconsistencies are found (until context end)
  1176.  
  1177.     refreshStart = (line += lines);
  1178.  
  1179.     for (lineNode = lastNode + 1; line < config->Lines; ++line, ++lineNode, ++refresh) {
  1180.  
  1181.         if (lineNode->Fold)
  1182.  
  1183.             // folds terminate parsing (context end)
  1184.  
  1185.             break;
  1186.  
  1187.         else if (lineNode->UserData == NULL) {
  1188.  
  1189.             // line not yet parsed
  1190.  
  1191.             syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
  1192.  
  1193.             if (syntaxInfo == EMPTY_STACK)
  1194.  
  1195.                 lineNode->UserData = EMPTY_STACK;
  1196.  
  1197.             else if (syntaxInfo)
  1198.  
  1199.                 lineNode->UserData = DupInfo(syntaxInfo);
  1200.         }
  1201.         else {
  1202.  
  1203.             // check whether highlighting of this line is consistent with previous line
  1204.  
  1205.             if (BadSuccessor(last, lineNode)) {
  1206.  
  1207.                 if (lineNode->UserData != EMPTY_STACK)
  1208.                     FreeVec(lineNode->UserData);
  1209.  
  1210.                 syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
  1211.  
  1212.                 if (syntaxInfo == EMPTY_STACK)
  1213.  
  1214.                     lineNode->UserData = EMPTY_STACK;
  1215.  
  1216.                 else if (syntaxInfo)
  1217.  
  1218.                     lineNode->UserData = DupInfo(syntaxInfo);
  1219.             }
  1220.             else
  1221.                 break;
  1222.         }
  1223.  
  1224.         if (lineNode->Fold || (lineNode->UserData == EMPTY_STACK) || (lineNode->UserData == NULL))
  1225.             last = COMMENT_NONE;
  1226.         else
  1227.             last = GET_FLAGS(lineNode);
  1228.     }
  1229.  
  1230.     if (refresh) {
  1231.  
  1232.         struct RefreshRequest *refreshRequest = &((struct BufferHandle *)scanID)->bh_RefreshRequest;
  1233.  
  1234.         refreshRequest->rr_Line  = refreshStart;
  1235.         refreshRequest->rr_Lines = refresh;
  1236.  
  1237.         return(refreshRequest);
  1238.     }
  1239.     else
  1240.         return(NULL);
  1241. }
  1242.  
  1243.  
  1244. /* ------------------------------- BadSuccessor --------------------------------
  1245.  
  1246.  Return TRUE if syntax information of two adjacent lines is inconsistent (<last>
  1247.  are the comment flags of the previous line).
  1248.  
  1249. */
  1250.  
  1251. BOOL
  1252. BadSuccessor(last, node)
  1253.  
  1254. UWORD            last;
  1255. struct LineNode *node;
  1256. {
  1257.     UWORD flags = (node->UserData == EMPTY_STACK) ? COMMENT_NONE : GET_FLAGS(node);
  1258.  
  1259.     if (flags & (COMMENT_BODY | COMMENT_END)) {
  1260.  
  1261.         // comment body/end without start ?
  1262.  
  1263.         if ((last & (COMMENT_START | COMMENT_BODY)) == FALSE)
  1264.             return(TRUE);
  1265.  
  1266.         // valid comment depth value ?
  1267.  
  1268.         if (CommentDepth(node->Text, node->Len, last) != COMMENT_DEPTH(flags))
  1269.             return(TRUE);
  1270.     }
  1271.     else if (last & COMMENT_START) {
  1272.  
  1273.         // comment start without body/end ?
  1274.  
  1275.         if ((flags & (COMMENT_BODY | COMMENT_END)) == FALSE)
  1276.  
  1277.             return(TRUE);
  1278.  
  1279.         // comment inside comment ?
  1280.  
  1281.         if (flags & COMMENT_START)
  1282.  
  1283.             return(TRUE);
  1284.     }
  1285.     else if (last & COMMENT_BODY) {
  1286.  
  1287.         // body line without end ?
  1288.  
  1289.         if ((flags & (COMMENT_BODY | COMMENT_END)) == FALSE)
  1290.  
  1291.             return(TRUE);
  1292.     }
  1293.  
  1294.     return(FALSE);
  1295. }             
  1296.  
  1297.  
  1298. /* --------------------------------- IsNumber ----------------------------------
  1299.  
  1300.  Return TRUE if text is a number
  1301.  
  1302. */
  1303.  
  1304. BOOL
  1305. IsNumber(text, len)
  1306.  
  1307. UBYTE *text;
  1308. UWORD  len;
  1309. {
  1310.     UWORD last = text[len - 1];
  1311.  
  1312.     if ((last == 'X') || (last == 'H')) {
  1313.  
  1314.         // check hex number
  1315.  
  1316.         while (--len) {
  1317.  
  1318.             if ((*text > '9') || (*text < '0')) {
  1319.  
  1320.                 UWORD next = UPPER(*text);
  1321.  
  1322.                 if ((next > 'F') || (next < 'A'))
  1323.                     return(FALSE);
  1324.             }
  1325.  
  1326.             ++text;
  1327.         }
  1328.  
  1329.         return(TRUE);
  1330.     }
  1331.     else {
  1332.  
  1333.         while (len--) {
  1334.  
  1335.             if ((*text > '9') || (*text < '0'))
  1336.                 return(FALSE);
  1337.             else
  1338.                 ++text;
  1339.         }
  1340.  
  1341.         return(TRUE);
  1342.     }
  1343. }
  1344.  
  1345. ///
  1346.