001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.cnd.editor.shell;
043:
044: import org.netbeans.editor.Syntax;
045: import org.netbeans.editor.TokenID;
046: import java.util.Stack;
047:
048: /**
049: * Syntax analyzes for Shell files.
050: * Tokens and internal states are given below.
051: *
052: */
053:
054: public class ShellSyntax extends Syntax {
055:
056: //internal analyzer states
057: //numbers assigned to states are not important as long as they are unique
058: private static final int AFTER_COLON = 1; // after ':'
059: private static final int AFTER_DOLLAR = 2; // after '$'
060: private static final int AFTER_BSLASH = 3; // after '\\'
061: private static final int AFTER_PLUS = 4; // after '+'
062: private static final int IN_STRING = 5; // inside string constant
063: private static final int IN_STRING_AFTER_BSLASH = 6; // inside string constant
064: // after backslash
065: private static final int IN_MACRO = 7; // inside macro
066: private static final int IN_MACRO_AFTER_DELIM = 8; // inside macro $() ${}
067: private static final int IN_WHITESPACE = 9; // inside white space
068: private static final int IN_LINE_COMMENT = 10; // inside line comment
069: private static final int IN_IDENTIFIER = 11; // inside identifier
070: private static final int IN_DOT_IDENTIFIER = 12; // inside .identifier
071: private static final int IN_COLON_IDENTIFIER = 13; // inside :identifier
072: private static final int IN_SHELL_COMMAND = 14; // inside shell command
073: private static final int START_COMMENT = 15; // #...
074:
075: /**specifies if the string is defined in double quotes
076: * or single quote
077: */
078: private static boolean STRING_IN_DOUBLE_QUOTE = true;
079:
080: /**specifies how many macro names has the current macro in it
081: * e.g. the macro $(USER-$(IDE)) is valid and nested_macro_num = 1
082: */
083: private static Integer BRACE_DELIM = new Integer(1);
084: private static Integer PARAN_DELIM = new Integer(2);
085: private Stack macroDelimStack = new Stack();
086:
087: /** constructor
088: */
089: public ShellSyntax() {
090: tokenContextPath = ShellTokenContext.contextPath;
091: }
092:
093: /** This is core function of analyzer and it returns either the token-id
094: * or null to indicate that the end of buffer was found.
095: * The function scans the active character and does one or more
096: * of the following actions:
097: * 1. change internal analyzer state
098: * 2. set the token-context-path and return token-id
099: * 3. adjust current position to signal different end of token;
100: * the character that offset points to is not included in the token
101: */
102: protected TokenID parseToken() {
103: char actChar;
104: //WHILE OFFSET
105: while (offset < stopOffset) {
106: actChar = buffer[offset];
107:
108: //STATE SWITCH
109: switch (state) {
110: //INIT STATE
111: case INIT:
112: switch (actChar) {
113: case '#': // Shell comments begin with a # and last to the end of line
114: state = START_COMMENT;
115: break;
116: case ':':
117: state = AFTER_COLON;
118: break;
119: case '.':
120: state = IN_DOT_IDENTIFIER;
121: break;
122: case '$':
123: state = AFTER_DOLLAR;
124: break;
125: case '\\':
126: state = AFTER_BSLASH;
127: break;
128: case '"':
129: state = IN_STRING;
130: STRING_IN_DOUBLE_QUOTE = true;
131: break;
132: case '\'':
133: state = IN_STRING;
134: STRING_IN_DOUBLE_QUOTE = false;
135: break;
136: case '+':
137: state = AFTER_PLUS;
138: break;
139: case '-':
140: offset++;
141: return ShellTokenContext.RULES_MINUS;
142: case '@':
143: offset++;
144: return ShellTokenContext.RULES_AT;
145: case '?':
146: offset++;
147: return ShellTokenContext.RULES_QUESTION_MARK;
148: case '!':
149: offset++;
150: return ShellTokenContext.RULES_EXCLAMATION;
151: case '%':
152: offset++;
153: return ShellTokenContext.TARGET_PERCENT;
154: case '=':
155: offset++;
156: return ShellTokenContext.MACRO_OP_EQUALS;
157: case '(':
158: offset++;
159: return ShellTokenContext.MACRO_LPAREN;
160: case ')':
161: offset++;
162: return ShellTokenContext.MACRO_RPAREN;
163: case '{':
164: offset++;
165: return ShellTokenContext.MACRO_LBRACE;
166: case '}':
167: offset++;
168: return ShellTokenContext.MACRO_RBRACE;
169:
170: default:
171: // Check for whitespace
172: if (Character.isWhitespace(actChar)) {
173: state = IN_WHITESPACE;
174: break;
175: }
176:
177: // Check for identifier
178: // To find out why we're using isJAVAidentifier
179: // here, grep for isJavaIdentifierStart in
180: // CCSyntax.java
181: if (Character.isJavaIdentifierStart(actChar)) {
182: state = IN_IDENTIFIER;
183: break;
184: }
185:
186: //for the sake of the syntax-highlighting
187: //assume non-identifiers and non language specific characters
188: //are identifiers
189: offset++;
190: return ShellTokenContext.IDENTIFIER;
191:
192: }//switch(actchar)
193: break;
194: //END INIT STATE
195:
196: case IN_WHITESPACE: // white space
197: if (!Character.isWhitespace(actChar)) {
198: state = INIT;
199: return ShellTokenContext.WHITESPACE;
200: }
201: break;
202:
203: case START_COMMENT:
204: if (actChar == '!')
205: state = IN_SHELL_COMMAND; // #! /bin/bash
206: else
207: state = IN_LINE_COMMENT; // # whatever...
208: break;
209:
210: case IN_LINE_COMMENT:
211: switch (actChar) {
212: case '\n':
213: state = INIT;
214: offset++;
215: return ShellTokenContext.LINE_COMMENT;
216: }//switch IN_LINE_COMMENT
217: break;
218:
219: case IN_SHELL_COMMAND:
220: switch (actChar) {
221: case '\n':
222: state = INIT;
223: offset++;
224: return ShellTokenContext.SHELL_COMMAND;
225: }
226: break;
227:
228: case AFTER_BSLASH:
229: switch (actChar) {
230: case '$':
231: offset++;
232: state = INIT;
233: return ShellTokenContext.MACRO_ESCAPED_DOLLAR;
234: default:
235: state = IN_IDENTIFIER;
236: offset--; //go back and evaluate the character
237: break;
238: }//switch AFTER_BSLASH
239: break;
240:
241: case AFTER_DOLLAR:
242: switch (actChar) {
243: case '$':
244: offset++;
245: state = INIT;
246: return ShellTokenContext.MACRO_DOLAR_REFERENCE;
247: case '*':
248: offset++;
249: state = INIT;
250: return ShellTokenContext.MACRO_DYN_TARGET_BASENAME;
251: case '<':
252: offset++;
253: state = INIT;
254: return ShellTokenContext.MACRO_DYN_DEPENDENCY_FILENAME;
255: case '@':
256: offset++;
257: state = INIT;
258: return ShellTokenContext.MACRO_DYN_CURRENTTARGET;
259: case '?':
260: offset++;
261: state = INIT;
262: return ShellTokenContext.MACRO_DYN_DEPENDENCY_LIST;
263: case '%':
264: offset++;
265: state = INIT;
266: return ShellTokenContext.MACRO_DYN_LIBRARYNAME;
267: case '(':
268: case '{':
269: offset--; //go back and evaluate the character
270: state = IN_MACRO_AFTER_DELIM;
271: break;
272: default:
273: state = IN_MACRO;
274: break;
275: }//switch AFTER_DOLLAR
276: break;
277:
278: case IN_MACRO:
279: if (!(Character.isJavaIdentifierPart(actChar))) {
280: switch (actChar) {
281: case '.': //allowable macroname characters
282: case '-':
283: break;
284: default:
285: state = INIT;
286: return ShellTokenContext.MACRO_LITERAL;
287: }//switch IN_MACRO
288: }
289: break;
290:
291: case IN_MACRO_AFTER_DELIM:
292: if (!(Character.isJavaIdentifierPart(actChar))) {
293: switch (actChar) {
294: case '(':
295: macroDelimStack.push(PARAN_DELIM);
296: break;
297: case '{':
298: macroDelimStack.push(BRACE_DELIM);
299: break;
300: case ')':
301: case '}':
302: Integer delim = (actChar == ')') ? PARAN_DELIM
303: : BRACE_DELIM;
304: if (!macroDelimStack.empty()) {
305: if (((Integer) macroDelimStack.pop()) == delim) {
306: if (macroDelimStack.empty()) {
307: state = INIT;
308: offset++;
309: return ShellTokenContext.MACRO_LITERAL;
310: } else
311: break;
312: }
313: }
314: //this statement is reached only if there is an error in macro string
315: state = INIT;
316: offset++;
317: macroDelimStack = new Stack();
318: return ShellTokenContext.ERR_INCOMPLETE_MACRO_LITERAL;
319: default:
320: if (macroDelimStack.empty()) {
321: state = INIT;
322: return ShellTokenContext.MACRO_LITERAL;
323: } else {
324: // allow everything in ()
325: state = INIT;
326: return ShellTokenContext.MACRO_LITERAL;
327: }
328: }//switch IN_MACRO_AFTER_DELIM
329: }
330: break;
331:
332: case AFTER_PLUS:
333: switch (actChar) {
334: case '=':
335: offset++;
336: state = INIT;
337: return ShellTokenContext.MACRO_OP_APPEND;
338: default:
339: state = INIT;
340: offset++;
341: return ShellTokenContext.RULES_PLUS;
342: }//switch AFTER_PLUS
343: //break;
344:
345: case AFTER_COLON:
346: switch (actChar) {
347: case '=':
348: offset++;
349: state = INIT;
350: return ShellTokenContext.MACRO_OP_CONDITIONAL;
351: case ':':
352: offset++;
353: state = INIT;
354: return ShellTokenContext.TARGET_DOUBLE_COLON;
355: case 's':
356: case 'S':
357: state = IN_COLON_IDENTIFIER;
358: break;
359: default:
360: state = INIT;
361: return ShellTokenContext.TARGET_COLON;
362: }//switch AFTER_COLON
363: break;
364:
365: case IN_COLON_IDENTIFIER:
366: if (!Character.isJavaIdentifierPart(actChar)) {
367: state = INIT;
368: TokenID tid = matchKeyword(buffer, tokenOffset,
369: offset - tokenOffset);
370: if (tid != null)
371: return tid;
372: else {
373: //highlight the first colon and reevaluate the rest of the string since colon
374: offset = tokenOffset + 1;
375: return ShellTokenContext.TARGET_COLON;
376: }
377: }
378: break;
379:
380: case IN_DOT_IDENTIFIER:
381: if (!Character.isJavaIdentifierPart(actChar)) {
382: state = INIT;
383: TokenID tid = matchKeyword(buffer, tokenOffset,
384: offset - tokenOffset);
385: if (tid != null)
386: return tid;
387: else {
388: //highlight the first dot and reevaluate the rest of the string since dot
389: offset = tokenOffset + 1;
390: state = INIT;
391: return ShellTokenContext.IDENTIFIER;
392: }
393: }
394: break;
395:
396: case IN_IDENTIFIER:
397: // To find out why we're using isJAVAidentifier
398: // here, grep for isJavaIdentifierStart in
399: // CCSyntax.java
400: if (!(Character.isJavaIdentifierPart(actChar))) {
401: state = INIT;
402: TokenID tid = matchKeyword(buffer, tokenOffset,
403: offset - tokenOffset);
404: return (tid != null) ? tid
405: : ShellTokenContext.IDENTIFIER;
406: }
407: break;
408:
409: case IN_STRING:
410: switch (actChar) {
411: case '\\':
412: state = IN_STRING_AFTER_BSLASH;
413: break;
414: case '\n':
415: state = INIT;
416: offset++;
417: supposedTokenID = ShellTokenContext.STRING_LITERAL;
418: return supposedTokenID;
419: case '"':
420: if (STRING_IN_DOUBLE_QUOTE) {
421: offset++;
422: state = INIT;
423: return ShellTokenContext.STRING_LITERAL;
424: }
425: break;
426: case '\'':
427: if (!STRING_IN_DOUBLE_QUOTE) {
428: offset++;
429: state = INIT;
430: return ShellTokenContext.STRING_LITERAL;
431: }
432: break;
433: } //switch IN_STRING
434: break;
435:
436: case IN_STRING_AFTER_BSLASH:
437: switch (actChar) {
438: case '"':
439: case '\'':
440: case '\\':
441: break; //ignore the meaning of these characters
442: default:
443: offset--; //go back and evaluate the character
444: break;
445: }//switch IN_STRING_AFTER_BSLASH:
446: state = IN_STRING;
447: break;
448:
449: } // end of switch(state)
450: //END STATE SWITCH
451: offset++;
452: } //while(offset...)
453: //END WHILE OFFSET
454:
455: /** At this stage there's no more text in the scanned buffer.
456: * Scanner first checks whether this is completely the last
457: * available buffer.
458: */
459: if (lastBuffer) {
460: switch (state) {
461: case IN_WHITESPACE:
462: state = INIT;
463: return ShellTokenContext.WHITESPACE;
464: case IN_DOT_IDENTIFIER:
465: case IN_COLON_IDENTIFIER:
466: case IN_IDENTIFIER:
467: state = INIT;
468: TokenID kwd = matchKeyword(buffer, tokenOffset, offset
469: - tokenOffset);
470: return (kwd != null) ? kwd
471: : ShellTokenContext.IDENTIFIER;
472: case IN_STRING:
473: case IN_STRING_AFTER_BSLASH:
474: return ShellTokenContext.STRING_LITERAL; // hold the state
475: case IN_MACRO:
476: case IN_MACRO_AFTER_DELIM:
477: state = INIT;
478: return ShellTokenContext.MACRO_LITERAL;
479: case AFTER_BSLASH:
480: state = INIT;
481: return ShellTokenContext.IDENTIFIER;
482: case AFTER_PLUS:
483: state = INIT;
484: return ShellTokenContext.RULES_PLUS;
485: case AFTER_DOLLAR:
486: state = INIT;
487: return ShellTokenContext.MACRO_DOLLAR;
488: case AFTER_COLON:
489: state = INIT;
490: return ShellTokenContext.TARGET_COLON;
491: case IN_LINE_COMMENT:
492: return ShellTokenContext.LINE_COMMENT; // stay in line-comment state
493: case IN_SHELL_COMMAND:
494: return ShellTokenContext.SHELL_COMMAND; // stay in shell-command state
495: } //switch
496: }//if (lastbuffer)
497:
498: /* At this stage there's no more text in the scanned buffer, but
499: * this buffer is not the last so the scan will continue on another buffer.
500: * The scanner tries to minimize the amount of characters
501: * that will be prescanned in the next buffer by returning the token
502: * where possible.
503: */
504: switch (state) {
505: case IN_WHITESPACE:
506: return ShellTokenContext.WHITESPACE;
507: }
508:
509: return null; // nothing found
510: }
511:
512: public String getStateName(int stateNumber) {
513: switch (stateNumber) {
514: case AFTER_COLON:
515: return "AFTER_COLON"; //NOI18N
516: case AFTER_DOLLAR:
517: return "AFTER_DOLLAR"; //NOI18N
518: case AFTER_BSLASH:
519: return "AFTER_BSLASH"; //NOI18N
520: case AFTER_PLUS:
521: return "AFTER_PLUS"; //NOI18N
522: case IN_STRING:
523: return "IN_STRING"; //NOI18N
524: case IN_STRING_AFTER_BSLASH:
525: return "IN_STRING_AFTER_BSLASH"; //NOI18N
526: case IN_MACRO:
527: return "IN_MACRO"; //NOI18N
528: case IN_MACRO_AFTER_DELIM:
529: return "IN_MACRO_AFTER_DELIM"; //NOI18N
530: case IN_LINE_COMMENT:
531: return "IN_LINE_COMMENT"; //NOI18N
532: case IN_SHELL_COMMAND:
533: return "IN_SHELL_COMMAND"; //NOI18N
534: case IN_IDENTIFIER:
535: return "IN_IDENTIFIER"; //NOI18N
536: case IN_DOT_IDENTIFIER:
537: return "IN_DOT_IDENTIFIER"; //NOI18N
538: case IN_COLON_IDENTIFIER:
539: return "IN_COLON_IDENTIFIER"; //NOI18N
540: case IN_WHITESPACE:
541: return "IN_WHITESPACE"; //NOI18N
542: default:
543: return super .getStateName(stateNumber);
544: }
545: }
546:
547: public static TokenID matchKeyword(char[] buffer, int offset,
548: int len) {
549: if ((len <= 1) || (len > 17))
550: return null;
551:
552: //BEGIN MOTHER SWITCH
553: switch (Character.toLowerCase(buffer[offset++])) {
554: //DOT
555: //.DEFAULT .DONE .FAILED .GET_POSIX .IGNORE .INIT .KEEP_STATE
556: //.KEEP_STATE_FILE .MAKE_VERSION .NO_PARALLEL .PARALLEL .POSIX .PRECIOUS
557: //.SCCS_GET .SCCS_GET_POSIX .SILENT .SUFFIXES .WAIT
558: case '.':
559: if ((len < 5) || (len > 16))
560: return null;
561: switch (Character.toLowerCase(buffer[offset++])) {
562: case 'd': // .DEFAULT .DONE
563: switch (Character.toLowerCase(buffer[offset++])) {
564: case 'e': // .DEFAULT
565: return (len == 8
566: && Character.toLowerCase(buffer[offset++]) == 'f'
567: && Character.toLowerCase(buffer[offset++]) == 'a'
568: && Character.toLowerCase(buffer[offset++]) == 'u'
569: && Character.toLowerCase(buffer[offset++]) == 'l' && Character
570: .toLowerCase(buffer[offset++]) == 't') ? ShellTokenContext.TARGET_DEFAULT
571: : null;
572: case 'o': // .DONE
573: return (len == 5
574: && Character.toLowerCase(buffer[offset++]) == 'n' && Character
575: .toLowerCase(buffer[offset++]) == 'e') ? ShellTokenContext.TARGET_DONE
576: : null;
577: default:
578: return null;
579: }//switch .d
580:
581: case 'f': //.FAILED
582: return (len == 7
583: && Character.toLowerCase(buffer[offset++]) == 'a'
584: && Character.toLowerCase(buffer[offset++]) == 'i'
585: && Character.toLowerCase(buffer[offset++]) == 'l'
586: && Character.toLowerCase(buffer[offset++]) == 'e' && Character
587: .toLowerCase(buffer[offset++]) == 'd') ? ShellTokenContext.TARGET_FAILED
588: : null;
589:
590: case 'g': //.GET_POSIX
591: return (len == 10
592: && Character.toLowerCase(buffer[offset++]) == 'e'
593: && Character.toLowerCase(buffer[offset++]) == 't'
594: && Character.toLowerCase(buffer[offset++]) == '_'
595: && Character.toLowerCase(buffer[offset++]) == 'p'
596: && Character.toLowerCase(buffer[offset++]) == 'o'
597: && Character.toLowerCase(buffer[offset++]) == 's'
598: && Character.toLowerCase(buffer[offset++]) == 'i' && Character
599: .toLowerCase(buffer[offset++]) == 'x') ? ShellTokenContext.TARGET_GETPOSIX
600: : null;
601:
602: case 'i': //.IGNORE .INIT
603: switch (Character.toLowerCase(buffer[offset++])) {
604: case 'g': //.IGNORE
605: return (len == 7
606: && Character.toLowerCase(buffer[offset++]) == 'n'
607: && Character.toLowerCase(buffer[offset++]) == 'o'
608: && Character.toLowerCase(buffer[offset++]) == 'r' && Character
609: .toLowerCase(buffer[offset++]) == 'e') ? ShellTokenContext.TARGET_IGNORE
610: : null;
611: case 'n': //.INIT
612: return (len == 5
613: && Character.toLowerCase(buffer[offset++]) == 'i' && Character
614: .toLowerCase(buffer[offset++]) == 't') ? ShellTokenContext.TARGET_INIT
615: : null;
616: default:
617: return null;
618: } //switch .i
619:
620: case 'k': //.KEEP_STATE .KEEP_STATE_FILE
621: if (len >= 11
622: && Character.toLowerCase(buffer[offset++]) == 'e'
623: && Character.toLowerCase(buffer[offset++]) == 'e'
624: && Character.toLowerCase(buffer[offset++]) == 'p'
625: && Character.toLowerCase(buffer[offset++]) == '_'
626: && Character.toLowerCase(buffer[offset++]) == 's'
627: && Character.toLowerCase(buffer[offset++]) == 't'
628: && Character.toLowerCase(buffer[offset++]) == 'a'
629: && Character.toLowerCase(buffer[offset++]) == 't'
630: && Character.toLowerCase(buffer[offset++]) == 'e') {
631:
632: if (len == 11)
633: return ShellTokenContext.TARGET_KEEPSTATE;
634:
635: switch (Character.toLowerCase(buffer[offset++])) {
636: case '_': //.KEEP_STATE_FILE
637: return (len == 16
638: && Character
639: .toLowerCase(buffer[offset++]) == 'f'
640: && Character
641: .toLowerCase(buffer[offset++]) == 'i'
642: && Character
643: .toLowerCase(buffer[offset++]) == 'l' && Character
644: .toLowerCase(buffer[offset++]) == 'e') ? ShellTokenContext.TARGET_KEEPSTATEFILE
645: : null;
646: default:
647: return null;
648: }//switch .KEEP_STATE
649: }//if
650: else {
651: return null;
652: }
653:
654: case 'm': //.MAKE_VERSION
655: return (len == 13
656: && Character.toLowerCase(buffer[offset++]) == 'a'
657: && Character.toLowerCase(buffer[offset++]) == 'k'
658: && Character.toLowerCase(buffer[offset++]) == 'e'
659: && Character.toLowerCase(buffer[offset++]) == '_'
660: && Character.toLowerCase(buffer[offset++]) == 'v'
661: && Character.toLowerCase(buffer[offset++]) == 'e'
662: && Character.toLowerCase(buffer[offset++]) == 'r'
663: && Character.toLowerCase(buffer[offset++]) == 's'
664: && Character.toLowerCase(buffer[offset++]) == 'i'
665: && Character.toLowerCase(buffer[offset++]) == 'o' && Character
666: .toLowerCase(buffer[offset++]) == 'n') ? ShellTokenContext.TARGET_MAKEVERSION
667: : null;
668:
669: case 'n': //.NO_PARALLEL
670: return (len == 12
671: && Character.toLowerCase(buffer[offset++]) == 'o'
672: && Character.toLowerCase(buffer[offset++]) == '_'
673: && Character.toLowerCase(buffer[offset++]) == 'p'
674: && Character.toLowerCase(buffer[offset++]) == 'a'
675: && Character.toLowerCase(buffer[offset++]) == 'r'
676: && Character.toLowerCase(buffer[offset++]) == 'a'
677: && Character.toLowerCase(buffer[offset++]) == 'l'
678: && Character.toLowerCase(buffer[offset++]) == 'l'
679: && Character.toLowerCase(buffer[offset++]) == 'e' && Character
680: .toLowerCase(buffer[offset++]) == 'l') ? ShellTokenContext.TARGET_NOPARALLEL
681: : null;
682:
683: case 'p': // .PARALLEL .POSIX .PRECIOUS
684: switch (Character.toLowerCase(buffer[offset++])) {
685: case 'a': //.PARALLEL
686: return (len == 9
687: && Character.toLowerCase(buffer[offset++]) == 'r'
688: && Character.toLowerCase(buffer[offset++]) == 'a'
689: && Character.toLowerCase(buffer[offset++]) == 'l'
690: && Character.toLowerCase(buffer[offset++]) == 'l'
691: && Character.toLowerCase(buffer[offset++]) == 'e' && Character
692: .toLowerCase(buffer[offset++]) == 'l') ? ShellTokenContext.TARGET_PARALLEL
693: : null;
694: case 'o': //.POSIX
695: return (len == 6
696: && Character.toLowerCase(buffer[offset++]) == 's'
697: && Character.toLowerCase(buffer[offset++]) == 'i' && Character
698: .toLowerCase(buffer[offset++]) == 'x') ? ShellTokenContext.TARGET_POSIX
699: : null;
700: case 'r': //.PRECIOUS
701: return (len == 9
702: && Character.toLowerCase(buffer[offset++]) == 'e'
703: && Character.toLowerCase(buffer[offset++]) == 'c'
704: && Character.toLowerCase(buffer[offset++]) == 'i'
705: && Character.toLowerCase(buffer[offset++]) == 'o'
706: && Character.toLowerCase(buffer[offset++]) == 'u' && Character
707: .toLowerCase(buffer[offset++]) == 's') ? ShellTokenContext.TARGET_PRECIOUS
708: : null;
709: default:
710: return null;
711: }//switch .p
712:
713: case 's': // .SCCS_GET .SCCS_GET_POSIX .SILENT .SUFFIXES
714: switch (Character.toLowerCase(buffer[offset++])) {
715: case 'c': //.SCCS_GET .SCCS_GET_POSIX
716: if (len >= 9
717: && Character.toLowerCase(buffer[offset++]) == 'c'
718: && Character.toLowerCase(buffer[offset++]) == 's'
719: && Character.toLowerCase(buffer[offset++]) == '_'
720: && Character.toLowerCase(buffer[offset++]) == 'g'
721: && Character.toLowerCase(buffer[offset++]) == 'e'
722: && Character.toLowerCase(buffer[offset++]) == 't') {
723:
724: if (len == 9)
725: return ShellTokenContext.TARGET_SCCSGET;
726:
727: switch (Character.toLowerCase(buffer[offset++])) {
728: case '_': //.SCCS_GET_POSIX
729: return (len == 15
730: && Character
731: .toLowerCase(buffer[offset++]) == 'p'
732: && Character
733: .toLowerCase(buffer[offset++]) == 'o'
734: && Character
735: .toLowerCase(buffer[offset++]) == 's'
736: && Character
737: .toLowerCase(buffer[offset++]) == 'i' && Character
738: .toLowerCase(buffer[offset++]) == 'x') ? ShellTokenContext.TARGET_SCCSGETPOSIX
739: : null;
740: default:
741: return null;
742: }//switch .SCCS_GET
743: }//if .sc
744: else {
745: return null;
746: }//else .sc
747: case 'i': //.SILENT
748: return (len == 7
749: && Character.toLowerCase(buffer[offset++]) == 'l'
750: && Character.toLowerCase(buffer[offset++]) == 'e'
751: && Character.toLowerCase(buffer[offset++]) == 'n' && Character
752: .toLowerCase(buffer[offset++]) == 't') ? ShellTokenContext.TARGET_SILENT
753: : null;
754: case 'u': //.SUFFIXES
755: return (len == 9
756: && Character.toLowerCase(buffer[offset++]) == 'f'
757: && Character.toLowerCase(buffer[offset++]) == 'f'
758: && Character.toLowerCase(buffer[offset++]) == 'i'
759: && Character.toLowerCase(buffer[offset++]) == 'x'
760: && Character.toLowerCase(buffer[offset++]) == 'e' && Character
761: .toLowerCase(buffer[offset++]) == 's') ? ShellTokenContext.TARGET_SUFFIXES
762: : null;
763: default:
764: return null;
765: }//switch .s
766:
767: case 'w': //.WAIT
768: return (len == 5
769: && Character.toLowerCase(buffer[offset++]) == 'a'
770: && Character.toLowerCase(buffer[offset++]) == 'i' && Character
771: .toLowerCase(buffer[offset++]) == 't') ? ShellTokenContext.TARGET_WAIT
772: : null;
773:
774: default:
775: return null;
776: }//switch dot
777: //END DOT
778:
779: //:
780: //:sh
781: case ':':
782: return (len == 3
783: && Character.toLowerCase(buffer[offset++]) == 's' && Character
784: .toLowerCase(buffer[offset++]) == 'h') ? ShellTokenContext.MACRO_COMMAND_SUBSTITUTE
785: : null;
786: //END :
787:
788: //I
789: //include
790: case 'i':
791: return (len == 7
792: && Character.toLowerCase(buffer[offset++]) == 'n'
793: && Character.toLowerCase(buffer[offset++]) == 'c'
794: && Character.toLowerCase(buffer[offset++]) == 'l'
795: && Character.toLowerCase(buffer[offset++]) == 'u'
796: && Character.toLowerCase(buffer[offset++]) == 'd' && Character
797: .toLowerCase(buffer[offset++]) == 'e') ? ShellTokenContext.GLOBAL_INCLUDE
798: : null;
799: //END I
800:
801: default:
802: return null;
803: } //switch
804: //END MOTHER SWITCH
805: } //matchKeyword
806:
807: }
|