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*f0cc3246Seric static char SccsId[] = "@(#)sccs.c 1.15 07/24/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 */ 297de81dc7Seric 30c4432be4Seric /* bits for sccsflags */ 317de81dc7Seric # define NO_SDOT 0001 /* no s. on front of args */ 327de81dc7Seric # define REALUSER 0002 /* protected (e.g., admin) */ 33adf8f7d4Seric 34b5d4f080Seric # ifdef CSVAX 35b5d4f080Seric # define PROGPATH(name) "/usr/local/name" 36b5d4f080Seric # endif CSVAX 37b5d4f080Seric 38b5d4f080Seric # ifndef PROGPATH 39b5d4f080Seric # define PROGPATH(name) "/usr/sccs/name" 40b5d4f080Seric # endif PROGPATH 41b5d4f080Seric 42adf8f7d4Seric struct sccsprog SccsProg[] = 43adf8f7d4Seric { 44b5d4f080Seric "admin", PROG, REALUSER, PROGPATH(admin), 45b5d4f080Seric "chghist", PROG, 0, PROGPATH(rmdel), 46b5d4f080Seric "comb", PROG, 0, PROGPATH(comb), 47b5d4f080Seric "delta", PROG, 0, PROGPATH(delta), 48b5d4f080Seric "get", PROG, 0, PROGPATH(get), 49b5d4f080Seric "help", PROG, NO_SDOT, PROGPATH(help), 50b5d4f080Seric "prt", PROG, 0, PROGPATH(prt), 51b5d4f080Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 52b5d4f080Seric "what", PROG, NO_SDOT, PROGPATH(what), 53*f0cc3246Seric "edit", CMACRO, 0, "get -e", 54*f0cc3246Seric "delget", CMACRO, 0, "delta/get", 55*f0cc3246Seric "deled", CMACRO, 0, "delta/get -e", 561777fbcbSeric "del", CMACRO, 0, "delta/get", 57172147faSeric "delt", CMACRO, 0, "delta/get", 58172147faSeric "fix", FIX, 0, NULL, 593a208c77Seric "clean", CLEAN, REALUSER, (char *) TRUE, 603a208c77Seric "info", CLEAN, REALUSER, (char *) FALSE, 617de81dc7Seric NULL, -1, 0, NULL 62adf8f7d4Seric }; 63adf8f7d4Seric 64c4432be4Seric char *SccsPath = "SCCS"; /* pathname of SCCS files */ 65c4432be4Seric bool RealUser; /* if set, running as real user */ 66*f0cc3246Seric # ifdef DEBUG 67*f0cc3246Seric bool Debug; /* turn on tracing */ 68*f0cc3246Seric # endif 69adf8f7d4Seric 70adf8f7d4Seric main(argc, argv) 71adf8f7d4Seric int argc; 72adf8f7d4Seric char **argv; 73adf8f7d4Seric { 74adf8f7d4Seric register char *p; 75b1ed8a43Seric extern struct sccsprog *lookup(); 76adf8f7d4Seric 77adf8f7d4Seric /* 78adf8f7d4Seric ** Detect and decode flags intended for this program. 79adf8f7d4Seric */ 80adf8f7d4Seric 817de81dc7Seric if (argc < 2) 82adf8f7d4Seric { 837de81dc7Seric fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); 847de81dc7Seric exit(EX_USAGE); 857de81dc7Seric } 867de81dc7Seric argv[argc] = NULL; 877de81dc7Seric 88b1ed8a43Seric if (lookup(argv[0]) == NULL) 89b1ed8a43Seric { 907de81dc7Seric while ((p = *++argv) != NULL) 917de81dc7Seric { 92adf8f7d4Seric if (*p != '-') 93adf8f7d4Seric break; 94adf8f7d4Seric switch (*++p) 95adf8f7d4Seric { 96adf8f7d4Seric case 'r': /* run as real user */ 97adf8f7d4Seric setuid(getuid()); 98c4432be4Seric RealUser++; 99adf8f7d4Seric break; 100adf8f7d4Seric 101adf8f7d4Seric case 'p': /* path of sccs files */ 102adf8f7d4Seric SccsPath = ++p; 103adf8f7d4Seric break; 104adf8f7d4Seric 105*f0cc3246Seric # ifdef DEBUG 106*f0cc3246Seric case 'T': /* trace */ 107*f0cc3246Seric Debug++; 108*f0cc3246Seric break; 109*f0cc3246Seric # endif 110*f0cc3246Seric 111adf8f7d4Seric default: 112adf8f7d4Seric fprintf(stderr, "Sccs: unknown option -%s\n", p); 113adf8f7d4Seric break; 114adf8f7d4Seric } 115adf8f7d4Seric } 1165cabffd9Seric if (SccsPath[0] == '\0') 1175cabffd9Seric SccsPath = "."; 118b1ed8a43Seric } 119adf8f7d4Seric 1201777fbcbSeric command(argv, FALSE); 1217de81dc7Seric exit(EX_OK); 1227de81dc7Seric } 1237de81dc7Seric 1241777fbcbSeric command(argv, forkflag) 1257de81dc7Seric char **argv; 1261777fbcbSeric bool forkflag; 1277de81dc7Seric { 1287de81dc7Seric register struct sccsprog *cmd; 1297de81dc7Seric register char *p; 1301777fbcbSeric register char *q; 1311777fbcbSeric char buf[40]; 132b1ed8a43Seric extern struct sccsprog *lookup(); 133*f0cc3246Seric char *nav[7]; 134*f0cc3246Seric char **avp; 135*f0cc3246Seric 136*f0cc3246Seric # ifdef DEBUG 137*f0cc3246Seric if (Debug) 138*f0cc3246Seric { 139*f0cc3246Seric printf("command:\n"); 140*f0cc3246Seric for (avp = argv; *avp != NULL; avp++) 141*f0cc3246Seric printf(" \"%s\"\n", *avp); 142*f0cc3246Seric } 143*f0cc3246Seric # endif 144c4432be4Seric 145c4432be4Seric /* 146adf8f7d4Seric ** Look up command. 1477de81dc7Seric ** At this point, argv points to the command name. 148adf8f7d4Seric */ 149adf8f7d4Seric 150*f0cc3246Seric cmd = lookup(*argv); 151b1ed8a43Seric if (cmd == NULL) 152adf8f7d4Seric { 153*f0cc3246Seric fprintf(stderr, "Sccs: Unknown command \"%s\"\n", *argv); 154adf8f7d4Seric exit(EX_USAGE); 155adf8f7d4Seric } 156adf8f7d4Seric 157adf8f7d4Seric /* 1587de81dc7Seric ** Interpret operation associated with this command. 159c4432be4Seric */ 160c4432be4Seric 1617de81dc7Seric switch (cmd->sccsoper) 1627de81dc7Seric { 1637de81dc7Seric case PROG: /* call an sccs prog */ 1641777fbcbSeric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 1651777fbcbSeric break; 1661777fbcbSeric 1671777fbcbSeric case CMACRO: /* command macro */ 1681777fbcbSeric for (p = cmd->sccspath; *p != '\0'; p++) 1691777fbcbSeric { 170*f0cc3246Seric avp = nav; 171*f0cc3246Seric *avp++ = buf; 1721777fbcbSeric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 173*f0cc3246Seric { 174*f0cc3246Seric if (*p == ' ') 175*f0cc3246Seric { 1761777fbcbSeric *q = '\0'; 177*f0cc3246Seric *avp++ = &q[1]; 178*f0cc3246Seric } 179*f0cc3246Seric else 180*f0cc3246Seric *q = *p; 181*f0cc3246Seric } 182*f0cc3246Seric *q = '\0'; 183*f0cc3246Seric *avp = NULL; 184*f0cc3246Seric xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2], 185*f0cc3246Seric nav[3], nav[4], nav[5], nav[6]); 1861777fbcbSeric } 1871777fbcbSeric fprintf(stderr, "Sccs internal error: CMACRO\n"); 1887de81dc7Seric exit(EX_SOFTWARE); 1897de81dc7Seric 190172147faSeric case FIX: /* fix a delta */ 191b2538d76Seric if (strcmpn(argv[1], "-r", 2) != 0) 192172147faSeric { 193172147faSeric fprintf(stderr, "Sccs: -r flag needed for fix command\n"); 194172147faSeric break; 195172147faSeric } 196172147faSeric xcommand(&argv[1], TRUE, "get", "-k", NULL); 197172147faSeric xcommand(&argv[1], TRUE, "rmdel", NULL); 198172147faSeric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 199172147faSeric fprintf(stderr, "Sccs internal error: FIX\n"); 200172147faSeric exit(EX_SOFTWARE); 201172147faSeric 202b2538d76Seric case CLEAN: 2033a208c77Seric clean((bool) cmd->sccspath); 204b2538d76Seric break; 205b2538d76Seric 2067de81dc7Seric default: 2077de81dc7Seric fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); 2087de81dc7Seric exit(EX_SOFTWARE); 2097de81dc7Seric } 2107de81dc7Seric } 211b1ed8a43Seric /* 212b1ed8a43Seric ** LOOKUP -- look up an SCCS command name. 213b1ed8a43Seric ** 214b1ed8a43Seric ** Parameters: 215b1ed8a43Seric ** name -- the name of the command to look up. 216b1ed8a43Seric ** 217b1ed8a43Seric ** Returns: 218b1ed8a43Seric ** ptr to command descriptor for this command. 219b1ed8a43Seric ** NULL if no such entry. 220b1ed8a43Seric ** 221b1ed8a43Seric ** Side Effects: 222b1ed8a43Seric ** none. 223b1ed8a43Seric */ 224b1ed8a43Seric 225b1ed8a43Seric struct sccsprog * 226b1ed8a43Seric lookup(name) 227b1ed8a43Seric char *name; 228b1ed8a43Seric { 229b1ed8a43Seric register struct sccsprog *cmd; 230b1ed8a43Seric 231b1ed8a43Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 232b1ed8a43Seric { 233b1ed8a43Seric if (strcmp(cmd->sccsname, name) == 0) 234b1ed8a43Seric return (cmd); 235b1ed8a43Seric } 236b1ed8a43Seric return (NULL); 237b1ed8a43Seric } 2387de81dc7Seric 239172147faSeric 240172147faSeric xcommand(argv, forkflag, arg0) 241172147faSeric char **argv; 242172147faSeric bool forkflag; 243172147faSeric char *arg0; 244172147faSeric { 245172147faSeric register char **av; 246172147faSeric char *newargv[1000]; 247172147faSeric register char **np; 248172147faSeric 249172147faSeric np = newargv; 250172147faSeric for (av = &arg0; *av != NULL; av++) 251172147faSeric *np++ = *av; 252172147faSeric for (av = argv; *av != NULL; av++) 253172147faSeric *np++ = *av; 254172147faSeric *np = NULL; 255172147faSeric command(newargv, forkflag); 256172147faSeric } 257172147faSeric 2587de81dc7Seric callprog(progpath, flags, argv, forkflag) 2597de81dc7Seric char *progpath; 2607de81dc7Seric short flags; 2617de81dc7Seric char **argv; 2627de81dc7Seric bool forkflag; 2637de81dc7Seric { 2647de81dc7Seric register char *p; 2657de81dc7Seric register char **av; 2667de81dc7Seric extern char *makefile(); 2677de81dc7Seric register int i; 2681777fbcbSeric auto int st; 2697de81dc7Seric 2707de81dc7Seric if (*argv == NULL) 2717de81dc7Seric return (-1); 272c4432be4Seric 273c4432be4Seric /* 274172147faSeric ** Fork if appropriate. 275adf8f7d4Seric */ 276adf8f7d4Seric 2777de81dc7Seric if (forkflag) 2787de81dc7Seric { 279*f0cc3246Seric # ifdef DEBUG 280*f0cc3246Seric if (Debug) 281*f0cc3246Seric printf("Forking\n"); 282*f0cc3246Seric # endif 2837de81dc7Seric i = fork(); 2847de81dc7Seric if (i < 0) 2857de81dc7Seric { 2867de81dc7Seric fprintf(stderr, "Sccs: cannot fork"); 2877de81dc7Seric exit(EX_OSERR); 2887de81dc7Seric } 2897de81dc7Seric else if (i > 0) 2901777fbcbSeric { 2911777fbcbSeric wait(&st); 2921777fbcbSeric return (st); 2931777fbcbSeric } 2947de81dc7Seric } 2957de81dc7Seric 2967de81dc7Seric /* 297172147faSeric ** Build new argument vector. 298172147faSeric */ 299172147faSeric 300172147faSeric /* copy program filename arguments and flags */ 301172147faSeric av = argv; 302172147faSeric while ((p = *++av) != NULL) 303172147faSeric { 304172147faSeric if (!bitset(NO_SDOT, flags) && *p != '-') 305172147faSeric *av = makefile(p); 306172147faSeric } 307172147faSeric 308172147faSeric /* 3097de81dc7Seric ** Set protection as appropriate. 3107de81dc7Seric */ 3117de81dc7Seric 3127de81dc7Seric if (bitset(REALUSER, flags)) 3137de81dc7Seric setuid(getuid()); 3147de81dc7Seric 3157de81dc7Seric /* 316172147faSeric ** Call real SCCS program. 3177de81dc7Seric */ 3187de81dc7Seric 319172147faSeric execv(progpath, argv); 320adf8f7d4Seric fprintf(stderr, "Sccs: cannot execute "); 3217de81dc7Seric perror(progpath); 322adf8f7d4Seric exit(EX_UNAVAILABLE); 323adf8f7d4Seric } 324adf8f7d4Seric 325adf8f7d4Seric 326adf8f7d4Seric char * 327adf8f7d4Seric makefile(name) 328adf8f7d4Seric char *name; 329adf8f7d4Seric { 330adf8f7d4Seric register char *p; 331adf8f7d4Seric register char c; 332adf8f7d4Seric char buf[512]; 333adf8f7d4Seric struct stat stbuf; 334adf8f7d4Seric extern char *malloc(); 335adf8f7d4Seric 336adf8f7d4Seric /* 337adf8f7d4Seric ** See if this filename should be used as-is. 338adf8f7d4Seric ** There are three conditions where this can occur. 339adf8f7d4Seric ** 1. The name already begins with "s.". 340adf8f7d4Seric ** 2. The name has a "/" in it somewhere. 341adf8f7d4Seric ** 3. The name references a directory. 342adf8f7d4Seric */ 343adf8f7d4Seric 344b2538d76Seric if (strcmpn(name, "s.", 2) == 0) 345adf8f7d4Seric return (name); 346adf8f7d4Seric for (p = name; (c = *p) != '\0'; p++) 347adf8f7d4Seric { 348adf8f7d4Seric if (c == '/') 349adf8f7d4Seric return (name); 350adf8f7d4Seric } 351adf8f7d4Seric if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR) 352adf8f7d4Seric return (name); 353adf8f7d4Seric 354adf8f7d4Seric /* 355adf8f7d4Seric ** Prepend the path of the sccs file. 356adf8f7d4Seric */ 357adf8f7d4Seric 358adf8f7d4Seric strcpy(buf, SccsPath); 359c4432be4Seric strcat(buf, "/s."); 360adf8f7d4Seric strcat(buf, name); 361adf8f7d4Seric p = malloc(strlen(buf) + 1); 362adf8f7d4Seric if (p == NULL) 363adf8f7d4Seric { 364adf8f7d4Seric perror("Sccs: no mem"); 365adf8f7d4Seric exit(EX_OSERR); 366adf8f7d4Seric } 367adf8f7d4Seric strcpy(p, buf); 368adf8f7d4Seric return (p); 369adf8f7d4Seric } 370b2538d76Seric /* 371b2538d76Seric ** CLEAN -- clean out recreatable files 372b2538d76Seric ** 373b2538d76Seric ** Any file for which an "s." file exists but no "p." file 374b2538d76Seric ** exists in the current directory is purged. 375b2538d76Seric ** 376b2538d76Seric ** Parameters: 3773a208c77Seric ** really -- if TRUE, remove everything. 3783a208c77Seric ** else, just report status. 379b2538d76Seric ** 380b2538d76Seric ** Returns: 381b2538d76Seric ** none. 382b2538d76Seric ** 383b2538d76Seric ** Side Effects: 384b2538d76Seric ** removes files in the current directory. 385b2538d76Seric */ 386b2538d76Seric 3873a208c77Seric clean(really) 3883a208c77Seric bool really; 389b2538d76Seric { 390b2538d76Seric struct direct dir; 391b2538d76Seric struct stat stbuf; 392b2538d76Seric char buf[100]; 3933a208c77Seric register FILE *dirfd; 3943a208c77Seric register char *basefile; 3952caac8fdSeric bool gotedit; 396b2538d76Seric 397b2538d76Seric dirfd = fopen(SccsPath, "r"); 398b2538d76Seric if (dirfd == NULL) 399b2538d76Seric { 400b2538d76Seric fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); 401b2538d76Seric return; 402b2538d76Seric } 403b2538d76Seric 404b2538d76Seric /* 405b2538d76Seric ** Scan the SCCS directory looking for s. files. 406b2538d76Seric */ 407b2538d76Seric 4082caac8fdSeric gotedit = FALSE; 409b2538d76Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 410b2538d76Seric { 411b2538d76Seric if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0) 412b2538d76Seric continue; 413b2538d76Seric 414b2538d76Seric /* got an s. file -- see if the p. file exists */ 415b2538d76Seric strcpy(buf, SccsPath); 416b2538d76Seric strcat(buf, "/p."); 4173a208c77Seric basefile = &buf[strlen(buf)]; 4183a208c77Seric basefile[sizeof dir.d_name - 2] = '\0'; 4193a208c77Seric strcpyn(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 420b2538d76Seric if (stat(buf, &stbuf) >= 0) 4213a208c77Seric { 4223a208c77Seric printf("%s: being editted\n", basefile); 4232caac8fdSeric gotedit = TRUE; 424b2538d76Seric continue; 4253a208c77Seric } 426b2538d76Seric 427b2538d76Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 4283a208c77Seric if (really) 4293a208c77Seric { 430b2538d76Seric strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2); 4313a208c77Seric buf[sizeof dir.d_name - 2] = '\0'; 432b2538d76Seric unlink(buf); 433b2538d76Seric } 4343a208c77Seric } 435b2538d76Seric 436b2538d76Seric fclose(dirfd); 4372caac8fdSeric if (!gotedit && !really) 4382caac8fdSeric printf("Nothing being editted\n"); 439b2538d76Seric } 440