
/*     Valerie Caro    COINS Research Computer Facility          */
/*     UNIVERSITY OF MASSACHUSETTS/ AMHERST, MA 01003            */

/**********************************************************************
C  FACILITY	IDENT:	V4.0
C
C     GQM allows GROUP managers and the SYSTEM manager to interactively
C alter disk quotas .
C
C     GROUP MANAGERS are users with the GRPMGR identifier.
C     The SYSTEM MANAGER is a user with the SYSMGR identifier.
C
C     Privilege status is determined from the RIGHTSLIST. 
C
C+++
C OPERATION
C
C     GQM can be installed as a foreign command. However,
C it may be invoked by the standard DCL run command.
C
C+++
C FILES
C
C     GQM depends on:
C
C	1) SYSUAF
C	   Data from this file is used to determine authorization and grouping.
C	2) RIGHTSLIST
C	   Data from this file is used to determine authorization.
C	3) QUOTA.SYS on all disks
C	   The information in these files furnishes the usage and quota info.
C	4) AQUOTA.SYS
C	   The information is this file is used to determine the overall group
C	   quotas.
C	5) RCF$HELP:GQM.HLB
C	   This file contains the helplibrary information for the program.
C+++
C LINKAGE
C
C     The following modules must be linked to create GQM.EXE:
C
C	1) GQM.OBJ
C	   This file contains the compiled code from GQM.C which is
C	   the high level control and user interface routines.
C	2) DQUOTASUB.OBJ
C	   This file contains the assembled code from DQUOTASUB.MAR which is
C	   the low level subroutines specific to GQM.
C	3) UAFSUB.OBJ
C	   This file contains the assembled code from UAFSUB.MAR which is the
C	   SYSUAF access functions.
C	4) GRPSUB.OBJ
C	   This module contains the group file access routines
C	5) DQUOTAMSG.OBJ
C	   This module contains the message text for the routines
C	6) UTLDQUOTA.OBJ
C	   This module contains subroutines that access the disk quota 
C	   data maintained by the ACP.
C	7) ALLDISK.OBJ
C	   This module contains the code for the SHOW DISK command which
C	   list usage for a group on all the disks
C
C+++
C DOCUMENTATION
C
C     Documentation for the program is available in GQM.HLB.
C+++
C MODIFICATION HISTORY
C
C V1.0 	8/24/81 Debugged Doug Stefanelli. This version had provision for
C		only two disks: DRA1 and DRA3.
C
C V4.0  3/85    Rewritten by Valerie Caro. Version to run on VMS V4.0
C       5/85    Added check for disk quotas disabled (VC)
C	1/86    Added code for SHOW DISK command (VC)
C
C       6/86    Modified to include identifiers in groups. 
C		Identifiers for a group start with the group name (VC)
C
***************************************************************************/




#define USE_GROUP_IDS 1		/* SET THIS TO 0 IF YOU DON'T HAVE GROUP
				   IDENTIFIERS WITH DISK QUOTAS */
#define lbr$c_read 1		/* librarian read func */

#include "gqm.h"
#include "dquotamsg.h"   /* there must eb a better way to get these values */
#include "kgbdef.h"	 /* used for identifiers */
#include dvidef

#include <iodef.h>
#include descrip
#include stdio
#include ctype

/*	Identifier list */
struct idlist_type  *idlist;

FILE *out_file;  		/* Output file */


/************************************************/
/*  GROUP DISKQUOTA MANAGEMENT PROGRAM		*/
/************************************************/

main(argc,argv)
int argc;
char *argv[];
{
	char *retval;
	int  status=1,l,grpriv=0, syspriv=0, diskchan = 0;
	unsigned curuic;

/*	Help variables */
	int helpfile=0, help_index = 0, help_op, help_notopen = 1;
	$DESCRIPTOR(hfile,"rcf$help:gqmhelp.hlb");

/*	Command loop varialbes */
	int take_exit=0, onetime = 0;
	int out_status = 0;

/*	Parse line variables */
	char cmdline[80], cmdstring[80];
	int command, subcommand, relative, quota_value, cmdlen=0;
	unsigned uic;
	$DESCRIPTOR(cmdlined,cmdline);	
	$DESCRIPTOR(cmdstringd,cmdstring);


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

	out_file = stdout;  		/* Output file */
	
/*	CHECK USER'S PRIVILEGES	*/
	chk_authorization(&curuic, &grpriv, &syspriv);

/* Initialize Structures */
	idlist = NULL;
/* Set Current Disk    */
	do_use("SYS$DISK", &diskchan, 8);


/* check if foreign command   */
	if (onetime = argc-1)
	  { strcpy(cmdline,argv[1]);
	    cmdlen = strlen(cmdline);
	    cmdlined.dsc$w_length = cmdlen;
	  }	

/*
  Initialize loop variables and enter main command loop
*/
      while (take_exit == 0)
	{
	   command=0;
	   subcommand=0;
	   strcpy(cmdstring,"  ");
	   cmdstringd.dsc$w_length = 80;
	   quota_value=0;
	   status=0;

	 if (cmdlen)	 /* parse command */
	   {
	     parse_command_line(&cmdlined ,&command, &subcommand,
			&uic, &cmdstringd, &relative,
			&quota_value, &status);
	      if ((status & 1) == 0) 
	        lib$signal(dqa_badparse,1,&cmdlined,0,0,0,0);
	      else /* EXECUTE COMMAND */
	      {
	      switch (command) {
	        case USE : 
			do_use(&cmdstring, &diskchan, cmdstringd.dsc$w_length); 
			break;

	        case SET : 
			do_set(subcommand, &out_status, &cmdstring,
					cmdstringd.dsc$w_length); 
			break;

		case SHOW : 
			do_show(diskchan, subcommand, &cmdstringd,uic);
			break;

		case DISK : 
		    {
			switch (subcommand)
			{
		      	 case DISKTOT :
			    do_show_tot_disks(&diskchan, &cmdstringd); break;
			 case DISKBRF :
			    do_show_brief_disks(&diskchan, &cmdstringd); break;
			 default :
			    do_show_acc_disks(&diskchan, &cmdstringd); break;
			} /* case subcommand */
                	break;
		     }

		case MOD : 
			do_mod(diskchan, curuic, grpriv, syspriv, subcommand, 
				relative, quota_value, &cmdstringd, uic); break;

		case RENAME :
			do_rename( subcommand, &cmdstringd, syspriv);break;

	
		case HELP : 
		    {   if (help_notopen)
/*  			Open Help file */
			  { help_op = lbr$c_read;
			    status = lbr$ini_control( &help_index,&help_op,0,0);
			    status=lbr$open( &help_index, &hfile, 0,0,0,0,0);
			    if (status & 1) helpfile=1;
			    help_notopen = 0;
			  }
			if (helpfile) do_help(&help_index,&cmdstring,
						cmdstringd.dsc$w_length);
	            	else lib$signal(dqa_nohelp,0,0,0,0,0);
			break;
		    }

		case ADD : 
			do_add( diskchan, subcommand, &cmdstringd, grpriv,
				  syspriv, curuic, uic, quota_value); break;

		case REMOVE : 
			do_remove(diskchan, subcommand, &cmdstringd,
					grpriv,syspriv,curuic, uic); break;

		case LIST :
			do_list(diskchan, subcommand, grpriv,syspriv,curuic);
			break;

		case REBUILD : 
			rebuild_acct(diskchan,subcommand, &cmdstringd, syspriv);
			break;

		case MAXIMIZE : 
			do_maximize(diskchan,subcommand, &cmdstringd, syspriv);
			break;

		case EXIT : take_exit=1; break;

		default : lib$signal(dqa_nocmd,0,0,0,0,0); break;
	    } /*case*/ } /*if*/ 
	   } /*if*/

	   if (onetime != 0) take_exit=1;
	   if (take_exit != 1) 
		{ printf("\n%s ",PROMPT);
		  retval = gets(cmdline);
		  if (retval == NULL) take_exit = 1;
		  cmdlen = strlen(cmdline);
		  cmdlined.dsc$w_length = cmdlen;
		}
	} /* end while */                              

 }


