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 8828Seric /* 9828Seric ** SCCS.C -- human-oriented front end to the SCCS system. 10828Seric ** 11828Seric ** Without trying to add any functionality to speak of, this 12828Seric ** program tries to make SCCS a little more accessible to human 13828Seric ** types. The main thing it does is automatically put the 14828Seric ** string "SCCS/s." on the front of names. Also, it has a 15828Seric ** couple of things that are designed to shorten frequent 16828Seric ** combinations, e.g., "delget" which expands to a "delta" 17828Seric ** and a "get". 18828Seric ** 19828Seric ** This program can also function as a setuid front end. 20828Seric ** To do this, you should copy the source, renaming it to 21828Seric ** whatever you want, e.g., "syssccs". Change any defaults 22828Seric ** in the program (e.g., syssccs might default -d to 23828Seric ** "/usr/src/sys"). Then recompile and put the result 24828Seric ** as setuid to whomever you want. In this mode, sccs 25828Seric ** knows to not run setuid for certain programs in order 26828Seric ** to preserve security, and so forth. 27828Seric ** 28828Seric ** Usage: 29828Seric ** sccs [flags] command [args] 30828Seric ** 31828Seric ** Flags: 32828Seric ** -d<dir> <dir> represents a directory to search 33828Seric ** out of. It should be a full pathname 34828Seric ** for general usage. E.g., if <dir> is 35828Seric ** "/usr/src/sys", then a reference to the 36828Seric ** file "dev/bio.c" becomes a reference to 37828Seric ** "/usr/src/sys/dev/bio.c". 38828Seric ** -p<path> prepends <path> to the final component 39828Seric ** of the pathname. By default, this is 40828Seric ** "SCCS". For example, in the -d example 41828Seric ** above, the path then gets modified to 42828Seric ** "/usr/src/sys/dev/SCCS/s.bio.c". In 43828Seric ** more common usage (without the -d flag), 44828Seric ** "prog.c" would get modified to 45828Seric ** "SCCS/s.prog.c". In both cases, the 46828Seric ** "s." gets automatically prepended. 47828Seric ** -r run as the real user. 48828Seric ** 49828Seric ** Commands: 50828Seric ** admin, 51828Seric ** get, 52828Seric ** delta, 53828Seric ** rmdel, 54828Seric ** chghist, 55828Seric ** etc. Straight out of SCCS; only difference 56828Seric ** is that pathnames get modified as 57828Seric ** described above. 58828Seric ** edit Macro for "get -e". 59828Seric ** unedit Removes a file being edited, knowing 60828Seric ** about p-files, etc. 61828Seric ** delget Macro for "delta" followed by "get". 62828Seric ** deledit Macro for "delta" followed by "get -e". 63828Seric ** info Tell what files being edited. 64828Seric ** clean Remove all files that can be 65828Seric ** regenerated from SCCS files. 661205Seric ** check Like info, but return exit status, for 67828Seric ** use in makefiles. 68828Seric ** fix Remove a top delta & reedit, but save 69828Seric ** the previous changes in that delta. 70828Seric ** 71828Seric ** Compilation Flags: 72828Seric ** UIDUSER -- determine who the user is by looking at the 73828Seric ** uid rather than the login name -- for machines 74828Seric ** where SCCS gets the user in this way. 751205Seric ** SRCDIR -- if defined, forces the -d flag to take on 761205Seric ** this value. This is so that the setuid 771205Seric ** aspects of this program cannot be abused. 78828Seric ** 79828Seric ** Compilation Instructions: 80828Seric ** cc -O -n -s sccs.c 81828Seric ** 82828Seric ** Author: 83828Seric ** Eric Allman, UCB/INGRES 84828Seric */ 85155Seric 86828Seric # ifdef CSVAX 87828Seric # define UIDUSER 88828Seric # endif 89828Seric 90*1207Seric static char SccsId[] = "@(#)sccs.c 1.28 10/03/80"; 91828Seric 92157Seric # define bitset(bit, word) ((bit) & (word)) 93157Seric 94157Seric typedef char bool; 95200Seric # define TRUE 1 96200Seric # define FALSE 0 97157Seric 98828Seric # ifdef UIDUSER 99828Seric # include <pwd.h> 100828Seric # endif UIDUSER 101828Seric 1021205Seric char MyName[] = "sccs"; 1031205Seric 104148Seric struct sccsprog 105148Seric { 106148Seric char *sccsname; /* name of SCCS routine */ 107200Seric short sccsoper; /* opcode, see below */ 108200Seric short sccsflags; /* flags, see below */ 109148Seric char *sccspath; /* pathname of binary implementing */ 110148Seric }; 111148Seric 112200Seric /* values for sccsoper */ 113200Seric # define PROG 0 /* call a program */ 114201Seric # define CMACRO 1 /* command substitution macro */ 115226Seric # define FIX 2 /* fix a delta */ 116261Seric # define CLEAN 3 /* clean out recreatable files */ 117396Seric # define UNEDIT 4 /* unedit a file */ 118200Seric 119157Seric /* bits for sccsflags */ 120200Seric # define NO_SDOT 0001 /* no s. on front of args */ 121200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 122148Seric 123819Seric /* modes for the "clean", "info", "check" ops */ 124819Seric # define CLEANC 0 /* clean command */ 125819Seric # define INFOC 1 /* info command */ 126819Seric # define CHECKC 2 /* check command */ 127819Seric 128202Seric # ifdef CSVAX 129202Seric # define PROGPATH(name) "/usr/local/name" 130202Seric # endif CSVAX 131202Seric 132202Seric # ifndef PROGPATH 133202Seric # define PROGPATH(name) "/usr/sccs/name" 134202Seric # endif PROGPATH 135202Seric 136148Seric struct sccsprog SccsProg[] = 137148Seric { 138202Seric "admin", PROG, REALUSER, PROGPATH(admin), 139202Seric "chghist", PROG, 0, PROGPATH(rmdel), 140202Seric "comb", PROG, 0, PROGPATH(comb), 141202Seric "delta", PROG, 0, PROGPATH(delta), 142202Seric "get", PROG, 0, PROGPATH(get), 143202Seric "help", PROG, NO_SDOT, PROGPATH(help), 144202Seric "prt", PROG, 0, PROGPATH(prt), 145202Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 146202Seric "what", PROG, NO_SDOT, PROGPATH(what), 147393Seric "edit", CMACRO, 0, "get -e", 148393Seric "delget", CMACRO, 0, "delta/get", 149398Seric "deledit", CMACRO, 0, "delta/get -e", 150226Seric "fix", FIX, 0, NULL, 151819Seric "clean", CLEAN, REALUSER, (char *) CLEANC, 152819Seric "info", CLEAN, REALUSER, (char *) INFOC, 153819Seric "check", CLEAN, REALUSER, (char *) CHECKC, 154396Seric "unedit", UNEDIT, 0, NULL, 155200Seric NULL, -1, 0, NULL 156148Seric }; 157148Seric 158396Seric struct pfile 159396Seric { 160396Seric char *p_osid; /* old SID */ 161396Seric char *p_nsid; /* new SID */ 162396Seric char *p_user; /* user who did edit */ 163396Seric char *p_date; /* date of get */ 164396Seric char *p_time; /* time of get */ 165396Seric }; 166396Seric 167157Seric char *SccsPath = "SCCS"; /* pathname of SCCS files */ 1681205Seric # ifdef SRCDIR 1691205Seric char *SccsDir = SRCDIR; /* directory to begin search from */ 1701205Seric # else 171588Seric char *SccsDir = ""; /* directory to begin search from */ 1721205Seric # endif 173157Seric bool RealUser; /* if set, running as real user */ 174393Seric # ifdef DEBUG 175393Seric bool Debug; /* turn on tracing */ 176393Seric # endif 177148Seric 178148Seric main(argc, argv) 179148Seric int argc; 180148Seric char **argv; 181148Seric { 182148Seric register char *p; 183262Seric extern struct sccsprog *lookup(); 184148Seric 185148Seric /* 186148Seric ** Detect and decode flags intended for this program. 187148Seric */ 188148Seric 189200Seric if (argc < 2) 190148Seric { 1911205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 192200Seric exit(EX_USAGE); 193200Seric } 194200Seric argv[argc] = NULL; 195200Seric 196262Seric if (lookup(argv[0]) == NULL) 197200Seric { 198262Seric while ((p = *++argv) != NULL) 199148Seric { 200262Seric if (*p != '-') 201262Seric break; 202262Seric switch (*++p) 203262Seric { 204262Seric case 'r': /* run as real user */ 205262Seric setuid(getuid()); 206262Seric RealUser++; 207262Seric break; 208148Seric 2091205Seric # ifndef SRCDIR 210262Seric case 'p': /* path of sccs files */ 211262Seric SccsPath = ++p; 212262Seric break; 213148Seric 214588Seric case 'd': /* directory to search from */ 215588Seric SccsDir = ++p; 216588Seric break; 2171205Seric # endif 218588Seric 219393Seric # ifdef DEBUG 220393Seric case 'T': /* trace */ 221393Seric Debug++; 222393Seric break; 223393Seric # endif 224393Seric 225262Seric default: 2261205Seric usrerr("unknown option -%s", p); 227262Seric break; 228262Seric } 229148Seric } 230262Seric if (SccsPath[0] == '\0') 231262Seric SccsPath = "."; 232148Seric } 233148Seric 234201Seric command(argv, FALSE); 235200Seric exit(EX_OK); 236200Seric } 237157Seric 238201Seric command(argv, forkflag) 239200Seric char **argv; 240201Seric bool forkflag; 241200Seric { 242200Seric register struct sccsprog *cmd; 243200Seric register char *p; 244201Seric register char *q; 245201Seric char buf[40]; 246262Seric extern struct sccsprog *lookup(); 247585Seric char *nav[200]; 248393Seric char **avp; 249585Seric register int i; 250585Seric extern bool unedit(); 251200Seric 252393Seric # ifdef DEBUG 253393Seric if (Debug) 254393Seric { 255393Seric printf("command:\n"); 256393Seric for (avp = argv; *avp != NULL; avp++) 257393Seric printf(" \"%s\"\n", *avp); 258393Seric } 259393Seric # endif 260393Seric 261157Seric /* 262148Seric ** Look up command. 263200Seric ** At this point, argv points to the command name. 264148Seric */ 265148Seric 266396Seric cmd = lookup(argv[0]); 267262Seric if (cmd == NULL) 268148Seric { 2691205Seric usrerr("Unknown command \"%s\"", argv[0]); 270148Seric exit(EX_USAGE); 271148Seric } 272148Seric 273148Seric /* 274200Seric ** Interpret operation associated with this command. 275157Seric */ 276157Seric 277200Seric switch (cmd->sccsoper) 278200Seric { 279200Seric case PROG: /* call an sccs prog */ 280201Seric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 281201Seric break; 282201Seric 283201Seric case CMACRO: /* command macro */ 284201Seric for (p = cmd->sccspath; *p != '\0'; p++) 285201Seric { 286393Seric avp = nav; 287393Seric *avp++ = buf; 288201Seric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 289393Seric { 290393Seric if (*p == ' ') 291393Seric { 292393Seric *q = '\0'; 293393Seric *avp++ = &q[1]; 294393Seric } 295393Seric else 296393Seric *q = *p; 297393Seric } 298201Seric *q = '\0'; 299393Seric *avp = NULL; 300393Seric xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2], 301393Seric nav[3], nav[4], nav[5], nav[6]); 302201Seric } 3031205Seric syserr("internal error: CMACRO"); 304200Seric exit(EX_SOFTWARE); 305157Seric 306226Seric case FIX: /* fix a delta */ 307568Seric if (strncmp(argv[1], "-r", 2) != 0) 308226Seric { 3091205Seric usrerr("-r flag needed for fix command"); 310226Seric break; 311226Seric } 312226Seric xcommand(&argv[1], TRUE, "get", "-k", NULL); 313226Seric xcommand(&argv[1], TRUE, "rmdel", NULL); 314226Seric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 3151205Seric syserr("FIX"); 316226Seric exit(EX_SOFTWARE); 317226Seric 318261Seric case CLEAN: 319819Seric clean((int) cmd->sccspath); 320261Seric break; 321261Seric 322396Seric case UNEDIT: 323585Seric i = 0; 324396Seric for (avp = &argv[1]; *avp != NULL; avp++) 325585Seric { 326585Seric if (unedit(*avp)) 327585Seric nav[i++] = *avp; 328585Seric } 329585Seric nav[i] = NULL; 330585Seric if (i > 0) 331585Seric xcommand(nav, FALSE, "get", NULL); 332396Seric break; 333396Seric 334200Seric default: 3351205Seric syserr("oper %d", cmd->sccsoper); 336200Seric exit(EX_SOFTWARE); 337200Seric } 338200Seric } 339262Seric /* 340262Seric ** LOOKUP -- look up an SCCS command name. 341262Seric ** 342262Seric ** Parameters: 343262Seric ** name -- the name of the command to look up. 344262Seric ** 345262Seric ** Returns: 346262Seric ** ptr to command descriptor for this command. 347262Seric ** NULL if no such entry. 348262Seric ** 349262Seric ** Side Effects: 350262Seric ** none. 351262Seric */ 352200Seric 353262Seric struct sccsprog * 354262Seric lookup(name) 355262Seric char *name; 356262Seric { 357262Seric register struct sccsprog *cmd; 358226Seric 359262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 360262Seric { 361262Seric if (strcmp(cmd->sccsname, name) == 0) 362262Seric return (cmd); 363262Seric } 364262Seric return (NULL); 365262Seric } 366262Seric 367262Seric 368226Seric xcommand(argv, forkflag, arg0) 369226Seric char **argv; 370226Seric bool forkflag; 371226Seric char *arg0; 372226Seric { 373226Seric register char **av; 374226Seric char *newargv[1000]; 375226Seric register char **np; 376226Seric 377226Seric np = newargv; 378226Seric for (av = &arg0; *av != NULL; av++) 379226Seric *np++ = *av; 380226Seric for (av = argv; *av != NULL; av++) 381226Seric *np++ = *av; 382226Seric *np = NULL; 383226Seric command(newargv, forkflag); 384226Seric } 385226Seric 386200Seric callprog(progpath, flags, argv, forkflag) 387200Seric char *progpath; 388200Seric short flags; 389200Seric char **argv; 390200Seric bool forkflag; 391200Seric { 392200Seric register char *p; 393200Seric register char **av; 394200Seric extern char *makefile(); 395200Seric register int i; 396201Seric auto int st; 397586Seric register char **nav; 398200Seric 399200Seric if (*argv == NULL) 400200Seric return (-1); 401200Seric 402157Seric /* 403226Seric ** Fork if appropriate. 404148Seric */ 405148Seric 406200Seric if (forkflag) 407200Seric { 408393Seric # ifdef DEBUG 409393Seric if (Debug) 410393Seric printf("Forking\n"); 411393Seric # endif 412200Seric i = fork(); 413200Seric if (i < 0) 414200Seric { 4151205Seric syserr("cannot fork"); 416200Seric exit(EX_OSERR); 417200Seric } 418200Seric else if (i > 0) 419201Seric { 420201Seric wait(&st); 421201Seric return (st); 422201Seric } 423200Seric } 424200Seric 425200Seric /* 426226Seric ** Build new argument vector. 427226Seric */ 428226Seric 429226Seric /* copy program filename arguments and flags */ 430586Seric nav = &argv[1]; 431226Seric av = argv; 432226Seric while ((p = *++av) != NULL) 433226Seric { 434226Seric if (!bitset(NO_SDOT, flags) && *p != '-') 435586Seric *nav = makefile(p); 436586Seric else 437586Seric *nav = p; 438586Seric if (*nav != NULL) 439586Seric nav++; 440226Seric } 441586Seric *nav = NULL; 442226Seric 443226Seric /* 444200Seric ** Set protection as appropriate. 445200Seric */ 446200Seric 447200Seric if (bitset(REALUSER, flags)) 448200Seric setuid(getuid()); 449226Seric 450200Seric /* 451226Seric ** Call real SCCS program. 452200Seric */ 453200Seric 454226Seric execv(progpath, argv); 4551205Seric syserr("cannot execute %s", progpath); 456148Seric exit(EX_UNAVAILABLE); 457148Seric } 458586Seric /* 459586Seric ** MAKEFILE -- make filename of SCCS file 460586Seric ** 461586Seric ** If the name passed is already the name of an SCCS file, 462586Seric ** just return it. Otherwise, munge the name into the name 463586Seric ** of the actual SCCS file. 464586Seric ** 465586Seric ** There are cases when it is not clear what you want to 466586Seric ** do. For example, if SccsPath is an absolute pathname 467586Seric ** and the name given is also an absolute pathname, we go 468586Seric ** for SccsPath (& only use the last component of the name 469586Seric ** passed) -- this is important for security reasons (if 470586Seric ** sccs is being used as a setuid front end), but not 471586Seric ** particularly intuitive. 472586Seric ** 473586Seric ** Parameters: 474586Seric ** name -- the file name to be munged. 475586Seric ** 476586Seric ** Returns: 477586Seric ** The pathname of the sccs file. 478586Seric ** NULL on error. 479586Seric ** 480586Seric ** Side Effects: 481586Seric ** none. 482586Seric */ 483148Seric 484148Seric char * 485148Seric makefile(name) 486148Seric char *name; 487148Seric { 488148Seric register char *p; 489148Seric register char c; 490148Seric char buf[512]; 491588Seric struct stat stbuf; 492148Seric extern char *malloc(); 493586Seric extern char *rindex(); 494588Seric extern bool safepath(); 495587Seric extern bool isdir(); 496587Seric register char *q; 497148Seric 498586Seric p = rindex(name, '/'); 499586Seric if (p == NULL) 500586Seric p = name; 501586Seric else 502586Seric p++; 503586Seric 504148Seric /* 505588Seric ** Check to see that the path is "safe", i.e., that we 506588Seric ** are not letting some nasty person use the setuid part 507588Seric ** of this program to look at or munge some presumably 508588Seric ** hidden files. 509148Seric */ 510148Seric 511588Seric if (SccsDir[0] == '/' && !safepath(name)) 512588Seric return (NULL); 513586Seric 514586Seric /* 515588Seric ** Create the base pathname. 516586Seric */ 517586Seric 518588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 519148Seric { 520588Seric strcpy(buf, SccsDir); 521586Seric strcat(buf, "/"); 522586Seric } 523586Seric else 524586Seric strcpy(buf, ""); 525587Seric strncat(buf, name, p - name); 526587Seric q = &buf[strlen(buf)]; 527587Seric strcpy(q, p); 528587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 529586Seric { 530588Seric strcpy(q, SccsPath); 531588Seric strcat(buf, "/s."); 532586Seric strcat(buf, p); 533586Seric } 534148Seric 535588Seric if (strcmp(buf, name) == 0) 536588Seric p = name; 537588Seric else 538148Seric { 539588Seric p = malloc(strlen(buf) + 1); 540588Seric if (p == NULL) 541588Seric { 542588Seric perror("Sccs: no mem"); 543588Seric exit(EX_OSERR); 544588Seric } 545588Seric strcpy(p, buf); 546148Seric } 547148Seric return (p); 548148Seric } 549261Seric /* 550587Seric ** ISDIR -- return true if the argument is a directory. 551587Seric ** 552587Seric ** Parameters: 553587Seric ** name -- the pathname of the file to check. 554587Seric ** 555587Seric ** Returns: 556587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 557587Seric ** 558587Seric ** Side Effects: 559587Seric ** none. 560587Seric */ 561587Seric 562587Seric bool 563587Seric isdir(name) 564587Seric char *name; 565587Seric { 566587Seric struct stat stbuf; 567587Seric 568587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 569587Seric } 570587Seric /* 571586Seric ** SAFEPATH -- determine whether a pathname is "safe" 572586Seric ** 573586Seric ** "Safe" pathnames only allow you to get deeper into the 574586Seric ** directory structure, i.e., full pathnames and ".." are 575586Seric ** not allowed. 576586Seric ** 577586Seric ** Parameters: 578586Seric ** p -- the name to check. 579586Seric ** 580586Seric ** Returns: 581586Seric ** TRUE -- if the path is safe. 582586Seric ** FALSE -- if the path is not safe. 583586Seric ** 584586Seric ** Side Effects: 585586Seric ** Prints a message if the path is not safe. 586586Seric */ 587586Seric 588586Seric bool 589586Seric safepath(p) 590586Seric register char *p; 591586Seric { 592586Seric extern char *index(); 593586Seric 594586Seric if (*p != '/') 595586Seric { 596586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 597586Seric { 598586Seric p = index(p, '/'); 599586Seric if (p == NULL) 600586Seric return (TRUE); 601586Seric p++; 602586Seric } 603586Seric } 604586Seric 605586Seric printf("You may not use full pathnames or \"..\"\n"); 606586Seric return (FALSE); 607586Seric } 608586Seric /* 609261Seric ** CLEAN -- clean out recreatable files 610261Seric ** 611261Seric ** Any file for which an "s." file exists but no "p." file 612261Seric ** exists in the current directory is purged. 613261Seric ** 614261Seric ** Parameters: 615819Seric ** tells whether this came from a "clean", "info", or 616819Seric ** "check" command. 617261Seric ** 618261Seric ** Returns: 619261Seric ** none. 620261Seric ** 621261Seric ** Side Effects: 622819Seric ** Removes files in the current directory. 623819Seric ** Prints information regarding files being edited. 624819Seric ** Exits if a "check" command. 625261Seric */ 626261Seric 627819Seric clean(mode) 628819Seric int mode; 629261Seric { 630261Seric struct direct dir; 631261Seric struct stat stbuf; 632261Seric char buf[100]; 633394Seric char pline[120]; 634346Seric register FILE *dirfd; 635346Seric register char *basefile; 636351Seric bool gotedit; 637394Seric FILE *pfp; 638261Seric 639*1207Seric strcpy(buf, SccsDir); 640*1207Seric if (buf[0] != '\0') 641*1207Seric strcat(buf, "/"); 642*1207Seric strcat(buf, SccsPath); 643*1207Seric dirfd = fopen(buf, "r"); 644261Seric if (dirfd == NULL) 645261Seric { 646*1207Seric usrerr("cannot open %s", buf); 647261Seric return; 648261Seric } 649261Seric 650261Seric /* 651261Seric ** Scan the SCCS directory looking for s. files. 652261Seric */ 653261Seric 654351Seric gotedit = FALSE; 655261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 656261Seric { 657568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 658261Seric continue; 659261Seric 660261Seric /* got an s. file -- see if the p. file exists */ 661*1207Seric strcpy(buf, SccsDir); 662*1207Seric if (buf[0] != '\0') 663*1207Seric strcat(buf, "/"); 664*1207Seric strcat(buf, SccsPath); 665261Seric strcat(buf, "/p."); 666346Seric basefile = &buf[strlen(buf)]; 667568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 668346Seric basefile[sizeof dir.d_name - 2] = '\0'; 669394Seric pfp = fopen(buf, "r"); 670394Seric if (pfp != NULL) 671346Seric { 672394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 673416Seric printf("%12s: being edited: %s", basefile, pline); 674394Seric fclose(pfp); 675351Seric gotedit = TRUE; 676261Seric continue; 677346Seric } 678261Seric 679261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 680819Seric if (mode == CLEANC) 681346Seric { 682568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 683346Seric buf[sizeof dir.d_name - 2] = '\0'; 684346Seric unlink(buf); 685346Seric } 686261Seric } 687261Seric 688261Seric fclose(dirfd); 689819Seric if (!gotedit && mode == INFOC) 690416Seric printf("Nothing being edited\n"); 691819Seric if (mode == CHECKC) 692819Seric exit(gotedit); 693261Seric } 694396Seric /* 695396Seric ** UNEDIT -- unedit a file 696396Seric ** 697396Seric ** Checks to see that the current user is actually editting 698396Seric ** the file and arranges that s/he is not editting it. 699396Seric ** 700396Seric ** Parameters: 701416Seric ** fn -- the name of the file to be unedited. 702396Seric ** 703396Seric ** Returns: 704585Seric ** TRUE -- if the file was successfully unedited. 705585Seric ** FALSE -- if the file was not unedited for some 706585Seric ** reason. 707396Seric ** 708396Seric ** Side Effects: 709396Seric ** fn is removed 710396Seric ** entries are removed from pfile. 711396Seric */ 712396Seric 713585Seric bool 714396Seric unedit(fn) 715396Seric char *fn; 716396Seric { 717396Seric register FILE *pfp; 718396Seric char *pfn; 719396Seric static char tfn[] = "/tmp/sccsXXXXX"; 720396Seric FILE *tfp; 721396Seric register char *p; 722396Seric register char *q; 723396Seric bool delete = FALSE; 724396Seric bool others = FALSE; 725396Seric char *myname; 726396Seric extern char *getlogin(); 727396Seric struct pfile *pent; 728396Seric extern struct pfile *getpfile(); 729396Seric char buf[120]; 730828Seric # ifdef UIDUSER 731828Seric struct passwd *pw; 732828Seric extern struct passwd *getpwuid(); 733828Seric # endif UIDUSER 734396Seric 735396Seric /* make "s." filename & find the trailing component */ 736396Seric pfn = makefile(fn); 737586Seric if (pfn == NULL) 738586Seric return (FALSE); 739586Seric q = rindex(pfn, '/'); 740586Seric if (q == NULL) 741586Seric q = &pfn[-1]; 742586Seric if (q[1] != 's' || q[2] != '.') 743396Seric { 7441205Seric usrerr("bad file name \"%s\"", fn); 745585Seric return (FALSE); 746396Seric } 747396Seric 748396Seric /* turn "s." into "p." */ 749396Seric *++q = 'p'; 750396Seric 751396Seric pfp = fopen(pfn, "r"); 752396Seric if (pfp == NULL) 753396Seric { 754416Seric printf("%12s: not being edited\n", fn); 755585Seric return (FALSE); 756396Seric } 757396Seric 758396Seric /* 759396Seric ** Copy p-file to temp file, doing deletions as needed. 760396Seric */ 761396Seric 762396Seric mktemp(tfn); 763396Seric tfp = fopen(tfn, "w"); 764396Seric if (tfp == NULL) 765396Seric { 7661205Seric usrerr("cannot create \"%s\"", tfn); 767396Seric exit(EX_OSERR); 768396Seric } 769396Seric 770828Seric # ifdef UIDUSER 771828Seric pw = getpwuid(getuid()); 772828Seric if (pw == NULL) 773828Seric { 7741205Seric syserr("who are you? (uid=%d)", getuid()); 775828Seric exit(EX_OSERR); 776828Seric } 777828Seric myname = pw->pw_name; 778828Seric # else 779396Seric myname = getlogin(); 780828Seric # endif UIDUSER 781396Seric while ((pent = getpfile(pfp)) != NULL) 782396Seric { 783396Seric if (strcmp(pent->p_user, myname) == 0) 784396Seric { 785396Seric /* a match */ 786396Seric delete++; 787396Seric } 788396Seric else 789396Seric { 790396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 791396Seric pent->p_nsid, pent->p_user, pent->p_date, 792396Seric pent->p_time); 793396Seric others++; 794396Seric } 795396Seric } 796396Seric 797396Seric /* do final cleanup */ 798396Seric if (others) 799396Seric { 800396Seric if (freopen(tfn, "r", tfp) == NULL) 801396Seric { 8021205Seric syserr("cannot reopen \"%s\"", tfn); 803396Seric exit(EX_OSERR); 804396Seric } 805396Seric if (freopen(pfn, "w", pfp) == NULL) 806396Seric { 8071205Seric usrerr("cannot create \"%s\"", pfn); 808585Seric return (FALSE); 809396Seric } 810396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 811396Seric fputs(buf, pfp); 812396Seric } 813396Seric else 814396Seric { 815396Seric unlink(pfn); 816396Seric } 817396Seric fclose(tfp); 818396Seric fclose(pfp); 819396Seric unlink(tfn); 820396Seric 821396Seric if (delete) 822396Seric { 823396Seric unlink(fn); 824396Seric printf("%12s: removed\n", fn); 825585Seric return (TRUE); 826396Seric } 827396Seric else 828396Seric { 829416Seric printf("%12s: not being edited by you\n", fn); 830585Seric return (FALSE); 831396Seric } 832396Seric } 833396Seric /* 834396Seric ** GETPFILE -- get an entry from the p-file 835396Seric ** 836396Seric ** Parameters: 837396Seric ** pfp -- p-file file pointer 838396Seric ** 839396Seric ** Returns: 840396Seric ** pointer to p-file struct for next entry 841396Seric ** NULL on EOF or error 842396Seric ** 843396Seric ** Side Effects: 844396Seric ** Each call wipes out results of previous call. 845396Seric */ 846396Seric 847396Seric struct pfile * 848396Seric getpfile(pfp) 849396Seric FILE *pfp; 850396Seric { 851396Seric static struct pfile ent; 852396Seric static char buf[120]; 853396Seric register char *p; 854396Seric extern char *nextfield(); 855396Seric 856396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 857396Seric return (NULL); 858396Seric 859396Seric ent.p_osid = p = buf; 860396Seric ent.p_nsid = p = nextfield(p); 861396Seric ent.p_user = p = nextfield(p); 862396Seric ent.p_date = p = nextfield(p); 863396Seric ent.p_time = p = nextfield(p); 864396Seric if (p == NULL || nextfield(p) != NULL) 865396Seric return (NULL); 866396Seric 867396Seric return (&ent); 868396Seric } 869396Seric 870396Seric 871396Seric char * 872396Seric nextfield(p) 873396Seric register char *p; 874396Seric { 875396Seric if (p == NULL || *p == '\0') 876396Seric return (NULL); 877396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 878396Seric p++; 879396Seric if (*p == '\n' || *p == '\0') 880396Seric { 881396Seric *p = '\0'; 882396Seric return (NULL); 883396Seric } 884396Seric *p++ = '\0'; 885396Seric return (p); 886396Seric } 8871205Seric /* 8881205Seric ** USRERR -- issue user-level error 8891205Seric ** 8901205Seric ** Parameters: 8911205Seric ** f -- format string. 8921205Seric ** p1-p3 -- parameters to a printf. 8931205Seric ** 8941205Seric ** Returns: 8951205Seric ** -1 8961205Seric ** 8971205Seric ** Side Effects: 8981205Seric ** none. 8991205Seric */ 9001205Seric 9011205Seric usrerr(f, p1, p2, p3) 9021205Seric char *f; 9031205Seric { 9041205Seric fprintf(stderr, "\n%s: ", MyName); 9051205Seric fprintf(stderr, f, p1, p2, p3); 9061205Seric fprintf(stderr, "\n"); 9071205Seric 9081205Seric return (-1); 9091205Seric } 9101205Seric /* 9111205Seric ** SYSERR -- print system-generated error. 9121205Seric ** 9131205Seric ** Parameters: 9141205Seric ** f -- format string to a printf. 9151205Seric ** p1, p2, p3 -- parameters to f. 9161205Seric ** 9171205Seric ** Returns: 9181205Seric ** never. 9191205Seric ** 9201205Seric ** Side Effects: 9211205Seric ** none. 9221205Seric */ 9231205Seric 9241205Seric syserr(f, p1, p2, p3) 9251205Seric char *f; 9261205Seric { 9271205Seric extern int errno; 9281205Seric 9291205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 9301205Seric fprintf(stderr, f, p1, p2, p3); 9311205Seric fprintf(stderr, "\n"); 9321205Seric if (errno == 0) 9331205Seric exit(EX_SOFTWARE); 9341205Seric else 9351205Seric { 9361205Seric perror(0); 9371205Seric exit(EX_OSERR); 9381205Seric } 9391205Seric } 940