[net.sources] groups command

rogers (06/12/82)

: run this file as a shell script
mkdir groups.src
cd groups.src
echo "Extracting README..."
cat > README <<'E*O*F'

Information regarding the groups command:

Groups is a C program designed to provide
the user with a list of the valid groups
which he may newgrp to.  There are several
options available, and the user may find out
the group names other users are in.

USAGE:

groups [-ncdxl] [user user ...]

Here is a brief summary of the options:

	none:	Provides list of gid names only.
	-n:	Gives the addtional group id number
	-c:	Gives the user's current group.
	-d:	Gives the user's default group.
	-x:	Expands the group name (see below)
	-l:	Provides all the above functions.

Options may be combined, of course, to get desired results.

IF THE FILE /etc/groupnames is not present,
you may want to add this file so that groups
will do the expansion of the groupnames.  The
program will run fine without ti though.

HERE IS THE FORMAT OF /etc/groupnames recognized
by groups:

grnam:gid:longname[;more info if desired]\n
   
   where : grnam is the name of the group as in
	   group(5),

	   gid is the group id number as in
	   group(5),

	   longname is ascii text describing the
	   group (also can contain other info after
	   the semicolon if desired.

	   \n is a newline to terminate the line
	   between entries.

After the make has completed it's appointed task,
The following should happen:

  Test the groups command by running the executable
  with each option seperately, and then combine options.

  Test the 'user' option by adding the name of several
  users to the end of the groups invoking.

  Move the file groups to /usr/bin or equivalent.

  Move the file groups.1 to /usr/man/man1 or equivalent,
  and test out the manual page (man groups).


  GOOD LUCK!!!!!!!    Roger Southwick.

E*O*F
echo "Extracting Manual Page..."
cat > groups.1 <<'E*O*F'
.TH GROUPS 1  "6/12/82"
.SH NAME
groups \- give valid group names for current user.
.SH SYNOPSIS
.B groups [\-ncdxl] [user user ...]
.SH DESCRIPTION
.I Groups
gives a list of valid group names to which the user may
change to by using a newgrp command.  By using the option
flags, the user may get the following additional data:
.IP
\-n : Provides the current group number.
.IP
\-c : Provides the current group name (as in whoami(1)).
.IP
\-d : Provides the user's default group name.
.IP
\-x : Expands the group name for more description.
.IP
\-l : Provides all the above options.
.IP
user : Provides the group names for the user(s) listed.  This
has the effect of turning off the -c and -d options.
.LP
The options may be combined as in:
.IP
groups -x -n
.LP
\-or\-
.IP
groups -xn
.SH FILES USED
/etc/group \- to get the user names and group names.
/etc/groupnames \- to get the group name expansions.
.SH NOTES
If /etc/groupnames is not on the system, the -x and
-l option will not provide group name expansion.
.LP
The effective user name is used, so if you are su'd,
you will get the group names for the current user name.
.SH AUTHOR
Roger Southwick
Tektronix, Inc.
P.O. Box 500, D.S. 92-525
Beaverton, Oregon 97077
.SH "SEE ALSO"
newgrp(1), whoami(1)
.SH BUGS
To be discovered.
E*O*F
echo "Extracting code ..."
cat > groups.c <<'E*O*F'
/***************************************************************

  groups	By: Roger Southwick
		    Tektronix, Inc.
		    P.O. Box 500, D.S. 92-525
		    Beaverton, Oregon 97077

		Date : 6/10/82

This program is designed to do the groups command.  This command
provides the user with the valid groupnames he may newgrp to.  
Using the option flags, the user may get different other info
(described below).  The user may also stack arguments together
for even more into.

USAGE: groups [-ncdxl] [user user ...]

The flags allowed are:

  none :  This provides a list of the gid names only.

    -n : This provides the addtional group id number.

    -c : This provides the current group name (as in whoami).

    -d : This provides the user's default group name.

    -x : This provides the expansion of the gid names
	 into the long name.  (This works only if
	 /etc/groupnames is present).

    -l : This provides the user with all of the functions.
	 (This works only if /etc/groupnames is present).

  user : The groups function will also print out the
	 group names for each user name specified, but this
	 also has the effect of turning off the -c and -d 
	 options.

---------------------------------------------------------------

Files used: 

	/etc/group  - provides the names of all the groups the
		      user is valid to newgrp to.

	/etc/groupnames - provides the expansion for the names.
		      (This file is optionally not in unix, so
		       is not required, but option -l & -x won't
		       work without it.)

	<grp.h>  - provides the needed mold for getgrgid & getgid.

	<pwd.h>  - mold for getpwuid and getuid.

------------------------------------------------------------------

MODIFICATION HISTORY:

   6/11/82 -	Changed the pnode structure so it has different names
		than the tnode structure (left becomes lft, right 
		becomes rght).  Makes program portable.

   6/11/82 -	Added the define for NCMP.  This change allows the
		makes the string comparisons only look at this many
		characters.  This is used in all strncmp's.
		
   6/11/82 -	Added the knowuser function.  This checks the user
		names (entered as parameters especially) to be valid
		user names or not.  This can be disabled by removing
		the #define KNOWENABLE  statement (or commenting
		it out) in the defines section.

********************************************************************/

#include <stdio.h>

/*-----------------------------------------------------------

   Structure pnode holds the name of a user which is allowed
   in the group.  The group structure, tnode, points to this
   structure at the name of the first user in the group.

-----------------------------------------------------------*/

struct pnode {
  char *name;			/* point to a user's name */
  struct pnode *lft;		/* left child */
  struct pnode *rght;		/* right child */
};

/*-----------------------------------------------------------

  Structure tnode holds a group name, the gid number,
  the group long name (if available), and the pointer
  to the first user (pnode) in the group

-----------------------------------------------------------*/

struct tnode {
  char *grpnm;			/* point to the group name */
  int gid;			/* hold the gid number */
  char *longname;		/* hold the expanded group name */
  struct pnode *frst;		/* point to the first person in the pnode */
  struct tnode *left;		/* left child */
  struct tnode *right;		/* right child */
};

/*-----------------------------------------------------------

   Structure helpmsg (init'ed into stuf), holds the
   help messages.

-----------------------------------------------------------*/

struct helpmsg {

 char *msgstr;

 }stuf[] = {

    "none : Provides a list of the gid names only.",

    "  -n : Provides group id number.",

    "  -c : Provides current group name (as in whoami).",

    "  -d : Provides default group name.",

    "  -x : Expands of the gid names into long name.",

    "  -l : Invokes all of the functions.",

    "Flags may be combined to give desired results.",

    "Other user names may be added, this turns off -d & -c "

};

#define NHLPMSGS  (sizeof(stuf) / sizeof(struct helpmsg))

main(argc,argv)

int argc;		/* number of arguments */
char *argv[];		/* pointers to the actual arguments */

{
  char *s, *username();

  struct tnode *stash(), *pntr;

  FILE *fp, *fgn, *fopen();

  int gnum = 0;		/* put out gid num if set to 1 */
  int cnam = 0;		/* put out current group name if 1 */
  int defgp = 0;	/* put out default group name if 1 */
  int xpnam = 0;	/* expand names if set to 1 */

/*-----------------------------------------------------------

   Now process the arguments. Set the appropriate flag
   to invoke the userpr afterwards

-----------------------------------------------------------*/

  while(--argc > 0 && (*++argv)[0] == '-')
    for (s = argv[0]+1; *s != '\0'; s++)

      switch (*s) {

        case 'n' : 		/* give gid number */
		   gnum = 1;
		   break;

        case 'c' :		/* give current group */
		   cnam = 1;
		   break;
		   
	case 'd' : 		/* give default group */
		   defgp = 1;
		   break;

	case 'x' :		/* expand the names */
		   xpnam = 1;
		   break;

        case 'l' :		/* give all the above */
		   gnum = 1;
		   cnam = 1;
		   defgp = 1;
		   xpnam = 1;
		   break;

	default : 		/* opps! bad flags */
		   help();
		   exit(1);
		   break;
       }

/*--------------------------------------------------------------

  Now open up the files: /etc/group & /etc/groupnames.  It is
  an error if /etc/group can't be opened, but not an error if
  /etc/groupnames can't be opened (this file is not std unix,
  so we must make allowances).

--------------------------------------------------------------*/

if((fp = fopen("/etc/group","r")) == NULL){
   fprintf(stderr,"groups: can not open /etc/group\n");
   exit(1);
 }
else {
   fgn = fopen("/etc/groupnames","r");

/*--------------------------------------------------------------

  Now to finally do the print out.  This is done by calling
  userpr with the top of the tnode tree (supplied by stash),
  the name of the user (supplied by username, which also prints
  out the header), the flags gnum and xpnam for expanding the
  printout.  We then call up the functions curname (if cnam is
  set) and defgrp (if defgp is set).  If there are more 
  arguments on the line, assume they are user names, which
  it is desired to give groups for.  In this case, we keep
  doing headers for each name (which must be known).  When
  getting groupnames for users OTHER THAN the logged in user,
  we cannot do the current or default group printouts.

--------------------------------------------------------------*/

pntr = stash(fp,fgn);			/* init the trees */

if(argc == 0){				/* if no users named... */
   header(username());			/* use the current user */
   userpr(pntr,username(),gnum,xpnam);
 }
 else {				/* use args as user names */

   defgp = cnam = 0;			/* turn off options */

   while (argc-- > 0)
     if(knowuser(pntr,*argv) == 1){	/* see if user is known */
        header(*argv);
        userpr(pntr,*argv++,gnum,xpnam);
     }
     else				/* we don't know this user */

	fprintf(stderr,"\ngroups: Unknown user: %s\n",*argv++);
 }


   putchar('\n');			/* add a <cr> */

   if(cnam == 1)
     curname();

   if(defgp == 1)
     defgrp();

   if(cnam == 1 || defgp == 1)
   	printf("\n\n");			/* add 2 <cr>'s */

   exit(0);
  }
}

/*--------------------------------------------------------------

   Function help:  This provides the usage message and a 
   brief description of each flag.

--------------------------------------------------------------*/

help()

{
   int i;

    fprintf(stderr,"\nUsage: groups [-ncdxl] [user user ...]\n\n");

    for(i = 0; i < NHLPMSGS ; i++)
      fprintf(stderr,"%s\n\n",stuf[i].msgstr);
}

/*--------------------------------------------------------------

   Format notes: 

   The format for the file /etc/group is given
   in group(5) of the V7 unix manual.

   The format assumed for /etc/groupnames is:

   grpnm:gid:longname[;other info]

     where: grpnm    - is the group name as in group(5),
	    gid      - is the group number, as in group(5),
	    longname - is the long group name (ascii text
	               describing the group name (grpnm).

--------------------------------------------------------------*/


/*--------------------------------------------------------------

   Globally available variables and constants

--------------------------------------------------------------*/

#define MAXLINE   10000	/* Maximum number of chars in a line of group */
#define EOL    '\012'	/* end of line character in groups and groupnames */
#define NAMESIZE  10	/* the maximum length of a user's name */
#define GNAME     20	/* the maximum length of a group name */
#define LNAME     50    /* the maximum length of a long grp name */
#define GIDLEN    5	/* maximum length of the gid number string */
#define  TAB      15	/* the place to tab out to after group name */
#define NCMP	  8	/* # of chars compared between groupnames & group */

#define KNOWENABLE	/* remove if you do not want to test params */

int cnt;		/* the count of letters done in a line */
char line[MAXLINE];	/* the line of text read */


/*--------------------------------------------------------------

   Function stash: returns a pointer to the top of the
   tnode tree.  Reads lines of fp (/etc/group) and places
   the group name, gid number, and user names into tnode tree
   by hashing these values out, and calling function tree.  The
   longnames are hashed out of fgn (/etc/groupnames, if available)
   when all the group names are done (/etc/group fully read out),
   and added to the tnodes with calls to addlong function.

--------------------------------------------------------------*/

struct tnode *stash(fp,fgn)

FILE *fp,*fgn;

{
   struct tnode *top,*tree();

   char gname[GNAME];
   char longnm[LNAME];
   char gidstr[GIDLEN];
   int gid,n;

   top = NULL;

   while (fgets(line,MAXLINE,fp)!= NULL){ 	/* read a line of input */

    for(cnt = 0; line[cnt] != ':';cnt++)  /* get group name place into gname */
       gname[cnt] = line[cnt];

    gname[cnt++] = '\0';		/* add a null */

    for( ; line[cnt] != ':'; cnt++) 	/* throw away group passwd */
	;
    
    for(++cnt,n = 0; line[cnt] != ':';cnt++,n++)	/* get the gid number */
       gidstr[n] = line[cnt];
    
    gidstr[++n] = '\0';		/* add a null */


    gid = atoi(gidstr);		/* convert to a number */

    top = tree(top,gname,gid);

    }

/*---------------------------------------------------------

  Now add the expanded groupnames to the list, if
  file fgn (/etc/groupnames) is available.

---------------------------------------------------------*/
   
    if(fgn != NULL){

      while (fgets(line,MAXLINE,fgn) != NULL){

	for(cnt = 0;line[cnt] != ':';cnt ++)	/* grab groupname */
	   gname[cnt] = line[cnt];

	gname[cnt++] = '\0';			/* add a null */

	for( ; line[cnt] != ':';cnt++)		/* discard gid number */
	   ;

	/* now grab the long name */

	for(++cnt,n = 0; line[cnt] != EOL && line[cnt] != ';';cnt++,n++)
	   longnm[n] = line[cnt];

	longnm[n] = '\0';			/* add a null */

	addlong(top,gname,longnm);		/* call addlong to install */
      }
    }

    return(top);		/* go back to calling program (main) */

}


/*---------------------------------------------------------

  Function tree provides a recursive method of storing the
  group name (nm) at node p or below.  Also stores the gid
  number.  Then it gets one user name at a time and calls
  ptree to install the user name in the associated pnode 
  tree.

---------------------------------------------------------*/

struct tnode *tree(p,nm,gid)

struct tnode *p;
char *nm;
int gid;

{
    struct tnode *talloc();
    char *strsave(),name[NAMESIZE];
    struct pnode *ptree();
    int n;

    if (p == NULL) {			/* a new node is desired */

      p = talloc();			/* make a new node in main memory */
      p->grpnm = strsave(nm);		/* save the group name */
      p->gid = gid;			/* save the gid number */
      p->longname = NULL;		/* make longname point nowhere */
      p->left = p->right = NULL; 	/* init pointers */
      p->frst = NULL;

      while(line[cnt] != EOL){		/* get a person's name */

        cnt++;				/* increment past , */

	/* grab the person's name */

        for(n = 0;line[cnt]!=',' && line[cnt]!=EOL;n++,cnt++)
           name[n] = line[cnt];

        name[n] = '\0';			/* add a null */

        p->frst = ptree(p->frst,name);	/* save the name */
      }
    }
    else if (strncmp(nm,p->grpnm,NCMP) <= 0) /* goes in left tree */
       p->left = tree(p->left,nm,gid);

    else
       p->right = tree(p->right,nm,gid); /* goes right */

   return(p);			/* return pointer to tnode */
}

/*---------------------------------------------------------

   Function ptree installs the user name (name) in the
   pnode tree at node p or below, by use of recursion.

---------------------------------------------------------*/

struct pnode *ptree(p,name)

struct pnode *p;
char *name;

{
  struct pnode *palloc();
  char *strsave();

  if(p == NULL)	{	 	/* make a new node */

    p = palloc();		/* create a new node in memory */
    p->name = strsave(name);	/* save the name of the user */
    p->lft = p->rght = NULL;	/* init the pointers */

  }
  else if(strncmp(name,p->name,NCMP) <= 0)	/* goes in left tree */
     p->lft = ptree(p->lft,name);

  else					/* goes in right tree */
     p->rght = ptree(p->rght,name);

  return(p);			/* return pointer to pnode */
}

/*----------------------------------------------------------------

  Add the longname lnm to the structure tnode for each groupname
  This is done by looking up the gid name nm in the list, and 
  adding the lnm  to p->longname.

----------------------------------------------------------------*/

addlong(p,nm,lnm)

struct tnode *p;
char *nm,*lnm;

{
   int cmp;

   if(p != NULL){	/* Don't add the long name if no short match */

     if((cmp = strncmp(nm,p->grpnm,NCMP)) == 0) /* found a match */
       p->longname = strsave(lnm);

     else if(cmp < 0)			/* go left */
       addlong(p->left,nm,lnm);

     else				/* go right */
       addlong(p->right,nm,lnm);
    }
}

/*--------------------------------------------------------------

   Memory allocation routine: talloc.  This provides main
   memory for a tnode by doing a cast of the main memory
   allocator malloc.  It returns a pointer to the new node
   out in memory.

--------------------------------------------------------------*/

struct tnode *talloc()

{
  return((struct tnode *) malloc(sizeof(struct tnode)));
}

/*--------------------------------------------------------------

   Memory allocation routine: palloc.  This provides main
   memory to a pnode by doing a cast of the main memory
   allocator malloc.  It returns a pointer to the new node
   out in memory.

--------------------------------------------------------------*/

struct pnode *palloc()

{
  return((struct pnode *) malloc(sizeof(struct pnode)));
}

/*--------------------------------------------------------------

   The strsave routine allocates some main memory for the
   string s, copys the string there and returns a pointer
   to the place in memory

--------------------------------------------------------------*/

char *strsave(s)

char *s;

{
   char *p;

   p = (char *)malloc(strlen(s) +1);
   strcpy(p,s);
   return(p);
}

/*--------------------------------------------------------------
  
  Print out the group names for the user: name.  This is
  done with recursive calls to userpr, which transverses the
  tnode tree, looking for the user name with the function:
  namein (below).  The info is printed out according to the
  parameters gnum (add the group id number) and xpnam (add the
  expanded group name)

--------------------------------------------------------------*/

userpr(p,name,gnum,xpnam)

struct tnode *p;
char *name;
int gnum, xpnam;

{
   int namein();
   int len;			/* the length of the group name */

   if (p != NULL) {		/* only print out if not a NULL node */

     userpr(p->left,name,gnum,xpnam);	/* go all the way left */

     if(namein(p->frst,name)){		/* found a match */

	len = strlen(p->grpnm);		/* get the string length */

	printf("%s",p->grpnm);		/* print group name */

	for( ; len <= TAB ; len ++)	/* add spaces to TAB */
	  putchar(' ');

	if(gnum == 1)			/* print out gid number */
	  printf("%d",p->gid);

        if(xpnam == 1)			/* add expanded name */
	  if(p->longname != NULL)	/* only if there, tho */
	    printf("\t%s",p->longname);

        putchar('\n');			/* add <cr> */
     }

     userpr(p->right,name,gnum,xpnam);	/* go down the right tree */
  }
}

/*--------------------------------------------------------------

   Function knowuser returns a 1 if the user name: name is
   anywhere in any of the pnode trees.  This function could
   be done without (since it takes time to check), and can be
   quickly disabled by removing the #define KNOWENABLE in the
   defines section.

--------------------------------------------------------------*/

int status = 0;		/* the status variable */

int knowuser(p,name)

struct tnode *p;
char *name;

{

#ifdef KNOWENABLE

  if(p != NULL)	{	/* don't do anything for a NULL node */

    knowuser(p->left,name);	/* go all the way left */

    if(namein(p->frst,name))	/* test for user in this group */
      status = 1;

    knowuser(p->right,name);	/* go all the way right */
  }

#else
  
  status  = 1;		/* if not enabled, we know invalid users */

#endif

  return(status);
}

/*--------------------------------------------------------------

   Function namein returns a 1 if user name: name is in
   the pnode tree pointed to by p.  This is done by recursively
   calling namein looking for a match.

--------------------------------------------------------------*/
int namein(p,name)

struct pnode *p;
char *name;

{
  int cond;

  if(p == NULL)		/* End of tree and name not found */
    return(0);

  else {
    if((cond =strncmp(name,p->name,NCMP)) == 0) /* test name for match */
       return(1);

    else if (cond < 0)			/* go left if less than */
       return(namein(p->lft,name));

    else				/* go right */
       return(namein(p->rght,name));
  }
}
/*-----------------------------------------------------------

  This function provides the current group name for the
  user.

------------------------------------------------------------*/

#include <grp.h>

struct group *gp;
struct group *getgrgid();

curname()

{
  gp = getgrgid(getgid());
  printf("Current group is: %s\t",gp->gr_name);
}


/*-----------------------------------------------------------

  This function returns a pointer to current user's name.

------------------------------------------------------------*/

#include <pwd.h>

struct passwd *getpwuid();
struct passwd *pp;

char *username()

{
  pp = getpwuid(getuid());
  return(pp->pw_name);
}

/*-----------------------------------------------------------

   This function prints a nice looking header for user
   name nm

-----------------------------------------------------------*/

header(nm)

char *nm;

{
  printf("\nValid groups for %s:\n",nm);
}

/*-----------------------------------------------------------

  This function prints out the user's default groupname

------------------------------------------------------------*/

defgrp()
{
 pp = getpwuid(getuid());
 gp = getgrgid(pp->pw_gid);
 printf("Default group is: %s",gp->gr_name);
}
E*O*F
echo "Extracting makefile..."
cat > makefile <<'E*O*F'
FILES = /usr/include/stdio.h /usr/include/grp.h /usr/include/pwd.h /etc/group

groups : groups.c $(FILES)
	cc -O -o groups groups.c
E*O*F
echo "Running Makefile..."
make