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