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 8828Seric /* 9828Seric ** SCCS.C -- human-oriented front end to the SCCS system. 10828Seric ** 11828Seric ** Without trying to add any functionality to speak of, this 12828Seric ** program tries to make SCCS a little more accessible to human 13828Seric ** types. The main thing it does is automatically put the 14828Seric ** string "SCCS/s." on the front of names. Also, it has a 15828Seric ** couple of things that are designed to shorten frequent 16828Seric ** combinations, e.g., "delget" which expands to a "delta" 17828Seric ** and a "get". 18828Seric ** 19828Seric ** This program can also function as a setuid front end. 20828Seric ** To do this, you should copy the source, renaming it to 21828Seric ** whatever you want, e.g., "syssccs". Change any defaults 22828Seric ** in the program (e.g., syssccs might default -d to 23828Seric ** "/usr/src/sys"). Then recompile and put the result 24828Seric ** as setuid to whomever you want. In this mode, sccs 25828Seric ** knows to not run setuid for certain programs in order 26828Seric ** to preserve security, and so forth. 27828Seric ** 28828Seric ** Usage: 29828Seric ** sccs [flags] command [args] 30828Seric ** 31828Seric ** Flags: 32828Seric ** -d<dir> <dir> represents a directory to search 33828Seric ** out of. It should be a full pathname 34828Seric ** for general usage. E.g., if <dir> is 35828Seric ** "/usr/src/sys", then a reference to the 36828Seric ** file "dev/bio.c" becomes a reference to 37828Seric ** "/usr/src/sys/dev/bio.c". 38828Seric ** -p<path> prepends <path> to the final component 39828Seric ** of the pathname. By default, this is 40828Seric ** "SCCS". For example, in the -d example 41828Seric ** above, the path then gets modified to 42828Seric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 43828Seric ** more common usage (without the -d flag), 44828Seric ** "prog.c" would get modified to 45828Seric ** "SCCS/s.prog.c". In both cases, the 46828Seric ** "s." gets automatically prepended. 47828Seric ** -r run as the real user. 48828Seric ** 49828Seric ** Commands: 50828Seric ** admin, 51828Seric ** get, 52828Seric ** delta, 53828Seric ** rmdel, 54828Seric ** chghist, 55828Seric ** etc. Straight out of SCCS; only difference 56828Seric ** is that pathnames get modified as 57828Seric ** described above. 58828Seric ** edit Macro for "get -e". 59828Seric ** unedit Removes a file being edited, knowing 60828Seric ** about p-files, etc. 61828Seric ** delget Macro for "delta" followed by "get". 62828Seric ** deledit Macro for "delta" followed by "get -e". 63828Seric ** info Tell what files being edited. 64828Seric ** clean Remove all files that can be 65828Seric ** regenerated from SCCS files. 661205Seric ** check Like info, but return exit status, for 67828Seric ** use in makefiles. 68828Seric ** fix Remove a top delta & reedit, but save 69828Seric ** the previous changes in that delta. 70828Seric ** 71828Seric ** Compilation Flags: 72828Seric ** UIDUSER -- determine who the user is by looking at the 73828Seric ** uid rather than the login name -- for machines 74828Seric ** where SCCS gets the user in this way. 751270Seric ** SCCSDIR -- if defined, forces the -d flag to take on 761205Seric ** this value. This is so that the setuid 771205Seric ** aspects of this program cannot be abused. 781270Seric ** This flag also disables the -p flag. 791270Seric ** SCCSPATH -- the default for the -p flag. 80828Seric ** 81828Seric ** Compilation Instructions: 82828Seric ** cc -O -n -s sccs.c 83828Seric ** 84828Seric ** Author: 85828Seric ** Eric Allman, UCB/INGRES 861270Seric ** Copyright 1980 Regents of the University of California 87828Seric */ 88155Seric 891270Seric /******************* Configuration Information ********************/ 901270Seric 91828Seric # ifdef CSVAX 92828Seric # define UIDUSER 931270Seric # define PROGPATH(name) "/usr/local/name" 941270Seric # endif CSVAX 95828Seric 961270Seric # define SCCSPATH "SCCS" 971270Seric /* put #define SCCSDIR here */ 98828Seric 991270Seric char MyName[] = "sccs"; /* name used in messages */ 1001270Seric 1011270Seric /**************** End of Configuration Information ****************/ 1021270Seric 103*1431Seric static char SccsId[] = "@(#)sccs.c 1.34 10/15/80"; 1041270Seric 105157Seric # define bitset(bit, word) ((bit) & (word)) 106157Seric 107157Seric typedef char bool; 108200Seric # define TRUE 1 109200Seric # define FALSE 0 110157Seric 111828Seric # ifdef UIDUSER 112828Seric # include <pwd.h> 113828Seric # endif UIDUSER 114828Seric 115148Seric struct sccsprog 116148Seric { 117148Seric char *sccsname; /* name of SCCS routine */ 118200Seric short sccsoper; /* opcode, see below */ 119200Seric short sccsflags; /* flags, see below */ 1201316Seric char *sccsklets; /* valid key-letters on macros */ 121148Seric char *sccspath; /* pathname of binary implementing */ 122148Seric }; 123148Seric 124200Seric /* values for sccsoper */ 125200Seric # define PROG 0 /* call a program */ 126201Seric # define CMACRO 1 /* command substitution macro */ 127226Seric # define FIX 2 /* fix a delta */ 128261Seric # define CLEAN 3 /* clean out recreatable files */ 129396Seric # define UNEDIT 4 /* unedit a file */ 130*1431Seric # define SHELL 5 /* call a shell file (like PROG) */ 131200Seric 132157Seric /* bits for sccsflags */ 133200Seric # define NO_SDOT 0001 /* no s. on front of args */ 134200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 135148Seric 136819Seric /* modes for the "clean", "info", "check" ops */ 137819Seric # define CLEANC 0 /* clean command */ 138819Seric # define INFOC 1 /* info command */ 139819Seric # define CHECKC 2 /* check command */ 140819Seric 141202Seric # ifndef PROGPATH 142202Seric # define PROGPATH(name) "/usr/sccs/name" 143202Seric # endif PROGPATH 144202Seric 145148Seric struct sccsprog SccsProg[] = 146148Seric { 1471316Seric "admin", PROG, REALUSER, "", PROGPATH(admin), 1481316Seric "chghist", PROG, 0, "", PROGPATH(rmdel), 1491316Seric "comb", PROG, 0, "", PROGPATH(comb), 1501316Seric "delta", PROG, 0, "mysrp", PROGPATH(delta), 1511317Seric "get", PROG, 0, "ixbeskcl", PROGPATH(get), 1521316Seric "help", PROG, NO_SDOT, "", PROGPATH(help), 1531316Seric "prt", PROG, 0, "", PROGPATH(prt), 1541316Seric "rmdel", PROG, REALUSER, "", PROGPATH(rmdel), 1551316Seric "what", PROG, NO_SDOT, "", PROGPATH(what), 156*1431Seric "sccsdiff", SHELL, REALUSER, "", PROGPATH(sccsdiff), 1571317Seric "edit", CMACRO, NO_SDOT, "ixbscl", "get -e", 1581316Seric "delget", CMACRO, NO_SDOT, "", "delta/get -t", 1591316Seric "deledit", CMACRO, NO_SDOT, "", "delta/get -e -t", 1601316Seric "fix", FIX, NO_SDOT, "", NULL, 1611316Seric "clean", CLEAN, REALUSER, "", (char *) CLEANC, 1621316Seric "info", CLEAN, REALUSER, "", (char *) INFOC, 1631316Seric "check", CLEAN, REALUSER, "", (char *) CHECKC, 1641316Seric "unedit", UNEDIT, NO_SDOT, "", NULL, 1651316Seric NULL, -1, 0, "", NULL 166148Seric }; 167148Seric 168396Seric struct pfile 169396Seric { 170396Seric char *p_osid; /* old SID */ 171396Seric char *p_nsid; /* new SID */ 172396Seric char *p_user; /* user who did edit */ 173396Seric char *p_date; /* date of get */ 174396Seric char *p_time; /* time of get */ 175396Seric }; 176396Seric 1771270Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 1781270Seric # ifdef SCCSDIR 1791270Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 1801205Seric # else 1811270Seric char *SccsDir = ""; 1821205Seric # endif 183157Seric bool RealUser; /* if set, running as real user */ 184393Seric # ifdef DEBUG 185393Seric bool Debug; /* turn on tracing */ 186393Seric # endif 187148Seric 188148Seric main(argc, argv) 189148Seric int argc; 190148Seric char **argv; 191148Seric { 192148Seric register char *p; 193262Seric extern struct sccsprog *lookup(); 1941282Seric register int i; 195148Seric 196148Seric /* 197148Seric ** Detect and decode flags intended for this program. 198148Seric */ 199148Seric 200200Seric if (argc < 2) 201148Seric { 2021205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 203200Seric exit(EX_USAGE); 204200Seric } 205200Seric argv[argc] = NULL; 206200Seric 207262Seric if (lookup(argv[0]) == NULL) 208200Seric { 209262Seric while ((p = *++argv) != NULL) 210148Seric { 211262Seric if (*p != '-') 212262Seric break; 213262Seric switch (*++p) 214262Seric { 215262Seric case 'r': /* run as real user */ 216262Seric setuid(getuid()); 217262Seric RealUser++; 218262Seric break; 219148Seric 2201270Seric # ifndef SCCSDIR 221262Seric case 'p': /* path of sccs files */ 222262Seric SccsPath = ++p; 223262Seric break; 224148Seric 225588Seric case 'd': /* directory to search from */ 226588Seric SccsDir = ++p; 227588Seric break; 2281205Seric # endif 229588Seric 230393Seric # ifdef DEBUG 231393Seric case 'T': /* trace */ 232393Seric Debug++; 233393Seric break; 234393Seric # endif 235393Seric 236262Seric default: 2371205Seric usrerr("unknown option -%s", p); 238262Seric break; 239262Seric } 240148Seric } 241262Seric if (SccsPath[0] == '\0') 242262Seric SccsPath = "."; 243148Seric } 244148Seric 2451316Seric i = command(argv, FALSE, FALSE, ""); 2461282Seric exit(i); 247200Seric } 2481282Seric /* 2491282Seric ** COMMAND -- look up and perform a command 2501282Seric ** 2511282Seric ** This routine is the guts of this program. Given an 2521282Seric ** argument vector, it looks up the "command" (argv[0]) 2531282Seric ** in the configuration table and does the necessary stuff. 2541282Seric ** 2551282Seric ** Parameters: 2561282Seric ** argv -- an argument vector to process. 2571282Seric ** forkflag -- if set, fork before executing the command. 2581316Seric ** editflag -- if set, only include flags listed in the 2591316Seric ** sccsklets field of the command descriptor. 2601316Seric ** arg0 -- a space-seperated list of arguments to insert 2611316Seric ** before argv. 2621282Seric ** 2631282Seric ** Returns: 2641282Seric ** zero -- command executed ok. 2651282Seric ** else -- error status. 2661282Seric ** 2671282Seric ** Side Effects: 2681282Seric ** none. 2691282Seric */ 270157Seric 2711316Seric command(argv, forkflag, editflag, arg0) 272200Seric char **argv; 273201Seric bool forkflag; 2741316Seric bool editflag; 2751316Seric char *arg0; 276200Seric { 277200Seric register struct sccsprog *cmd; 278200Seric register char *p; 279201Seric char buf[40]; 280262Seric extern struct sccsprog *lookup(); 2811316Seric char *nav[1000]; 2821316Seric char **np; 283*1431Seric register char **ap; 284585Seric register int i; 285*1431Seric register char *q; 286585Seric extern bool unedit(); 2871282Seric int rval = 0; 2881316Seric extern char *index(); 2891316Seric extern char *makefile(); 290200Seric 291393Seric # ifdef DEBUG 292393Seric if (Debug) 293393Seric { 2941316Seric printf("command:\n\t\"%s\"\n", arg0); 2951316Seric for (np = argv; *np != NULL; np++) 2961316Seric printf("\t\"%s\"\n", *np); 297393Seric } 298393Seric # endif 299393Seric 300157Seric /* 3011316Seric ** Copy arguments. 3021316Seric ** Phase one -- from arg0 & if necessary argv[0]. 3031316Seric */ 3041316Seric 305*1431Seric np = ap = &nav[1]; 3061316Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 3071316Seric { 3081316Seric *np++ = q; 3091316Seric while (*p == ' ') 3101316Seric p++; 3111316Seric while (*p != ' ' && *p != '\0' && *p != '/') 3121316Seric *q++ = *p++; 3131316Seric *q++ = '\0'; 3141316Seric } 3151316Seric *np = NULL; 316*1431Seric if (*ap == NULL) 3171316Seric *np++ = *argv++; 3181316Seric 3191316Seric /* 320148Seric ** Look up command. 321*1431Seric ** At this point, *ap is the command name. 322148Seric */ 323148Seric 324*1431Seric cmd = lookup(*ap); 325262Seric if (cmd == NULL) 326148Seric { 327*1431Seric usrerr("Unknown command \"%s\"", *ap); 3281282Seric return (EX_USAGE); 329148Seric } 330148Seric 331148Seric /* 3321316Seric ** Copy remaining arguments doing editing as appropriate. 3331316Seric */ 3341316Seric 3351316Seric for (; *argv != NULL; argv++) 3361316Seric { 3371316Seric p = *argv; 3381316Seric if (*p == '-') 3391316Seric { 3401316Seric if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || 3411316Seric index(cmd->sccsklets, p[1]) != NULL) 3421316Seric *np++ = p; 3431316Seric } 3441316Seric else 3451316Seric { 3461316Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 3471316Seric p = makefile(p); 3481316Seric if (p != NULL) 3491316Seric *np++ = p; 3501316Seric } 3511316Seric } 3521316Seric *np = NULL; 3531316Seric 3541316Seric /* 355200Seric ** Interpret operation associated with this command. 356157Seric */ 357157Seric 358200Seric switch (cmd->sccsoper) 359200Seric { 360*1431Seric case SHELL: /* call a shell file */ 361*1431Seric *ap = cmd->sccspath; 362*1431Seric *--ap = "sh"; 363*1431Seric rval = callprog("/bin/sh", cmd->sccsflags, ap, forkflag); 364*1431Seric break; 365*1431Seric 366200Seric case PROG: /* call an sccs prog */ 367*1431Seric rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); 368201Seric break; 369201Seric 370201Seric case CMACRO: /* command macro */ 371201Seric for (p = cmd->sccspath; *p != '\0'; p++) 372201Seric { 3731316Seric q = p; 3741316Seric while (*p != '\0' && *p != '/') 3751316Seric p++; 376*1431Seric rval = command(&ap[1], *p != '\0', TRUE, q); 3771282Seric if (rval != 0) 3781282Seric break; 379201Seric } 3801282Seric break; 381157Seric 382226Seric case FIX: /* fix a delta */ 383*1431Seric if (strncmp(ap[1], "-r", 2) != 0) 384226Seric { 3851205Seric usrerr("-r flag needed for fix command"); 3861282Seric rval = EX_USAGE; 387226Seric break; 388226Seric } 389*1431Seric rval = command(&ap[1], TRUE, TRUE, "get -k"); 3901282Seric if (rval == 0) 391*1431Seric rval = command(&ap[1], TRUE, TRUE, "rmdel"); 3921282Seric if (rval == 0) 393*1431Seric rval = command(&ap[2], FALSE, TRUE, "get -e -g"); 3941282Seric break; 395226Seric 396261Seric case CLEAN: 3971282Seric rval = clean((int) cmd->sccspath); 398261Seric break; 399261Seric 400396Seric case UNEDIT: 401*1431Seric for (argv = np = &ap[1]; *argv != NULL; argv++) 402585Seric { 4031316Seric if (unedit(*argv)) 4041316Seric *np++ = *argv; 405585Seric } 4061316Seric *np = NULL; 407585Seric if (i > 0) 408*1431Seric rval = command(&ap[1], FALSE, FALSE, "get"); 409396Seric break; 410396Seric 411200Seric default: 4121205Seric syserr("oper %d", cmd->sccsoper); 413200Seric exit(EX_SOFTWARE); 414200Seric } 4151282Seric # ifdef DEBUG 4161282Seric if (Debug) 4171282Seric printf("command: rval=%d\n", rval); 4181282Seric # endif 4191282Seric return (rval); 420200Seric } 421262Seric /* 422262Seric ** LOOKUP -- look up an SCCS command name. 423262Seric ** 424262Seric ** Parameters: 425262Seric ** name -- the name of the command to look up. 426262Seric ** 427262Seric ** Returns: 428262Seric ** ptr to command descriptor for this command. 429262Seric ** NULL if no such entry. 430262Seric ** 431262Seric ** Side Effects: 432262Seric ** none. 433262Seric */ 434200Seric 435262Seric struct sccsprog * 436262Seric lookup(name) 437262Seric char *name; 438262Seric { 439262Seric register struct sccsprog *cmd; 440226Seric 441262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 442262Seric { 443262Seric if (strcmp(cmd->sccsname, name) == 0) 444262Seric return (cmd); 445262Seric } 446262Seric return (NULL); 447262Seric } 4481282Seric /* 4491282Seric ** CALLPROG -- call a program 4501282Seric ** 4511316Seric ** Used to call the SCCS programs. 4521282Seric ** 4531282Seric ** Parameters: 4541282Seric ** progpath -- pathname of the program to call. 4551282Seric ** flags -- status flags from the command descriptors. 4561282Seric ** argv -- an argument vector to pass to the program. 4571282Seric ** forkflag -- if true, fork before calling, else just 4581282Seric ** exec. 4591282Seric ** 4601282Seric ** Returns: 4611282Seric ** The exit status of the program. 4621282Seric ** Nothing if forkflag == FALSE. 4631282Seric ** 4641282Seric ** Side Effects: 4651282Seric ** Can exit if forkflag == FALSE. 4661282Seric */ 467226Seric 468200Seric callprog(progpath, flags, argv, forkflag) 469200Seric char *progpath; 470200Seric short flags; 471200Seric char **argv; 472200Seric bool forkflag; 473200Seric { 474200Seric register int i; 475201Seric auto int st; 476200Seric 4771316Seric # ifdef DEBUG 4781316Seric if (Debug) 4791316Seric { 4801316Seric printf("callprog:\n"); 4811316Seric for (i = 0; argv[i] != NULL; i++) 4821316Seric printf("\t\"%s\"\n", argv[i]); 4831316Seric } 4841316Seric # endif 4851316Seric 486200Seric if (*argv == NULL) 487200Seric return (-1); 488200Seric 489157Seric /* 490226Seric ** Fork if appropriate. 491148Seric */ 492148Seric 493200Seric if (forkflag) 494200Seric { 495393Seric # ifdef DEBUG 496393Seric if (Debug) 497393Seric printf("Forking\n"); 498393Seric # endif 499200Seric i = fork(); 500200Seric if (i < 0) 501200Seric { 5021205Seric syserr("cannot fork"); 503200Seric exit(EX_OSERR); 504200Seric } 505200Seric else if (i > 0) 506201Seric { 507201Seric wait(&st); 5081282Seric if ((st & 0377) == 0) 5091282Seric st = (st >> 8) & 0377; 510201Seric return (st); 511201Seric } 512200Seric } 513200Seric 514200Seric /* 515200Seric ** Set protection as appropriate. 516200Seric */ 517200Seric 518200Seric if (bitset(REALUSER, flags)) 519200Seric setuid(getuid()); 520226Seric 521200Seric /* 522226Seric ** Call real SCCS program. 523200Seric */ 524200Seric 525226Seric execv(progpath, argv); 5261205Seric syserr("cannot execute %s", progpath); 527148Seric exit(EX_UNAVAILABLE); 528148Seric } 529586Seric /* 530586Seric ** MAKEFILE -- make filename of SCCS file 531586Seric ** 532586Seric ** If the name passed is already the name of an SCCS file, 533586Seric ** just return it. Otherwise, munge the name into the name 534586Seric ** of the actual SCCS file. 535586Seric ** 536586Seric ** There are cases when it is not clear what you want to 537586Seric ** do. For example, if SccsPath is an absolute pathname 538586Seric ** and the name given is also an absolute pathname, we go 539586Seric ** for SccsPath (& only use the last component of the name 540586Seric ** passed) -- this is important for security reasons (if 541586Seric ** sccs is being used as a setuid front end), but not 542586Seric ** particularly intuitive. 543586Seric ** 544586Seric ** Parameters: 545586Seric ** name -- the file name to be munged. 546586Seric ** 547586Seric ** Returns: 548586Seric ** The pathname of the sccs file. 549586Seric ** NULL on error. 550586Seric ** 551586Seric ** Side Effects: 552586Seric ** none. 553586Seric */ 554148Seric 555148Seric char * 556148Seric makefile(name) 557148Seric char *name; 558148Seric { 559148Seric register char *p; 560148Seric register char c; 561148Seric char buf[512]; 562588Seric struct stat stbuf; 563148Seric extern char *malloc(); 564586Seric extern char *rindex(); 565588Seric extern bool safepath(); 566587Seric extern bool isdir(); 567587Seric register char *q; 568148Seric 569586Seric p = rindex(name, '/'); 570586Seric if (p == NULL) 571586Seric p = name; 572586Seric else 573586Seric p++; 574586Seric 575148Seric /* 576588Seric ** Check to see that the path is "safe", i.e., that we 577588Seric ** are not letting some nasty person use the setuid part 578588Seric ** of this program to look at or munge some presumably 579588Seric ** hidden files. 580148Seric */ 581148Seric 582588Seric if (SccsDir[0] == '/' && !safepath(name)) 583588Seric return (NULL); 584586Seric 585586Seric /* 586588Seric ** Create the base pathname. 587586Seric */ 588586Seric 589588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 590148Seric { 591588Seric strcpy(buf, SccsDir); 592586Seric strcat(buf, "/"); 593586Seric } 594586Seric else 595586Seric strcpy(buf, ""); 596587Seric strncat(buf, name, p - name); 597587Seric q = &buf[strlen(buf)]; 598587Seric strcpy(q, p); 599587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 600586Seric { 601588Seric strcpy(q, SccsPath); 602588Seric strcat(buf, "/s."); 603586Seric strcat(buf, p); 604586Seric } 605148Seric 606588Seric if (strcmp(buf, name) == 0) 607588Seric p = name; 608588Seric else 609148Seric { 610588Seric p = malloc(strlen(buf) + 1); 611588Seric if (p == NULL) 612588Seric { 613588Seric perror("Sccs: no mem"); 614588Seric exit(EX_OSERR); 615588Seric } 616588Seric strcpy(p, buf); 617148Seric } 618148Seric return (p); 619148Seric } 620261Seric /* 621587Seric ** ISDIR -- return true if the argument is a directory. 622587Seric ** 623587Seric ** Parameters: 624587Seric ** name -- the pathname of the file to check. 625587Seric ** 626587Seric ** Returns: 627587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 628587Seric ** 629587Seric ** Side Effects: 630587Seric ** none. 631587Seric */ 632587Seric 633587Seric bool 634587Seric isdir(name) 635587Seric char *name; 636587Seric { 637587Seric struct stat stbuf; 638587Seric 639587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 640587Seric } 641587Seric /* 642586Seric ** SAFEPATH -- determine whether a pathname is "safe" 643586Seric ** 644586Seric ** "Safe" pathnames only allow you to get deeper into the 645586Seric ** directory structure, i.e., full pathnames and ".." are 646586Seric ** not allowed. 647586Seric ** 648586Seric ** Parameters: 649586Seric ** p -- the name to check. 650586Seric ** 651586Seric ** Returns: 652586Seric ** TRUE -- if the path is safe. 653586Seric ** FALSE -- if the path is not safe. 654586Seric ** 655586Seric ** Side Effects: 656586Seric ** Prints a message if the path is not safe. 657586Seric */ 658586Seric 659586Seric bool 660586Seric safepath(p) 661586Seric register char *p; 662586Seric { 663586Seric extern char *index(); 664586Seric 665586Seric if (*p != '/') 666586Seric { 667586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 668586Seric { 669586Seric p = index(p, '/'); 670586Seric if (p == NULL) 671586Seric return (TRUE); 672586Seric p++; 673586Seric } 674586Seric } 675586Seric 676586Seric printf("You may not use full pathnames or \"..\"\n"); 677586Seric return (FALSE); 678586Seric } 679586Seric /* 680261Seric ** CLEAN -- clean out recreatable files 681261Seric ** 682261Seric ** Any file for which an "s." file exists but no "p." file 683261Seric ** exists in the current directory is purged. 684261Seric ** 685261Seric ** Parameters: 686819Seric ** tells whether this came from a "clean", "info", or 687819Seric ** "check" command. 688261Seric ** 689261Seric ** Returns: 690261Seric ** none. 691261Seric ** 692261Seric ** Side Effects: 693819Seric ** Removes files in the current directory. 694819Seric ** Prints information regarding files being edited. 695819Seric ** Exits if a "check" command. 696261Seric */ 697261Seric 698819Seric clean(mode) 699819Seric int mode; 700261Seric { 701261Seric struct direct dir; 702261Seric struct stat stbuf; 703261Seric char buf[100]; 704394Seric char pline[120]; 705346Seric register FILE *dirfd; 706346Seric register char *basefile; 707351Seric bool gotedit; 708394Seric FILE *pfp; 709261Seric 7101207Seric strcpy(buf, SccsDir); 7111207Seric if (buf[0] != '\0') 7121207Seric strcat(buf, "/"); 7131207Seric strcat(buf, SccsPath); 7141207Seric dirfd = fopen(buf, "r"); 715261Seric if (dirfd == NULL) 716261Seric { 7171207Seric usrerr("cannot open %s", buf); 7181282Seric return (EX_NOINPUT); 719261Seric } 720261Seric 721261Seric /* 722261Seric ** Scan the SCCS directory looking for s. files. 723261Seric */ 724261Seric 725351Seric gotedit = FALSE; 726261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 727261Seric { 728568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 729261Seric continue; 730261Seric 731261Seric /* got an s. file -- see if the p. file exists */ 7321207Seric strcpy(buf, SccsDir); 7331207Seric if (buf[0] != '\0') 7341207Seric strcat(buf, "/"); 7351207Seric strcat(buf, SccsPath); 736261Seric strcat(buf, "/p."); 737346Seric basefile = &buf[strlen(buf)]; 738568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 739346Seric basefile[sizeof dir.d_name - 2] = '\0'; 740394Seric pfp = fopen(buf, "r"); 741394Seric if (pfp != NULL) 742346Seric { 743394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 744416Seric printf("%12s: being edited: %s", basefile, pline); 745394Seric fclose(pfp); 746351Seric gotedit = TRUE; 747261Seric continue; 748346Seric } 749261Seric 750261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 751819Seric if (mode == CLEANC) 752346Seric { 753568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 754346Seric buf[sizeof dir.d_name - 2] = '\0'; 755346Seric unlink(buf); 756346Seric } 757261Seric } 758261Seric 759261Seric fclose(dirfd); 760819Seric if (!gotedit && mode == INFOC) 761416Seric printf("Nothing being edited\n"); 762819Seric if (mode == CHECKC) 763819Seric exit(gotedit); 7641282Seric return (EX_OK); 765261Seric } 766396Seric /* 767396Seric ** UNEDIT -- unedit a file 768396Seric ** 769396Seric ** Checks to see that the current user is actually editting 770396Seric ** the file and arranges that s/he is not editting it. 771396Seric ** 772396Seric ** Parameters: 773416Seric ** fn -- the name of the file to be unedited. 774396Seric ** 775396Seric ** Returns: 776585Seric ** TRUE -- if the file was successfully unedited. 777585Seric ** FALSE -- if the file was not unedited for some 778585Seric ** reason. 779396Seric ** 780396Seric ** Side Effects: 781396Seric ** fn is removed 782396Seric ** entries are removed from pfile. 783396Seric */ 784396Seric 785585Seric bool 786396Seric unedit(fn) 787396Seric char *fn; 788396Seric { 789396Seric register FILE *pfp; 790396Seric char *pfn; 791396Seric static char tfn[] = "/tmp/sccsXXXXX"; 792396Seric FILE *tfp; 793396Seric register char *p; 794396Seric register char *q; 795396Seric bool delete = FALSE; 796396Seric bool others = FALSE; 797396Seric char *myname; 798396Seric extern char *getlogin(); 799396Seric struct pfile *pent; 800396Seric extern struct pfile *getpfile(); 801396Seric char buf[120]; 8021316Seric extern char *makefile(); 803828Seric # ifdef UIDUSER 804828Seric struct passwd *pw; 805828Seric extern struct passwd *getpwuid(); 806828Seric # endif UIDUSER 807396Seric 808396Seric /* make "s." filename & find the trailing component */ 809396Seric pfn = makefile(fn); 810586Seric if (pfn == NULL) 811586Seric return (FALSE); 812586Seric q = rindex(pfn, '/'); 813586Seric if (q == NULL) 814586Seric q = &pfn[-1]; 815586Seric if (q[1] != 's' || q[2] != '.') 816396Seric { 8171205Seric usrerr("bad file name \"%s\"", fn); 818585Seric return (FALSE); 819396Seric } 820396Seric 821396Seric /* turn "s." into "p." */ 822396Seric *++q = 'p'; 823396Seric 824396Seric pfp = fopen(pfn, "r"); 825396Seric if (pfp == NULL) 826396Seric { 827416Seric printf("%12s: not being edited\n", fn); 828585Seric return (FALSE); 829396Seric } 830396Seric 831396Seric /* 832396Seric ** Copy p-file to temp file, doing deletions as needed. 833396Seric */ 834396Seric 835396Seric mktemp(tfn); 836396Seric tfp = fopen(tfn, "w"); 837396Seric if (tfp == NULL) 838396Seric { 8391205Seric usrerr("cannot create \"%s\"", tfn); 840396Seric exit(EX_OSERR); 841396Seric } 842396Seric 843828Seric # ifdef UIDUSER 844828Seric pw = getpwuid(getuid()); 845828Seric if (pw == NULL) 846828Seric { 8471205Seric syserr("who are you? (uid=%d)", getuid()); 848828Seric exit(EX_OSERR); 849828Seric } 850828Seric myname = pw->pw_name; 851828Seric # else 852396Seric myname = getlogin(); 853828Seric # endif UIDUSER 854396Seric while ((pent = getpfile(pfp)) != NULL) 855396Seric { 856396Seric if (strcmp(pent->p_user, myname) == 0) 857396Seric { 858396Seric /* a match */ 859396Seric delete++; 860396Seric } 861396Seric else 862396Seric { 863396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 864396Seric pent->p_nsid, pent->p_user, pent->p_date, 865396Seric pent->p_time); 866396Seric others++; 867396Seric } 868396Seric } 869396Seric 870396Seric /* do final cleanup */ 871396Seric if (others) 872396Seric { 873396Seric if (freopen(tfn, "r", tfp) == NULL) 874396Seric { 8751205Seric syserr("cannot reopen \"%s\"", tfn); 876396Seric exit(EX_OSERR); 877396Seric } 878396Seric if (freopen(pfn, "w", pfp) == NULL) 879396Seric { 8801205Seric usrerr("cannot create \"%s\"", pfn); 881585Seric return (FALSE); 882396Seric } 883396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 884396Seric fputs(buf, pfp); 885396Seric } 886396Seric else 887396Seric { 888396Seric unlink(pfn); 889396Seric } 890396Seric fclose(tfp); 891396Seric fclose(pfp); 892396Seric unlink(tfn); 893396Seric 894396Seric if (delete) 895396Seric { 896396Seric unlink(fn); 897396Seric printf("%12s: removed\n", fn); 898585Seric return (TRUE); 899396Seric } 900396Seric else 901396Seric { 902416Seric printf("%12s: not being edited by you\n", fn); 903585Seric return (FALSE); 904396Seric } 905396Seric } 906396Seric /* 907396Seric ** GETPFILE -- get an entry from the p-file 908396Seric ** 909396Seric ** Parameters: 910396Seric ** pfp -- p-file file pointer 911396Seric ** 912396Seric ** Returns: 913396Seric ** pointer to p-file struct for next entry 914396Seric ** NULL on EOF or error 915396Seric ** 916396Seric ** Side Effects: 917396Seric ** Each call wipes out results of previous call. 918396Seric */ 919396Seric 920396Seric struct pfile * 921396Seric getpfile(pfp) 922396Seric FILE *pfp; 923396Seric { 924396Seric static struct pfile ent; 925396Seric static char buf[120]; 926396Seric register char *p; 927396Seric extern char *nextfield(); 928396Seric 929396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 930396Seric return (NULL); 931396Seric 932396Seric ent.p_osid = p = buf; 933396Seric ent.p_nsid = p = nextfield(p); 934396Seric ent.p_user = p = nextfield(p); 935396Seric ent.p_date = p = nextfield(p); 936396Seric ent.p_time = p = nextfield(p); 937396Seric if (p == NULL || nextfield(p) != NULL) 938396Seric return (NULL); 939396Seric 940396Seric return (&ent); 941396Seric } 942396Seric 943396Seric 944396Seric char * 945396Seric nextfield(p) 946396Seric register char *p; 947396Seric { 948396Seric if (p == NULL || *p == '\0') 949396Seric return (NULL); 950396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 951396Seric p++; 952396Seric if (*p == '\n' || *p == '\0') 953396Seric { 954396Seric *p = '\0'; 955396Seric return (NULL); 956396Seric } 957396Seric *p++ = '\0'; 958396Seric return (p); 959396Seric } 9601205Seric /* 9611205Seric ** USRERR -- issue user-level error 9621205Seric ** 9631205Seric ** Parameters: 9641205Seric ** f -- format string. 9651205Seric ** p1-p3 -- parameters to a printf. 9661205Seric ** 9671205Seric ** Returns: 9681205Seric ** -1 9691205Seric ** 9701205Seric ** Side Effects: 9711205Seric ** none. 9721205Seric */ 9731205Seric 9741205Seric usrerr(f, p1, p2, p3) 9751205Seric char *f; 9761205Seric { 9771205Seric fprintf(stderr, "\n%s: ", MyName); 9781205Seric fprintf(stderr, f, p1, p2, p3); 9791205Seric fprintf(stderr, "\n"); 9801205Seric 9811205Seric return (-1); 9821205Seric } 9831205Seric /* 9841205Seric ** SYSERR -- print system-generated error. 9851205Seric ** 9861205Seric ** Parameters: 9871205Seric ** f -- format string to a printf. 9881205Seric ** p1, p2, p3 -- parameters to f. 9891205Seric ** 9901205Seric ** Returns: 9911205Seric ** never. 9921205Seric ** 9931205Seric ** Side Effects: 9941205Seric ** none. 9951205Seric */ 9961205Seric 9971205Seric syserr(f, p1, p2, p3) 9981205Seric char *f; 9991205Seric { 10001205Seric extern int errno; 10011205Seric 10021205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 10031205Seric fprintf(stderr, f, p1, p2, p3); 10041205Seric fprintf(stderr, "\n"); 10051205Seric if (errno == 0) 10061205Seric exit(EX_SOFTWARE); 10071205Seric else 10081205Seric { 10091205Seric perror(0); 10101205Seric exit(EX_OSERR); 10111205Seric } 10121205Seric } 1013