1148Seric # include <stdio.h> 2148Seric # include <sys/types.h> 3148Seric # include <sys/stat.h> 4*261Seric # include <sys/dir.h> 5148Seric # include <sysexits.h> 6202Seric # include <whoami.h> 7148Seric 8*261Seric static char SccsId[] = "@(#)sccs.c 1.11 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 */ 28*261Seric # 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, 56*261Seric "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; 68148Seric 69148Seric /* 70148Seric ** Detect and decode flags intended for this program. 71148Seric */ 72148Seric 73200Seric if (argc < 2) 74148Seric { 75200Seric fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); 76200Seric exit(EX_USAGE); 77200Seric } 78200Seric argv[argc] = NULL; 79200Seric 80200Seric while ((p = *++argv) != NULL) 81200Seric { 82148Seric if (*p != '-') 83148Seric break; 84148Seric switch (*++p) 85148Seric { 86148Seric case 'r': /* run as real user */ 87148Seric setuid(getuid()); 88157Seric RealUser++; 89148Seric break; 90148Seric 91148Seric case 'p': /* path of sccs files */ 92148Seric SccsPath = ++p; 93148Seric break; 94148Seric 95148Seric default: 96148Seric fprintf(stderr, "Sccs: unknown option -%s\n", p); 97148Seric break; 98148Seric } 99148Seric } 100158Seric if (SccsPath[0] == '\0') 101158Seric SccsPath = "."; 102148Seric 103201Seric command(argv, FALSE); 104200Seric exit(EX_OK); 105200Seric } 106157Seric 107201Seric command(argv, forkflag) 108200Seric char **argv; 109201Seric bool forkflag; 110200Seric { 111200Seric register struct sccsprog *cmd; 112200Seric register char *p; 113201Seric register char *q; 114201Seric char buf[40]; 115200Seric 116157Seric /* 117148Seric ** Look up command. 118200Seric ** At this point, argv points to the command name. 119148Seric */ 120148Seric 121200Seric p = *argv; 122148Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 123148Seric { 124148Seric if (strcmp(cmd->sccsname, p) == 0) 125148Seric break; 126148Seric } 127148Seric if (cmd->sccsname == NULL) 128148Seric { 129148Seric fprintf(stderr, "Sccs: Unknown command \"%s\"\n", p); 130148Seric exit(EX_USAGE); 131148Seric } 132148Seric 133148Seric /* 134200Seric ** Interpret operation associated with this command. 135157Seric */ 136157Seric 137200Seric switch (cmd->sccsoper) 138200Seric { 139200Seric case PROG: /* call an sccs prog */ 140201Seric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 141201Seric break; 142201Seric 143201Seric case CMACRO: /* command macro */ 144201Seric for (p = cmd->sccspath; *p != '\0'; p++) 145201Seric { 146201Seric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 147201Seric *q = *p; 148201Seric *q = '\0'; 149201Seric argv[0] = buf; 150201Seric command(argv, *p != '\0'); 151201Seric } 152201Seric fprintf(stderr, "Sccs internal error: CMACRO\n"); 153200Seric exit(EX_SOFTWARE); 154157Seric 155226Seric case FIX: /* fix a delta */ 156*261Seric if (strcmpn(argv[1], "-r", 2) != 0) 157226Seric { 158226Seric fprintf(stderr, "Sccs: -r flag needed for fix command\n"); 159226Seric break; 160226Seric } 161226Seric xcommand(&argv[1], TRUE, "get", "-k", NULL); 162226Seric xcommand(&argv[1], TRUE, "rmdel", NULL); 163226Seric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 164226Seric fprintf(stderr, "Sccs internal error: FIX\n"); 165226Seric exit(EX_SOFTWARE); 166226Seric 167*261Seric case CLEAN: 168*261Seric clean(); 169*261Seric break; 170*261Seric 171200Seric default: 172200Seric fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); 173200Seric exit(EX_SOFTWARE); 174200Seric } 175200Seric } 176200Seric 177226Seric 178226Seric xcommand(argv, forkflag, arg0) 179226Seric char **argv; 180226Seric bool forkflag; 181226Seric char *arg0; 182226Seric { 183226Seric register char **av; 184226Seric char *newargv[1000]; 185226Seric register char **np; 186226Seric 187226Seric np = newargv; 188226Seric for (av = &arg0; *av != NULL; av++) 189226Seric *np++ = *av; 190226Seric for (av = argv; *av != NULL; av++) 191226Seric *np++ = *av; 192226Seric *np = NULL; 193226Seric command(newargv, forkflag); 194226Seric } 195226Seric 196200Seric callprog(progpath, flags, argv, forkflag) 197200Seric char *progpath; 198200Seric short flags; 199200Seric char **argv; 200200Seric bool forkflag; 201200Seric { 202200Seric register char *p; 203200Seric register char **av; 204200Seric extern char *makefile(); 205200Seric register int i; 206201Seric auto int st; 207200Seric 208200Seric if (*argv == NULL) 209200Seric return (-1); 210200Seric 211157Seric /* 212226Seric ** Fork if appropriate. 213148Seric */ 214148Seric 215200Seric if (forkflag) 216200Seric { 217200Seric i = fork(); 218200Seric if (i < 0) 219200Seric { 220200Seric fprintf(stderr, "Sccs: cannot fork"); 221200Seric exit(EX_OSERR); 222200Seric } 223200Seric else if (i > 0) 224201Seric { 225201Seric wait(&st); 226201Seric return (st); 227201Seric } 228200Seric } 229200Seric 230200Seric /* 231226Seric ** Build new argument vector. 232226Seric */ 233226Seric 234226Seric /* copy program filename arguments and flags */ 235226Seric av = argv; 236226Seric while ((p = *++av) != NULL) 237226Seric { 238226Seric if (!bitset(NO_SDOT, flags) && *p != '-') 239226Seric *av = makefile(p); 240226Seric } 241226Seric 242226Seric /* 243200Seric ** Set protection as appropriate. 244200Seric */ 245200Seric 246200Seric if (bitset(REALUSER, flags)) 247200Seric setuid(getuid()); 248226Seric 249200Seric /* 250226Seric ** Call real SCCS program. 251200Seric */ 252200Seric 253226Seric execv(progpath, argv); 254148Seric fprintf(stderr, "Sccs: cannot execute "); 255200Seric perror(progpath); 256148Seric exit(EX_UNAVAILABLE); 257148Seric } 258148Seric 259148Seric 260148Seric char * 261148Seric makefile(name) 262148Seric char *name; 263148Seric { 264148Seric register char *p; 265148Seric register char c; 266148Seric char buf[512]; 267148Seric struct stat stbuf; 268148Seric extern char *malloc(); 269148Seric 270148Seric /* 271148Seric ** See if this filename should be used as-is. 272148Seric ** There are three conditions where this can occur. 273148Seric ** 1. The name already begins with "s.". 274148Seric ** 2. The name has a "/" in it somewhere. 275148Seric ** 3. The name references a directory. 276148Seric */ 277148Seric 278*261Seric if (strcmpn(name, "s.", 2) == 0) 279148Seric return (name); 280148Seric for (p = name; (c = *p) != '\0'; p++) 281148Seric { 282148Seric if (c == '/') 283148Seric return (name); 284148Seric } 285148Seric if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR) 286148Seric return (name); 287148Seric 288148Seric /* 289148Seric ** Prepend the path of the sccs file. 290148Seric */ 291148Seric 292148Seric strcpy(buf, SccsPath); 293157Seric strcat(buf, "/s."); 294148Seric strcat(buf, name); 295148Seric p = malloc(strlen(buf) + 1); 296148Seric if (p == NULL) 297148Seric { 298148Seric perror("Sccs: no mem"); 299148Seric exit(EX_OSERR); 300148Seric } 301148Seric strcpy(p, buf); 302148Seric return (p); 303148Seric } 304*261Seric /* 305*261Seric ** CLEAN -- clean out recreatable files 306*261Seric ** 307*261Seric ** Any file for which an "s." file exists but no "p." file 308*261Seric ** exists in the current directory is purged. 309*261Seric ** 310*261Seric ** Parameters: 311*261Seric ** none. 312*261Seric ** 313*261Seric ** Returns: 314*261Seric ** none. 315*261Seric ** 316*261Seric ** Side Effects: 317*261Seric ** removes files in the current directory. 318*261Seric */ 319*261Seric 320*261Seric clean() 321*261Seric { 322*261Seric struct direct dir; 323*261Seric struct stat stbuf; 324*261Seric char buf[100]; 325*261Seric FILE *dirfd; 326*261Seric 327*261Seric dirfd = fopen(SccsPath, "r"); 328*261Seric if (dirfd == NULL) 329*261Seric { 330*261Seric fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); 331*261Seric return; 332*261Seric } 333*261Seric 334*261Seric /* 335*261Seric ** Scan the SCCS directory looking for s. files. 336*261Seric */ 337*261Seric 338*261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 339*261Seric { 340*261Seric if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0) 341*261Seric continue; 342*261Seric 343*261Seric /* got an s. file -- see if the p. file exists */ 344*261Seric strcpy(buf, SccsPath); 345*261Seric strcat(buf, "/p."); 346*261Seric buf[strlen(buf) + sizeof dir.d_name - 2] = '\0'; 347*261Seric strcatn(buf, &dir.d_name[2], sizeof dir.d_name - 2); 348*261Seric if (stat(buf, &stbuf) >= 0) 349*261Seric continue; 350*261Seric 351*261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 352*261Seric buf[sizeof dir.d_name - 2] = '\0'; 353*261Seric strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2); 354*261Seric unlink(buf); 355*261Seric } 356*261Seric 357*261Seric fclose(dirfd); 358*261Seric } 359