/* C+++++
C FUNCTION
C
C     DO_USE sets the quota operations to a particular disk or volume.
C+++
C INPUT PARAMETERS
C
C     diskname	name of the disk
C+++
C OUTPUT PARAMETERS
C
C     chan	channel number for disk
C+++++
*/
do_use(diskname,quota_chan, length)
	char *diskname;
	int  *quota_chan, length;
	
      {
	int	status;			/*status return value*/
	char    devname[64];
	short 	ret_len=0;
	struct  iosb_s { int io1, io2; } iosb;

	struct dvi_itm { short len, code;
			 char *buff_addr;
			 short  *retlen; 
			 int  end_of_buff; } dvi_item;

	$DESCRIPTOR(dnamed,diskname); 
	$DESCRIPTOR(devnamed, &devname);

	if (length == 80) 
 	   { diskname[length] = '/0';
	     length = strcspn(diskname," ");
	   }
	dnamed.dsc$w_length = length; 
	   
	dvi_item.len = 16;
	dvi_item.code = DVI$_DEVNAM;
	dvi_item.buff_addr = &devname;
	dvi_item.retlen = &ret_len;
	dvi_item.end_of_buff = 0;

	status = sys$getdviw(0, 0, &dnamed, &dvi_item, &iosb, 0,0,0);
	devnamed.dsc$w_length = ret_len;

	if (status != 1)  /* ILLEGAL DISK NAME */
	  { lib$signal(dqa_nodisk,0,0,0,0,0,0);
	    if (*quota_chan == 0) exit();  /* EXIT IF JUST STARTING */
	    out_file = stdout;
	    return;
	  }

	if (*quota_chan != 0) 
	  { sys$dassgn(*quota_chan); 
	    status = grp_close();
	  }

	status=sys$assign(&devnamed, quota_chan,0,0);
	if (status != 1) 
	   { lib$signal(dqa_badassign,1, &devnamed,0,0,0);
	     lib$signal(dqa_nodisk,0,0,0,0,0,0);
	   }

	status = grp_open(&devnamed);
	if (status != 1)
	   { lib$signal(dqa_nogfile,0,0,0,0,0);
	     exit();
	   }
	out_file = stdout;

      }

/* C+++++
C
C     SET_OUTPUT establishes an output file on the indicated channel.
C+++
C INPUT PARAMETERS
C
C     option		open or close file
C     filename		filename for open
C+++
C OUTPUT PARAMETERS
C
C     out_status	whether channel currently open (yes=1)
*/
do_set(option, out_status, filename, length)
	char *filename;
	int  *out_status, length, option;
      {

	int	status, file_num;		/*status return value*/

	filename[length] = '\0';

	if (*out_status) /* output currently to file */
	 {
	   status = fclose(out_file);
	   out_file = stdout;
	   *out_status=0;
	 }

	if (option == NOOUTPUT) return;

	if ((file_num = creat(filename, 0,"rat=cr", "rfm=var")) == -1) 
	   { lib$signal(dqa_badsetout,0,0,0,0,0);
	     return;
	   }

	out_file = fdopen(file_num, "w");
	if (out_file == NULL)
	   { lib$signal(dqa_badsetout,0,0,0,0,0);
	     return;
	   }

	*out_status=1;

      }


/*
C+++++
C     DO_SHOW produces reports of disk space allocations for:
C	1) the specified USERNAME         
C	2) the specified GROUP name
C	3) the entire system (all groups)
C	4) the specified UIC
C
C+++
C INPUT PARAMETERS
C
C     diskchan		channel number of disk
C     subcommand	which report to produce
C     cmdstring		USER/GROUP to report
C     uic		uic
C+++
*/
do_show(diskchan, subcommand, username, uic)

	unsigned uic, diskchan, subcommand;
	struct dsc$descriptor_s *username;
   {
	unsigned real_uic;
	char acct[13];
	$DESCRIPTOR(acctd,acct);
	$DESCRIPTOR(vagd,"VAGRANT");
	int status, grp_quota, grp_used;


	if (subcommand == DEFREP) subcommand = USERREP; /* user = default */

   switch (subcommand)
	{
	  case USERREP :     
	    { status = get_user_info(username, &real_uic, &acctd);
	      wrt_user_report(diskchan,username, real_uic, &acctd);
	      break;
 	    }

	  case UICREP :
	    { status = get_uic_info(username, &uic, &acctd);
	      if (status != 1)
	 	{ acctd.dsc$w_length = 0;
	          wrt_user_report(diskchan, &vagd, uic, &acctd);
		}
	      else
	          wrt_user_report(diskchan,username, uic, &acctd);
	      break;
	    }

	  case GROUPREP :
	    { wrt_group_report(diskchan, username);
	      break;
	    }

	  case SYSTEMREP :
	     { 
/*		close group file first to start sequential search */
		status = grp_close();
/* 		Find the groupname */
       		while (grp_get_next_name(&acctd, &grp_quota,&grp_used) == 1)
	      	   wrt_group_report(diskchan, &acctd);
		break;
	     }
 
	  case IDREP :
	     identifier_sub(diskchan, SHOW, username, 1, 0, 0); break;

	  default : lib$signal(dqa_nocmd,0,0,0,0,0);	            

	} /* end switch */

}

/*
C+++++
C FUNCTION
C     WRT_USER_REPORT outputs user data to the output channel.
C+++
C INPUT PARAMEERS
C     diskchan	channel of current disk
C     username	username of user
C     uic	UIC of user
C     account	account of user
C
C+++++
*/
wrt_user_report(diskchan, username, uic, account)

	struct dsc$descriptor_s *username, *account;
	unsigned uic, diskchan;
     {
	int	quota, usage, status, free;

/* Look up user in quota file and write report if present */

	status=utl_get_dquota(&diskchan,&uic,&quota,&usage);
	if (status == 1) 
	 { free = quota-usage;
	   if (free < 0) 
	  fprintf(out_file,"User %1.12s has %6d Allocated, %6d Used, %6d Overdrawn\n",
			username->dsc$a_pointer, quota,usage,-free);

	   else
	    fprintf(out_file, "User %1.12s has %6d Allocated, %6d Used, %6d Free\n",
			username->dsc$a_pointer, quota,usage,free);

	 fprintf(out_file,"User %1.12s (UIC [%o,%o]) is in Account %0.*s\n\n",
		 username->dsc$a_pointer, grp(&uic), mem(&uic), 
		 account->dsc$w_length, account->dsc$a_pointer);
	}

    else 
     { if (status == 980)
         lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
       else
	 lib$signal(dqa_noqta, 1, username,0,0,0,0); /* no quota for user */
     }
   }


