home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / MAWK113.ZIP / mawk113 / scan.c < prev    next >
C/C++ Source or Header  |  1993-01-14  |  22KB  |  859 lines

  1.  
  2. /********************************************
  3. scan.c
  4. copyright 1991, Michael D. Brennan
  5.  
  6. This is a source file for mawk, an implementation of
  7. the AWK programming language.
  8.  
  9. Mawk is distributed without warranty under the terms of
  10. the GNU General Public License, version 2, 1991.
  11. ********************************************/
  12.  
  13.  
  14. /* $Log: scan.c,v $
  15.  * Revision 5.4.1.1  1993/01/15  03:33:50  mike
  16.  * patch3: safer double to int conversion
  17.  *
  18.  * Revision 5.4  1992/11/29  18:57:50  mike
  19.  * field expressions convert to long so 16 bit and 32 bit
  20.  * systems behave the same
  21.  *
  22.  * Revision 5.3  1992/07/08  15:43:41  brennan
  23.  * patch2: length returns.  I am a wimp
  24.  *
  25.  * Revision 5.2  1992/02/21  14:16:53  brennan
  26.  * fix:  getline <=
  27.  *
  28.  * Revision 5.1  91/12/05  07:56:27  brennan
  29.  * 1.1 pre-release
  30.  * 
  31. */
  32.  
  33.  
  34. #include  "mawk.h"
  35. #include  "sizes.h"
  36. #include  "scan.h"
  37. #include  "memory.h"
  38. #include  "field.h"
  39. #include  "init.h"
  40. #include  "fin.h"
  41. #include  "repl.h"
  42. #include  "code.h"
  43.  
  44. #if HAVE_FCNTL_H
  45. #include  <fcntl.h>
  46. #endif
  47.  
  48. #include  "files.h"
  49.  
  50.  
  51. /* static functions */
  52. static void PROTO(scan_fillbuff, (void) ) ;
  53. static void PROTO(scan_open, (void) ) ;
  54. static int PROTO(slow_next, (void) ) ;
  55. static void PROTO(eat_comment, (void) ) ;
  56. static void PROTO(eat_semi_colon, (void) ) ;
  57. static double PROTO(collect_decimal, (int, int *) ) ;
  58. static int PROTO(collect_string, (void) ) ;
  59. static int  PROTO(collect_RE, (void) ) ;
  60.  
  61.  
  62. /*-----------------------------
  63.   program file management
  64.  *----------------------------*/
  65.  
  66. char *pfile_name ;
  67. STRING  *program_string ;
  68. PFILE *pfile_list ;
  69. static  unsigned char *buffer ;
  70. static  unsigned char *buffp ;  
  71.     /* unsigned so it works with 8 bit chars */
  72. static  int  program_fd   ; 
  73. static  int  eof_flag ;
  74.  
  75. void  scan_init(cmdline_program)
  76.   char * cmdline_program ;
  77.   if ( cmdline_program )
  78.   {
  79.     program_fd = -1 ; /* command line program */
  80.     program_string = new_STRING((char *)0, 
  81.                 strlen(cmdline_program) + 1 ) ;
  82.     (void) strcpy(program_string->str, cmdline_program) ;
  83.     /* simulate file termination */
  84.     program_string->str[program_string->len-1] = '\n' ;
  85.     buffp = (unsigned char *)  program_string->str ;
  86.     eof_flag = 1 ;
  87.   }
  88.   else /* program from file[s] */
  89.   {
  90.     scan_open() ;
  91.     buffp = buffer = (unsigned char *) zmalloc( BUFFSZ+1 ) ;
  92.     scan_fillbuff() ;
  93.   }
  94.  
  95.   eat_nl() ; /* scan to first token */
  96.   if ( next() == 0 ) { errmsg(0, "no program") ; mawk_exit(1) ; }
  97.   un_next() ;
  98.   
  99. }
  100.  
  101. static void  scan_open() /* open pfile_name */
  102. {
  103.   if ( pfile_name[0] == '-' && pfile_name[1] == 0 )
  104.         program_fd = 0 ;
  105.   else
  106.   if ( (program_fd = open(pfile_name, O_RDONLY, 0)) == -1 )
  107.   { errmsg( errno, "cannot open %s", pfile_name) ; mawk_exit(1) ; }
  108. }
  109.  
  110. void scan_cleanup()
  111.   if ( program_fd >= 0 ) zfree(buffer, BUFFSZ+1) ;
  112.   else  free_STRING(program_string) ;
  113.  
  114.   if ( program_fd > 0 )  (void) close(program_fd) ;
  115.  
  116.   /* redefine SPACE as [ \t\n] */
  117.  
  118.   scan_code['\n'] = posix_space_flag && rs_shadow.type != SEP_MLR
  119.             ? SC_UNEXPECTED : SC_SPACE ;
  120.   scan_code['\f'] = SC_UNEXPECTED ; /*value doesn't matter */
  121.   scan_code['\013'] = SC_UNEXPECTED ; /* \v not space */
  122.   scan_code['\r'] = SC_UNEXPECTED ;
  123. }
  124.  
  125. /*--------------------------------
  126.   global variables shared by yyparse() and yylex()
  127.   and used for error messages too
  128.  *-------------------------------*/
  129.  
  130. int  current_token = -1 ; 
  131. unsigned  token_lineno ;
  132. unsigned  compile_error_count ;
  133. int   NR_flag ; /* are we tracking NR */
  134. int   paren_cnt ;
  135. int   brace_cnt ;
  136. int   print_flag ;  /* changes meaning of '>' */
  137. int   getline_flag ; /* changes meaning of '<' */
  138.  
  139. extern  YYSTYPE  yylval ;
  140.  
  141. /*----------------------------------------
  142.  file reading functions
  143.  next() and un_next(c) are macros in scan.h
  144.  
  145.  *---------------------*/
  146.  
  147. static  unsigned lineno = 1 ;
  148.  
  149.  
  150. static  void scan_fillbuff()
  151. { unsigned r ;
  152.  
  153.   r = fillbuff(program_fd, (char *)buffer, BUFFSZ) ;
  154.   if ( r < BUFFSZ ) 
  155.   { eof_flag = 1 ;
  156.     /* check eof is terminated */
  157.     if ( r && buffer[r-1] != '\n' )
  158.     { buffer[r] = '\n' ; buffer[r+1] = 0 ; }
  159.   }
  160. }
  161.  
  162. /* read one character -- slowly */
  163. static int slow_next()
  164.    
  165.   while ( *buffp == 0 )
  166.   {
  167.     if ( !eof_flag ) 
  168.     { buffp = buffer ; scan_fillbuff() ; }
  169.     else
  170.     if ( pfile_list /* open another program file */ )
  171.     {
  172.       PFILE *q ;
  173.  
  174.       if ( program_fd > 0 ) (void) close(program_fd) ;
  175.       eof_flag = 0 ;
  176.       pfile_name = pfile_list->fname ;
  177.       q = pfile_list ;
  178.       pfile_list = pfile_list->link ;
  179.       ZFREE(q) ;
  180.       scan_open() ;
  181.       token_lineno = lineno = 1 ;
  182.     }
  183.     else  break /* real eof */ ;
  184.   }
  185.  
  186.   return *buffp++ ; /* note can un_next() , eof which is zero */
  187. }
  188.  
  189. static void eat_comment()
  190. { register int c ;
  191.  
  192.   while ( (c = next()) != '\n' && scan_code[c] ) ;
  193.   un_next() ;
  194. }
  195.  
  196. /* this is how we handle extra semi-colons that are
  197.    now allowed to separate pattern-action blocks
  198.  
  199.    A proof that they are useless clutter to the language:
  200.    we throw them away
  201. */
  202.  
  203. static  void  eat_semi_colon()
  204. /* eat one semi-colon on the current line */
  205. { register int c ;
  206.  
  207.   while ( scan_code[c = next()] == SC_SPACE )  ;
  208.   if ( c != ';' )  un_next() ;
  209. }
  210.  
  211. void eat_nl() /* eat all space including newlines */
  212. {
  213.   while ( 1 )
  214.     switch( scan_code[next()] )
  215.     { 
  216.       case SC_COMMENT : 
  217.          eat_comment() ;
  218.          break ;
  219.          
  220.       case  SC_NL  :   lineno++ ;
  221.       /* fall thru  */
  222.       case  SC_SPACE  :   break ;
  223.       default :  
  224.           un_next() ; return ;
  225.     }
  226. }
  227.  
  228. int yylex()
  229.   register int c ;
  230.  
  231.   token_lineno = lineno ;
  232.  
  233. reswitch:
  234.  
  235.     switch( scan_code[c = next()] )
  236.     {
  237.       case  0  :  
  238.           ct_ret(EOF) ;
  239.           
  240.       case  SC_SPACE  :   goto reswitch ;
  241.  
  242.       case  SC_COMMENT :
  243.           eat_comment() ; goto reswitch ;
  244.  
  245.       case  SC_NL  : 
  246.           lineno++ ; eat_nl() ;
  247.           ct_ret(NL) ;
  248.  
  249.       case SC_ESCAPE :
  250.           while ( scan_code[ c = next() ] == SC_SPACE ) ;
  251.           if ( c == '\n')
  252.           { token_lineno = ++lineno ; goto reswitch ; }
  253.           if ( c == 0 )  ct_ret(EOF) ;
  254.           un_next() ;
  255.           yylval.ival = '\\' ;
  256.           ct_ret(UNEXPECTED) ;
  257.  
  258.  
  259.       case  SC_SEMI_COLON  : 
  260.           eat_nl() ;
  261.           ct_ret(SEMI_COLON) ;
  262.  
  263.       case  SC_LBRACE :  
  264.           eat_nl() ; brace_cnt++ ;
  265.           ct_ret(LBRACE) ;
  266.  
  267.       case  SC_PLUS  :
  268.           switch( next() )
  269.           {
  270.             case '+' :  
  271.                 yylval.ival = '+' ;
  272.                 string_buff[0] = 
  273.                      string_buff[1] = '+' ;
  274.                 string_buff[2] = 0 ;
  275.                 ct_ret(INC_or_DEC) ;
  276.  
  277.             case  '=' :
  278.                 ct_ret(ADD_ASG) ;
  279.  
  280.             default :  un_next() ; ct_ret(PLUS) ;
  281.           }
  282.  
  283.       case  SC_MINUS :
  284.           switch( next() )
  285.           {
  286.             case '-' :  
  287.                 yylval.ival = '-' ;
  288.                 string_buff[0] = 
  289.                      string_buff[1] = '-' ;
  290.                 string_buff[2] = 0 ;
  291.                 ct_ret(INC_or_DEC) ;
  292.  
  293.             case  '=' :
  294.                 ct_ret(SUB_ASG) ;
  295.  
  296.             default :  un_next() ; ct_ret(MINUS) ;
  297.           }
  298.  
  299.       case  SC_COMMA :  eat_nl() ; ct_ret(COMMA) ;
  300.  
  301.       case  SC_MUL  :  test1_ret('=', MUL_ASG, MUL) ;
  302.       case  SC_DIV :   
  303.           { static int can_precede_div[] =
  304.         { DOUBLE, STRING_, RPAREN, ID, D_ID, RE, RBOX, FIELD,
  305.           GETLINE, INC_or_DEC, -1 } ;
  306.           
  307.           int *p = can_precede_div ;
  308.  
  309.             do
  310.                 if ( *p == current_token )
  311.         {
  312.           if ( *p != INC_or_DEC ) 
  313.             test1_ret('=', DIV_ASG, DIV) ;
  314.  
  315.           if ( next() == '=' )
  316.           { un_next() ; ct_ret( collect_RE() ) ;  }
  317.         }
  318.                  
  319.             while ( * ++p != -1 ) ;
  320.  
  321.             ct_ret( collect_RE() ) ;
  322.           }
  323.  
  324.       case  SC_MOD  :  test1_ret('=', MOD_ASG, MOD) ;
  325.       case  SC_POW :   test1_ret('=' , POW_ASG, POW) ;
  326.       case  SC_LPAREN : 
  327.           paren_cnt++ ;
  328.           ct_ret(LPAREN) ;
  329.  
  330.       case  SC_RPAREN : 
  331.           if ( --paren_cnt < 0 )
  332.           { compile_error( "extra ')'" ) ;
  333.             paren_cnt = 0 ;
  334.             goto reswitch ; }
  335.  
  336.           ct_ret(RPAREN) ;
  337.  
  338.       case  SC_LBOX   : ct_ret(LBOX) ;
  339.       case  SC_RBOX   : ct_ret(RBOX) ;
  340.  
  341.       case  SC_MATCH  : 
  342.       string_buff[0] = '~' ; string_buff[0] = 0 ;
  343.       yylval.ival = 1 ;
  344.       ct_ret(MATCH) ;
  345.  
  346.       case  SC_EQUAL  :
  347.           test1_ret( '=', EQ, ASSIGN ) ;
  348.  
  349.       case  SC_NOT : /* !  */
  350.       if ( (c = next()) == '~' )
  351.       {
  352.         string_buff[0] = '!' ;
  353.         string_buff[1] = '~' ;
  354.         string_buff[2] = 0 ;
  355.         yylval.ival = 0 ;
  356.         ct_ret(MATCH) ;
  357.       }
  358.       else
  359.       if ( c == '=' )  ct_ret(NEQ) ;
  360.  
  361.       un_next() ;
  362.       ct_ret(NOT) ;
  363.  
  364.  
  365.       case  SC_LT  :  /* '<' */
  366.       if ( next() == '=' ) ct_ret(LTE) ;
  367.       else  un_next() ;
  368.  
  369.           if ( getline_flag )
  370.           { getline_flag = 0 ; ct_ret(IO_IN) ; }
  371.           else  ct_ret(LT) ;
  372.  
  373.       case  SC_GT  :  /* '>' */
  374.           if ( print_flag && paren_cnt == 0 )
  375.           { print_flag = 0 ;
  376.             /* there are 3 types of IO_OUT 
  377.                -- build the error string in string_buff */
  378.             string_buff[0] = '>' ;
  379.             if ( next() == '>' ) 
  380.             { 
  381.               yylval.ival = F_APPEND ;
  382.               string_buff[1] = '>' ;
  383.               string_buff[2] =  0 ;
  384.             }
  385.             else
  386.             { un_next() ; 
  387.               yylval.ival = F_TRUNC ; 
  388.               string_buff[1] = 0 ;
  389.             }
  390.             return current_token = IO_OUT ;
  391.           }
  392.  
  393.           test1_ret('=', GTE, GT) ;
  394.  
  395.       case  SC_OR :
  396.           if ( next() == '|' ) 
  397.           { eat_nl() ; ct_ret(OR) ; }
  398.           else 
  399.           { un_next() ; 
  400.  
  401.             if ( print_flag && paren_cnt == 0 )
  402.             { print_flag = 0 ; 
  403.               yylval.ival = PIPE_OUT;
  404.               string_buff[0] = '|' ;
  405.               string_buff[1] = 0 ;
  406.               ct_ret(IO_OUT) ;
  407.             }
  408.             else  ct_ret(PIPE) ;
  409.           }
  410.  
  411.       case  SC_AND :
  412.           if ( next() == '&' )  
  413.           { eat_nl() ; ct_ret(AND) ; }
  414.           else 
  415.           { un_next() ; yylval.ival = '&' ; ct_ret(UNEXPECTED) ; }
  416.  
  417.       case  SC_QMARK  :  ct_ret(QMARK) ;
  418.       case  SC_COLON  :  ct_ret(COLON) ;
  419.       case  SC_RBRACE :
  420.           if ( --brace_cnt < 0 )
  421.           { compile_error("extra '}'" ) ;
  422.             eat_semi_colon() ;
  423.             brace_cnt = 0 ; goto reswitch ; }
  424.  
  425.           if ( (c = current_token) == NL || c == SEMI_COLON 
  426.                || c == SC_FAKE_SEMI_COLON  || c == RBRACE  )
  427.           { 
  428.             /* if the brace_cnt is zero , we've completed
  429.                a pattern action block. If the user insists
  430.                on adding a semi-colon on the same line
  431.                we will eat it.  Note what we do below:
  432.                physical law -- conservation of semi-colons */
  433.  
  434.             if ( brace_cnt == 0 )  eat_semi_colon() ;
  435.             eat_nl() ;
  436.             ct_ret(RBRACE) ;
  437.           }
  438.  
  439.           /* supply missing semi-colon to statement that
  440.              precedes a '}' */
  441.           brace_cnt++ ; un_next() ;
  442.           current_token = SC_FAKE_SEMI_COLON ;
  443.           return  SEMI_COLON ;
  444.  
  445.       case  SC_DIGIT  :
  446.       case  SC_DOT    :
  447.           { double d ;
  448.             int flag ;
  449.         static double double_zero = 0.0 ;
  450.         static double double_one = 1.0 ;
  451.  
  452.             if ( (d = collect_decimal(c, &flag)) == 0.0 )
  453.                 if ( flag )  ct_ret(flag) ;
  454.                 else  yylval.ptr = (PTR) &double_zero ;
  455.             else if ( d == 1.0 ) yylval.ptr = (PTR) &double_one ;
  456.             else 
  457.             { yylval.ptr = (PTR) ZMALLOC(double) ;
  458.           *(double*)yylval.ptr = d ;
  459.             }
  460.             ct_ret( DOUBLE ) ;
  461.           }
  462.  
  463.       case  SC_DOLLAR :  /* '$' */
  464.           { double d ;
  465.             int flag ;
  466.  
  467.             while ( scan_code[c = next()] == SC_SPACE )  ;
  468.             if ( scan_code[c] != SC_DIGIT &&
  469.                  scan_code[c] != SC_DOT )
  470.             { un_next() ; ct_ret(DOLLAR) ; }
  471.             /* compute field address at compile time */
  472.             if ( (d = collect_decimal(c, &flag)) == 0.0 )
  473.                 if ( flag )  ct_ret(flag) ; /* an error */
  474.                 else  yylval.cp = &field[0] ;
  475.             else
  476.             { 
  477.               if ( d > MAX_FIELD )
  478.               { compile_error(
  479.                    "$%g exceeds maximum field(%d)" , d, MAX_FIELD) ;
  480.                 d = MAX_FIELD ;
  481.               }
  482.               yylval.cp = field_ptr((int)d) ;
  483.             }
  484.  
  485.             ct_ret(FIELD) ;
  486.           }
  487.  
  488.       case  SC_DQUOTE :
  489.           return current_token = collect_string() ;
  490.  
  491.       case  SC_IDCHAR : /* collect an identifier */
  492.             { unsigned char *p =
  493.                     (unsigned char *)string_buff + 1 ;
  494.               SYMTAB *stp ;
  495.  
  496.               string_buff[0] = c ;
  497.  
  498.               while ( 
  499.                 (c = scan_code[ *p++ = next()]) == SC_IDCHAR ||
  500.                        c == SC_DIGIT )  ;
  501.               
  502.               un_next() ; * --p = 0 ;
  503.  
  504.               switch( (stp = find(string_buff))->type )
  505.               { case ST_NONE :  
  506.                   /* check for function call before defined */
  507.                       if ( next() == '(' )
  508.                       { stp->type = ST_FUNCT ;
  509.                         stp->stval.fbp = (FBLOCK *)
  510.                                 zmalloc(sizeof(FBLOCK)) ;
  511.                         stp->stval.fbp->name = stp->name ;
  512.                         stp->stval.fbp->code = (INST *) 0 ;
  513.                         yylval.fbp = stp->stval.fbp ;
  514.                         current_token = FUNCT_ID ;
  515.                       }
  516.                       else
  517.                       { yylval.stp = stp ;
  518.                         current_token = 
  519.                             current_token == DOLLAR ? D_ID : ID ;
  520.                       }
  521.                       un_next() ;
  522.                       break ;
  523.  
  524.                 case  ST_NR  :
  525.               NR_flag = 1 ;
  526.                       stp->type = ST_VAR ;
  527.                       /* fall thru */
  528.                         
  529.                 case ST_VAR :
  530.                 case  ST_ARRAY :
  531.                 case  ST_LOCAL_NONE :
  532.                 case  ST_LOCAL_VAR :
  533.                 case  ST_LOCAL_ARRAY :
  534.  
  535.                       yylval.stp = stp ;
  536.                       current_token =
  537.                             current_token == DOLLAR ? D_ID : ID ;
  538.                       break ;
  539.  
  540.         case ST_ENV :
  541.               stp->type = ST_ARRAY ;
  542.               stp->stval.array = new_ARRAY() ;
  543.               load_environ(stp->stval.array) ;
  544.               yylval.stp = stp ;
  545.               current_token =
  546.               current_token == DOLLAR ? D_ID : ID ;
  547.               break ;
  548.  
  549.                 case ST_FUNCT :
  550.                       yylval.fbp = stp->stval.fbp ;
  551.                       current_token = FUNCT_ID ;
  552.                       break ;
  553.  
  554.                 case ST_KEYWORD :  
  555.                       current_token = stp->stval.kw ;
  556.                       break ;
  557.  
  558.                 case  ST_BUILTIN :
  559.                       yylval.bip = stp->stval.bip ;
  560.                       current_token = BUILTIN ;
  561.                       break ;
  562.  
  563.                 case  ST_LENGTH  :
  564.  
  565.               yylval.bip = stp->stval.bip ;
  566.  
  567.               /* check for length alone, this is an ugly
  568.              hack */
  569.                       while ( scan_code[ c = next() ] == SC_SPACE ) ;
  570.                       un_next() ;
  571.  
  572.               current_token = c == '(' ? BUILTIN : LENGTH ;
  573.               break ;
  574.  
  575.                 case  ST_FIELD  :
  576.                       yylval.cp = stp->stval.cp ;
  577.                       current_token = FIELD ;
  578.                       break ;
  579.  
  580.                 default : 
  581.                       bozo("find returned bad st type") ;
  582.               }
  583.               return  current_token  ;
  584.             }
  585.  
  586.  
  587.       case  SC_UNEXPECTED :
  588.             yylval.ival = c & 0xff ;
  589.             ct_ret(UNEXPECTED) ;
  590.     }
  591.     return  0 ; /* never get here make lint happy */
  592. }
  593.  
  594. /* collect a decimal constant in temp_buff.
  595.    Return the value and error conditions by reference */
  596.  
  597. static double collect_decimal(c, flag)
  598.   int c ; int *flag ;
  599. { register unsigned char *p = (unsigned char*) string_buff + 1;
  600.   unsigned char *endp ;
  601.   double d ;
  602.  
  603.   *flag = 0 ;
  604.   string_buff[0] = c ;
  605.  
  606.   if ( c == '.' )
  607.   { if ( scan_code[*p++ = next()] != SC_DIGIT )
  608.     { *flag = UNEXPECTED ; yylval.ival = '.' ;
  609.       return 0.0 ; }
  610.   }
  611.   else
  612.   {  while ( scan_code[*p++ = next()] == SC_DIGIT ) ;
  613.      if ( p[-1] != '.' )
  614.      { un_next() ; p-- ; }
  615.   }
  616.   /* get rest of digits after decimal point */
  617.   while ( scan_code[*p++ = next()] == SC_DIGIT )  ;
  618.  
  619.   /* check for exponent */
  620.   if ( p[-1] != 'e' && p[-1] != 'E' )
  621.   { un_next() ; * --p = 0 ; }
  622.   else  /* get the exponent */
  623.     if ( scan_code[*p = next()] != SC_DIGIT &&
  624.          *p != '-' && *p != '+' )
  625.     { *++p = 0 ; *flag = BAD_DECIMAL ;
  626.       return 0.0 ; }
  627.     else  /* get the rest of the exponent */
  628.     { p++ ;
  629.       while ( scan_code[*p++ = next()] == SC_DIGIT )  ;
  630.       un_next() ; * --p = 0 ;
  631.     }
  632.  
  633.   errno = 0 ; /* check for overflow/underflow */
  634.   d = strtod( string_buff, (char **)&endp ) ;
  635.  
  636. #ifndef  STRTOD_UNDERFLOW_ON_ZERO_BUG
  637.   if ( errno )
  638.       compile_error( "%s : decimal %sflow" , string_buff,
  639.         d == 0.0 ? "under" : "over") ;
  640. #else /* sun4 bug */
  641.   if ( errno && d != 0.0 )
  642.       compile_error( "%s : decimal overflow", string_buff) ;
  643. #endif
  644.  
  645.   if ( endp < p )
  646.   { *flag = BAD_DECIMAL ; return 0.0 ; }
  647.   return d ;
  648. }
  649.  
  650. /*----------  process escape characters ---------------*/
  651.  
  652. static char hex_val['f' - 'A' + 1] = {
  653. 10,11,12,13,14,15, 0, 0,
  654.  0, 0, 0, 0, 0, 0, 0, 0,
  655.  0, 0, 0, 0, 0, 0, 0, 0,
  656.  0, 0, 0, 0, 0, 0, 0, 0,
  657. 10,11,12,13,14,15 } ;
  658.  
  659. #define isoctal(x)  ((x)>='0'&&(x)<='7')
  660.  
  661. #define  hex_value(x)   hex_val[(x)-'A']
  662.  
  663. #define ishex(x) (scan_code[x] == SC_DIGIT ||\
  664.                   'A' <= (x) && (x) <= 'f' && hex_value(x))
  665.  
  666. static int PROTO(octal, (char **)) ;
  667. static int PROTO(hex, (char **)) ;
  668.  
  669. /* process one , two or three octal digits
  670.    moving a pointer forward by reference */
  671. static int octal( start_p )
  672.   char **start_p ;
  673. { register char *p = *start_p ;
  674.   register unsigned x ;
  675.  
  676.   x = *p++ - '0' ;
  677.   if ( isoctal(*p) )
  678.   {
  679.     x = (x<<3) + *p++ - '0' ;
  680.     if ( isoctal(*p) )   x = (x<<3) + *p++ - '0' ;
  681.   }
  682.   *start_p = p ;
  683.   return  x & 0xff ;
  684. }
  685.  
  686. /* process one or two hex digits
  687.    moving a pointer forward by reference */
  688.  
  689. static int  hex( start_p )
  690.   char **start_p ;
  691. { register unsigned char *p = (unsigned char*) *start_p ;
  692.   register unsigned x ;
  693.   unsigned t ;
  694.  
  695.   if ( scan_code[*p] == SC_DIGIT )
  696.         x = *p++ - '0' ;
  697.   else  x = hex_value(*p++) ;
  698.  
  699.   if ( scan_code[*p] == SC_DIGIT )
  700.         x = (x<<4) + *p++ - '0' ;
  701.   else
  702.   if ( 'A' <= *p && *p <= 'f' && (t = hex_value(*p)) )
  703.   { x = (x<<4) + t ; p++ ; }
  704.  
  705.   *start_p = (char *) p ;
  706.   return x ;
  707. }
  708.  
  709. #define  ET_END     9
  710.  
  711. static struct { char in , out ; } escape_test[ET_END+1] = {
  712. 'n' , '\n',
  713. 't' , '\t',
  714. 'f' , '\f',
  715. 'b' , '\b',
  716. 'r' , '\r',
  717. 'a' , '\07',
  718. 'v' , '\013',
  719. '\\', '\\',
  720. '\"', '\"',
  721. 0 , 0 } ;
  722.  
  723.  
  724. /* process the escape characters in a string, in place . */
  725.  
  726. char *rm_escape(s)
  727.   char *s ;
  728. { register char *p, *q ;
  729.   char *t ;
  730.   int i ;
  731.  
  732.   q = p = s ;
  733.  
  734.   while ( *p )
  735.       if ( *p == '\\' )
  736.       { 
  737.         escape_test[ET_END].in = * ++p ; /* sentinal */
  738.         i = 0 ;
  739.         while ( escape_test[i].in != *p )  i++ ;
  740.  
  741.         if ( i != ET_END )  /* in table */
  742.         { 
  743.           p++ ; *q++ = escape_test[i].out ;
  744.         }
  745.         else
  746.         if ( isoctal(*p) ) 
  747.         {
  748.           t = p ;  *q++ = octal(&t) ; p = t ;
  749.         }
  750.         else
  751.         if ( *p == 'x' && ishex(*(unsigned char*)(p+1)) )
  752.         {
  753.           t = p+1 ; *q++ = hex(&t) ; p = t ;
  754.         }
  755.         else
  756.         if ( *p == 0 ) /* can only happen with command line assign */
  757.             *q++ = '\\' ;
  758.         else  /* not an escape sequence */
  759.         { 
  760.           *q++ = '\\' ; *q++ = *p++ ;
  761.         }
  762.       }
  763.       else  *q++ = *p++ ;
  764.  
  765.   *q = 0 ;
  766.   return s ;
  767. }
  768.  
  769. static  int  collect_string()
  770. { register unsigned char *p = (unsigned char *)string_buff ;
  771.   int c ;
  772.   int e_flag = 0 ; /* on if have an escape char */
  773.  
  774.   while ( 1 )
  775.       switch( scan_code[ *p++ = next() ] )
  776.       { case  SC_DQUOTE : /* done */
  777.               * --p = 0 ;  goto out ;
  778.  
  779.         case  SC_NL :
  780.               p[-1] = 0 ;
  781.               /* fall thru */
  782.  
  783.         case  0 :   /* unterminated string */
  784.               compile_error(
  785.               "runaway string constant \"%.10s ..." ,
  786.               string_buff, token_lineno ) ;
  787.               mawk_exit(1) ;
  788.  
  789.         case SC_ESCAPE :
  790.               if ( (c = next()) == '\n' )
  791.               { p-- ; lineno++ ; }
  792.               else  
  793.                 if ( c == 0 )  un_next() ;   
  794.                 else 
  795.                 { *p++ = c ; e_flag = 1 ; }
  796.  
  797.               break ;
  798.  
  799.         default : break ;
  800.       }
  801.  
  802. out:
  803.     yylval.ptr = (PTR) new_STRING(
  804.          e_flag ? rm_escape( string_buff ) 
  805.                 : string_buff ) ;
  806.     return  STRING_ ;
  807. }
  808.  
  809.  
  810. static  int  collect_RE()
  811. { register unsigned char *p = (unsigned char*) string_buff ;
  812.   int c ;
  813.   STRING *sval ;
  814.  
  815.   while ( 1 )
  816.       switch( scan_code[ *p++ = next() ] )
  817.       { case  SC_DIV : /* done */
  818.               * --p = 0 ;  goto out ;
  819.  
  820.         case  SC_NL :
  821.               p[-1] = 0 ;
  822.               /* fall thru */
  823.  
  824.         case  0 :   /* unterminated re */
  825.               compile_error(
  826.               "runaway regular expression /%.10s ..." ,
  827.               string_buff, token_lineno ) ;
  828.               mawk_exit(1) ;
  829.  
  830.         case SC_ESCAPE :
  831.               switch( c = next() )
  832.               { case '/' :  
  833.                       p[-1] = '/' ; break ;
  834.  
  835.                 case '\n' :
  836.                       p-- ;  break ;
  837.  
  838.                 case  0   :
  839.                       un_next() ;  break ;
  840.  
  841.                 default :
  842.                       *p++ = c ; break ;
  843.               }
  844.               break ;
  845.       }
  846.  
  847. out:
  848.   /* now we've got the RE, so compile it */
  849.   sval = new_STRING( string_buff ) ;
  850.   yylval.ptr = re_compile(sval) ;
  851.   free_STRING(sval) ;
  852.   return RE ;
  853. }
  854.  
  855.