1148Seric # include <stdio.h> 2148Seric # include <sys/types.h> 3148Seric # include <sys/stat.h> 4261Seric # include <sys/dir.h> 5148Seric # include <sysexits.h> 6202Seric # include <whoami.h> 7148Seric 8*587Seric static char SccsId[] = "@(#)sccs.c 1.23.1.1 08/09/80"; 9155Seric 10157Seric # define bitset(bit, word) ((bit) & (word)) 11157Seric 12157Seric typedef char bool; 13200Seric # define TRUE 1 14200Seric # define FALSE 0 15157Seric 16148Seric struct sccsprog 17148Seric { 18148Seric char *sccsname; /* name of SCCS routine */ 19200Seric short sccsoper; /* opcode, see below */ 20200Seric short sccsflags; /* flags, see below */ 21148Seric char *sccspath; /* pathname of binary implementing */ 22148Seric }; 23148Seric 24200Seric /* values for sccsoper */ 25200Seric # define PROG 0 /* call a program */ 26201Seric # define CMACRO 1 /* command substitution macro */ 27226Seric # define FIX 2 /* fix a delta */ 28261Seric # define CLEAN 3 /* clean out recreatable files */ 29396Seric # define UNEDIT 4 /* unedit a file */ 30200Seric 31157Seric /* bits for sccsflags */ 32200Seric # define NO_SDOT 0001 /* no s. on front of args */ 33200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 34148Seric 35202Seric # ifdef CSVAX 36202Seric # define PROGPATH(name) "/usr/local/name" 37202Seric # endif CSVAX 38202Seric 39202Seric # ifndef PROGPATH 40202Seric # define PROGPATH(name) "/usr/sccs/name" 41202Seric # endif PROGPATH 42202Seric 43148Seric struct sccsprog SccsProg[] = 44148Seric { 45202Seric "admin", PROG, REALUSER, PROGPATH(admin), 46202Seric "chghist", PROG, 0, PROGPATH(rmdel), 47202Seric "comb", PROG, 0, PROGPATH(comb), 48202Seric "delta", PROG, 0, PROGPATH(delta), 49202Seric "get", PROG, 0, PROGPATH(get), 50202Seric "help", PROG, NO_SDOT, PROGPATH(help), 51202Seric "prt", PROG, 0, PROGPATH(prt), 52202Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 53202Seric "what", PROG, NO_SDOT, PROGPATH(what), 54393Seric "edit", CMACRO, 0, "get -e", 55393Seric "delget", CMACRO, 0, "delta/get", 56398Seric "deledit", CMACRO, 0, "delta/get -e", 57201Seric "del", CMACRO, 0, "delta/get", 58226Seric "delt", CMACRO, 0, "delta/get", 59226Seric "fix", FIX, 0, NULL, 60346Seric "clean", CLEAN, REALUSER, (char *) TRUE, 61346Seric "info", CLEAN, REALUSER, (char *) FALSE, 62396Seric "unedit", UNEDIT, 0, NULL, 63200Seric NULL, -1, 0, NULL 64148Seric }; 65148Seric 66396Seric struct pfile 67396Seric { 68396Seric char *p_osid; /* old SID */ 69396Seric char *p_nsid; /* new SID */ 70396Seric char *p_user; /* user who did edit */ 71396Seric char *p_date; /* date of get */ 72396Seric char *p_time; /* time of get */ 73396Seric }; 74396Seric 75157Seric char *SccsPath = "SCCS"; /* pathname of SCCS files */ 76157Seric bool RealUser; /* if set, running as real user */ 77393Seric # ifdef DEBUG 78393Seric bool Debug; /* turn on tracing */ 79393Seric # endif 80148Seric 81148Seric main(argc, argv) 82148Seric int argc; 83148Seric char **argv; 84148Seric { 85148Seric register char *p; 86262Seric extern struct sccsprog *lookup(); 87148Seric 88148Seric /* 89148Seric ** Detect and decode flags intended for this program. 90148Seric */ 91148Seric 92200Seric if (argc < 2) 93148Seric { 94200Seric fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); 95200Seric exit(EX_USAGE); 96200Seric } 97200Seric argv[argc] = NULL; 98200Seric 99262Seric if (lookup(argv[0]) == NULL) 100200Seric { 101262Seric while ((p = *++argv) != NULL) 102148Seric { 103262Seric if (*p != '-') 104262Seric break; 105262Seric switch (*++p) 106262Seric { 107262Seric case 'r': /* run as real user */ 108262Seric setuid(getuid()); 109262Seric RealUser++; 110262Seric break; 111148Seric 112262Seric case 'p': /* path of sccs files */ 113262Seric SccsPath = ++p; 114262Seric break; 115148Seric 116393Seric # ifdef DEBUG 117393Seric case 'T': /* trace */ 118393Seric Debug++; 119393Seric break; 120393Seric # endif 121393Seric 122262Seric default: 123262Seric fprintf(stderr, "Sccs: unknown option -%s\n", p); 124262Seric break; 125262Seric } 126148Seric } 127262Seric if (SccsPath[0] == '\0') 128262Seric SccsPath = "."; 129148Seric } 130148Seric 131201Seric command(argv, FALSE); 132200Seric exit(EX_OK); 133200Seric } 134157Seric 135201Seric command(argv, forkflag) 136200Seric char **argv; 137201Seric bool forkflag; 138200Seric { 139200Seric register struct sccsprog *cmd; 140200Seric register char *p; 141201Seric register char *q; 142201Seric char buf[40]; 143262Seric extern struct sccsprog *lookup(); 144585Seric char *nav[200]; 145393Seric char **avp; 146585Seric register int i; 147585Seric extern bool unedit(); 148200Seric 149393Seric # ifdef DEBUG 150393Seric if (Debug) 151393Seric { 152393Seric printf("command:\n"); 153393Seric for (avp = argv; *avp != NULL; avp++) 154393Seric printf(" \"%s\"\n", *avp); 155393Seric } 156393Seric # endif 157393Seric 158157Seric /* 159148Seric ** Look up command. 160200Seric ** At this point, argv points to the command name. 161148Seric */ 162148Seric 163396Seric cmd = lookup(argv[0]); 164262Seric if (cmd == NULL) 165148Seric { 166396Seric fprintf(stderr, "Sccs: Unknown command \"%s\"\n", argv[0]); 167148Seric exit(EX_USAGE); 168148Seric } 169148Seric 170148Seric /* 171200Seric ** Interpret operation associated with this command. 172157Seric */ 173157Seric 174200Seric switch (cmd->sccsoper) 175200Seric { 176200Seric case PROG: /* call an sccs prog */ 177201Seric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 178201Seric break; 179201Seric 180201Seric case CMACRO: /* command macro */ 181201Seric for (p = cmd->sccspath; *p != '\0'; p++) 182201Seric { 183393Seric avp = nav; 184393Seric *avp++ = buf; 185201Seric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 186393Seric { 187393Seric if (*p == ' ') 188393Seric { 189393Seric *q = '\0'; 190393Seric *avp++ = &q[1]; 191393Seric } 192393Seric else 193393Seric *q = *p; 194393Seric } 195201Seric *q = '\0'; 196393Seric *avp = NULL; 197393Seric xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2], 198393Seric nav[3], nav[4], nav[5], nav[6]); 199201Seric } 200201Seric fprintf(stderr, "Sccs internal error: CMACRO\n"); 201200Seric exit(EX_SOFTWARE); 202157Seric 203226Seric case FIX: /* fix a delta */ 204568Seric if (strncmp(argv[1], "-r", 2) != 0) 205226Seric { 206226Seric fprintf(stderr, "Sccs: -r flag needed for fix command\n"); 207226Seric break; 208226Seric } 209226Seric xcommand(&argv[1], TRUE, "get", "-k", NULL); 210226Seric xcommand(&argv[1], TRUE, "rmdel", NULL); 211226Seric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 212226Seric fprintf(stderr, "Sccs internal error: FIX\n"); 213226Seric exit(EX_SOFTWARE); 214226Seric 215261Seric case CLEAN: 216346Seric clean((bool) cmd->sccspath); 217261Seric break; 218261Seric 219396Seric case UNEDIT: 220585Seric i = 0; 221396Seric for (avp = &argv[1]; *avp != NULL; avp++) 222585Seric { 223585Seric if (unedit(*avp)) 224585Seric nav[i++] = *avp; 225585Seric } 226585Seric nav[i] = NULL; 227585Seric if (i > 0) 228585Seric xcommand(nav, FALSE, "get", NULL); 229396Seric break; 230396Seric 231200Seric default: 232200Seric fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); 233200Seric exit(EX_SOFTWARE); 234200Seric } 235200Seric } 236262Seric /* 237262Seric ** LOOKUP -- look up an SCCS command name. 238262Seric ** 239262Seric ** Parameters: 240262Seric ** name -- the name of the command to look up. 241262Seric ** 242262Seric ** Returns: 243262Seric ** ptr to command descriptor for this command. 244262Seric ** NULL if no such entry. 245262Seric ** 246262Seric ** Side Effects: 247262Seric ** none. 248262Seric */ 249200Seric 250262Seric struct sccsprog * 251262Seric lookup(name) 252262Seric char *name; 253262Seric { 254262Seric register struct sccsprog *cmd; 255226Seric 256262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 257262Seric { 258262Seric if (strcmp(cmd->sccsname, name) == 0) 259262Seric return (cmd); 260262Seric } 261262Seric return (NULL); 262262Seric } 263262Seric 264262Seric 265226Seric xcommand(argv, forkflag, arg0) 266226Seric char **argv; 267226Seric bool forkflag; 268226Seric char *arg0; 269226Seric { 270226Seric register char **av; 271226Seric char *newargv[1000]; 272226Seric register char **np; 273226Seric 274226Seric np = newargv; 275226Seric for (av = &arg0; *av != NULL; av++) 276226Seric *np++ = *av; 277226Seric for (av = argv; *av != NULL; av++) 278226Seric *np++ = *av; 279226Seric *np = NULL; 280226Seric command(newargv, forkflag); 281226Seric } 282226Seric 283200Seric callprog(progpath, flags, argv, forkflag) 284200Seric char *progpath; 285200Seric short flags; 286200Seric char **argv; 287200Seric bool forkflag; 288200Seric { 289200Seric register char *p; 290200Seric register char **av; 291200Seric extern char *makefile(); 292200Seric register int i; 293201Seric auto int st; 294586Seric register char **nav; 295200Seric 296200Seric if (*argv == NULL) 297200Seric return (-1); 298200Seric 299157Seric /* 300226Seric ** Fork if appropriate. 301148Seric */ 302148Seric 303200Seric if (forkflag) 304200Seric { 305393Seric # ifdef DEBUG 306393Seric if (Debug) 307393Seric printf("Forking\n"); 308393Seric # endif 309200Seric i = fork(); 310200Seric if (i < 0) 311200Seric { 312200Seric fprintf(stderr, "Sccs: cannot fork"); 313200Seric exit(EX_OSERR); 314200Seric } 315200Seric else if (i > 0) 316201Seric { 317201Seric wait(&st); 318201Seric return (st); 319201Seric } 320200Seric } 321200Seric 322200Seric /* 323226Seric ** Build new argument vector. 324226Seric */ 325226Seric 326226Seric /* copy program filename arguments and flags */ 327586Seric nav = &argv[1]; 328226Seric av = argv; 329226Seric while ((p = *++av) != NULL) 330226Seric { 331226Seric if (!bitset(NO_SDOT, flags) && *p != '-') 332586Seric *nav = makefile(p); 333586Seric else 334586Seric *nav = p; 335586Seric if (*nav != NULL) 336586Seric nav++; 337226Seric } 338586Seric *nav = NULL; 339226Seric 340226Seric /* 341200Seric ** Set protection as appropriate. 342200Seric */ 343200Seric 344200Seric if (bitset(REALUSER, flags)) 345200Seric setuid(getuid()); 346226Seric 347200Seric /* 348226Seric ** Call real SCCS program. 349200Seric */ 350200Seric 351226Seric execv(progpath, argv); 352148Seric fprintf(stderr, "Sccs: cannot execute "); 353200Seric perror(progpath); 354148Seric exit(EX_UNAVAILABLE); 355148Seric } 356586Seric /* 357586Seric ** MAKEFILE -- make filename of SCCS file 358586Seric ** 359586Seric ** If the name passed is already the name of an SCCS file, 360586Seric ** just return it. Otherwise, munge the name into the name 361586Seric ** of the actual SCCS file. 362586Seric ** 363586Seric ** There are cases when it is not clear what you want to 364586Seric ** do. For example, if SccsPath is an absolute pathname 365586Seric ** and the name given is also an absolute pathname, we go 366586Seric ** for SccsPath (& only use the last component of the name 367586Seric ** passed) -- this is important for security reasons (if 368586Seric ** sccs is being used as a setuid front end), but not 369586Seric ** particularly intuitive. 370586Seric ** 371586Seric ** Parameters: 372586Seric ** name -- the file name to be munged. 373586Seric ** 374586Seric ** Returns: 375586Seric ** The pathname of the sccs file. 376586Seric ** NULL on error. 377586Seric ** 378586Seric ** Side Effects: 379586Seric ** none. 380586Seric */ 381148Seric 382148Seric char * 383148Seric makefile(name) 384148Seric char *name; 385148Seric { 386148Seric register char *p; 387148Seric register char c; 388148Seric char buf[512]; 389148Seric extern char *malloc(); 390586Seric extern char *rindex(); 391*587Seric extern bool isdir(); 392*587Seric register char *q; 393148Seric 394586Seric p = rindex(name, '/'); 395586Seric if (p == NULL) 396586Seric p = name; 397586Seric else 398586Seric p++; 399586Seric 400148Seric /* 401*587Seric ** See if the name can be used as-is. 402148Seric */ 403148Seric 404*587Seric if (SccsPath[0] != '/' || name[0] == '/' || strncmp(name, "./", 2) == 0) 405*587Seric { 406*587Seric if (strncmp(p, "s.", 2) == 0) 407*587Seric return (name); 408*587Seric if (isdir(name)) 409*587Seric return (name); 410*587Seric } 411586Seric 412586Seric /* 413*587Seric ** Create the actual pathname. 414586Seric */ 415586Seric 416*587Seric if (name[0] != '/') 417148Seric { 418586Seric strcpy(buf, SccsPath); 419586Seric strcat(buf, "/"); 420586Seric } 421586Seric else 422586Seric strcpy(buf, ""); 423*587Seric strncat(buf, name, p - name); 424*587Seric q = &buf[strlen(buf)]; 425*587Seric strcpy(q, p); 426*587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 427586Seric { 428*587Seric strcpy(q, ""); 429*587Seric if (SccsPath[0] != '/' && name[0] == '/') 430586Seric { 431586Seric strcat(buf, SccsPath); 432586Seric strcat(buf, "/"); 433586Seric } 434586Seric strcat(buf, "s."); 435586Seric strcat(buf, p); 436586Seric } 437148Seric 438148Seric p = malloc(strlen(buf) + 1); 439148Seric if (p == NULL) 440148Seric { 441148Seric perror("Sccs: no mem"); 442148Seric exit(EX_OSERR); 443148Seric } 444148Seric strcpy(p, buf); 445148Seric return (p); 446148Seric } 447261Seric /* 448*587Seric ** ISDIR -- return true if the argument is a directory. 449*587Seric ** 450*587Seric ** Parameters: 451*587Seric ** name -- the pathname of the file to check. 452*587Seric ** 453*587Seric ** Returns: 454*587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 455*587Seric ** 456*587Seric ** Side Effects: 457*587Seric ** none. 458*587Seric */ 459*587Seric 460*587Seric bool 461*587Seric isdir(name) 462*587Seric char *name; 463*587Seric { 464*587Seric struct stat stbuf; 465*587Seric 466*587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 467*587Seric } 468*587Seric /* 469586Seric ** SAFEPATH -- determine whether a pathname is "safe" 470586Seric ** 471586Seric ** "Safe" pathnames only allow you to get deeper into the 472586Seric ** directory structure, i.e., full pathnames and ".." are 473586Seric ** not allowed. 474586Seric ** 475586Seric ** Parameters: 476586Seric ** p -- the name to check. 477586Seric ** 478586Seric ** Returns: 479586Seric ** TRUE -- if the path is safe. 480586Seric ** FALSE -- if the path is not safe. 481586Seric ** 482586Seric ** Side Effects: 483586Seric ** Prints a message if the path is not safe. 484586Seric */ 485586Seric 486586Seric bool 487586Seric safepath(p) 488586Seric register char *p; 489586Seric { 490586Seric extern char *index(); 491586Seric 492586Seric if (*p != '/') 493586Seric { 494586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 495586Seric { 496586Seric p = index(p, '/'); 497586Seric if (p == NULL) 498586Seric return (TRUE); 499586Seric p++; 500586Seric } 501586Seric } 502586Seric 503586Seric printf("You may not use full pathnames or \"..\"\n"); 504586Seric return (FALSE); 505586Seric } 506586Seric /* 507261Seric ** CLEAN -- clean out recreatable files 508261Seric ** 509261Seric ** Any file for which an "s." file exists but no "p." file 510261Seric ** exists in the current directory is purged. 511261Seric ** 512261Seric ** Parameters: 513346Seric ** really -- if TRUE, remove everything. 514346Seric ** else, just report status. 515261Seric ** 516261Seric ** Returns: 517261Seric ** none. 518261Seric ** 519261Seric ** Side Effects: 520261Seric ** removes files in the current directory. 521261Seric */ 522261Seric 523346Seric clean(really) 524346Seric bool really; 525261Seric { 526261Seric struct direct dir; 527261Seric struct stat stbuf; 528261Seric char buf[100]; 529394Seric char pline[120]; 530346Seric register FILE *dirfd; 531346Seric register char *basefile; 532351Seric bool gotedit; 533394Seric FILE *pfp; 534261Seric 535261Seric dirfd = fopen(SccsPath, "r"); 536261Seric if (dirfd == NULL) 537261Seric { 538261Seric fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); 539261Seric return; 540261Seric } 541261Seric 542261Seric /* 543261Seric ** Scan the SCCS directory looking for s. files. 544261Seric */ 545261Seric 546351Seric gotedit = FALSE; 547261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 548261Seric { 549568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 550261Seric continue; 551261Seric 552261Seric /* got an s. file -- see if the p. file exists */ 553261Seric strcpy(buf, SccsPath); 554261Seric strcat(buf, "/p."); 555346Seric basefile = &buf[strlen(buf)]; 556568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 557346Seric basefile[sizeof dir.d_name - 2] = '\0'; 558394Seric pfp = fopen(buf, "r"); 559394Seric if (pfp != NULL) 560346Seric { 561394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 562416Seric printf("%12s: being edited: %s", basefile, pline); 563394Seric fclose(pfp); 564351Seric gotedit = TRUE; 565261Seric continue; 566346Seric } 567261Seric 568261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 569346Seric if (really) 570346Seric { 571568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 572346Seric buf[sizeof dir.d_name - 2] = '\0'; 573346Seric unlink(buf); 574346Seric } 575261Seric } 576261Seric 577261Seric fclose(dirfd); 578351Seric if (!gotedit && !really) 579416Seric printf("Nothing being edited\n"); 580261Seric } 581396Seric /* 582396Seric ** UNEDIT -- unedit a file 583396Seric ** 584396Seric ** Checks to see that the current user is actually editting 585396Seric ** the file and arranges that s/he is not editting it. 586396Seric ** 587396Seric ** Parameters: 588416Seric ** fn -- the name of the file to be unedited. 589396Seric ** 590396Seric ** Returns: 591585Seric ** TRUE -- if the file was successfully unedited. 592585Seric ** FALSE -- if the file was not unedited for some 593585Seric ** reason. 594396Seric ** 595396Seric ** Side Effects: 596396Seric ** fn is removed 597396Seric ** entries are removed from pfile. 598396Seric */ 599396Seric 600585Seric bool 601396Seric unedit(fn) 602396Seric char *fn; 603396Seric { 604396Seric register FILE *pfp; 605396Seric char *pfn; 606396Seric static char tfn[] = "/tmp/sccsXXXXX"; 607396Seric FILE *tfp; 608396Seric register char *p; 609396Seric register char *q; 610396Seric bool delete = FALSE; 611396Seric bool others = FALSE; 612396Seric char *myname; 613396Seric extern char *getlogin(); 614396Seric struct pfile *pent; 615396Seric extern struct pfile *getpfile(); 616396Seric char buf[120]; 617396Seric 618396Seric /* make "s." filename & find the trailing component */ 619396Seric pfn = makefile(fn); 620586Seric if (pfn == NULL) 621586Seric return (FALSE); 622586Seric q = rindex(pfn, '/'); 623586Seric if (q == NULL) 624586Seric q = &pfn[-1]; 625586Seric if (q[1] != 's' || q[2] != '.') 626396Seric { 627396Seric fprintf(stderr, "Sccs: bad file name \"%s\"\n", fn); 628585Seric return (FALSE); 629396Seric } 630396Seric 631396Seric /* turn "s." into "p." */ 632396Seric *++q = 'p'; 633396Seric 634396Seric pfp = fopen(pfn, "r"); 635396Seric if (pfp == NULL) 636396Seric { 637416Seric printf("%12s: not being edited\n", fn); 638585Seric return (FALSE); 639396Seric } 640396Seric 641396Seric /* 642396Seric ** Copy p-file to temp file, doing deletions as needed. 643396Seric */ 644396Seric 645396Seric mktemp(tfn); 646396Seric tfp = fopen(tfn, "w"); 647396Seric if (tfp == NULL) 648396Seric { 649396Seric fprintf(stderr, "Sccs: cannot create \"%s\"\n", tfn); 650396Seric exit(EX_OSERR); 651396Seric } 652396Seric 653396Seric myname = getlogin(); 654396Seric while ((pent = getpfile(pfp)) != NULL) 655396Seric { 656396Seric if (strcmp(pent->p_user, myname) == 0) 657396Seric { 658396Seric /* a match */ 659396Seric delete++; 660396Seric } 661396Seric else 662396Seric { 663396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 664396Seric pent->p_nsid, pent->p_user, pent->p_date, 665396Seric pent->p_time); 666396Seric others++; 667396Seric } 668396Seric } 669396Seric 670396Seric /* do final cleanup */ 671396Seric if (others) 672396Seric { 673396Seric if (freopen(tfn, "r", tfp) == NULL) 674396Seric { 675396Seric fprintf(stderr, "Sccs: cannot reopen \"%s\"\n", tfn); 676396Seric exit(EX_OSERR); 677396Seric } 678396Seric if (freopen(pfn, "w", pfp) == NULL) 679396Seric { 680396Seric fprintf(stderr, "Sccs: cannot create \"%s\"\n", pfn); 681585Seric return (FALSE); 682396Seric } 683396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 684396Seric fputs(buf, pfp); 685396Seric } 686396Seric else 687396Seric { 688396Seric unlink(pfn); 689396Seric } 690396Seric fclose(tfp); 691396Seric fclose(pfp); 692396Seric unlink(tfn); 693396Seric 694396Seric if (delete) 695396Seric { 696396Seric unlink(fn); 697396Seric printf("%12s: removed\n", fn); 698585Seric return (TRUE); 699396Seric } 700396Seric else 701396Seric { 702416Seric printf("%12s: not being edited by you\n", fn); 703585Seric return (FALSE); 704396Seric } 705396Seric } 706396Seric /* 707396Seric ** GETPFILE -- get an entry from the p-file 708396Seric ** 709396Seric ** Parameters: 710396Seric ** pfp -- p-file file pointer 711396Seric ** 712396Seric ** Returns: 713396Seric ** pointer to p-file struct for next entry 714396Seric ** NULL on EOF or error 715396Seric ** 716396Seric ** Side Effects: 717396Seric ** Each call wipes out results of previous call. 718396Seric */ 719396Seric 720396Seric struct pfile * 721396Seric getpfile(pfp) 722396Seric FILE *pfp; 723396Seric { 724396Seric static struct pfile ent; 725396Seric static char buf[120]; 726396Seric register char *p; 727396Seric extern char *nextfield(); 728396Seric 729396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 730396Seric return (NULL); 731396Seric 732396Seric ent.p_osid = p = buf; 733396Seric ent.p_nsid = p = nextfield(p); 734396Seric ent.p_user = p = nextfield(p); 735396Seric ent.p_date = p = nextfield(p); 736396Seric ent.p_time = p = nextfield(p); 737396Seric if (p == NULL || nextfield(p) != NULL) 738396Seric return (NULL); 739396Seric 740396Seric return (&ent); 741396Seric } 742396Seric 743396Seric 744396Seric char * 745396Seric nextfield(p) 746396Seric register char *p; 747396Seric { 748396Seric if (p == NULL || *p == '\0') 749396Seric return (NULL); 750396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 751396Seric p++; 752396Seric if (*p == '\n' || *p == '\0') 753396Seric { 754396Seric *p = '\0'; 755396Seric return (NULL); 756396Seric } 757396Seric *p++ = '\0'; 758396Seric return (p); 759396Seric } 760