1148Seric # include <stdio.h> 2148Seric # include <sys/types.h> 3148Seric # include <sys/stat.h> 4261Seric # include <sys/dir.h> 5148Seric # include <sysexits.h> 6202Seric # include <whoami.h> 7148Seric 8*262Seric static char SccsId[] = "@(#)sccs.c 1.12 06/09/80"; 9155Seric 10157Seric # define bitset(bit, word) ((bit) & (word)) 11157Seric 12157Seric typedef char bool; 13200Seric # define TRUE 1 14200Seric # define FALSE 0 15157Seric 16148Seric struct sccsprog 17148Seric { 18148Seric char *sccsname; /* name of SCCS routine */ 19200Seric short sccsoper; /* opcode, see below */ 20200Seric short sccsflags; /* flags, see below */ 21148Seric char *sccspath; /* pathname of binary implementing */ 22148Seric }; 23148Seric 24200Seric /* values for sccsoper */ 25200Seric # define PROG 0 /* call a program */ 26201Seric # define CMACRO 1 /* command substitution macro */ 27226Seric # define FIX 2 /* fix a delta */ 28261Seric # define CLEAN 3 /* clean out recreatable files */ 29200Seric 30157Seric /* bits for sccsflags */ 31200Seric # define NO_SDOT 0001 /* no s. on front of args */ 32200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 33148Seric 34202Seric # ifdef CSVAX 35202Seric # define PROGPATH(name) "/usr/local/name" 36202Seric # endif CSVAX 37202Seric 38202Seric # ifndef PROGPATH 39202Seric # define PROGPATH(name) "/usr/sccs/name" 40202Seric # endif PROGPATH 41202Seric 42148Seric struct sccsprog SccsProg[] = 43148Seric { 44202Seric "admin", PROG, REALUSER, PROGPATH(admin), 45202Seric "chghist", PROG, 0, PROGPATH(rmdel), 46202Seric "comb", PROG, 0, PROGPATH(comb), 47202Seric "delta", PROG, 0, PROGPATH(delta), 48202Seric "get", PROG, 0, PROGPATH(get), 49202Seric "help", PROG, NO_SDOT, PROGPATH(help), 50202Seric "prt", PROG, 0, PROGPATH(prt), 51202Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 52202Seric "what", PROG, NO_SDOT, PROGPATH(what), 53201Seric "del", CMACRO, 0, "delta/get", 54226Seric "delt", CMACRO, 0, "delta/get", 55226Seric "fix", FIX, 0, NULL, 56261Seric "clean", CLEAN, REALUSER, NULL, 57200Seric NULL, -1, 0, NULL 58148Seric }; 59148Seric 60157Seric char *SccsPath = "SCCS"; /* pathname of SCCS files */ 61157Seric bool RealUser; /* if set, running as real user */ 62148Seric 63148Seric main(argc, argv) 64148Seric int argc; 65148Seric char **argv; 66148Seric { 67148Seric register char *p; 68*262Seric extern struct sccsprog *lookup(); 69148Seric 70148Seric /* 71148Seric ** Detect and decode flags intended for this program. 72148Seric */ 73148Seric 74200Seric if (argc < 2) 75148Seric { 76200Seric fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); 77200Seric exit(EX_USAGE); 78200Seric } 79200Seric argv[argc] = NULL; 80200Seric 81*262Seric if (lookup(argv[0]) == NULL) 82200Seric { 83*262Seric while ((p = *++argv) != NULL) 84148Seric { 85*262Seric if (*p != '-') 86*262Seric break; 87*262Seric switch (*++p) 88*262Seric { 89*262Seric case 'r': /* run as real user */ 90*262Seric setuid(getuid()); 91*262Seric RealUser++; 92*262Seric break; 93148Seric 94*262Seric case 'p': /* path of sccs files */ 95*262Seric SccsPath = ++p; 96*262Seric break; 97148Seric 98*262Seric default: 99*262Seric fprintf(stderr, "Sccs: unknown option -%s\n", p); 100*262Seric break; 101*262Seric } 102148Seric } 103*262Seric if (SccsPath[0] == '\0') 104*262Seric SccsPath = "."; 105148Seric } 106148Seric 107201Seric command(argv, FALSE); 108200Seric exit(EX_OK); 109200Seric } 110157Seric 111201Seric command(argv, forkflag) 112200Seric char **argv; 113201Seric bool forkflag; 114200Seric { 115200Seric register struct sccsprog *cmd; 116200Seric register char *p; 117201Seric register char *q; 118201Seric char buf[40]; 119*262Seric extern struct sccsprog *lookup(); 120200Seric 121157Seric /* 122148Seric ** Look up command. 123200Seric ** At this point, argv points to the command name. 124148Seric */ 125148Seric 126200Seric p = *argv; 127*262Seric cmd = lookup(p); 128*262Seric if (cmd == NULL) 129148Seric { 130148Seric fprintf(stderr, "Sccs: Unknown command \"%s\"\n", p); 131148Seric exit(EX_USAGE); 132148Seric } 133148Seric 134148Seric /* 135200Seric ** Interpret operation associated with this command. 136157Seric */ 137157Seric 138200Seric switch (cmd->sccsoper) 139200Seric { 140200Seric case PROG: /* call an sccs prog */ 141201Seric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 142201Seric break; 143201Seric 144201Seric case CMACRO: /* command macro */ 145201Seric for (p = cmd->sccspath; *p != '\0'; p++) 146201Seric { 147201Seric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 148201Seric *q = *p; 149201Seric *q = '\0'; 150201Seric argv[0] = buf; 151201Seric command(argv, *p != '\0'); 152201Seric } 153201Seric fprintf(stderr, "Sccs internal error: CMACRO\n"); 154200Seric exit(EX_SOFTWARE); 155157Seric 156226Seric case FIX: /* fix a delta */ 157261Seric if (strcmpn(argv[1], "-r", 2) != 0) 158226Seric { 159226Seric fprintf(stderr, "Sccs: -r flag needed for fix command\n"); 160226Seric break; 161226Seric } 162226Seric xcommand(&argv[1], TRUE, "get", "-k", NULL); 163226Seric xcommand(&argv[1], TRUE, "rmdel", NULL); 164226Seric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 165226Seric fprintf(stderr, "Sccs internal error: FIX\n"); 166226Seric exit(EX_SOFTWARE); 167226Seric 168261Seric case CLEAN: 169261Seric clean(); 170261Seric break; 171261Seric 172200Seric default: 173200Seric fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); 174200Seric exit(EX_SOFTWARE); 175200Seric } 176200Seric } 177*262Seric /* 178*262Seric ** LOOKUP -- look up an SCCS command name. 179*262Seric ** 180*262Seric ** Parameters: 181*262Seric ** name -- the name of the command to look up. 182*262Seric ** 183*262Seric ** Returns: 184*262Seric ** ptr to command descriptor for this command. 185*262Seric ** NULL if no such entry. 186*262Seric ** 187*262Seric ** Side Effects: 188*262Seric ** none. 189*262Seric */ 190200Seric 191*262Seric struct sccsprog * 192*262Seric lookup(name) 193*262Seric char *name; 194*262Seric { 195*262Seric register struct sccsprog *cmd; 196226Seric 197*262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 198*262Seric { 199*262Seric if (strcmp(cmd->sccsname, name) == 0) 200*262Seric return (cmd); 201*262Seric } 202*262Seric return (NULL); 203*262Seric } 204*262Seric 205*262Seric 206226Seric xcommand(argv, forkflag, arg0) 207226Seric char **argv; 208226Seric bool forkflag; 209226Seric char *arg0; 210226Seric { 211226Seric register char **av; 212226Seric char *newargv[1000]; 213226Seric register char **np; 214226Seric 215226Seric np = newargv; 216226Seric for (av = &arg0; *av != NULL; av++) 217226Seric *np++ = *av; 218226Seric for (av = argv; *av != NULL; av++) 219226Seric *np++ = *av; 220226Seric *np = NULL; 221226Seric command(newargv, forkflag); 222226Seric } 223226Seric 224200Seric callprog(progpath, flags, argv, forkflag) 225200Seric char *progpath; 226200Seric short flags; 227200Seric char **argv; 228200Seric bool forkflag; 229200Seric { 230200Seric register char *p; 231200Seric register char **av; 232200Seric extern char *makefile(); 233200Seric register int i; 234201Seric auto int st; 235200Seric 236200Seric if (*argv == NULL) 237200Seric return (-1); 238200Seric 239157Seric /* 240226Seric ** Fork if appropriate. 241148Seric */ 242148Seric 243200Seric if (forkflag) 244200Seric { 245200Seric i = fork(); 246200Seric if (i < 0) 247200Seric { 248200Seric fprintf(stderr, "Sccs: cannot fork"); 249200Seric exit(EX_OSERR); 250200Seric } 251200Seric else if (i > 0) 252201Seric { 253201Seric wait(&st); 254201Seric return (st); 255201Seric } 256200Seric } 257200Seric 258200Seric /* 259226Seric ** Build new argument vector. 260226Seric */ 261226Seric 262226Seric /* copy program filename arguments and flags */ 263226Seric av = argv; 264226Seric while ((p = *++av) != NULL) 265226Seric { 266226Seric if (!bitset(NO_SDOT, flags) && *p != '-') 267226Seric *av = makefile(p); 268226Seric } 269226Seric 270226Seric /* 271200Seric ** Set protection as appropriate. 272200Seric */ 273200Seric 274200Seric if (bitset(REALUSER, flags)) 275200Seric setuid(getuid()); 276226Seric 277200Seric /* 278226Seric ** Call real SCCS program. 279200Seric */ 280200Seric 281226Seric execv(progpath, argv); 282148Seric fprintf(stderr, "Sccs: cannot execute "); 283200Seric perror(progpath); 284148Seric exit(EX_UNAVAILABLE); 285148Seric } 286148Seric 287148Seric 288148Seric char * 289148Seric makefile(name) 290148Seric char *name; 291148Seric { 292148Seric register char *p; 293148Seric register char c; 294148Seric char buf[512]; 295148Seric struct stat stbuf; 296148Seric extern char *malloc(); 297148Seric 298148Seric /* 299148Seric ** See if this filename should be used as-is. 300148Seric ** There are three conditions where this can occur. 301148Seric ** 1. The name already begins with "s.". 302148Seric ** 2. The name has a "/" in it somewhere. 303148Seric ** 3. The name references a directory. 304148Seric */ 305148Seric 306261Seric if (strcmpn(name, "s.", 2) == 0) 307148Seric return (name); 308148Seric for (p = name; (c = *p) != '\0'; p++) 309148Seric { 310148Seric if (c == '/') 311148Seric return (name); 312148Seric } 313148Seric if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR) 314148Seric return (name); 315148Seric 316148Seric /* 317148Seric ** Prepend the path of the sccs file. 318148Seric */ 319148Seric 320148Seric strcpy(buf, SccsPath); 321157Seric strcat(buf, "/s."); 322148Seric strcat(buf, name); 323148Seric p = malloc(strlen(buf) + 1); 324148Seric if (p == NULL) 325148Seric { 326148Seric perror("Sccs: no mem"); 327148Seric exit(EX_OSERR); 328148Seric } 329148Seric strcpy(p, buf); 330148Seric return (p); 331148Seric } 332261Seric /* 333261Seric ** CLEAN -- clean out recreatable files 334261Seric ** 335261Seric ** Any file for which an "s." file exists but no "p." file 336261Seric ** exists in the current directory is purged. 337261Seric ** 338261Seric ** Parameters: 339261Seric ** none. 340261Seric ** 341261Seric ** Returns: 342261Seric ** none. 343261Seric ** 344261Seric ** Side Effects: 345261Seric ** removes files in the current directory. 346261Seric */ 347261Seric 348261Seric clean() 349261Seric { 350261Seric struct direct dir; 351261Seric struct stat stbuf; 352261Seric char buf[100]; 353261Seric FILE *dirfd; 354261Seric 355261Seric dirfd = fopen(SccsPath, "r"); 356261Seric if (dirfd == NULL) 357261Seric { 358261Seric fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); 359261Seric return; 360261Seric } 361261Seric 362261Seric /* 363261Seric ** Scan the SCCS directory looking for s. files. 364261Seric */ 365261Seric 366261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 367261Seric { 368261Seric if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0) 369261Seric continue; 370261Seric 371261Seric /* got an s. file -- see if the p. file exists */ 372261Seric strcpy(buf, SccsPath); 373261Seric strcat(buf, "/p."); 374261Seric buf[strlen(buf) + sizeof dir.d_name - 2] = '\0'; 375261Seric strcatn(buf, &dir.d_name[2], sizeof dir.d_name - 2); 376261Seric if (stat(buf, &stbuf) >= 0) 377261Seric continue; 378261Seric 379261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 380261Seric buf[sizeof dir.d_name - 2] = '\0'; 381261Seric strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2); 382261Seric unlink(buf); 383261Seric } 384261Seric 385261Seric fclose(dirfd); 386261Seric } 387