Git init
[external/mawk.git] / fin.c
1
2 /********************************************
3 fin.c
4 copyright 1991, 1992.  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 /*$Log: fin.c,v $
14  * Revision 1.10  1995/12/24  22:23:22  mike
15  * remove errmsg() from inside FINopen
16  *
17  * Revision 1.9  1995/06/06  00:18:29  mike
18  * change mawk_exit(1) to mawk_exit(2)
19  *
20  * Revision 1.8  1994/12/13  00:26:35  mike
21  * rt_nr and rt_fnr for run-time error messages
22  *
23  * Revision 1.7  1994/12/11  23:25:05  mike
24  * -Wi option
25  *
26  * Revision 1.6  1994/12/11  22:14:15  mike
27  * remove THINK_C #defines.  Not a political statement, just no indication
28  * that anyone ever used it.
29  *
30  * Revision 1.5  1994/10/08  19:15:42  mike
31  * remove SM_DOS
32  *
33  * Revision 1.4  1993/07/17  13:22:55  mike
34  * indent and general code cleanup
35  *
36  * Revision 1.3  1993/07/15  13:26:55  mike
37  * SIZE_T and indent
38  *
39  * Revision 1.2  1993/07/04  12:51:57  mike
40  * start on autoconfig changes
41  *
42  * Revision 1.1.1.1  1993/07/03  18:58:13  mike
43  * move source to cvs
44  *
45  * Revision 5.7  1993/01/01  21:30:48  mike
46  * split new_STRING() into new_STRING and new_STRING0
47  *
48  * Revision 5.6  1992/12/17  02:48:01  mike
49  * 1.1.2d changes for DOS
50  *
51  * Revision 5.5  1992/07/28  15:11:30  brennan
52  * minor change in finding eol, needed for MsDOS
53  *
54  * Revision 5.4  1992/07/10  16:17:10  brennan
55  * MsDOS: remove NO_BINMODE macro
56  *
57  * Revision 5.3  1992/07/08  16:14:27  brennan
58  * FILENAME and FNR retain last values in the
59  * END block.
60  *
61  * Revision 5.2  1992/02/21  13:30:08  brennan
62  * fixed bug that free'd FILENAME twice if
63  * command line was var=value only
64  *
65  * Revision 5.1  91/12/05  07:56:02  brennan
66  * 1.1 pre-release
67  *
68 */
69
70 /* fin.c */
71
72 #include "mawk.h"
73 #include "fin.h"
74 #include "memory.h"
75 #include "bi_vars.h"
76 #include "field.h"
77 #include "symtype.h"
78 #include "scan.h"
79
80 #ifndef   NO_FCNTL_H
81 #include <fcntl.h>
82 #endif
83
84 /* This file handles input files.  Opening, closing,
85    buffering and (most important) splitting files into
86    records, FINgets().
87 */
88
89 int PROTO(isatty, (int)) ;
90
91 static FIN *PROTO(next_main, (int)) ;
92 static char *PROTO(enlarge_fin_buffer, (FIN *)) ;
93 static void PROTO(set_main_to_stdin, (void)) ;
94 int PROTO(is_cmdline_assign, (char *)) ; /* also used by init */
95
96 /* convert file-descriptor to FIN*.
97    It's the main stream if main_flag is set
98 */
99 FIN *
100 FINdopen(fd, main_flag)
101    int fd, main_flag ;
102 {
103    register FIN *fin = ZMALLOC(FIN) ;
104
105    fin->fd = fd ;
106    fin->flags = main_flag ? (MAIN_FLAG | START_FLAG) : START_FLAG ;
107    fin->buffp = fin->buff = (char *) zmalloc(BUFFSZ + 1) ;
108    fin->nbuffs = 1 ;
109    fin->buff[0] = 0 ;
110
111    if ((isatty(fd) && rs_shadow.type == SEP_CHAR && rs_shadow.c == '\n')
112        || (interactive_flag && fd == 0) )
113    {
114       /* interactive, i.e., line buffer this file */
115       if (fd == 0)  fin->fp = stdin ;
116       else if (!(fin->fp = fdopen(fd, "r")))
117       {
118          errmsg(errno, "fdopen failed") ; mawk_exit(2) ; 
119       }
120    }
121    else  fin->fp = (FILE *) 0 ;
122
123    return fin ;
124 }
125
126 /* open a FIN* by filename.
127    It's the main stream if main_flag is set.
128    Recognizes "-" as stdin.
129 */
130
131 FIN *
132 FINopen(filename, main_flag)
133    char *filename ;
134    int main_flag ;
135 {
136    int fd ;
137    int oflag = O_RDONLY ;
138
139 #if  MSDOS
140    int bm = binmode() & 1 ;
141    if (bm)  oflag |= O_BINARY ;
142 #endif
143
144    if (filename[0] == '-' && filename[1] == 0)
145    {
146 #if  MSDOS
147       if (bm)  setmode(0, O_BINARY) ;
148 #endif
149       return FINdopen(0, main_flag) ;
150    }
151
152    if ((fd = open(filename, oflag, 0)) == -1)
153       return (FIN *) 0 ;
154    else 
155       return FINdopen(fd, main_flag) ;
156 }
157
158 /* frees the buffer and fd, but leaves FIN structure until
159    the user calls close() */
160
161 void
162 FINsemi_close(fin)
163    register FIN *fin ;
164 {
165    static char dead = 0 ;
166
167    if (fin->buff != &dead)
168    {
169       zfree(fin->buff, fin->nbuffs * BUFFSZ + 1) ;
170
171       if (fin->fd)
172         {
173          if (fin->fp)  fclose(fin->fp) ;
174          else  close(fin->fd) ;
175         }
176
177       fin->buff = fin->buffp = &dead ;   /* marks it semi_closed */
178    }
179    /* else was already semi_closed */
180 }
181
182 /* user called close() on input file */
183 void
184 FINclose(fin)
185    FIN *fin ;
186 {
187    FINsemi_close(fin) ;
188    ZFREE(fin) ;
189 }
190
191 /* return one input record as determined by RS,
192    from input file (FIN)  fin
193 */
194
195 char *
196 FINgets(fin, len_p)
197    FIN *fin ;
198    unsigned *len_p ;
199 {
200    register char *p, *q ;
201    unsigned match_len ;
202    unsigned r ;
203
204 restart :
205
206    if (!(p = fin->buffp)[0])    /* need a refill */
207    {
208       if (fin->flags & EOF_FLAG)
209       {
210          if (fin->flags & MAIN_FLAG)
211          {
212             fin = next_main(0) ;  goto restart ; 
213          }
214          else
215          {
216             *len_p = 0 ; return (char *) 0 ; 
217          }
218       }
219
220       if (fin->fp)
221       {
222          /* line buffering */
223          if (!fgets(fin->buff, BUFFSZ + 1, fin->fp))
224          {
225             fin->flags |= EOF_FLAG ;
226             fin->buff[0] = 0 ;
227             fin->buffp = fin->buff ;
228             goto restart ;       /* might be main_fin */
229          }
230          else  /* return this line */
231          {
232             /* find eol */
233             p = fin->buff ;
234             while (*p != '\n' && *p != 0)  p++ ;
235
236             *p = 0 ; *len_p = p - fin->buff ;
237             fin->buffp = p ;
238             return fin->buff ;
239          }
240       }
241       else
242       {
243          /* block buffering */
244          r = fillbuff(fin->fd, fin->buff, fin->nbuffs * BUFFSZ) ;
245          if (r == 0)
246          {
247             fin->flags |= EOF_FLAG ;
248             fin->buffp = fin->buff ;
249             goto restart ;       /* might be main */
250          }
251          else if (r < fin->nbuffs * BUFFSZ)
252          {
253             fin->flags |= EOF_FLAG ;
254          }
255
256          p = fin->buffp = fin->buff ;
257
258          if (fin->flags & START_FLAG)
259          {
260             fin->flags &= ~START_FLAG ;
261             if (rs_shadow.type == SEP_MLR)
262             {
263                /* trim blank lines from front of file */
264                while (*p == '\n')  p++ ;
265                fin->buffp = p ;
266                if (*p == 0)  goto restart ;
267             }
268          }
269       }
270    }
271
272 retry:
273
274    switch (rs_shadow.type)
275    {
276       case SEP_CHAR:
277          q = strchr(p, rs_shadow.c) ;
278          match_len = 1 ;
279          break ;
280
281       case SEP_STR:
282          q = str_str(p, ((STRING *) rs_shadow.ptr)->str,
283                      match_len = ((STRING *) rs_shadow.ptr)->len) ;
284          break ;
285
286       case SEP_MLR:
287       case SEP_RE:
288          q = re_pos_match(p, rs_shadow.ptr, &match_len) ;
289          /* if the match is at the end, there might still be
290                more to match in the file */
291          if (q && q[match_len] == 0 && !(fin->flags & EOF_FLAG))
292             q = (char *) 0 ;
293          break ;
294
295       default:
296          bozo("type of rs_shadow") ;
297    }
298
299    if (q)
300    {
301       /* the easy and normal case */
302       *q = 0 ;  *len_p = q - p ;
303       fin->buffp = q + match_len ;
304       return p ;
305    }
306
307    if (fin->flags & EOF_FLAG)
308    {
309       /* last line without a record terminator */
310       *len_p = r = strlen(p) ; fin->buffp = p+r ;
311
312       if (rs_shadow.type == SEP_MLR && fin->buffp[-1] == '\n'
313           && r != 0)
314       {
315          (*len_p)-- ;
316          *--fin->buffp = 0 ;
317       }
318       return p ;
319    }
320
321    if (p == fin->buff)
322    {
323       /* current record is too big for the input buffer, grow buffer */
324       p = enlarge_fin_buffer(fin) ;
325    }
326    else
327    {
328       /* move a partial line to front of buffer and try again */
329       unsigned rr ;
330
331       p = (char *) memcpy(fin->buff, p, r = strlen(p)) ;
332       q = p+r ;  rr = fin->nbuffs*BUFFSZ - r ;
333
334       if ((r = fillbuff(fin->fd, q, rr)) < rr)
335          fin->flags |= EOF_FLAG ;
336    }
337    goto retry ;
338 }
339
340 static char *
341 enlarge_fin_buffer(fin)
342    FIN *fin ;
343 {
344    unsigned r ;
345    unsigned oldsize = fin->nbuffs * BUFFSZ + 1 ;
346
347 #ifdef  MSDOS
348    /* I'm not sure this can really happen:
349      avoid "16bit wrap" */
350    if (fin->nbuffs == MAX_BUFFS)
351    {
352       errmsg(0, "out of input buffer space") ;
353       mawk_exit(2) ;
354    }
355 #endif
356
357    fin->buffp =
358       fin->buff = (char *) zrealloc(fin->buff, oldsize, oldsize + BUFFSZ) ;
359    fin->nbuffs++ ;
360
361    r = fillbuff(fin->fd, fin->buff + (oldsize - 1), BUFFSZ) ;
362    if (r < BUFFSZ)  fin->flags |= EOF_FLAG ;
363
364    return fin->buff ;
365 }
366
367 /*--------
368   target is big enough to hold size + 1 chars
369   on exit the back of the target is zero terminated
370  *--------------*/
371 unsigned
372 fillbuff(fd, target, size)
373    int fd ;
374    register char *target ;
375    unsigned size ;
376 {
377    register int r ;
378    unsigned entry_size = size ;
379
380    while (size)
381       switch (r = read(fd, target, size))
382       {
383          case -1:
384             errmsg(errno, "read error") ;
385             mawk_exit(2) ;
386
387          case 0:
388             goto out ;
389
390          default:
391             target += r ; size -= r ;
392             break ;
393       }
394
395 out :
396    *target = 0 ;
397    return entry_size - size ;
398 }
399
400 /* main_fin is a handle to the main input stream
401    == 0  never been opened   */
402
403 FIN *main_fin ;
404 ARRAY Argv ;                     /* to the user this is ARGV  */
405 static double argi = 1.0 ;       /* index of next ARGV[argi] to try to open */
406
407
408 static void
409 set_main_to_stdin()
410 {
411    cell_destroy(FILENAME) ;
412    FILENAME->type = C_STRING ;
413    FILENAME->ptr = (PTR) new_STRING("-") ;
414    cell_destroy(FNR) ;
415    FNR->type = C_DOUBLE ;
416    FNR->dval = 0.0 ; rt_fnr = 0 ;
417    main_fin = FINdopen(0, 1) ;
418 }
419
420
421 /* this gets called once to get the input stream going.
422    It is called after the execution of the BEGIN block
423    unless there is a getline inside BEGIN {}
424 */
425 void
426 open_main()
427 {
428    CELL argc ;
429
430 #if  MSDOS
431    int k = binmode() ;
432
433    if (k & 1)  setmode(0, O_BINARY) ;
434    if ( k & 2 ) { setmode(1,O_BINARY) ; setmode(2,O_BINARY) ; }
435 #endif
436
437    cellcpy(&argc, ARGC) ;
438    if (argc.type != C_DOUBLE)  cast1_to_d(&argc) ;
439
440    if (argc.dval == 1.0)  set_main_to_stdin() ;
441    else  next_main(1) ;
442 }
443
444 /* get the next command line file open */
445 static FIN *
446 next_main(open_flag)
447    int open_flag ;               /* called by open_main() if on */
448 {
449    register CELL *cp ;
450    CELL argc ;                   /* copy of ARGC */
451    CELL c_argi ;                 /* cell copy of argi */
452    CELL argval ;                 /* copy of ARGV[c_argi] */
453
454
455    argval.type = C_NOINIT ;
456    c_argi.type = C_DOUBLE ;
457
458    if (main_fin)  FINclose(main_fin) ;
459    /* FILENAME and FNR don't change unless we really open
460      a new file */
461
462    /* make a copy of ARGC to avoid side effect */
463    if (cellcpy(&argc, ARGC)->type != C_DOUBLE)  cast1_to_d(&argc) ;
464
465    while (argi < argc.dval)
466    {
467       c_argi.dval = argi ;
468       argi += 1.0 ;
469
470       if (!(cp = array_find(Argv, &c_argi, NO_CREATE)))
471          continue ;              /* its deleted */
472
473       /* make a copy so we can cast w/o side effect */
474       cell_destroy(&argval) ;
475       cp = cellcpy(&argval, cp) ;
476       if (cp->type < C_STRING)  cast1_to_s(cp) ;
477       if (string(cp)->len == 0)  continue ;
478       /* file argument is "" */
479
480       /* it might be a command line assignment */
481       if (is_cmdline_assign(string(cp)->str))  continue ;
482
483       /* try to open it -- we used to continue on failure,
484        but posix says we should quit */
485       if (!(main_fin = FINopen(string(cp)->str, 1)))  
486       {
487          errmsg(errno, "cannot open %s", string(cp)->str) ;
488          mawk_exit(2) ;
489       }
490
491       /* success -- set FILENAME and FNR */
492       cell_destroy(FILENAME) ;
493       cellcpy(FILENAME, cp) ;
494       free_STRING(string(cp)) ;
495       cell_destroy(FNR) ;
496       FNR->type = C_DOUBLE ;
497       FNR->dval = 0.0 ; rt_fnr = 0 ;
498
499       return main_fin ;
500    }
501    /* failure */
502    cell_destroy(&argval) ;
503
504    if (open_flag)
505    {
506       /* all arguments were null or assignment */
507       set_main_to_stdin() ;
508       return main_fin ;
509    }
510
511    /* real failure */
512    {
513       /* this is how we mark EOF on main_fin  */
514       static char dead_buff = 0 ;
515       static FIN dead_main =
516       {0, (FILE *) 0, &dead_buff, &dead_buff,
517        1, EOF_FLAG} ;
518
519       return main_fin = &dead_main ;
520       /* since MAIN_FLAG is not set, FINgets won't call next_main() */
521    }
522 }
523
524
525 int
526 is_cmdline_assign(s)
527    char *s ;
528 {
529    register char *p ;
530    int c ;
531    SYMTAB *stp ;
532    CELL *cp ;
533    unsigned len ;
534    CELL cell ;                   /* used if command line assign to pseudo field */
535    CELL *fp = (CELL *) 0 ;       /* ditto */
536
537    if (scan_code[*(unsigned char *) s] != SC_IDCHAR)  return 0 ;
538
539    p = s + 1 ;
540    while ((c = scan_code[*(unsigned char *) p]) == SC_IDCHAR
541           || c == SC_DIGIT)
542       p++ ;
543
544    if (*p != '=')  return 0 ;
545
546    *p = 0 ;
547    stp = find(s) ;
548
549    switch (stp->type)
550    {
551       case ST_NONE:
552          stp->type = ST_VAR ;
553          stp->stval.cp = cp = ZMALLOC(CELL) ;
554          break ;
555
556       case ST_VAR:
557       case ST_NR:               /* !! no one will do this */
558          cp = stp->stval.cp ;
559          cell_destroy(cp) ;
560          break ;
561
562       case ST_FIELD:
563          /* must be pseudo field */
564          fp = stp->stval.cp ;
565          cp = &cell ;
566          break ;
567
568       default:
569          rt_error(
570          "cannot command line assign to %s\n\ttype clash or keyword"
571                     ,s) ;
572    }
573
574    /* we need to keep ARGV[i] intact */
575    *p++ = '=' ;
576    len = strlen(p) + 1 ;
577    /* posix says escape sequences are on from command line */
578    p = rm_escape(strcpy((char *) zmalloc(len), p)) ;
579    cp->ptr = (PTR) new_STRING(p) ;
580    zfree(p, len) ;
581    check_strnum(cp) ;            /* sets cp->type */
582    if (fp)                      /* move it from cell to pfield[] */
583    {
584       field_assign(fp, cp) ;
585       free_STRING(string(cp)) ;
586    }
587    return 1 ;
588 }