/*
C+++++
C     WRT_GROUP_REPORT outputs a report for the group whose name
C is specified on the command line.
C
C+++
C INPUT PARAMETERS
C
C     diskchan  channel of disk
C     group	name of the group for report
C+++
*/
wrt_group_report(diskchan, groupd)
	unsigned diskchan;
	struct dsc$descriptor_s *groupd;

    {
	int status, grp_quota = 0, grp_used = 0, grp_free = 0, quota,usage;
	int tot_quota = 0, tot_used = 0, tot_free = 0;
	unsigned uic;
	char acct[9],user[32];
	$DESCRIPTOR(userd,user);   
	$DESCRIPTOR(acctd,acct);          
	int length, free;
	int status1 = 0;
	struct idlist_type *id;

/* Find the groupname in the GROUP information file and return
   the data. */

	
	if ((grp_get_info(groupd, &grp_quota, &grp_used) & 1) == 0) 
	     { lib$signal(dqa_nogroup, 1,&groupd, 0,0,0);
	       return;
	     }

	wrt_header(GRP_HEADER);
	grp_free = grp_quota - grp_used;
	if (grp_free < 0)
	  fprintf(out_file, "%8.8s       %10d     %10d   %10d Overdrawn\n\n",
			groupd->dsc$a_pointer, grp_quota,grp_used, -grp_free);
	else
	  fprintf(out_file, "%8.8s       %10d     %10d   %10d\n\n",
			groupd->dsc$a_pointer, grp_quota,grp_used, grp_free);

	status = uaf_close();

    groupd->dsc$a_pointer[8] = '\0';
    length = strcspn(groupd->dsc$a_pointer,"0 ");

    while ((status = uaf_get_next_user(&userd, &uic, &acctd)) & 1)
     {
       if ((strncmp(acct,groupd->dsc$a_pointer,length) == 0) && 
		(strcspn(acct,"0 ") == length))
	{
/* Look up user in quota file and write report if present */
	status=utl_get_dquota(&diskchan,&uic,&quota,&usage);
        if (status == 980)
	 { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	   return;
         }
	if (status == 1) 
	 { free = quota-usage;
/*	   update account total */
	   tot_quota += quota;
	   tot_used  += usage;
	   tot_free  += free;

	   if (free < 0) 
	      fprintf(out_file, "%12.12s       %6d         %6d       %6d Overdrawn\n",
			user, quota,usage,-free);
	   else
	      fprintf(out_file, "%12.12s       %6d         %6d       %6d\n",user, quota,usage,free);
	  }
	}
      } /* while */

/*   Now list the identifiers also */
/*   First put all the identifiers in a linked list if haven't already */
     if (idlist == NULL) make_id_list();

     id = idlist;
     while (id != NULL)
       {
       	 if ((status1 = strncmp(id->idname, groupd->dsc$a_pointer,length)) == 0)
	   {
/* 	     Look up id in quota file and write report if present */
	     if (utl_get_dquota(&diskchan,&id->idval,&quota,&usage) == 1)
	      { free = quota-usage;
/*	   	update account total */
	   	tot_quota += quota;
	   	tot_used  += usage;
	   	tot_free  += free;

		if (free < 0) 
	      fprintf(out_file, "%12.12s*      %6d         %6d       %6d Overdrawn\n",
			id->idname, quota,usage,-free);
	        else
	      fprintf(out_file, "%12.12s*      %6d         %6d       %6d\n",
	     		id->idname, quota,usage,free);
	   } /* if get quota */
	  } /* if got id */

	   id = id->next;  /* go through list of ids */
	   if (status1 > 0) break; /* alphabetically ordered, so quit */
	 } /* while */


/* Print calculated account totals  */
   fprintf(out_file, "%54.54s\n", UNDERLINE);
   if (tot_free < 0)
      fprintf(out_file, "TOTAL          %10d     %10d   %10d Overdrawn\n\n\n",
			tot_quota, tot_used, -tot_free);
   else
       fprintf(out_file, "TOTAL          %10d     %10d   %10d\n\n\n",
			tot_quota, tot_used, tot_free);

/*** UPDATE TOTAL ALLOCATED IF IT IS WRONG ****/
   if (grp_used != tot_quota)
	{ status = grp_upd_by_name(groupd, &grp_quota, &tot_quota);
	  lib$signal(dqa_grpmod, 0,0,0,0,0);
	}
  }

 
/* MAKE A LIST OF ALL IDENTIFIERS WITH RESOURCES */
 make_id_list()
{
	struct idlist_type *idlast, *id;
	char user[32];
	$DESCRIPTOR(userd,user);   
	int idlength;
	int wild_uic= -1, attributes = 0, context = 0;
	unsigned int uic;

/*   Don't do this if USE_GROUP_IDS is set to 0. */
     if (USE_GROUP_IDS == 0) return;

/*   First put all the identifiers in a linked list if haven't already */
	idlast = NULL;
	while (sys$idtoasc(wild_uic, &idlength, &userd,
	       		 &uic, &attributes, &context) & 1)
	  { if (attributes & KGB$M_RESOURCE)
	      { user[userd.dsc$w_length] = '\0';
		if (((struct idlist_type *)id = malloc(21)) == 0)
		 { fprintf(stderr, "Error allocating memory for identifiers\n");
		   if (idlast != NULL) idlast->next = NULL;
		   sys$finish_rdb(&context);
		   break;
		 }
	       	strcpy(id->idname, user);
 		id->idval = uic;
		if (idlist == NULL) (struct idlist_type *)idlist = id;
		else idlast->next = id;
		(struct idlist_type *)idlast = id;
	     } /* if resource */
	  } /* while */
	 idlast->next = NULL;
	 sys$finish_rdb(&context);

 } /* make_id_list */


              
/*
C+++++
C     DO_mod is the root processor for mod commands. Privilege
C checking for within group quota setting and for overall group
C setting is centralized here. Specific semantics of subcommands
C are processed in individual routines.
C
C+++
C INPUT PARAMETERS
C
C     diskchan		current disk quota channel
C     curuic		current UIC 
C     grpprv		whether user has GRPPRV
C     sysprv		whether user is SYSTEM MANAGER
C     subcommand	subcommand index
C     relative		relative quota flag
C     quota_value	new quota value for mod USER,mod GROUP
C     cmdstring		USERNAME,GROUPNAME, or OUTFILE name
C     uic		uic
C
C+++
*/
do_mod(diskchan, curuic, grpprv, sysprv, subcommand,
	relative, quota_value, cmdstring, uic)

	struct dsc$descriptor_s *cmdstring;
	int	diskchan, grpprv, sysprv, subcommand;
	int	relative, quota_value;
	unsigned curuic, uic ;
    {
	int	old_quota, old_usage, cmd_len, status;
	unsigned uic1,uic2, real_uic, vagrant = 0;
	char	grp_name[8];
	char acct[13],username[32];
	$DESCRIPTOR(acctd,acct);
	$DESCRIPTOR(userd,username);


   if ((grpprv || sysprv) == 0)
     { lib$signal(dqa_nosetprv,0,0,0,0,0);
       return;
     }
   if (subcommand == DEFREP) subcommand = USERREP; /* user = default */

/* Dispatch on subcommand */
   switch (subcommand)
     {
       case UICREP : 
	{ status = get_uic_info(&userd, &uic, &acctd);
	  if (status != 1)
	     { lib$signal(dqa_nouser,0,0,0,0,0);
		vagrant = 1; /* this is a vagrant */
	     }
/*	  Check for privilege to do this */
	  uic1 = real_uic;
	  uic2 = curuic;
	  if (((uic1 >> 16) != (uic2 >> 16)) && (sysprv == 0))
	     { lib$signal(dqa_nosetprv,0,0,0,0,0);
	       return;
	     }

/*	   Get old disk quota */
	   status = utl_get_dquota(&diskchan, &uic,&old_quota, &old_usage);
 	   if (status == 980)
	     { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	       return;
             }
	   if ((status & 1)==0)
	    { old_quota=0;
	      old_usage=0;
	    }
	  set_user(diskchan, uic,old_quota,quota_value,old_usage,
			relative,&acctd, vagrant);

	fprintf(out_file, "\n New Quota :\n === =====\n");
	if (vagrant)
	   do_show(diskchan, UICREP, &userd, uic);
	else
	   do_show(diskchan, USERREP, &userd, uic);

	break;
	}
		       

       case USERREP :  
	{ status = get_user_info(cmdstring, &real_uic, &acctd);
	  if (status != 1)
	     { lib$signal(dqa_nouser,0,0,0,0,0);
	       return;
	     }

/*	  Check for privilege to do this */
	  uic1 = real_uic;
	  uic2 = curuic;
	  if (((uic1 >> 16) != (uic2 >> 16)) && (sysprv == 0))
	     { lib$signal(dqa_nosetprv,0,0,0,0,0);
	       return;
	     }

/*	   Get old disk quota */
	   status = utl_get_dquota(&diskchan, &real_uic,&old_quota, &old_usage);
        if (status == 980)
	 { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	   return;
         }
	   if ((status & 1)==0)
	    { old_quota=0;
	      old_usage=0;
	    }
	  set_user(diskchan, real_uic,old_quota,quota_value,old_usage,
			relative,&acctd, vagrant);
	fprintf(out_file, "\n New Quota :\n === =====\n");
	do_show(diskchan, USERREP, cmdstring, real_uic);

	break;
	} /* case USERREP */

      case GROUPREP : 
	{ if (sysprv == 0)
	     { lib$signal(dqa_nosetprv,0,0,0,0,0);
	       return;
	     }
/*	  Get old disk quota */
	  status = grp_get_info(cmdstring, &old_quota, &old_usage);
	  if ((status & 1)==0)
	    { old_quota=0;
	      old_usage=0;
	    }

/* If it is a relative change, find the new absolute quota */
	  if (relative) quota_value =old_quota + relative*quota_value;
	  if (old_usage > quota_value) lib$signal(dqa_qtaltuse, 0,0,0,0,0);

      	  status = grp_upd_by_name(cmdstring, &quota_value, &old_usage);
	  if (status != 1) 
	    lib$signal(dqa_badgrpchg, 0,0,0,0,0);

	  break;
	} /* case GROUPREP */


       case IDREP :
	{ if (sysprv == 0)  /* IF GRPMGR CHECK IF THIS IS A GROUP ID */
	    { status = get_uic_info(&userd, &curuic, &acctd);
	      acct[acctd.dsc$w_length] = '\0';
	      if ((status != 1) || 
	 	  (strncmp(cmdstring->dsc$a_pointer, acct, 
			strcspn(acctd.dsc$a_pointer, " ")))) 
	      {
	       lib$signal(dqa_nosetprv,0,0,0,0,0);
	       return;
	      }
	    }
	  identifier_sub(diskchan,MOD, cmdstring,sysprv, relative, quota_value);
	  break;
	}

       default : lib$signal(dqa_nocmd,0,0,0,0,0);	            
      } /* end switch */

   }

