Subject: some changes to recent bin/ln/ln.c
To: None <tech-userlevel@netbsd.org>
From: enami tsugutomo <enami@sm.sony.co.jp>
List: tech-userlevel
Date: 09/07/2003 09:03:32
Attached diff includes some changes to bin/ln/ln.c.  Especially,

- ln(1) in the tree may pass printescpaed() string to syscall, which
  isn't user wanted to.

- It initializes stdout_ok after it may be used.  As a result, even
  the most usual case doesn't work as expected.

- It assumes that stdout == stderr.

I'm not sure if it is really necessary to output filenames in raw form
in the error message when stderr is not a terminal.

enami.

Index: ln.c
===================================================================
RCS file: /cvsroot/src/bin/ln/ln.c,v
retrieving revision 1.25
diff -c -r1.25 ln.c
*** ln.c	2003/08/21 04:30:25	1.25
--- ln.c	2003/09/06 23:30:39
***************
*** 57,68 ****
  int	fflag;				/* Unlink existing files. */
  int	hflag;				/* Check new name for symlink first. */
  int	sflag;				/* Symbolic, not hard, link. */
! int	vflag;                          /* Verbose output */
! int	stdout_ok;			/* stdout connected to a terminal */
  
  					/* System link call. */
! int (*linkf)(const char *, const char *);
! char   linkch;
  
  int	linkit(char *, char *, int);
  void	usage(void);
--- 57,69 ----
  int	fflag;				/* Unlink existing files. */
  int	hflag;				/* Check new name for symlink first. */
  int	sflag;				/* Symbolic, not hard, link. */
! int	vflag;				/* Verbose output */
! int	stdout_isatty;			/* stdout connected to a terminal */
! int	stderr_isatty;			/* stderr connected to a terminal */
  
  					/* System link call. */
! int	(*linkf)(const char *, const char *);
! char	linkch;
  
  int	linkit(char *, char *, int);
  void	usage(void);
***************
*** 89,97 ****
  		case 's':
  			sflag = 1;
  			break;
! 		case 'v':               
! 		vflag = 1;      
! 		break;   
  		case '?':
  		default:
  			usage();
--- 90,98 ----
  		case 's':
  			sflag = 1;
  			break;
! 		case 'v':
! 			vflag = 1;
! 			break;
  		case '?':
  		default:
  			usage();
***************
*** 108,115 ****
  		linkf  = link;
  		linkch = '=';
  	}
  
! 	switch(argc) {
  	case 0:
  		usage();
  		/* NOTREACHED */
--- 109,119 ----
  		linkf  = link;
  		linkch = '=';
  	}
+ 
+ 	stdout_isatty = isatty(STDOUT_FILENO);
+ 	stderr_isatty = isatty(STDERR_FILENO);
  
! 	switch (argc) {
  	case 0:
  		usage();
  		/* NOTREACHED */
***************
*** 121,139 ****
  		/* NOTREACHED */
  	}
  
- 	stdout_ok = isatty(STDOUT_FILENO);
- 
  					/* ln target1 target2 directory */
  	sourcedir = argv[argc - 1];
  	if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
! 		/* we were asked not to follow symlinks, but found one at
! 		   the target--simulate "not a directory" error */
  		errno = ENOTDIR;
! 		err(EXIT_FAILURE, "%s", printescaped(sourcedir));
  		/* NOTREACHED */
  	}
  	if (stat(sourcedir, &sb)) {
! 		err(EXIT_FAILURE, "%s", printescaped(sourcedir));
  		/* NOTREACHED */
  	}
  	if (!S_ISDIR(sb.st_mode)) {
--- 125,145 ----
  		/* NOTREACHED */
  	}
  
  					/* ln target1 target2 directory */
  	sourcedir = argv[argc - 1];
  	if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
! 		/*
! 		 * We were asked not to follow symlinks, but found one at
! 		 * the target--simulate "not a directory" error
! 		 */
  		errno = ENOTDIR;
