[unix-pc.sources] Reimplementation of BSD chown

arnold@skeeve.UUCP (Arnold D. Robbins) (03/16/88)

The following piece of code has been doanted to the Free Software Foundation
(aka the GNU project) and should be considered copyright by them, even though
I wrote it.

I use a BSD machine at work. One of the nice things about the 4.3 chown(1)
command is that you can say

	chown [ -R ] user[.group] file ....

The -R means recursive; do the chown on any directories, and subdirectory
trees. The optional .group says chgrp the files to the specified group too.
User and Group may both be numeric or identifiers looked up in the respective
file.

I missed the above features badly on my 3B1, and since System V makes the
recursive part real easy via ftw(3), I decided to reimplement the command.
It was pretty easy.

I hope the rest of the Unix-pc net finds this of interest too.
---------------------
/*
 * chown.c
 *
 * Reimplementation of the 4.3 BSD chown, which can recursively chown
 * a directory, and take a user + group combination argument.
 *
 * Requires System V ftw(3) library routine, but a PD version is in the
 * source archives.
 *
 * Arnold Robbins
 * skeeve!arnold
 *
 * Usage: chown [ -R ] user[.group] file ...
 *
 * This program belongs to the Free Software Foundation, their copyright
 * and copying rules apply.
 */

#include <stdio.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <ftw.h>
#include <sys/types.h>
#include <sys/stat.h>

int user;		/* new uid */
int group;		/* new gid */

char *myname;		/* for errors */

#define MAXFDS	17	/* Any Unix will have at least this many */

int problems = 0;	/* exit non-zero if problems != 0 */

main (argc, argv)
int argc;
char **argv;
{
	extern int optind, getopt ();
	extern char *strchr ();		/* use index if necessary */
	extern char *strrchr ();	/* use rindex if necessary */
	int recursive = 0;
	int c, ret;
	char *cp;
	struct passwd *pwd, *getpwnam ();
	struct group *grp, *getgrnam ();
	extern int do_chown ();

	/* save program name */
	if ((myname = strrchr (argv[0], '/')) != NULL)
		myname++;
	else
		myname = argv[0];

	/* are we recursive? */
	while ((c = getopt (argc, argv, "R")) != EOF) {
		switch (c) {
		case 'R':
			recursive = 1;
			break;
		default:
			usage ();
			break;
		}
	}

	if (optind >= argc)
		usage ();

	/* check if group supplied too */
	if ((cp = strchr (argv[optind], '.')) != NULL) {
		*cp++ = '\0';
		grp = getgrnam (cp);
		if (grp == NULL) {
			if (isnumber (cp)) {
				group = atoi (cp);
			} else {
				fprintf (stderr, "%s: %s: no such group\n",
					myname, cp);
				exit (1);
			}
		} else
			group = grp -> gr_gid;
	} else
		group = -1;

	/* get user to chown to */
	pwd = getpwnam (argv[optind]);
	if (pwd == NULL) {
		if (isnumber (argv[optind])) {
			user = atoi (argv[optind]);
		} else {
			fprintf (stderr, "%s: %s: no such user\n", myname,
				argv[optind]);
			exit (1);
		}
	} else
		user = pwd -> pw_uid;

	if (++optind >= argc)
		usage ();	/* no files specified */

	/* actually do something to the files */
	for (; optind < argc; optind++) {
		if (recursive && isdir (argv[optind])) {
			ret = ftw (argv[optind], do_chown, MAXFDS);
			if (ret == -1)
				perror (myname);	/* no file name */
		} else
			(void) chown1 (argv[optind]);
	}
	exit (problems != 0);
}

int
do_chown (path, sbuf, flag)
char *path;
struct stat *sbuf;
int flag;
{
	int g;
	char buf[BUFSIZ];

	switch (flag) {
	case FTW_F:		/* file */
	case FTW_D:		/* directory */
		g = (group == -1) ? sbuf -> st_gid : group;
		if (chown (path, user, g) < 0)
			perror (path);
		return 0;	/* so ftw() will continue */

	case FTW_DNR:		/* directory not readable */
		sprintf (buf, "%s: directory %s not readable\n", myname, path);
		break;

	case FTW_NS:		/* file could not be stat'ed */
		sprintf (buf, "%s: could not stat %s\n", myname, path);
		break;

	default:
		sprintf (buf, "%s: impossible value (%d) from FTW\n", myname,
			flag);
		break;
	}
	fprintf (stderr, "%s", buf);
	problems = 1;
	return 0;
}

int
chown1 (path)
char *path;
{
	struct stat buf;
	int g;

	if (group == -1) {	/* get old group */
		if (stat (path, & buf) < 0) {
			problems = 1;
			fprintf (stderr, "%s: ", path);
			perror ("stat");
			return -1;
		}
		else
			g = buf.st_gid;
	} else
		g = group;

	if (chown (path, user, g) < 0) {
		perror (path);
		problems = 1;
	}
	return 0;
}

int
isnumber (str)
char *str;
{
	register int ret = 1;

	for (; *str; str++) {
		if (! isdigit (*str)) {
			ret = 0;
			break;
		}
	}
	return (ret);
}

int
isdir (file)
char *file;
{
	struct stat buf;

	return (stat (file, &buf) == 0 &&
		(buf.st_mode & S_IFMT) == S_IFDIR);
}

int
usage ()
{
	fprintf (stderr, "usage: %s [ -R ] user[.group] file ...\n", myname);
	exit (1);
}
-- 
Arnold Robbins -- The Basement Computer
UUCP:	{ gatech, emory, gt-eedsp, bakerst, gladys }!skeeve!arnold

/bin/csh: Just Say NO!

arnold@skeeve.UUCP (Arnold D. Robbins) (03/21/88)

In article <125@skeeve.UUCP> I wrote:
>The following piece of code has been doanted to the Free Software Foundation
>(aka the GNU project) and should be considered copyright by them, even though
>I wrote it.
>
> [ stuff deleted ... ]
>/*
> * chown.c
> * [ more deleted ]
> *
> * This program belongs to the Free Software Foundation, their copyright
> * and copying rules apply.
> */

After some email correspondence with the Free Software Foundation, it turns
out I did not do this correctly. Therefore, I am explicitly abandoning
any copyrights to the source code posted in <125@skeeve.UUCP> and labeling
that program completely Public Domain.

Anyone who wants to may do anything they please with the code. I will be
working with FSF to get it included as part of GNU. Meanwhile, the code is
completely Public Domain.

If you wish to contribute code to the GNU project, be sure to contact them
first. DO NOT post it or do anything else with it until they've had a chance
to take it and put their copyright on it.

[ inews filler ! ]
[ inews filler ! ]
[ inews filler ! ]
[ inews filler ! ]
[ inews filler ! ]
[ inews filler ! ]
[ inews filler ! ]
[ inews filler ! ]
[ inews filler ! ]
[ inews filler ! ]
-- 
Arnold Robbins -- The Basement Computer
UUCP:	{ gatech, emory, gt-eedsp, bakerst, gladys }!skeeve!arnold

/bin/csh: Just Say NO!