(linenum→info "unix/slp.c:2238")

binutils/2.18/binutils/nlmheader.y

    1: %{/* nlmheader.y - parse NLM header specification keywords.
    2:      Copyright 1993, 1994, 1995, 1997, 1998, 2001, 2002, 2003, 2007
    3:      Free Software Foundation, Inc.
    4: 
    5:      This file is part of GNU Binutils.
    6: 
    7:      This program is free software; you can redistribute it and/or modify
    8:      it under the terms of the GNU General Public License as published by
    9:      the Free Software Foundation; either version 3 of the License, or
   10:      (at your option) any later version.
   11: 
   12:      This program is distributed in the hope that it will be useful,
   13:      but WITHOUT ANY WARRANTY; without even the implied warranty of
   14:      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15:      GNU General Public License for more details.
   16: 
   17:      You should have received a copy of the GNU General Public License
   18:      along with this program; if not, write to the Free Software
   19:      Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
   20:      MA 02110-1301, USA.  */
   21: 
   22: /* Written by Ian Lance Taylor <ian@cygnus.com>.
   23: 
   24:    This bison file parses the commands recognized by the NetWare NLM
   25:    linker, except for lists of object files.  It stores the
   26:    information in global variables.
   27: 
   28:    This implementation is based on the description in the NetWare Tool
   29:    Maker Specification manual, edition 1.0.  */
   30: 
   31: #include "sysdep.h"
   32: #include "safe-ctype.h"
   33: #include "bfd.h"
   34: #include "nlm/common.h"
   35: #include "nlm/internal.h"
   36: #include "bucomm.h"
   37: #include "nlmconv.h"
   38: 
   39: /* Information is stored in the structures pointed to by these
   40:    variables.  */
   41: 
   42: Nlm_Internal_Fixed_Header *fixed_hdr;
   43: Nlm_Internal_Variable_Header *var_hdr;
   44: Nlm_Internal_Version_Header *version_hdr;
   45: Nlm_Internal_Copyright_Header *copyright_hdr;
   46: Nlm_Internal_Extended_Header *extended_hdr;
   47: 
   48: /* Procedure named by CHECK.  */
   49: char *check_procedure;
   50: /* File named by CUSTOM.  */
   51: char *custom_file;
   52: /* Whether to generate debugging information (DEBUG).  */
   53: bfd_boolean debug_info;
   54: /* Procedure named by EXIT.  */
   55: char *exit_procedure;
   56: /* Exported symbols (EXPORT).  */
   57: struct string_list *export_symbols;
   58: /* List of files from INPUT.  */
   59: struct string_list *input_files;
   60: /* Map file name (MAP, FULLMAP).  */
   61: char *map_file;
   62: /* Whether a full map has been requested (FULLMAP).  */
   63: bfd_boolean full_map;
   64: /* File named by HELP.  */
   65: char *help_file;
   66: /* Imported symbols (IMPORT).  */
   67: struct string_list *import_symbols;
   68: /* File named by MESSAGES.  */
   69: char *message_file;
   70: /* Autoload module list (MODULE).  */
   71: struct string_list *modules;
   72: /* File named by OUTPUT.  */
   73: char *output_file;
   74: /* File named by SHARELIB.  */
   75: char *sharelib_file;
   76: /* Start procedure name (START).  */
   77: char *start_procedure;
   78: /* VERBOSE.  */
   79: bfd_boolean verbose;
   80: /* RPC description file (XDCDATA).  */
   81: char *rpc_file;
   82: 
   83: /* The number of serious errors that have occurred.  */
   84: int parse_errors;
   85: 
   86: /* The current symbol prefix when reading a list of import or export
   87:    symbols.  */
   88: static char *symbol_prefix;
   89: 
   90: /* Parser error message handler.  */
   91: #define yyerror(msg) nlmheader_error (msg);
   92: 
   93: /* Local functions.  */
   94: static int yylex (void);
   95: static void nlmlex_file_push (const char *);
   96: static bfd_boolean nlmlex_file_open (const char *);
   97: static int nlmlex_buf_init (void);
   98: static char nlmlex_buf_add (int);
   99: static long nlmlex_get_number (const char *);
  100: static void nlmheader_identify (void);
  101: static void nlmheader_warn (const char *, int);
  102: static void nlmheader_error (const char *);
  103: static struct string_list * string_list_cons (char *, struct string_list *);
  104: static struct string_list * string_list_append (struct string_list *,
  105:                                                 struct string_list *);
  106: static struct string_list * string_list_append1 (struct string_list *,
  107:                                                  char *);
  108: static char *xstrdup (const char *);
  109: 
  110: %}
  111: 
  112: %union
  113: {
  114:   char *string;
  115:   struct string_list *list;
  116: };
  117: 
  118: /* The reserved words.  */
  119: 
  120: %token CHECK CODESTART COPYRIGHT CUSTOM DATE DEBUG DESCRIPTION EXIT
  121: %token EXPORT FLAG_ON FLAG_OFF FULLMAP HELP IMPORT INPUT MAP MESSAGES
  122: %token MODULE MULTIPLE OS_DOMAIN OUTPUT PSEUDOPREEMPTION REENTRANT
  123: %token SCREENNAME SHARELIB STACK START SYNCHRONIZE
  124: %token THREADNAME TYPE VERBOSE VERSIONK XDCDATA
  125: 
  126: /* Arguments.  */
  127: 
  128: %token <string> STRING
  129: %token <string> QUOTED_STRING
  130: 
  131: /* Typed non-terminals.  */
  132: %type <list> symbol_list_opt symbol_list string_list
  133: %type <string> symbol
  134: 
  135: %%
  136: 
  137: /* Keywords must start in the leftmost column of the file.  Arguments
  138:    may appear anywhere else.  The lexer uses this to determine what
  139:    token to return, so we don't have to worry about it here.  */
  140: 
  141: /* The entire file is just a list of commands.  */
  142: 
  143: file:
  144:           commands
  145:         ;
  146: 
  147: /* A possibly empty list of commands.  */
  148: 
  149: commands:
  150:           /* May be empty.  */
  151:         | command commands
  152:         ;
  153: 
  154: /* A single command.  There is where most of the work takes place.  */
  155: 
  156: command:
  157:           CHECK STRING
  158:           {
  159:             check_procedure = $2;
  160:           }
  161:         | CODESTART STRING
  162:           {
  163:             nlmheader_warn (_("CODESTART is not implemented; sorry"), -1);
  164:             free ($2);
  165:           }
  166:         | COPYRIGHT QUOTED_STRING
  167:           {
  168:             int len;
  169: 
  170:             strncpy (copyright_hdr->stamp, "CoPyRiGhT=", 10);
  171:             len = strlen ($2);
  172:             if (len >= NLM_MAX_COPYRIGHT_MESSAGE_LENGTH)
  173:               {
  174:                 nlmheader_warn (_("copyright string is too long"),
  175:                                 NLM_MAX_COPYRIGHT_MESSAGE_LENGTH - 1);
  176:                 len = NLM_MAX_COPYRIGHT_MESSAGE_LENGTH - 1;
  177:               }
  178:             copyright_hdr->copyrightMessageLength = len;
  179:             strncpy (copyright_hdr->copyrightMessage, $2, len);
  180:             copyright_hdr->copyrightMessage[len] = '\0';
  181:             free ($2);
  182:           }
  183:         | CUSTOM STRING
  184:           {
  185:             custom_file = $2;
  186:           }
  187:         | DATE STRING STRING STRING
  188:           {
  189:             /* We don't set the version stamp here, because we use the
  190:                version stamp to detect whether the required VERSION
  191:                keyword was given.  */
  192:             version_hdr->month = nlmlex_get_number ($2);
  193:             version_hdr->day = nlmlex_get_number ($3);
  194:             version_hdr->year = nlmlex_get_number ($4);
  195:             free ($2);
  196:             free ($3);
  197:             free ($4);
  198:             if (version_hdr->month < 1 || version_hdr->month > 12)
  199:               nlmheader_warn (_("illegal month"), -1);
  200:             if (version_hdr->day < 1 || version_hdr->day > 31)
  201:               nlmheader_warn (_("illegal day"), -1);
  202:             if (version_hdr->year < 1900 || version_hdr->year > 3000)
  203:               nlmheader_warn (_("illegal year"), -1);
  204:           }
  205:         | DEBUG
  206:           {
  207:             debug_info = TRUE;
  208:           }
  209:         | DESCRIPTION QUOTED_STRING
  210:           {
  211:             int len;
  212: 
  213:             len = strlen ($2);
  214:             if (len > NLM_MAX_DESCRIPTION_LENGTH)
  215:               {
  216:                 nlmheader_warn (_("description string is too long"),
  217:                                 NLM_MAX_DESCRIPTION_LENGTH);
  218:                 len = NLM_MAX_DESCRIPTION_LENGTH;
  219:               }
  220:             var_hdr->descriptionLength = len;
  221:             strncpy (var_hdr->descriptionText, $2, len);
  222:             var_hdr->descriptionText[len] = '\0';
  223:             free ($2);
  224:           }
  225:         | EXIT STRING
  226:           {
  227:             exit_procedure = $2;
  228:           }
  229:         | EXPORT
  230:           {
  231:             symbol_prefix = NULL;
  232:           }
  233:           symbol_list_opt
  234:           {
  235:             export_symbols = string_list_append (export_symbols, $3);
  236:           }
  237:         | FLAG_ON STRING
  238:           {
  239:             fixed_hdr->flags |= nlmlex_get_number ($2);
  240:             free ($2);
  241:           }
  242:         | FLAG_OFF STRING
  243:           {
  244:             fixed_hdr->flags &=~ nlmlex_get_number ($2);
  245:             free ($2);
  246:           }
  247:         | FULLMAP
  248:           {
  249:             map_file = "";
  250:             full_map = TRUE;
  251:           }
  252:         | FULLMAP STRING
  253:           {
  254:             map_file = $2;
  255:             full_map = TRUE;
  256:           }
  257:         | HELP STRING
  258:           {
  259:             help_file = $2;
  260:           }
  261:         | IMPORT
  262:           {
  263:             symbol_prefix = NULL;
  264:           }
  265:           symbol_list_opt
  266:           {
  267:             import_symbols = string_list_append (import_symbols, $3);
  268:           }
  269:         | INPUT string_list
  270:           {
  271:             input_files = string_list_append (input_files, $2);
  272:           }
  273:         | MAP
  274:           {
  275:             map_file = "";
  276:           }
  277:         | MAP STRING
  278:           {
  279:             map_file = $2;
  280:           }
  281:         | MESSAGES STRING
  282:           {
  283:             message_file = $2;
  284:           }
  285:         | MODULE string_list
  286:           {
  287:             modules = string_list_append (modules, $2);
  288:           }
  289:         | MULTIPLE
  290:           {
  291:             fixed_hdr->flags |= 0x2;
  292:           }
  293:         | OS_DOMAIN
  294:           {
  295:             fixed_hdr->flags |= 0x10;
  296:           }
  297:         | OUTPUT STRING
  298:           {
  299:             if (output_file == NULL)
  300:               output_file = $2;
  301:             else
  302:               nlmheader_warn (_("ignoring duplicate OUTPUT statement"), -1);
  303:           }
  304:         | PSEUDOPREEMPTION
  305:           {
  306:             fixed_hdr->flags |= 0x8;
  307:           }
  308:         | REENTRANT
  309:           {
  310:             fixed_hdr->flags |= 0x1;
  311:           }
  312:         | SCREENNAME QUOTED_STRING
  313:           {
  314:             int len;
  315: 
  316:             len = strlen ($2);
  317:             if (len >= NLM_MAX_SCREEN_NAME_LENGTH)
  318:               {
  319:                 nlmheader_warn (_("screen name is too long"),
  320:                                 NLM_MAX_SCREEN_NAME_LENGTH);
  321:                 len = NLM_MAX_SCREEN_NAME_LENGTH;
  322:               }
  323:             var_hdr->screenNameLength = len;
  324:             strncpy (var_hdr->screenName, $2, len);
  325:             var_hdr->screenName[NLM_MAX_SCREEN_NAME_LENGTH] = '\0';
  326:             free ($2);
  327:           }
  328:         | SHARELIB STRING
  329:           {
  330:             sharelib_file = $2;
  331:           }
  332:         | STACK STRING
  333:           {
  334:             var_hdr->stackSize = nlmlex_get_number ($2);
  335:             free ($2);
  336:           }
  337:         | START STRING
  338:           {
  339:             start_procedure = $2;
  340:           }
  341:         | SYNCHRONIZE
  342:           {
  343:             fixed_hdr->flags |= 0x4;
  344:           }
  345:         | THREADNAME QUOTED_STRING
  346:           {
  347:             int len;
  348: 
  349:             len = strlen ($2);
  350:             if (len >= NLM_MAX_THREAD_NAME_LENGTH)
  351:               {
  352:                 nlmheader_warn (_("thread name is too long"),
  353:                                 NLM_MAX_THREAD_NAME_LENGTH);
  354:                 len = NLM_MAX_THREAD_NAME_LENGTH;
  355:               }
  356:             var_hdr->threadNameLength = len;
  357:             strncpy (var_hdr->threadName, $2, len);
  358:             var_hdr->threadName[len] = '\0';
  359:             free ($2);
  360:           }
  361:         | TYPE STRING
  362:           {
  363:             fixed_hdr->moduleType = nlmlex_get_number ($2);
  364:             free ($2);
  365:           }
  366:         | VERBOSE
  367:           {
  368:             verbose = TRUE;
  369:           }
  370:         | VERSIONK STRING STRING STRING
  371:           {
  372:             long val;
  373: 
  374:             strncpy (version_hdr->stamp, "VeRsIoN#", 8);
  375:             version_hdr->majorVersion = nlmlex_get_number ($2);
  376:             val = nlmlex_get_number ($3);
  377:             if (val < 0 || val > 99)
  378:               nlmheader_warn (_("illegal minor version number (must be between 0 and 99)"),
  379:                               -1);
  380:             else
  381:               version_hdr->minorVersion = val;
  382:             val = nlmlex_get_number ($4);
  383:             if (val < 0)
  384:               nlmheader_warn (_("illegal revision number (must be between 0 and 26)"),
  385:                               -1);
  386:             else if (val > 26)
  387:               version_hdr->revision = 0;
  388:             else
  389:               version_hdr->revision = val;
  390:             free ($2);
  391:             free ($3);
  392:             free ($4);
  393:           }
  394:         | VERSIONK STRING STRING
  395:           {
  396:             long val;
  397: 
  398:             strncpy (version_hdr->stamp, "VeRsIoN#", 8);
  399:             version_hdr->majorVersion = nlmlex_get_number ($2);
  400:             val = nlmlex_get_number ($3);
  401:             if (val < 0 || val > 99)
  402:               nlmheader_warn (_("illegal minor version number (must be between 0 and 99)"),
  403:                               -1);
  404:             else
  405:               version_hdr->minorVersion = val;
  406:             version_hdr->revision = 0;
  407:             free ($2);
  408:             free ($3);
  409:           }
  410:         | XDCDATA STRING
  411:           {
  412:             rpc_file = $2;
  413:           }
  414:         ;
  415: 
  416: /* A possibly empty list of symbols.  */
  417: 
  418: symbol_list_opt:
  419:           /* Empty.  */
  420:           {
  421:             $$ = NULL;
  422:           }
  423:         | symbol_list
  424:           {
  425:             $$ = $1;
  426:           }
  427:         ;
  428: 
  429: /* A list of symbols in an import or export list.  Prefixes may appear
  430:    in parentheses.  We need to use left recursion here to avoid
  431:    building up a large import list on the parser stack.  */
  432: 
  433: symbol_list:
  434:           symbol
  435:           {
  436:             $$ = string_list_cons ($1, NULL);
  437:           }
  438:         | symbol_prefix
  439:           {
  440:             $$ = NULL;
  441:           }
  442:         | symbol_list symbol
  443:           {
  444:             $$ = string_list_append1 ($1, $2);
  445:           }
  446:         | symbol_list symbol_prefix
  447:           {
  448:             $$ = $1;
  449:           }
  450:         ;
  451: 
  452: /* A prefix for subsequent symbols.  */
  453: 
  454: symbol_prefix:
  455:           '(' STRING ')'
  456:           {
  457:             if (symbol_prefix != NULL)
  458:               free (symbol_prefix);
  459:             symbol_prefix = $2;
  460:           }
  461:         ;
  462: 
  463: /* A single symbol.  */
  464: 
  465: symbol:
  466:           STRING
  467:           {
  468:             if (symbol_prefix == NULL)
  469:               $$ = $1;
  470:             else
  471:               {
  472:                 $$ = xmalloc (strlen (symbol_prefix) + strlen ($1) + 2);
  473:                 sprintf ($$, "%s@%s", symbol_prefix, $1);
  474:                 free ($1);
  475:               }
  476:           }
  477:         ;
  478: 
  479: /* A list of strings.  */
  480: 
  481: string_list:
  482:           /* May be empty.  */
  483:           {
  484:             $$ = NULL;
  485:           }
  486:         | STRING string_list
  487:           {
  488:             $$ = string_list_cons ($1, $2);
  489:           }
  490:         ;
  491: 
  492: %%
  493: 
  494: /* If strerror is just a macro, we want to use the one from libiberty
  495:    since it will handle undefined values.  */
  496: #undef strerror
  497: extern char *strerror PARAMS ((int));
  498: 
  499: /* The lexer is simple, too simple for flex.  Keywords are only
  500:    recognized at the start of lines.  Everything else must be an
  501:    argument.  A comma is treated as whitespace.  */
  502: 
  503: /* The states the lexer can be in.  */
  504: 
  505: enum lex_state
  506: {
  507:   /* At the beginning of a line.  */
  508:   BEGINNING_OF_LINE,
  509:   /* In the middle of a line.  */
  510:   IN_LINE
  511: };
  512: 
  513: /* We need to keep a stack of files to handle file inclusion.  */
  514: 
  515: struct input
  516: {
  517:   /* The file to read from.  */
  518:   FILE *file;
  519:   /* The name of the file.  */
  520:   char *name;
  521:   /* The current line number.  */
  522:   int lineno;
  523:   /* The current state.  */
  524:   enum lex_state state;
  525:   /* The next file on the stack.  */
  526:   struct input *next;
  527: };
  528: 
  529: /* The current input file.  */
  530: 
  531: static struct input current;
  532: 
  533: /* The character which introduces comments.  */
  534: #define COMMENT_CHAR '#'
  535: ^L
  536: /* Start the lexer going on the main input file.  */
  537: 
  538: bfd_boolean
  539: nlmlex_file (const char *name)
  540: {
  541:   current.next = NULL;
  542:   return nlmlex_file_open (name);
  543: }
  544: 
  545: /* Start the lexer going on a subsidiary input file.  */
  546: 
  547: static void
  548: nlmlex_file_push (const char *name)
  549: {
  550:   struct input *push;
  551: 
  552:   push = (struct input *) xmalloc (sizeof (struct input));
  553:   *push = current;
  554:   if (nlmlex_file_open (name))
  555:     current.next = push;
  556:   else
  557:     {
  558:       current = *push;
  559:       free (push);
  560:     }
  561: }
  562: 
  563: /* Start lexing from a file.  */
  564: 
  565: static bfd_boolean
  566: nlmlex_file_open (const char *name)
  567: {
  568:   current.file = fopen (name, "r");
  569:   if (current.file == NULL)
  570:     {
  571:       fprintf (stderr, "%s:%s: %s\n", program_name, name, strerror (errno));
  572:       ++parse_errors;
  573:       return FALSE;
  574:     }
  575:   current.name = xstrdup (name);
  576:   current.lineno = 1;
  577:   current.state = BEGINNING_OF_LINE;
  578:   return TRUE;
  579: }
  580: ^L
  581: /* Table used to turn keywords into tokens.  */
  582: 
  583: struct keyword_tokens_struct
  584: {
  585:   const char *keyword;
  586:   int token;
  587: };
  588: 
  589: static struct keyword_tokens_struct keyword_tokens[] =
  590: {
  591:   { "CHECK", CHECK },
  592:   { "CODESTART", CODESTART },
  593:   { "COPYRIGHT", COPYRIGHT },
  594:   { "CUSTOM", CUSTOM },
  595:   { "DATE", DATE },
  596:   { "DEBUG", DEBUG },
  597:   { "DESCRIPTION", DESCRIPTION },
  598:   { "EXIT", EXIT },
  599:   { "EXPORT", EXPORT },
  600:   { "FLAG_ON", FLAG_ON },
  601:   { "FLAG_OFF", FLAG_OFF },
  602:   { "FULLMAP", FULLMAP },
  603:   { "HELP", HELP },
  604:   { "IMPORT", IMPORT },
  605:   { "INPUT", INPUT },
  606:   { "MAP", MAP },
  607:   { "MESSAGES", MESSAGES },
  608:   { "MODULE", MODULE },
  609:   { "MULTIPLE", MULTIPLE },
  610:   { "OS_DOMAIN", OS_DOMAIN },
  611:   { "OUTPUT", OUTPUT },
  612:   { "PSEUDOPREEMPTION", PSEUDOPREEMPTION },
  613:   { "REENTRANT", REENTRANT },
  614:   { "SCREENNAME", SCREENNAME },
  615:   { "SHARELIB", SHARELIB },
  616:   { "STACK", STACK },
  617:   { "STACKSIZE", STACK },
  618:   { "START", START },
  619:   { "SYNCHRONIZE", SYNCHRONIZE },
  620:   { "THREADNAME", THREADNAME },
  621:   { "TYPE", TYPE },
  622:   { "VERBOSE", VERBOSE },
  623:   { "VERSION", VERSIONK },
  624:   { "XDCDATA", XDCDATA }
  625: };
  626: 
  627: #define KEYWORD_COUNT (sizeof (keyword_tokens) / sizeof (keyword_tokens[0]))
  628: ^L
  629: /* The lexer accumulates strings in these variables.  */
  630: static char *lex_buf;
  631: static int lex_size;
  632: static int lex_pos;
  633: 
  634: /* Start accumulating strings into the buffer.  */
  635: #define BUF_INIT() \
  636:   ((void) (lex_buf != NULL ? lex_pos = 0 : nlmlex_buf_init ()))
  637: 
  638: static int
  639: nlmlex_buf_init (void)
  640: {
  641:   lex_size = 10;
  642:   lex_buf = xmalloc (lex_size + 1);
  643:   lex_pos = 0;
  644:   return 0;
  645: }
  646: 
  647: /* Finish a string in the buffer.  */
  648: #define BUF_FINISH() ((void) (lex_buf[lex_pos] = '\0'))
  649: 
  650: /* Accumulate a character into the buffer.  */
  651: #define BUF_ADD(c) \
  652:   ((void) (lex_pos < lex_size \
  653:            ? lex_buf[lex_pos++] = (c) \
  654:            : nlmlex_buf_add (c)))
  655: 
  656: static char
  657: nlmlex_buf_add (int c)
  658: {
  659:   if (lex_pos >= lex_size)
  660:     {
  661:       lex_size *= 2;
  662:       lex_buf = xrealloc (lex_buf, lex_size + 1);
  663:     }
  664: 
  665:   return lex_buf[lex_pos++] = c;
  666: }
  667: ^L
  668: /* The lexer proper.  This is called by the bison generated parsing
  669:    code.  */
  670: 
  671: static int
  672: yylex (void)
  673: {
  674:   int c;
  675: 
  676: tail_recurse:
  677: 
  678:   c = getc (current.file);
  679: 
  680:   /* Commas are treated as whitespace characters.  */
  681:   while (ISSPACE (c) || c == ',')
  682:     {
  683:       current.state = IN_LINE;
  684:       if (c == '\n')
  685:         {
  686:           ++current.lineno;
  687:           current.state = BEGINNING_OF_LINE;
  688:         }
  689:       c = getc (current.file);
  690:     }
  691: 
  692:   /* At the end of the file we either pop to the previous file or
  693:      finish up.  */
  694:   if (c == EOF)
  695:     {
  696:       fclose (current.file);
  697:       free (current.name);
  698:       if (current.next == NULL)
  699:         return 0;
  700:       else
  701:         {
  702:           struct input *next;
  703: 
  704:           next = current.next;
  705:           current = *next;
  706:           free (next);
  707:           goto tail_recurse;
  708:         }
  709:     }
  710: 
  711:   /* A comment character always means to drop everything until the
  712:      next newline.  */
  713:   if (c == COMMENT_CHAR)
  714:     {
  715:       do
  716:         {
  717:           c = getc (current.file);
  718:         }
  719:       while (c != '\n');
  720:       ++current.lineno;
  721:       current.state = BEGINNING_OF_LINE;
  722:       goto tail_recurse;
  723:     }
  724: 
  725:   /* An '@' introduces an include file.  */
  726:   if (c == '@')
  727:     {
  728:       do
  729:         {
  730:           c = getc (current.file);
  731:           if (c == '\n')
  732:             ++current.lineno;
  733:         }
  734:       while (ISSPACE (c));
  735:       BUF_INIT ();
  736:       while (! ISSPACE (c) && c != EOF)
  737:         {
  738:           BUF_ADD (c);
  739:           c = getc (current.file);
  740:         }
  741:       BUF_FINISH ();
  742: 
  743:       ungetc (c, current.file);
  744: 
  745:       nlmlex_file_push (lex_buf);
  746:       goto tail_recurse;
  747:     }
  748: 
  749:   /* A non-space character at the start of a line must be the start of
  750:      a keyword.  */
  751:   if (current.state == BEGINNING_OF_LINE)
  752:     {
  753:       BUF_INIT ();
  754:       while (ISALNUM (c) || c == '_')
  755:         {
  756:           BUF_ADD (TOUPPER (c));
  757:           c = getc (current.file);
  758:         }
  759:       BUF_FINISH ();
  760: 
  761:       if (c != EOF && ! ISSPACE (c) && c != ',')
  762:         {
  763:           nlmheader_identify ();
  764:           fprintf (stderr, _("%s:%d: illegal character in keyword: %c\n"),
  765:                    current.name, current.lineno, c);
  766:         }
  767:       else
  768:         {
  769:           unsigned int i;
  770: 
  771:           for (i = 0; i < KEYWORD_COUNT; i++)
  772:             {
  773:               if (lex_buf[0] == keyword_tokens[i].keyword[0]
  774:                   && strcmp (lex_buf, keyword_tokens[i].keyword) == 0)
  775:                 {
  776:                   /* Pushing back the final whitespace avoids worrying
  777:                      about \n here.  */
  778:                   ungetc (c, current.file);
  779:                   current.state = IN_LINE;
  780:                   return keyword_tokens[i].token;
  781:                 }
  782:             }
  783: 
  784:           nlmheader_identify ();