zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: chown and chgrp in files module
@ 1999-12-09 16:02 zefram
  1999-12-09 17:20 ` James Kirkpatrick
  0 siblings, 1 reply; 7+ messages in thread
From: zefram @ 1999-12-09 16:02 UTC (permalink / raw)
  To: zsh-workers

I got a request for chown/chgrp as shell builtins, so here they are.
It's pretty simple; most of the code change is actually just factoring
out the recursion code from rm, so that it can be shared with chown.
As a bonus, chown gets rm's -s option.

I'm not 100% happy about the error handling in the recursion code.
It seems to me that if the current directory is lost, the error message
and return code should not be suppressed by rm's -f option.  But I've
left it as it is, because I don't immediately see how to separate out
that case from the other errors handled by that code.

-zefram

diff -cr ../zsh-/Doc/Zsh/mod_files.yo ./Doc/Zsh/mod_files.yo
*** ../zsh-/Doc/Zsh/mod_files.yo	Sun Nov 28 17:42:27 1999
--- ./Doc/Zsh/mod_files.yo	Thu Dec  9 12:33:23 1999
***************
*** 4,9 ****
--- 4,46 ----
  The tt(files) module makes some standard commands available as builtins:
  
  startitem()
+ findex(chgrp)
+ item(tt(chgrp) [ tt(-Rs) ] var(group) var(filename) ...)(
+ Changes group of files specified.  This is equivalent to tt(chown) with
+ a var(user-spec) argument of `tt(:)var(group)'.
+ )
+ findex(chown)
+ item(tt(chown) [ tt(-Rs) ] var(user-spec) var(filename) ...)(
+ Changes ownership and group of files specified.
+ 
+ The var(user-spec) can be in four forms:
+ 
+ startsitem()
+ sitem(var(user))(change owner to var(user); do not change group)
+ sitem(var(user)tt(:))(change owner to var(user); change group to var(user)'s primary group)
+ sitem(var(user)tt(:)var(group))(change owner to var(user); change group to var(group))
+ sitem(tt(:)var(group))(do not change owner; change group to var(group))
+ endsitem()
+ 
+ In each case, the `tt(:)' may instead be a `tt(.)'.
+ Each of var(user) and var(group) may be either a username (or group name, as
+ appropriate) or a decimal user ID (group ID).  Interpretation as a name
+ takes precedence, if there is an all-numeric username (or group name).
+ 
+ The tt(-R) option causes tt(chown) to recursively descend into directories,
+ changing the ownership of all files in the directory after
+ changing the ownership of the directory itself.
+ 
+ The tt(-s) option is a zsh extension to tt(chown) functionality.  It enables
+ paranoid behaviour, intended to avoid security problems involving
+ a tt(chown) being tricked into affecting files other than the ones
+ intended.  It will refuse to follow symbolic links, so that (for example)
+ ``tt(chown luser /tmp/foo/passwd)'' can't accidentally chown tt(/etc/passwd)
+ if tt(/tmp/foo) happens to be a link to tt(/etc).  It will also check
+ where it is after leaving directories, so that a recursive chown of
+ a deep directory tree can't end up recursively chowning tt(/usr) as
+ a result of directories being moved up the tree.
+ )
  findex(ln)
  xitem(tt(ln) [ tt(-dfis) ] var(filename) var(dest))
  item(tt(ln) [ tt(-dfis) ] var(filename) ... var(dir))(
diff -cr ../zsh-/Src/Modules/files.c ./Src/Modules/files.c
*** ../zsh-/Src/Modules/files.c	Sun Nov 28 17:42:28 1999
--- ./Src/Modules/files.c	Thu Dec  9 15:43:52 1999
***************
*** 30,35 ****
--- 30,36 ----
  #include "files.mdh"
  
  typedef int (*MoveFunc) _((char const *, char const *));
+ typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *));
  
  #ifndef STDC_HEADERS
  extern int link _((const char *, const char *));
***************
*** 37,42 ****
--- 38,45 ----
  extern int rename _((const char *, const char *));
  #endif
  
+ struct recursivecmd;
+ 
  #include "files.pro"
  
  /**/
***************
*** 312,331 ****
      return 0;
  }
  
! /* rm builtin */
  
  /**/
  static int