/*
C+++++
C     SET_USER sets a user quota value. Privilege to do so and the
C existence of the USER have previously been verified.
C+++
C INPUT PARAMETERS
C
C     diskchan		channel of current disk
C     uic		UIC of user
C     old_quota		value user has now
C     new_quota		value user will have
C     usage	   	current disk usage
C     relative		relative quota change flag
C     acctd		account
C     VAGRANT		Is this a vagrant UIC
C
C+++++
*/

set_user(diskchan, uic, old_quota, new_quota, usage, relative,acctd, vagrant)

	int old_quota, new_quota, relative, usage;
	struct dsc$descriptor_s *acctd;
	unsigned uic, diskchan, vagrant;
     {
	int status, dummy, grp_quota = 0, grp_used = 0;

/* If it is a relative change, find the new absolute quota */
	if (relative) 
	 new_quota=old_quota + relative*new_quota;

      if (vagrant == 0)
	{ /* DON'T DO GROUP STUFF IF VAGRANT UIC */	
	if ((grp_get_info(acctd, &grp_quota, &grp_used) & 1) == 0) 
	     { lib$signal(dqa_nogroup, 1,&acctd, 0,0,0);
	       vagrant = 1;
	     }
	else 
/*	     calculate new group usage */
	     grp_used = grp_used - old_quota + new_quota;
	} /* if not vagrant */

/* Case on whether REMOVING or ADDING quota for user */
	if (new_quota <= old_quota) 
	  {/* for REMOVE */
	   if (new_quota < usage) 
	     { lib$signal(dqa_qtaltuse, 0,0,0,0,0);
	       lib$signal(dqa_nochange,0,0,0,0,0);
	       return;
	     }
	   }
	else /* for ADD */
	  { if (grp_used > grp_quota)
	     {	lib$signal(dqa_exgrpqta,0,0,0,0,0);
	        lib$signal(dqa_nochange,0,0,0,0,0);
	    	return;
	     }
	  }/* if */

/* First try modifying quota but if error then add it */

	status = utl_mod_dquota(&diskchan, &uic, &new_quota, &dummy);
        if (status == 980)
	 { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	   return;
         }
	if ((status & 1)==0)
	 {
	   status = utl_add_dquota(&diskchan, &uic, &new_quota, &dummy);
	   if ((status & 1) ==0)
	     {  lib$signal(dqa_badaddqta,0,0,0,0,0);
		lib$signal(dqa_nochange,0,0,0,0,0);
		return;
	     }
	 }
/* now change the group quota */
	if (vagrant == 0)
	    status = grp_upd_by_name(acctd, &grp_quota, &grp_used);
	else lib$signal( dqa_vaguic,0,0,0,0,0);
   }



/* LIst stuff */
do_list(diskchan, subcommand, grpriv,syspriv,curuic)

	int	diskchan, grpriv, syspriv, subcommand;
	unsigned curuic;
      {
	switch (subcommand)
	{
	  case GROUPREP : lst_groups();break;

	  case USERREP  : lst_users(diskchan);break;

	  case VAGREP   : lst_vags(diskchan);break;

	  case IDREP    : lst_ids(diskchan);break;

         default : lib$signal(dqa_nocmd,0,0,0,0,0);	            

	} /* end case */
  }


/*
C+++++
C     LST_GROUPS outputs a report for the groups
C
C+++
*/
lst_groups()
    {
	int status, grp_quota = 0, grp_used = 0, grp_free;
	int tot_quota = 0, tot_used = 0, tot_free = 0;
	char *group[8];
	$DESCRIPTOR(groupd, group);

/*	close group file first to start sequential search */
	status = grp_close();

/* Find the groupname in the GROUP information file and return
C the data. */

	wrt_header(GRP_HEADER);

       while (grp_get_next_name(&groupd, &grp_quota,&grp_used) == 1)
	{
	   grp_free = grp_quota - grp_used;
	   tot_free += grp_free;
	   tot_used += grp_used;
	   tot_quota += grp_quota;
	   if (grp_free < 0)
	      fprintf(out_file, "%8.8s       %10d     %10d   %10d Overdrawn\n",
			group,grp_quota,grp_used, -grp_free);
	   else
	      fprintf(out_file, "%8.8s       %10d     %10d   %10d\n",
			group,grp_quota,grp_used, grp_free);
	   groupd.dsc$w_length = 8;
	   strcpy(group,"        ");
	   grp_quota = 0;
	   grp_used  = 0;
	 }

   fprintf(out_file, "%52.52s\n", UNDERLINE);
   fprintf(out_file, "%TOTAL          %10d     %10d   %10d\n",
		tot_quota, tot_used, tot_free);


    }

