xref: /386bsd/usr/src/usr.bin/awk/files.c (revision a2142627)
1 
2 /********************************************
3 files.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: files.c,v $
14  * Revision 5.5  1992/12/17  02:48:01  mike
15  * 1.1.2d changes for DOS
16  *
17  * Revision 5.4  1992/07/10  16:10:30  brennan
18  * patch2
19  * MsDOS: remove useless NO_BINMODE macro
20  * get process exit code on in pipes
21  *
22  * Revision 5.3  1992/04/07  20:21:17  brennan
23  * patch 2
24  * unbuffered output to a tty
25  *
26  * Revision 5.2  1992/04/07  16:03:08  brennan
27  * patch 2
28  * allow same filename for output and input, but use different descriptors
29  * E.g. < "/dev/tty" and > "/dev/tty"
30  *
31  * Revision 5.1  91/12/05  07:56:00  brennan
32  * 1.1 pre-release
33  *
34 */
35 
36 /* files.c */
37 
38 #include "mawk.h"
39 #include "files.h"
40 #include "memory.h"
41 #include "fin.h"
42 
43 static FILE  *PROTO(tfopen, (char*,char*)) ;
44 static  void  PROTO(add_to_child_list, (int,int)) ;
45 static struct child *PROTO(remove_from_child_list, (int)) ;
46 extern int PROTO(isatty, (int)) ;
47 
48 #ifdef  V7
49 #include  <sgtty.h>    /* defines FIOCLEX */
50 #endif
51 
52 
53 #if  HAVE_FCNTL_H
54 
55 #include <fcntl.h>
56 #define  CLOSE_ON_EXEC(fd)   (void) fcntl(fd, F_SETFD, 1)
57 
58 #else
59 #define  CLOSE_ON_EXEC(fd) ioctl(fd, FIOCLEX, (PTR) 0)
60 #endif
61 
62 
63 /* We store dynamically created files on a linked linear
64    list with move to the front (big surprise)  */
65 
66 typedef struct file {
67 struct file *link ;
68 STRING  *name ;
69 short type ;
70 int pid ;  /* we need to wait() when we close a pipe */
71            /* holds temp file index under MSDOS */
72 
73 #if  HAVE_FAKE_PIPES
74 int inpipe_exit ;
75 #endif
76 
77 PTR   ptr ;  /* FIN*   or  FILE*   */
78 }  FILE_NODE ;
79 
80 static FILE_NODE *file_list ;
81 
82 
83 /* find a file on file_list */
file_find(sval,type)84 PTR  file_find( sval, type )
85   STRING *sval ;
86   int type ;
87 { register FILE_NODE *p = file_list ;
88   FILE_NODE *q = (FILE_NODE *) 0 ;
89   char *name = sval->str ;
90   char *ostr ;
91 
92   while (1)
93   {
94     if ( !p )   /* open a new one */
95     {
96       p = ZMALLOC(FILE_NODE) ;
97       switch( p->type = type )
98       {
99         case  F_TRUNC :
100 #if MSDOS
101             ostr = (binmode()&2) ? "wb" : "w" ;
102 #else
103             ostr = "w" ;
104 #endif
105             if ( !(p->ptr = (PTR) tfopen(name, ostr)) )
106                 goto out_failure ;
107             break ;
108 
109         case  F_APPEND :
110 #if MSDOS
111             ostr = (binmode()&2) ? "ab" : "a" ;
112 #else
113             ostr = "a" ;
114 #endif
115             if ( !(p->ptr = (PTR) tfopen(name, ostr)) )
116                 goto out_failure ;
117             break ;
118 
119         case  F_IN  :
120             if ( !(p->ptr = (PTR) FINopen(name, 0)) )
121             { zfree(p, sizeof(FILE_NODE)) ; return (PTR) 0 ; }
122             break ;
123 
124         case  PIPE_OUT :
125         case  PIPE_IN :
126 
127 #if    HAVE_REAL_PIPES || HAVE_FAKE_PIPES
128 
129             if ( !(p->ptr = get_pipe(name, type, &p->pid)) )
130                 if ( type == PIPE_OUT ) goto out_failure ;
131                 else
132                 { zfree(p, sizeof(FILE_NODE) ) ;
133                   return (PTR) 0 ;
134                 }
135 #else
136          rt_error("pipes not supported") ;
137 #endif
138             break ;
139 
140 #ifdef  DEBUG
141         default :
142             bozo("bad file type") ;
143 #endif
144       }
145       /* successful open */
146       p->name = sval ;
147       sval->ref_cnt++ ;
148       break ; /* while loop */
149     }
150 
151     /* search is by name and type */
152     if ( strcmp(name, p->name->str) == 0 &&
153 	 ( p->type == type ||
154            /* no distinction between F_APPEND and F_TRUNC here */
155 	   p->type >= F_APPEND && type >= F_APPEND ))
156 
157     { /* found */
158       if ( !q )  /*at front of list */
159           return  p->ptr ;
160       /* delete from list for move to front */
161       q->link = p->link ;
162       break ; /* while loop */
163     }
164     q = p ; p = p->link ;
165   } /* end while loop */
166 
167   /* put p at the front of the list */
168   p->link = file_list ;
169   return  (PTR) (file_list = p)->ptr ;
170 
171 out_failure:
172   errmsg(errno, "cannot open \"%s\" for output", name) ;
173   mawk_exit(1) ;
174 
175 }
176 
177 
178 /* Close a file and delete it's node from the file_list.
179    Walk the whole list, in case a name has two nodes,
180    e.g. < "/dev/tty" and > "/dev/tty"
181 */
182 
file_close(sval)183 int  file_close( sval )
184   STRING *sval ;
185 { register FILE_NODE *p = file_list ;
186   FILE_NODE *q = (FILE_NODE *) 0 ; /* trails p */
187   FILE_NODE *hold ;
188   char *name = sval->str ;
189   int retval = -1 ;
190 
191   while ( p )
192         if ( strcmp(name,p->name->str) == 0 ) /* found */
193         {
194           switch( p->type )
195           {
196             case  F_TRUNC :
197             case  F_APPEND :
198                 (void) fclose((FILE *) p->ptr) ;
199 		retval = 0 ;
200                 break ;
201 
202             case  PIPE_OUT :
203                 (void) fclose((FILE *) p->ptr) ;
204 
205 #if  HAVE_REAL_PIPES
206                 retval =  wait_for(p->pid) ;
207 #endif
208 #if  HAVE_FAKE_PIPES
209                 retval = close_fake_outpipe(p->name->str,p->pid) ;
210 #endif
211                 break ;
212 
213             case F_IN  :
214                 FINclose((FIN *) p->ptr) ;
215 		retval = 0 ;
216                 break ;
217 
218             case PIPE_IN :
219                 FINclose((FIN *) p->ptr) ;
220 
221 #if  HAVE_REAL_PIPES
222                 retval = wait_for(p->pid) ;
223 #endif
224 #if  HAVE_FAKE_PIPES
225 	      {
226 		char xbuff[100] ;
227                 (void) unlink(tmp_file_name(p->pid,xbuff)) ;
228 		retval = p->inpipe_exit ;
229 	      }
230 #endif
231                 break ;
232           }
233 
234           free_STRING(p->name) ;
235 	  hold = p ;
236           if ( q )  q->link = p = p->link ;
237           else  file_list = p = p->link ;
238 
239           ZFREE(hold) ;
240         }
241         else { q = p ; p = p->link ; }
242 
243   return retval ;
244 }
245 
246 /* When we exit, we need to close and wait for all output pipes */
247 
248 
249 #if   HAVE_REAL_PIPES
250 
close_out_pipes()251 void close_out_pipes()
252 { register FILE_NODE *p = file_list ;
253 
254   while ( p )
255   { if ( p->type == PIPE_OUT )
256     { (void) fclose((FILE *) p->ptr) ;  (void) wait_for(p->pid) ; }
257     p = p->link ;
258   }
259 }
260 
261 #else
262 #if  HAVE_FAKE_PIPES  /* pipes are faked with temp files */
263 
close_fake_pipes()264 void  close_fake_pipes()
265 { register FILE_NODE *p = file_list ;
266   char xbuff[100] ;
267 
268   /* close input pipes first to free descriptors for children */
269   while ( p )
270   {
271     if ( p->type == PIPE_IN )
272     { FINclose((FIN *) p->ptr) ;
273       (void) unlink(tmp_file_name(p->pid,xbuff)) ;
274     }
275     p = p->link ;
276   }
277   /* doit again */
278   p = file_list ;
279   while ( p )
280   {
281     if ( p->type == PIPE_OUT )
282     {
283       (void) fclose(p->ptr) ;
284       (void) close_fake_outpipe(p->name->str,p->pid) ;
285     }
286     p = p->link ;
287   }
288 }
289 #endif /* HAVE_FAKE_PIPES */
290 #endif /* ! HAVE_REAL_PIPES */
291 
292 /* hardwire to /bin/sh for portability of programs */
293 char *shell = "/bin/sh" ;
294 
295 #if  HAVE_REAL_PIPES
296 
get_pipe(name,type,pid_ptr)297 PTR get_pipe( name, type, pid_ptr)
298   char *name ;
299   int type ;
300   int *pid_ptr ;
301 { int the_pipe[2], local_fd, remote_fd ;
302 
303   if ( pipe(the_pipe) == -1 )  return (PTR) 0 ;
304   local_fd = the_pipe[type == PIPE_OUT] ;
305   remote_fd = the_pipe[type == PIPE_IN ] ;
306   /* to keep output ordered correctly */
307   fflush(stdout) ; fflush(stderr) ;
308 
309   switch( *pid_ptr = fork() )
310   { case -1 :
311       (void) close(local_fd) ;
312       (void) close(remote_fd) ;
313       return (PTR) 0 ;
314 
315     case  0 :
316         (void) close(local_fd) ;
317         (void) close(type == PIPE_IN) ;
318         (void) dup( remote_fd ) ;
319         (void) close( remote_fd ) ;
320         (void) execl(shell, shell, "-c", name, (char *) 0 ) ;
321         errmsg(errno, "failed to exec %s -c %s" , shell, name) ;
322         fflush(stderr) ;
323         _exit(128) ;
324 
325     default :
326         (void) close(remote_fd) ;
327         /* we could deadlock if future child inherit the local fd ,
328            set close on exec flag */
329         CLOSE_ON_EXEC(local_fd) ;
330         break ;
331   }
332 
333   return  type == PIPE_IN ? (PTR) FINdopen(local_fd, 0) :
334                             (PTR)  fdopen(local_fd, "w")  ;
335 }
336 
337 
338 
339 /*------------ children ------------------*/
340 
341 /* we need to wait for children at the end of output pipes to
342    complete so we know any files they have created are complete */
343 
344 /* dead children are kept on this list */
345 
346 static struct child {
347 int pid ;
348 int exit_status ;
349 struct child *link ;
350 }  *child_list ;
351 
add_to_child_list(pid,exit_status)352 static  void  add_to_child_list(pid, exit_status)
353   int pid, exit_status ;
354 { register struct child *p =
355           (struct child *) zmalloc(sizeof(struct child)) ;
356 
357   p->pid = pid ; p->exit_status = exit_status ;
358   p->link = child_list ; child_list = p ;
359 }
360 
remove_from_child_list(pid)361 static struct child *remove_from_child_list(pid)
362   int pid ;
363 { register struct child *p = child_list ;
364   struct child *q = (struct child *) 0 ;
365 
366   while ( p )
367     if ( p->pid == pid )
368     {
369         if ( q ) q->link = p->link ;
370         else child_list = p->link ;
371         break ;
372     }
373     else { q = p ; p = p->link ; }
374 
375   return p ;  /* null return if not in the list */
376 }
377 
378 
379 /* wait for a specific child to complete and return its
380    exit status
381 
382    If pid is zero, wait for any single child
383 */
384 
wait_for(pid)385 int wait_for(pid)
386   int pid ;
387 { int exit_status ;
388   struct child *p ;
389   int id ;
390 
391   if ( pid == 0 )
392   {
393     id = wait(&exit_status) ;
394     add_to_child_list(id, exit_status) ;
395   }
396   else
397   /* see if an earlier wait() caught our child */
398   if ( p = remove_from_child_list(pid) )
399   { exit_status = p->exit_status ;
400     ZFREE(p) ;
401   }
402   else /* need to really wait */
403     while ( (id = wait(&exit_status)) != pid )
404         if ( id == -1 ) /* can't happen */  bozo("wait_for") ;
405         else
406         { /* we got the exit status of another child
407              put it on the child list and try again */
408           add_to_child_list(id, exit_status ) ;
409         }
410 
411   if ( exit_status & 0xff )
412        exit_status = 128 + (exit_status & 0xff) ;
413   else  exit_status = (exit_status & 0xff00)>>8 ;
414 
415   return exit_status ;
416 }
417 
418 #endif  /* HAVE_REAL_PIPES */
set_stderr()419 void set_stderr()
420 {
421   file_list = ZMALLOC(FILE_NODE) ;
422   file_list->link = (FILE_NODE*) 0 ;
423   file_list->type = F_TRUNC ;
424   file_list->name = new_STRING("/dev/stderr") ;
425   file_list->ptr = (PTR) stderr ;
426 }
427 
428 /* fopen() but no buffering to ttys */
tfopen(name,mode)429 static FILE *tfopen(name, mode)
430   char *name, *mode ;
431 {
432   FILE *retval = fopen(name,mode) ;
433 
434   if ( retval )
435   {
436     if ( isatty(fileno(retval)) )  setbuf(retval, (char*)0) ;
437     else
438     {
439 #if  LM_DOS
440        enlarge_output_buffer(retval) ;
441 #endif
442     }
443   }
444   return retval ;
445 }
446 
447 #if  LM_DOS
enlarge_output_buffer(fp)448 void enlarge_output_buffer( fp )
449   FILE *fp ;
450 {
451   if ( setvbuf(fp, (char*) 0, _IOFBF, BUFFSZ) < 0 )
452   {
453     errmsg(errno, "setvbuf failed on fileno %d", fileno(fp)) ;
454     mawk_exit(1) ;
455   }
456 }
457 #endif
458 
459 #if  MSDOS
460 void
stdout_init()461 stdout_init()
462 {
463 #if  LM_DOS
464    if ( ! isatty(1) )  enlarge_output_buffer(stdout) ;
465 #endif
466    if ( binmode() & 2 ) { setmode(1,O_BINARY) ; setmode(2,O_BINARY) ; }
467 }
468 #endif /* MSDOS */
469