Git init
[external/mawk.git] / files.c
1
2 /********************************************
3 files.c
4 copyright 1991-94.  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: files.c,v $
14  * Revision 1.9  1996/01/14  17:14:10  mike
15  * flush_all_output()
16  *
17  * Revision 1.8  1995/06/06  00:18:27  mike
18  * change mawk_exit(1) to mawk_exit(2)
19  *
20  * Revision 1.7  1994/12/11  20:48:50  mike
21  * fflush builtin
22  *
23  * Revision 1.6  1994/10/08  19:15:40  mike
24  * remove SM_DOS
25  *
26  * Revision 1.5  1994/04/17  20:01:37  mike
27  * recognize filename "/dev/stdout"
28  *
29  * Revision 1.4  1994/02/21  00:11:07  mike
30  * code cleanup
31  *
32  * Revision 1.3  1993/07/16  01:00:36  mike
33  * cleanup and indent
34  *
35  * Revision 5.5  1992/12/17  02:48:01  mike
36  * 1.1.2d changes for DOS
37  *
38  * Revision 5.4  1992/07/10  16:10:30  brennan
39  * patch2
40  * MsDOS: remove useless NO_BINMODE macro
41  * get process exit code on in pipes
42  *
43  * Revision 5.3  1992/04/07  20:21:17  brennan
44  * patch 2
45  * unbuffered output to a tty
46  *
47  * Revision 5.2  1992/04/07  16:03:08  brennan
48  * patch 2
49  * allow same filename for output and input, but use different descriptors
50  * E.g. < "/dev/tty" and > "/dev/tty"
51  *
52  * Revision 5.1  91/12/05  07:56:00  brennan
53  * 1.1 pre-release
54  *
55 */
56
57 /* files.c */
58
59 #include "mawk.h"
60 #include "files.h"
61 #include "memory.h"
62 #include "fin.h"
63
64 #include <sys/types.h>
65 #include <sys/wait.h>
66 #include <unistd.h>
67
68 #ifdef  V7
69 #include  <sgtty.h>             /* defines FIOCLEX */
70 #endif
71
72 #ifndef  NO_FCNTL_H
73
74 #include <fcntl.h>
75 #define  CLOSE_ON_EXEC(fd)    fcntl(fd, F_SETFD, 1)
76
77 #else
78 #define  CLOSE_ON_EXEC(fd) ioctl(fd, FIOCLEX, (PTR) 0)
79 #endif
80
81
82 /* We store dynamically created files on a linked linear
83    list with move to the front (big surprise)  */
84
85 typedef struct file
86 {
87    struct file *link ;
88    STRING *name ;
89    short type ;
90    int pid ;                     /* we need to wait() when we close a pipe */
91    /* holds temp file index under MSDOS */
92
93 #if  HAVE_FAKE_PIPES
94    int inpipe_exit ;
95 #endif
96
97    PTR ptr ;                     /* FIN*   or  FILE*   */
98 }
99 FILE_NODE ;
100
101 static FILE_NODE *file_list ;
102
103 /* Prototypes for local functions */
104
105 static FILE *PROTO(tfopen, (char *, char *)) ;
106 static void PROTO(efflush, (FILE*)) ;
107 static void PROTO(add_to_child_list, (int, int)) ;
108 static struct child *PROTO(remove_from_child_list, (int)) ;
109 extern int PROTO(isatty, (int)) ;
110 static void PROTO(close_error, (FILE_NODE *p));
111
112 /* find a file on file_list */
113 PTR
114 file_find(sval, type)
115    STRING *sval ;
116    int type ;
117 {
118    register FILE_NODE *p = file_list ;
119    FILE_NODE *q = (FILE_NODE *) 0 ;
120    char *name = sval->str ;
121    char *ostr ;
122
123    while (1)
124    {
125       if (!p)
126       {
127          /* open a new one */
128          p = ZMALLOC(FILE_NODE) ;
129
130          switch (p->type = type)
131          {
132             case F_TRUNC:
133 #if MSDOS
134                ostr = (binmode() & 2) ? "wb" : "w" ;
135 #else
136                ostr = "w" ;
137 #endif
138                if (!(p->ptr = (PTR) tfopen(name, ostr)))
139                   goto out_failure ;
140                break ;
141
142             case F_APPEND:
143 #if MSDOS
144                ostr = (binmode() & 2) ? "ab" : "a" ;
145 #else
146                ostr = "a" ;
147 #endif
148                if (!(p->ptr = (PTR) tfopen(name, ostr)))
149                   goto out_failure ;
150                break ;
151
152             case F_IN:
153                if (!(p->ptr = (PTR) FINopen(name, 0)))
154                {
155                   zfree(p, sizeof(FILE_NODE)) ;
156                   return (PTR) 0 ;
157                }
158                break ;
159
160             case PIPE_OUT:
161             case PIPE_IN:
162
163 #if    HAVE_REAL_PIPES || HAVE_FAKE_PIPES
164
165                if (!(p->ptr = get_pipe(name, type, &p->pid)))
166                {
167                   if (type == PIPE_OUT)  goto out_failure ;
168                   else
169                   {
170                      zfree(p, sizeof(FILE_NODE)) ;
171                      return (PTR) 0 ;
172                   }
173                }
174 #else
175                rt_error("pipes not supported") ;
176 #endif
177                break ;
178
179 #ifdef  DEBUG
180             default:
181                bozo("bad file type") ;
182 #endif
183          }
184          /* successful open */
185          p->name = sval ;
186          sval->ref_cnt++ ;
187          break ;                 /* while loop */
188       }
189
190       /* search is by name and type */
191       if (strcmp(name, p->name->str) == 0 &&
192           (p->type == type ||
193       /* no distinction between F_APPEND and F_TRUNC here */
194            (p->type >= F_APPEND && type >= F_APPEND)))
195
196       {
197          /* found */
198          if (!q)                /*at front of list */
199             return p->ptr ;
200          /* delete from list for move to front */
201          q->link = p->link ;
202          break ;                 /* while loop */
203       }
204
205       q = p ; p = p->link ;
206    }                            /* end while loop */
207
208    /* put p at the front of the list */
209    p->link = file_list ;
210    return (PTR) (file_list = p)->ptr ;
211
212 out_failure:
213    errmsg(errno, "cannot open \"%s\" for output", name) ;
214    mawk_exit(2) ;
215
216 }
217
218
219 /* Close a file and delete it's node from the file_list.
220    Walk the whole list, in case a name has two nodes,
221    e.g. < "/dev/tty" and > "/dev/tty"
222 */
223
224 int
225 file_close(sval)
226    STRING *sval ;
227 {
228    FILE_NODE dummy ;
229    register FILE_NODE *p ;
230    FILE_NODE *q = &dummy ;       /* trails p */
231    FILE_NODE *hold ;
232    char *name = sval->str ;
233    int retval = -1 ;
234
235    dummy.link = p = file_list ;
236    while (p)
237    {
238       if (strcmp(name, p->name->str) == 0)
239       {
240          /* found */
241
242          /* Remove it from the list first because we might be called 
243             again if an error occurs leading to an infinite loop. 
244
245             Note that we don't have to consider the list corruption 
246             caused by a recursive call because it will never return. */
247
248          q->link = p->link ;
249          file_list = dummy.link ;   /* maybe it was the first file */
250          
251          switch (p->type)
252          {
253             case F_TRUNC:
254             case F_APPEND:
255                if( fclose((FILE *) p->ptr) != 0 )
256                   close_error(p) ;
257                retval = 0 ;
258                break ;
259
260             case PIPE_OUT:
261                if( fclose((FILE *) p->ptr) != 0 )
262                   close_error(p) ;
263
264 #if  HAVE_REAL_PIPES
265                retval = wait_for(p->pid) ;
266 #endif
267 #if  HAVE_FAKE_PIPES
268                retval = close_fake_outpipe(p->name->str, p->pid) ;
269 #endif
270                break ;
271
272             case F_IN:
273                FINclose((FIN *) p->ptr) ;
274                retval = 0 ;
275                break ;
276
277             case PIPE_IN:
278                FINclose((FIN *) p->ptr) ;
279
280 #if  HAVE_REAL_PIPES
281                retval = wait_for(p->pid) ;
282 #endif
283 #if  HAVE_FAKE_PIPES
284                {
285                   char xbuff[100] ;
286                   unlink(tmp_file_name(p->pid, xbuff)) ;
287                   retval = p->inpipe_exit ;
288                }
289 #endif
290                break ;
291          }
292
293          free_STRING(p->name) ;
294          hold = p ;
295          p = p->link ;
296          ZFREE(hold) ;
297       }
298       else
299       {
300          q = p ; p = p->link ; 
301       }
302    }
303
304    return retval ;
305 }
306
307 /*
308 find an output file with name == sval and fflush it
309 */
310
311 int
312 file_flush(sval)
313    STRING *sval ;
314 {
315    int ret = -1 ;
316    register FILE_NODE *p = file_list ;
317    unsigned len = sval->len ;
318    char *str = sval->str ;
319
320    if (len==0) 
321    {
322       /* for consistency with gawk */
323       flush_all_output() ;
324       return 0 ;
325    }
326       
327    while( p )
328    {
329       if ( IS_OUTPUT(p->type) &&
330            len == p->name->len &&
331            strcmp(str,p->name->str) == 0 )
332       {
333          ret = 0 ;
334          efflush((FILE*)p->ptr) ;
335          /* it's possible for a command and a file to have the same
336             name -- so keep looking */
337       }
338       p = p->link ;
339    }
340    return ret ;
341 }
342
343 void
344 flush_all_output() 
345 {
346    FILE_NODE *p ;
347
348    for(p=file_list; p ; p = p->link)
349       if (IS_OUTPUT(p->type)) efflush((FILE*)p->ptr) ;
350 }
351
352 static void
353 efflush(fp)
354    FILE *fp ;
355 {
356    if (fflush(fp) < 0)
357    {
358       errmsg(errno, "unexpected write error") ;
359       mawk_exit(2) ;
360    }
361 }
362
363
364 /* When we exit, we need to close and wait for all output pipes */
365
366 #if   HAVE_REAL_PIPES
367
368 /* work around for bug in AIX 4.1 -- If there are exactly 16 or 
369    32 or 48 ..., open files then the last one doesn't get flushed on
370    exit.  So the following is now a misnomer as we'll really close
371    all output.
372 */
373
374 void
375 close_out_pipes()
376 {
377    register FILE_NODE *p = file_list ;
378
379    while (p)
380    {
381       if (IS_OUTPUT(p->type))
382       {
383          if( fclose((FILE *) p->ptr) != 0 )
384          {
385             /* if another error occurs we do not want to be called 
386                for the same file again */
387
388             file_list = p->link ;
389             close_error(p) ;
390          }
391          if (p->type == PIPE_OUT) wait_for(p->pid) ; 
392       }
393
394       p = p->link ;
395    }
396 }
397
398 #else
399 #if  HAVE_FAKE_PIPES            /* pipes are faked with temp files */
400
401 void
402 close_fake_pipes()
403 {
404    register FILE_NODE *p = file_list ;
405    char xbuff[100] ;
406
407    /* close input pipes first to free descriptors for children */
408    while (p)
409    {
410       if (p->type == PIPE_IN)
411       {
412          FINclose((FIN *) p->ptr) ;
413          unlink(tmp_file_name(p->pid, xbuff)) ;
414       }
415       p = p->link ;
416    }
417    /* doit again */
418    p = file_list ;
419    while (p)
420    {
421       if (p->type == PIPE_OUT)
422       {
423          if( fclose(p->ptr) != 0 )
424             close_error(p) ;
425          close_fake_outpipe(p->name->str, p->pid) ;
426       }
427       p = p->link ;
428    }
429 }
430 #endif /* HAVE_FAKE_PIPES */
431 #endif /* ! HAVE_REAL_PIPES */
432
433 /* hardwire to /bin/sh for portability of programs */
434 char *shell = "/bin/sh" ;
435
436 #if  HAVE_REAL_PIPES
437
438 PTR
439 get_pipe(name, type, pid_ptr)
440    char *name ;
441    int type ;
442    int *pid_ptr ;
443 {
444    int the_pipe[2], local_fd, remote_fd ;
445
446    if (pipe(the_pipe) == -1)  return (PTR) 0 ;
447    local_fd = the_pipe[type == PIPE_OUT] ;
448    remote_fd = the_pipe[type == PIPE_IN] ;
449    /* to keep output ordered correctly */
450    fflush(stdout) ; fflush(stderr) ;
451
452    switch (*pid_ptr = fork())
453    {
454       case -1:
455          close(local_fd) ;
456          close(remote_fd) ;
457          return (PTR) 0 ;
458
459       case 0:
460          close(local_fd) ;
461          close(type == PIPE_IN) ;
462          dup(remote_fd) ;
463          close(remote_fd) ;
464          execl(shell, shell, "-c", name, (char *) 0) ;
465          errmsg(errno, "failed to exec %s -c %s", shell, name) ;
466          fflush(stderr) ;
467          _exit(128) ;
468
469       default:
470          close(remote_fd) ;
471          /* we could deadlock if future child inherit the local fd ,
472            set close on exec flag */
473          CLOSE_ON_EXEC(local_fd) ;
474          break ;
475    }
476
477    return type == PIPE_IN ? (PTR) FINdopen(local_fd, 0) :
478       (PTR) fdopen(local_fd, "w") ;
479 }
480
481
482
483 /*------------ children ------------------*/
484
485 /* we need to wait for children at the end of output pipes to
486    complete so we know any files they have created are complete */
487
488 /* dead children are kept on this list */
489
490 static struct child
491 {
492    int pid ;
493    int exit_status ;
494    struct child *link ;
495 } *child_list ;
496
497 static void
498 add_to_child_list(pid, exit_status)
499    int pid, exit_status ;
500 {
501    register struct child *p = ZMALLOC(struct child) ;
502
503    p->pid = pid ; p->exit_status = exit_status ;
504    p->link = child_list ; child_list = p ;
505 }
506
507 static struct child *
508 remove_from_child_list(pid)
509    int pid ;
510 {
511    struct child dummy ;
512    register struct child *p ;
513    struct child *q = &dummy ;
514
515    dummy.link = p = child_list ;
516    while (p)
517    {
518       if (p->pid == pid)
519       {
520          q->link = p->link ;
521          break ;
522       }
523       else
524       {
525          q = p ; p = p->link ; 
526       }
527    }
528
529    child_list = dummy.link ;
530    return p ;   
531    /* null return if not in the list */
532 }
533
534
535 /* wait for a specific child to complete and return its
536    exit status
537
538    If pid is zero, wait for any single child and
539    put it on the dead children list
540 */
541
542 int
543 wait_for(pid)
544    int pid ;
545 {
546    int exit_status ;
547    struct child *p ;
548    int id ;
549
550    if (pid == 0)
551    {
552       id = wait(&exit_status) ;
553       add_to_child_list(id, exit_status) ;
554    }
555    /* see if an earlier wait() caught our child */
556    else if ((p = remove_from_child_list(pid)))
557    {
558       exit_status = p->exit_status ;
559       ZFREE(p) ;
560    }
561    else
562    {
563       /* need to really wait */
564       while ((id = wait(&exit_status)) != pid)
565       {
566          if (id == -1)          /* can't happen */
567             bozo("wait_for") ;
568          else
569          {
570             /* we got the exit status of another child
571             put it on the child list and try again */
572             add_to_child_list(id, exit_status) ;
573          }
574       }
575    }
576
577    if (exit_status & 0xff)  exit_status = 128 + (exit_status & 0xff) ;
578    else  exit_status = (exit_status & 0xff00) >> 8 ;
579
580    return exit_status ;
581 }
582
583 #endif /* HAVE_REAL_PIPES */
584
585
586 void
587 set_stderr()   /* and stdout */
588 {
589    FILE_NODE *p, *q ; 
590
591    /* We insert stderr first to get it at the end of the list. This is 
592       needed because we want to output errors encountered on closing 
593       stdout. */
594    
595    q = ZMALLOC(FILE_NODE);
596    q->link = (FILE_NODE*) 0 ;
597    q->type = F_TRUNC ;
598    q->name = new_STRING("/dev/stderr") ;
599    q->ptr = (PTR) stderr ;
600
601    p = ZMALLOC(FILE_NODE) ;
602    p->link = q;
603    p->type = F_TRUNC ;
604    p->name = new_STRING("/dev/stdout") ;
605    p->ptr = (PTR) stdout ;
606
607    file_list = p ;
608 }
609
610 /* fopen() but no buffering to ttys */
611 static FILE *
612 tfopen(name, mode)
613    char *name, *mode ;
614 {
615    FILE *retval = fopen(name, mode) ;
616
617    if (retval)
618    {
619       if (isatty(fileno(retval)))  setbuf(retval, (char *) 0) ;
620       else
621       {
622 #ifdef MSDOS
623          enlarge_output_buffer(retval) ;
624 #endif
625       }
626    }
627    return retval ;
628 }
629
630 #ifdef  MSDOS
631 void
632 enlarge_output_buffer(fp)
633    FILE *fp ;
634 {
635    if (setvbuf(fp, (char *) 0, _IOFBF, BUFFSZ) < 0)
636    {
637       errmsg(errno, "setvbuf failed on fileno %d", fileno(fp)) ;
638       mawk_exit(2) ;
639    }
640 }
641
642 void
643 stdout_init()
644 {
645    if (!isatty(1))  enlarge_output_buffer(stdout) ;
646    if (binmode() & 2)
647    {
648       setmode(1,O_BINARY) ; setmode(2,O_BINARY) ; 
649    }
650 }
651 #endif /* MSDOS */
652
653 /* An error occured closing the file referred to by P. We tell the 
654    user and terminate the program. */
655
656 static void close_error(p)
657    FILE_NODE *p ;
658 {
659    errmsg(errno, "close failed on file %s", p->name->str) ;
660    mawk_exit(2) ;
661 }