/*
C+++++
C     LST_USERS outputs a report for all users
C
C+++
*/
lst_users(diskchan)
  int diskchan;
    {
	int status, free, quota,usage;
	int tot_quota = 0, tot_used = 0, tot_free =  0;
	unsigned uic;
	char acct[13],user[32];
	$DESCRIPTOR(userd,user);
	$DESCRIPTOR(acctd,acct);


/*	close uaf file first to start sequential search */
	status = uaf_close();
	wrt_header(USER_HEADER);

    while ((status = uaf_get_next_user(&userd, &uic, &acctd)) & 1)
      {/* Look up user in quota file and write report if present */
	if ((status = utl_get_dquota(&diskchan,&uic,&quota,&usage)) & 1)
	 { free = quota-usage;
/*	   update total */
	   tot_quota += quota;
	   tot_used  += usage;
	   tot_free  += free;

	   if (free < 0) 
	      fprintf(out_file, 
		   "%12.12s  [%3o,%3o]      %6d     %6d    %6d Overdrawn\n",
			user,grp(&uic), mem(&uic), quota,usage,-free);
	   else
	      fprintf(out_file, "%12.12s  [%3o,%3o]      %6d     %6d    %6d\n",
			user, grp(&uic), mem(&uic), quota,usage,free);
	  }
	else
        if (status == 980)
	 { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	   return;
         }

      } /* while */

/* Print calculated totals  */
   fprintf(out_file, "%56.56s\n", UNDERLINE);
   if (tot_free < 0)
      fprintf(out_file, "TOTAL                     %9d  %9d %9d Overdrawn\n\n\n",
			tot_quota, tot_used, -tot_free);
   else
      fprintf(out_file, "TOTAL                     %9d  %9d %9d\n\n\n",
			tot_quota, tot_used, tot_free);

    }

/*
C+++++
C     LST_VAGRANTS outputs a report for all uic's not associated
C	with a username.
C+++
*/
lst_vags(diskchan)
  int diskchan;
    {
	int status, free, quota,usage, wcc_count = 0;
	int qtot = 0, utot = 0, vag_tot = 0;
	unsigned uic=0;
	char acct[9],user[32];
	$DESCRIPTOR(userd,user);
	$DESCRIPTOR(acctd,acct);

	wrt_header(UIC_HEADER); 

/*	Search sequentially through the diskquota file */
     while ((status = utl_get_dquota_seq(&wcc_count ,&diskchan,&uic,&quota,&usage)) == 1)
      {
/*	See if a username corresponds to the UIC returned */
	if ((get_uic_info(&userd, &uic, &acctd) != 1) && (grp(&uic) < 32768))
	   { free = quota - usage;
	    if (free < 0) 
		fprintf(out_file," [%3o,%3o]      %6d       %6d     %6d Overdrawn\n",
			 grp(&uic), mem(&uic),quota, usage,-free); 
	    else
		fprintf(out_file," [%3o,%3o]      %6d       %6d     %6d\n",
			 grp(&uic), mem(&uic),quota, usage,free); 
	     utot += usage;
	     qtot += quota;
	     vag_tot += 1;
	   } /* if */
        } /* while */

        if (status == 980)
	 { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	   return;
         }

/*   Print calculated totals  */
     fprintf(out_file, "%46.46s\n", UNDERLINE);
     fprintf(out_file, "%d TOTAL      %9d    %9d\n\n\n", vag_tot, qtot, utot);

  }


/*
C+++++
C     LST_IDS outputs a report for all uic's associated
C	with an identifier.
C+++
*/
lst_ids(diskchan)
  int diskchan;
    {
	int status, free, quota,usage, wcc_count = 0;
	int qtot = 0, utot = 0, vag_tot = 0, length;
	unsigned uic=0;
	char acct[9],user[32];
	$DESCRIPTOR(userd,user);
	$DESCRIPTOR(acctd,acct);
	struct idlist_type *id;

	wrt_header(UIC_HEADER); 

/*   put all the identifiers in a linked list if haven't already */
     if (idlist == NULL) make_id_list();

     id = idlist;
     while (id != NULL)
       {
/* 	 Look up id in quota file and write report if present */
 	 if ((status = utl_get_dquota(&diskchan,&id->idval,&quota,&usage)) == 1)
	   { free = quota-usage;
	     fprintf(out_file, "%12.12s",id->idname);
	     if (free < 0) 
		fprintf(out_file,"    %6d       %6d     %6d Overdrawn\n",
			 quota, usage,-free); 
	     else
		fprintf(out_file,"    %6d       %6d     %6d\n",
			  quota, usage,free); 
	     utot += usage;
	     qtot += quota;
	     vag_tot += 1;

	   } /* if get quota */
	 else
           if (status == 980)
	    { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	      return;
            }

	 id = id->next;  /* go through list of ids */
	} /* while */                    

/*   Print calculated totals  */
     fprintf(out_file, "%46.46s\n", UNDERLINE);
     fprintf(out_file, "%d TOTAL      %9d    %9d\n\n\n", vag_tot, qtot, utot);

  }



/*  Add a user to the diskquota file */

do_add(diskchan,subcommand, name,grpprv,sysprv, curuic,
	real_uic, quota_val)

	int diskchan, subcommand, grpprv, sysprv, quota_val;
	unsigned real_uic, curuic;
	struct dsc$descriptor_s *name;
	
    {   int	old_quota, old_usage, cmd_len, status, temp_used, dummy = 0;
	unsigned uic1,uic2, uic, vagrant = 0;
	int dummy_quota = 0, temp;
	char	grp_name[8];
	char acct[13], username[32];
	$DESCRIPTOR(acctd,acct);
	$DESCRIPTOR(usernamed,username);


/* Dispatch on subcommand */
      if ((grpprv || sysprv) == 0)
	 { lib$signal(dqa_noaddprv,0,0,0,0,0);
	   return;
	 }

      if (subcommand == DEFREP) subcommand = USERREP; /* user = default */
      switch (subcommand)
	 {
	case USERREP :  
	  { status = get_user_info(name, &real_uic, &acctd);
	    if (status != 1)
	     { lib$signal(dqa_nouser,0,0,0,0,0);
	       return;
	     }

	    uic1 = real_uic;
	    uic2 = curuic;
	    if (((uic1 >> 16) != (uic2 >> 16)) && (sysprv == 0))
	      { lib$signal(dqa_noaddprv,0,0,0,0,0);
	        return;
	      }
	    status = utl_get_dquota(&diskchan, &real_uic,&temp,&temp);
	    if (status == 980)
	      { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	        return;
	      }
	    if (status == 1) 
	      { lib$signal(dqa_dupuser,0,0,0,0,0);
		return;
	      }
	    set_user(diskchan, real_uic, dummy_quota, quota_val,
			dummy_quota, dummy_quota, &acctd,vagrant);


	    break;
	 }

	case UICREP :  
	  { status = get_uic_info(&usernamed, &real_uic, &acctd);
	    if (status != 1)
	     { lib$signal(dqa_nouser,0,0,0,0,0);
	       return;
	     }

	    uic1 = real_uic;
	    uic2 = curuic;
	    if (((uic1 >> 16) != (uic2 >> 16)) && (sysprv == 0))
	      { lib$signal(dqa_noaddprv,0,0,0,0,0);
	        return;
	      }
	    status = utl_get_dquota(&diskchan, &real_uic,&temp,&temp);
	    if (status == 980)
	     { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	       return;
             }
	    if (status == 1) 
	      { lib$signal(dqa_dupuser,0,0,0,0,0);
		return;
	      }
	    set_user(diskchan, real_uic, dummy_quota, quota_val,
			dummy_quota, dummy_quota, &acctd, vagrant);

	    break;
	 }

       case GROUPREP :
	{ if (sysprv == 0) 
	    { lib$signal(dqa_noaddprv,0,0,0,0,0);
	      return;
	    }
	  status = grp_put(name, &quota_val, &dummy);
	  if (status != 1) lib$signal(dqa_dupgrp, 0,0,0,0,0);
	  break;
	}

       case IDREP :
	{ if (sysprv == 0) 
	    { lib$signal(dqa_noaddprv,0,0,0,0,0);
	      return;
	    }
	 identifier_sub(diskchan, ADD, name, sysprv, 0, quota_val); break;
	}

       default : lib$signal(dqa_nocmd,0,0,0,0,0);	            
     } /* case */
}



/***************************************/
/* Remove entries from the quota files */
/* Update the Group Total.	       */
/***************************************/

