1 /*******************************************************************************
2 *									       *
3 * highlight.c -- Nirvana Editor syntax highlighting (text coloring and font    *
4 *   	    	 selected by file content				       *
5 *									       *
6 * Copyright (C) 1999 Mark Edel						       *
7 *									       *
8 * This is free software; you can redistribute it and/or modify it under the    *
9 * terms of the GNU General Public License as published by the Free Software    *
10 * Foundation; either version 2 of the License, or (at your option) any later   *
11 * version. In addition, you may distribute version of this program linked to   *
12 * Motif or Open Motif. See README for details.                                 *
13 * 									       *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *
16 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License        *
17 * for more details.							       *
18 * 									       *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple     *
21 * Place, Suite 330, Boston, MA  02111-1307 USA		                       *
22 *									       *
23 * Nirvana Text Editor	    						       *
24 * June 24, 1996								       *
25 *									       *
26 * Written by Mark Edel							       *
27 *									       *
28 *******************************************************************************/
29 
30 #ifdef HAVE_CONFIG_H
31 #include "../config.h"
32 #endif
33 
34 #include "highlight.h"
35 #include "textBuf.h"
36 #include "textDisp.h"
37 #include "text.h"
38 #include "textP.h"
39 #include "nedit.h"
40 #include "regularExp.h"
41 #include "highlightData.h"
42 #include "preferences.h"
43 #include "window.h"
44 #include "../util/misc.h"
45 #include "../util/DialogF.h"
46 #include "../util/nedit_malloc.h"
47 
48 #include <stdio.h>
49 #include <limits.h>
50 #include <math.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #ifdef VMS
54 #include "../util/VMSparam.h"
55 #else
56 #ifndef __MVS__
57 #include <sys/param.h>
58 #endif
59 #endif /*VMS*/
60 #include <inttypes.h>
61 
62 #include <Xm/Xm.h>
63 #include <Xm/XmP.h>
64 #if XmVersion >= 1002
65 #include <Xm/PrimitiveP.h>
66 #endif
67 
68 #ifdef HAVE_DEBUG_H
69 #include "../debug.h"
70 #endif
71 
72 /* How much re-parsing to do when an unfinished style is encountered */
73 #define PASS_2_REPARSE_CHUNK_SIZE 1000
74 
75 /* Initial forward expansion of parsing region in incremental reparsing,
76    when style changes propagate forward beyond the original modification.
77    This distance is increased by a factor of two for each subsequent step. */
78 #define REPARSE_CHUNK_SIZE 80
79 
80 /* Meanings of style buffer characters (styles). Don't use plain 'A' or 'B';
81    it causes problems with EBCDIC coding (possibly negative offsets when
82    subtracting 'A'). */
83 #define UNFINISHED_STYLE ASCII_A
84 #define PLAIN_STYLE     (ASCII_A+1)
85 #define IS_PLAIN(style) (style == PLAIN_STYLE || style == UNFINISHED_STYLE)
86 #define IS_STYLED(style) (style != PLAIN_STYLE && style != UNFINISHED_STYLE)
87 
88 /* Compare two styles where one of the styles may not yet have been processed
89    with pass2 patterns */
90 #define EQUIVALENT_STYLE(style1, style2, firstPass2Style) (style1 == style2 || \
91     	(style1 == UNFINISHED_STYLE && \
92     	 (style2 == PLAIN_STYLE || (unsigned char)style2 >= firstPass2Style)) || \
93     	(style2 == UNFINISHED_STYLE && \
94     	 (style1 == PLAIN_STYLE || (unsigned char)style1 >= firstPass2Style)))
95 
96 /* Scanning context can be reduced (with big efficiency gains) if we
97    know that patterns can't cross line boundaries, which is implied
98    by a context requirement of 1 line and 0 characters */
99 #define CAN_CROSS_LINE_BOUNDARIES(contextRequirements) \
100     	(contextRequirements->nLines != 1 || contextRequirements->nChars != 0)
101 
102 /* "Compiled" version of pattern specification */
103 typedef struct _highlightDataRec {
104     regexp *startRE;
105     regexp *endRE;
106     regexp *errorRE;
107     regexp *subPatternRE;
108     char style;
109     int colorOnly;
110     signed char startSubexprs[NSUBEXP+1];
111     signed char endSubexprs[NSUBEXP+1];
112     int flags;
113     int nSubPatterns;
114     int nSubBranches; /* Number of top-level branches of subPatternRE */
115     int userStyleIndex;
116     struct _highlightDataRec **subPatterns;
117 } highlightDataRec;
118 
119 /* Context requirements for incremental reparsing of a pattern set */
120 typedef struct {
121     int nLines;
122     int nChars;
123 } reparseContext;
124 
125 /* Data structure attached to window to hold all syntax highlighting
126    information (for both drawing and incremental reparsing) */
127 typedef struct {
128     highlightDataRec *pass1Patterns;
129     highlightDataRec *pass2Patterns;
130     char *parentStyles;
131     reparseContext contextRequirements;
132     styleTableEntry *styleTable;
133     int nStyles;
134     textBuffer *styleBuffer;
135     patternSet *patternSetForWindow;
136 } windowHighlightData;
137 
138 static windowHighlightData *createHighlightData(WindowInfo *window,
139 	patternSet *patSet);
140 static void freeHighlightData(windowHighlightData *hd);
141 static patternSet *findPatternsForWindow(WindowInfo *window, int warn);
142 static highlightDataRec *compilePatterns(Widget dialogParent,
143     	highlightPattern *patternSrc, int nPatterns);
144 static void freePatterns(highlightDataRec *patterns);
145 static void handleUnparsedRegion(const WindowInfo* win, textBuffer* styleBuf,
146         int pos);
147 static void handleUnparsedRegionCB(const textDisp* textD, int pos,
148         const void* cbArg);
149 static void incrementalReparse(windowHighlightData *highlightData,
150     	textBuffer *buf, int pos, int nInserted, const char *delimiters);
151 static int parseBufferRange(highlightDataRec *pass1Patterns,
152     	highlightDataRec *pass2Patterns, textBuffer *buf, textBuffer *styleBuf,
153         reparseContext *contextRequirements, int beginParse, int endParse,
154         const char *delimiters);
155 static int parseString(highlightDataRec *pattern, const char **string,
156     	char **styleString, int length, char *prevChar, int anchored,
157     	const char *delimiters, const char* lookBehindTo, const char* match_till);
158 static void passTwoParseString(highlightDataRec *pattern, char *string,
159         char *styleString, int length, char *prevChar, const char *delimiters,
160         const char* lookBehindTo, const char* match_till);
161 static void fillStyleString(const char **stringPtr, char **stylePtr,
162         const char *toPtr, char style, char *prevChar);
163 static void modifyStyleBuf(textBuffer *styleBuf, char *styleString,
164     	int startPos, int endPos, int firstPass2Style);
165 static int lastModified(textBuffer *styleBuf);
166 static int max(int i1, int i2);
167 static int min(int i1, int i2);
168 static char getPrevChar(textBuffer *buf, int pos);
169 static regexp *compileREAndWarn(Widget parent, const char *re);
170 static int parentStyleOf(const char *parentStyles, int style);
171 static int isParentStyle(const char *parentStyles, int style1, int style2);
172 static int findSafeParseRestartPos(textBuffer *buf,
173     	windowHighlightData *highlightData, int *pos);
174 static int backwardOneContext(textBuffer *buf, reparseContext *context,
175     	int fromPos);
176 static int forwardOneContext(textBuffer *buf, reparseContext *context,
177     	int fromPos);
178 static void recolorSubexpr(regexp *re, int subexpr, int style,
179         const char *string, char *styleString);
180 static int indexOfNamedPattern(highlightPattern *patList, int nPats,
181     	const char *patName);
182 static int findTopLevelParentIndex(highlightPattern *patList, int nPats,
183     	int index);
184 static highlightDataRec *patternOfStyle(highlightDataRec *patterns, int style);
185 static void updateWindowHeight(WindowInfo *window, int oldFontHeight);
186 static int getFontHeight(WindowInfo *window);
187 static styleTableEntry *styleTableEntryOfCode(WindowInfo *window, int hCode);
188 
189 /*
190 ** Buffer modification callback for triggering re-parsing of modified
191 ** text and keeping the style buffer synchronized with the text buffer.
192 ** This must be attached to the the text buffer BEFORE any widget text
193 ** display callbacks, so it can get the style buffer ready to be used
194 ** by the text display routines.
195 **
196 ** Update the style buffer for changes to the text, and mark any style
197 ** changes by selecting the region in the style buffer.  This strange
198 ** protocol of informing the text display to redraw style changes by
199 ** making selections in the style buffer is used because this routine
200 ** is intended to be called BEFORE the text display callback paints the
201 ** text (to minimize redraws and, most importantly, to synchronize the
202 ** style buffer with the text buffer).  If we redraw now, the text
203 ** display hasn't yet processed the modification, redrawing later is
204 ** not only complicated, it will double-draw almost everything typed.
205 **
206 ** Note: This routine must be kept efficient.  It is called for every
207 ** character typed.
208 */
SyntaxHighlightModifyCB(int pos,int nInserted,int nDeleted,int nRestyled,const char * deletedText,void * cbArg)209 void SyntaxHighlightModifyCB(int pos, int nInserted, int nDeleted,
210     	int nRestyled, const char *deletedText, void *cbArg)
211 {
212     WindowInfo *window = (WindowInfo *)cbArg;
213     windowHighlightData
214     	    *highlightData = (windowHighlightData *)window->highlightData;
215 
216     if (highlightData == NULL)
217     	return;
218 
219     /* Restyling-only modifications (usually a primary or secondary  selection)
220        don't require any processing, but clear out the style buffer selection
221        so the widget doesn't think it has to keep redrawing the old area */
222     if (nInserted == 0 && nDeleted == 0) {
223     	BufUnselect(highlightData->styleBuffer);
224     	return;
225     }
226 
227     /* First and foremost, the style buffer must track the text buffer
228        accurately and correctly */
229     if (nInserted > 0) {
230         char *insStyle;
231         int i;
232 
233     	insStyle = (char*)NEditMalloc(nInserted + 1);
234     	for (i=0; i<nInserted; i++)
235     	    insStyle[i] = UNFINISHED_STYLE;
236     	insStyle[i] = '\0';
237     	BufReplace(highlightData->styleBuffer, pos, pos+nDeleted, insStyle);
238     	NEditFree(insStyle);
239     } else {
240     	BufRemove(highlightData->styleBuffer, pos, pos+nDeleted);
241     }
242 
243     /* Mark the changed region in the style buffer as requiring redraw.  This
244        is not necessary for getting it redrawn, it will be redrawn anyhow by
245        the text display callback, but it clears the previous selection and
246        saves the modifyStyleBuf routine from unnecessary work in tracking
247        changes that are already scheduled for redraw */
248     BufSelect(highlightData->styleBuffer, pos, pos+nInserted);
249 
250     /* Re-parse around the changed region */
251     if (highlightData->pass1Patterns)
252     	incrementalReparse(highlightData, window->buffer, pos, nInserted,
253     	    	GetWindowDelimiters(window));
254 }
255 
256 /*
257 ** Turn on syntax highlighting.  If "warn" is true, warn the user when it
258 ** can't be done, otherwise, just return.
259 */
StartHighlighting(WindowInfo * window,int warn)260 void StartHighlighting(WindowInfo *window, int warn)
261 {
262     patternSet *patterns;
263     windowHighlightData *highlightData;
264     char *stylePtr, *styleString;
265     const char  *stringPtr, *bufString;
266     char prevChar = '\0';
267     int i, oldFontHeight;
268 
269     /* Find the pattern set matching the window's current
270        language mode, tell the user if it can't be done */
271     patterns = findPatternsForWindow(window, warn);
272     if (patterns == NULL)
273     	return;
274 
275     /* Compile the patterns */
276     highlightData = createHighlightData(window, patterns);
277     if (highlightData == NULL)
278     	return;
279 
280     /* Prepare for a long delay, refresh display and put up a watch cursor */
281     BeginWait(window->shell);
282     XmUpdateDisplay(window->shell);
283 
284     /* Parse the buffer with pass 1 patterns.  If there are none, initialize
285        the style buffer to all UNFINISHED_STYLE to trigger parsing later */
286     stylePtr = styleString = (char*)NEditMalloc(window->buffer->length + 1);
287     if (highlightData->pass1Patterns == NULL) {
288     	for (i=0; i<window->buffer->length; i++)
289     	    *stylePtr++ = UNFINISHED_STYLE;
290     } else {
291 	stringPtr = bufString = BufAsString(window->buffer);
292 	parseString(highlightData->pass1Patterns, &stringPtr, &stylePtr,
293     		window->buffer->length, &prevChar, False,
294     		GetWindowDelimiters(window), bufString, NULL);
295     }
296     *stylePtr = '\0';
297     BufSetAll(highlightData->styleBuffer, styleString);
298     NEditFree(styleString);
299 
300     /* install highlight pattern data in the window data structure */
301     window->highlightData = highlightData;
302 
303     /* Get the height of the current font in the window, to be used after
304        highlighting is turned on to resize the window to make room for
305        additional highlight fonts which may be sized differently */
306     oldFontHeight = getFontHeight(window);
307 
308     /* Attach highlight information to text widgets in each pane */
309     AttachHighlightToWidget(window->textArea, window);
310     for (i=0; i<window->nPanes; i++)
311 	AttachHighlightToWidget(window->textPanes[i], window);
312 
313     /* Re-size the window to fit the highlight fonts properly & tell the
314        window manager about the potential line-height change as well */
315     updateWindowHeight(window, oldFontHeight);
316     UpdateWMSizeHints(window);
317     UpdateMinPaneHeights(window);
318 
319     /* Make sure that if the window has grown, the additional area gets
320        repainted. Otherwise, it is possible that the area gets moved before a
321        repaint event is received and the area doesn't get repainted at all
322        (eg. because of a -line command line argument that moves the text). */
323     XmUpdateDisplay(window->shell);
324     EndWait(window->shell);
325 }
326 
327 /*
328 ** Turn off syntax highlighting and free style buffer, compiled patterns, and
329 ** related data.
330 */
StopHighlighting(WindowInfo * window)331 void StopHighlighting(WindowInfo *window)
332 {
333     int i, oldFontHeight;
334 
335     if (window->highlightData==NULL)
336     	return;
337 
338     /* Get the line height being used by the highlight fonts in the window,
339        to be used after highlighting is turned off to resize the window
340        back to the line height of the primary font */
341     oldFontHeight = getFontHeight(window);
342 
343     /* Free and remove the highlight data from the window */
344     freeHighlightData((windowHighlightData *)window->highlightData);
345     window->highlightData = NULL;
346 
347     /* Remove and detach style buffer and style table from all text
348        display(s) of window, and redisplay without highlighting */
349     RemoveWidgetHighlight(window->textArea);
350     for (i=0; i<window->nPanes; i++)
351 	RemoveWidgetHighlight(window->textPanes[i]);
352 
353     /* Re-size the window to fit the primary font properly & tell the window
354        manager about the potential line-height change as well */
355     updateWindowHeight(window, oldFontHeight);
356     UpdateWMSizeHints(window);
357     UpdateMinPaneHeights(window);
358 }
359 
360 /*
361 ** Free highlighting data from a window destined for destruction, without
362 ** redisplaying.
363 */
FreeHighlightingData(WindowInfo * window)364 void FreeHighlightingData(WindowInfo *window)
365 {
366     int i;
367 
368     if (window->highlightData == NULL)
369 	return;
370 
371     /* Free and remove the highlight data from the window */
372     freeHighlightData((windowHighlightData *)window->highlightData);
373     window->highlightData = NULL;
374 
375     /* The text display may make a last desperate attempt to access highlight
376        information when it is destroyed, which would be a disaster. */
377     ((TextWidget)window->textArea)->text.textD->styleBuffer = NULL;
378     for (i=0; i<window->nPanes; i++)
379 	((TextWidget)window->textPanes[i])->text.textD->styleBuffer = NULL;
380 }
381 
382 /*
383 ** Attach style information from a window's highlight data to a
384 ** text widget and redisplay.
385 */
AttachHighlightToWidget(Widget widget,WindowInfo * window)386 void AttachHighlightToWidget(Widget widget, WindowInfo *window)
387 {
388     windowHighlightData *highlightData =
389     	    (windowHighlightData *)window->highlightData;
390 
391     TextDAttachHighlightData(((TextWidget)widget)->text.textD,
392     	    highlightData->styleBuffer, highlightData->styleTable,
393     	    highlightData->nStyles, UNFINISHED_STYLE, handleUnparsedRegionCB,
394     	    window);
395 }
396 
397 /*
398 ** Remove style information from a text widget and redisplay it.
399 */
RemoveWidgetHighlight(Widget widget)400 void RemoveWidgetHighlight(Widget widget)
401 {
402     TextDAttachHighlightData(((TextWidget)widget)->text.textD,
403     	    NULL, NULL, 0, UNFINISHED_STYLE, NULL, NULL);
404 }
405 
406 /*
407 ** Change highlight fonts and/or styles in a highlighted window, without
408 ** re-parsing.
409 */
UpdateHighlightStyles(WindowInfo * window)410 void UpdateHighlightStyles(WindowInfo *window)
411 {
412     patternSet *patterns;
413     windowHighlightData *highlightData;
414     windowHighlightData *oldHighlightData =
415     	    (windowHighlightData *)window->highlightData;
416     textBuffer *styleBuffer;
417     int i;
418 
419     /* Do nothing if window not highlighted */
420     if (window->highlightData == NULL)
421     	return;
422 
423     /* Find the pattern set for the window's current language mode */
424     patterns = findPatternsForWindow(window, False);
425     if (patterns == NULL) {
426     	StopHighlighting(window);
427     	return;
428     }
429 
430     /* Build new patterns */
431     highlightData = createHighlightData(window, patterns);
432     if (highlightData == NULL) {
433     	StopHighlighting(window);
434     	return;
435     }
436 
437     /* Update highlight pattern data in the window data structure, but
438        preserve all of the effort that went in to parsing the buffer
439        by swapping it with the empty one in highlightData (which is then
440        freed in freeHighlightData) */
441     styleBuffer = oldHighlightData->styleBuffer;
442     oldHighlightData->styleBuffer = highlightData->styleBuffer;
443     freeHighlightData(oldHighlightData);
444     highlightData->styleBuffer = styleBuffer;
445     window->highlightData = highlightData;
446 
447     /* Attach new highlight information to text widgets in each pane
448        (and redraw) */
449     AttachHighlightToWidget(window->textArea, window);
450     for (i=0; i<window->nPanes; i++)
451 	AttachHighlightToWidget(window->textPanes[i], window);
452 }
453 
454 /*
455 ** Do a test compile of patterns in "patSet" and report problems to the
456 ** user via dialog.  Returns True if patterns are ok.
457 **
458 ** This is somewhat kludgy in that it uses createHighlightData, which
459 ** requires a window to find the fonts to use, and just uses a random
460 ** window from the window list.  Since the window is used to get the
461 ** dialog parent as well, in non-popups-under-pointer mode, these dialogs
462 ** will appear in odd places on the screen.
463 */
TestHighlightPatterns(patternSet * patSet)464 int TestHighlightPatterns(patternSet *patSet)
465 {
466     windowHighlightData *highlightData;
467 
468     /* Compile the patterns (passing a random window as a source for fonts, and
469        parent for dialogs, since we really don't care what fonts are used) */
470     highlightData = createHighlightData(WindowList, patSet);
471     if (highlightData == NULL)
472     	return False;
473     freeHighlightData(highlightData);
474     return True;
475 }
476 
477 /*
478 ** Returns the highlight style of the character at a given position of a
479 ** window. To avoid breaking encapsulation, the highlight style is converted
480 ** to a void* pointer (no other module has to know that characters are used
481 ** to represent highlight styles; that would complicate future extensions).
482 ** Returns NULL if the window has highlighting turned off.
483 ** The only guarantee that this function offers, is that when the same
484 ** pointer is returned for two positions, the corresponding characters have
485 ** the same highlight style.
486 **/
GetHighlightInfo(WindowInfo * window,int pos)487 void* GetHighlightInfo(WindowInfo *window, int pos)
488 {
489     int style;
490     highlightDataRec *pattern = NULL;
491     windowHighlightData *highlightData =
492 	(windowHighlightData *)window->highlightData;
493     if (!highlightData)
494 	return NULL;
495 
496     /* Be careful with signed/unsigned conversions. NO conversion here! */
497     style = (int)BufGetCharacter(highlightData->styleBuffer, pos);
498 
499     /* Beware of unparsed regions. */
500     if (style == UNFINISHED_STYLE) {
501 	handleUnparsedRegion(window, highlightData->styleBuffer, pos);
502 	style = (int)BufGetCharacter(highlightData->styleBuffer, pos);
503     }
504 
505     if (highlightData->pass1Patterns) {
506        pattern = patternOfStyle(highlightData->pass1Patterns, style);
507     }
508 
509     if (!pattern && highlightData->pass2Patterns) {
510 	pattern = patternOfStyle(highlightData->pass2Patterns, style);
511     }
512 
513     if (!pattern) {
514 	return NULL;
515     }
516     return (void*)(intptr_t)pattern->userStyleIndex;
517 }
518 
519 /*
520 ** Free allocated memory associated with highlight data, including compiled
521 ** regular expressions, style buffer and style table.  Note: be sure to
522 ** NULL out the widget references to the objects in this structure before
523 ** calling this.  Because of the slow, multi-phase destruction of
524 ** widgets, this data can be referenced even AFTER destroying the widget.
525 */
freeHighlightData(windowHighlightData * hd)526 static void freeHighlightData(windowHighlightData *hd)
527 {
528     if (hd == NULL)
529     	return;
530     if (hd->pass1Patterns != NULL)
531     	freePatterns(hd->pass1Patterns);
532     if (hd->pass2Patterns != NULL)
533     	freePatterns(hd->pass2Patterns);
534     NEditFree(hd->parentStyles);
535     BufFree(hd->styleBuffer);
536     NEditFree(hd->styleTable);
537     NEditFree(hd);
538 }
539 
540 /*
541 ** Find the pattern set matching the window's current language mode, or
542 ** tell the user if it can't be done (if warn is True) and return NULL.
543 */
findPatternsForWindow(WindowInfo * window,int warn)544 static patternSet *findPatternsForWindow(WindowInfo *window, int warn)
545 {
546     patternSet *patterns;
547     char *modeName;
548 
549     /* Find the window's language mode.  If none is set, warn user */
550     modeName = LanguageModeName(window->languageMode);
551     if (modeName == NULL) {
552     	if (warn)
553     	    DialogF(DF_WARN, window->shell, 1, "Language Mode",
554                     "No language-specific mode has been set for this file.\n\n"
555                     "To use syntax highlighting in this window, please select a\n"
556                     "language from the Preferences -> Language Modes menu.\n\n"
557                     "New language modes and syntax highlighting patterns can be\n"
558                     "added via Preferences -> Default Settings -> Language Modes,\n"
559                     "and Preferences -> Default Settings -> Syntax Highlighting.",
560                     "OK");
561     	return NULL;
562     }
563 
564     /* Look up the appropriate pattern for the language */
565     patterns = FindPatternSet(modeName);
566     if (patterns == NULL)
567     {
568         if (warn)
569         {
570             DialogF(DF_WARN, window->shell, 1, "Language Mode",
571                     "Syntax highlighting is not available in language\n"
572                     "mode %s.\n\n"
573                     "You can create new syntax highlight patterns in the\n"
574                     "Preferences -> Default Settings -> Syntax Highlighting\n"
575                     "dialog, or choose a different language mode from:\n"
576                     "Preferences -> Language Mode.", "OK", modeName);
577             return NULL;
578         }
579     }
580 
581     return patterns;
582 }
583 
584 /*
585 ** Create complete syntax highlighting information from "patternSrc", using
586 ** highlighting fonts from "window", includes pattern compilation.  If errors
587 ** are encountered, warns user with a dialog and returns NULL.  To free the
588 ** allocated components of the returned data structure, use freeHighlightData.
589 */
createHighlightData(WindowInfo * window,patternSet * patSet)590 static windowHighlightData *createHighlightData(WindowInfo *window,
591     	patternSet *patSet)
592 {
593     highlightPattern *patternSrc = patSet->patterns;
594     int nPatterns = patSet->nPatterns;
595     int contextLines = patSet->lineContext;
596     int contextChars = patSet->charContext;
597     int i, nPass1Patterns, nPass2Patterns;
598     int noPass1, noPass2;
599     char *parentStyles, *parentStylesPtr, *parentName;
600     highlightPattern *pass1PatternSrc, *pass2PatternSrc, *p1Ptr, *p2Ptr;
601     styleTableEntry *styleTable, *styleTablePtr;
602     textBuffer *styleBuf;
603     highlightDataRec *pass1Pats, *pass2Pats;
604     windowHighlightData *highlightData;
605 
606     /* The highlighting code can't handle empty pattern sets, quietly say no */
607     if (nPatterns == 0)
608     {
609         return NULL;
610     }
611 
612     /* Check that the styles and parent pattern names actually exist */
613     if (!NamedStyleExists("Plain"))
614     {
615         DialogF(DF_WARN, window->shell, 1, "Highlight Style",
616                 "Highlight style \"Plain\" is missing", "OK");
617         return NULL;
618     }
619 
620     for (i=0; i<nPatterns; i++)
621     {
622         if (patternSrc[i].subPatternOf != NULL
623                 && indexOfNamedPattern(patternSrc, nPatterns,
624                         patternSrc[i].subPatternOf) == -1)
625         {
626             DialogF(DF_WARN, window->shell, 1, "Parent Pattern",
627                     "Parent field \"%s\" in pattern \"%s\"\n"
628                     "does not match any highlight patterns in this set",
629                     "OK", patternSrc[i].subPatternOf, patternSrc[i].name);
630             return NULL;
631         }
632     }
633 
634     for (i=0; i<nPatterns; i++)
635     {
636         if (!NamedStyleExists(patternSrc[i].style))
637         {
638             DialogF(DF_WARN, window->shell, 1, "Highlight Style",
639                     "Style \"%s\" named in pattern \"%s\"\n"
640                     "does not match any existing style", "OK",
641                     patternSrc[i].style, patternSrc[i].name);
642             return NULL;
643         }
644     }
645 
646     /* Make DEFER_PARSING flags agree with top level patterns (originally,
647        individual flags had to be correct and were checked here, but dialog now
648        shows this setting only on top patterns which is much less confusing) */
649     for (i = 0; i < nPatterns; i++)
650     {
651         if (patternSrc[i].subPatternOf != NULL)
652         {
653             int parentindex;
654 
655             parentindex=findTopLevelParentIndex(patternSrc, nPatterns, i);
656             if (parentindex==-1)
657             {
658                 DialogF(DF_WARN, window->shell, 1, "Parent Pattern",
659                         "Pattern \"%s\" does not have valid parent", "OK",
660                         patternSrc[i].name);
661                 return NULL;
662             }
663 
664             if (patternSrc[parentindex].flags & DEFER_PARSING)
665             {
666                 patternSrc[i].flags |= DEFER_PARSING;
667             } else
668             {
669                 patternSrc[i].flags &= ~DEFER_PARSING;
670             }
671         }
672     }
673 
674     /* Sort patterns into those to be used in pass 1 parsing, and those to
675        be used in pass 2, and add default pattern (0) to each list */
676     nPass1Patterns = 1;
677     nPass2Patterns = 1;
678     for (i=0; i<nPatterns; i++)
679     	if (patternSrc[i].flags & DEFER_PARSING)
680     	    nPass2Patterns++;
681     	else
682     	    nPass1Patterns++;
683     p1Ptr = pass1PatternSrc = (highlightPattern *)NEditMalloc(
684     	    sizeof(highlightPattern) * nPass1Patterns);
685     p2Ptr = pass2PatternSrc = (highlightPattern *)NEditMalloc(
686     	    sizeof(highlightPattern) * nPass2Patterns);
687     p1Ptr->name = p2Ptr->name = "";
688     p1Ptr->startRE = p2Ptr->startRE = NULL;
689     p1Ptr->endRE = p2Ptr->endRE = NULL;
690     p1Ptr->errorRE = p2Ptr->errorRE = NULL;
691     p1Ptr->style = p2Ptr->style = "Plain";
692     p1Ptr->subPatternOf = p2Ptr->subPatternOf = NULL;
693     p1Ptr->flags = p2Ptr->flags = 0;
694     p1Ptr++; p2Ptr++;
695     for (i=0; i<nPatterns; i++) {
696     	if (patternSrc[i].flags & DEFER_PARSING)
697     	    *p2Ptr++ = patternSrc[i];
698     	else
699     	    *p1Ptr++ = patternSrc[i];
700     }
701 
702     /* If a particular pass is empty except for the default pattern, don't
703        bother compiling it or setting up styles */
704     if (nPass1Patterns == 1)
705     	nPass1Patterns = 0;
706     if (nPass2Patterns == 1)
707     	nPass2Patterns = 0;
708 
709     /* Compile patterns */
710     if (nPass1Patterns == 0)
711     	pass1Pats = NULL;
712     else {
713 	pass1Pats = compilePatterns(window->shell, pass1PatternSrc,
714     		nPass1Patterns);
715 	if (pass1Pats == NULL)
716     	    return NULL;
717     }
718     if (nPass2Patterns == 0)
719     	pass2Pats = NULL;
720     else {
721 	pass2Pats = compilePatterns(window->shell, pass2PatternSrc,
722     		nPass2Patterns);
723 	if (pass2Pats == NULL)
724     	    return NULL;
725     }
726 
727     /* Set pattern styles.  If there are pass 2 patterns, pass 1 pattern
728        0 should have a default style of UNFINISHED_STYLE.  With no pass 2
729        patterns, unstyled areas of pass 1 patterns should be PLAIN_STYLE
730        to avoid triggering re-parsing every time they are encountered */
731     noPass1 = nPass1Patterns == 0;
732     noPass2 = nPass2Patterns == 0;
733     if (noPass2)
734     	pass1Pats[0].style = PLAIN_STYLE;
735     else if (noPass1)
736     	pass2Pats[0].style = PLAIN_STYLE;
737     else {
738 	pass1Pats[0].style = UNFINISHED_STYLE;
739 	pass2Pats[0].style = PLAIN_STYLE;
740     }
741     for (i=1; i<nPass1Patterns; i++)
742        	pass1Pats[i].style = PLAIN_STYLE + i;
743     for (i=1; i<nPass2Patterns; i++)
744        	pass2Pats[i].style = PLAIN_STYLE + (noPass1 ? 0 : nPass1Patterns-1) + i;
745 
746     /* Create table for finding parent styles */
747     parentStylesPtr = parentStyles = (char*)NEditMalloc(nPass1Patterns+nPass2Patterns+2);
748     *parentStylesPtr++ = '\0';
749     *parentStylesPtr++ = '\0';
750     for (i=1; i<nPass1Patterns; i++) {
751 	parentName = pass1PatternSrc[i].subPatternOf;
752 	*parentStylesPtr++ = parentName == NULL ? PLAIN_STYLE :
753 		pass1Pats[indexOfNamedPattern(pass1PatternSrc,
754 		nPass1Patterns, parentName)].style;
755     }
756     for (i=1; i<nPass2Patterns; i++) {
757 	parentName = pass2PatternSrc[i].subPatternOf;
758 	*parentStylesPtr++ = parentName == NULL ? PLAIN_STYLE :
759 		pass2Pats[indexOfNamedPattern(pass2PatternSrc,
760 		nPass2Patterns, parentName)].style;
761     }
762 
763     /* Set up table for mapping colors and fonts to syntax */
764     styleTablePtr = styleTable = (styleTableEntry *)NEditMalloc(
765     	    sizeof(styleTableEntry) * (nPass1Patterns + nPass2Patterns + 1));
766 #define setStyleTablePtr(styleTablePtr, patternSrc) \
767     do { \
768       styleTableEntry *p = styleTablePtr; \
769       highlightPattern *pat = patternSrc; \
770       int r, g, b; \
771       \
772       p->highlightName = pat->name; \
773       p->styleName = pat->style; \
774       p->colorName = ColorOfNamedStyle(pat->style); \
775       p->bgColorName = BgColorOfNamedStyle(pat->style); \
776       p->isBold = FontOfNamedStyleIsBold(pat->style); \
777       p->isItalic = FontOfNamedStyleIsItalic(pat->style); \
778       /* And now for the more physical stuff */ \
779       p->color = AllocColor(window->textArea, p->colorName, &r, &g, &b); \
780       p->red = r; \
781       p->green = g; \
782       p->blue = b; \
783       if (p->bgColorName) { \
784         p->bgColor = AllocColor(window->textArea, p->bgColorName, &r, &g, &b); \
785         p->bgRed = r; \
786         p->bgGreen = g; \
787         p->bgBlue = b; \
788       } \
789       else { \
790         p->bgColor = p->color; \
791         p->bgRed = r; \
792         p->bgGreen = g; \
793         p->bgBlue = b; \
794       } \
795       p->font = FontOfNamedStyle(window, pat->style); \
796     } while (0)
797 
798     /* PLAIN_STYLE (pass 1) */
799     styleTablePtr->underline = FALSE;
800     setStyleTablePtr(styleTablePtr++,
801                    noPass1 ? &pass2PatternSrc[0] : &pass1PatternSrc[0]);
802     /* PLAIN_STYLE (pass 2) */
803     styleTablePtr->underline = FALSE;
804     setStyleTablePtr(styleTablePtr++,
805                    noPass2 ? &pass1PatternSrc[0] : &pass2PatternSrc[0]);
806     /* explicit styles (pass 1) */
807     for (i=1; i<nPass1Patterns; i++) {
808     	styleTablePtr->underline = FALSE;
809       setStyleTablePtr(styleTablePtr++, &pass1PatternSrc[i]);
810     }
811     /* explicit styles (pass 2) */
812     for (i=1; i<nPass2Patterns; i++) {
813     	styleTablePtr->underline = FALSE;
814       setStyleTablePtr(styleTablePtr++, &pass2PatternSrc[i]);
815     }
816 
817     /* Free the temporary sorted pattern source list */
818     NEditFree(pass1PatternSrc);
819     NEditFree(pass2PatternSrc);
820 
821     /* Create the style buffer */
822     styleBuf = BufCreate();
823 
824     /* Collect all of the highlighting information in a single structure */
825     highlightData =(windowHighlightData *)NEditMalloc(sizeof(windowHighlightData));
826     highlightData->pass1Patterns = pass1Pats;
827     highlightData->pass2Patterns = pass2Pats;
828     highlightData->parentStyles = parentStyles;
829     highlightData->styleTable = styleTable;
830     highlightData->nStyles = styleTablePtr - styleTable;
831     highlightData->styleBuffer = styleBuf;
832     highlightData->contextRequirements.nLines = contextLines;
833     highlightData->contextRequirements.nChars = contextChars;
834     highlightData->patternSetForWindow = patSet;
835 
836     return highlightData;
837 }
838 
839 /*
840 ** Transform pattern sources into the compiled highlight information
841 ** actually used by the code.  Output is a tree of highlightDataRec structures
842 ** containing compiled regular expressions and style information.
843 */
compilePatterns(Widget dialogParent,highlightPattern * patternSrc,int nPatterns)844 static highlightDataRec *compilePatterns(Widget dialogParent,
845     	highlightPattern *patternSrc, int nPatterns)
846 {
847     int i, nSubExprs, patternNum, length, subPatIndex, subExprNum, charsRead;
848     int parentIndex;
849     char *ptr, *bigPattern, *compileMsg;
850     highlightDataRec *compiledPats;
851 
852     /* Allocate memory for the compiled patterns.  The list is terminated
853        by a record with style == 0. */
854     compiledPats = (highlightDataRec *)NEditMalloc(sizeof(highlightDataRec) *
855     	    (nPatterns + 1));
856     compiledPats[nPatterns].style = 0;
857 
858     /* Build the tree of parse expressions */
859     for (i=0; i<nPatterns; i++) {
860     	compiledPats[i].nSubPatterns = 0;
861     	compiledPats[i].nSubBranches = 0;
862     }
863     for (i=1; i<nPatterns; i++)
864     	if (patternSrc[i].subPatternOf == NULL)
865     	    compiledPats[0].nSubPatterns++;
866     	else
867     	    compiledPats[indexOfNamedPattern(patternSrc, nPatterns,
868     	    	    patternSrc[i].subPatternOf)].nSubPatterns++;
869     for (i=0; i<nPatterns; i++)
870     	compiledPats[i].subPatterns = compiledPats[i].nSubPatterns == 0 ?
871     	    	NULL : (highlightDataRec **)NEditMalloc(
872     	    	    sizeof(highlightDataRec *) * compiledPats[i].nSubPatterns);
873     for (i=0; i<nPatterns; i++)
874     	compiledPats[i].nSubPatterns = 0;
875     for (i=1; i<nPatterns; i++) {
876     	if (patternSrc[i].subPatternOf == NULL) {
877     	    compiledPats[0].subPatterns[compiledPats[0].nSubPatterns++] =
878     	    		&compiledPats[i];
879     	} else {
880     	    parentIndex = indexOfNamedPattern(patternSrc,
881     	    	    nPatterns, patternSrc[i].subPatternOf);
882     	    compiledPats[parentIndex].subPatterns[compiledPats[parentIndex].
883     	    	    nSubPatterns++] = &compiledPats[i];
884     	}
885     }
886 
887     /* Process color-only sub patterns (no regular expressions to match,
888        just colors and fonts for sub-expressions of the parent pattern */
889     for (i=0; i<nPatterns; i++) {
890         compiledPats[i].colorOnly = patternSrc[i].flags & COLOR_ONLY;
891         compiledPats[i].userStyleIndex = IndexOfNamedStyle(patternSrc[i].style);
892         if (compiledPats[i].colorOnly && compiledPats[i].nSubPatterns != 0)
893         {
894             DialogF(DF_WARN, dialogParent, 1, "Color-only Pattern",
895                     "Color-only pattern \"%s\" may not have subpatterns",
896                     "OK", patternSrc[i].name);
897             return NULL;
898         }
899         nSubExprs = 0;
900         if (patternSrc[i].startRE != NULL) {
901             ptr = patternSrc[i].startRE;
902             while(TRUE) {
903 		if (*ptr == '&') {
904 		    compiledPats[i].startSubexprs[nSubExprs++] = 0;
905 		    ptr++;
906 		} else if (sscanf(ptr, "\\%d%n", &subExprNum, &charsRead)==1) {
907         	    compiledPats[i].startSubexprs[nSubExprs++] = subExprNum;
908         	    ptr += charsRead;
909 		} else
910 		    break;
911             }
912         }
913         compiledPats[i].startSubexprs[nSubExprs] = -1;
914         nSubExprs = 0;
915         if (patternSrc[i].endRE != NULL) {
916             ptr = patternSrc[i].endRE;
917             while(TRUE) {
918 		if (*ptr == '&') {
919 		    compiledPats[i].endSubexprs[nSubExprs++] = 0;
920 		    ptr++;
921 		} else if (sscanf(ptr, "\\%d%n", &subExprNum, &charsRead)==1) {
922         	    compiledPats[i].endSubexprs[nSubExprs++] = subExprNum;
923         	    ptr += charsRead;
924 		} else
925 		    break;
926             }
927         }
928         compiledPats[i].endSubexprs[nSubExprs] = -1;
929     }
930 
931     /* Compile regular expressions for all highlight patterns */
932     for (i=0; i<nPatterns; i++) {
933 	if (patternSrc[i].startRE == NULL || compiledPats[i].colorOnly)
934 	    compiledPats[i].startRE = NULL;
935 	else {
936 	    if ((compiledPats[i].startRE = compileREAndWarn(dialogParent,
937 	    	    patternSrc[i].startRE)) == NULL)
938    		return NULL;
939     	}
940     	if (patternSrc[i].endRE == NULL || compiledPats[i].colorOnly)
941     	    compiledPats[i].endRE = NULL;
942     	else {
943     	    if ((compiledPats[i].endRE = compileREAndWarn(dialogParent,
944     	    	    patternSrc[i].endRE)) == NULL)
945  		return NULL;
946     	}
947     	if (patternSrc[i].errorRE == NULL)
948     	    compiledPats[i].errorRE = NULL;
949     	else {
950     	    if ((compiledPats[i].errorRE = compileREAndWarn(dialogParent,
951     	    	    patternSrc[i].errorRE)) == NULL)
952  		return NULL;
953     	}
954     }
955 
956     /* Construct and compile the great hairy pattern to match the OR of the
957        end pattern, the error pattern, and all of the start patterns of the
958        sub-patterns */
959     for (patternNum=0; patternNum<nPatterns; patternNum++) {
960 	if (patternSrc[patternNum].endRE == NULL &&
961 	    	patternSrc[patternNum].errorRE == NULL &&
962 	    	compiledPats[patternNum].nSubPatterns == 0) {
963 	    compiledPats[patternNum].subPatternRE = NULL;
964 	    continue;
965 	}
966 	length = (compiledPats[patternNum].colorOnly ||
967 	    	patternSrc[patternNum].endRE == NULL) ? 0 :
968 	    	strlen(patternSrc[patternNum].endRE) + 5;
969 	length += (compiledPats[patternNum].colorOnly ||
970                    patternSrc[patternNum].errorRE == NULL) ? 0 :
971 	    	strlen(patternSrc[patternNum].errorRE) + 5;
972 	for (i=0; i<compiledPats[patternNum].nSubPatterns; i++) {
973     	    subPatIndex = compiledPats[patternNum].subPatterns[i]-compiledPats;
974     	    length += compiledPats[subPatIndex].colorOnly ? 0 :
975     	    	    strlen(patternSrc[subPatIndex].startRE) + 5;
976 	}
977 	if (length == 0) {
978 	    compiledPats[patternNum].subPatternRE = NULL;
979 	    continue;
980 	}
981 	bigPattern = (char*)NEditMalloc(length+1);
982 	ptr=bigPattern;
983 	if (patternSrc[patternNum].endRE != NULL) {
984 	    *ptr++ = '('; *ptr++ = '?'; *ptr++ = ':';
985     	    strcpy(ptr, patternSrc[patternNum].endRE);
986     	    ptr += strlen(patternSrc[patternNum].endRE);
987     	    *ptr++ = ')';
988     	    *ptr++ = '|';
989 	    compiledPats[patternNum].nSubBranches++;
990 	}
991 	if (patternSrc[patternNum].errorRE != NULL) {
992 	    *ptr++ = '('; *ptr++ = '?'; *ptr++ = ':';
993     	    strcpy(ptr, patternSrc[patternNum].errorRE);
994     	    ptr += strlen(patternSrc[patternNum].errorRE);
995     	    *ptr++ = ')';
996     	    *ptr++ = '|';
997 	    compiledPats[patternNum].nSubBranches++;
998 	}
999 	for (i=0; i<compiledPats[patternNum].nSubPatterns; i++) {
1000     	    subPatIndex = compiledPats[patternNum].subPatterns[i]-compiledPats;
1001     	    if (compiledPats[subPatIndex].colorOnly)
1002     	    	continue;
1003 	    *ptr++ = '('; *ptr++ = '?'; *ptr++ = ':';
1004     	    strcpy(ptr, patternSrc[subPatIndex].startRE);
1005     	    ptr += strlen(patternSrc[subPatIndex].startRE);
1006     	    *ptr++ = ')';
1007     	    *ptr++ = '|';
1008 	    compiledPats[patternNum].nSubBranches++;
1009 	}
1010 	*(ptr-1) = '\0';
1011 	compiledPats[patternNum].subPatternRE = CompileRE(bigPattern,
1012 	    	&compileMsg, REDFLT_STANDARD);
1013 	if (compiledPats[patternNum].subPatternRE == NULL) {
1014     	    fprintf(stderr, "Error compiling syntax highlight patterns:\n%s",
1015     	    	    compileMsg);
1016     	    return NULL;
1017 	}
1018 	NEditFree(bigPattern);
1019     }
1020 
1021     /* Copy remaining parameters from pattern template to compiled tree */
1022     for (i=0; i<nPatterns; i++)
1023         compiledPats[i].flags = patternSrc[i].flags;
1024 
1025     return compiledPats;
1026 }
1027 
1028 /*
1029 ** Free a pattern list and all of its allocated components
1030 */
freePatterns(highlightDataRec * patterns)1031 static void freePatterns(highlightDataRec *patterns)
1032 {
1033     int i;
1034 
1035     for (i=0; patterns[i].style!=0; i++) {
1036     	if (patterns[i].startRE != NULL)
1037     	    free((char *)patterns[i].startRE);
1038     	if (patterns[i].endRE != NULL)
1039     	    free((char *)patterns[i].endRE);
1040     	if (patterns[i].errorRE != NULL)
1041     	    free((char *)patterns[i].errorRE);
1042     	if (patterns[i].subPatternRE != NULL)
1043     	    free((char *)patterns[i].subPatternRE);
1044     }
1045 
1046     for (i=0; patterns[i].style!=0; i++) {
1047         NEditFree(patterns[i].subPatterns);
1048     }
1049 
1050     NEditFree(patterns);
1051 }
1052 
1053 /*
1054 ** Find the highlightPattern structure with a given name in the window.
1055 */
FindPatternOfWindow(WindowInfo * window,char * name)1056 highlightPattern *FindPatternOfWindow(WindowInfo *window, char *name)
1057 {
1058     windowHighlightData *hData = (windowHighlightData *)window->highlightData;
1059     patternSet *set;
1060     int i;
1061 
1062     if (hData && (set = hData->patternSetForWindow)) {
1063       for (i = 0; i < set->nPatterns; i++)
1064           if (strcmp(set->patterns[i].name, name) == 0)
1065               return &set->patterns[i];
1066     }
1067     return NULL;
1068 }
1069 
1070 /*
1071 ** Picks up the entry in the style buffer for the position (if any). Rather
1072 ** like styleOfPos() in textDisp.c. Returns the style code or zero.
1073 */
HighlightCodeOfPos(WindowInfo * window,int pos)1074 int HighlightCodeOfPos(WindowInfo *window, int pos)
1075 {
1076     windowHighlightData *highlightData =
1077           (windowHighlightData *)window->highlightData;
1078     textBuffer *styleBuf =
1079           highlightData ? highlightData->styleBuffer : NULL;
1080     int hCode = 0;
1081 
1082     if (styleBuf != NULL) {
1083       hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1084       if (hCode == UNFINISHED_STYLE) {
1085           /* encountered "unfinished" style, trigger parsing */
1086           handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1087           hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1088       }
1089     }
1090     return hCode;
1091 }
1092 
1093 /*
1094 ** Returns the length over which a particular highlight code applies, starting
1095 ** at pos. If the initial code value *checkCode is zero, the highlight code of
1096 ** pos is used.
1097 */
1098 /* YOO: This is called form only one other function, which uses a constant
1099     for checkCode and never evaluates it after the call. */
HighlightLengthOfCodeFromPos(WindowInfo * window,int pos,int * checkCode)1100 int HighlightLengthOfCodeFromPos(WindowInfo *window, int pos, int *checkCode)
1101 {
1102     windowHighlightData *highlightData =
1103           (windowHighlightData *)window->highlightData;
1104     textBuffer *styleBuf =
1105           highlightData ? highlightData->styleBuffer : NULL;
1106     int hCode = 0;
1107     int oldPos = pos;
1108 
1109     if (styleBuf != NULL) {
1110       hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1111       if (!hCode)
1112           return 0;
1113       if (hCode == UNFINISHED_STYLE) {
1114           /* encountered "unfinished" style, trigger parsing */
1115           handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1116           hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1117       }
1118       if (*checkCode == 0)
1119           *checkCode = hCode;
1120       while (hCode == *checkCode || hCode == UNFINISHED_STYLE) {
1121           if (hCode == UNFINISHED_STYLE) {
1122               /* encountered "unfinished" style, trigger parsing, then loop */
1123               handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1124               hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1125           }
1126           else {
1127               /* advance the position and get the new code */
1128               hCode = (unsigned char)BufGetCharacter(styleBuf, ++pos);
1129           }
1130       }
1131     }
1132     return pos - oldPos;
1133 }
1134 
1135 /*
1136 ** Returns the length over which a particular style applies, starting at pos.
1137 ** If the initial code value *checkCode is zero, the highlight code of pos
1138 ** is used.
1139 */
StyleLengthOfCodeFromPos(WindowInfo * window,int pos,const char ** checkStyleName)1140 int StyleLengthOfCodeFromPos(WindowInfo *window, int pos,
1141                              const char **checkStyleName)
1142 {
1143     windowHighlightData *highlightData =
1144           (windowHighlightData *)window->highlightData;
1145     textBuffer *styleBuf =
1146           highlightData ? highlightData->styleBuffer : NULL;
1147     int hCode = 0;
1148     int oldPos = pos;
1149     styleTableEntry *entry;
1150 
1151     if (styleBuf != NULL) {
1152       hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1153       if (!hCode)
1154           return 0;
1155       if (hCode == UNFINISHED_STYLE) {
1156           /* encountered "unfinished" style, trigger parsing */
1157           handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1158           hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1159       }
1160       entry = styleTableEntryOfCode(window, hCode);
1161       if (entry == NULL)
1162           return 0;
1163       if ((*checkStyleName) == NULL)
1164           (*checkStyleName) = entry->styleName;
1165       while (hCode == UNFINISHED_STYLE ||
1166              ((entry = styleTableEntryOfCode(window, hCode)) &&
1167                 strcmp(entry->styleName, (*checkStyleName)) == 0)) {
1168           if (hCode == UNFINISHED_STYLE) {
1169               /* encountered "unfinished" style, trigger parsing, then loop */
1170               handleUnparsedRegion(window, highlightData->styleBuffer, pos);
1171               hCode = (unsigned char)BufGetCharacter(styleBuf, pos);
1172           }
1173           else {
1174               /* advance the position and get the new code */
1175               hCode = (unsigned char)BufGetCharacter(styleBuf, ++pos);
1176           }
1177       }
1178     }
1179     return pos - oldPos;
1180 }
1181 
1182 /*
1183 ** Returns a pointer to the entry in the style table for the entry of code
1184 ** hCode (if any).
1185 */
styleTableEntryOfCode(WindowInfo * window,int hCode)1186 static styleTableEntry *styleTableEntryOfCode(WindowInfo *window, int hCode)
1187 {
1188     windowHighlightData *highlightData =
1189           (windowHighlightData *)window->highlightData;
1190 
1191     hCode -= UNFINISHED_STYLE;       /* get the correct index value */
1192     if (!highlightData || hCode < 0 || hCode >= highlightData->nStyles)
1193       return NULL;
1194     return &highlightData->styleTable[hCode];
1195 }
1196 
1197 /*
1198 ** Functions to return style information from the highlighting style table.
1199 */
1200 
HighlightNameOfCode(WindowInfo * window,int hCode)1201 char *HighlightNameOfCode(WindowInfo *window, int hCode)
1202 {
1203     styleTableEntry *entry = styleTableEntryOfCode(window, hCode);
1204     return entry ? entry->highlightName : "";
1205 }
1206 
HighlightStyleOfCode(WindowInfo * window,int hCode)1207 char *HighlightStyleOfCode(WindowInfo *window, int hCode)
1208 {
1209     styleTableEntry *entry = styleTableEntryOfCode(window, hCode);
1210     return entry ? entry->styleName : "";
1211 }
1212 
HighlightColorValueOfCode(WindowInfo * window,int hCode,int * r,int * g,int * b)1213 Pixel HighlightColorValueOfCode(WindowInfo *window, int hCode,
1214       int *r, int *g, int *b)
1215 {
1216     styleTableEntry *entry = styleTableEntryOfCode(window, hCode);
1217     if (entry) {
1218         *r = entry->red;
1219         *g = entry->green;
1220         *b = entry->blue;
1221         return entry->color;
1222     }
1223     else
1224     {
1225         /* pick up foreground color of the (first) text widget of the window */
1226         XColor colorDef;
1227         Colormap cMap;
1228         Display *display = XtDisplay(window->textArea);
1229         *r = *g = *b = 0;
1230         XtVaGetValues(window->textArea,
1231                       XtNcolormap,   &cMap,
1232                       XtNforeground, &colorDef.pixel,
1233                       NULL);
1234         if (XQueryColor(display, cMap, &colorDef)) {
1235             *r = colorDef.red;
1236             *g = colorDef.green;
1237             *b = colorDef.blue;
1238         }
1239         return colorDef.pixel;
1240     }
1241 }
1242 
GetHighlightBGColorOfCode(WindowInfo * window,int hCode,int * r,int * g,int * b)1243 Pixel GetHighlightBGColorOfCode(WindowInfo *window, int hCode,
1244       int *r, int *g, int *b)
1245 {
1246     styleTableEntry *entry = styleTableEntryOfCode(window, hCode);
1247     if (entry && entry->bgColorName) {
1248         *r = entry->bgRed;
1249         *g = entry->bgGreen;
1250         *b = entry->bgBlue;
1251         return entry->bgColor;
1252     }
1253     else
1254     {
1255         /* pick up background color of the (first) text widget of the window */
1256         XColor colorDef;
1257         Colormap cMap;
1258         Display *display = XtDisplay(window->textArea);
1259         *r = *g = *b = 0;
1260         XtVaGetValues(window->textArea,
1261                       XtNcolormap,   &cMap,
1262                       XtNbackground, &colorDef.pixel,
1263                       NULL);
1264         if (XQueryColor(display, cMap, &colorDef)) {
1265             *r = colorDef.red;
1266             *g = colorDef.green;
1267             *b = colorDef.blue;
1268         }
1269         return colorDef.pixel;
1270     }
1271 }
1272 
1273 /*
1274 ** Callback to parse an "unfinished" region of the buffer.  "unfinished" means
1275 ** that the buffer has been parsed with pass 1 patterns, but this section has
1276 ** not yet been exposed, and thus never had pass 2 patterns applied.  This
1277 ** callback is invoked when the text widget's display routines encounter one
1278 ** of these unfinished regions.  "pos" is the first position encountered which
1279 ** needs re-parsing.  This routine applies pass 2 patterns to a chunk of
1280 ** the buffer of size PASS_2_REPARSE_CHUNK_SIZE beyond pos.
1281 */
handleUnparsedRegion(const WindowInfo * window,textBuffer * styleBuf,int pos)1282 static void handleUnparsedRegion(const WindowInfo* window, textBuffer* styleBuf,
1283         int pos)
1284 {
1285     textBuffer *buf = window->buffer;
1286     int beginParse, endParse, beginSafety, endSafety, p;
1287     windowHighlightData *highlightData =
1288             (windowHighlightData *)window->highlightData;
1289 
1290     reparseContext *context = &highlightData->contextRequirements;
1291     highlightDataRec *pass2Patterns = highlightData->pass2Patterns;
1292     char *string, *styleString, *stylePtr, c, prevChar;
1293     const char *stringPtr;
1294     int firstPass2Style = (unsigned char)pass2Patterns[1].style;
1295 
1296     /* If there are no pass 2 patterns to process, do nothing (but this
1297        should never be triggered) */
1298     if (pass2Patterns == NULL)
1299     	return;
1300 
1301     /* Find the point at which to begin parsing to ensure that the character at
1302        pos is parsed correctly (beginSafety), at most one context distance back
1303        from pos, unless there is a pass 1 section from which to start */
1304     beginParse = pos;
1305     beginSafety = backwardOneContext(buf, context, beginParse);
1306     for (p=beginParse; p>=beginSafety; p--) {
1307     	c = BufGetCharacter(styleBuf, p);
1308     	if (c != UNFINISHED_STYLE && c != PLAIN_STYLE &&
1309 		(unsigned char)c < firstPass2Style) {
1310     	    beginSafety = p + 1;
1311     	    break;
1312     	}
1313     }
1314 
1315     /* Decide where to stop (endParse), and the extra distance (endSafety)
1316        necessary to ensure that the changes at endParse are correct.  Stop at
1317        the end of the unfinished region, or a max. of PASS_2_REPARSE_CHUNK_SIZE
1318        characters forward from the requested position */
1319     endParse = min(buf->length, pos + PASS_2_REPARSE_CHUNK_SIZE);
1320     endSafety = forwardOneContext(buf, context, endParse);
1321     for (p=pos; p<endSafety; p++) {
1322     	c = BufGetCharacter(styleBuf, p);
1323     	if (c != UNFINISHED_STYLE && c != PLAIN_STYLE &&
1324 		(unsigned char)c < firstPass2Style) {
1325     	    endParse = min(endParse, p);
1326     	    endSafety = p;
1327     	    break;
1328     	} else if (c != UNFINISHED_STYLE && p < endParse) {
1329     	    endParse = p;
1330     	    if ((unsigned char)c < firstPass2Style)
1331     	    	endSafety = p;
1332     	    else
1333     	    	endSafety = forwardOneContext(buf, context, endParse);
1334     	    break;
1335     	}
1336     }
1337 
1338     /* Copy the buffer range into a string */
1339     /* printf("callback pass2 parsing from %d thru %d w/ safety from %d thru %d\n",
1340     	    beginParse, endParse, beginSafety, endSafety); */
1341     stringPtr = string = BufGetRange(buf, beginSafety, endSafety);
1342     styleString = stylePtr = BufGetRange(styleBuf, beginSafety, endSafety);
1343 
1344     /* Parse it with pass 2 patterns */
1345     prevChar = getPrevChar(buf, beginSafety);
1346     parseString(pass2Patterns, &stringPtr, &stylePtr, endParse - beginSafety,
1347     	    &prevChar, False, GetWindowDelimiters(window), string, NULL);
1348 
1349     /* Update the style buffer the new style information, but only between
1350        beginParse and endParse.  Skip the safety region */
1351     styleString[endParse-beginSafety] = '\0';
1352     BufReplace(styleBuf, beginParse, endParse,
1353     	    &styleString[beginParse-beginSafety]);
1354     NEditFree(styleString);
1355     NEditFree(string);
1356 }
1357 
1358 /*
1359 ** Callback wrapper around the above function.
1360 */
handleUnparsedRegionCB(const textDisp * textD,int pos,const void * cbArg)1361 static void handleUnparsedRegionCB(const textDisp* textD, int pos,
1362         const void* cbArg)
1363 {
1364     handleUnparsedRegion((WindowInfo*) cbArg, textD->styleBuffer, pos);
1365 }
1366 
1367 /*
1368 ** Re-parse the smallest region possible around a modification to buffer "buf"
1369 ** to gurantee that the promised context lines and characters have
1370 ** been presented to the patterns.  Changes the style buffer in "highlightData"
1371 ** with the parsing result.
1372 */
incrementalReparse(windowHighlightData * highlightData,textBuffer * buf,int pos,int nInserted,const char * delimiters)1373 static void incrementalReparse(windowHighlightData *highlightData,
1374     	textBuffer *buf, int pos, int nInserted, const char *delimiters)
1375 {
1376     int beginParse, endParse, endAt, lastMod, parseInStyle, nPasses;
1377     textBuffer *styleBuf = highlightData->styleBuffer;
1378     highlightDataRec *pass1Patterns = highlightData->pass1Patterns;
1379     highlightDataRec *pass2Patterns = highlightData->pass2Patterns;
1380     highlightDataRec *startPattern;
1381     reparseContext *context = &highlightData->contextRequirements;
1382     char *parentStyles = highlightData->parentStyles;
1383 
1384     /* Find the position "beginParse" at which to begin reparsing.  This is
1385        far enough back in the buffer such that the guranteed number of
1386        lines and characters of context are examined. */
1387     beginParse = pos;
1388     parseInStyle = findSafeParseRestartPos(buf, highlightData, &beginParse);
1389 
1390     /* Find the position "endParse" at which point it is safe to stop
1391        parsing, unless styles are getting changed beyond the last
1392        modification */
1393     lastMod = pos + nInserted;
1394     endParse = forwardOneContext(buf, context, lastMod);
1395 
1396     /*
1397     ** Parse the buffer from beginParse, until styles compare
1398     ** with originals for one full context distance.  Distance increases
1399     ** by powers of two until nothing changes from previous step.  If
1400     ** parsing ends before endParse, start again one level up in the
1401     ** pattern hierarchy
1402     */
1403     for (nPasses=0; ; nPasses++) {
1404 
1405 	/* Parse forward from beginParse to one context beyond the end
1406 	   of the last modification */
1407         startPattern = patternOfStyle(pass1Patterns, parseInStyle);
1408         /* If there is no pattern matching the style, it must be a pass-2
1409            style. It that case, it is (probably) safe to start parsing with
1410            the root pass-1 pattern again. Anyway, passing a NULL-pointer to
1411            the parse routine would result in a crash; restarting with pass-1
1412            patterns is certainly preferable, even if there is a slight chance
1413            of a faulty coloring. */
1414 	if (!startPattern) {
1415 	    startPattern = pass1Patterns;
1416 	}
1417     	endAt = parseBufferRange(startPattern,
1418     	    	pass2Patterns, buf, styleBuf, context, beginParse, endParse,
1419 		delimiters);
1420 
1421 	/* If parse completed at this level, move one style up in the
1422 	   hierarchy and start again from where the previous parse left off. */
1423 	if (endAt < endParse) {
1424 	    beginParse = endAt;
1425 	    endParse = forwardOneContext(buf, context,
1426 	    	    max(endAt, max(lastModified(styleBuf), lastMod)));
1427 	    if (IS_PLAIN(parseInStyle)) {
1428 		fprintf(stderr,
1429 			"NEdit internal error: incr. reparse fell short\n");
1430 		return;
1431 	    }
1432 	    parseInStyle = parentStyleOf(parentStyles, parseInStyle);
1433 
1434 	/* One context distance beyond last style changed means we're done */
1435 	} else if (lastModified(styleBuf) <= lastMod) {
1436 	    return;
1437 
1438 	/* Styles are changing beyond the modification, continue extending
1439 	   the end of the parse range by powers of 2 * REPARSE_CHUNK_SIZE and
1440 	   reparse until nothing changes */
1441 	} else {
1442 	    lastMod = lastModified(styleBuf);
1443     	    endParse = min(buf->length, forwardOneContext(buf, context, lastMod)
1444     	    	    + (REPARSE_CHUNK_SIZE << nPasses));
1445 	}
1446     }
1447 }
1448 
1449 /*
1450 ** Parse text in buffer "buf" between positions "beginParse" and "endParse"
1451 ** using pass 1 patterns over the entire range and pass 2 patterns where needed
1452 ** to determine whether re-parsed areas have changed and need to be redrawn.
1453 ** Deposits style information in "styleBuf" and expands the selection in
1454 ** styleBuf to show the additional areas which have changed and need
1455 ** redrawing.  beginParse must be a position from which pass 1 parsing may
1456 ** safely be started using the pass1Patterns given.  Internally, adds a
1457 ** "takeoff" safety region before beginParse, so that pass 2 patterns will be
1458 ** allowed to match properly if they begin before beginParse, and a "landing"
1459 ** safety region beyond endparse so that endParse is guranteed to be parsed
1460 ** correctly in both passes.  Returns the buffer position at which parsing
1461 ** finished (this will normally be endParse, unless the pass1Patterns is a
1462 ** pattern which does end and the end is reached).
1463 */
parseBufferRange(highlightDataRec * pass1Patterns,highlightDataRec * pass2Patterns,textBuffer * buf,textBuffer * styleBuf,reparseContext * contextRequirements,int beginParse,int endParse,const char * delimiters)1464 static int parseBufferRange(highlightDataRec *pass1Patterns,
1465     	highlightDataRec *pass2Patterns, textBuffer *buf, textBuffer *styleBuf,
1466         reparseContext *contextRequirements, int beginParse, int endParse,
1467         const char *delimiters)
1468 {
1469     char *string, *styleString, *stylePtr, *temp, prevChar;
1470     const char *stringPtr;
1471     int endSafety, endPass2Safety, startPass2Safety, tempLen;
1472     int modStart, modEnd, beginSafety, beginStyle, p, style;
1473     int firstPass2Style = pass2Patterns == NULL ? INT_MAX :
1474 	    (unsigned char)pass2Patterns[1].style;
1475 
1476     /* Begin parsing one context distance back (or to the last style change) */
1477     beginStyle = pass1Patterns->style;
1478     if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements)) {
1479     	beginSafety = backwardOneContext(buf, contextRequirements, beginParse);
1480      	for (p=beginParse; p>=beginSafety; p--) {
1481     	    style = BufGetCharacter(styleBuf, p-1);
1482     	    if (!EQUIVALENT_STYLE(style, beginStyle, firstPass2Style)) {
1483     	    	beginSafety = p;
1484     	    	break;
1485     	    }
1486     	}
1487     } else {
1488     	for (beginSafety=max(0,beginParse-1); beginSafety>0; beginSafety--) {
1489     	    style = BufGetCharacter(styleBuf, beginSafety);
1490     	    if (!EQUIVALENT_STYLE(style, beginStyle, firstPass2Style) ||
1491     	    	    BufGetCharacter(buf, beginSafety) == '\n') {
1492     	    	beginSafety++;
1493     	    	break;
1494     	    }
1495     	}
1496     }
1497 
1498     /* Parse one parse context beyond requested end to gurantee that parsing
1499        at endParse is complete, unless patterns can't cross line boundaries,
1500        in which case the end of the line is fine */
1501     if (endParse == 0)
1502     	return 0;
1503     if (CAN_CROSS_LINE_BOUNDARIES(contextRequirements))
1504     	endSafety = forwardOneContext(buf, contextRequirements, endParse);
1505     else if (endParse>=buf->length || (BufGetCharacter(buf,endParse-1)=='\n'))
1506     	endSafety = endParse;
1507     else
1508     	endSafety = min(buf->length, BufEndOfLine(buf, endParse) + 1);
1509 
1510     /* copy the buffer range into a string */
1511     string = BufGetRange(buf, beginSafety, endSafety);
1512     styleString = BufGetRange(styleBuf, beginSafety, endSafety);
1513 
1514     /* Parse it with pass 1 patterns */
1515     /* printf("parsing from %d thru %d\n", beginSafety, endSafety); */
1516     prevChar = getPrevChar(buf, beginParse);
1517     stringPtr = &string[beginParse-beginSafety];
1518     stylePtr = &styleString[beginParse-beginSafety];
1519     parseString(pass1Patterns, &stringPtr, &stylePtr, endParse-beginParse,
1520     	    &prevChar, False, delimiters, string, NULL);
1521 
1522     /* On non top-level patterns, parsing can end early */
1523     endParse = min(endParse, stringPtr-string + beginSafety);
1524 
1525     /* If there are no pass 2 patterns, we're done */
1526     if (pass2Patterns == NULL)
1527     	goto parseDone;
1528 
1529     /* Parsing of pass 2 patterns is done only as necessary for determining
1530        where styles have changed.  Find the area to avoid, which is already
1531        marked as changed (all inserted text and previously modified areas) */
1532     if (styleBuf->primary.selected) {
1533 	modStart = styleBuf->primary.start;
1534 	modEnd = styleBuf->primary.end;
1535     } else
1536     	modStart = modEnd = 0;
1537 
1538     /* Re-parse the areas before the modification with pass 2 patterns, from
1539        beginSafety to far enough beyond modStart to gurantee that parsing at
1540        modStart is correct (pass 2 patterns must match entirely within one
1541        context distance, and only on the top level).  If the parse region
1542        ends entirely before the modification or at or beyond modEnd, parse
1543        the whole thing and take advantage of the safety region which will be
1544        thrown away below.  Otherwise save the contents of the safety region
1545        temporarily, and restore it after the parse. */
1546     if (beginSafety < modStart) {
1547 	if (endSafety > modStart) {
1548 	    endPass2Safety = forwardOneContext(buf, contextRequirements,
1549 	    	    modStart);
1550 	    if (endPass2Safety + PASS_2_REPARSE_CHUNK_SIZE >= modEnd)
1551 	    	endPass2Safety = endSafety;
1552     	} else
1553     	    endPass2Safety = endSafety;
1554 	prevChar = getPrevChar(buf, beginSafety);
1555 	if (endPass2Safety == endSafety) {
1556 	    passTwoParseString(pass2Patterns, string, styleString,
1557                     endParse - beginSafety, &prevChar, delimiters, string, NULL);
1558 	    goto parseDone;
1559 	} else {
1560 	    tempLen = endPass2Safety - modStart;
1561 	    temp = (char*)NEditMalloc(tempLen);
1562 	    strncpy(temp, &styleString[modStart-beginSafety], tempLen);
1563 	    passTwoParseString(pass2Patterns, string, styleString,
1564                     modStart - beginSafety, &prevChar, delimiters, string, NULL);
1565 	    strncpy(&styleString[modStart-beginSafety], temp, tempLen);
1566 	    NEditFree(temp);
1567 	}
1568     }
1569 
1570     /* Re-parse the areas after the modification with pass 2 patterns, from
1571        modEnd to endSafety, with an additional safety region before modEnd
1572        to ensure that parsing at modEnd is correct. */
1573     if (endParse > modEnd) {
1574 	if (beginSafety > modEnd) {
1575 	    prevChar = getPrevChar(buf, beginSafety);
1576 	    passTwoParseString(pass2Patterns, string, styleString,
1577                     endParse - beginSafety, &prevChar, delimiters, string, NULL);
1578 	} else {
1579 	    startPass2Safety = max(beginSafety,
1580 	    	    backwardOneContext(buf, contextRequirements, modEnd));
1581 	    tempLen = modEnd - startPass2Safety;
1582 	    temp = (char*)NEditMalloc(tempLen);
1583 	    strncpy(temp, &styleString[startPass2Safety-beginSafety], tempLen);
1584 	    prevChar = getPrevChar(buf, startPass2Safety);
1585 	    passTwoParseString(pass2Patterns,
1586                     &string[startPass2Safety-beginSafety],
1587                     &styleString[startPass2Safety-beginSafety],
1588                     endParse-startPass2Safety, &prevChar, delimiters, string,
1589                     NULL);
1590 	    strncpy(&styleString[startPass2Safety-beginSafety], temp, tempLen);
1591 	    NEditFree(temp);
1592 	}
1593     }
1594 
1595 parseDone:
1596 
1597     /* Update the style buffer with the new style information, but only
1598        through endParse.  Skip the safety region at the end */
1599     styleString[endParse-beginSafety] = '\0';
1600     modifyStyleBuf(styleBuf, &styleString[beginParse-beginSafety],
1601     	    beginParse, endParse, firstPass2Style);
1602     NEditFree(styleString);
1603     NEditFree(string);
1604 
1605     return endParse;
1606 }
1607 
1608 /*
1609 ** Parses "string" according to compiled regular expressions in "pattern"
1610 ** until endRE is or errorRE are matched, or end of string is reached.
1611 ** Advances "string", "styleString" pointers to the next character past
1612 ** the end of the parsed section, and updates "prevChar" to reflect
1613 ** the new character before "string".
1614 ** If "anchored" is true, just scan the sub-pattern starting at the beginning
1615 ** of the string.  "length" is how much of the string must be parsed, but
1616 ** "string" must still be null terminated, the termination indicating how
1617 ** far the string should be searched, and "length" the part which is actually
1618 ** required (the string may or may not be parsed beyond "length").
1619 **
1620 ** "lookBehindTo" indicates the boundary till where look-behind patterns may
1621 ** look back. If NULL, the start of the string is assumed to be the boundary.
1622 **
1623 ** "match_till" indicates the boundary till where matches may extend. If NULL,
1624 ** it is assumed that the terminating \0 indicates the boundary. Note that
1625 ** look-ahead patterns can peek beyond the boundary, if supplied.
1626 **
1627 ** Returns True if parsing was done and the parse succeeded.  Returns False if
1628 ** the error pattern matched, if the end of the string was reached without
1629 ** matching the end expression, or in the unlikely event of an internal error.
1630 */
parseString(highlightDataRec * pattern,const char ** string,char ** styleString,int length,char * prevChar,int anchored,const char * delimiters,const char * lookBehindTo,const char * match_till)1631 static int parseString(highlightDataRec *pattern, const char **string,
1632     	char **styleString, int length, char *prevChar, int anchored,
1633     	const char *delimiters, const char* lookBehindTo,
1634     	const char* match_till)
1635 {
1636     int i, subExecuted, subIndex;
1637     char *stylePtr;
1638     const char *stringPtr, *savedStartPtr, *startingStringPtr;
1639     signed char *subExpr;
1640     char savedPrevChar;
1641     char succChar = match_till ? (*match_till) : '\0';
1642     highlightDataRec *subPat = NULL, *subSubPat;
1643 
1644     if (length <= 0)
1645     	return False;
1646 
1647     stringPtr = *string;
1648     stylePtr = *styleString;
1649 
1650     while (ExecRE(pattern->subPatternRE, stringPtr, anchored ? *string+1 :
1651 	    *string+length+1, False, *prevChar, succChar, delimiters,
1652             lookBehindTo, match_till)) {
1653 	/* Beware of the case where only one real branch exists, but that
1654 	   branch has sub-branches itself. In that case the top_branch refers
1655 	   to the matching sub-branch and must be ignored. */
1656 	subIndex = (pattern->nSubBranches > 1) ?
1657 	   pattern->subPatternRE->top_branch : 0;
1658     	/* Combination of all sub-patterns and end pattern matched */
1659     	/* printf("combined patterns RE matched at %d\n",
1660     	    	pattern->subPatternRE->startp[0] - *string); */
1661 	startingStringPtr = stringPtr;
1662 
1663 	/* Fill in the pattern style for the text that was skipped over before
1664 	   the match, and advance the pointers to the start of the pattern */
1665 	fillStyleString(&stringPtr, &stylePtr, pattern->subPatternRE->startp[0],
1666 	    	pattern->style, prevChar);
1667 
1668     	/* If the combined pattern matched this pattern's end pattern, we're
1669     	   done.  Fill in the style string, update the pointers, color the
1670 	   end expression if there were coloring sub-patterns, and return */
1671 	savedStartPtr = stringPtr;
1672 	savedPrevChar = *prevChar;
1673 	if (pattern->endRE != NULL) {
1674 	    if (subIndex == 0) {
1675 		fillStyleString(&stringPtr, &stylePtr,
1676 		    pattern->subPatternRE->endp[0], pattern->style, prevChar);
1677 		subExecuted = False;
1678 		for (i=0;i<pattern->nSubPatterns; i++) {
1679 		    subPat = pattern->subPatterns[i];
1680 		    if (subPat->colorOnly) {
1681 			if (!subExecuted) {
1682                             if (!ExecRE(pattern->endRE, savedStartPtr,
1683 				savedStartPtr+1, False, savedPrevChar,
1684 				succChar, delimiters, lookBehindTo, match_till)) {
1685 				fprintf(stderr, "Internal error, failed to "
1686 					"recover end match in parseString\n");
1687 				return False;
1688 			    }
1689 			    subExecuted = True;
1690 			}
1691 			for (subExpr=subPat->endSubexprs; *subExpr!=-1; subExpr++)
1692 		    	    recolorSubexpr(pattern->endRE, *subExpr,
1693 				subPat->style, *string, *styleString);
1694     	    	    }
1695 		}
1696     		*string = stringPtr;
1697 		*styleString = stylePtr;
1698 		return True;
1699 	    }
1700 	    --subIndex;
1701     	}
1702 
1703     	/* If the combined pattern matched this pattern's error pattern, we're
1704     	   done.  Fill in the style string, update the pointers, and return */
1705     	if (pattern->errorRE != NULL) {
1706 	    if (subIndex == 0) {
1707 		fillStyleString(&stringPtr, &stylePtr,
1708 		    pattern->subPatternRE->startp[0], pattern->style, prevChar);
1709     		    *string = stringPtr;
1710 		*styleString = stylePtr;
1711 		return False;
1712 	    }
1713 	    --subIndex;
1714     	}
1715 
1716     	/* Figure out which sub-pattern matched */
1717     	for (i=0; i<pattern->nSubPatterns; i++) {
1718 	    subPat = pattern->subPatterns[i];
1719 	    if (subPat->colorOnly) ++subIndex;
1720 	    else if (i == subIndex) break;
1721 	}
1722     	if (i == pattern->nSubPatterns) {
1723     	    fprintf(stderr, "Internal error, failed to match in parseString\n");
1724     	    return False;
1725     	}
1726 
1727     	/* the sub-pattern is a simple match, just color it */
1728     	if (subPat->subPatternRE == NULL) {
1729     	    fillStyleString(&stringPtr, &stylePtr, pattern->subPatternRE->endp[0], /* subPat->startRE->endp[0],*/
1730     	    	    subPat->style, prevChar);
1731 
1732     	/* Parse the remainder of the sub-pattern */
1733     	} else if (subPat->endRE != NULL) {
1734             /* The pattern is a starting/ending type of pattern, proceed with
1735                the regular hierarchical parsing. */
1736 
1737     	    /* If parsing should start after the start pattern, advance
1738     	       to that point (this is currently always the case) */
1739     	    if (!(subPat->flags & PARSE_SUBPATS_FROM_START))
1740     		fillStyleString(&stringPtr, &stylePtr, pattern->subPatternRE->endp[0], /* subPat->startRE->endp[0],*/
1741 			subPat->style, prevChar);
1742 
1743    	    /* Parse to the end of the subPattern */
1744    	    parseString(subPat, &stringPtr, &stylePtr, length -
1745    	    	    (stringPtr - *string), prevChar, False, delimiters,
1746                     lookBehindTo, match_till);
1747     	} else {
1748     	    /* If the parent pattern is not a start/end pattern, the
1749                sub-pattern can between the boundaries of the parent's
1750                match. Note that we must limit the recursive matches such
1751                that they do not exceed the parent's ending boundary.
1752                Without that restriction, matching becomes unstable. */
1753 
1754    	    /* Parse to the end of the subPattern */
1755    	    parseString(subPat, &stringPtr, &stylePtr,
1756                 pattern->subPatternRE->endp[0]-stringPtr, prevChar, False,
1757                 delimiters, lookBehindTo, pattern->subPatternRE->endp[0]);
1758     	}
1759 
1760     	/* If the sub-pattern has color-only sub-sub-patterns, add color
1761 	   based on the coloring sub-expression references */
1762 	subExecuted = False;
1763     	for (i=0; i<subPat->nSubPatterns; i++) {
1764 	    subSubPat = subPat->subPatterns[i];
1765 	    if (subSubPat->colorOnly) {
1766 		if (!subExecuted) {
1767                    if (!ExecRE(subPat->startRE, savedStartPtr,
1768 			savedStartPtr+1, False, savedPrevChar, succChar,
1769 			delimiters, lookBehindTo, match_till)) {
1770 			fprintf(stderr, "Internal error, failed to recover "
1771 					"start match in parseString\n");
1772 			return False;
1773 		    }
1774 		    subExecuted = True;
1775 		}
1776 	    	for (subExpr=subSubPat->startSubexprs; *subExpr!=-1; subExpr++)
1777 	    	    recolorSubexpr(subPat->startRE, *subExpr, subSubPat->style,
1778 	    	    	    *string, *styleString);
1779 	    }
1780 	}
1781 
1782 	/* Make sure parsing progresses.  If patterns match the empty string,
1783 	   they can get stuck and hang the process */
1784 	if (stringPtr == startingStringPtr) {
1785 	    /* Avoid stepping over the end of the string (possible for
1786                zero-length matches at end of the string) */
1787 	    if (*stringPtr == '\0')
1788 		break;
1789 	    fillStyleString(&stringPtr, &stylePtr, stringPtr+1,
1790 			pattern->style, prevChar);
1791 	}
1792     }
1793 
1794     /* If this is an anchored match (must match on first character), and
1795        nothing matched, return False */
1796     if (anchored && stringPtr == *string)
1797     	return False;
1798 
1799     /* Reached end of string, fill in the remaining text with pattern style
1800        (unless this was an anchored match) */
1801     if (!anchored)
1802         fillStyleString(&stringPtr, &stylePtr, *string+length, pattern->style,
1803                 prevChar);
1804 
1805     /* Advance the string and style pointers to the end of the parsed text */
1806     *string = stringPtr;
1807     *styleString = stylePtr;
1808     return pattern->endRE == NULL;
1809 }
1810 
1811 /*
1812 ** Takes a string which has already been parsed through pass1 parsing and
1813 ** re-parses the areas where pass two patterns are applicable.  Parameters
1814 ** have the same meaning as in parseString, except that strings aren't doubly
1815 ** indirect and string pointers are not updated.
1816 */
passTwoParseString(highlightDataRec * pattern,char * string,char * styleString,int length,char * prevChar,const char * delimiters,const char * lookBehindTo,const char * match_till)1817 static void passTwoParseString(highlightDataRec *pattern, char *string,
1818         char *styleString, int length, char *prevChar, const char *delimiters,
1819         const char *lookBehindTo, const char *match_till)
1820 {
1821     int inParseRegion = False;
1822     char *stylePtr, temp, *parseStart = NULL, *parseEnd, *s, *c;
1823     const char *stringPtr;
1824     int firstPass2Style = (unsigned char)pattern[1].style;
1825 
1826     for (c = string, s = styleString; ; c++, s++) {
1827     	if (!inParseRegion && *c != '\0' && (*s == UNFINISHED_STYLE ||
1828     	    	*s == PLAIN_STYLE || (unsigned char)*s >= firstPass2Style)) {
1829     	    parseStart = c;
1830     	    inParseRegion = True;
1831     	}
1832     	if (inParseRegion && (*c == '\0' || !(*s == UNFINISHED_STYLE ||
1833     	    	*s == PLAIN_STYLE || (unsigned char)*s >= firstPass2Style))) {
1834     	    parseEnd = c;
1835 	    if (parseStart != string)
1836 	    	*prevChar = *(parseStart-1);
1837 	    stringPtr = parseStart;
1838 	    stylePtr = &styleString[parseStart - string];
1839     	    temp = *parseEnd;
1840     	    *parseEnd = '\0';
1841     	    /* printf("pass2 parsing %d chars\n", strlen(stringPtr)); */
1842     	    parseString(pattern, &stringPtr, &stylePtr,
1843     	    	    min(parseEnd - parseStart, length - (parseStart - string)),
1844     	    	    prevChar, False, delimiters, lookBehindTo, match_till);
1845     	    *parseEnd = temp;
1846     	    inParseRegion = False;
1847     	}
1848     	if (*c == '\0' || (!inParseRegion && c - string >= length))
1849     	    break;
1850     }
1851 }
1852 
1853 /*
1854 ** Advance "stringPtr" and "stylePtr" until "stringPtr" == "toPtr", filling
1855 ** "stylePtr" with style "style".  Can also optionally update the pre-string
1856 ** character, prevChar, which is fed to regular the expression matching
1857 ** routines for determining word and line boundaries at the start of the string.
1858 */
fillStyleString(const char ** stringPtr,char ** stylePtr,const char * toPtr,char style,char * prevChar)1859 static void fillStyleString(const char **stringPtr, char **stylePtr,
1860     	const char *toPtr, char style, char *prevChar)
1861 {
1862     int i, len = toPtr-*stringPtr;
1863 
1864     if (*stringPtr >= toPtr)
1865     	return;
1866 
1867     for (i=0; i<len; i++)
1868     	*(*stylePtr)++ = style;
1869     if (prevChar != NULL) *prevChar = *(toPtr-1);
1870     *stringPtr = toPtr;
1871 }
1872 
1873 /*
1874 ** Incorporate changes from styleString into styleBuf, tracking changes
1875 ** in need of redisplay, and marking them for redisplay by the text
1876 ** modification callback in textDisp.c.  "firstPass2Style" is necessary
1877 ** for distinguishing pass 2 styles which compare as equal to the unfinished
1878 ** style in the original buffer, from pass1 styles which signal a change.
1879 */
modifyStyleBuf(textBuffer * styleBuf,char * styleString,int startPos,int endPos,int firstPass2Style)1880 static void modifyStyleBuf(textBuffer *styleBuf, char *styleString,
1881     	int startPos, int endPos, int firstPass2Style)
1882 {
1883     char *c, bufChar;
1884     int pos, modStart, modEnd, minPos = INT_MAX, maxPos = 0;
1885     selection *sel = &styleBuf->primary;
1886 
1887     /* Skip the range already marked for redraw */
1888     if (sel->selected) {
1889 	modStart = sel->start;
1890 	modEnd = sel->end;
1891     } else
1892     	modStart = modEnd = startPos;
1893 
1894     /* Compare the original style buffer (outside of the modified range) with
1895        the new string with which it will be updated, to find the extent of
1896        the modifications.  Unfinished styles in the original match any
1897        pass 2 style */
1898     for (c=styleString, pos=startPos; pos<modStart && pos<endPos; c++, pos++) {
1899     	bufChar = BufGetCharacter(styleBuf, pos);
1900     	if (*c != bufChar && !(bufChar == UNFINISHED_STYLE &&
1901     	    	(*c == PLAIN_STYLE || (unsigned char)*c >= firstPass2Style))) {
1902     	    if (pos < minPos) minPos = pos;
1903     	    if (pos > maxPos) maxPos = pos;
1904     	}
1905     }
1906     for (c=&styleString[max(0, modEnd-startPos)], pos=max(modEnd, startPos);
1907     	    pos<endPos; c++, pos++) {
1908     	bufChar = BufGetCharacter(styleBuf, pos);
1909     	if (*c != bufChar && !(bufChar == UNFINISHED_STYLE &&
1910     	    	(*c == PLAIN_STYLE || (unsigned char)*c >= firstPass2Style))) {
1911     	    if (pos < minPos) minPos = pos;
1912     	    if (pos+1 > maxPos) maxPos = pos+1;
1913     	}
1914     }
1915 
1916     /* Make the modification */
1917     BufReplace(styleBuf, startPos, endPos, styleString);
1918 
1919     /* Mark or extend the range that needs to be redrawn.  Even if no
1920        change was made, it's important to re-establish the selection,
1921        because it can get damaged by the BufReplace above */
1922     BufSelect(styleBuf, min(modStart, minPos), max(modEnd, maxPos));
1923 }
1924 
1925 /*
1926 ** Return the last modified position in styleBuf (as marked by modifyStyleBuf
1927 ** by the convention used for conveying modification information to the
1928 ** text widget, which is selecting the text)
1929 */
lastModified(textBuffer * styleBuf)1930 static int lastModified(textBuffer *styleBuf)
1931 {
1932     if (styleBuf->primary.selected)
1933     	return max(0, styleBuf->primary.end);
1934     return 0;
1935 }
1936 
1937 /*
1938 ** Compute the distance between two colors.
1939 */
1940 
colorDistance(const XColor * c1,const XColor * c2)1941 static double colorDistance(const XColor *c1, const XColor *c2)
1942 {
1943     /* This is done in RGB space, which is close, but not optimal.  It's
1944        probably better to do it in HSV or YIQ space, however, that means
1945        a whole lot of extra conversions.  This would allow us to weight
1946        the coordinates differently, e.g, prefer to match hue over
1947        brightness. */
1948 
1949     static const double scale = 65535;
1950 
1951     double tred   = c1->red   / scale - c2->red   / scale;
1952     double tgreen = c1->green / scale - c2->green / scale;
1953     double tblue  = c1->blue  / scale - c2->blue  / scale;
1954 
1955     /* use square Euclidian distance */
1956     return tred * tred + tgreen * tgreen + tblue * tblue;
1957 }
1958 
1959 /*
1960 ** use this canned function to call AllocColor() when
1961 ** the r, g & b components is not needed, thus saving
1962 ** the little hassle of creating the dummy variable.
1963 */
AllocateColor(Widget w,const char * colorName)1964 Pixel AllocateColor(Widget w, const char *colorName)
1965 {
1966     int dummy;
1967 
1968     return AllocColor(w, colorName, &dummy, &dummy, &dummy);
1969 }
1970 
1971 /*
1972 ** Allocate a read-only (shareable) colormap cell for a named color, from the
1973 ** the default colormap of the screen on which the widget (w) is displayed. If
1974 ** the colormap is full and there's no suitable substitute, print an error on
1975 ** stderr, and return the widget's foreground color as a backup.
1976 */
1977 
AllocColor(Widget w,const char * colorName,int * r,int * g,int * b)1978 Pixel AllocColor(Widget w, const char *colorName, int *r, int *g, int *b)
1979 {
1980     XColor       colorDef;
1981     XColor      *allColorDefs;
1982     Display     *display = XtDisplay(w);
1983     Colormap     cMap;
1984     Pixel        foreground, bestPixel;
1985     double       small = 1.0e9;
1986     int          depth;
1987     unsigned int ncolors;
1988     unsigned long i, best = 0;    /* pixel value */
1989 
1990     /* Get the correct colormap for compatability with the "best" visual
1991        feature in 5.2.  Default visual of screen is no good here. */
1992 
1993     XtVaGetValues(w,
1994                   XtNcolormap,   &cMap,
1995                   XtNdepth,      &depth,
1996                   XtNforeground, &foreground,
1997                   NULL);
1998 
1999     bestPixel = foreground; /* Our last fallback */
2000 
2001     /* First, check for valid syntax */
2002     if (! XParseColor(display, cMap, colorName, &colorDef)) {
2003         fprintf(stderr, "NEdit: Color name %s not in database\n",  colorName);
2004         colorDef.pixel = foreground;
2005         if (XQueryColor(display, cMap, &colorDef)) {
2006             *r = colorDef.red;
2007 	    *g = colorDef.green;
2008 	    *b = colorDef.blue;
2009         }
2010         return foreground;
2011     }
2012 
2013     /* Attempt allocation of the exact color. */
2014     if (XAllocColor(display, cMap, &colorDef)) {
2015 	*r = colorDef.red;
2016 	*g = colorDef.green;
2017 	*b = colorDef.blue;
2018         return colorDef.pixel;
2019     }
2020 
2021     /* ---------- Allocation failed, the colormap may be full. ---------- */
2022 
2023 #if 0
2024     printf("Couldn't allocate %d %d %d\n", colorDef.red, colorDef.green, colorDef.blue);
2025 #endif
2026 
2027     /* We can't do the nearest-match on other than 8 bit visuals because
2028        it just takes too long.  */
2029 
2030     if (depth > 8) {             /* Oh no! */
2031         colorDef.pixel = foreground;
2032         if (XQueryColor(display, cMap, &colorDef)) {
2033 	    *r = colorDef.red;
2034 	    *g = colorDef.green;
2035 	    *b = colorDef.blue;
2036         }
2037         return foreground;
2038     }
2039 
2040     /* Get the entire colormap so we can find the closest one. */
2041     ncolors = (1 << depth);
2042     allColorDefs = malloc(ncolors * sizeof(XColor));
2043     memset(allColorDefs, 0, ncolors * sizeof(XColor));
2044 
2045     for (i = 0; i < ncolors; i++)
2046         allColorDefs[i].pixel = i;
2047 
2048     XQueryColors(display, cMap, allColorDefs, ncolors);
2049 
2050     /* Scan through each color, looking for the closest one. */
2051     for (i = 0; i < ncolors; i++)
2052     {
2053         double dist = colorDistance(&allColorDefs[i], &colorDef);
2054 
2055         if (dist < small)
2056         {
2057             best = i;
2058             small = dist;
2059         }
2060     }
2061 
2062     /* Legally try to acquire the shared color- we should loop through
2063        the shortest distances here.  We could sort the map in order
2064        of decreasing distances and loop through it until one works. */
2065 
2066     if (XAllocColor(display, cMap, &allColorDefs[best]))
2067         bestPixel = allColorDefs[best].pixel;
2068 
2069 #if 0
2070     printf("Got %d %d %d, ", allColorDefs[best].red,
2071                              allColorDefs[best].green,
2072                              allColorDefs[best].blue);
2073     printf("That's %f off\n", small);
2074 #endif
2075 
2076     *r = allColorDefs[best].red;
2077     *g = allColorDefs[best].green;
2078     *b = allColorDefs[best].blue;
2079     free(allColorDefs);
2080     return bestPixel;
2081 }
2082 
2083 /*
2084 ** Get the character before position "pos" in buffer "buf"
2085 */
getPrevChar(textBuffer * buf,int pos)2086 static char getPrevChar(textBuffer *buf, int pos)
2087 {
2088     return pos == 0 ? '\0' : BufGetCharacter(buf, pos-1);
2089 }
2090 
2091 /*
2092 ** compile a regular expression and present a user friendly dialog on failure.
2093 */
compileREAndWarn(Widget parent,const char * re)2094 static regexp *compileREAndWarn(Widget parent, const char *re)
2095 {
2096     regexp *compiledRE;
2097     char *compileMsg;
2098 
2099     compiledRE = CompileRE(re, &compileMsg, REDFLT_STANDARD);
2100     if (compiledRE == NULL)
2101     {
2102         char *boundedRe = NEditStrdup(re);
2103         size_t maxLength = DF_MAX_MSG_LENGTH - strlen(compileMsg) - 60;
2104 
2105         /* Prevent buffer overflow in DialogF. If the re is too long,
2106         truncate it and append ... */
2107         if (strlen(boundedRe) > maxLength)
2108         {
2109             strcpy(&boundedRe[maxLength-3], "...");
2110         }
2111 
2112         DialogF(DF_WARN, parent, 1, "Error in Regex",
2113                 "Error in syntax highlighting regular expression:\n%s\n%s",
2114                 "OK", boundedRe, compileMsg);
2115         NEditFree(boundedRe);
2116         return NULL;
2117     }
2118     return compiledRE;
2119 }
2120 
parentStyleOf(const char * parentStyles,int style)2121 static int parentStyleOf(const char *parentStyles, int style)
2122 {
2123     return parentStyles[(unsigned char)style-UNFINISHED_STYLE];
2124 }
2125 
isParentStyle(const char * parentStyles,int style1,int style2)2126 static int isParentStyle(const char *parentStyles, int style1, int style2)
2127 {
2128     int p;
2129 
2130     for (p = parentStyleOf(parentStyles, style2); p != '\0';
2131 	    p = parentStyleOf(parentStyles, p))
2132 	if (style1 == p)
2133 	    return TRUE;
2134     return FALSE;
2135 }
2136 
2137 /*
2138 ** Discriminates patterns which can be used with parseString from those which
2139 ** can't.  Leaf patterns are not suitable for parsing, because patterns
2140 ** contain the expressions used for parsing within the context of their own
2141 ** operation, i.e. the parent pattern initiates, and leaf patterns merely
2142 ** confirm and color.  Returns TRUE if the pattern is suitable for parsing.
2143 */
patternIsParsable(highlightDataRec * pattern)2144 static int patternIsParsable(highlightDataRec *pattern)
2145 {
2146     return pattern != NULL && pattern->subPatternRE != NULL;
2147 }
2148 
2149 /*
2150 ** Back up position pointed to by "pos" enough that parsing from that point
2151 ** on will satisfy context gurantees for pattern matching for modifications
2152 ** at pos.  Returns the style with which to begin parsing.  The caller is
2153 ** guranteed that parsing may safely BEGIN with that style, but not that it
2154 ** will continue at that level.
2155 **
2156 ** This routine can be fooled if a continuous style run of more than one
2157 ** context distance in length is produced by multiple pattern matches which
2158 ** abut, rather than by a single continuous match.  In this  case the
2159 ** position returned by this routine may be a bad starting point which will
2160 ** result in an incorrect re-parse.  However this will happen very rarely,
2161 ** and, if it does, is unlikely to result in incorrect highlighting.
2162 */
findSafeParseRestartPos(textBuffer * buf,windowHighlightData * highlightData,int * pos)2163 static int findSafeParseRestartPos(textBuffer *buf,
2164     	windowHighlightData *highlightData, int *pos)
2165 {
2166     int style, startStyle, runningStyle, checkBackTo, safeParseStart, i;
2167     char *parentStyles = highlightData->parentStyles;
2168     highlightDataRec *pass1Patterns = highlightData->pass1Patterns;
2169     reparseContext *context = &highlightData->contextRequirements;
2170 
2171     /* We must begin at least one context distance back from the change */
2172     *pos = backwardOneContext(buf, context, *pos);
2173 
2174     /* If the new position is outside of any styles or at the beginning of
2175        the buffer, this is a safe place to begin parsing, and we're done */
2176     if (*pos == 0)
2177     	return PLAIN_STYLE;
2178     startStyle = BufGetCharacter(highlightData->styleBuffer, *pos);
2179     if (IS_PLAIN(startStyle))
2180     	return PLAIN_STYLE;
2181 
2182     /*
2183     ** The new position is inside of a styled region, meaning, its pattern
2184     ** could potentially be affected by the modification.
2185     **
2186     ** Follow the style back by enough context to ensure that if we don't find
2187     ** its beginning, at least we've found a safe place to begin parsing
2188     ** within the styled region.
2189     **
2190     ** A safe starting position within a style is either at a style
2191     ** boundary, or far enough from the beginning and end of the style run
2192     ** to ensure that it's not within the start or end expression match
2193     ** (unfortunately, abutting styles can produce false runs so we're not
2194     ** really ensuring it, just making it likely).
2195     */
2196     if (patternIsParsable(patternOfStyle(pass1Patterns, startStyle))) {
2197     	safeParseStart = backwardOneContext(buf, context, *pos);
2198     	checkBackTo = backwardOneContext(buf, context, safeParseStart);
2199     } else {
2200 	safeParseStart = 0;
2201 	checkBackTo = 0;
2202     }
2203     runningStyle = startStyle;
2204     for (i = *pos-1; ; i--) {
2205 
2206     	/* The start of the buffer is certainly a safe place to parse from */
2207     	if (i == 0) {
2208     	    *pos = 0;
2209     	    return PLAIN_STYLE;
2210     	}
2211 
2212     	/* If the style is preceded by a parent style, it's safe to parse
2213 	   with the parent style, provided that the parent is parsable. */
2214     	style = BufGetCharacter(highlightData->styleBuffer, i);
2215 	if (isParentStyle(parentStyles, style, runningStyle)) {
2216 	    if (patternIsParsable(patternOfStyle(pass1Patterns, style))) {
2217 		*pos = i + 1;
2218 		return style;
2219 	    } else {
2220 		/* The parent is not parsable, so well have to continue
2221 		   searching. The parent now becomes the running style. */
2222 	    	runningStyle = style;
2223 	    }
2224     	}
2225 
2226 	/* If the style is preceded by a child style, it's safe to resume
2227 	   parsing with the running style, provided that the running
2228 	   style is parsable. */
2229     	else if (isParentStyle(parentStyles, runningStyle, style)) {
2230 	    if (patternIsParsable
2231 		   (patternOfStyle(pass1Patterns, runningStyle))) {
2232 		*pos = i + 1;
2233 		return runningStyle;
2234 	    }
2235 	    /* Else: keep searching; it's no use switching to the child style
2236 	       because even the running one is not parsable. */
2237     	}
2238 
2239 	/* If the style is preceded by a sibling style, it's safe to resume
2240 	   parsing with the common ancestor style, provided that the ancestor
2241 	   is parsable. Checking for siblings is very hard; checking whether
2242 	   the style has the same parent will probably catch 99% of the cases
2243 	   in practice. */
2244 	else if (runningStyle != style &&
2245 	    isParentStyle(parentStyles,
2246 			  parentStyleOf(parentStyles, runningStyle), style)) {
2247 	    int parentStyle = parentStyleOf(parentStyles, runningStyle);
2248 	    if (patternIsParsable(patternOfStyle(pass1Patterns, parentStyle))) {
2249 		*pos = i + 1;
2250 		return parentStyle;
2251 	    } else {
2252 		/* Switch to the new style */
2253 		runningStyle = style;
2254 	    }
2255     	}
2256 
2257 	/* If the style is preceded by an unrelated style, it's safe to
2258 	   resume parsing with PLAIN_STYLE. (Actually, it isn't, because
2259 	   we didn't really check for all possible sibling relations; but
2260 	   it will be ok in practice.) */
2261 	else if (runningStyle != style) {
2262 	    *pos = i + 1;
2263     	    return PLAIN_STYLE;
2264     	}
2265 
2266 	/* If the style is parsable and didn't change for one whole context
2267 	   distance on either side of safeParseStart, safeParseStart is a
2268 	   reasonable guess at a place to start parsing.
2269 	   Note: No 'else' here! We may come from one of the 'fall-through
2270 	   cases' above. */
2271     	if (i == checkBackTo) {
2272     	    *pos = safeParseStart;
2273 
2274 	    /* We should never return a non-parsable style, because it will
2275 	       result in an internal error. If the current style is not
2276 	       parsable, the pattern set most probably contains a context
2277 	       distance violation. In that case we can only avoid internal
2278 	       errors (by climbing the pattern hierarchy till we find a
2279 	       parsable ancestor) and hope that the highlighting errors are
2280 	       minor. */
2281 	    while (!patternIsParsable
2282 			(patternOfStyle(pass1Patterns, runningStyle)))
2283 	       runningStyle = parentStyleOf(parentStyles, runningStyle);
2284 
2285     	    return runningStyle;
2286     	}
2287     }
2288 }
2289 
2290 /*
2291 ** Return a position far enough back in "buf" from "fromPos" to give patterns
2292 ** their guranteed amount of context for matching (from "context").  If
2293 ** backing up by lines yields the greater distance, the returned position will
2294 ** be to the newline character before the start of the line, rather than to
2295 ** the first character of the line.  (I did this because earlier prototypes of
2296 ** the syntax highlighting code, which were based on single-line context, used
2297 ** this to ensure that line-spanning expressions would be detected.  I think
2298 ** it may reduce some 2 line context requirements to one line, at a cost of
2299 ** only one extra character, but I'm not sure, and my brain hurts from
2300 ** thinking about it).
2301 */
backwardOneContext(textBuffer * buf,reparseContext * context,int fromPos)2302 static int backwardOneContext(textBuffer *buf, reparseContext *context,
2303     	int fromPos)
2304 {
2305     if (context->nLines == 0)
2306     	return max(0, fromPos - context->nChars);
2307     else if (context->nChars == 0)
2308     	return max(0,
2309     	    	BufCountBackwardNLines(buf, fromPos, context->nLines-1) - 1);
2310     else
2311     	return max(0, min(max(0, BufCountBackwardNLines(buf, fromPos,
2312     	    context->nLines-1) -1), fromPos - context->nChars));
2313 }
2314 
2315 /*
2316 ** Return a position far enough forward in "buf" from "fromPos" to ensure
2317 ** that patterns are given their required amount of context for matching
2318 ** (from "context").  If moving forward by lines yields the greater
2319 ** distance, the returned position will be the first character of of the
2320 ** next line, rather than the newline character at the end (see notes in
2321 ** backwardOneContext).
2322 */
forwardOneContext(textBuffer * buf,reparseContext * context,int fromPos)2323 static int forwardOneContext(textBuffer *buf, reparseContext *context,
2324     	int fromPos)
2325 {
2326     if (context->nLines == 0)
2327     	return min(buf->length, fromPos + context->nChars);
2328     else if (context->nChars == 0)
2329     	return min(buf->length,
2330     	    	BufCountForwardNLines(buf, fromPos, context->nLines));
2331     else
2332     	return min(buf->length, max(BufCountForwardNLines(buf, fromPos,
2333     	    	context->nLines), fromPos + context->nChars));
2334 }
2335 
2336 /*
2337 ** Change styles in the portion of "styleString" to "style" where a particular
2338 ** sub-expression, "subExpr", of regular expression "re" applies to the
2339 ** corresponding portion of "string".
2340 */
recolorSubexpr(regexp * re,int subexpr,int style,const char * string,char * styleString)2341 static void recolorSubexpr(regexp *re, int subexpr, int style,
2342     	const char *string, char *styleString)
2343 {
2344     const char *stringPtr;
2345     char *stylePtr;
2346 
2347     stringPtr = re->startp[subexpr];
2348     stylePtr = &styleString[stringPtr - string];
2349     fillStyleString(&stringPtr, &stylePtr, re->endp[subexpr], style, NULL);
2350 }
2351 
2352 /*
2353 ** Search for a pattern in pattern list "patterns" with style "style"
2354 */
patternOfStyle(highlightDataRec * patterns,int style)2355 static highlightDataRec *patternOfStyle(highlightDataRec *patterns, int style)
2356 {
2357     int i;
2358     for (i=0; patterns[i].style!=0; i++)
2359     	if (patterns[i].style == style)
2360     	    return &patterns[i];
2361     if (style == PLAIN_STYLE || style == UNFINISHED_STYLE)
2362 	return &patterns[0];
2363     return NULL;
2364 }
2365 
max(int i1,int i2)2366 static int max(int i1, int i2)
2367 {
2368     return i1 >= i2 ? i1 : i2;
2369 }
2370 
min(int i1,int i2)2371 static int min(int i1, int i2)
2372 {
2373     return i1 <= i2 ? i1 : i2;
2374 }
2375 
indexOfNamedPattern(highlightPattern * patList,int nPats,const char * patName)2376 static int indexOfNamedPattern(highlightPattern *patList, int nPats,
2377     	const char *patName)
2378 {
2379     int i;
2380 
2381     if (patName == NULL)
2382     	return -1;
2383     for (i=0; i<nPats; i++)
2384     	if (!strcmp(patList[i].name, patName))
2385     	    return i;
2386     return -1;
2387 }
2388 
findTopLevelParentIndex(highlightPattern * patList,int nPats,int index)2389 static int findTopLevelParentIndex(highlightPattern *patList, int nPats,
2390     	int index)
2391 {
2392     int topIndex;
2393 
2394     topIndex = index;
2395     while (patList[topIndex].subPatternOf != NULL) {
2396     	topIndex = indexOfNamedPattern(patList, nPats,
2397     	    	patList[topIndex].subPatternOf);
2398 	if (index==topIndex)
2399 	   return -1; /* amai: circular dependency ?! */
2400     }
2401     return topIndex;
2402 }
2403 
2404 /*
2405 ** Re-size (or re-height, anyhow) a window after adding or removing
2406 ** highlight fonts has changed the required vertical spacing (horizontal
2407 ** spacing is determined by the primary font, which doesn't change).
2408 **
2409 ** Note that this messes up the window manager's height increment hint,
2410 ** which must be subsequently reset by UpdateWMSizeHints.
2411 */
updateWindowHeight(WindowInfo * window,int oldFontHeight)2412 static void updateWindowHeight(WindowInfo *window, int oldFontHeight)
2413 {
2414     int i, borderHeight, marginHeight;
2415     Dimension windowHeight, textAreaHeight, textHeight, newWindowHeight;
2416 
2417     /* resize if there's only _one_ document in the window, to avoid
2418        the growing-window bug */
2419     if (NDocuments(window) > 1)
2420     	return;
2421 
2422     /* Decompose the window height into the part devoted to displaying
2423        text (textHeight) and the non-text part (boderHeight) */
2424     XtVaGetValues(window->shell, XmNheight, &windowHeight, NULL);
2425     XtVaGetValues(window->textArea, XmNheight, &textAreaHeight,
2426     	    textNmarginHeight, &marginHeight, NULL);
2427     textHeight = textAreaHeight - 2*marginHeight;
2428     for (i=0; i<window->nPanes; i++) {
2429     	XtVaGetValues(window->textPanes[i], XmNheight, &textAreaHeight, NULL);
2430     	textHeight += textAreaHeight - 2*marginHeight;
2431     }
2432     borderHeight = windowHeight - textHeight;
2433 
2434     /* Calculate a new window height appropriate for the new font */
2435     newWindowHeight = (textHeight*getFontHeight(window)) / oldFontHeight +
2436     	    borderHeight;
2437 
2438     /* Many window managers enforce window size increments even on client resize
2439        requests.  Our height increment is probably wrong because it is still
2440        set for the previous font.  Set the new height in advance, before
2441        attempting to resize. */
2442     XtVaSetValues(window->shell, XmNheightInc, getFontHeight(window), NULL);
2443 
2444     /* Re-size the window */
2445     XtVaSetValues(window->shell, XmNheight, newWindowHeight, NULL);
2446 }
2447 
2448 /*
2449 ** Find the height currently being used to display text, which is
2450 ** a composite of all of the active highlighting fonts as determined by the
2451 ** text display component
2452 */
getFontHeight(WindowInfo * window)2453 static int getFontHeight(WindowInfo *window)
2454 {
2455     textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2456 
2457     return textD->ascent + textD->descent;
2458 }
2459