1adf8f7d4Seric # include <stdio.h> 2adf8f7d4Seric # include <sys/types.h> 3adf8f7d4Seric # include <sys/stat.h> 4b2538d76Seric # include <sys/dir.h> 5adf8f7d4Seric # include <sysexits.h> 6b5d4f080Seric # include <whoami.h> 7adf8f7d4Seric 8*7d70f5d8Seric static char SccsId[] = "@(#)sccs.c 1.23.1.1 08/09/80"; 9c4432be4Seric 10c4432be4Seric # define bitset(bit, word) ((bit) & (word)) 11c4432be4Seric 12c4432be4Seric typedef char bool; 137de81dc7Seric # define TRUE 1 147de81dc7Seric # define FALSE 0 15d02a4f42Seric 16adf8f7d4Seric struct sccsprog 17adf8f7d4Seric { 18adf8f7d4Seric char *sccsname; /* name of SCCS routine */ 197de81dc7Seric short sccsoper; /* opcode, see below */ 207de81dc7Seric short sccsflags; /* flags, see below */ 21adf8f7d4Seric char *sccspath; /* pathname of binary implementing */ 22adf8f7d4Seric }; 23adf8f7d4Seric 247de81dc7Seric /* values for sccsoper */ 257de81dc7Seric # define PROG 0 /* call a program */ 261777fbcbSeric # define CMACRO 1 /* command substitution macro */ 27172147faSeric # define FIX 2 /* fix a delta */ 28b2538d76Seric # define CLEAN 3 /* clean out recreatable files */ 29e39a5722Seric # define UNEDIT 4 /* unedit a file */ 307de81dc7Seric 31c4432be4Seric /* bits for sccsflags */ 327de81dc7Seric # define NO_SDOT 0001 /* no s. on front of args */ 337de81dc7Seric # define REALUSER 0002 /* protected (e.g., admin) */ 34adf8f7d4Seric 35b5d4f080Seric # ifdef CSVAX 36b5d4f080Seric # define PROGPATH(name) "/usr/local/name" 37b5d4f080Seric # endif CSVAX 38b5d4f080Seric 39b5d4f080Seric # ifndef PROGPATH 40b5d4f080Seric # define PROGPATH(name) "/usr/sccs/name" 41b5d4f080Seric # endif PROGPATH 42b5d4f080Seric 43adf8f7d4Seric struct sccsprog SccsProg[] = 44adf8f7d4Seric { 45b5d4f080Seric "admin", PROG, REALUSER, PROGPATH(admin), 46b5d4f080Seric "chghist", PROG, 0, PROGPATH(rmdel), 47b5d4f080Seric "comb", PROG, 0, PROGPATH(comb), 48b5d4f080Seric "delta", PROG, 0, PROGPATH(delta), 49b5d4f080Seric "get", PROG, 0, PROGPATH(get), 50b5d4f080Seric "help", PROG, NO_SDOT, PROGPATH(help), 51b5d4f080Seric "prt", PROG, 0, PROGPATH(prt), 52b5d4f080Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 53b5d4f080Seric "what", PROG, NO_SDOT, PROGPATH(what), 54f0cc3246Seric "edit", CMACRO, 0, "get -e", 55f0cc3246Seric "delget", CMACRO, 0, "delta/get", 56d7e8b658Seric "deledit", CMACRO, 0, "delta/get -e", 571777fbcbSeric "del", CMACRO, 0, "delta/get", 58172147faSeric "delt", CMACRO, 0, "delta/get", 59172147faSeric "fix", FIX, 0, NULL, 603a208c77Seric "clean", CLEAN, REALUSER, (char *) TRUE, 613a208c77Seric "info", CLEAN, REALUSER, (char *) FALSE, 62e39a5722Seric "unedit", UNEDIT, 0, NULL, 637de81dc7Seric NULL, -1, 0, NULL 64adf8f7d4Seric }; 65adf8f7d4Seric 66e39a5722Seric struct pfile 67e39a5722Seric { 68e39a5722Seric char *p_osid; /* old SID */ 69e39a5722Seric char *p_nsid; /* new SID */ 70e39a5722Seric char *p_user; /* user who did edit */ 71e39a5722Seric char *p_date; /* date of get */ 72e39a5722Seric char *p_time; /* time of get */ 73e39a5722Seric }; 74e39a5722Seric 75c4432be4Seric char *SccsPath = "SCCS"; /* pathname of SCCS files */ 76c4432be4Seric bool RealUser; /* if set, running as real user */ 77f0cc3246Seric # ifdef DEBUG 78f0cc3246Seric bool Debug; /* turn on tracing */ 79f0cc3246Seric # endif 80adf8f7d4Seric 81adf8f7d4Seric main(argc, argv) 82adf8f7d4Seric int argc; 83adf8f7d4Seric char **argv; 84adf8f7d4Seric { 85adf8f7d4Seric register char *p; 86b1ed8a43Seric extern struct sccsprog *lookup(); 87adf8f7d4Seric 88adf8f7d4Seric /* 89adf8f7d4Seric ** Detect and decode flags intended for this program. 90adf8f7d4Seric */ 91adf8f7d4Seric 927de81dc7Seric if (argc < 2) 93adf8f7d4Seric { 947de81dc7Seric fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); 957de81dc7Seric exit(EX_USAGE); 967de81dc7Seric } 977de81dc7Seric argv[argc] = NULL; 987de81dc7Seric 99b1ed8a43Seric if (lookup(argv[0]) == NULL) 100b1ed8a43Seric { 1017de81dc7Seric while ((p = *++argv) != NULL) 1027de81dc7Seric { 103adf8f7d4Seric if (*p != '-') 104adf8f7d4Seric break; 105adf8f7d4Seric switch (*++p) 106adf8f7d4Seric { 107adf8f7d4Seric case 'r': /* run as real user */ 108adf8f7d4Seric setuid(getuid()); 109c4432be4Seric RealUser++; 110adf8f7d4Seric break; 111adf8f7d4Seric 112adf8f7d4Seric case 'p': /* path of sccs files */ 113adf8f7d4Seric SccsPath = ++p; 114adf8f7d4Seric break; 115adf8f7d4Seric 116f0cc3246Seric # ifdef DEBUG 117f0cc3246Seric case 'T': /* trace */ 118f0cc3246Seric Debug++; 119f0cc3246Seric break; 120f0cc3246Seric # endif 121f0cc3246Seric 122adf8f7d4Seric default: 123adf8f7d4Seric fprintf(stderr, "Sccs: unknown option -%s\n", p); 124adf8f7d4Seric break; 125adf8f7d4Seric } 126adf8f7d4Seric } 1275cabffd9Seric if (SccsPath[0] == '\0') 1285cabffd9Seric SccsPath = "."; 129b1ed8a43Seric } 130adf8f7d4Seric 1311777fbcbSeric command(argv, FALSE); 1327de81dc7Seric exit(EX_OK); 1337de81dc7Seric } 1347de81dc7Seric 1351777fbcbSeric command(argv, forkflag) 1367de81dc7Seric char **argv; 1371777fbcbSeric bool forkflag; 1387de81dc7Seric { 1397de81dc7Seric register struct sccsprog *cmd; 1407de81dc7Seric register char *p; 1411777fbcbSeric register char *q; 1421777fbcbSeric char buf[40]; 143b1ed8a43Seric extern struct sccsprog *lookup(); 144d51cd7e5Seric char *nav[200]; 145f0cc3246Seric char **avp; 146d51cd7e5Seric register int i; 147d51cd7e5Seric extern bool unedit(); 148f0cc3246Seric 149f0cc3246Seric # ifdef DEBUG 150f0cc3246Seric if (Debug) 151f0cc3246Seric { 152f0cc3246Seric printf("command:\n"); 153f0cc3246Seric for (avp = argv; *avp != NULL; avp++) 154f0cc3246Seric printf(" \"%s\"\n", *avp); 155f0cc3246Seric } 156f0cc3246Seric # endif 157c4432be4Seric 158c4432be4Seric /* 159adf8f7d4Seric ** Look up command. 1607de81dc7Seric ** At this point, argv points to the command name. 161adf8f7d4Seric */ 162adf8f7d4Seric 163e39a5722Seric cmd = lookup(argv[0]); 164b1ed8a43Seric if (cmd == NULL) 165adf8f7d4Seric { 166e39a5722Seric fprintf(stderr, "Sccs: Unknown command \"%s\"\n", argv[0]); 167adf8f7d4Seric exit(EX_USAGE); 168adf8f7d4Seric } 169adf8f7d4Seric 170adf8f7d4Seric /* 1717de81dc7Seric ** Interpret operation associated with this command. 172c4432be4Seric */ 173c4432be4Seric 1747de81dc7Seric switch (cmd->sccsoper) 1757de81dc7Seric { 1767de81dc7Seric case PROG: /* call an sccs prog */ 1771777fbcbSeric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 1781777fbcbSeric break; 1791777fbcbSeric 1801777fbcbSeric case CMACRO: /* command macro */ 1811777fbcbSeric for (p = cmd->sccspath; *p != '\0'; p++) 1821777fbcbSeric { 183f0cc3246Seric avp = nav; 184f0cc3246Seric *avp++ = buf; 1851777fbcbSeric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 186f0cc3246Seric { 187f0cc3246Seric if (*p == ' ') 188f0cc3246Seric { 1891777fbcbSeric *q = '\0'; 190f0cc3246Seric *avp++ = &q[1]; 191f0cc3246Seric } 192f0cc3246Seric else 193f0cc3246Seric *q = *p; 194f0cc3246Seric } 195f0cc3246Seric *q = '\0'; 196f0cc3246Seric *avp = NULL; 197f0cc3246Seric xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2], 198f0cc3246Seric nav[3], nav[4], nav[5], nav[6]); 1991777fbcbSeric } 2001777fbcbSeric fprintf(stderr, "Sccs internal error: CMACRO\n"); 2017de81dc7Seric exit(EX_SOFTWARE); 2027de81dc7Seric 203172147faSeric case FIX: /* fix a delta */ 204c9c62fd0Seric if (strncmp(argv[1], "-r", 2) != 0) 205172147faSeric { 206172147faSeric fprintf(stderr, "Sccs: -r flag needed for fix command\n"); 207172147faSeric break; 208172147faSeric } 209172147faSeric xcommand(&argv[1], TRUE, "get", "-k", NULL); 210172147faSeric xcommand(&argv[1], TRUE, "rmdel", NULL); 211172147faSeric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 212172147faSeric fprintf(stderr, "Sccs internal error: FIX\n"); 213172147faSeric exit(EX_SOFTWARE); 214172147faSeric 215b2538d76Seric case CLEAN: 2163a208c77Seric clean((bool) cmd->sccspath); 217b2538d76Seric break; 218b2538d76Seric 219e39a5722Seric case UNEDIT: 220d51cd7e5Seric i = 0; 221e39a5722Seric for (avp = &argv[1]; *avp != NULL; avp++) 222d51cd7e5Seric { 223d51cd7e5Seric if (unedit(*avp)) 224d51cd7e5Seric nav[i++] = *avp; 225d51cd7e5Seric } 226d51cd7e5Seric nav[i] = NULL; 227d51cd7e5Seric if (i > 0) 228d51cd7e5Seric xcommand(nav, FALSE, "get", NULL); 229e39a5722Seric break; 230e39a5722Seric 2317de81dc7Seric default: 2327de81dc7Seric fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); 2337de81dc7Seric exit(EX_SOFTWARE); 2347de81dc7Seric } 2357de81dc7Seric } 236b1ed8a43Seric /* 237b1ed8a43Seric ** LOOKUP -- look up an SCCS command name. 238b1ed8a43Seric ** 239b1ed8a43Seric ** Parameters: 240b1ed8a43Seric ** name -- the name of the command to look up. 241b1ed8a43Seric ** 242b1ed8a43Seric ** Returns: 243b1ed8a43Seric ** ptr to command descriptor for this command. 244b1ed8a43Seric ** NULL if no such entry. 245b1ed8a43Seric ** 246b1ed8a43Seric ** Side Effects: 247b1ed8a43Seric ** none. 248b1ed8a43Seric */ 249b1ed8a43Seric 250b1ed8a43Seric struct sccsprog * 251b1ed8a43Seric lookup(name) 252b1ed8a43Seric char *name; 253b1ed8a43Seric { 254b1ed8a43Seric register struct sccsprog *cmd; 255b1ed8a43Seric 256b1ed8a43Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 257b1ed8a43Seric { 258b1ed8a43Seric if (strcmp(cmd->sccsname, name) == 0) 259b1ed8a43Seric return (cmd); 260b1ed8a43Seric } 261b1ed8a43Seric return (NULL); 262b1ed8a43Seric } 2637de81dc7Seric 264172147faSeric 265172147faSeric xcommand(argv, forkflag, arg0) 266172147faSeric char **argv; 267172147faSeric bool forkflag; 268172147faSeric char *arg0; 269172147faSeric { 270172147faSeric register char **av; 271172147faSeric char *newargv[1000]; 272172147faSeric register char **np; 273172147faSeric 274172147faSeric np = newargv; 275172147faSeric for (av = &arg0; *av != NULL; av++) 276172147faSeric *np++ = *av; 277172147faSeric for (av = argv; *av != NULL; av++) 278172147faSeric *np++ = *av; 279172147faSeric *np = NULL; 280172147faSeric command(newargv, forkflag); 281172147faSeric } 282172147faSeric 2837de81dc7Seric callprog(progpath, flags, argv, forkflag) 2847de81dc7Seric char *progpath; 2857de81dc7Seric short flags; 2867de81dc7Seric char **argv; 2877de81dc7Seric bool forkflag; 2887de81dc7Seric { 2897de81dc7Seric register char *p; 2907de81dc7Seric register char **av; 2917de81dc7Seric extern char *makefile(); 2927de81dc7Seric register int i; 2931777fbcbSeric auto int st; 294cdc1aa65Seric register char **nav; 2957de81dc7Seric 2967de81dc7Seric if (*argv == NULL) 2977de81dc7Seric return (-1); 298c4432be4Seric 299c4432be4Seric /* 300172147faSeric ** Fork if appropriate. 301adf8f7d4Seric */ 302adf8f7d4Seric 3037de81dc7Seric if (forkflag) 3047de81dc7Seric { 305f0cc3246Seric # ifdef DEBUG 306f0cc3246Seric if (Debug) 307f0cc3246Seric printf("Forking\n"); 308f0cc3246Seric # endif 3097de81dc7Seric i = fork(); 3107de81dc7Seric if (i < 0) 3117de81dc7Seric { 3127de81dc7Seric fprintf(stderr, "Sccs: cannot fork"); 3137de81dc7Seric exit(EX_OSERR); 3147de81dc7Seric } 3157de81dc7Seric else if (i > 0) 3161777fbcbSeric { 3171777fbcbSeric wait(&st); 3181777fbcbSeric return (st); 3191777fbcbSeric } 3207de81dc7Seric } 3217de81dc7Seric 3227de81dc7Seric /* 323172147faSeric ** Build new argument vector. 324172147faSeric */ 325172147faSeric 326172147faSeric /* copy program filename arguments and flags */ 327cdc1aa65Seric nav = &argv[1]; 328172147faSeric av = argv; 329172147faSeric while ((p = *++av) != NULL) 330172147faSeric { 331172147faSeric if (!bitset(NO_SDOT, flags) && *p != '-') 332cdc1aa65Seric *nav = makefile(p); 333cdc1aa65Seric else 334cdc1aa65Seric *nav = p; 335cdc1aa65Seric if (*nav != NULL) 336cdc1aa65Seric nav++; 337172147faSeric } 338cdc1aa65Seric *nav = NULL; 339172147faSeric 340172147faSeric /* 3417de81dc7Seric ** Set protection as appropriate. 3427de81dc7Seric */ 3437de81dc7Seric 3447de81dc7Seric if (bitset(REALUSER, flags)) 3457de81dc7Seric setuid(getuid()); 3467de81dc7Seric 3477de81dc7Seric /* 348172147faSeric ** Call real SCCS program. 3497de81dc7Seric */ 3507de81dc7Seric 351172147faSeric execv(progpath, argv); 352adf8f7d4Seric fprintf(stderr, "Sccs: cannot execute "); 3537de81dc7Seric perror(progpath); 354adf8f7d4Seric exit(EX_UNAVAILABLE); 355adf8f7d4Seric } 356cdc1aa65Seric /* 357cdc1aa65Seric ** MAKEFILE -- make filename of SCCS file 358cdc1aa65Seric ** 359cdc1aa65Seric ** If the name passed is already the name of an SCCS file, 360cdc1aa65Seric ** just return it. Otherwise, munge the name into the name 361cdc1aa65Seric ** of the actual SCCS file. 362cdc1aa65Seric ** 363cdc1aa65Seric ** There are cases when it is not clear what you want to 364cdc1aa65Seric ** do. For example, if SccsPath is an absolute pathname 365cdc1aa65Seric ** and the name given is also an absolute pathname, we go 366cdc1aa65Seric ** for SccsPath (& only use the last component of the name 367cdc1aa65Seric ** passed) -- this is important for security reasons (if 368cdc1aa65Seric ** sccs is being used as a setuid front end), but not 369cdc1aa65Seric ** particularly intuitive. 370cdc1aa65Seric ** 371cdc1aa65Seric ** Parameters: 372cdc1aa65Seric ** name -- the file name to be munged. 373cdc1aa65Seric ** 374cdc1aa65Seric ** Returns: 375cdc1aa65Seric ** The pathname of the sccs file. 376cdc1aa65Seric ** NULL on error. 377cdc1aa65Seric ** 378cdc1aa65Seric ** Side Effects: 379cdc1aa65Seric ** none. 380cdc1aa65Seric */ 381adf8f7d4Seric 382adf8f7d4Seric char * 383adf8f7d4Seric makefile(name) 384adf8f7d4Seric char *name; 385adf8f7d4Seric { 386adf8f7d4Seric register char *p; 387adf8f7d4Seric register char c; 388adf8f7d4Seric char buf[512]; 389adf8f7d4Seric extern char *malloc(); 390cdc1aa65Seric extern char *rindex(); 391*7d70f5d8Seric extern bool isdir(); 392*7d70f5d8Seric register char *q; 393cdc1aa65Seric 394cdc1aa65Seric p = rindex(name, '/'); 395cdc1aa65Seric if (p == NULL) 396cdc1aa65Seric p = name; 397cdc1aa65Seric else 398cdc1aa65Seric p++; 399adf8f7d4Seric 400adf8f7d4Seric /* 401*7d70f5d8Seric ** See if the name can be used as-is. 402adf8f7d4Seric */ 403adf8f7d4Seric 404*7d70f5d8Seric if (SccsPath[0] != '/' || name[0] == '/' || strncmp(name, "./", 2) == 0) 405*7d70f5d8Seric { 406*7d70f5d8Seric if (strncmp(p, "s.", 2) == 0) 407*7d70f5d8Seric return (name); 408*7d70f5d8Seric if (isdir(name)) 409*7d70f5d8Seric return (name); 410*7d70f5d8Seric } 411cdc1aa65Seric 412cdc1aa65Seric /* 413*7d70f5d8Seric ** Create the actual pathname. 414cdc1aa65Seric */ 415cdc1aa65Seric 416*7d70f5d8Seric if (name[0] != '/') 417adf8f7d4Seric { 418cdc1aa65Seric strcpy(buf, SccsPath); 419cdc1aa65Seric strcat(buf, "/"); 420cdc1aa65Seric } 421cdc1aa65Seric else 422cdc1aa65Seric strcpy(buf, ""); 423cdc1aa65Seric strncat(buf, name, p - name); 424*7d70f5d8Seric q = &buf[strlen(buf)]; 425*7d70f5d8Seric strcpy(q, p); 426*7d70f5d8Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 427*7d70f5d8Seric { 428*7d70f5d8Seric strcpy(q, ""); 429*7d70f5d8Seric if (SccsPath[0] != '/' && name[0] == '/') 430cdc1aa65Seric { 431cdc1aa65Seric strcat(buf, SccsPath); 432cdc1aa65Seric strcat(buf, "/"); 433cdc1aa65Seric } 434cdc1aa65Seric strcat(buf, "s."); 435cdc1aa65Seric strcat(buf, p); 436cdc1aa65Seric } 437adf8f7d4Seric 438adf8f7d4Seric p = malloc(strlen(buf) + 1); 439adf8f7d4Seric if (p == NULL) 440adf8f7d4Seric { 441adf8f7d4Seric perror("Sccs: no mem"); 442adf8f7d4Seric exit(EX_OSERR); 443adf8f7d4Seric } 444adf8f7d4Seric strcpy(p, buf); 445adf8f7d4Seric return (p); 446adf8f7d4Seric } 447b2538d76Seric /* 448*7d70f5d8Seric ** ISDIR -- return true if the argument is a directory. 449*7d70f5d8Seric ** 450*7d70f5d8Seric ** Parameters: 451*7d70f5d8Seric ** name -- the pathname of the file to check. 452*7d70f5d8Seric ** 453*7d70f5d8Seric ** Returns: 454*7d70f5d8Seric ** TRUE if 'name' is a directory, FALSE otherwise. 455*7d70f5d8Seric ** 456*7d70f5d8Seric ** Side Effects: 457*7d70f5d8Seric ** none. 458*7d70f5d8Seric */ 459*7d70f5d8Seric 460*7d70f5d8Seric bool 461*7d70f5d8Seric isdir(name) 462*7d70f5d8Seric char *name; 463*7d70f5d8Seric { 464*7d70f5d8Seric struct stat stbuf; 465*7d70f5d8Seric 466*7d70f5d8Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 467*7d70f5d8Seric } 468*7d70f5d8Seric /* 469cdc1aa65Seric ** SAFEPATH -- determine whether a pathname is "safe" 470cdc1aa65Seric ** 471cdc1aa65Seric ** "Safe" pathnames only allow you to get deeper into the 472cdc1aa65Seric ** directory structure, i.e., full pathnames and ".." are 473cdc1aa65Seric ** not allowed. 474cdc1aa65Seric ** 475cdc1aa65Seric ** Parameters: 476cdc1aa65Seric ** p -- the name to check. 477cdc1aa65Seric ** 478cdc1aa65Seric ** Returns: 479cdc1aa65Seric ** TRUE -- if the path is safe. 480cdc1aa65Seric ** FALSE -- if the path is not safe. 481cdc1aa65Seric ** 482cdc1aa65Seric ** Side Effects: 483cdc1aa65Seric ** Prints a message if the path is not safe. 484cdc1aa65Seric */ 485cdc1aa65Seric 486cdc1aa65Seric bool 487cdc1aa65Seric safepath(p) 488cdc1aa65Seric register char *p; 489cdc1aa65Seric { 490cdc1aa65Seric extern char *index(); 491cdc1aa65Seric 492cdc1aa65Seric if (*p != '/') 493cdc1aa65Seric { 494cdc1aa65Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 495cdc1aa65Seric { 496cdc1aa65Seric p = index(p, '/'); 497cdc1aa65Seric if (p == NULL) 498cdc1aa65Seric return (TRUE); 499cdc1aa65Seric p++; 500cdc1aa65Seric } 501cdc1aa65Seric } 502cdc1aa65Seric 503cdc1aa65Seric printf("You may not use full pathnames or \"..\"\n"); 504cdc1aa65Seric return (FALSE); 505cdc1aa65Seric } 506cdc1aa65Seric /* 507b2538d76Seric ** CLEAN -- clean out recreatable files 508b2538d76Seric ** 509b2538d76Seric ** Any file for which an "s." file exists but no "p." file 510b2538d76Seric ** exists in the current directory is purged. 511b2538d76Seric ** 512b2538d76Seric ** Parameters: 5133a208c77Seric ** really -- if TRUE, remove everything. 5143a208c77Seric ** else, just report status. 515b2538d76Seric ** 516b2538d76Seric ** Returns: 517b2538d76Seric ** none. 518b2538d76Seric ** 519b2538d76Seric ** Side Effects: 520b2538d76Seric ** removes files in the current directory. 521b2538d76Seric */ 522b2538d76Seric 5233a208c77Seric clean(really) 5243a208c77Seric bool really; 525b2538d76Seric { 526b2538d76Seric struct direct dir; 527b2538d76Seric struct stat stbuf; 528b2538d76Seric char buf[100]; 529bf9c1e66Seric char pline[120]; 5303a208c77Seric register FILE *dirfd; 5313a208c77Seric register char *basefile; 5322caac8fdSeric bool gotedit; 533bf9c1e66Seric FILE *pfp; 534b2538d76Seric 535b2538d76Seric dirfd = fopen(SccsPath, "r"); 536b2538d76Seric if (dirfd == NULL) 537b2538d76Seric { 538b2538d76Seric fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); 539b2538d76Seric return; 540b2538d76Seric } 541b2538d76Seric 542b2538d76Seric /* 543b2538d76Seric ** Scan the SCCS directory looking for s. files. 544b2538d76Seric */ 545b2538d76Seric 5462caac8fdSeric gotedit = FALSE; 547b2538d76Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 548b2538d76Seric { 549c9c62fd0Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 550b2538d76Seric continue; 551b2538d76Seric 552b2538d76Seric /* got an s. file -- see if the p. file exists */ 553b2538d76Seric strcpy(buf, SccsPath); 554b2538d76Seric strcat(buf, "/p."); 5553a208c77Seric basefile = &buf[strlen(buf)]; 556c9c62fd0Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 557bf9c1e66Seric basefile[sizeof dir.d_name - 2] = '\0'; 558bf9c1e66Seric pfp = fopen(buf, "r"); 559bf9c1e66Seric if (pfp != NULL) 5603a208c77Seric { 561bf9c1e66Seric while (fgets(pline, sizeof pline, pfp) != NULL) 5622444cd3eSeric printf("%12s: being edited: %s", basefile, pline); 563bf9c1e66Seric fclose(pfp); 5642caac8fdSeric gotedit = TRUE; 565b2538d76Seric continue; 5663a208c77Seric } 567b2538d76Seric 568b2538d76Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 5693a208c77Seric if (really) 5703a208c77Seric { 571c9c62fd0Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 5723a208c77Seric buf[sizeof dir.d_name - 2] = '\0'; 573b2538d76Seric unlink(buf); 574b2538d76Seric } 5753a208c77Seric } 576b2538d76Seric 577b2538d76Seric fclose(dirfd); 5782caac8fdSeric if (!gotedit && !really) 5792444cd3eSeric printf("Nothing being edited\n"); 580b2538d76Seric } 581e39a5722Seric /* 582e39a5722Seric ** UNEDIT -- unedit a file 583e39a5722Seric ** 584e39a5722Seric ** Checks to see that the current user is actually editting 585e39a5722Seric ** the file and arranges that s/he is not editting it. 586e39a5722Seric ** 587e39a5722Seric ** Parameters: 5882444cd3eSeric ** fn -- the name of the file to be unedited. 589e39a5722Seric ** 590e39a5722Seric ** Returns: 591d51cd7e5Seric ** TRUE -- if the file was successfully unedited. 592d51cd7e5Seric ** FALSE -- if the file was not unedited for some 593d51cd7e5Seric ** reason. 594e39a5722Seric ** 595e39a5722Seric ** Side Effects: 596e39a5722Seric ** fn is removed 597e39a5722Seric ** entries are removed from pfile. 598e39a5722Seric */ 599e39a5722Seric 600d51cd7e5Seric bool 601e39a5722Seric unedit(fn) 602e39a5722Seric char *fn; 603e39a5722Seric { 604e39a5722Seric register FILE *pfp; 605e39a5722Seric char *pfn; 606e39a5722Seric static char tfn[] = "/tmp/sccsXXXXX"; 607e39a5722Seric FILE *tfp; 608e39a5722Seric register char *p; 609e39a5722Seric register char *q; 610e39a5722Seric bool delete = FALSE; 611e39a5722Seric bool others = FALSE; 612e39a5722Seric char *myname; 613e39a5722Seric extern char *getlogin(); 614e39a5722Seric struct pfile *pent; 615e39a5722Seric extern struct pfile *getpfile(); 616e39a5722Seric char buf[120]; 617e39a5722Seric 618e39a5722Seric /* make "s." filename & find the trailing component */ 619e39a5722Seric pfn = makefile(fn); 620cdc1aa65Seric if (pfn == NULL) 621cdc1aa65Seric return (FALSE); 622cdc1aa65Seric q = rindex(pfn, '/'); 623cdc1aa65Seric if (q == NULL) 624cdc1aa65Seric q = &pfn[-1]; 625cdc1aa65Seric if (q[1] != 's' || q[2] != '.') 626e39a5722Seric { 627e39a5722Seric fprintf(stderr, "Sccs: bad file name \"%s\"\n", fn); 628d51cd7e5Seric return (FALSE); 629e39a5722Seric } 630e39a5722Seric 631e39a5722Seric /* turn "s." into "p." */ 632e39a5722Seric *++q = 'p'; 633e39a5722Seric 634e39a5722Seric pfp = fopen(pfn, "r"); 635e39a5722Seric if (pfp == NULL) 636e39a5722Seric { 6372444cd3eSeric printf("%12s: not being edited\n", fn); 638d51cd7e5Seric return (FALSE); 639e39a5722Seric } 640e39a5722Seric 641e39a5722Seric /* 642e39a5722Seric ** Copy p-file to temp file, doing deletions as needed. 643e39a5722Seric */ 644e39a5722Seric 645e39a5722Seric mktemp(tfn); 646e39a5722Seric tfp = fopen(tfn, "w"); 647e39a5722Seric if (tfp == NULL) 648e39a5722Seric { 649e39a5722Seric fprintf(stderr, "Sccs: cannot create \"%s\"\n", tfn); 650e39a5722Seric exit(EX_OSERR); 651e39a5722Seric } 652e39a5722Seric 653e39a5722Seric myname = getlogin(); 654e39a5722Seric while ((pent = getpfile(pfp)) != NULL) 655e39a5722Seric { 656e39a5722Seric if (strcmp(pent->p_user, myname) == 0) 657e39a5722Seric { 658e39a5722Seric /* a match */ 659e39a5722Seric delete++; 660e39a5722Seric } 661e39a5722Seric else 662e39a5722Seric { 663e39a5722Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 664e39a5722Seric pent->p_nsid, pent->p_user, pent->p_date, 665e39a5722Seric pent->p_time); 666e39a5722Seric others++; 667e39a5722Seric } 668e39a5722Seric } 669e39a5722Seric 670e39a5722Seric /* do final cleanup */ 671e39a5722Seric if (others) 672e39a5722Seric { 673e39a5722Seric if (freopen(tfn, "r", tfp) == NULL) 674e39a5722Seric { 675e39a5722Seric fprintf(stderr, "Sccs: cannot reopen \"%s\"\n", tfn); 676e39a5722Seric exit(EX_OSERR); 677e39a5722Seric } 678e39a5722Seric if (freopen(pfn, "w", pfp) == NULL) 679e39a5722Seric { 680e39a5722Seric fprintf(stderr, "Sccs: cannot create \"%s\"\n", pfn); 681d51cd7e5Seric return (FALSE); 682e39a5722Seric } 683e39a5722Seric while (fgets(buf, sizeof buf, tfp) != NULL) 684e39a5722Seric fputs(buf, pfp); 685e39a5722Seric } 686e39a5722Seric else 687e39a5722Seric { 688e39a5722Seric unlink(pfn); 689e39a5722Seric } 690e39a5722Seric fclose(tfp); 691e39a5722Seric fclose(pfp); 692e39a5722Seric unlink(tfn); 693e39a5722Seric 694e39a5722Seric if (delete) 695e39a5722Seric { 696e39a5722Seric unlink(fn); 697e39a5722Seric printf("%12s: removed\n", fn); 698d51cd7e5Seric return (TRUE); 699e39a5722Seric } 700e39a5722Seric else 701e39a5722Seric { 7022444cd3eSeric printf("%12s: not being edited by you\n", fn); 703d51cd7e5Seric return (FALSE); 704e39a5722Seric } 705e39a5722Seric } 706e39a5722Seric /* 707e39a5722Seric ** GETPFILE -- get an entry from the p-file 708e39a5722Seric ** 709e39a5722Seric ** Parameters: 710e39a5722Seric ** pfp -- p-file file pointer 711e39a5722Seric ** 712e39a5722Seric ** Returns: 713e39a5722Seric ** pointer to p-file struct for next entry 714e39a5722Seric ** NULL on EOF or error 715e39a5722Seric ** 716e39a5722Seric ** Side Effects: 717e39a5722Seric ** Each call wipes out results of previous call. 718e39a5722Seric */ 719e39a5722Seric 720e39a5722Seric struct pfile * 721e39a5722Seric getpfile(pfp) 722e39a5722Seric FILE *pfp; 723e39a5722Seric { 724e39a5722Seric static struct pfile ent; 725e39a5722Seric static char buf[120]; 726e39a5722Seric register char *p; 727e39a5722Seric extern char *nextfield(); 728e39a5722Seric 729e39a5722Seric if (fgets(buf, sizeof buf, pfp) == NULL) 730e39a5722Seric return (NULL); 731e39a5722Seric 732e39a5722Seric ent.p_osid = p = buf; 733e39a5722Seric ent.p_nsid = p = nextfield(p); 734e39a5722Seric ent.p_user = p = nextfield(p); 735e39a5722Seric ent.p_date = p = nextfield(p); 736e39a5722Seric ent.p_time = p = nextfield(p); 737e39a5722Seric if (p == NULL || nextfield(p) != NULL) 738e39a5722Seric return (NULL); 739e39a5722Seric 740e39a5722Seric return (&ent); 741e39a5722Seric } 742e39a5722Seric 743e39a5722Seric 744e39a5722Seric char * 745e39a5722Seric nextfield(p) 746e39a5722Seric register char *p; 747e39a5722Seric { 748e39a5722Seric if (p == NULL || *p == '\0') 749e39a5722Seric return (NULL); 750e39a5722Seric while (*p != ' ' && *p != '\n' && *p != '\0') 751e39a5722Seric p++; 752e39a5722Seric if (*p == '\n' || *p == '\0') 753e39a5722Seric { 754e39a5722Seric *p = '\0'; 755e39a5722Seric return (NULL); 756e39a5722Seric } 757e39a5722Seric *p++ = '\0'; 758e39a5722Seric return (p); 759e39a5722Seric } 760