! bin_rm(char *nam, char **args, char *ops, int func)
  {
      int err = 0, len;
      char *rp, *s;
      struct dirsav ds;
  
      ds.ino = ds.dev = 0;
      ds.dirname = NULL;
      ds.dirfd = ds.level = -1;
!     if (ops['r'] || ops['s']) {
  	if ((ds.dirfd = open(".", O_RDONLY|O_NOCTTY)) < 0 &&
  	    zgetdir(&ds) && *ds.dirname != '/')
  	    ds.dirfd = open("..", O_RDONLY|O_NOCTTY);
--- 315,356 ----
      return 0;
  }
  
! /* general recursion */
! 
! struct recursivecmd {
!     char *nam;
!     int opt_noerr;
!     int opt_recurse;
!     int opt_safe;
!     RecurseFunc dirpre_func;
!     RecurseFunc dirpost_func;
!     RecurseFunc leaf_func;
!     void *magic;
! };
  
  /**/
  static int
! recursivecmd(char *nam, int opt_noerr, int opt_recurse, int opt_safe,
!     char **args, RecurseFunc dirpre_func, RecurseFunc dirpost_func,
!     RecurseFunc leaf_func, void *magic)
  {
      int err = 0, len;
      char *rp, *s;
      struct dirsav ds;
+     struct recursivecmd reccmd;
  
+     reccmd.nam = nam;
+     reccmd.opt_noerr = opt_noerr;
+     reccmd.opt_recurse = opt_recurse;
+     reccmd.opt_safe = opt_safe;
+     reccmd.dirpre_func = dirpre_func;
+     reccmd.dirpost_func = dirpost_func;
+     reccmd.leaf_func = leaf_func;
+     reccmd.magic = magic;
      ds.ino = ds.dev = 0;
      ds.dirname = NULL;
      ds.dirfd = ds.level = -1;
!     if (opt_recurse || opt_safe) {
  	if ((ds.dirfd = open(".", O_RDONLY|O_NOCTTY)) < 0 &&
  	    zgetdir(&ds) && *ds.dirname != '/')
  	    ds.dirfd = open("..", O_RDONLY|O_NOCTTY);
***************
*** 333,339 ****
      for(; !errflag && !(err & 2) && *args; args++) {
  	rp = ztrdup(*args);
  	unmetafy(rp, &len);
! 	if (ops['s']) {
  	    s = strrchr(rp, '/');
  	    if (s && !s[1]) {
  		while (*s == '/' && s > rp)
--- 358,364 ----
      for(; !errflag && !(err & 2) && *args; args++) {
  	rp = ztrdup(*args);
  	unmetafy(rp, &len);
! 	if (opt_safe) {
  	    s = strrchr(rp, '/');
  	    if (s && !s[1]) {
  		while (*s == '/' && s > rp)
***************
*** 353,368 ****
  		    d.ino = d.dev = 0;
  		    d.dirname = NULL;
  		    d.dirfd = d.level = -1;
! 		    err |= dorm(nam, *args, s + 1, ops, &d, 0);
  		    zsfree(d.dirname);
  		    if (restoredir(&ds))
  			err |= 2;
! 		} else
  		    zwarnnam(nam, "%s: %e", *args, errno);
  	    } else
! 		err |= dorm(nam, *args, rp, ops, &ds, 0);
  	} else
! 	    err |= dorm(nam, *args, rp, ops, &ds, 1);
  	zfree(rp, len + 1);
      }
      if ((err & 2) && ds.dirfd >= 0 && restoredir(&ds) && zchdir(pwd)) {
--- 378,393 ----
  		    d.ino = d.dev = 0;
  		    d.dirname = NULL;
  		    d.dirfd = d.level = -1;
! 		    err |= recursivecmd_doone(&reccmd, *args, s + 1, &d, 0);
  		    zsfree(d.dirname);
  		    if (restoredir(&ds))
  			err |= 2;
! 		} else if(!opt_noerr)
  		    zwarnnam(nam, "%s: %e", *args, errno);
  	    } else
! 		err |= recursivecmd_doone(&reccmd, *args, rp, &ds, 0);
  	} else
! 	    err |= recursivecmd_doone(&reccmd, *args, rp, &ds, 1);
  	zfree(rp, len + 1);
      }
      if ((err & 2) && ds.dirfd >= 0 && restoredir(&ds) && zchdir(pwd)) {
***************
*** 373,448 ****
      if (ds.dirfd >= 0)
  	close(ds.dirfd);
      zsfree(ds.dirname);
!     return ops['f'] ? 0 : !!err;
  }
  
  /**/
  static int
! dorm(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
  {
!     struct stat st;
  
!     if((!ops['d'] || !ops['f']) && !lstat(rp, &st)) {
! 	if(!ops['d'] && S_ISDIR(st.st_mode)) {
! 	    if(ops['r'])
! 		return dormr(nam, arg, rp, ops, ds, first);
! 	    if(!ops['f'])
! 		zwarnnam(nam, "%s: %e", arg, EISDIR);
! 	    return 1;
! 	}
! 	if(!ops['f'] && ops['i']) {
! 	    nicezputs(nam, stderr);
! 	    fputs(": remove `", stderr);
! 	    nicezputs(arg, stderr);
! 	    fputs("'? ", stderr);
! 	    fflush(stderr);
! 	    if(!ask())
! 		return 0;
! 	} else if(!ops['f'] &&
! 		!S_ISLNK(st.st_mode) &&
! 	    	access(rp, W_OK)) {
! 	    nicezputs(nam, stderr);
! 	    fputs(": remove `", stderr);
! 	    nicezputs(arg, stderr);
! 	    fprintf(stderr, "', overriding mode %04o? ",
! 		mode_to_octal(st.st_mode));
! 	    fflush(stderr);
! 	    if(!ask())
! 		return 0;
! 	}
      }
!     if(!unlink(rp))
! 	return 0;
!     if(!ops['f'])
! 	zwarnnam(nam, "%s: %e", arg, errno);
!     return 1;
  }
  
  /**/
  static int
! dormr(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
  {
      char *fn;
      DIR *d;
!     int err;
      struct dirsav dsav;
      char *files = NULL;
      int fileslen = 0;
  
      err = -lchdir(rp, ds, !first);
      if (err) {
! 	if (!ops['f'])
! 	    zwarnnam(nam, "%s: %e", arg, errno);
  	return err;
      }
  
      dsav.ino = dsav.dev = 0;
      dsav.dirname = NULL;
      dsav.dirfd = dsav.level = -1;
      d = opendir(".");
      if(!d) {
! 	if(!ops['f'])
! 	    zwarnnam(nam, "%s: %e", arg, errno);
  	err = 1;
      } else {
  	int arglen = strlen(arg) + 1;
--- 398,452 ----
      if (ds.dirfd >= 0)
  	close(ds.dirfd);
      zsfree(ds.dirname);
!     return !!err;
  }
  
  /**/
  static int
! recursivecmd_doone(struct recursivecmd const *reccmd,
!     char *arg, char *rp, struct dirsav *ds, int first)
  {
!     struct stat st, *sp = NULL;
  
!     if(reccmd->opt_recurse && !lstat(rp, &st)) {
! 	if(S_ISDIR(st.st_mode))
! 	    return recursivecmd_dorec(reccmd, arg, rp, &st, ds, first);
! 	sp = &st;
      }
!     return reccmd->leaf_func(arg, rp, sp, reccmd->magic);
  }
  
  /**/
  static int
! recursivecmd_dorec(struct recursivecmd const *reccmd,
!     char *arg, char *rp, struct stat const *sp, struct dirsav *ds, int first)
  {
      char *fn;
      DIR *d;
!     int err, err1;
      struct dirsav dsav;
      char *files = NULL;
      int fileslen = 0;
  
+     err1 = reccmd->dirpre_func(arg, rp, sp, reccmd->magic);
+     if(err1 & 2)
+ 	return 2;
+ 
      err = -lchdir(rp, ds, !first);
      if (err) {
! 	if(!reccmd->opt_noerr)
! 	    zwarnnam(reccmd->nam, "%s: %e", arg, errno);
  	return err;
      }
+     err = err1;
  
      dsav.ino = dsav.dev = 0;
      dsav.dirname = NULL;
      dsav.dirfd = dsav.level = -1;
      d = opendir(".");
      if(!d) {
! 	if(!reccmd->opt_noerr)
! 	    zwarnnam(reccmd->nam, "%s: %e", arg, errno);
  	err = 1;
      } else {
  	int arglen = strlen(arg) + 1;
***************
*** 462,468 ****
  	    narg[arglen-1] = '/';
  	    strcpy(narg + arglen, fn);
  	    unmetafy(fn, NULL);
! 	    err |= dorm(nam, narg, fn, ops, &dsav, 0);
  	    fn += l;
  	}
  	hrealloc(files, fileslen, 0);
--- 466,472 ----
  	    narg[arglen-1] = '/';
  	    strcpy(narg + arglen, fn);
  	    unmetafy(fn, NULL);
! 	    err |= recursivecmd_doone(reccmd, narg, fn, &dsav, 0);
  	    fn += l;
  	}
  	hrealloc(files, fileslen, 0);
***************
*** 471,495 ****
      if (err & 2)
  	return 2;
      if (restoredir(ds)) {
! 	if(!ops['f'])
! 	    zwarnnam(nam, "failed to return to previous directory: %e",
  		     NULL, errno);
  	return 2;
      }
!     if(!ops['f'] && ops['i']) {
! 	nicezputs(nam, stderr);
  	fputs(": remove `", stderr);
  	nicezputs(arg, stderr);
  	fputs("'? ", stderr);
  	fflush(stderr);
  	if(!ask())
! 	    return err;
      }
!     if(!rmdir(rp))
! 	return err;
!     if(!ops['f'])
! 	zwarnnam(nam, "%s: %e", arg, errno);
!     return 1;
  }
  
  /* module paraphernalia */
--- 475,693 ----
      if (err & 2)
  	return 2;
      if (restoredir(ds)) {
! 	if(!reccmd->opt_noerr)
! 	    zwarnnam(reccmd->nam, "failed to return to previous directory: %e",
  		     NULL, errno);
  	return 2;
      }
!     return err | reccmd->dirpost_func(arg, rp, sp, reccmd->magic);
! }
! 
! /**/
! static int
! recurse_donothing(char *arg, char *rp, struct stat const *sp, void *magic)
! {
!     return 0;
! }
! 
! /* rm builtin */
! 
! struct rmmagic {
!     char *nam;
!     int opt_force;
!     int opt_interact;
!     int opt_unlinkdir;
! };
! 
! /**/
! static int
! rm_leaf(char *arg, char *rp, struct stat const *sp, void *magic)
! {
!     struct rmmagic *rmm = magic;
!     struct stat st;
! 
!     if(!rmm->opt_unlinkdir || !rmm->opt_force) {
! 	if(!sp) {
! 	    if(!lstat(rp, &st))
! 		sp = &st;
! 	}
! 	if(sp) {
! 	    if(!rmm->opt_unlinkdir && S_ISDIR(sp->st_mode)) {
! 		if(rmm->opt_force)
! 		    return 0;
! 		zwarnnam(rmm->nam, "%s: %e", arg, EISDIR);
! 		return 1;
! 	    }
! 	    if(rmm->opt_interact) {
! 		nicezputs(rmm->nam, stderr);
! 		fputs(": remove `", stderr);
! 		nicezputs(arg, stderr);
! 		fputs("'? ", stderr);
! 		fflush(stderr);
! 		if(!ask())
! 		    return 0;
! 	    } else if(!rmm->opt_force &&
! 		    !S_ISLNK(sp->st_mode) &&
! 		    access(rp, W_OK)) {
! 		nicezputs(rmm->nam, stderr);
! 		fputs(": remove `", stderr);
! 		nicezputs(arg, stderr);
! 		fprintf(stderr, "', overriding mode %04o? ",
! 		    mode_to_octal(sp->st_mode));
! 		fflush(stderr);
! 		if(!ask())
! 		    return 0;
! 	    }
! 	}
!     }
!     if(unlink(rp) && !rmm->opt_force) {
! 	zwarnnam(rmm->nam, "%s: %e", arg, errno);
! 	return 1;
!     }
!     return 0;
! }
! 
! /**/
! static int
! rm_dirpost(char *arg, char *rp, struct stat const *sp, void *magic)
! {
!     struct rmmagic *rmm = magic;
! 
!     if(rmm->opt_interact) {
! 	nicezputs(rmm->nam, stderr);
  	fputs(": remove `", stderr);
  	nicezputs(arg, stderr);
  	fputs("'? ", stderr);
  	fflush(stderr);
  	if(!ask())
! 	    return 0;
      }
!     if(rmdir(rp) && !rmm->opt_force) {
! 	zwarnnam(rmm->nam, "%s: %e", arg, errno);
! 	return 1;
!     }
!     return 0;
! }
! 
! /**/
! static int
! bin_rm(char *nam, char **args, char *ops, int func)
! {
!     struct rmmagic rmm;
!     int err;
! 
!     rmm.nam = nam;
!     rmm.opt_force = ops['f'];
!     rmm.opt_interact = ops['i'] && !ops['f'];
!     rmm.opt_unlinkdir = ops['d'];
!     err = recursivecmd(nam, ops['f'], ops['r'] && !ops['d'], ops['s'],
! 	args, recurse_donothing, rm_dirpost, rm_leaf, &rmm);
!     return ops['f'] ? 0 : err;
! }
! 
! /* chown builtin */
! 
! struct chownmagic {
!     char *nam;
!     uid_t uid;
!     gid_t gid;
! };
! 
! /**/
! static int
! chown_dochown(char *arg, char *rp, struct stat const *sp, void *magic)
! {
!     struct chownmagic *chm = magic;
! 
!     if(lchown(rp, chm->uid, chm->gid)) {
! 	zwarnnam(chm->nam, "%s: %e", arg, errno);
! 	return 1;
!     }
!     return 0;
! }
! 
! /**/
! static unsigned long getnumeric(char *p, int *errp)
! {
!     unsigned long ret;
! 
!     if(*p < '0' || *p > '9') {
! 	*errp = 1;
! 	return 0;
!     }
!     ret = strtoul(p, &p, 10);
!     *errp = !!*p;
!     return ret;
! }
! 
! enum { BIN_CHOWN, BIN_CHGRP };
! 
! /**/
! static int
! bin_chown(char *nam, char **args, char *ops, int func)
! {
!     struct chownmagic chm;
!     char *uspec = ztrdup(*args), *p = uspec;
! 
!     chm.nam = nam;
!     if(func == BIN_CHGRP) {
! 	chm.uid = -1;
! 	goto dogroup;
!     }
!     if(*p == ':' || *p == '.') {
! 	chm.uid = -1;
! 	p++;
! 	goto dogroup;
!     } else {
! 	struct passwd *pwd;
! 	char *end = strchr(p, ':');
! 	if(!end)
! 	    end = strchr(p, '.');
! 	if(end)
! 	    *end = 0;
! 	pwd = getpwnam(p);
! 	if(pwd)
! 	    chm.uid = pwd->pw_uid;
! 	else {
! 	    int err;
! 	    chm.uid = getnumeric(p, &err);
! 	    if(err) {
! 		zwarnnam(nam, "%s: no such user", p, 0);
! 		free(uspec);
! 		return 1;
! 	    }
! 	}
! 	if(end) {
! 	    p = end+1;
! 	    if(!*p) {
! 		if(!pwd && !(pwd = getpwuid(chm.uid))) {
! 		    zwarnnam(nam, "%s: no such user", uspec, 0);
! 		    free(uspec);
! 		    return 1;
! 		}
! 		chm.gid = pwd->pw_gid;
! 	    } else {
! 		struct group *grp;
! 		dogroup:
! 		grp = getgrnam(p);
! 		if(grp)
! 		    chm.gid = grp->gr_gid;
! 		else {
! 		    int err;
! 		    chm.gid = getnumeric(p, &err);
! 		    if(err) {
! 			zwarnnam(nam, "%s: no such group", p, 0);
! 			free(uspec);
! 			return 1;
! 		    }
! 		}
! 	    }
! 	 } else
! 	    chm.gid = -1;
!     }
!     free(uspec);
!     return recursivecmd(nam, 0, ops['R'], ops['s'],
! 	args + 1, chown_dochown, recurse_donothing, chown_dochown, &chm);
  }
  
  /* module paraphernalia */
***************
*** 501,512 ****
  #endif
  
  static struct builtin bintab[] = {
!     BUILTIN("ln",    0, bin_ln,    1, -1, BIN_LN, LN_OPTS, NULL),
!     BUILTIN("mkdir", 0, bin_mkdir, 1, -1, 0,      "pm",    NULL),
!     BUILTIN("mv",    0, bin_ln,    2, -1, BIN_MV, "fi",    NULL),
!     BUILTIN("rm",    0, bin_rm,    1, -1, 0,      "dfirs", NULL),
!     BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0,      NULL,    NULL),
!     BUILTIN("sync",  0, bin_sync,  0,  0, 0,      NULL,    NULL),
  };
  
  /**/
--- 699,712 ----
  #endif
  
  static struct builtin bintab[] = {
!     BUILTIN("chgrp", 0, bin_chown, 2, -1, BIN_CHGRP, "Rs",    NULL),
!     BUILTIN("chown", 0, bin_chown, 2, -1, BIN_CHOWN, "Rs",    NULL),
!     BUILTIN("ln",    0, bin_ln,    1, -1, BIN_LN,    LN_OPTS, NULL),
!     BUILTIN("mkdir", 0, bin_mkdir, 1, -1, 0,         "pm",    NULL),
!     BUILTIN("mv",    0, bin_ln,    2, -1, BIN_MV,    "fi",    NULL),
!     BUILTIN("rm",    0, bin_rm,    1, -1, 0,         "dfirs", NULL),
!     BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0,         NULL,    NULL),
!     BUILTIN("sync",  0, bin_sync,  0,  0, 0,         NULL,    NULL),
  };
  
  /**/
diff -cr ../zsh-/Src/Modules/files.mdd ./Src/Modules/files.mdd
*** ../zsh-/Src/Modules/files.mdd	Sun Nov 28 17:42:28 1999
--- ./Src/Modules/files.mdd	Thu Dec  9 12:33:44 1999
***************
*** 1,3 ****
! autobins="ln mkdir mv rm rmdir sync"
  
  objects="files.o"
--- 1,3 ----
! autobins="chgrp chown ln mkdir mv rm rmdir sync"
  
  objects="files.o"
diff -cr ../zsh-/Src/system.h ./Src/system.h
*** ../zsh-/Src/system.h	Sun Nov 28 17:42:28 1999
--- ./Src/system.h	Thu Dec  9 15:19:03 1999
***************
*** 583,588 ****
--- 583,592 ----
  # define R_OK 4
  #endif
  
+ #ifndef HAVE_LCHOWN
+ # define lchown chown
+ #endif
+ 
  #ifndef HAVE_MEMCPY
  # define memcpy memmove
  #endif
diff -cr ../zsh-/configure.in ./configure.in
*** ../zsh-/configure.in	Wed Dec  8 19:58:26 1999
--- ./configure.in	Thu Dec  9 15:18:05 1999
***************
*** 784,790 ****
  dnl need to integrate this function
  dnl AC_FUNC_STRFTIME
  
! AC_CHECK_FUNCS(memcpy memmove \
                strftime waitpid select poll tcsetpgrp tcgetattr strstr lstat \
                getlogin setpgid gettimeofday gethostname mkfifo wait3 difftime \
                sigblock sigsetmask sigrelse sighold killpg sigaction getrlimit \
--- 784,790 ----
  dnl need to integrate this function
  dnl AC_FUNC_STRFTIME
  
! AC_CHECK_FUNCS(lchown memcpy memmove \
                strftime waitpid select poll tcsetpgrp tcgetattr strstr lstat \
                getlogin setpgid gettimeofday gethostname mkfifo wait3 difftime \
                sigblock sigsetmask sigrelse sighold killpg sigaction getrlimit \
END


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PATCH: chown and chgrp in files module
  1999-12-09 16:02 PATCH: chown and chgrp in files module zefram
@ 1999-12-09 17:20 ` James Kirkpatrick
  1999-12-09 17:41   ` Zefram
  0 siblings, 1 reply; 7+ messages in thread
From: James Kirkpatrick @ 1999-12-09 17:20 UTC (permalink / raw)
  To: zefram; +Cc: zsh-workers

I'm not sure I understand.  chown and chgrp are now builtins?  I need to
object to this.  We use, for example, the GNU shell utilities, and rely on
certain of the options and syntaxes available.  Will the zsh builtins
attempt to emulate all options from everybody's chown and chgrp?  Are they
POSIX compliant?

This does not seem to be the sort of thing that should be built in to a
shell, any more than rm, ls, find, or a C compiler :-)

Jim

On Thu, 9 Dec 1999 zefram@fysh.org wrote:

> I got a request for chown/chgrp as shell builtins, so here they are.
> It's pretty simple; most of the code change is actually just factoring
> out the recursion code from rm, so that it can be shared with chown.
> As a bonus, chown gets rm's -s option.
> 
> I'm not 100% happy about the error handling in the recursion code.
> It seems to me that if the current directory is lost, the error message
> and return code should not be suppressed by rm's -f option.  But I've
> left it as it is, because I don't immediately see how to separate out
> that case from the other errors handled by that code.
> 
> -zefram
> 
> diff -cr ../zsh-/Doc/Zsh/mod_files.yo ./Doc/Zsh/mod_files.yo
> *** ../zsh-/Doc/Zsh/mod_files.yo	Sun Nov 28 17:42:27 1999
> --- ./Doc/Zsh/mod_files.yo	Thu Dec  9 12:33:23 1999
> ***************
> *** 4,9 ****
> --- 4,46 ----
>   The tt(files) module makes some standard commands available as builtins:
>   
>   startitem()
> + findex(chgrp)
> + item(tt(chgrp) [ tt(-Rs) ] var(group) var(filename) ...)(
> + Changes group of files specified.  This is equivalent to tt(chown) with
> + a var(user-spec) argument of `tt(:)var(group)'.
> + )
> + findex(chown)
> + item(tt(chown) [ tt(-Rs) ] var(user-spec) var(filename) ...)(
> + Changes ownership and group of files specified.
> + 
> + The var(user-spec) can be in four forms:
> + 
> + startsitem()
> + sitem(var(user))(change owner to var(user); do not change group)
> + sitem(var(user)tt(:))(change owner to var(user); change group to var(user)'s primary group)
> + sitem(var(user)tt(:)var(group))(change owner to var(user); change group to var(group))
> + sitem(tt(:)var(group))(do not change owner; change group to var(group))
> + endsitem()
> + 
> + In each case, the `tt(:)' may instead be a `tt(.)'.
> + Each of var(user) and var(group) may be either a username (or group name, as
> + appropriate) or a decimal user ID (group ID).  Interpretation as a name
> + takes precedence, if there is an all-numeric username (or group name).
> + 
> + The tt(-R) option causes tt(chown) to recursively descend into directories,
> + changing the ownership of all files in the directory after
> + changing the ownership of the directory itself.
> + 
> + The tt(-s) option is a zsh extension to tt(chown) functionality.  It enables
> + paranoid behaviour, intended to avoid security problems involving
> + a tt(chown) being tricked into affecting files other than the ones
> + intended.  It will refuse to follow symbolic links, so that (for example)
> + ``tt(chown luser /tmp/foo/passwd)'' can't accidentally chown tt(/etc/passwd)
> + if tt(/tmp/foo) happens to be a link to tt(/etc).  It will also check
> + where it is after leaving directories, so that a recursive chown of
> + a deep directory tree can't end up recursively chowning tt(/usr) as
> + a result of directories being moved up the tree.
> + )
>   findex(ln)
>   xitem(tt(ln) [ tt(-dfis) ] var(filename) var(dest))
>   item(tt(ln) [ tt(-dfis) ] var(filename) ... var(dir))(
> diff -cr ../zsh-/Src/Modules/files.c ./Src/Modules/files.c
> *** ../zsh-/Src/Modules/files.c	Sun Nov 28 17:42:28 1999
> --- ./Src/Modules/files.c	Thu Dec  9 15:43:52 1999
> ***************
> *** 30,35 ****
> --- 30,36 ----
>   #include "files.mdh"
>   
>   typedef int (*MoveFunc) _((char const *, char const *));
> + typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *));
>   
>   #ifndef STDC_HEADERS
>   extern int link _((const char *, const char *));
> ***************
> *** 37,42 ****
> --- 38,45 ----
>   extern int rename _((const char *, const char *));
>   #endif
>   
> + struct recursivecmd;
> + 
>   #include "files.pro"
>   
>   /**/
> ***************
> *** 312,331 ****
>       return 0;
>   }
>   
> ! /* rm builtin */
>   
>   /**/
>   static int
> ! bin_rm(char *nam, char **args, char *ops, int func)
>   {
>       int err = 0, len;
>       char *rp, *s;
>       struct dirsav ds;
>   
>       ds.ino = ds.dev = 0;
>       ds.dirname = NULL;
>       ds.dirfd = ds.level = -1;
> !     if (ops['r'] || ops['s']) {
>   	if ((ds.dirfd = open(".", O_RDONLY|O_NOCTTY)) < 0 &&
>   	    zgetdir(&ds) && *ds.dirname != '/')
>   	    ds.dirfd = open("..", O_RDONLY|O_NOCTTY);
> --- 315,356 ----
>       return 0;
>   }
>   
> ! /* general recursion */
> ! 
> ! struct recursivecmd {
> !     char *nam;
> !     int opt_noerr;
> !     int opt_recurse;
> !     int opt_safe;
> !     RecurseFunc dirpre_func;
> !     RecurseFunc dirpost_func;
> !     RecurseFunc leaf_func;
> !     void *magic;
> ! };
>   
>   /**/
>   static int
> ! recursivecmd(char *nam, int opt_noerr, int opt_recurse, int opt_safe,
> !     char **args, RecurseFunc dirpre_func, RecurseFunc dirpost_func,
> !     RecurseFunc leaf_func, void *magic)
>   {
>       int err = 0, len;
>       char *rp, *s;
>       struct dirsav ds;
> +     struct recursivecmd reccmd;
>   
> +     reccmd.nam = nam;
> +     reccmd.opt_noerr = opt_noerr;
> +     reccmd.opt_recurse = opt_recurse;
> +     reccmd.opt_safe = opt_safe;
> +     reccmd.dirpre_func = dirpre_func;
> +     reccmd.dirpost_func = dirpost_func;
> +     reccmd.leaf_func = leaf_func;
> +     reccmd.magic = magic;
>       ds.ino = ds.dev = 0;
>       ds.dirname = NULL;
>       ds.dirfd = ds.level = -1;
> !     if (opt_recurse || opt_safe) {
>   	if ((ds.dirfd = open(".", O_RDONLY|O_NOCTTY)) < 0 &&
>   	    zgetdir(&ds) && *ds.dirname != '/')
>   	    ds.dirfd = open("..", O_RDONLY|O_NOCTTY);
> ***************
> *** 333,339 ****
>       for(; !errflag && !(err & 2) && *args; args++) {
>   	rp = ztrdup(*args);
>   	unmetafy(rp, &len);
> ! 	if (ops['s']) {
>   	    s = strrchr(rp, '/');
>   	    if (s && !s[1]) {
>   		while (*s == '/' && s > rp)
> --- 358,364 ----
>       for(; !errflag && !(err & 2) && *args; args++) {
>   	rp = ztrdup(*args);
>   	unmetafy(rp, &len);
> ! 	if (opt_safe) {
>   	    s = strrchr(rp, '/');
>   	    if (s && !s[1]) {
>   		while (*s == '/' && s > rp)
> ***************
> *** 353,368 ****
>   		    d.ino = d.dev = 0;
>   		    d.dirname = NULL;
>   		    d.dirfd = d.level = -1;
> ! 		    err |= dorm(nam, *args, s + 1, ops, &d, 0);
>   		    zsfree(d.dirname);
>   		    if (restoredir(&ds))
>   			err |= 2;
> ! 		} else
>   		    zwarnnam(nam, "%s: %e", *args, errno);
>   	    } else
> ! 		err |= dorm(nam, *args, rp, ops, &ds, 0);
>   	} else
> ! 	    err |= dorm(nam, *args, rp, ops, &ds, 1);
>   	zfree(rp, len + 1);
>       }
>       if ((err & 2) && ds.dirfd >= 0 && restoredir(&ds) && zchdir(pwd)) {
> --- 378,393 ----
>   		    d.ino = d.dev = 0;
>   		    d.dirname = NULL;
>   		    d.dirfd = d.level = -1;
> ! 		    err |= recursivecmd_doone(&reccmd, *args, s + 1, &d, 0);
>   		    zsfree(d.dirname);
>   		    if (restoredir(&ds))
>   			err |= 2;
> ! 		} else if(!opt_noerr)
>   		    zwarnnam(nam, "%s: %e", *args, errno);
>   	    } else
> ! 		err |= recursivecmd_doone(&reccmd, *args, rp, &ds, 0);
>   	} else
> ! 	    err |= recursivecmd_doone(&reccmd, *args, rp, &ds, 1);
>   	zfree(rp, len + 1);
>       }
>       if ((err & 2) && ds.dirfd >= 0 && restoredir(&ds) && zchdir(pwd)) {
> ***************
> *** 373,448 ****
>       if (ds.dirfd >= 0)
>   	close(ds.dirfd);
>       zsfree(ds.dirname);
> !     return ops['f'] ? 0 : !!err;
>   }
>   
>   /**/
>   static int
> ! dorm(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
>   {
> !     struct stat st;
>   
> !     if((!ops['d'] || !ops['f']) && !lstat(rp, &st)) {
> ! 	if(!ops['d'] && S_ISDIR(st.st_mode)) {
> ! 	    if(ops['r'])
> ! 		return dormr(nam, arg, rp, ops, ds, first);
> ! 	    if(!ops['f'])
> ! 		zwarnnam(nam, "%s: %e", arg, EISDIR);
> ! 	    return 1;
> ! 	}
> ! 	if(!ops['f'] && ops['i']) {
> ! 	    nicezputs(nam, stderr);
> ! 	    fputs(": remove `", stderr);
> ! 	    nicezputs(arg, stderr);
> ! 	    fputs("'? ", stderr);
> ! 	    fflush(stderr);
> ! 	    if(!ask())
> ! 		return 0;
> ! 	} else if(!ops['f'] &&
> ! 		!S_ISLNK(st.st_mode) &&
> ! 	    	access(rp, W_OK)) {
> ! 	    nicezputs(nam, stderr);
> ! 	    fputs(": remove `", stderr);
> ! 	    nicezputs(arg, stderr);
> ! 	    fprintf(stderr, "', overriding mode %04o? ",
> ! 		mode_to_octal(st.st_mode));
> ! 	    fflush(stderr);
> ! 	    if(!ask())
> ! 		return 0;
> ! 	}
>       }
> !     if(!unlink(rp))
> ! 	return 0;
> !     if(!ops['f'])
> ! 	zwarnnam(nam, "%s: %e", arg, errno);
> !     return 1;
>   }
>   
>   /**/
>   static int
> ! dormr(char *nam, char *arg, char *rp, char *ops, struct dirsav *ds, int first)
>   {
>       char *fn;
>       DIR *d;
> !     int err;
>       struct dirsav dsav;
>       char *files = NULL;
>       int fileslen = 0;
>   
>       err = -lchdir(rp, ds, !first);
>       if (err) {
> ! 	if (!ops['f'])
> ! 	    zwarnnam(nam, "%s: %e", arg, errno);
>   	return err;
>       }
>   
>       dsav.ino = dsav.dev = 0;
>       dsav.dirname = NULL;
>       dsav.dirfd = dsav.level = -1;
>       d = opendir(".");
>       if(!d) {
> ! 	if(!ops['f'])
> ! 	    zwarnnam(nam, "%s: %e", arg, errno);
>   	err = 1;
>       } else {
>   	int arglen = strlen(arg) + 1;
> --- 398,452 ----
>       if (ds.dirfd >= 0)
>   	close(ds.dirfd);
>       zsfree(ds.dirname);
> !     return !!err;
>   }
>   
>   /**/
>   static int
> ! recursivecmd_doone(struct recursivecmd const *reccmd,
> !     char *arg, char *rp, struct dirsav *ds, int first)
>   {
> !     struct stat st, *sp = NULL;
>   
> !     if(reccmd->opt_recurse && !lstat(rp, &st)) {
> ! 	if(S_ISDIR(st.st_mode))
> ! 	    return recursivecmd_dorec(reccmd, arg, rp, &st, ds, first);
> ! 	sp = &st;
>       }
> !     return reccmd->leaf_func(arg, rp, sp, reccmd->magic);
>   }
>   
>   /**/
>   static int
> ! recursivecmd_dorec(struct recursivecmd const *reccmd,
> !     char *arg, char *rp, struct stat const *sp, struct dirsav *ds, int first)
>   {
>       char *fn;
>       DIR *d;
> !     int err, err1;
>       struct dirsav dsav;
>       char *files = NULL;
>       int fileslen = 0;
>   
> +     err1 = reccmd->dirpre_func(arg, rp, sp, reccmd->magic);
> +     if(err1 & 2)
> + 	return 2;
> + 
>       err = -lchdir(rp, ds, !first);
>       if (err) {
> ! 	if(!reccmd->opt_noerr)
> ! 	    zwarnnam(reccmd->nam, "%s: %e", arg, errno);
>   	return err;
>       }
> +     err = err1;
>   
>       dsav.ino = dsav.dev = 0;
>       dsav.dirname = NULL;
>       dsav.dirfd = dsav.level = -1;
>       d = opendir(".");
>       if(!d) {
> ! 	if(!reccmd->opt_noerr)
> ! 	    zwarnnam(reccmd->nam, "%s: %e", arg, errno);
>   	err = 1;
>       } else {
>   	int arglen = strlen(arg) + 1;
> ***************
> *** 462,468 ****
>   	    narg[arglen-1] = '/';
>   	    strcpy(narg + arglen, fn);
>   	    unmetafy(fn, NULL);
> ! 	    err |= dorm(nam, narg, fn, ops, &dsav, 0);
>   	    fn += l;
>   	}
>   	hrealloc(files, fileslen, 0);
> --- 466,472 ----
>   	    narg[arglen-1] = '/';
>   	    strcpy(narg + arglen, fn);
>   	    unmetafy(fn, NULL);
> ! 	    err |= recursivecmd_doone(reccmd, narg, fn, &dsav, 0);
>   	    fn += l;
>   	}
>   	hrealloc(files, fileslen, 0);
> ***************
> *** 471,495 ****
>       if (err & 2)
>   	return 2;
>       if (restoredir(ds)) {
> ! 	if(!ops['f'])
> ! 	    zwarnnam(nam, "failed to return to previous directory: %e",
>   		     NULL, errno);
>   	return 2;
>       }
> !     if(!ops['f'] && ops['i']) {
> ! 	nicezputs(nam, stderr);
>   	fputs(": remove `", stderr);
>   	nicezputs(arg, stderr);
>   	fputs("'? ", stderr);
>   	fflush(stderr);
>   	if(!ask())
> ! 	    return err;
>       }
> !     if(!rmdir(rp))
> ! 	return err;
> !     if(!ops['f'])
> ! 	zwarnnam(nam, "%s: %e", arg, errno);
> !     return 1;
>   }
>   
>   /* module paraphernalia */
> --- 475,693 ----
>       if (err & 2)
>   	return 2;
>       if (restoredir(ds)) {
> ! 	if(!reccmd->opt_noerr)
> ! 	    zwarnnam(reccmd->nam, "failed to return to previous directory: %e",
>   		     NULL, errno);
>   	return 2;
>       }
> !     return err | reccmd->dirpost_func(arg, rp, sp, reccmd->magic);
> ! }
> ! 
> ! /**/
> ! static int
> ! recurse_donothing(char *arg, char *rp, struct stat const *sp, void *magic)
> ! {
> !     return 0;
> ! }
> ! 
> ! /* rm builtin */
> ! 
> ! struct rmmagic {
> !     char *nam;
> !     int opt_force;
> !     int opt_interact;
> !     int opt_unlinkdir;
> ! };
> ! 
> ! /**/
> ! static int
> ! rm_leaf(char *arg, char *rp, struct stat const *sp, void *magic)
> ! {
> !     struct rmmagic *rmm = magic;
> !     struct stat st;
> ! 
> !     if(!rmm->opt_unlinkdir || !rmm->opt_force) {
> ! 	if(!sp) {
> ! 	    if(!lstat(rp, &st))
> ! 		sp = &st;
> ! 	}
> ! 	if(sp) {
> ! 	    if(!rmm->opt_unlinkdir && S_ISDIR(sp->st_mode)) {
> ! 		if(rmm->opt_force)
> ! 		    return 0;
> ! 		zwarnnam(rmm->nam, "%s: %e", arg, EISDIR);
> ! 		return 1;
> ! 	    }
> ! 	    if(rmm->opt_interact) {
> ! 		nicezputs(rmm->nam, stderr);
> ! 		fputs(": remove `", stderr);
> ! 		nicezputs(arg, stderr);
> ! 		fputs("'? ", stderr);
> ! 		fflush(stderr);
> ! 		if(!ask())
> ! 		    return 0;
> ! 	    } else if(!rmm->opt_force &&
> ! 		    !S_ISLNK(sp->st_mode) &&
> ! 		    access(rp, W_OK)) {
> ! 		nicezputs(rmm->nam, stderr);
> ! 		fputs(": remove `", stderr);
> ! 		nicezputs(arg, stderr);
> ! 		fprintf(stderr, "', overriding mode %04o? ",
> ! 		    mode_to_octal(sp->st_mode));
> ! 		fflush(stderr);
> ! 		if(!ask())
> ! 		    return 0;
> ! 	    }
> ! 	}
> !     }
> !     if(unlink(rp) && !rmm->opt_force) {
> ! 	zwarnnam(rmm->nam, "%s: %e", arg, errno);
> ! 	return 1;
> !     }
> !     return 0;
> ! }
> ! 
> ! /**/
> ! static int
> ! rm_dirpost(char *arg, char *rp, struct stat const *sp, void *magic)
> ! {
> !     struct rmmagic *rmm = magic;
> ! 
> !     if(rmm->opt_interact) {
> ! 	nicezputs(rmm->nam, stderr);
>   	fputs(": remove `", stderr);
>   	nicezputs(arg, stderr);
>   	fputs("'? ", stderr);
>   	fflush(stderr);
>   	if(!ask())
> ! 	    return 0;
>       }
> !     if(rmdir(rp) && !rmm->opt_force) {
> ! 	zwarnnam(rmm->nam, "%s: %e", arg, errno);
> ! 	return 1;
> !     }
> !     return 0;
> ! }
> ! 
> ! /**/
> ! static int
> ! bin_rm(char *nam, char **args, char *ops, int func)
> ! {
> !     struct rmmagic rmm;
> !     int err;
> ! 
> !     rmm.nam = nam;
> !     rmm.opt_force = ops['f'];
> !     rmm.opt_interact = ops['i'] && !ops['f'];
> !     rmm.opt_unlinkdir = ops['d'];
> !     err = recursivecmd(nam, ops['f'], ops['r'] && !ops['d'], ops['s'],
> ! 	args, recurse_donothing, rm_dirpost, rm_leaf, &rmm);
> !     return ops['f'] ? 0 : err;
> ! }
> ! 
> ! /* chown builtin */
> ! 
> ! struct chownmagic {
> !     char *nam;
> !     uid_t uid;
> !     gid_t gid;
> ! };
> ! 
> ! /**/
> ! static int
> ! chown_dochown(char *arg, char *rp, struct stat const *sp, void *magic)
> ! {
> !     struct chownmagic *chm = magic;
> ! 
> !     if(lchown(rp, chm->uid, chm->gid)) {
> ! 	zwarnnam(chm->nam, "%s: %e", arg, errno);
> ! 	return 1;
> !     }
> !     return 0;
> ! }
> ! 
> ! /**/
> ! static unsigned long getnumeric(char *p, int *errp)
> ! {
> !     unsigned long ret;
> ! 
> !     if(*p < '0' || *p > '9') {
> ! 	*errp = 1;
> ! 	return 0;
> !     }
> !     ret = strtoul(p, &p, 10);
> !     *errp = !!*p;
> !     return ret;
> ! }
> ! 
> ! enum { BIN_CHOWN, BIN_CHGRP };
> ! 
> ! /**/
> ! static int
> ! bin_chown(char *nam, char **args, char *ops, int func)
> ! {
> !     struct chownmagic chm;
> !     char *uspec = ztrdup(*args), *p = uspec;
> ! 
> !     chm.nam = nam;
> !     if(func == BIN_CHGRP) {
> ! 	chm.uid = -1;
> ! 	goto dogroup;
> !     }
> !     if(*p == ':' || *p == '.') {
> ! 	chm.uid = -1;
> ! 	p++;
> ! 	goto dogroup;
> !     } else {
> ! 	struct passwd *pwd;
> ! 	char *end = strchr(p, ':');
> ! 	if(!end)
> ! 	    end = strchr(p, '.');
> ! 	if(end)
> ! 	    *end = 0;
> ! 	pwd = getpwnam(p);
> ! 	if(pwd)
> ! 	    chm.uid = pwd->pw_uid;
> ! 	else {
> ! 	    int err;
> ! 	    chm.uid = getnumeric(p, &err);
> ! 	    if(err) {
> ! 		zwarnnam(nam, "%s: no such user", p, 0);
> ! 		free(uspec);
> ! 		return 1;
> ! 	    }
> ! 	}
> ! 	if(end) {
> ! 	    p = end+1;
> ! 	    if(!*p) {
> ! 		if(!pwd && !(pwd = getpwuid(chm.uid))) {
> ! 		    zwarnnam(nam, "%s: no such user", uspec, 0);
> ! 		    free(uspec);
> ! 		    return 1;
> ! 		}
> ! 		chm.gid = pwd->pw_gid;
> ! 	    } else {
> ! 		struct group *grp;
> ! 		dogroup:
> ! 		grp = getgrnam(p);
> ! 		if(grp)
> ! 		    chm.gid = grp->gr_gid;
> ! 		else {
> ! 		    int err;
> ! 		    chm.gid = getnumeric(p, &err);
> ! 		    if(err) {
> ! 			zwarnnam(nam, "%s: no such group", p, 0);
> ! 			free(uspec);
> ! 			return 1;
> ! 		    }
> ! 		}
> ! 	    }
> ! 	 } else
> ! 	    chm.gid = -1;
> !     }
> !     free(uspec);
> !     return recursivecmd(nam, 0, ops['R'], ops['s'],
> ! 	args + 1, chown_dochown, recurse_donothing, chown_dochown, &chm);
>   }
>   
>   /* module paraphernalia */
> ***************
> *** 501,512 ****
>   #endif
>   
>   static struct builtin bintab[] = {
> !     BUILTIN("ln",    0, bin_ln,    1, -1, BIN_LN, LN_OPTS, NULL),
> !     BUILTIN("mkdir", 0, bin_mkdir, 1, -1, 0,      "pm",    NULL),
> !     BUILTIN("mv",    0, bin_ln,    2, -1, BIN_MV, "fi",    NULL),
> !     BUILTIN("rm",    0, bin_rm,    1, -1, 0,      "dfirs", NULL),
> !     BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0,      NULL,    NULL),
> !     BUILTIN("sync",  0, bin_sync,  0,  0, 0,      NULL,    NULL),
>   };
>   
>   /**/
> --- 699,712 ----
>   #endif
>   
>   static struct builtin bintab[] = {
> !     BUILTIN("chgrp", 0, bin_chown, 2, -1, BIN_CHGRP, "Rs",    NULL),
> !     BUILTIN("chown", 0, bin_chown, 2, -1, BIN_CHOWN, "Rs",    NULL),
> !     BUILTIN("ln",    0, bin_ln,    1, -1, BIN_LN,    LN_OPTS, NULL),
> !     BUILTIN("mkdir", 0, bin_mkdir, 1, -1, 0,         "pm",    NULL),
> !     BUILTIN("mv",    0, bin_ln,    2, -1, BIN_MV,    "fi",    NULL),
> !     BUILTIN("rm",    0, bin_rm,    1, -1, 0,         "dfirs", NULL),
> !     BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0,         NULL,    NULL),
> !     BUILTIN("sync",  0, bin_sync,  0,  0, 0,         NULL,    NULL),
>   };
>   
>   /**/
> diff -cr ../zsh-/Src/Modules/files.mdd ./Src/Modules/files.mdd
> *** ../zsh-/Src/Modules/files.mdd	Sun Nov 28 17:42:28 1999
> --- ./Src/Modules/files.mdd	Thu Dec  9 12:33:44 1999
> ***************
> *** 1,3 ****
> ! autobins="ln mkdir mv rm rmdir sync"
>   
>   objects="files.o"
> --- 1,3 ----
> ! autobins="chgrp chown ln mkdir mv rm rmdir sync"
>   
>   objects="files.o"
> diff -cr ../zsh-/Src/system.h ./Src/system.h
> *** ../zsh-/Src/system.h	Sun Nov 28 17:42:28 1999
> --- ./Src/system.h	Thu Dec  9 15:19:03 1999
> ***************
> *** 583,588 ****
> --- 583,592 ----
>   # define R_OK 4
>   #endif
>   
> + #ifndef HAVE_LCHOWN
> + # define lchown chown
> + #endif
> + 
>   #ifndef HAVE_MEMCPY
>   # define memcpy memmove
>   #endif
> diff -cr ../zsh-/configure.in ./configure.in
> *** ../zsh-/configure.in	Wed Dec  8 19:58:26 1999
> --- ./configure.in	Thu Dec  9 15:18:05 1999
> ***************
> *** 784,790 ****
>   dnl need to integrate this function
>   dnl AC_FUNC_STRFTIME
>   
> ! AC_CHECK_FUNCS(memcpy memmove \
>                 strftime waitpid select poll tcsetpgrp tcgetattr strstr lstat \
>                 getlogin setpgid gettimeofday gethostname mkfifo wait3 difftime \
>                 sigblock sigsetmask sigrelse sighold killpg sigaction getrlimit \
> --- 784,790 ----
>   dnl need to integrate this function
>   dnl AC_FUNC_STRFTIME
>   
> ! AC_CHECK_FUNCS(lchown memcpy memmove \
>                 strftime waitpid select poll tcsetpgrp tcgetattr strstr lstat \
>                 getlogin setpgid gettimeofday gethostname mkfifo wait3 difftime \
>                 sigblock sigsetmask sigrelse sighold killpg sigaction getrlimit \
> END
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PATCH: chown and chgrp in files module
  1999-12-09 17:20 ` James Kirkpatrick
@ 1999-12-09 17:41   ` Zefram
  1999-12-09 18:19     ` James Kirkpatrick
                       ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Zefram @ 1999-12-09 17:41 UTC (permalink / raw)
  To: James Kirkpatrick; +Cc: zefram, zsh-workers

James Kirkpatrick wrote:
>I'm not sure I understand.  chown and chgrp are now builtins?

If you load the files module.  If you do nothing, you get the usual
external programs.  rm, mv, ln, mkdir, rmdir and sync are already
available as builtins in exactly the same way.

>                                                                  Are they
>POSIX compliant?

Almost.  chown accepts "." to separate username and group, in addition to
the POSIX ":".  This is the same level of POSIX conformance as GNU chown.

>This does not seem to be the sort of thing that should be built in to a
>shell, any more than rm, ls, find, or a C compiler :-)

It's very useful to have these things built into a statically-linked
system administration shell.  (That's where the request for a builtin
chown originated.)  In addition to the utilities I listed above, we have
a `stat' module that effectively makes possible a full implementation
of ls as a shell function, and zsh globbing with glob qualifiers very
nearly makes find redundant.

-zefram


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PATCH: chown and chgrp in files module
  1999-12-09 17:41   ` Zefram
@ 1999-12-09 18:19     ` James Kirkpatrick
  1999-12-09 23:40     ` James Antill
  1999-12-10 21:43     ` Clint Adams
  2 siblings, 0 replies; 7+ messages in thread
From: James Kirkpatrick @ 1999-12-09 18:19 UTC (permalink / raw)
  To: Zefram; +Cc: zsh-workers

OK, I understand.  "Conditionally builtin" :-)

Jim

On Thu, 9 Dec 1999, Zefram wrote:

> James Kirkpatrick wrote:
> >I'm not sure I understand.  chown and chgrp are now builtins?
> 
> If you load the files module.  If you do nothing, you get the usual
> external programs.  rm, mv, ln, mkdir, rmdir and sync are already
> available as builtins in exactly the same way.
> 
> >                                                                  Are they
> >POSIX compliant?
> 
> Almost.  chown accepts "." to separate username and group, in addition to
> the POSIX ":".  This is the same level of POSIX conformance as GNU chown.
> 
> >This does not seem to be the sort of thing that should be built in to a
> >shell, any more than rm, ls, find, or a C compiler :-)
> 
> It's very useful to have these things built into a statically-linked
> system administration shell.  (That's where the request for a builtin
> chown originated.)  In addition to the utilities I listed above, we have
> a `stat' module that effectively makes possible a full implementation
> of ls as a shell function, and zsh globbing with glob qualifiers very
> nearly makes find redundant.
> 
> -zefram


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PATCH: chown and chgrp in files module
  1999-12-09 17:41   ` Zefram
  1999-12-09 18:19     ` James Kirkpatrick
@ 1999-12-09 23:40     ` James Antill
  1999-12-13 12:10       ` Zefram
  1999-12-10 21:43     ` Clint Adams
  2 siblings, 1 reply; 7+ messages in thread