do_remove(diskchan,subcommand, name,grpprv,sysprv, curuic, real_uic)

	int diskchan, subcommand, grpprv, sysprv;
	unsigned real_uic, curuic;
	struct dsc$descriptor_s *name;
	
    {   int	old_quota, old_usage, cmd_len, status, temp_used, dummy = 0;
	int new_used, grp_used, grp_quota, vag_status;
	unsigned uic1,uic2, uic;
	char	grp_name[8];
	char acct[13], username[32];
	$DESCRIPTOR(acctd,acct);
	$DESCRIPTOR(usernamed,username);


      if ((grpprv || sysprv) == 0)
	 { lib$signal(dqa_noaddprv,0,0,0,0,0);
	   return;
	 }

/* Dispatch on subcommand */
    if (subcommand == DEFREP) subcommand = USERREP; /* user = default */
    switch (subcommand)
      {
	case USERREP :
	  { status = get_user_info(name, &uic, &acctd);
	    if (status != 1)
	     { lib$signal(dqa_nouser,0,0,0,0,0);
	       return;
	     }

/*	 Check privileges */
	    uic1 = uic;
	    uic2 = curuic;
	    if (((uic1 >> 16) != (uic2 >> 16)) && (sysprv == 0))
	      { lib$signal(dqa_noremprv,0,0,0,0,0);
	        return;
	      }
/*	 Check for no usage */
	    status = utl_get_dquota(&diskchan,&uic,&old_quota,&old_usage);
	    if (status == 980)
	      { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	        return;
              }
	    if ((status & 1)==0)
	     { lib$signal(dqa_noqta,0,0,0,0,0); 
		return;
	     }		
	    if (old_usage) 
	     {	lib$signal(dqa_usgenon0,0,0,0,0,0);
		return;
	     }
	    status = utl_rem_dquota(&diskchan, &uic, &old_usage, &old_usage);
	    if (status != 1) { lib$signal(dqa_badremqta,0,0,0,0,0); return;}

/*	    Update the group quota */
	    if ((grp_get_info(&acctd, &grp_quota, &grp_used) & 1) == 0) 
	       { lib$signal(dqa_nogroup, 1,&acctd, 0,0,0);
	         return;
	       }
	     new_used = grp_used - old_quota;
	     status = grp_upd_by_name(&acctd, &grp_quota, &new_used );

	     break;
	   }


	case UICREP  : 

	  { if ((vag_status = get_uic_info(&usernamed, &real_uic, &acctd)) != 1)
	     lib$signal(dqa_nouser,0,0,0,0,0);

/*	 Check privileges */
	    uic1 = real_uic;
	    uic2 = curuic;
	    if (((uic1 >> 16) != (uic2 >> 16)) && (sysprv == 0))
	      { lib$signal(dqa_noremprv,0,0,0,0,0);
	        return;
	      }

/*	 Check for no usage */
	 status = utl_get_dquota(&diskchan,&real_uic,&old_quota,&old_usage);
         if (status == 980)
	   { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	     return;
           }
	 if ((status & 1)==0)
	     { lib$signal(dqa_noqta,0,0,0,0,0); 
		return;
	     }		
	  if (old_usage) 
	     { 
		lib$signal(dqa_usgenon0,0,0,0,0,0);
		return;
	      }

	   status = utl_rem_dquota(&diskchan,&real_uic,&old_usage,&old_usage);
	   if (status != 1) { lib$signal(dqa_badremqta,0,0,0,0,0); return; }

/*	   Update the group quota */
	   if ((vag_status != 1) ||
		((grp_get_info(&acctd, &grp_quota, &grp_used) & 1) == 0))
	       { lib$signal(dqa_nogroup, 1,&acctd, 0,0,0);
	         return;
	       }
	    new_used = grp_used - old_quota;
	    status = grp_upd_by_name(&acctd, &grp_quota, &new_used );

	    break;
	   }


	case IDREP :
	  { 
	  if (sysprv == 0)  /* IF GRPMGR CHECK IF THIS IS A GROUP ID */
	    { status = get_uic_info(&usernamed, &curuic, &acctd);
	      acct[acctd.dsc$w_length] = '\0';
	      if ((status != 1) || 
	 	  (strncmp(name->dsc$a_pointer, acct, 
			strcspn(acctd.dsc$a_pointer, " ")))) 
	      { lib$signal(dqa_noremprv,0,0,0,0,0);
	        return;
	      }
	    }
	    identifier_sub(diskchan,REMOVE, name,sysprv, 0, 0);

	     break;
	   }



	case GROUPREP : 
	   { if ( sysprv == 0)
		 { lib$signal(dqa_noremprv,0,0,0,0,0);
	   	   return;
	 	 }
	     status = grp_del_by_name( name);
	     if (status != 1) 
		lib$signal(dqa_badremqta, 0,0,0,0,0);
	     break;
	   }

       case NULLACC :
	 { 
	   if ( sysprv == 0)
		{ lib$signal(dqa_noremprv,0,0,0,0,0);
	   	  return;
	 	}
/*	close group file first to start sequential search */
	status = grp_close();

/* 	Find the null groups in the GROUP information file */
       while (grp_get_next_name(&acctd, &grp_quota,&grp_used) == 1)
	{ if (grp_used == 0)
	   { status = grp_del_by_name( &acctd);
	     if (status != 1) 
		lib$signal(dqa_badremqta, 0,0,0,0,0);
	     else
	        fprintf(out_file, "Removed null account %8.8s\n", acct);
	    }
	   acctd.dsc$w_length = 8;
	   strcpy(acct,"        ");
	   grp_used  = 0;
	  } /* while */

	break;
	} /* case nullrep */

       default : lib$signal(dqa_nocmd,0,0,0,0,0);	            
    } /* case */
  }	


/*********************/
/* Rename an account */
/*********************/

do_rename( subcommand, names, sysprv)

	int subcommand, sysprv;
	struct dsc$descriptor_s *names;
	
    {   int	status, grp_quota, grp_used;
	int	index = 0;
	char	group1[10], group2[10];
	$DESCRIPTOR(group1d,group1);
	$DESCRIPTOR(group2d,group2);

	if (sysprv == 0) 
	  { lib$signal(dqa_nosetprv,0,0,0,0,0);
	    return;
	  }

	names->dsc$a_pointer[names->dsc$w_length] = '\0';
	index = next_param(names->dsc$a_pointer, index ,&group1d);
	index = next_param(names->dsc$a_pointer, index, &group2d);

/*	Get old account information */
	status = grp_get_info(&group1d, &grp_quota, &grp_used);
	if ((status & 1) == 0) 
	     { lib$signal(dqa_nogroup, 1,&group1d, 0,0,0);
	       return;
	     }

/*      Add new account with old account's quota values */
	status = grp_put(&group2d, &grp_quota, &grp_used);
	if (status != 1) 
	   { lib$signal(dqa_dupgrp, 0,0,0,0,0);
	     return;
	   }

/*	Delete old account */
	status = grp_del_by_name( &group1d);
	if (status != 1) 
	   lib$signal(dqa_badgrpchg, 0,0,0,0,0);

 }

/********************************************************************/
/* REBUILD ACCOUNTS : RECALCULATE ALL THE USAGE FOR THE ACCOUNTS BY */
/* ADDING THE ALLOCATION OF ALL USERS IN AN ACCOUNT.		    */
/* GET FIRST ACCOUNT FROM GROUP QUOTA FILE, SEARCH UAF FOR USERS IN */
/* THIS ACCOUNT AND ADD UP ALLOCATION.				    */

