[net.bugs.4bsd] make can confuse cpp

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]