From: James Antill @ 1999-12-09 23:40 UTC (permalink / raw)
  To: Zefram; +Cc: James Kirkpatrick, zsh-workers

Zefram <zefram@fysh.org> writes:

> James Kirkpatrick wrote:
> 
> >This does not seem to be the sort of thing that should be built in to a
> >shell, any more than rm, ls, find, or a C compiler :-)
> 
> It's very useful to have these things built into a statically-linked
> system administration shell.  (That's where the request for a builtin
> chown originated.)  In addition to the utilities I listed above, we have
> a `stat' module that effectively makes possible a full implementation
> of ls as a shell function, and zsh globbing with glob qualifiers very
> nearly makes find redundant.

 Which begs the question, who's working on the C compiler ?:)

ObZsh: Would it be possible to have a statically linked sys-admin
shell built as part of the build process. Then you could literally
just have it[1] on a floppy and boot Linux with init=/bin/zsh_admin.

[1] Ignoreing the kernel and a _very_ small root fs (ok I guess you
probably want vi and fsck too ... but not much more).

-- 
James Antill -- james@and.org
I am always an optimist, but frankly there is no hope.
   -Hosni Mubarek


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PATCH: chown and chgrp in files module
  1999-12-09 17:41   ` Zefram
  1999-12-09 18:19     ` James Kirkpatrick
  1999-12-09 23:40     ` James Antill