rebuild_acct(diskchan, subcommand, account, sysprv)
    int diskchan, subcommand, sysprv;
	struct dsc$descriptor_s *account;
	
    {   int	status, grp_quota = 0, grp_used = 0, usage = 0, quota = 0;
	int	index = 0, tot_used = 0, length =0;
	unsigned uic;
	char	group[10], acct[10], username[32];
	$DESCRIPTOR(acctd,acct);
	$DESCRIPTOR(groupd,group);
	$DESCRIPTOR(userd,username);
	struct idlist_type *id;

	if (sysprv == 0) 
	  { lib$signal(dqa_nosetprv,0,0,0,0,0);
	    return;
	  }
 if (subcommand == DEFREP) subcommand = GROUPREP;
switch (subcommand)
  {
   case SYSTEMREP : 
   {
/*	close group file first to start sequential search */
	status = grp_close();

/* Find the groupname in the GROUP information file and calculate
  the data. */

  while (grp_get_next_name(&groupd, &grp_quota,&grp_used) == 1)
    {
	groupd.dsc$a_pointer[8] = '\0';
	length = strcspn(groupd.dsc$a_pointer,"0 ");
/*	MAKE SURE WE START AT BEGINNING OF UAF FILE */
	status = uaf_close();

/*	NOW FIND ALL USERS IN THIS ACCOUNT   */
	while (uaf_get_next_user(&userd, &uic, &acctd) & 1)
	 {
	   if ((strncmp(acct,groupd.dsc$a_pointer,length) == 0) && 
	  	  (strcspn(acct,"0 ") == length))
/*		FIND USER IN QUOTA FILE AND UPDATE ACCOUNT TOTAL */
		quota = 0;
		if (status = utl_get_dquota(&diskchan,&uic,&quota,&usage))
	 	    tot_used += quota;
		if (status == 980)
		   { lib$signal(dqa_qfnotact, 0,0,0,0,0,0); /* q not active */
		     return;
	           }

          } /* while uaf */


/*   Now get the identifiers also */
/*   put all the identifiers in a linked list if haven't already */
     if (idlist == NULL) make_id_list();

     id = idlist;
     while (id != NULL)
       {
/* 	 Look up id in quota file and write report if present */
 	 if ((status = utl_get_dquota(&diskchan,&id->idval,&quota,&usage)) == 1)
 	    tot_used += quota;
	 id = id->next;
	} /* while */

/* now change the group quota */
	status = grp_upd_by_name(&groupd, &grp_quota, &tot_used);


/*	CLEANUP AND CONTINUE */
	groupd.dsc$w_length = 8;
	strcpy(group,"        ");
	grp_quota = 0;
	grp_used  = 0;
	tot_used = 0;
      } /* WHILE GROUP QUOTA */

      break;
    } /* case DEFREP */

  case GROUPREP :

    {
/* Find the groupname in the GROUP information file and calculate the data. */
	status = grp_get_info(account, &grp_quota, &grp_used);
	if ((status & 1) == 0) 
         { lib$signal(dqa_nogroup, 1,account, 0,0,0);
           return;
         }
	account->dsc$a_pointer[8] = '\0';
	length = strcspn(account->dsc$a_pointer,"0 ");
/*	MAKE SURE WE START AT BEGINNING OF UAF FILE */
	status = uaf_close();

/*	FIND ALL USERS IN THIS ACCOUNT   */
	while (uaf_get_next_user(&userd, &uic, &acctd) & 1)
	 {
	   if ((strncmp(acct,account->dsc$a_pointer,length) == 0) && 
	  	  (strcspn(acct,"0 ") == length))
/*		FIND USER IN QUOTA FILE AND UPDATE ACCOUNT TOTAL */
		if (status = utl_get_dquota(&diskchan,&uic,&quota,&usage) & 1)
	 	     tot_used += quota;
	        if (status == 980)
		 { lib$signal(dqa_qfnotact, 0,0,0,0,0,0); /* q not active */
	   	   return;
         	 }

          } /* while uaf */



/*   Now get the identifiers also */
/*   put all the identifiers in a linked list if haven't already */
     if (idlist == NULL) make_id_list();

     id = idlist;
     while (id != NULL)
       {
/* 	 Look up id in quota file and write report if present */
 	 if ((status = utl_get_dquota(&diskchan,&id->idval,&quota,&usage)) == 1)
 	    tot_used += quota;
	 id = id->next;
	} /* while */

/* now change the group quota */
      status = grp_upd_by_name(account, &grp_quota, &tot_used);
      break;

    } /* case GROUPREP */

  default : lib$signal(dqa_nocmd,0,0,0,0,0);	            
 } /* switch */

  }    


/**********************************************************************/
/* MAXIMIZE ACCOUNTS : RECALCULATE ALL THE USAGE FOR THE ACCOUNTS BY  */
/* ADDING THE ALLOCATION OF ALL USERS IN AN ACCOUNT.		      */
/* SET THE ALLOCATION TO THE LARGER OF THE USAGE AND CURRENT ALLOCATION */

do_maximize(diskchan, subcommand, account, sysprv)
   	int diskchan, subcommand, sysprv;
	struct dsc$descriptor_s *account;
	   
    {   int	status, grp_quota, grp_used, usage, quota;
	char	group[10], username[32];
	unsigned uic, vagrant = 0;
	$DESCRIPTOR(groupd,group);
	$DESCRIPTOR(userd,username);
	struct idlist_type *id;

 if (sysprv == 0) 
   { lib$signal(dqa_nosetprv,0,0,0,0,0);
     return;
   }

if (subcommand == DEFREP) subcommand = GROUPREP;

switch (subcommand)
  {
   case USERREP :

/* Set users quota to larger of usage and allocation */
     {/* MAKE SURE WE START AT BEGINNING OF UAF FILE */
	status = uaf_close();

      while (uaf_get_next_user(&userd, &uic, &groupd) & 1)
        { /* Look up user in quota file and write report if present */
	  quota = 0; usage = 0;
	  if ((status = utl_get_dquota(&diskchan,&uic,&quota,&usage)) & 1)
       	     if (usage > quota)
	       { set_user(diskchan, uic, quota,usage, usage,0, &groupd,vagrant);
	  	fprintf(out_file, "%12.12s changed from  %8d  to   %8d\n",
	  		username, quota, usage);
	       }
          if (status == 980)
	     { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	       return;
             }

        } /* while */

/*   Now get the identifiers also */
/*   put all the identifiers in a linked list if haven't already */
     if (idlist == NULL) make_id_list();

     id = idlist;
     while (id != NULL)
       {
	 quota = 0; usage = 0;
/* 	 Look up id in quota file and write report if present */
 	 if ((utl_get_dquota(&diskchan,&id->idval,&quota,&usage) & 1) &&
       	    (usage > quota))
	      { strcpy(username, id->idname);
		userd.dsc$w_length = strlen(username); 
		identifier_sub(diskchan,MOD, &userd, sysprv, 0, usage);
	  	fprintf(out_file, "%12.12s changed from  %8d  to   %8d\n",
	  	 	id->idname, quota, usage);
	       }
	 id = id->next;
	} /* while */

	break;
     } /* USERREP */


   case SYSTEMREP : /* MAXIMIZE ACCOUNTS */

   {  rebuild_acct(diskchan, SYSTEMREP, account, sysprv);

/*    close group file first to start sequential search */
      status = grp_close();

/*    Find the GROUP information and update quota to MAX(QUOTA, USAGE) */
     while (grp_get_next_name(&groupd, &grp_quota,&grp_used) == 1)
      {
       if ((grp_used > grp_quota) && 
	   (grp_upd_by_name(&groupd, &grp_used, &grp_used) & 1))
	      fprintf(out_file, "%12.12s  changed from  %8d  to   %8d\n",
			group, grp_quota, grp_used);
	grp_used = 0; grp_quota = 0;
       }

      break;
    } /* case DEFREP */

  case GROUPREP :

   {  rebuild_acct(diskchan, GROUPREP, account, sysprv);

/*    Find the GROUP information and update quota to MAX(QUOTA, USAGE) */
      if ((grp_get_info(account, &grp_quota, &grp_used) & 1) == 0) 
         { lib$signal(dqa_nogroup, 1,account, 0,0,0);
           return;
         }

       if (grp_used > grp_quota)
	  status = grp_upd_by_name(account, &grp_used, &grp_used);

      break;
    } /* case GROUPREP */


  default : lib$signal(dqa_nocmd,0,0,0,0,0);	            
 } /* switch */

  }    

