1: # include   <stdio.h>
   2: # include   <ingres.h>
   3: # include   <aux.h>
   4: # include   <version.h>
   5: # include   <opsys.h>
   6: # include   <access.h>
   7: # include   <lock.h>
   8: # include   <signal.h>
   9: # include   <sccs.h>
  10: # include   <setjmp.h>
  11: # include   <pwd.h>
  12: 
  13: SCCSID(@(#)initucode.c	8.5 	2/8/85)
  14: 
  15: /*
  16: **  INITUCODE -- initialize standalone process
  17: **
  18: **	This function initializes a standalone process, initializing
  19: **	a lot of global variables, scanning the argument vector for
  20: **	some special flags (-u and +-w), seperating flags and
  21: **	parameters, and so forth.
  22: **
  23: **	Every standalone program should begin with the lines:
  24: **			i = initucode(argc, argv, ...);
  25: **			switch (i)
  26: **				...
  27: **
  28: **	On a return of 2, 3, or 4, essentially none of the processing
  29: **	is done (particularly true with return 4).  Virtually nothing
  30: **	can be done in the calling program except print a "usage"
  31: **	message and exit.  The exception to this is that 'Pathname'
  32: **	is set, so that it can be used in the error printing.  For
  33: **	example, ingres.c cats file .../files/usage on this sort of
  34: **	error.
  35: **
  36: **	If it is preferable to not lock the database at this time,
  37: **	the 'waitmode' parameter should be passed as -1.  This still
  38: **	causes the 'Wait_action' variable to be initialized, but the
  39: **	database is not actually locked.  It can be locked by calling:
  40: **		db_lock(Dbpath, M_EXCL);
  41: **	at the proper time.
  42: **
  43: **	For the main effects of this routine, see the "Side Effects"
  44: **	section below.
  45: **
  46: **	Parameters:
  47: **		argc -- argc from main.
  48: **		argv -- argv from main.
  49: **		dbflag -- TRUE -- take the first parameter as the
  50: **				database name.
  51: **			FALSE -- don't take the first parameter as
  52: **				the database name.
  53: **		paramlist -- a pointer to an array[4] of pointers
  54: **			to character; set to the extra fields of
  55: **			the users file entry for the real user
  56: **			executing the code (not the user on the
  57: **			-u flag).  If NULL, this is ignored.
  58: **		waitmode -- M_EXCL -- set an exclusive lock on the
  59: **				database.
  60: **			M_SHARE -- set a shared lock on the database.
  61: **			-1 -- don't set a lock on the database.
  62: **				However, other stuff (Wait_action) is
  63: **				still set up so that the lock can be
  64: **				placed later by calling 'db_lock'.
  65: **
  66: **	Returns:
  67: **		0 -- everything is ok.
  68: **		1(NODB) -- the database does not exist.
  69: **		2(NOACCESS)-- you are not authorized to access this database.
  70: **		3(INVALIDUSR)-- you are not a valid INGRES user.
  71: **		4(NODBNAME)-- no database name was specified (only if dbflag
  72: **			== TRUE).
  73: **		5(INDIRECT)-- everything is ok, but there was an indirect
  74: **			taken.
  75: **		6(INDNODB)-- there was an indirect taken, but there was no
  76: **			database there.
  77: **
  78: **		If dbflag == FALSE, you can only get returns 0 and
  79: **			3.
  80: **
  81: **	Side Effects:
  82: **		A lot of variables are set, as follows:
  83: **
  84: **		Dbpath -- set to the pathname of the database (only
  85: **			if dbflag == TRUE).  It is set even if the
  86: **			database does not exist.
  87: **		Parmvect -- set to the parameters from argv, that is,
  88: **			anything not beginning with '+' or '-'.
  89: **		Flagvect -- set to the flags from argv, that is,
  90: **			everything beginning with '+' or '-'.  The
  91: **			flags '+w', '-w', and '-u' are stripped out,
  92: **			however.
  93: **		Wait_action -- set to the appropriate action (A_SLP
  94: **			or A_RTN) based on the +-w flags and whether
  95: **			we are running in background or not.
  96: **			This is automatically used by 'db_lock()'.
  97: **		Usercode -- set to the persons effective user code
  98: **			(that is, after the -u processing).  Only
  99: **			the INGRES user or the DBA can use the -u
 100: **			flag.
 101: **		Pathname -- set to the pathname of the INGRES subtree.
 102: **		Status -- an integer set to the user status field
 103: **			of the users file for the real user.
 104: **		Ing_uid -- set to the user id of the INGRES user.
 105: **
 106: **		The rubout signal (signal 2) is caught, and refered
 107: **		to the standard rubout processor (see rub.c); thus,
 108: **		a routine called 'rubproc' must be defined in the
 109: **		standalone code (which will just call exit, in the
 110: **		normal case).
 111: **
 112: **		The 'adminhdr' part of the 'Admin' struct is filled
 113: **		in.  This is not done with readadmin() and is not
 114: **		equivalent to an 'admininit()', but it does make
 115: **		the DBA and database status available.
 116: **
 117: **		This routine can also exit immediately with an
 118: **		error message.
 119: **
 120: **	Defined Constants:
 121: **		MAXPARGS -- the maximum number of parameter type
 122: **			arguments to any standalone program.
 123: **		MAXFARGS -- the maximum number of flag type arg-
 124: **			uments to any standalong program (not inclu-
 125: **			ding flags in the users file, and the +-w
 126: **			and -u flags).
 127: **
 128: **	Files:
 129: **		/etc/passwd -- to get the pathname for user "ingres".
 130: **		.../files/users -- to get all the per-user information,
 131: **			and to process the -u flag.
 132: **
 133: **	Compilation Flags:
 134: **		xB_UNIX, xV6_UNIX -- see comments in aux.h
 135: **
 136: **	Trace Flags:
 137: **		none
 138: */
 139: 
 140: 
 141: # define    MAXFARGS    15  /* maximum flag-type arguments */
 142: # define    MAXPARGS    20  /* maximum parameter-type args */
 143: 
 144: char    *Usercode;  /* the usercode of the effective user */
 145: char    *Pathname;  /* path of INGRES subtree */
 146: int Status;     /* the user status of the real user */
 147: int Rubignored; /* set if rubouts ignored */
 148:             /* (also in initproc for system processes) */
 149: int Wait_action;    /* the action on the db_lock */
 150: char    *Dbpath;    /* the pathname of the database */
 151: char    *Flagvect[MAXFARGS+1];  /* the flags from argv */
 152: char    *Parmvect[MAXPARGS+1];  /* the parameters from argv */
 153: int Ing_uid;    /* the user id of the INGRES user */
 154: jmp_buf Initbuf;    /* Buffer to go back to initucode with */
 155: 
 156: initucode(argc, argv, dbflag, paramlist, waitmode)
 157: int argc;
 158: char    **argv;
 159: int dbflag;
 160: char    *paramlist[4];
 161: int waitmode;
 162: {
 163:     register char   *p;
 164:     char        *q;
 165:     char        c;
 166:     FILE        *iop;
 167:     static char sbuf[MAXLINE * 2];
 168:     register char   *sbufp;
 169:     char        buf[MAXLINE+1];
 170:     register int    i;
 171:     int     npermit;
 172:     int     rtval;
 173:     char        *field[UF_NFIELDS];
 174:     int     actualuid;
 175:     auto int    uid;
 176:     auto int    gid;
 177:     int     waitflag;
 178:     char        *userflag;
 179:     struct sgttyb   gttydummy;
 180:     int     fvi, pvi;
 181:     char        **avp;
 182:     char        usr_ovrd[3];
 183:     extern      rubcatch();
 184:     static short    tvect[100];
 185:     bool        nobuffer;
 186:     struct  passwd  *pwd;
 187:     struct  passwd  *getpwnam();
 188: # ifdef xV7_UNIX
 189:     extern char *getenv();
 190: # endif xV7_UNIX
 191: 
 192:     /*
 193: 	**  Set up interrupts.
 194: 	*/
 195: 
 196:     if ( setjmp(Initbuf) )
 197:         exit(-1);
 198:     if (signal(SIGINT, SIG_IGN) == SIG_DFL)
 199:         signal(SIGINT, rubcatch);
 200: # ifdef xV6_UNIX
 201:     for (avp = argv; *avp != 0 && *avp != (char *) -1; avp++)
 202:         continue;
 203:     *avp = NULL;
 204: # endif
 205: 
 206:     /*
 207: 	**  Do basic initialization, such as setting trace flags.
 208: 	*/
 209: 
 210:     nobuffer = tTrace(argv, 'T', tvect, 100);
 211:     if (!nobuffer)
 212:         set_so_buf();
 213:     sbufp = sbuf;
 214: 
 215:     /*
 216: 	**  Get pathname of INGRES subtree from /etc/passwd file
 217: 	**  entry for USERINGRES (presumably "ingres") and save it
 218: 	**  in 'Pathname'.
 219: 	**
 220: 	**  This algorithm suggested by Jim Popa.
 221: 	*/
 222: 
 223:     if ( (pwd = getpwnam(USERINGRES)) == NULL )
 224:         syserr("initucode: No user %s in password file",USERINGRES);
 225: # ifdef xV7_UNIX
 226:     Pathname = getenv("INGPATH");
 227:     if (Pathname == NULL)
 228:     {
 229: # endif xV7_UNIX
 230:         Pathname = sbufp;
 231:         sbufp += smove(pwd->pw_dir, sbufp) + 1;
 232: # ifdef PATHEXT
 233:         sbufp += smove(PATHEXT, sbufp - 1);
 234: # endif PATHEXT
 235: # ifdef xV7_UNIX
 236:     }
 237: # endif xV7_UNIX
 238: 
 239:     /* create the INGRES user id */
 240:     Ing_uid = pwd->pw_uid;
 241: # ifdef xV6_UNIX
 242:     Ing_uid &= I1MASK;
 243: # endif
 244: # ifdef xB_UNIX
 245:     gid = pwd->pw_gid;
 246:     Ing_uid = (Ing_uid & I1MASK) | ((gid & I1MASK) << 8);
 247: # endif
 248:     endpwent();
 249: 
 250:     /*
 251: 	**  Scan the argument vector.  The following flags are pulled
 252: 	**  out of the vector (and argc and argv are adjusted so it
 253: 	**  looks like they never existed):
 254: 	**	+w, -w -- (don't) wait for the database to be free.
 255: 	**	-uxxx -- run as user xxx.  If first character is a
 256: 	**	colon, the format must be '-u:xx' where 'xx' is the
 257: 	**	internal user code.
 258: 	*/
 259: 
 260:     avp = argv;
 261:     fvi = 0;
 262:     pvi = 0;
 263:     waitflag = 0;
 264:     userflag = NULL;
 265:     usr_ovrd[0] = 0;
 266: 
 267:     for (i = argc; --i > 0; )
 268:     {
 269:         p = *++avp;
 270:         if (p[0] == '+')
 271:         {
 272:             if (p[1] == 'w')
 273:                 waitflag = 1;
 274:             else
 275:                 goto boring;
 276:         }
 277:         else if (p[0] == '-')
 278:         {
 279:             switch (p[1])
 280:             {
 281:               case 'w':
 282:                 waitflag = -1;
 283:                 break;
 284: 
 285:               case 'u':
 286:                 if (p[2] == ':')
 287:                 {
 288:                     if (p[3] == 0 || p[4] == 0 || p[5] != 0)
 289:                     {
 290:                         printf("Bad flag %s\n", p);
 291:                         exit(-1);
 292:                     }
 293:                     smove(&p[3], usr_ovrd);
 294:                 }
 295:                 else
 296:                     userflag = &p[2];
 297:                 break;
 298: 
 299:               default:
 300:                 /* not an interesting flag */
 301:             boring:
 302:                 if (fvi >= MAXFARGS)
 303:                 {
 304:                     printf("Too many flags\n");
 305:                     exit(-1);
 306:                 }
 307:                 Flagvect[fvi++] = p;
 308:                 break;
 309:             }
 310:         }
 311:         else
 312:         {
 313:             /* not a flag: save in Parmvect */
 314:             if (pvi >= MAXPARGS)
 315:             {
 316:                 printf("Too many parmameters\n");
 317:                 exit(-1);
 318:             }
 319:             Parmvect[pvi++] = p;
 320:         }
 321:     }
 322: 
 323:     if (pvi <= 0 && dbflag)
 324:     {
 325:         return (NODBNAME);  /* no database name specified */
 326:     }
 327: 
 328:     /*
 329: 	**  Scan the "users" file.
 330: 	*/
 331: 
 332:     if ((iop = fopen(ztack(Pathname, "/files/users"), "r")) == NULL)
 333:         syserr("initucode: open error");
 334: 
 335:     /* get uid (out of loop) for test */
 336: #	ifdef xV6_UNIX
 337:     actualuid = getuid() & I1MASK;
 338: #	endif
 339: #	ifndef xV6_UNIX
 340:     actualuid = getuid();
 341: #	endif
 342: 
 343:     /* scan users file, one line at a time */
 344:     rtval = INVALIDUSR;
 345:     while ((Usercode == NULL || userflag != NULL) && fgets(buf, MAXLINE, iop) != NULL)
 346:     {
 347: 
 348:         /* decode users file entry */
 349:         i = 0;
 350:         field[0] = buf;
 351:         for (p = buf; *p != '\n' && *p != '\0'; p++)
 352:         {
 353:             if (*p == ':')
 354:             {
 355:                 *p = 0;
 356:                 i++;
 357:                 field[i] = p + 1;
 358:             }
 359:         }
 360:         *p = '\0';
 361: 
 362:         /* check for correct number of fields */
 363:         if (i != UF_NFIELDS - 1)
 364:             syserr("initucode: users fmt %s", buf);
 365: 
 366:         /*
 367: 		**  Check to see if this entry is the override user.
 368: 		**  If so, save his user code in usr_ovrd.
 369: 		*/
 370: 
 371:         if (userflag != NULL && sequal(userflag, field[UF_NAME]))
 372:         {
 373:             smove(field[UF_UCODE], usr_ovrd);
 374:             userflag = NULL;
 375:         }
 376: 
 377:         /* don't bother with this shit if not needed */
 378:         if (Usercode != NULL)
 379:             continue;
 380: 
 381:         /*
 382: 		**  Build the user id of this entry into 'uid'
 383: 		**  and see if it is this user.
 384: 		*/
 385: 
 386:         uid = atoi(field[UF_UID]);
 387: 
 388: #		ifdef xB_UNIX
 389:         gid = atoi(field[UF_GID]);
 390:         uid = (uid & I1MASK) | ((gid & I1MASK) << 8);
 391: #		endif
 392: 
 393: #		ifdef xV6_UNIX
 394:         if ((uid & I1MASK) != actualuid)
 395:             continue;
 396: #		endif
 397: #		ifndef xV6_UNIX
 398:         if (uid != actualuid)
 399:             continue;
 400: #		endif
 401: 
 402:         /*
 403: 		**  We now have the real user entry.
 404: 		**	Fetch the usercode, the status bits, and other
 405: 		**	fields from the users file, and save them in
 406: 		**	a safe place (sbuf).
 407: 		*/
 408: 
 409:         Usercode = sbufp;
 410:         sbufp += smove(field[UF_UCODE], sbufp) + 1;
 411:         Status = oatoi(field[UF_STAT]);
 412:         if (paramlist != NULL)
 413:         {
 414:             for (i = 0; i < 4; i++)
 415:             {
 416:                 paramlist[i] = sbufp;
 417:                 sbufp += smove(field[UF_FLAGS + i], sbufp) + 1;
 418:             }
 419:         }
 420: 
 421:         /* validate access permission */
 422:         rtval = 0;
 423:         if (!dbflag || (Status & U_SUPER) != 0)
 424:             continue;
 425:         p = field[UF_DBLIST];
 426:         if (*p == 0)
 427:             continue;
 428: 
 429:         /* select permission/no-permission */
 430:         npermit = 0;
 431:         if (*p == '-')
 432:         {
 433:             p++;
 434:             npermit++;
 435:         }
 436: 
 437:         /* scan for database listed */
 438:         if (!npermit)
 439:             rtval = NOACCESS;
 440:         for (c = *p; c != 0; p = q + 1)
 441:         {
 442:             for (q = p; *q != ',' && *q != 0; q++)
 443:                 continue;
 444:             c = *q;
 445:             *q = 0;
 446:             if (sequal(Parmvect[0], p))
 447:             {
 448:                 rtval = npermit ? NOACCESS : 0;
 449:                 break;
 450:             }
 451:         }
 452:     }
 453:     fclose(iop);
 454: 
 455:     if (rtval != 0)
 456:         return (rtval);
 457: 
 458:     /*
 459: 	**  Check for existance of the database.  This is done by
 460: 	**	first building the pathname of the database into
 461: 	**	'Dbpath', and then reading the admin file (just
 462: 	**	the adhdr part).
 463: 	*/
 464: 
 465:     if (dbflag)
 466:     {
 467:         Dbpath = sbufp;
 468:         switch (i = initdbpath(Parmvect[0], Dbpath, TRUE))
 469:         {
 470:           case DBEXIST:
 471:             rtval = 0;
 472:             break;
 473: 
 474:           case PTR2DB:
 475:             rtval = INDIRECT;
 476:             break;
 477: 
 478:           case NODBS:
 479:             rtval = NODB;
 480:             break;
 481: 
 482:           case PTR2NODBS:
 483:             rtval = INDNODB;
 484:             break;
 485: 
 486:           default:
 487:             syserr("initucode: initdbpath %d", i);
 488:         }
 489:         sbufp += length(Dbpath) + 1;
 490: 
 491:         if (rtval == 0 || rtval == INDIRECT)
 492:         {
 493:             i = open(ztack(Dbpath, "/admin"), O_RDONLY);
 494:             if (i < 0)
 495:                 rtval += 1;
 496:             else
 497:             {
 498:                 /* open and check admin file */
 499:                 checkadmin(i);
 500:                 close(i);
 501:             }
 502:         }
 503:     }
 504: 
 505:     /*
 506: 	**  Check to see if the name on the -u flag is valid, and
 507: 	**	that this user is allowed to use it.
 508: 	*/
 509: 
 510:     if (userflag != NULL)
 511:     {
 512:         printf("Invalid user name %s\n", userflag);
 513:         exit(-1);
 514:     }
 515:     if (usr_ovrd[0] != '\0')
 516:     {
 517:         if ((Status & U_SUPER) == 0)
 518:         {
 519:             if (!dbflag || !bequal(Admin.adhdr.adowner, Usercode, UCODE_SZ))
 520:             {
 521:                 printf("You may not use the -u flag\n");
 522:                 exit(-1);
 523:             }
 524:         }
 525:         bmove(usr_ovrd, Usercode, UCODE_SZ);
 526:     }
 527: 
 528:     /*
 529: 	**  Process the +-w flag.
 530: 	**	First, determine the locking mode.  If +w, always
 531: 	**	wait; if -w, never wait; if unspecified, wait if in
 532: 	**	background, but print error and exit if running
 533: 	**	interactive.
 534: 	*/
 535: 
 536:     if (waitflag > 0 || (waitflag == 0 && gtty(0, &gttydummy) < 0))
 537:         Wait_action = A_SLP;
 538:     else
 539:         Wait_action = A_RTN;
 540:     if (dbflag && waitmode >= 0)
 541:         db_lock(waitmode);
 542: 
 543:     /*
 544: 	**  Return authorization value.
 545: 	*/
 546: 
 547:     return (rtval);
 548: }
 549: /*
 550: **  DB_LOCK -- lock database
 551: **
 552: **	Locks the database.  Everyone should do this before using any
 553: **	database.
 554: **
 555: **	Parameters:
 556: **		database -- the pathname of the database.
 557: **		mode -- M_EXCL -- get an exclusive lock.
 558: **			M_SHARE -- get a shared lock.
 559: **
 560: **	Returns:
 561: **		none
 562: **
 563: **	Side Effects:
 564: **		Alockdes is opened.
 565: */
 566: 
 567: struct lockreq  Lock;   /* the database lock structure */
 568: 
 569: db_lock(mode)
 570: int mode;
 571: {
 572:     if ((Admin.adhdr.adflags & A_DBCONCUR) == 0)
 573:         return;
 574:     if (Alockdes < 0)
 575:         Alockdes = start_up_lock_driver();
 576:     if (setdbl(Wait_action, mode) < 0)
 577:     {
 578:         printf("Database temporarily unavailable\n");
 579:         exit(1);
 580:     }
 581: }
 582: /*
 583: **  INITDBPATH -- initialize the pathname of the database
 584: **
 585: **	The pathname of a specified database is created.  Indirection
 586: **	via a file is supported, so that if the pathname is a file,
 587: **	the first line of the file is read and used as the pathname
 588: **	of the real database.
 589: **
 590: **	Parameters:
 591: **		database -- the name of the database.  If NULL,
 592: **			the pathname of datadir is returned.
 593: **		dbbuf -- a buffer into which the pathname should
 594: **			be dumped.
 595: **		follow -- if set, follow the indirect chain of
 596: **			database pathnames.
 597: **
 598: **	Returns:
 599: **		0(DBEXIST)-- database exists in datadir
 600: **		1(PTR2DB)-- database exists, but I followed a pointer.
 601: **		2(NODBS)-- database doesn't exist in datadir.
 602: **		3(PRT2NODBS)-- database doesn't exist, but I followed a pointer.
 603: **
 604: **	Side Effects:
 605: **		none.
 606: */
 607: 
 608: initdbpath(database, dbpath, follow)
 609: char    *database;
 610: char    *dbpath;
 611: int follow;
 612: {
 613:     struct stat ibuf;
 614:     register char   *d;
 615:     register FILE   *f;
 616:     register int    phase;
 617:     int     retval;
 618:     int     uid;
 619:     extern char *index();
 620: 
 621:     d = dbpath;
 622: 
 623:     if (database == NULL)
 624:     {
 625: # ifndef xDBPATH
 626:         concat(Pathname, "/data/base/", d);
 627: # else
 628:         smove(xDBPATH, d);
 629: # endif
 630:         return (DBEXIST);
 631:     }
 632: 
 633:     /* get the basic pathname */
 634:     concat(ztack(Pathname, "/datadir/"), database, d);
 635: 
 636:     /*
 637: 	** Iterate looking for database.
 638: 	**	"Phase" is what we are trying:
 639: 	**	   -1 -- looking in datadir
 640: 	**	    0 -- looking in data/base
 641: 	**	    1 -- following indirect.
 642: 	*/
 643: 
 644:     retval = NODBS;
 645:     for (phase = -1;;)
 646:     {
 647:         /* find out what sort of filesystem node this is */
 648:         if (stat(d, &ibuf) < 0)
 649:         {
 650:             if (phase < 0)
 651:             {
 652: # ifdef xDBPATH
 653:                 concat(xDBPATH, database, d);
 654: # else
 655:                 concat(ztack(Pathname, "/data/base/"), database, d);
 656: # endif
 657:                 phase = 0;
 658:                 continue;
 659:             }
 660:             else
 661:                 return (retval);
 662:         }
 663: 
 664:         /* set up the lock structure for future use */
 665:         bmove(&ibuf, Lock.dbnode, 4);
 666: 
 667:         retval -= 2;
 668:         if ((ibuf.st_mode & S_IFMT) == S_IFDIR)
 669:             return (retval);
 670: 
 671:         /* if second time through, the database must be a directory */
 672:         if (phase > 0)
 673:             syserr("initdbpath: not direc");
 674: 
 675:         /* if we shouldn't follow the chain, say it exists */
 676:         if (!follow)
 677:             return (PTR2NODBS);
 678: 
 679:         /* it's a file -- see if we can use it */
 680:         uid = ibuf.st_uid;
 681: #		ifdef xB_UNIX
 682:         uid = (uid & I1MASK) | ((ibuf.st_gid & I1MASK) << 8);
 683: #		endif
 684: #		ifdef xV6_UNIX
 685:         uid &= I1MASK;
 686: #		endif
 687:         if (uid != Ing_uid || (ibuf.st_mode & 0777) != 0600)
 688:             return (PTR2NODBS);
 689: 
 690:         f = fopen(d, "r");
 691:         if (f == NULL)
 692:             syserr("initdbpath: fopen");
 693: 
 694:         /* read the pathname of the database */
 695:         if (fgets(d, MAXLINE, f) == NULL || d[0] != '/')
 696:             syserr("initdbpath: bad indirect");
 697:         *index(d, '\n') = '\0';
 698:         fclose(f);
 699: 
 700:         /* prepare for next iteration */
 701:         retval = 3;
 702:         phase = 1;
 703:     }
 704: }

Defined functions

db_lock defined in line 569; used 1 times
initdbpath defined in line 608; used 1 times
initucode defined in line 156; never used

Defined variables

Dbpath defined in line 150; used 4 times
Flagvect defined in line 151; used 1 times
Ing_uid defined in line 153; used 5 times
Initbuf defined in line 154; used 1 times
Lock defined in line 567; used 1 times
Parmvect defined in line 152; used 3 times
Pathname defined in line 145; used 7 times
Rubignored defined in line 147; never used
Status defined in line 146; used 3 times
Usercode defined in line 144; used 5 times
Wait_action defined in line 149; used 3 times

Defined macros

MAXFARGS defined in line 141; used 2 times
MAXPARGS defined in line 142; used 2 times
Last modified: 1986-04-17
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1935
Valid CSS Valid XHTML 1.0 Strict