[comp.sources.misc] v09i087: Public domain chown/chgrp clone

arnold@mathcs.emory.edu (Arnold D. Robbins {EUCC}) (12/27/89)

Posting-number: Volume 9, Issue 87
Submitted-by: arnold@mathcs.emory.edu (Arnold D. Robbins {EUCC})
Archive-name: chgrp_ar

This is a Chanukah gift for all you System V-ers out there who might
like the BSD chown, which can (a) recursively chown a tree, and (b)
take an option group argument, which saves tarversing a file tree twice.

I wrote this because I had the BSD chown at work but not at home, and
sorely missed the functionality.

It requires the ftw(3) library routine, but PD versions are available
in the archives.

This program is PUBLIC DOMAIN.  No warranties of any sort.

Enjoy,

Arnold Robbins
--------------------------------------------------
/*
 * chown.c
 *
 * Reimplementation of the 4.3 BSD chown, which can recursively chown
 * a directory, and take a user + group combination argument.
 * If called as chgrp, just do chgrp.
 *
 * Requires System V ftw(3) library routine, but a PD version is in the
 * source archives.
 *
 * Arnold Robbins
 * arnold@skeeve.atl.ga.us
 * skeeve!arnold
 *
 * Usage: chown [ -R ] user[.group] file ...
 * Usage: chgrp [ -R ] group file ...
 *
 * Posted December 1989.
 */

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

char chown_name[] = "chown";
char chgrp_name[] = "chgrp";

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

char *myname;		/* for errors, and to decide behavior */

#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 *username, *groupname;
	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];

	if (strcmp (myname, chown_name) != 0 && strcmp (myname, chgrp_name) != 0) {
		fprintf (stderr, "%s: not called as `chown' or `chgrp'\n", myname);
		exit (1);
	}

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

	if (optind >= argc)
		usage ();

	user = group = -1;
	if (strcmp (myname, chown_name) == 0) {
		/* called as chown, get user and group */

		/* first, get user and group names */
		username = argv[optind];
		groupname = strchr (argv[optind], '.');
		if (groupname != NULL)
			*groupname++ = '\0';	/* skip dot */

		/* get uid number */
		pwd = getpwnam (username);
		if (pwd == NULL) {
			if (isnumber (username)) {
				user = atoi (username);
			} else {
				fprintf (stderr, "%s: %s: no such user\n",
					myname, username);
				exit (1);
			}
		} else
			user = pwd -> pw_uid;
		endpwent ();

		if (groupname != NULL)
			goto getgroup;
	} else {
		/* just get group name */
		groupname = argv[optind];

	getgroup:
		grp = getgrnam (groupname);
		if (grp == NULL) {
			if (isnumber (groupname)) {
				group = atoi (groupname);
			} else {
				fprintf (stderr, "%s: %s: no such group\n",
					myname, groupname);
				exit (1);
			}
		} else
			group = grp -> gr_gid;
		endgrent ();
	}

	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 u, g;
	char buf[BUFSIZ];

	switch (flag) {
	case FTW_F:		/* file */
	case FTW_D:		/* directory */
		u = (user == -1) ? sbuf -> st_uid : user;
		g = (group == -1) ? sbuf -> st_gid : group;
		if (chown (path, u, g) < 0) {
			perror (path);
			problems = 1;
		}
		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;
	}
	fputs (buf, stderr);
	problems = 1;
	return 0;
}

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

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

	u = (user == -1) ? buf.st_uid : user;
	g = (group == -1) ? buf.st_gid : group;

	if (chown (path, u, 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%s file ...\n", myname,
		strcmp (myname, chown_name) == 0 ? "[.group]" : "");
	exit (1);
}

#ifdef debug
/* for debugging --- delete for real */

int
chown (path, user, group)
char *path;
int user, group;
{
	printf ("chown (\"%s\", %d, %d);\n", path, user, group);
	return 0;
}
#endif
-- 
Arnold Robbins -- guest account at Emory Math/CS	| Laundry increases
DOMAIN: arnold@emory.mathcs.emory.edu			| exponentially in the
UUCP: gatech!emory!arnold  PHONE: +1 404 636-7221	| number of children.
BITNET: arnold@emory	   				| -- Miriam Hartholz