/**********************************************/
/* Identifier routine for SHOW, ADD, MOD, REM */
/**********************************************/

identifier_sub(diskchan, subcommand, idname, sysprv, relative, quota_val)
   	int diskchan, subcommand, sysprv, quota_val, relative;
	struct dsc$descriptor_s *idname;

   { int quota, usage, status, free;
     int dummy_quota = 0;
     unsigned idval;
     $DESCRIPTOR(dummy," ");
     char acct[9], acct_print[11];
     $DESCRIPTOR(acctd,acct);
     int grp_quota, grp_used, new_used, idpos;
     int vagrant = 0;  /* not vagrant identifier */

    /* translate indentifier name to value */
    idname->dsc$w_length = strcspn(idname->dsc$a_pointer," ");
    if ((sys$asctoid(idname, &idval, 0) & 1) == 0)
	{ lib$signal(dqa_nouser,0,0,0,0,0);
	  return;
	}

/* Look up identifier in quota file */
    status=utl_get_dquota(&diskchan, &idval, &quota, &usage);
        if (status == 980)
	 { lib$signal(dqa_qfnotact, 0, 0,0,0,0,0); /* quotas not active */
	   return;
         }

/* Find group of identifier */
	strcpy(acct, "        ");
	for (idpos = 3; idpos <= idname->dsc$w_length; idpos++)
	  { strncpy(acct, idname->dsc$a_pointer, idpos);
	    acctd.dsc$w_length = idpos;
	    if (grp_get_info(&acctd, &grp_quota, &grp_used) & 1) 
	      break; /* got one */
	  }
/* No such group ? */
	if (idpos > idname->dsc$w_length)
	 { lib$signal(dqa_nogroup, 1,&acctd, 0,0,0);
	   fprintf(out_file, "\n");
	   acctd.dsc$w_length = 0;
	   vagrant = 1;
	 }

    switch (subcommand)
     {
      case SHOW :
	{ /* if identifier exists, write report */ 
	if (status == 1) 
	 { free = quota-usage;
	   if (acctd.dsc$w_length) 
	     sprintf(acct_print, "(%1.*s)",acctd.dsc$w_length, acct);
	   else acct_print[0] = '\0';
	   if (free < 0) 
	     fprintf(out_file,
	       "Identifier %1.12s %s has %6d Allocated, %6d Used, %6d Overdrawn\n",
			idname->dsc$a_pointer, acct_print, quota, usage, -free);

	   else
	     fprintf(out_file, 
	       "Identifier %1.12s %s has %6d Allocated, %6d Used, %6d Free\n",
			idname->dsc$a_pointer, acct_print, quota,usage,free);
	 }

	else lib$signal(dqa_noident, 1, idname,0,0,0,0); /* no quota for user */

       break;
       } /* case show */

     case MOD :
	{
	  if (status != 1)
	     { lib$signal(dqa_noident, 0, 0,0,0,0,0); /* no quota for user */
		return;
	     }
/*	  change quota  */
	  set_user(diskchan, idval, quota, quota_val, usage,
			relative, &acctd, vagrant);

	  break;
	} /* case mod */

     case ADD :
	{
/*	  change quota  */
	  set_user(diskchan, idval, dummy_quota, quota_val,
			dummy_quota, dummy_quota, &acctd, vagrant);

	  break;
	} /* case add */


     case REMOVE :
	{
	  if (status != 1)
	     { lib$signal(dqa_noident, 0, 0,0,0,0,0); /* no quota for user */
		return;
	     }

	    if (usage) 
	     {	lib$signal(dqa_usgenon0,0,0,0,0,0);
		return;
	     }
	    status = utl_rem_dquota(&diskchan, &idval, &usage, &usage);
	    if (status != 1) { lib$signal(dqa_badremqta,0,0,0,0,0); return;}

/*	    Update the group quota */
	    if (vagrant) 
	         return;
	     new_used = grp_used - quota;
	     status = grp_upd_by_name(&acctd, &grp_quota, &new_used );
	  break;
	} /* case rem */

     default : lib$signal(dqa_nocmd,0,0,0,0,0);
    } /* switch */
  }

/*
C+++++
C     CHK_AUTHORIZATION obtains the attributes of the USERNAME 
C	from the SYSUAF file and the RIGHTSLIST.
C
C+++
C OUTPUT PARAMETERS
C
C     curuic	[grp,mem] of the current user
C     grpprv	whether the user can alter group quotas
C     sysprv	whether the user can alter the overall group attribs.
C     status	whether the PROCESS and SYSUAF attributes match
C
C+++
***/

chk_authorization(curuic, grpprv, sysprv)
	int *grpprv, *sysprv;
	unsigned *curuic;

    {   char	username[12], groupname[8];
      	unsigned uic;	
	int status;

	$DESCRIPTOR(userd,username);
	$DESCRIPTOR(groupd,groupname);

/* Get information from SYSUAF using username from process */
	cuserid(username);
	userd.dsc$w_length=strlen(username);
	status = uaf_get_auth(&userd, curuic, &groupd, grpprv, sysprv);
	if (status != 1) lib$signal(dqa_nopriv,0,0,0,0,0);

   }

/*
C	DO_HELP displays help for the specified functions in the
C	command string. A maximum of 4 levels of help is permitted.
C
C+++
C INPUT PARAMETERS
C
C	help_index	index of the DQUOTA help file returned from
C			initialization.
C	cmdstring	the help string that was entered.
C	length		the length of the string
C+++++
*/

do_help(help_index,cmdstring, length)

	int	*help_index, length;
	char	*cmdstring;

     {
	int	start[4], finish[4], ptr=0, i, index = 0;
	char	param1[20], param2[20], param3[20], param4[20];
	$DESCRIPTOR(param1d,param1);
	$DESCRIPTOR(param2d,param2);
	$DESCRIPTOR(param3d,param3);
	$DESCRIPTOR(param4d,param4);
	$DESCRIPTOR(gqmd,"GQM");

	cmdstring[length] = '\0';
	index = next_param(cmdstring,index ,&param1d);
	index = next_param(cmdstring,index, &param2d);
	index = next_param(cmdstring,index, &param3d);
	index = next_param(cmdstring,index, &param4d);

	lbr$get_help(help_index,0,0,0, &gqmd, &param1d, 
			&param2d, &param3d, &param4d, 0, 0,0,0,0);
	fprintf(out_file, "\n\n\n");
  }




/* WRITE A HEADER OUT */
wrt_header(head_format)
	char *head_format;
  {
	fprintf(out_file, "\n");
	fprintf(out_file, head_format);
	fprintf(out_file, "%*.*s\n", strlen(head_format), strlen(head_format), UNDERLINE);

  }


/********************************************/
/*  RETURN THE NEXT PARAMETER		    */
/*  STARTING AT THE INDEX START.	    */

  next_param(string,start, paramd)

  char  string[];
  int   start;
  struct dsc$descriptor_s *paramd;

  {	int i, end = 0, eol;
	char *param;

	param = paramd->dsc$a_pointer;
	eol = strlen(string);
	i = start - 1;
	do  i += 1;
	while (isspace(string[i]) != 0);

	if (i == eol) 
	  { param[0] = '\0';
	    paramd->dsc$w_length = 0;
	    paramd->dsc$a_pointer = 0;
	    return(start);
	  }

	do (param[end++] = string[i]);
	while (isspace(string[++i]) == 0);
	param[end] = '\0';
	paramd->dsc$w_length = strlen(param);


/*	RETURN INDEX OF END OF PARAMETER */
	return(i);
  }

