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*416Seric static char SccsId[] = "@(#)sccs.c 1.19 07/28/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 */ 29396Seric # define UNEDIT 4 /* unedit a file */ 30200Seric 31157Seric /* bits for sccsflags */ 32200Seric # define NO_SDOT 0001 /* no s. on front of args */ 33200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 34148Seric 35202Seric # ifdef CSVAX 36202Seric # define PROGPATH(name) "/usr/local/name" 37202Seric # endif CSVAX 38202Seric 39202Seric # ifndef PROGPATH 40202Seric # define PROGPATH(name) "/usr/sccs/name" 41202Seric # endif PROGPATH 42202Seric 43148Seric struct sccsprog SccsProg[] = 44148Seric { 45202Seric "admin", PROG, REALUSER, PROGPATH(admin), 46202Seric "chghist", PROG, 0, PROGPATH(rmdel), 47202Seric "comb", PROG, 0, PROGPATH(comb), 48202Seric "delta", PROG, 0, PROGPATH(delta), 49202Seric "get", PROG, 0, PROGPATH(get), 50202Seric "help", PROG, NO_SDOT, PROGPATH(help), 51202Seric "prt", PROG, 0, PROGPATH(prt), 52202Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 53202Seric "what", PROG, NO_SDOT, PROGPATH(what), 54393Seric "edit", CMACRO, 0, "get -e", 55393Seric "delget", CMACRO, 0, "delta/get", 56398Seric "deledit", CMACRO, 0, "delta/get -e", 57201Seric "del", CMACRO, 0, "delta/get", 58226Seric "delt", CMACRO, 0, "delta/get", 59226Seric "fix", FIX, 0, NULL, 60346Seric "clean", CLEAN, REALUSER, (char *) TRUE, 61346Seric "info", CLEAN, REALUSER, (char *) FALSE, 62396Seric "unedit", UNEDIT, 0, NULL, 63200Seric NULL, -1, 0, NULL 64148Seric }; 65148Seric 66396Seric struct pfile 67396Seric { 68396Seric char *p_osid; /* old SID */ 69396Seric char *p_nsid; /* new SID */ 70396Seric char *p_user; /* user who did edit */ 71396Seric char *p_date; /* date of get */ 72396Seric char *p_time; /* time of get */ 73396Seric }; 74396Seric 75157Seric char *SccsPath = "SCCS"; /* pathname of SCCS files */ 76157Seric bool RealUser; /* if set, running as real user */ 77393Seric # ifdef DEBUG 78393Seric bool Debug; /* turn on tracing */ 79393Seric # endif 80148Seric 81148Seric main(argc, argv) 82148Seric int argc; 83148Seric char **argv; 84148Seric { 85148Seric register char *p; 86262Seric extern struct sccsprog *lookup(); 87148Seric 88148Seric /* 89148Seric ** Detect and decode flags intended for this program. 90148Seric */ 91148Seric 92200Seric if (argc < 2) 93148Seric { 94200Seric fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); 95200Seric exit(EX_USAGE); 96200Seric } 97200Seric argv[argc] = NULL; 98200Seric 99262Seric if (lookup(argv[0]) == NULL) 100200Seric { 101262Seric while ((p = *++argv) != NULL) 102148Seric { 103262Seric if (*p != '-') 104262Seric break; 105262Seric switch (*++p) 106262Seric { 107262Seric case 'r': /* run as real user */ 108262Seric setuid(getuid()); 109262Seric RealUser++; 110262Seric break; 111148Seric 112262Seric case 'p': /* path of sccs files */ 113262Seric SccsPath = ++p; 114262Seric break; 115148Seric 116393Seric # ifdef DEBUG 117393Seric case 'T': /* trace */ 118393Seric Debug++; 119393Seric break; 120393Seric # endif 121393Seric 122262Seric default: 123262Seric fprintf(stderr, "Sccs: unknown option -%s\n", p); 124262Seric break; 125262Seric } 126148Seric } 127262Seric if (SccsPath[0] == '\0') 128262Seric SccsPath = "."; 129148Seric } 130148Seric 131201Seric command(argv, FALSE); 132200Seric exit(EX_OK); 133200Seric } 134157Seric 135201Seric command(argv, forkflag) 136200Seric char **argv; 137201Seric bool forkflag; 138200Seric { 139200Seric register struct sccsprog *cmd; 140200Seric register char *p; 141201Seric register char *q; 142201Seric char buf[40]; 143262Seric extern struct sccsprog *lookup(); 144393Seric char *nav[7]; 145393Seric char **avp; 146200Seric 147393Seric # ifdef DEBUG 148393Seric if (Debug) 149393Seric { 150393Seric printf("command:\n"); 151393Seric for (avp = argv; *avp != NULL; avp++) 152393Seric printf(" \"%s\"\n", *avp); 153393Seric } 154393Seric # endif 155393Seric 156157Seric /* 157148Seric ** Look up command. 158200Seric ** At this point, argv points to the command name. 159148Seric */ 160148Seric 161396Seric cmd = lookup(argv[0]); 162262Seric if (cmd == NULL) 163148Seric { 164396Seric fprintf(stderr, "Sccs: Unknown command \"%s\"\n", argv[0]); 165148Seric exit(EX_USAGE); 166148Seric } 167148Seric 168148Seric /* 169200Seric ** Interpret operation associated with this command. 170157Seric */ 171157Seric 172200Seric switch (cmd->sccsoper) 173200Seric { 174200Seric case PROG: /* call an sccs prog */ 175201Seric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 176201Seric break; 177201Seric 178201Seric case CMACRO: /* command macro */ 179201Seric for (p = cmd->sccspath; *p != '\0'; p++) 180201Seric { 181393Seric avp = nav; 182393Seric *avp++ = buf; 183201Seric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 184393Seric { 185393Seric if (*p == ' ') 186393Seric { 187393Seric *q = '\0'; 188393Seric *avp++ = &q[1]; 189393Seric } 190393Seric else 191393Seric *q = *p; 192393Seric } 193201Seric *q = '\0'; 194393Seric *avp = NULL; 195393Seric xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2], 196393Seric nav[3], nav[4], nav[5], nav[6]); 197201Seric } 198201Seric fprintf(stderr, "Sccs internal error: CMACRO\n"); 199200Seric exit(EX_SOFTWARE); 200157Seric 201226Seric case FIX: /* fix a delta */ 202261Seric if (strcmpn(argv[1], "-r", 2) != 0) 203226Seric { 204226Seric fprintf(stderr, "Sccs: -r flag needed for fix command\n"); 205226Seric break; 206226Seric } 207226Seric xcommand(&argv[1], TRUE, "get", "-k", NULL); 208226Seric xcommand(&argv[1], TRUE, "rmdel", NULL); 209226Seric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 210226Seric fprintf(stderr, "Sccs internal error: FIX\n"); 211226Seric exit(EX_SOFTWARE); 212226Seric 213261Seric case CLEAN: 214346Seric clean((bool) cmd->sccspath); 215261Seric break; 216261Seric 217396Seric case UNEDIT: 218396Seric for (avp = &argv[1]; *avp != NULL; avp++) 219396Seric unedit(*avp); 220396Seric break; 221396Seric 222200Seric default: 223200Seric fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); 224200Seric exit(EX_SOFTWARE); 225200Seric } 226200Seric } 227262Seric /* 228262Seric ** LOOKUP -- look up an SCCS command name. 229262Seric ** 230262Seric ** Parameters: 231262Seric ** name -- the name of the command to look up. 232262Seric ** 233262Seric ** Returns: 234262Seric ** ptr to command descriptor for this command. 235262Seric ** NULL if no such entry. 236262Seric ** 237262Seric ** Side Effects: 238262Seric ** none. 239262Seric */ 240200Seric 241262Seric struct sccsprog * 242262Seric lookup(name) 243262Seric char *name; 244262Seric { 245262Seric register struct sccsprog *cmd; 246226Seric 247262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 248262Seric { 249262Seric if (strcmp(cmd->sccsname, name) == 0) 250262Seric return (cmd); 251262Seric } 252262Seric return (NULL); 253262Seric } 254262Seric 255262Seric 256226Seric xcommand(argv, forkflag, arg0) 257226Seric char **argv; 258226Seric bool forkflag; 259226Seric char *arg0; 260226Seric { 261226Seric register char **av; 262226Seric char *newargv[1000]; 263226Seric register char **np; 264226Seric 265226Seric np = newargv; 266226Seric for (av = &arg0; *av != NULL; av++) 267226Seric *np++ = *av; 268226Seric for (av = argv; *av != NULL; av++) 269226Seric *np++ = *av; 270226Seric *np = NULL; 271226Seric command(newargv, forkflag); 272226Seric } 273226Seric 274200Seric callprog(progpath, flags, argv, forkflag) 275200Seric char *progpath; 276200Seric short flags; 277200Seric char **argv; 278200Seric bool forkflag; 279200Seric { 280200Seric register char *p; 281200Seric register char **av; 282200Seric extern char *makefile(); 283200Seric register int i; 284201Seric auto int st; 285200Seric 286200Seric if (*argv == NULL) 287200Seric return (-1); 288200Seric 289157Seric /* 290226Seric ** Fork if appropriate. 291148Seric */ 292148Seric 293200Seric if (forkflag) 294200Seric { 295393Seric # ifdef DEBUG 296393Seric if (Debug) 297393Seric printf("Forking\n"); 298393Seric # endif 299200Seric i = fork(); 300200Seric if (i < 0) 301200Seric { 302200Seric fprintf(stderr, "Sccs: cannot fork"); 303200Seric exit(EX_OSERR); 304200Seric } 305200Seric else if (i > 0) 306201Seric { 307201Seric wait(&st); 308201Seric return (st); 309201Seric } 310200Seric } 311200Seric 312200Seric /* 313226Seric ** Build new argument vector. 314226Seric */ 315226Seric 316226Seric /* copy program filename arguments and flags */ 317226Seric av = argv; 318226Seric while ((p = *++av) != NULL) 319226Seric { 320226Seric if (!bitset(NO_SDOT, flags) && *p != '-') 321226Seric *av = makefile(p); 322226Seric } 323226Seric 324226Seric /* 325200Seric ** Set protection as appropriate. 326200Seric */ 327200Seric 328200Seric if (bitset(REALUSER, flags)) 329200Seric setuid(getuid()); 330226Seric 331200Seric /* 332226Seric ** Call real SCCS program. 333200Seric */ 334200Seric 335226Seric execv(progpath, argv); 336148Seric fprintf(stderr, "Sccs: cannot execute "); 337200Seric perror(progpath); 338148Seric exit(EX_UNAVAILABLE); 339148Seric } 340148Seric 341148Seric 342148Seric char * 343148Seric makefile(name) 344148Seric char *name; 345148Seric { 346148Seric register char *p; 347148Seric register char c; 348148Seric char buf[512]; 349148Seric struct stat stbuf; 350148Seric extern char *malloc(); 351148Seric 352148Seric /* 353148Seric ** See if this filename should be used as-is. 354148Seric ** There are three conditions where this can occur. 355148Seric ** 1. The name already begins with "s.". 356148Seric ** 2. The name has a "/" in it somewhere. 357148Seric ** 3. The name references a directory. 358148Seric */ 359148Seric 360261Seric if (strcmpn(name, "s.", 2) == 0) 361148Seric return (name); 362148Seric for (p = name; (c = *p) != '\0'; p++) 363148Seric { 364148Seric if (c == '/') 365148Seric return (name); 366148Seric } 367148Seric if (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR) 368148Seric return (name); 369148Seric 370148Seric /* 371148Seric ** Prepend the path of the sccs file. 372148Seric */ 373148Seric 374148Seric strcpy(buf, SccsPath); 375157Seric strcat(buf, "/s."); 376148Seric strcat(buf, name); 377148Seric p = malloc(strlen(buf) + 1); 378148Seric if (p == NULL) 379148Seric { 380148Seric perror("Sccs: no mem"); 381148Seric exit(EX_OSERR); 382148Seric } 383148Seric strcpy(p, buf); 384148Seric return (p); 385148Seric } 386261Seric /* 387261Seric ** CLEAN -- clean out recreatable files 388261Seric ** 389261Seric ** Any file for which an "s." file exists but no "p." file 390261Seric ** exists in the current directory is purged. 391261Seric ** 392261Seric ** Parameters: 393346Seric ** really -- if TRUE, remove everything. 394346Seric ** else, just report status. 395261Seric ** 396261Seric ** Returns: 397261Seric ** none. 398261Seric ** 399261Seric ** Side Effects: 400261Seric ** removes files in the current directory. 401261Seric */ 402261Seric 403346Seric clean(really) 404346Seric bool really; 405261Seric { 406261Seric struct direct dir; 407261Seric struct stat stbuf; 408261Seric char buf[100]; 409394Seric char pline[120]; 410346Seric register FILE *dirfd; 411346Seric register char *basefile; 412351Seric bool gotedit; 413394Seric FILE *pfp; 414261Seric 415261Seric dirfd = fopen(SccsPath, "r"); 416261Seric if (dirfd == NULL) 417261Seric { 418261Seric fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); 419261Seric return; 420261Seric } 421261Seric 422261Seric /* 423261Seric ** Scan the SCCS directory looking for s. files. 424261Seric */ 425261Seric 426351Seric gotedit = FALSE; 427261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 428261Seric { 429261Seric if (dir.d_ino == 0 || strcmpn(dir.d_name, "s.", 2) != 0) 430261Seric continue; 431261Seric 432261Seric /* got an s. file -- see if the p. file exists */ 433261Seric strcpy(buf, SccsPath); 434261Seric strcat(buf, "/p."); 435346Seric basefile = &buf[strlen(buf)]; 436394Seric strcpyn(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 437346Seric basefile[sizeof dir.d_name - 2] = '\0'; 438394Seric pfp = fopen(buf, "r"); 439394Seric if (pfp != NULL) 440346Seric { 441394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 442*416Seric printf("%12s: being edited: %s", basefile, pline); 443394Seric fclose(pfp); 444351Seric gotedit = TRUE; 445261Seric continue; 446346Seric } 447261Seric 448261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 449346Seric if (really) 450346Seric { 451346Seric strcpyn(buf, &dir.d_name[2], sizeof dir.d_name - 2); 452346Seric buf[sizeof dir.d_name - 2] = '\0'; 453346Seric unlink(buf); 454346Seric } 455261Seric } 456261Seric 457261Seric fclose(dirfd); 458351Seric if (!gotedit && !really) 459*416Seric printf("Nothing being edited\n"); 460261Seric } 461396Seric /* 462396Seric ** UNEDIT -- unedit a file 463396Seric ** 464396Seric ** Checks to see that the current user is actually editting 465396Seric ** the file and arranges that s/he is not editting it. 466396Seric ** 467396Seric ** Parameters: 468*416Seric ** fn -- the name of the file to be unedited. 469396Seric ** 470396Seric ** Returns: 471396Seric ** none. 472396Seric ** 473396Seric ** Side Effects: 474396Seric ** fn is removed 475396Seric ** entries are removed from pfile. 476396Seric */ 477396Seric 478396Seric unedit(fn) 479396Seric char *fn; 480396Seric { 481396Seric register FILE *pfp; 482396Seric char *pfn; 483396Seric static char tfn[] = "/tmp/sccsXXXXX"; 484396Seric FILE *tfp; 485396Seric register char *p; 486396Seric register char *q; 487396Seric bool delete = FALSE; 488396Seric bool others = FALSE; 489396Seric char *myname; 490396Seric extern char *getlogin(); 491396Seric struct pfile *pent; 492396Seric extern struct pfile *getpfile(); 493396Seric char buf[120]; 494396Seric 495396Seric /* make "s." filename & find the trailing component */ 496396Seric pfn = makefile(fn); 497396Seric q = &pfn[strlen(pfn) - 1]; 498396Seric while (q > pfn && *q != '/') 499396Seric q--; 500396Seric if (q <= pfn && (q[0] != 's' || q[1] != '.')) 501396Seric { 502396Seric fprintf(stderr, "Sccs: bad file name \"%s\"\n", fn); 503396Seric return; 504396Seric } 505396Seric 506396Seric /* turn "s." into "p." */ 507396Seric *++q = 'p'; 508396Seric 509396Seric pfp = fopen(pfn, "r"); 510396Seric if (pfp == NULL) 511396Seric { 512*416Seric printf("%12s: not being edited\n", fn); 513396Seric return; 514396Seric } 515396Seric 516396Seric /* 517396Seric ** Copy p-file to temp file, doing deletions as needed. 518396Seric */ 519396Seric 520396Seric mktemp(tfn); 521396Seric tfp = fopen(tfn, "w"); 522396Seric if (tfp == NULL) 523396Seric { 524396Seric fprintf(stderr, "Sccs: cannot create \"%s\"\n", tfn); 525396Seric exit(EX_OSERR); 526396Seric } 527396Seric 528396Seric myname = getlogin(); 529396Seric while ((pent = getpfile(pfp)) != NULL) 530396Seric { 531396Seric if (strcmp(pent->p_user, myname) == 0) 532396Seric { 533396Seric /* a match */ 534396Seric delete++; 535396Seric } 536396Seric else 537396Seric { 538396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 539396Seric pent->p_nsid, pent->p_user, pent->p_date, 540396Seric pent->p_time); 541396Seric others++; 542396Seric } 543396Seric } 544396Seric 545396Seric /* do final cleanup */ 546396Seric if (others) 547396Seric { 548396Seric if (freopen(tfn, "r", tfp) == NULL) 549396Seric { 550396Seric fprintf(stderr, "Sccs: cannot reopen \"%s\"\n", tfn); 551396Seric exit(EX_OSERR); 552396Seric } 553396Seric if (freopen(pfn, "w", pfp) == NULL) 554396Seric { 555396Seric fprintf(stderr, "Sccs: cannot create \"%s\"\n", pfn); 556396Seric return; 557396Seric } 558396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 559396Seric fputs(buf, pfp); 560396Seric } 561396Seric else 562396Seric { 563396Seric unlink(pfn); 564396Seric } 565396Seric fclose(tfp); 566396Seric fclose(pfp); 567396Seric unlink(tfn); 568396Seric 569396Seric if (delete) 570396Seric { 571396Seric unlink(fn); 572396Seric printf("%12s: removed\n", fn); 573396Seric } 574396Seric else 575396Seric { 576*416Seric printf("%12s: not being edited by you\n", fn); 577396Seric } 578396Seric } 579396Seric /* 580396Seric ** GETPFILE -- get an entry from the p-file 581396Seric ** 582396Seric ** Parameters: 583396Seric ** pfp -- p-file file pointer 584396Seric ** 585396Seric ** Returns: 586396Seric ** pointer to p-file struct for next entry 587396Seric ** NULL on EOF or error 588396Seric ** 589396Seric ** Side Effects: 590396Seric ** Each call wipes out results of previous call. 591396Seric */ 592396Seric 593396Seric struct pfile * 594396Seric getpfile(pfp) 595396Seric FILE *pfp; 596396Seric { 597396Seric static struct pfile ent; 598396Seric static char buf[120]; 599396Seric register char *p; 600396Seric extern char *nextfield(); 601396Seric 602396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 603396Seric return (NULL); 604396Seric 605396Seric ent.p_osid = p = buf; 606396Seric ent.p_nsid = p = nextfield(p); 607396Seric ent.p_user = p = nextfield(p); 608396Seric ent.p_date = p = nextfield(p); 609396Seric ent.p_time = p = nextfield(p); 610396Seric if (p == NULL || nextfield(p) != NULL) 611396Seric return (NULL); 612396Seric 613396Seric return (&ent); 614396Seric } 615396Seric 616396Seric 617396Seric char * 618396Seric nextfield(p) 619396Seric register char *p; 620396Seric { 621396Seric if (p == NULL || *p == '\0') 622396Seric return (NULL); 623396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 624396Seric p++; 625396Seric if (*p == '\n' || *p == '\0') 626396Seric { 627396Seric *p = '\0'; 628396Seric return (NULL); 629396Seric } 630396Seric *p++ = '\0'; 631396Seric return (p); 632396Seric } 633