stevesu@copper.UUCP (Steve Summit) (11/04/86)
Description: Under suitably obscure circumstances, make invokes processes with standard input closed. Marginal coding practices in cpp can't handle this situation. Repeat-By: Use make -f -. For instance, if the file m contains x.o: x.c then we can do $ touch x.c $ make -f - < m cc -c x.c x.c: 0: No source file /tmp/ctma3348 *** Error code 1 Stop. $ The problem is cpp's file-processing algorithm, which is as follows: 1. Set the input file descriptor (fin) to standard input (0). 2. If there is a command line filename, open it, setting fin to the resulting file descriptor. 3. If there is a second command line filename, indicated by fin being nonzero, open it as the output file. Therefore, if cpp is invoked with the standard input closed, the first command line filename will be opened, but will end up on file descriptor 0, so that when the second command line argument is seen, it will look as if no command line argument was seen, and an attempt will be made to open the second one for input. Some time ago, there was a discussion on net.unix-wizards on why daemons always opened up file descriptors 0, 1, and 2 on /dev/null. Here's one more reason: it's easy to write apparently reasonable programs which fail if an open returns a file descriptor less than 3. By the way, if you fix cpp but not make, you may discover that the assembler appears to have a similar problem, which I haven't fixed. Fix: Here are fixes for both make and cpp. The fixed make will only close the Makefile file descriptor if make opened it (if it wasn't standard input). When make -f - is used, this has the effect that the processes forked by make all have their standard input opened on the Makefile, which is at EOF. It is arguable that make should reopen standard input on /dev/null in this case: if(! unequal(descfile, "-")) { r = rdd1(stdin); (void)fclose(stdin); (void)open("/dev/null", 0); /* better return 0 */ return(r); } The fix for cpp is to use an explicit binary flag to keep track of whether an input filename has been seen, rather than trying to intuit it from the state of fin. Interestingly enough, the mysterious comment inside the #ifndef gcos in cpp.c, that John couldn't figure out, may reflect the same problem. *** main.c Thu Sep 1 16:52:16 1983 --- main.new.c Mon Nov 3 12:10:32 1986 *************** *** 275,280 char *descfile; { FILE * k; /* read and parse description */ --- 275,281 ----- char *descfile; { FILE * k; + int r; /* read and parse description */ *************** *** 303,310 if(! unequal(descfile, "-")) return( rdd1(stdin) ); ! if( (k = fopen(descfile,"r")) != NULL) ! return( rdd1(k) ); return(1); } --- 304,314 ----- if(! unequal(descfile, "-")) return( rdd1(stdin) ); ! if( (k = fopen(descfile,"r")) != NULL) { ! r = rdd1(k); ! (void)fclose(k); ! return(r); ! } return(1); } *************** *** 324,332 if( yyparse() ) fatal("Description file error"); - - if(fin != NULL) - fclose(fin); return(0); } --- 328,333 ----- if( yyparse() ) fatal("Description file error"); return(0); } *** cpp.c Thu May 16 16:41:24 1985 --- cpp.new.c Mon Nov 3 12:31:28 1986 *************** *** 1019,1024 register int i,c; register char *p; char *tf,**cp2; # if gcos if (setjmp(env)) return (exfail); --- 1019,1025 ----- register int i,c; register char *p; char *tf,**cp2; + int seeninput = 0; # if gcos if (setjmp(env)) return (exfail); *************** *** 1112,1118 continue; } default: ! if (fin==STDIN) { if (0>(fin=open(argv[i], READ))) { pperror("No source file %s",argv[i]); exit(8); } --- 1113,1119 ----- continue; } default: ! if (!seeninput) { if (0>(fin=open(argv[i], READ))) { pperror("No source file %s",argv[i]); exit(8); } *************** *** 1116,1125 if (0>(fin=open(argv[i], READ))) { pperror("No source file %s",argv[i]); exit(8); } fnames[ifno]=copy(argv[i]); dirs[0]=dirnams[ifno]=trmdir(argv[i]); # ifndef gcos /* too dangerous to have file name in same syntactic position be input or output file depending on file redirections, so force output to stdout, willy-nilly [i don't see what the problem is. jfr] --- 1117,1127 ----- if (0>(fin=open(argv[i], READ))) { pperror("No source file %s",argv[i]); exit(8); } fnames[ifno]=copy(argv[i]); dirs[0]=dirnams[ifno]=trmdir(argv[i]); + seeninput = 1; # ifndef gcos /* too dangerous to have file name in same syntactic position be input or output file depending on file redirections, so force output to stdout, willy-nilly [i don't see what the problem is. jfr]