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*1316Seric static char SccsId[] = "@(#)sccs.c 1.32 10/09/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 */ 120*1316Seric 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 */ 130200Seric 131157Seric /* bits for sccsflags */ 132200Seric # define NO_SDOT 0001 /* no s. on front of args */ 133200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 134148Seric 135819Seric /* modes for the "clean", "info", "check" ops */ 136819Seric # define CLEANC 0 /* clean command */ 137819Seric # define INFOC 1 /* info command */ 138819Seric # define CHECKC 2 /* check command */ 139819Seric 140202Seric # ifndef PROGPATH 141202Seric # define PROGPATH(name) "/usr/sccs/name" 142202Seric # endif PROGPATH 143202Seric 144148Seric struct sccsprog SccsProg[] = 145148Seric { 146*1316Seric "admin", PROG, REALUSER, "", PROGPATH(admin), 147*1316Seric "chghist", PROG, 0, "", PROGPATH(rmdel), 148*1316Seric "comb", PROG, 0, "", PROGPATH(comb), 149*1316Seric "delta", PROG, 0, "mysrp", PROGPATH(delta), 150*1316Seric "get", PROG, 0, "ixbeskc", PROGPATH(get), 151*1316Seric "help", PROG, NO_SDOT, "", PROGPATH(help), 152*1316Seric "prt", PROG, 0, "", PROGPATH(prt), 153*1316Seric "rmdel", PROG, REALUSER, "", PROGPATH(rmdel), 154*1316Seric "what", PROG, NO_SDOT, "", PROGPATH(what), 155*1316Seric "edit", CMACRO, NO_SDOT, "ixbsc", "get -e", 156*1316Seric "delget", CMACRO, NO_SDOT, "", "delta/get -t", 157*1316Seric "deledit", CMACRO, NO_SDOT, "", "delta/get -e -t", 158*1316Seric "fix", FIX, NO_SDOT, "", NULL, 159*1316Seric "clean", CLEAN, REALUSER, "", (char *) CLEANC, 160*1316Seric "info", CLEAN, REALUSER, "", (char *) INFOC, 161*1316Seric "check", CLEAN, REALUSER, "", (char *) CHECKC, 162*1316Seric "unedit", UNEDIT, NO_SDOT, "", NULL, 163*1316Seric NULL, -1, 0, "", NULL 164148Seric }; 165148Seric 166396Seric struct pfile 167396Seric { 168396Seric char *p_osid; /* old SID */ 169396Seric char *p_nsid; /* new SID */ 170396Seric char *p_user; /* user who did edit */ 171396Seric char *p_date; /* date of get */ 172396Seric char *p_time; /* time of get */ 173396Seric }; 174396Seric 1751270Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 1761270Seric # ifdef SCCSDIR 1771270Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 1781205Seric # else 1791270Seric char *SccsDir = ""; 1801205Seric # endif 181157Seric bool RealUser; /* if set, running as real user */ 182393Seric # ifdef DEBUG 183393Seric bool Debug; /* turn on tracing */ 184393Seric # endif 185148Seric 186148Seric main(argc, argv) 187148Seric int argc; 188148Seric char **argv; 189148Seric { 190148Seric register char *p; 191262Seric extern struct sccsprog *lookup(); 1921282Seric register int i; 193148Seric 194148Seric /* 195148Seric ** Detect and decode flags intended for this program. 196148Seric */ 197148Seric 198200Seric if (argc < 2) 199148Seric { 2001205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 201200Seric exit(EX_USAGE); 202200Seric } 203200Seric argv[argc] = NULL; 204200Seric 205262Seric if (lookup(argv[0]) == NULL) 206200Seric { 207262Seric while ((p = *++argv) != NULL) 208148Seric { 209262Seric if (*p != '-') 210262Seric break; 211262Seric switch (*++p) 212262Seric { 213262Seric case 'r': /* run as real user */ 214262Seric setuid(getuid()); 215262Seric RealUser++; 216262Seric break; 217148Seric 2181270Seric # ifndef SCCSDIR 219262Seric case 'p': /* path of sccs files */ 220262Seric SccsPath = ++p; 221262Seric break; 222148Seric 223588Seric case 'd': /* directory to search from */ 224588Seric SccsDir = ++p; 225588Seric break; 2261205Seric # endif 227588Seric 228393Seric # ifdef DEBUG 229393Seric case 'T': /* trace */ 230393Seric Debug++; 231393Seric break; 232393Seric # endif 233393Seric 234262Seric default: 2351205Seric usrerr("unknown option -%s", p); 236262Seric break; 237262Seric } 238148Seric } 239262Seric if (SccsPath[0] == '\0') 240262Seric SccsPath = "."; 241148Seric } 242148Seric 243*1316Seric i = command(argv, FALSE, FALSE, ""); 2441282Seric exit(i); 245200Seric } 2461282Seric /* 2471282Seric ** COMMAND -- look up and perform a command 2481282Seric ** 2491282Seric ** This routine is the guts of this program. Given an 2501282Seric ** argument vector, it looks up the "command" (argv[0]) 2511282Seric ** in the configuration table and does the necessary stuff. 2521282Seric ** 2531282Seric ** Parameters: 2541282Seric ** argv -- an argument vector to process. 2551282Seric ** forkflag -- if set, fork before executing the command. 256*1316Seric ** editflag -- if set, only include flags listed in the 257*1316Seric ** sccsklets field of the command descriptor. 258*1316Seric ** arg0 -- a space-seperated list of arguments to insert 259*1316Seric ** before argv. 2601282Seric ** 2611282Seric ** Returns: 2621282Seric ** zero -- command executed ok. 2631282Seric ** else -- error status. 2641282Seric ** 2651282Seric ** Side Effects: 2661282Seric ** none. 2671282Seric */ 268157Seric 269*1316Seric command(argv, forkflag, editflag, arg0) 270200Seric char **argv; 271201Seric bool forkflag; 272*1316Seric bool editflag; 273*1316Seric char *arg0; 274200Seric { 275200Seric register struct sccsprog *cmd; 276200Seric register char *p; 277201Seric register char *q; 278201Seric char buf[40]; 279262Seric extern struct sccsprog *lookup(); 280*1316Seric char *nav[1000]; 281*1316Seric char **np; 282*1316Seric char **ap; 283585Seric register int i; 284585Seric extern bool unedit(); 2851282Seric int rval = 0; 286*1316Seric extern char *index(); 287*1316Seric extern char *makefile(); 288200Seric 289393Seric # ifdef DEBUG 290393Seric if (Debug) 291393Seric { 292*1316Seric printf("command:\n\t\"%s\"\n", arg0); 293*1316Seric for (np = argv; *np != NULL; np++) 294*1316Seric printf("\t\"%s\"\n", *np); 295393Seric } 296393Seric # endif 297393Seric 298157Seric /* 299*1316Seric ** Copy arguments. 300*1316Seric ** Phase one -- from arg0 & if necessary argv[0]. 301*1316Seric */ 302*1316Seric 303*1316Seric np = nav; 304*1316Seric for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) 305*1316Seric { 306*1316Seric *np++ = q; 307*1316Seric while (*p == ' ') 308*1316Seric p++; 309*1316Seric while (*p != ' ' && *p != '\0' && *p != '/') 310*1316Seric *q++ = *p++; 311*1316Seric *q++ = '\0'; 312*1316Seric } 313*1316Seric *np = NULL; 314*1316Seric if (nav[0] == NULL) 315*1316Seric *np++ = *argv++; 316*1316Seric 317*1316Seric /* 318148Seric ** Look up command. 319*1316Seric ** At this point, nav[0] is the command name. 320148Seric */ 321148Seric 322*1316Seric cmd = lookup(nav[0]); 323262Seric if (cmd == NULL) 324148Seric { 3251205Seric usrerr("Unknown command \"%s\"", argv[0]); 3261282Seric return (EX_USAGE); 327148Seric } 328148Seric 329148Seric /* 330*1316Seric ** Copy remaining arguments doing editing as appropriate. 331*1316Seric */ 332*1316Seric 333*1316Seric for (; *argv != NULL; argv++) 334*1316Seric { 335*1316Seric p = *argv; 336*1316Seric if (*p == '-') 337*1316Seric { 338*1316Seric if (p[1] == '\0' || !editflag || cmd->sccsklets == NULL || 339*1316Seric index(cmd->sccsklets, p[1]) != NULL) 340*1316Seric *np++ = p; 341*1316Seric } 342*1316Seric else 343*1316Seric { 344*1316Seric if (!bitset(NO_SDOT, cmd->sccsflags)) 345*1316Seric p = makefile(p); 346*1316Seric if (p != NULL) 347*1316Seric *np++ = p; 348*1316Seric } 349*1316Seric } 350*1316Seric *np = NULL; 351*1316Seric 352*1316Seric /* 353200Seric ** Interpret operation associated with this command. 354157Seric */ 355157Seric 356200Seric switch (cmd->sccsoper) 357200Seric { 358200Seric case PROG: /* call an sccs prog */ 359*1316Seric rval = callprog(cmd->sccspath, cmd->sccsflags, nav, forkflag); 360201Seric break; 361201Seric 362201Seric case CMACRO: /* command macro */ 363201Seric for (p = cmd->sccspath; *p != '\0'; p++) 364201Seric { 365*1316Seric q = p; 366*1316Seric while (*p != '\0' && *p != '/') 367*1316Seric p++; 368*1316Seric rval = command(&nav[1], *p != '\0', TRUE, q); 3691282Seric if (rval != 0) 3701282Seric break; 371201Seric } 3721282Seric break; 373157Seric 374226Seric case FIX: /* fix a delta */ 375*1316Seric if (strncmp(nav[1], "-r", 2) != 0) 376226Seric { 3771205Seric usrerr("-r flag needed for fix command"); 3781282Seric rval = EX_USAGE; 379226Seric break; 380226Seric } 381*1316Seric rval = command(&nav[1], TRUE, TRUE, "get -k"); 3821282Seric if (rval == 0) 383*1316Seric rval = command(&nav[1], TRUE, TRUE, "rmdel"); 3841282Seric if (rval == 0) 385*1316Seric rval = command(&nav[2], FALSE, TRUE, "get -e -g"); 3861282Seric break; 387226Seric 388261Seric case CLEAN: 3891282Seric rval = clean((int) cmd->sccspath); 390261Seric break; 391261Seric 392396Seric case UNEDIT: 393*1316Seric for (argv = np = &nav[1]; *argv != NULL; argv++) 394585Seric { 395*1316Seric if (unedit(*argv)) 396*1316Seric *np++ = *argv; 397585Seric } 398*1316Seric *np = NULL; 399585Seric if (i > 0) 400*1316Seric rval = command(&nav[1], FALSE, FALSE, "get"); 401396Seric break; 402396Seric 403200Seric default: 4041205Seric syserr("oper %d", cmd->sccsoper); 405200Seric exit(EX_SOFTWARE); 406200Seric } 4071282Seric # ifdef DEBUG 4081282Seric if (Debug) 4091282Seric printf("command: rval=%d\n", rval); 4101282Seric # endif 4111282Seric return (rval); 412200Seric } 413262Seric /* 414262Seric ** LOOKUP -- look up an SCCS command name. 415262Seric ** 416262Seric ** Parameters: 417262Seric ** name -- the name of the command to look up. 418262Seric ** 419262Seric ** Returns: 420262Seric ** ptr to command descriptor for this command. 421262Seric ** NULL if no such entry. 422262Seric ** 423262Seric ** Side Effects: 424262Seric ** none. 425262Seric */ 426200Seric 427262Seric struct sccsprog * 428262Seric lookup(name) 429262Seric char *name; 430262Seric { 431262Seric register struct sccsprog *cmd; 432226Seric 433262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 434262Seric { 435262Seric if (strcmp(cmd->sccsname, name) == 0) 436262Seric return (cmd); 437262Seric } 438262Seric return (NULL); 439262Seric } 4401282Seric /* 4411282Seric ** CALLPROG -- call a program 4421282Seric ** 443*1316Seric ** Used to call the SCCS programs. 4441282Seric ** 4451282Seric ** Parameters: 4461282Seric ** progpath -- pathname of the program to call. 4471282Seric ** flags -- status flags from the command descriptors. 4481282Seric ** argv -- an argument vector to pass to the program. 4491282Seric ** forkflag -- if true, fork before calling, else just 4501282Seric ** exec. 4511282Seric ** 4521282Seric ** Returns: 4531282Seric ** The exit status of the program. 4541282Seric ** Nothing if forkflag == FALSE. 4551282Seric ** 4561282Seric ** Side Effects: 4571282Seric ** Can exit if forkflag == FALSE. 4581282Seric */ 459226Seric 460200Seric callprog(progpath, flags, argv, forkflag) 461200Seric char *progpath; 462200Seric short flags; 463200Seric char **argv; 464200Seric bool forkflag; 465200Seric { 466200Seric register int i; 467201Seric auto int st; 468200Seric 469*1316Seric # ifdef DEBUG 470*1316Seric if (Debug) 471*1316Seric { 472*1316Seric printf("callprog:\n"); 473*1316Seric for (i = 0; argv[i] != NULL; i++) 474*1316Seric printf("\t\"%s\"\n", argv[i]); 475*1316Seric } 476*1316Seric # endif 477*1316Seric 478200Seric if (*argv == NULL) 479200Seric return (-1); 480200Seric 481157Seric /* 482226Seric ** Fork if appropriate. 483148Seric */ 484148Seric 485200Seric if (forkflag) 486200Seric { 487393Seric # ifdef DEBUG 488393Seric if (Debug) 489393Seric printf("Forking\n"); 490393Seric # endif 491200Seric i = fork(); 492200Seric if (i < 0) 493200Seric { 4941205Seric syserr("cannot fork"); 495200Seric exit(EX_OSERR); 496200Seric } 497200Seric else if (i > 0) 498201Seric { 499201Seric wait(&st); 5001282Seric if ((st & 0377) == 0) 5011282Seric st = (st >> 8) & 0377; 502201Seric return (st); 503201Seric } 504200Seric } 505200Seric 506200Seric /* 507200Seric ** Set protection as appropriate. 508200Seric */ 509200Seric 510200Seric if (bitset(REALUSER, flags)) 511200Seric setuid(getuid()); 512226Seric 513200Seric /* 514226Seric ** Call real SCCS program. 515200Seric */ 516200Seric 517226Seric execv(progpath, argv); 5181205Seric syserr("cannot execute %s", progpath); 519148Seric exit(EX_UNAVAILABLE); 520148Seric } 521586Seric /* 522586Seric ** MAKEFILE -- make filename of SCCS file 523586Seric ** 524586Seric ** If the name passed is already the name of an SCCS file, 525586Seric ** just return it. Otherwise, munge the name into the name 526586Seric ** of the actual SCCS file. 527586Seric ** 528586Seric ** There are cases when it is not clear what you want to 529586Seric ** do. For example, if SccsPath is an absolute pathname 530586Seric ** and the name given is also an absolute pathname, we go 531586Seric ** for SccsPath (& only use the last component of the name 532586Seric ** passed) -- this is important for security reasons (if 533586Seric ** sccs is being used as a setuid front end), but not 534586Seric ** particularly intuitive. 535586Seric ** 536586Seric ** Parameters: 537586Seric ** name -- the file name to be munged. 538586Seric ** 539586Seric ** Returns: 540586Seric ** The pathname of the sccs file. 541586Seric ** NULL on error. 542586Seric ** 543586Seric ** Side Effects: 544586Seric ** none. 545586Seric */ 546148Seric 547148Seric char * 548148Seric makefile(name) 549148Seric char *name; 550148Seric { 551148Seric register char *p; 552148Seric register char c; 553148Seric char buf[512]; 554588Seric struct stat stbuf; 555148Seric extern char *malloc(); 556586Seric extern char *rindex(); 557588Seric extern bool safepath(); 558587Seric extern bool isdir(); 559587Seric register char *q; 560148Seric 561586Seric p = rindex(name, '/'); 562586Seric if (p == NULL) 563586Seric p = name; 564586Seric else 565586Seric p++; 566586Seric 567148Seric /* 568588Seric ** Check to see that the path is "safe", i.e., that we 569588Seric ** are not letting some nasty person use the setuid part 570588Seric ** of this program to look at or munge some presumably 571588Seric ** hidden files. 572148Seric */ 573148Seric 574588Seric if (SccsDir[0] == '/' && !safepath(name)) 575588Seric return (NULL); 576586Seric 577586Seric /* 578588Seric ** Create the base pathname. 579586Seric */ 580586Seric 581588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 582148Seric { 583588Seric strcpy(buf, SccsDir); 584586Seric strcat(buf, "/"); 585586Seric } 586586Seric else 587586Seric strcpy(buf, ""); 588587Seric strncat(buf, name, p - name); 589587Seric q = &buf[strlen(buf)]; 590587Seric strcpy(q, p); 591587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 592586Seric { 593588Seric strcpy(q, SccsPath); 594588Seric strcat(buf, "/s."); 595586Seric strcat(buf, p); 596586Seric } 597148Seric 598588Seric if (strcmp(buf, name) == 0) 599588Seric p = name; 600588Seric else 601148Seric { 602588Seric p = malloc(strlen(buf) + 1); 603588Seric if (p == NULL) 604588Seric { 605588Seric perror("Sccs: no mem"); 606588Seric exit(EX_OSERR); 607588Seric } 608588Seric strcpy(p, buf); 609148Seric } 610148Seric return (p); 611148Seric } 612261Seric /* 613587Seric ** ISDIR -- return true if the argument is a directory. 614587Seric ** 615587Seric ** Parameters: 616587Seric ** name -- the pathname of the file to check. 617587Seric ** 618587Seric ** Returns: 619587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 620587Seric ** 621587Seric ** Side Effects: 622587Seric ** none. 623587Seric */ 624587Seric 625587Seric bool 626587Seric isdir(name) 627587Seric char *name; 628587Seric { 629587Seric struct stat stbuf; 630587Seric 631587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 632587Seric } 633587Seric /* 634586Seric ** SAFEPATH -- determine whether a pathname is "safe" 635586Seric ** 636586Seric ** "Safe" pathnames only allow you to get deeper into the 637586Seric ** directory structure, i.e., full pathnames and ".." are 638586Seric ** not allowed. 639586Seric ** 640586Seric ** Parameters: 641586Seric ** p -- the name to check. 642586Seric ** 643586Seric ** Returns: 644586Seric ** TRUE -- if the path is safe. 645586Seric ** FALSE -- if the path is not safe. 646586Seric ** 647586Seric ** Side Effects: 648586Seric ** Prints a message if the path is not safe. 649586Seric */ 650586Seric 651586Seric bool 652586Seric safepath(p) 653586Seric register char *p; 654586Seric { 655586Seric extern char *index(); 656586Seric 657586Seric if (*p != '/') 658586Seric { 659586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 660586Seric { 661586Seric p = index(p, '/'); 662586Seric if (p == NULL) 663586Seric return (TRUE); 664586Seric p++; 665586Seric } 666586Seric } 667586Seric 668586Seric printf("You may not use full pathnames or \"..\"\n"); 669586Seric return (FALSE); 670586Seric } 671586Seric /* 672261Seric ** CLEAN -- clean out recreatable files 673261Seric ** 674261Seric ** Any file for which an "s." file exists but no "p." file 675261Seric ** exists in the current directory is purged. 676261Seric ** 677261Seric ** Parameters: 678819Seric ** tells whether this came from a "clean", "info", or 679819Seric ** "check" command. 680261Seric ** 681261Seric ** Returns: 682261Seric ** none. 683261Seric ** 684261Seric ** Side Effects: 685819Seric ** Removes files in the current directory. 686819Seric ** Prints information regarding files being edited. 687819Seric ** Exits if a "check" command. 688261Seric */ 689261Seric 690819Seric clean(mode) 691819Seric int mode; 692261Seric { 693261Seric struct direct dir; 694261Seric struct stat stbuf; 695261Seric char buf[100]; 696394Seric char pline[120]; 697346Seric register FILE *dirfd; 698346Seric register char *basefile; 699351Seric bool gotedit; 700394Seric FILE *pfp; 701261Seric 7021207Seric strcpy(buf, SccsDir); 7031207Seric if (buf[0] != '\0') 7041207Seric strcat(buf, "/"); 7051207Seric strcat(buf, SccsPath); 7061207Seric dirfd = fopen(buf, "r"); 707261Seric if (dirfd == NULL) 708261Seric { 7091207Seric usrerr("cannot open %s", buf); 7101282Seric return (EX_NOINPUT); 711261Seric } 712261Seric 713261Seric /* 714261Seric ** Scan the SCCS directory looking for s. files. 715261Seric */ 716261Seric 717351Seric gotedit = FALSE; 718261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 719261Seric { 720568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 721261Seric continue; 722261Seric 723261Seric /* got an s. file -- see if the p. file exists */ 7241207Seric strcpy(buf, SccsDir); 7251207Seric if (buf[0] != '\0') 7261207Seric strcat(buf, "/"); 7271207Seric strcat(buf, SccsPath); 728261Seric strcat(buf, "/p."); 729346Seric basefile = &buf[strlen(buf)]; 730568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 731346Seric basefile[sizeof dir.d_name - 2] = '\0'; 732394Seric pfp = fopen(buf, "r"); 733394Seric if (pfp != NULL) 734346Seric { 735394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 736416Seric printf("%12s: being edited: %s", basefile, pline); 737394Seric fclose(pfp); 738351Seric gotedit = TRUE; 739261Seric continue; 740346Seric } 741261Seric 742261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 743819Seric if (mode == CLEANC) 744346Seric { 745568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 746346Seric buf[sizeof dir.d_name - 2] = '\0'; 747346Seric unlink(buf); 748346Seric } 749261Seric } 750261Seric 751261Seric fclose(dirfd); 752819Seric if (!gotedit && mode == INFOC) 753416Seric printf("Nothing being edited\n"); 754819Seric if (mode == CHECKC) 755819Seric exit(gotedit); 7561282Seric return (EX_OK); 757261Seric } 758396Seric /* 759396Seric ** UNEDIT -- unedit a file 760396Seric ** 761396Seric ** Checks to see that the current user is actually editting 762396Seric ** the file and arranges that s/he is not editting it. 763396Seric ** 764396Seric ** Parameters: 765416Seric ** fn -- the name of the file to be unedited. 766396Seric ** 767396Seric ** Returns: 768585Seric ** TRUE -- if the file was successfully unedited. 769585Seric ** FALSE -- if the file was not unedited for some 770585Seric ** reason. 771396Seric ** 772396Seric ** Side Effects: 773396Seric ** fn is removed 774396Seric ** entries are removed from pfile. 775396Seric */ 776396Seric 777585Seric bool 778396Seric unedit(fn) 779396Seric char *fn; 780396Seric { 781396Seric register FILE *pfp; 782396Seric char *pfn; 783396Seric static char tfn[] = "/tmp/sccsXXXXX"; 784396Seric FILE *tfp; 785396Seric register char *p; 786396Seric register char *q; 787396Seric bool delete = FALSE; 788396Seric bool others = FALSE; 789396Seric char *myname; 790396Seric extern char *getlogin(); 791396Seric struct pfile *pent; 792396Seric extern struct pfile *getpfile(); 793396Seric char buf[120]; 794*1316Seric extern char *makefile(); 795828Seric # ifdef UIDUSER 796828Seric struct passwd *pw; 797828Seric extern struct passwd *getpwuid(); 798828Seric # endif UIDUSER 799396Seric 800396Seric /* make "s." filename & find the trailing component */ 801396Seric pfn = makefile(fn); 802586Seric if (pfn == NULL) 803586Seric return (FALSE); 804586Seric q = rindex(pfn, '/'); 805586Seric if (q == NULL) 806586Seric q = &pfn[-1]; 807586Seric if (q[1] != 's' || q[2] != '.') 808396Seric { 8091205Seric usrerr("bad file name \"%s\"", fn); 810585Seric return (FALSE); 811396Seric } 812396Seric 813396Seric /* turn "s." into "p." */ 814396Seric *++q = 'p'; 815396Seric 816396Seric pfp = fopen(pfn, "r"); 817396Seric if (pfp == NULL) 818396Seric { 819416Seric printf("%12s: not being edited\n", fn); 820585Seric return (FALSE); 821396Seric } 822396Seric 823396Seric /* 824396Seric ** Copy p-file to temp file, doing deletions as needed. 825396Seric */ 826396Seric 827396Seric mktemp(tfn); 828396Seric tfp = fopen(tfn, "w"); 829396Seric if (tfp == NULL) 830396Seric { 8311205Seric usrerr("cannot create \"%s\"", tfn); 832396Seric exit(EX_OSERR); 833396Seric } 834396Seric 835828Seric # ifdef UIDUSER 836828Seric pw = getpwuid(getuid()); 837828Seric if (pw == NULL) 838828Seric { 8391205Seric syserr("who are you? (uid=%d)", getuid()); 840828Seric exit(EX_OSERR); 841828Seric } 842828Seric myname = pw->pw_name; 843828Seric # else 844396Seric myname = getlogin(); 845828Seric # endif UIDUSER 846396Seric while ((pent = getpfile(pfp)) != NULL) 847396Seric { 848396Seric if (strcmp(pent->p_user, myname) == 0) 849396Seric { 850396Seric /* a match */ 851396Seric delete++; 852396Seric } 853396Seric else 854396Seric { 855396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 856396Seric pent->p_nsid, pent->p_user, pent->p_date, 857396Seric pent->p_time); 858396Seric others++; 859396Seric } 860396Seric } 861396Seric 862396Seric /* do final cleanup */ 863396Seric if (others) 864396Seric { 865396Seric if (freopen(tfn, "r", tfp) == NULL) 866396Seric { 8671205Seric syserr("cannot reopen \"%s\"", tfn); 868396Seric exit(EX_OSERR); 869396Seric } 870396Seric if (freopen(pfn, "w", pfp) == NULL) 871396Seric { 8721205Seric usrerr("cannot create \"%s\"", pfn); 873585Seric return (FALSE); 874396Seric } 875396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 876396Seric fputs(buf, pfp); 877396Seric } 878396Seric else 879396Seric { 880396Seric unlink(pfn); 881396Seric } 882396Seric fclose(tfp); 883396Seric fclose(pfp); 884396Seric unlink(tfn); 885396Seric 886396Seric if (delete) 887396Seric { 888396Seric unlink(fn); 889396Seric printf("%12s: removed\n", fn); 890585Seric return (TRUE); 891396Seric } 892396Seric else 893396Seric { 894416Seric printf("%12s: not being edited by you\n", fn); 895585Seric return (FALSE); 896396Seric } 897396Seric } 898396Seric /* 899396Seric ** GETPFILE -- get an entry from the p-file 900396Seric ** 901396Seric ** Parameters: 902396Seric ** pfp -- p-file file pointer 903396Seric ** 904396Seric ** Returns: 905396Seric ** pointer to p-file struct for next entry 906396Seric ** NULL on EOF or error 907396Seric ** 908396Seric ** Side Effects: 909396Seric ** Each call wipes out results of previous call. 910396Seric */ 911396Seric 912396Seric struct pfile * 913396Seric getpfile(pfp) 914396Seric FILE *pfp; 915396Seric { 916396Seric static struct pfile ent; 917396Seric static char buf[120]; 918396Seric register char *p; 919396Seric extern char *nextfield(); 920396Seric 921396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 922396Seric return (NULL); 923396Seric 924396Seric ent.p_osid = p = buf; 925396Seric ent.p_nsid = p = nextfield(p); 926396Seric ent.p_user = p = nextfield(p); 927396Seric ent.p_date = p = nextfield(p); 928396Seric ent.p_time = p = nextfield(p); 929396Seric if (p == NULL || nextfield(p) != NULL) 930396Seric return (NULL); 931396Seric 932396Seric return (&ent); 933396Seric } 934396Seric 935396Seric 936396Seric char * 937396Seric nextfield(p) 938396Seric register char *p; 939396Seric { 940396Seric if (p == NULL || *p == '\0') 941396Seric return (NULL); 942396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 943396Seric p++; 944396Seric if (*p == '\n' || *p == '\0') 945396Seric { 946396Seric *p = '\0'; 947396Seric return (NULL); 948396Seric } 949396Seric *p++ = '\0'; 950396Seric return (p); 951396Seric } 9521205Seric /* 9531205Seric ** USRERR -- issue user-level error 9541205Seric ** 9551205Seric ** Parameters: 9561205Seric ** f -- format string. 9571205Seric ** p1-p3 -- parameters to a printf. 9581205Seric ** 9591205Seric ** Returns: 9601205Seric ** -1 9611205Seric ** 9621205Seric ** Side Effects: 9631205Seric ** none. 9641205Seric */ 9651205Seric 9661205Seric usrerr(f, p1, p2, p3) 9671205Seric char *f; 9681205Seric { 9691205Seric fprintf(stderr, "\n%s: ", MyName); 9701205Seric fprintf(stderr, f, p1, p2, p3); 9711205Seric fprintf(stderr, "\n"); 9721205Seric 9731205Seric return (-1); 9741205Seric } 9751205Seric /* 9761205Seric ** SYSERR -- print system-generated error. 9771205Seric ** 9781205Seric ** Parameters: 9791205Seric ** f -- format string to a printf. 9801205Seric ** p1, p2, p3 -- parameters to f. 9811205Seric ** 9821205Seric ** Returns: 9831205Seric ** never. 9841205Seric ** 9851205Seric ** Side Effects: 9861205Seric ** none. 9871205Seric */ 9881205Seric 9891205Seric syserr(f, p1, p2, p3) 9901205Seric char *f; 9911205Seric { 9921205Seric extern int errno; 9931205Seric 9941205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 9951205Seric fprintf(stderr, f, p1, p2, p3); 9961205Seric fprintf(stderr, "\n"); 9971205Seric if (errno == 0) 9981205Seric exit(EX_SOFTWARE); 9991205Seric else 10001205Seric { 10011205Seric perror(0); 10021205Seric exit(EX_OSERR); 10031205Seric } 10041205Seric } 1005