! 		err(EXIT_FAILURE, "%s",
! 		    stderr_isatty ? printescaped(sourcedir) : sourcedir);
  		/* NOTREACHED */
  	}
  	if (stat(sourcedir, &sb)) {
! 		err(EXIT_FAILURE, "%s",
! 		    stderr_isatty ? printescaped(sourcedir) : sourcedir);
  		/* NOTREACHED */
  	}
  	if (!S_ISDIR(sb.st_mode)) {
***************
*** 152,185 ****
  	struct stat sb;
  	char *p, path[MAXPATHLEN];
  	char *sn, *tn;
! 
! 	sn = printescaped(source);
! 	tn = printescaped(target);
  
  	if (!sflag) {
  		/* If target doesn't exist, quit now. */
  		if (stat(target, &sb)) {
  			warn("%s", tn);
! 			free(sn);
! 			free(tn);
! 			return (1);
  		}
  	}
  
! 	/* If the source is a directory (and not a symlink if hflag),
! 	   append the target's name. */
  	if (isdir ||
! 	    (!lstat(source, &sb) && S_ISDIR(sb.st_mode)) ||
! 	    (!hflag && !stat(source, &sb) && S_ISDIR(sb.st_mode))) {
  		if ((p = strrchr(target, '/')) == NULL)
  			p = target;
  		else
  			++p;
! 		p = printescaped(p);
! 		(void)snprintf(path, sizeof(path), "%s/%s", sn, p);
! 		free(p);
  		source = path;
  	}
  
  	/*
  	 * If the file exists, and -f was specified, unlink it.
--- 158,195 ----
  	struct stat sb;
  	char *p, path[MAXPATHLEN];
  	char *sn, *tn;
! 	int rv = 1;
  
+ 	if (stderr_isatty)
+ 		tn = printescaped(target);
+ 	else
+ 		tn = target;
  	if (!sflag) {
  		/* If target doesn't exist, quit now. */
  		if (stat(target, &sb)) {
  			warn("%s", tn);
! 			goto out1;
  		}
  	}
  
! 	/*
! 	 * If the source is a directory (and not a symlink if hflag),
! 	 * append the target's name.
! 	 */
  	if (isdir ||
! 	    ((hflag ? lstat : stat)(source, &sb) == 0 &&
! 	    S_ISDIR(sb.st_mode))) {
  		if ((p = strrchr(target, '/')) == NULL)
  			p = target;
  		else
  			++p;
! 		(void)snprintf(path, sizeof(path), "%s/%s", source, p);
  		source = path;
  	}
+ 	if (stderr_isatty)
+ 		sn = printescaped(source);
+ 	else
+ 		sn = source;
  
  	/*
  	 * If the file exists, and -f was specified, unlink it.
***************
*** 187,203 ****
  	 */
  	if ((fflag && unlink(source) < 0 && errno != ENOENT) ||
  	    (*linkf)(target, source)) {
! 		warn("%s", source);
! 		free(sn);
! 		free(tn);
! 		return (1);
  	}
! 	if (vflag)
! 		(void)printf("%s %c> %s\n", sn, linkch, tn);
  
! 	free(sn);
! 	free(tn);
! 	return (0);
  }
  
  void
--- 197,224 ----
  	 */
  	if ((fflag && unlink(source) < 0 && errno != ENOENT) ||
  	    (*linkf)(target, source)) {
! 		warn("%s", sn);
! 		goto out2;
! 	}
! 	if (vflag) {
! 		if (stdout_isatty) {
! 			if (!stderr_isatty) {
! 				tn = printescaped(target);
! 				sn = printescaped(source);
! 			}
! 			(void)printf("%s %c> %s\n", sn, linkch, tn);
! 		} else
! 			(void)printf("%s %c> %s\n", source, linkch, target);
  	}
! 	rv = 0;
  
! out2:
! 	if (sn != source)
! 		free(sn);
! out1:
! 	if (tn != target)
! 		free(tn);
! 	return (rv);
  }
  
  void
***************
*** 205,211 ****
  {
  
  	(void)fprintf(stderr,
! 	    "Usage:\t%s [-fhns] file1 file2\n\t%s [-fhnsv] file ... directory\n",
  	    getprogname(), getprogname());
  	exit(1);
  	/* NOTREACHED */
--- 226,233 ----
  {
  
  	(void)fprintf(stderr,
! 	    "Usage:\t%s [-fhns] file1 file2\n"
! 	    "\t%s [-fhnsv] file ... directory\n",
  	    getprogname(), getprogname());
  	exit(1);
  	/* NOTREACHED */
***************
*** 218,236 ****
  	char *retval;
  
  	len = strlen(src);
! 	if (len != 0 && SIZE_T_MAX/len <= 4) {
! 		errx(EXIT_FAILURE, "%s: name too long", src);
  		/* NOTREACHED */
  	}
  
! 	retval = (char *)malloc(4*len+1);
! 	if (retval != NULL) {
! 		if (stdout_ok)
! 			(void)strvis(retval, src, VIS_NL | VIS_CSTYLE);
! 		else
! 			(void)strlcpy(retval, src, 4 * len + 1);
! 		return retval;
! 	} else
! 		errx(EXIT_FAILURE, "out of memory!");
  		/* NOTREACHED */
  }
--- 240,256 ----
  	char *retval;
  
  	len = strlen(src);
! 	if (len >= SIZE_T_MAX / 4) {
! 		errx(EXIT_FAILURE, "%s: name too long: %d", src, len);
  		/* NOTREACHED */
  	}
  
! 	retval = (char *)malloc(4 * len + 1);
! 	if (retval == NULL) {
! 		err(EXIT_FAILURE, "malloc(%d)", 4 * len + 1);
  		/* NOTREACHED */
+ 	}
+ 
+ 	(void)strvis(retval, src, VIS_NL | VIS_CSTYLE);
+ 	return (retval);
  }