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*393Seric static char SccsId[] = "@(#)sccs.c 1.15 07/24/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), 53*393Seric "edit", CMACRO, 0, "get -e", 54*393Seric "delget", CMACRO, 0, "delta/get", 55*393Seric "deled", CMACRO, 0, "delta/get -e", 56201Seric "del", CMACRO, 0, "delta/get", 57226Seric "delt", CMACRO, 0, "delta/get", 58226Seric "fix", FIX, 0, NULL, 59346Seric "clean", CLEAN, REALUSER, (char *) TRUE, 60346Seric "info", CLEAN, REALUSER, (char *) FALSE, 61200Seric NULL, -1, 0, NULL 62148Seric }; 63148Seric 64157Seric char *SccsPath = "SCCS"; /* pathname of SCCS files */ 65157Seric bool RealUser; /* if set, running as real user */ 66*393Seric # ifdef DEBUG 67*393Seric bool Debug; /* turn on tracing */ 68*393Seric # endif 69148Seric 70148Seric main(argc, argv) 71148Seric int argc; 72148Seric char **argv; 73148Seric { 74148Seric register char *p; 75262Seric extern struct sccsprog *lookup(); 76148Seric 77148Seric /* 78148Seric ** Detect and decode flags intended for this program. 79148Seric */ 80148Seric 81200Seric if (argc < 2) 82148Seric { 83200Seric fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); 84200Seric exit(EX_USAGE); 85200Seric } 86200Seric argv[argc] = NULL; 87200Seric 88262Seric if (lookup(argv[0]) == NULL) 89200Seric { 90262Seric while ((p = *++argv) != NULL) 91148Seric { 92262Seric if (*p != '-') 93262Seric break; 94262Seric switch (*++p) 95262Seric { 96262Seric case 'r': /* run as real user */ 97262Seric setuid(getuid()); 98262Seric RealUser++; 99262Seric break; 100148Seric 101262Seric case 'p': /* path of sccs files */ 102262Seric SccsPath = ++p; 103262Seric break; 104148Seric 105*393Seric # ifdef DEBUG 106*393Seric case 'T': /* trace */ 107*393Seric Debug++; 108*393Seric break; 109*393Seric # endif 110*393Seric 111262Seric default: 112262Seric fprintf(stderr, "Sccs: unknown option -%s\n", p); 113262Seric break; 114262Seric } 115148Seric } 116262Seric if (SccsPath[0] == '\0') 117262Seric SccsPath = "."; 118148Seric } 119148Seric 120201Seric command(argv, FALSE); 121200Seric exit(EX_OK); 122200Seric } 123157Seric 124201Seric command(argv, forkflag) 125200Seric char **argv; 126201Seric bool forkflag; 127200Seric { 128200Seric register struct sccsprog *cmd; 129200Seric register char *p; 130201Seric register char *q; 131201Seric char buf[40]; 132262Seric extern struct sccsprog *lookup(); 133*393Seric char *nav[7]; 134*393Seric char **avp; 135200Seric 136*393Seric # ifdef DEBUG 137*393Seric if (Debug) 138*393Seric { 139*393Seric printf("command:\n"); 140*393Seric for (avp = argv; *avp != NULL; avp++) 141*393Seric printf(" \"%s\"\n", *avp); 142*393Seric } 143*393Seric # endif 144*393Seric 145157Seric /* 146148Seric ** Look up command. 147200Seric ** At this point, argv points to the command name. 148148Seric */ 149148Seric 150*393Seric cmd = lookup(*argv); 151262Seric if (cmd == NULL) 152148Seric { 153*393Seric fprintf(stderr, "Sccs: Unknown command \"%s\"\n", *argv); 154148Seric exit(EX_USAGE); 155148Seric } 156148Seric 157148Seric /* 158200Seric ** Interpret operation associated with this command. 159157Seric */ 160157Seric 161200Seric switch (cmd->sccsoper) 162200Seric { 163200Seric case PROG: /* call an sccs prog */ 164201Seric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 165201Seric break; 166201Seric 167201Seric case CMACRO: /* command macro */ 168201Seric for (p = cmd->sccspath; *p != '\0'; p++) 169201Seric { 170*393Seric avp = nav; 171*393Seric *avp++ = buf; 172201Seric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 173*393Seric { 174*393Seric if (*p == ' ') 175*393Seric { 176*393Seric *q = '\0'; 177*393Seric *avp++ = &q[1]; 178*393Seric } 179*393Seric else 180*393Seric *q = *p; 181*393Seric } 182201Seric *q = '\0'; 183*393Seric *avp = NULL; 184*393Seric xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2], 185*393Seric nav[3], nav[4], nav[5], nav[6]); 186201Seric } 187201Seric fprintf(stderr, "Sccs internal error: CMACRO\n"); 188200Seric exit(EX_SOFTWARE); 189157Seric 190226Seric case FIX: /* fix a delta */ 191261Seric if (strcmpn(argv[1], "-r", 2) != 0) 192226Seric { 193226Seric fprintf(stderr, "Sccs: -r flag needed for fix command\n"); 194226Seric break; 195226Seric } 196226Seric xcommand(&argv[1], TRUE, "get", "-k", NULL); 197226Seric xcommand(&argv[1], TRUE, "rmdel", NULL); 198226Seric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 199226Seric fprintf(stderr, "Sccs internal error: FIX\n"); 200226Seric exit(EX_SOFTWARE); 201226Seric 202261Seric case CLEAN: 203346Seric clean((bool) cmd->sccspath); 204261Seric break; 205261Seric 206200Seric default: 207200Seric fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); 208200Seric exit(EX_SOFTWARE); 209200Seric } 210200Seric } 211262Seric /* 212262Seric ** LOOKUP -- look up an SCCS command name. 213262Seric ** 214262Seric ** Parameters: 215262Seric ** name -- the name of the command to look up. 216262Seric ** 217262Seric ** Returns: 218262Seric ** ptr to command descriptor for this command. 219262Seric ** NULL if no such entry. 220262Seric ** 221262Seric ** Side Effects: 222262Seric ** none. 223262Seric */ 224200Seric 225262Seric struct sccsprog * 226262Seric lookup(name) 227262Seric char *name; 228262Seric { 229262Seric register struct sccsprog *cmd; 230226Seric 231262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 232262Seric { 233262Seric if (strcmp(cmd->sccsname, name) == 0) 234262Seric return (cmd); 235262Seric } 236262Seric return (NULL); 237262Seric } 238262Seric 239262Seric 240226Seric xcommand(argv, forkflag, arg0) 241226Seric char **argv; 242226Seric bool forkflag; 243226Seric char *arg0; 244226Seric { 245226Seric register char **av; 246226Seric char *newargv[1000]; 247226Seric register char **np; 248226Seric 249226Seric np = newargv; 250226Seric for (av = &arg0; *av != NULL; av++) 251226Seric *np++ = *av; 252226Seric for (av = argv; *av != NULL; av++) 253226Seric *np++ = *av; 254226Seric *np = NULL; 255226Seric command(newargv, forkflag); 256226Seric } 257226Seric 258200Seric callprog(progpath, flags, argv, forkflag) 259200Seric char *progpath; 260200Seric short flags; 261200Seric char **argv; 262200Seric bool forkflag; 263200Seric { 264200Seric register char *p; 265200Seric register char **av; 266200Seric extern char *makefile(); 267200Seric register int i; 268201Seric auto int st; 269200Seric 270200Seric if (*argv == NULL) 271200Seric return (-1); 272200Seric 273157Seric /* 274226Seric ** Fork if appropriate. 275148Seric */ 276148Seric 277200Seric if (forkflag) 278200Seric { 279*393Seric # ifdef DEBUG 280*393Seric if (Debug) 281*393Seric printf("Forking\n"); 282*393Seric # endif 283200Seric i = fork(); 284200Seric if (i < 0) 285200Seric { 286200Seric fprintf(stderr, "Sccs: cannot fork"); 287200Seric exit(EX_OSERR); 288200Seric } 289200Seric else if (i > 0) 290201Seric { 291201Seric wait(&st); 292201Seric return (st); 293201Seric } 294200Seric } 295200Seric 296200Seric /* 297226Seric ** Build new argument vector. 298226Seric */ 299226Seric 300226Seric /* copy program filename arguments and flags */ 301226Seric av = argv; 302226Seric while ((p = *++av) != NULL) 303226Seric { 304226Seric if (!bitset(NO_SDOT, flags) && *p != '-') 305226Seric *av = makefile(p); 306226Seric } 307226Seric 308226Seric /* 309200Seric ** Set protection as appropriate. 310200Seric */ 311200Seric 312200Seric if (bitset(REALUSER, flags)) 313200Seric setuid(getuid()); 314226Seric 315200Seric /* 316226Seric ** Call real SCCS program. 317200Seric */ 318200Seric 319226Seric execv(progpath, argv); 320148Seric fprintf(stderr, "Sccs: cannot execute "); 321200Seric perror(progpath); 322148Seric exit(EX_UNAVAILABLE); 323148Seric } 324148Seric 325148Seric 326148Seric char * 327148Seric makefile(name) 328148Seric char *name; 329148Seric { 330148Seric register char *p; 331148Seric register char c; 332148Seric char buf[512]; 333148Seric struct stat stbuf; 334148Seric extern char *malloc(); 335148Seric 336148Seric /* 337148Seric ** See if this filename should be used as-is. 338148Seric ** There are three conditions where this can occur. 339148Seric ** 1. The name already begins with "s.". 340148Seric ** 2. The name has a "/" in it somewhere. 341148Seric ** 3. The name references a directory. 342148Seric */ 343148Seric 344261Seric if (strcmpn(name, "s.", 2) == 0) 345148Seric return (name); 346148Seric for (p = name; (c = *p) != '\0'; p++) 347148Seric { 348148Seric if (c == '/') 349148Seric return (name); 350148Seric } 351148Seric if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR) 352148Seric return (name); 353148Seric 354148Seric /* 355148Seric ** Prepend the path of the sccs file. 356148Seric */ 357148Seric 358148Seric strcpy(buf, SccsPath); 359157Seric strcat(buf, "/s."); 360148Seric strcat(buf, name); 361148Seric p = malloc(strlen(buf) + 1); 362148Seric if (p == NULL) 363148Seric { 364148Seric perror("Sccs: no mem"); 365148Seric exit(EX_OSERR); 366148Seric } 367148Seric strcpy(p, buf); 368148Seric return (p); 369148Seric } 370261Seric /* 371261Seric ** CLEAN -- clean out recreatable files 372261Seric ** 373261Seric ** Any file for which an "s." file exists but no "p." file 374261Seric ** exists in the current directory is purged. 375261Seric ** 376261Seric ** Parameters: 377346Seric ** really -- if TRUE, remove everything. 378346Seric ** else, just report status. 379261Seric ** 380261Seric ** Returns: 381261Seric ** none. 382261Seric ** 383261Seric ** Side Effects: 384261Seric ** removes files in the current directory. 385261Seric */ 386261Seric 387346Seric clean(really) 388346Seric bool really; 389261Seric { 390261Seric struct direct dir; 391261Seric struct stat stbuf; 392261Seric char buf[100]; 393346Seric register FILE *dirfd; 394346Seric register char *basefile; 395351Seric bool gotedit; 396261Seric 397261Seric dirfd = fopen(SccsPath, "r"); 398261Seric if (dirfd == NULL) 399261Seric { 400261Seric fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); 401261Seric return; 402261Seric } 403261Seric 404261Seric /* 405261Seric ** Scan the SCCS directory looking for s. files. 406261Seric */ 407261Seric 408351Seric gotedit = FALSE; 409261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 410261Seric { 411261Seric if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0) 412261Seric continue; 413261Seric 414261Seric /* got an s. file -- see if the p. file exists */ 415261Seric strcpy(buf, SccsPath); 416261Seric strcat(buf, "/p."); 417346Seric basefile = &buf[strlen(buf)]; 418346Seric basefile[sizeof dir.d_name - 2] = '\0'; 419346Seric strcpyn(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 420261Seric if (stat(buf, &stbuf) >= 0) 421346Seric { 422346Seric printf("%s: being editted\n", basefile); 423351Seric gotedit = TRUE; 424261Seric continue; 425346Seric } 426261Seric 427261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 428346Seric if (really) 429346Seric { 430346Seric strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2); 431346Seric buf[sizeof dir.d_name - 2] = '\0'; 432346Seric unlink(buf); 433346Seric } 434261Seric } 435261Seric 436261Seric fclose(dirfd); 437351Seric if (!gotedit && !really) 438351Seric printf("Nothing being editted\n"); 439261Seric } 440