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*351Seric static char SccsId[] = "@(#)sccs.c 1.14 07/07/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, 56346Seric "clean", CLEAN, REALUSER, (char *) TRUE, 57346Seric "info", CLEAN, REALUSER, (char *) FALSE, 58200Seric NULL, -1, 0, NULL 59148Seric }; 60148Seric 61157Seric char *SccsPath = "SCCS"; /* pathname of SCCS files */ 62157Seric bool RealUser; /* if set, running as real user */ 63148Seric 64148Seric main(argc, argv) 65148Seric int argc; 66148Seric char **argv; 67148Seric { 68148Seric register char *p; 69262Seric extern struct sccsprog *lookup(); 70148Seric 71148Seric /* 72148Seric ** Detect and decode flags intended for this program. 73148Seric */ 74148Seric 75200Seric if (argc < 2) 76148Seric { 77200Seric fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); 78200Seric exit(EX_USAGE); 79200Seric } 80200Seric argv[argc] = NULL; 81200Seric 82262Seric if (lookup(argv[0]) == NULL) 83200Seric { 84262Seric while ((p = *++argv) != NULL) 85148Seric { 86262Seric if (*p != '-') 87262Seric break; 88262Seric switch (*++p) 89262Seric { 90262Seric case 'r': /* run as real user */ 91262Seric setuid(getuid()); 92262Seric RealUser++; 93262Seric break; 94148Seric 95262Seric case 'p': /* path of sccs files */ 96262Seric SccsPath = ++p; 97262Seric break; 98148Seric 99262Seric default: 100262Seric fprintf(stderr, "Sccs: unknown option -%s\n", p); 101262Seric break; 102262Seric } 103148Seric } 104262Seric if (SccsPath[0] == '\0') 105262Seric SccsPath = "."; 106148Seric } 107148Seric 108201Seric command(argv, FALSE); 109200Seric exit(EX_OK); 110200Seric } 111157Seric 112201Seric command(argv, forkflag) 113200Seric char **argv; 114201Seric bool forkflag; 115200Seric { 116200Seric register struct sccsprog *cmd; 117200Seric register char *p; 118201Seric register char *q; 119201Seric char buf[40]; 120262Seric extern struct sccsprog *lookup(); 121200Seric 122157Seric /* 123148Seric ** Look up command. 124200Seric ** At this point, argv points to the command name. 125148Seric */ 126148Seric 127200Seric p = *argv; 128262Seric cmd = lookup(p); 129262Seric if (cmd == NULL) 130148Seric { 131148Seric fprintf(stderr, "Sccs: Unknown command \"%s\"\n", p); 132148Seric exit(EX_USAGE); 133148Seric } 134148Seric 135148Seric /* 136200Seric ** Interpret operation associated with this command. 137157Seric */ 138157Seric 139200Seric switch (cmd->sccsoper) 140200Seric { 141200Seric case PROG: /* call an sccs prog */ 142201Seric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 143201Seric break; 144201Seric 145201Seric case CMACRO: /* command macro */ 146201Seric for (p = cmd->sccspath; *p != '\0'; p++) 147201Seric { 148201Seric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 149201Seric *q = *p; 150201Seric *q = '\0'; 151201Seric argv[0] = buf; 152201Seric command(argv, *p != '\0'); 153201Seric } 154201Seric fprintf(stderr, "Sccs internal error: CMACRO\n"); 155200Seric exit(EX_SOFTWARE); 156157Seric 157226Seric case FIX: /* fix a delta */ 158261Seric if (strcmpn(argv[1], "-r", 2) != 0) 159226Seric { 160226Seric fprintf(stderr, "Sccs: -r flag needed for fix command\n"); 161226Seric break; 162226Seric } 163226Seric xcommand(&argv[1], TRUE, "get", "-k", NULL); 164226Seric xcommand(&argv[1], TRUE, "rmdel", NULL); 165226Seric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 166226Seric fprintf(stderr, "Sccs internal error: FIX\n"); 167226Seric exit(EX_SOFTWARE); 168226Seric 169261Seric case CLEAN: 170346Seric clean((bool) cmd->sccspath); 171261Seric break; 172261Seric 173200Seric default: 174200Seric fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); 175200Seric exit(EX_SOFTWARE); 176200Seric } 177200Seric } 178262Seric /* 179262Seric ** LOOKUP -- look up an SCCS command name. 180262Seric ** 181262Seric ** Parameters: 182262Seric ** name -- the name of the command to look up. 183262Seric ** 184262Seric ** Returns: 185262Seric ** ptr to command descriptor for this command. 186262Seric ** NULL if no such entry. 187262Seric ** 188262Seric ** Side Effects: 189262Seric ** none. 190262Seric */ 191200Seric 192262Seric struct sccsprog * 193262Seric lookup(name) 194262Seric char *name; 195262Seric { 196262Seric register struct sccsprog *cmd; 197226Seric 198262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 199262Seric { 200262Seric if (strcmp(cmd->sccsname, name) == 0) 201262Seric return (cmd); 202262Seric } 203262Seric return (NULL); 204262Seric } 205262Seric 206262Seric 207226Seric xcommand(argv, forkflag, arg0) 208226Seric char **argv; 209226Seric bool forkflag; 210226Seric char *arg0; 211226Seric { 212226Seric register char **av; 213226Seric char *newargv[1000]; 214226Seric register char **np; 215226Seric 216226Seric np = newargv; 217226Seric for (av = &arg0; *av != NULL; av++) 218226Seric *np++ = *av; 219226Seric for (av = argv; *av != NULL; av++) 220226Seric *np++ = *av; 221226Seric *np = NULL; 222226Seric command(newargv, forkflag); 223226Seric } 224226Seric 225200Seric callprog(progpath, flags, argv, forkflag) 226200Seric char *progpath; 227200Seric short flags; 228200Seric char **argv; 229200Seric bool forkflag; 230200Seric { 231200Seric register char *p; 232200Seric register char **av; 233200Seric extern char *makefile(); 234200Seric register int i; 235201Seric auto int st; 236200Seric 237200Seric if (*argv == NULL) 238200Seric return (-1); 239200Seric 240157Seric /* 241226Seric ** Fork if appropriate. 242148Seric */ 243148Seric 244200Seric if (forkflag) 245200Seric { 246200Seric i = fork(); 247200Seric if (i < 0) 248200Seric { 249200Seric fprintf(stderr, "Sccs: cannot fork"); 250200Seric exit(EX_OSERR); 251200Seric } 252200Seric else if (i > 0) 253201Seric { 254201Seric wait(&st); 255201Seric return (st); 256201Seric } 257200Seric } 258200Seric 259200Seric /* 260226Seric ** Build new argument vector. 261226Seric */ 262226Seric 263226Seric /* copy program filename arguments and flags */ 264226Seric av = argv; 265226Seric while ((p = *++av) != NULL) 266226Seric { 267226Seric if (!bitset(NO_SDOT, flags) && *p != '-') 268226Seric *av = makefile(p); 269226Seric } 270226Seric 271226Seric /* 272200Seric ** Set protection as appropriate. 273200Seric */ 274200Seric 275200Seric if (bitset(REALUSER, flags)) 276200Seric setuid(getuid()); 277226Seric 278200Seric /* 279226Seric ** Call real SCCS program. 280200Seric */ 281200Seric 282226Seric execv(progpath, argv); 283148Seric fprintf(stderr, "Sccs: cannot execute "); 284200Seric perror(progpath); 285148Seric exit(EX_UNAVAILABLE); 286148Seric } 287148Seric 288148Seric 289148Seric char * 290148Seric makefile(name) 291148Seric char *name; 292148Seric { 293148Seric register char *p; 294148Seric register char c; 295148Seric char buf[512]; 296148Seric struct stat stbuf; 297148Seric extern char *malloc(); 298148Seric 299148Seric /* 300148Seric ** See if this filename should be used as-is. 301148Seric ** There are three conditions where this can occur. 302148Seric ** 1. The name already begins with "s.". 303148Seric ** 2. The name has a "/" in it somewhere. 304148Seric ** 3. The name references a directory. 305148Seric */ 306148Seric 307261Seric if (strcmpn(name, "s.", 2) == 0) 308148Seric return (name); 309148Seric for (p = name; (c = *p) != '\0'; p++) 310148Seric { 311148Seric if (c == '/') 312148Seric return (name); 313148Seric } 314148Seric if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR) 315148Seric return (name); 316148Seric 317148Seric /* 318148Seric ** Prepend the path of the sccs file. 319148Seric */ 320148Seric 321148Seric strcpy(buf, SccsPath); 322157Seric strcat(buf, "/s."); 323148Seric strcat(buf, name); 324148Seric p = malloc(strlen(buf) + 1); 325148Seric if (p == NULL) 326148Seric { 327148Seric perror("Sccs: no mem"); 328148Seric exit(EX_OSERR); 329148Seric } 330148Seric strcpy(p, buf); 331148Seric return (p); 332148Seric } 333261Seric /* 334261Seric ** CLEAN -- clean out recreatable files 335261Seric ** 336261Seric ** Any file for which an "s." file exists but no "p." file 337261Seric ** exists in the current directory is purged. 338261Seric ** 339261Seric ** Parameters: 340346Seric ** really -- if TRUE, remove everything. 341346Seric ** else, just report status. 342261Seric ** 343261Seric ** Returns: 344261Seric ** none. 345261Seric ** 346261Seric ** Side Effects: 347261Seric ** removes files in the current directory. 348261Seric */ 349261Seric 350346Seric clean(really) 351346Seric bool really; 352261Seric { 353261Seric struct direct dir; 354261Seric struct stat stbuf; 355261Seric char buf[100]; 356346Seric register FILE *dirfd; 357346Seric register char *basefile; 358*351Seric bool gotedit; 359261Seric 360261Seric dirfd = fopen(SccsPath, "r"); 361261Seric if (dirfd == NULL) 362261Seric { 363261Seric fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); 364261Seric return; 365261Seric } 366261Seric 367261Seric /* 368261Seric ** Scan the SCCS directory looking for s. files. 369261Seric */ 370261Seric 371*351Seric gotedit = FALSE; 372261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 373261Seric { 374261Seric if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0) 375261Seric continue; 376261Seric 377261Seric /* got an s. file -- see if the p. file exists */ 378261Seric strcpy(buf, SccsPath); 379261Seric strcat(buf, "/p."); 380346Seric basefile = &buf[strlen(buf)]; 381346Seric basefile[sizeof dir.d_name - 2] = '\0'; 382346Seric strcpyn(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 383261Seric if (stat(buf, &stbuf) >= 0) 384346Seric { 385346Seric printf("%s: being editted\n", basefile); 386*351Seric gotedit = TRUE; 387261Seric continue; 388346Seric } 389261Seric 390261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 391346Seric if (really) 392346Seric { 393346Seric strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2); 394346Seric buf[sizeof dir.d_name - 2] = '\0'; 395346Seric unlink(buf); 396346Seric } 397261Seric } 398261Seric 399261Seric fclose(dirfd); 400*351Seric if (!gotedit && !really) 401*351Seric printf("Nothing being editted\n"); 402261Seric } 403