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