@ 1999-12-10 21:43     ` Clint Adams
  2 siblings, 0 replies; 7+ messages in thread
From: Clint Adams @ 1999-12-10 21:43 UTC (permalink / raw)
  To: Zefram; +Cc: James Kirkpatrick, zsh-workers

> Almost.  chown accepts "." to separate username and group, in addition to
> the POSIX ":".  This is the same level of POSIX conformance as GNU chown.

Which is unlike any other chown on the planet.  GNU chown would be a
lot more tolerable if it supported some sort of override to allow
usernames containing periods; such as '--ignore-group' or '::'
ideas which have been suggested elsewhere.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PATCH: chown and chgrp in files module
  1999-12-09 23:40     ` James Antill
@ 1999-12-13 12:10       ` Zefram
  0 siblings, 0 replies; 7+ messages in thread
From: Zefram @ 1999-12-13 12:10 UTC (permalink / raw)
  To: james; +Cc: zefram, jimkirk, zsh-workers

James Antill wrote:
> Which begs the question, who's working on the C compiler ?:)

Actually, the friend that asked for builtin chown started off asking for
tar as a builtin (the aim being to restore the system from tape backup
using only the running root shell).  Once I pointed at zftp he conceded
that zftp plus builtins for ln, chown and chmod (next thing for me to do)
would be sufficient.  Actually, I think we need mknod too.  mount and
umount wouldn't be a bad idea, although somewhat system-dependent.
And halt and reboot would be nice... hmm, maybe we need an admintools
module.

Anyway, the thing about tar got me thinking, maybe GNU tar could be
modified so that it can be compiled as a zsh module.  Certain other
tools may be worth modifying this way too.  Not gcc, I think.

I remember one of the other free shells -- I think it was ash -- had a way
of doing its builtins so that they could also be compiled as standalone
programs.  With such a well-defined interface, we could probably produce
some automated way to compile a group of ash builtins as a zsh module.

>ObZsh: Would it be possible to have a statically linked sys-admin
>shell built as part of the build process. Then you could literally
>just have it[1] on a floppy and boot Linux with init=/bin/zsh_admin.

You can do that manually already.  Set LDFLAGS for static linking when
you configure, and edit Src/xmods.conf to list all (and only) the modules
that you want to link in.  You shouldn't need to do --disable-dynamic;
module loading won't work with static linking anyway, and configure will
detect that.

Another thing I'd like to do is to make that easier: a configure switch to
change LDFLAGS to do static linking, and another switch to control which
modules get linked in and which get autoloaded.  Then one could build an
admin zsh from a script just as readily as one can build the normal zsh.

Come to think of it, a shorthand configure switch --enable-admin-config
to do all of that would be a good idea.

-zefram


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~1999-12-13 12:10 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-12-09 16:02 PATCH: chown and chgrp in files module zefram
1999-12-09 17:20 ` James Kirkpatrick
1999-12-09 17:41   ` Zefram
1999-12-09 18:19     ` James Kirkpatrick
1999-12-09 23:40     ` James Antill
1999-12-13 12:10       ` Zefram
1999-12-10 21:43     ` Clint Adams

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).