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. 75*1270Seric ** SCCSDIR -- 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. 78*1270Seric ** This flag also disables the -p flag. 79*1270Seric ** SCCSPATH -- the default for the -p flag. 80828Seric ** 81828Seric ** Compilation Instructions: 82828Seric ** cc -O -n -s sccs.c 83828Seric ** 84828Seric ** Author: 85828Seric ** Eric Allman, UCB/INGRES 86*1270Seric ** Copyright 1980 Regents of the University of California 87828Seric */ 88155Seric 89*1270Seric /******************* Configuration Information ********************/ 90*1270Seric 91828Seric # ifdef CSVAX 92828Seric # define UIDUSER 93*1270Seric # define PROGPATH(name) "/usr/local/name" 94*1270Seric # endif CSVAX 95828Seric 96*1270Seric # define SCCSPATH "SCCS" 97*1270Seric /* put #define SCCSDIR here */ 98828Seric 99*1270Seric char MyName[] = "sccs"; /* name used in messages */ 100*1270Seric 101*1270Seric /**************** End of Configuration Information ****************/ 102*1270Seric 103*1270Seric static char SccsId[] = "@(#)sccs.c 1.29 10/08/80"; 104*1270Seric 105157Seric # define bitset(bit, word) ((bit) & (word)) 106157Seric 107157Seric typedef char bool; 108200Seric # define TRUE 1 109200Seric # define FALSE 0 110157Seric 111828Seric # ifdef UIDUSER 112828Seric # include <pwd.h> 113828Seric # endif UIDUSER 114828Seric 115148Seric struct sccsprog 116148Seric { 117148Seric char *sccsname; /* name of SCCS routine */ 118200Seric short sccsoper; /* opcode, see below */ 119200Seric short sccsflags; /* flags, see below */ 120148Seric char *sccspath; /* pathname of binary implementing */ 121148Seric }; 122148Seric 123200Seric /* values for sccsoper */ 124200Seric # define PROG 0 /* call a program */ 125201Seric # define CMACRO 1 /* command substitution macro */ 126226Seric # define FIX 2 /* fix a delta */ 127261Seric # define CLEAN 3 /* clean out recreatable files */ 128396Seric # define UNEDIT 4 /* unedit a file */ 129200Seric 130157Seric /* bits for sccsflags */ 131200Seric # define NO_SDOT 0001 /* no s. on front of args */ 132200Seric # define REALUSER 0002 /* protected (e.g., admin) */ 133148Seric 134819Seric /* modes for the "clean", "info", "check" ops */ 135819Seric # define CLEANC 0 /* clean command */ 136819Seric # define INFOC 1 /* info command */ 137819Seric # define CHECKC 2 /* check command */ 138819Seric 139202Seric # ifndef PROGPATH 140202Seric # define PROGPATH(name) "/usr/sccs/name" 141202Seric # endif PROGPATH 142202Seric 143148Seric struct sccsprog SccsProg[] = 144148Seric { 145202Seric "admin", PROG, REALUSER, PROGPATH(admin), 146202Seric "chghist", PROG, 0, PROGPATH(rmdel), 147202Seric "comb", PROG, 0, PROGPATH(comb), 148202Seric "delta", PROG, 0, PROGPATH(delta), 149202Seric "get", PROG, 0, PROGPATH(get), 150202Seric "help", PROG, NO_SDOT, PROGPATH(help), 151202Seric "prt", PROG, 0, PROGPATH(prt), 152202Seric "rmdel", PROG, REALUSER, PROGPATH(rmdel), 153202Seric "what", PROG, NO_SDOT, PROGPATH(what), 154393Seric "edit", CMACRO, 0, "get -e", 155393Seric "delget", CMACRO, 0, "delta/get", 156398Seric "deledit", CMACRO, 0, "delta/get -e", 157226Seric "fix", FIX, 0, NULL, 158819Seric "clean", CLEAN, REALUSER, (char *) CLEANC, 159819Seric "info", CLEAN, REALUSER, (char *) INFOC, 160819Seric "check", CLEAN, REALUSER, (char *) CHECKC, 161396Seric "unedit", UNEDIT, 0, NULL, 162200Seric NULL, -1, 0, NULL 163148Seric }; 164148Seric 165396Seric struct pfile 166396Seric { 167396Seric char *p_osid; /* old SID */ 168396Seric char *p_nsid; /* new SID */ 169396Seric char *p_user; /* user who did edit */ 170396Seric char *p_date; /* date of get */ 171396Seric char *p_time; /* time of get */ 172396Seric }; 173396Seric 174*1270Seric char *SccsPath = SCCSPATH; /* pathname of SCCS files */ 175*1270Seric # ifdef SCCSDIR 176*1270Seric char *SccsDir = SCCSDIR; /* directory to begin search from */ 1771205Seric # else 178*1270Seric char *SccsDir = ""; 1791205Seric # endif 180157Seric bool RealUser; /* if set, running as real user */ 181393Seric # ifdef DEBUG 182393Seric bool Debug; /* turn on tracing */ 183393Seric # endif 184148Seric 185148Seric main(argc, argv) 186148Seric int argc; 187148Seric char **argv; 188148Seric { 189148Seric register char *p; 190262Seric extern struct sccsprog *lookup(); 191148Seric 192148Seric /* 193148Seric ** Detect and decode flags intended for this program. 194148Seric */ 195148Seric 196200Seric if (argc < 2) 197148Seric { 1981205Seric fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); 199200Seric exit(EX_USAGE); 200200Seric } 201200Seric argv[argc] = NULL; 202200Seric 203262Seric if (lookup(argv[0]) == NULL) 204200Seric { 205262Seric while ((p = *++argv) != NULL) 206148Seric { 207262Seric if (*p != '-') 208262Seric break; 209262Seric switch (*++p) 210262Seric { 211262Seric case 'r': /* run as real user */ 212262Seric setuid(getuid()); 213262Seric RealUser++; 214262Seric break; 215148Seric 216*1270Seric # ifndef SCCSDIR 217262Seric case 'p': /* path of sccs files */ 218262Seric SccsPath = ++p; 219262Seric break; 220148Seric 221588Seric case 'd': /* directory to search from */ 222588Seric SccsDir = ++p; 223588Seric break; 2241205Seric # endif 225588Seric 226393Seric # ifdef DEBUG 227393Seric case 'T': /* trace */ 228393Seric Debug++; 229393Seric break; 230393Seric # endif 231393Seric 232262Seric default: 2331205Seric usrerr("unknown option -%s", p); 234262Seric break; 235262Seric } 236148Seric } 237262Seric if (SccsPath[0] == '\0') 238262Seric SccsPath = "."; 239148Seric } 240148Seric 241201Seric command(argv, FALSE); 242200Seric exit(EX_OK); 243200Seric } 244157Seric 245201Seric command(argv, forkflag) 246200Seric char **argv; 247201Seric bool forkflag; 248200Seric { 249200Seric register struct sccsprog *cmd; 250200Seric register char *p; 251201Seric register char *q; 252201Seric char buf[40]; 253262Seric extern struct sccsprog *lookup(); 254585Seric char *nav[200]; 255393Seric char **avp; 256585Seric register int i; 257585Seric extern bool unedit(); 258200Seric 259393Seric # ifdef DEBUG 260393Seric if (Debug) 261393Seric { 262393Seric printf("command:\n"); 263393Seric for (avp = argv; *avp != NULL; avp++) 264393Seric printf(" \"%s\"\n", *avp); 265393Seric } 266393Seric # endif 267393Seric 268157Seric /* 269148Seric ** Look up command. 270200Seric ** At this point, argv points to the command name. 271148Seric */ 272148Seric 273396Seric cmd = lookup(argv[0]); 274262Seric if (cmd == NULL) 275148Seric { 2761205Seric usrerr("Unknown command \"%s\"", argv[0]); 277148Seric exit(EX_USAGE); 278148Seric } 279148Seric 280148Seric /* 281200Seric ** Interpret operation associated with this command. 282157Seric */ 283157Seric 284200Seric switch (cmd->sccsoper) 285200Seric { 286200Seric case PROG: /* call an sccs prog */ 287201Seric callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); 288201Seric break; 289201Seric 290201Seric case CMACRO: /* command macro */ 291201Seric for (p = cmd->sccspath; *p != '\0'; p++) 292201Seric { 293393Seric avp = nav; 294393Seric *avp++ = buf; 295201Seric for (q = buf; *p != '/' && *p != '\0'; p++, q++) 296393Seric { 297393Seric if (*p == ' ') 298393Seric { 299393Seric *q = '\0'; 300393Seric *avp++ = &q[1]; 301393Seric } 302393Seric else 303393Seric *q = *p; 304393Seric } 305201Seric *q = '\0'; 306393Seric *avp = NULL; 307393Seric xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2], 308393Seric nav[3], nav[4], nav[5], nav[6]); 309201Seric } 3101205Seric syserr("internal error: CMACRO"); 311200Seric exit(EX_SOFTWARE); 312157Seric 313226Seric case FIX: /* fix a delta */ 314568Seric if (strncmp(argv[1], "-r", 2) != 0) 315226Seric { 3161205Seric usrerr("-r flag needed for fix command"); 317226Seric break; 318226Seric } 319226Seric xcommand(&argv[1], TRUE, "get", "-k", NULL); 320226Seric xcommand(&argv[1], TRUE, "rmdel", NULL); 321226Seric xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); 3221205Seric syserr("FIX"); 323226Seric exit(EX_SOFTWARE); 324226Seric 325261Seric case CLEAN: 326819Seric clean((int) cmd->sccspath); 327261Seric break; 328261Seric 329396Seric case UNEDIT: 330585Seric i = 0; 331396Seric for (avp = &argv[1]; *avp != NULL; avp++) 332585Seric { 333585Seric if (unedit(*avp)) 334585Seric nav[i++] = *avp; 335585Seric } 336585Seric nav[i] = NULL; 337585Seric if (i > 0) 338585Seric xcommand(nav, FALSE, "get", NULL); 339396Seric break; 340396Seric 341200Seric default: 3421205Seric syserr("oper %d", cmd->sccsoper); 343200Seric exit(EX_SOFTWARE); 344200Seric } 345200Seric } 346262Seric /* 347262Seric ** LOOKUP -- look up an SCCS command name. 348262Seric ** 349262Seric ** Parameters: 350262Seric ** name -- the name of the command to look up. 351262Seric ** 352262Seric ** Returns: 353262Seric ** ptr to command descriptor for this command. 354262Seric ** NULL if no such entry. 355262Seric ** 356262Seric ** Side Effects: 357262Seric ** none. 358262Seric */ 359200Seric 360262Seric struct sccsprog * 361262Seric lookup(name) 362262Seric char *name; 363262Seric { 364262Seric register struct sccsprog *cmd; 365226Seric 366262Seric for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) 367262Seric { 368262Seric if (strcmp(cmd->sccsname, name) == 0) 369262Seric return (cmd); 370262Seric } 371262Seric return (NULL); 372262Seric } 373262Seric 374262Seric 375226Seric xcommand(argv, forkflag, arg0) 376226Seric char **argv; 377226Seric bool forkflag; 378226Seric char *arg0; 379226Seric { 380226Seric register char **av; 381226Seric char *newargv[1000]; 382226Seric register char **np; 383226Seric 384226Seric np = newargv; 385226Seric for (av = &arg0; *av != NULL; av++) 386226Seric *np++ = *av; 387226Seric for (av = argv; *av != NULL; av++) 388226Seric *np++ = *av; 389226Seric *np = NULL; 390226Seric command(newargv, forkflag); 391226Seric } 392226Seric 393200Seric callprog(progpath, flags, argv, forkflag) 394200Seric char *progpath; 395200Seric short flags; 396200Seric char **argv; 397200Seric bool forkflag; 398200Seric { 399200Seric register char *p; 400200Seric register char **av; 401200Seric extern char *makefile(); 402200Seric register int i; 403201Seric auto int st; 404586Seric register char **nav; 405200Seric 406200Seric if (*argv == NULL) 407200Seric return (-1); 408200Seric 409157Seric /* 410226Seric ** Fork if appropriate. 411148Seric */ 412148Seric 413200Seric if (forkflag) 414200Seric { 415393Seric # ifdef DEBUG 416393Seric if (Debug) 417393Seric printf("Forking\n"); 418393Seric # endif 419200Seric i = fork(); 420200Seric if (i < 0) 421200Seric { 4221205Seric syserr("cannot fork"); 423200Seric exit(EX_OSERR); 424200Seric } 425200Seric else if (i > 0) 426201Seric { 427201Seric wait(&st); 428201Seric return (st); 429201Seric } 430200Seric } 431200Seric 432200Seric /* 433226Seric ** Build new argument vector. 434226Seric */ 435226Seric 436226Seric /* copy program filename arguments and flags */ 437586Seric nav = &argv[1]; 438226Seric av = argv; 439226Seric while ((p = *++av) != NULL) 440226Seric { 441226Seric if (!bitset(NO_SDOT, flags) && *p != '-') 442586Seric *nav = makefile(p); 443586Seric else 444586Seric *nav = p; 445586Seric if (*nav != NULL) 446586Seric nav++; 447226Seric } 448586Seric *nav = NULL; 449226Seric 450226Seric /* 451200Seric ** Set protection as appropriate. 452200Seric */ 453200Seric 454200Seric if (bitset(REALUSER, flags)) 455200Seric setuid(getuid()); 456226Seric 457200Seric /* 458226Seric ** Call real SCCS program. 459200Seric */ 460200Seric 461226Seric execv(progpath, argv); 4621205Seric syserr("cannot execute %s", progpath); 463148Seric exit(EX_UNAVAILABLE); 464148Seric } 465586Seric /* 466586Seric ** MAKEFILE -- make filename of SCCS file 467586Seric ** 468586Seric ** If the name passed is already the name of an SCCS file, 469586Seric ** just return it. Otherwise, munge the name into the name 470586Seric ** of the actual SCCS file. 471586Seric ** 472586Seric ** There are cases when it is not clear what you want to 473586Seric ** do. For example, if SccsPath is an absolute pathname 474586Seric ** and the name given is also an absolute pathname, we go 475586Seric ** for SccsPath (& only use the last component of the name 476586Seric ** passed) -- this is important for security reasons (if 477586Seric ** sccs is being used as a setuid front end), but not 478586Seric ** particularly intuitive. 479586Seric ** 480586Seric ** Parameters: 481586Seric ** name -- the file name to be munged. 482586Seric ** 483586Seric ** Returns: 484586Seric ** The pathname of the sccs file. 485586Seric ** NULL on error. 486586Seric ** 487586Seric ** Side Effects: 488586Seric ** none. 489586Seric */ 490148Seric 491148Seric char * 492148Seric makefile(name) 493148Seric char *name; 494148Seric { 495148Seric register char *p; 496148Seric register char c; 497148Seric char buf[512]; 498588Seric struct stat stbuf; 499148Seric extern char *malloc(); 500586Seric extern char *rindex(); 501588Seric extern bool safepath(); 502587Seric extern bool isdir(); 503587Seric register char *q; 504148Seric 505586Seric p = rindex(name, '/'); 506586Seric if (p == NULL) 507586Seric p = name; 508586Seric else 509586Seric p++; 510586Seric 511148Seric /* 512588Seric ** Check to see that the path is "safe", i.e., that we 513588Seric ** are not letting some nasty person use the setuid part 514588Seric ** of this program to look at or munge some presumably 515588Seric ** hidden files. 516148Seric */ 517148Seric 518588Seric if (SccsDir[0] == '/' && !safepath(name)) 519588Seric return (NULL); 520586Seric 521586Seric /* 522588Seric ** Create the base pathname. 523586Seric */ 524586Seric 525588Seric if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) 526148Seric { 527588Seric strcpy(buf, SccsDir); 528586Seric strcat(buf, "/"); 529586Seric } 530586Seric else 531586Seric strcpy(buf, ""); 532587Seric strncat(buf, name, p - name); 533587Seric q = &buf[strlen(buf)]; 534587Seric strcpy(q, p); 535587Seric if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) 536586Seric { 537588Seric strcpy(q, SccsPath); 538588Seric strcat(buf, "/s."); 539586Seric strcat(buf, p); 540586Seric } 541148Seric 542588Seric if (strcmp(buf, name) == 0) 543588Seric p = name; 544588Seric else 545148Seric { 546588Seric p = malloc(strlen(buf) + 1); 547588Seric if (p == NULL) 548588Seric { 549588Seric perror("Sccs: no mem"); 550588Seric exit(EX_OSERR); 551588Seric } 552588Seric strcpy(p, buf); 553148Seric } 554148Seric return (p); 555148Seric } 556261Seric /* 557587Seric ** ISDIR -- return true if the argument is a directory. 558587Seric ** 559587Seric ** Parameters: 560587Seric ** name -- the pathname of the file to check. 561587Seric ** 562587Seric ** Returns: 563587Seric ** TRUE if 'name' is a directory, FALSE otherwise. 564587Seric ** 565587Seric ** Side Effects: 566587Seric ** none. 567587Seric */ 568587Seric 569587Seric bool 570587Seric isdir(name) 571587Seric char *name; 572587Seric { 573587Seric struct stat stbuf; 574587Seric 575587Seric return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); 576587Seric } 577587Seric /* 578586Seric ** SAFEPATH -- determine whether a pathname is "safe" 579586Seric ** 580586Seric ** "Safe" pathnames only allow you to get deeper into the 581586Seric ** directory structure, i.e., full pathnames and ".." are 582586Seric ** not allowed. 583586Seric ** 584586Seric ** Parameters: 585586Seric ** p -- the name to check. 586586Seric ** 587586Seric ** Returns: 588586Seric ** TRUE -- if the path is safe. 589586Seric ** FALSE -- if the path is not safe. 590586Seric ** 591586Seric ** Side Effects: 592586Seric ** Prints a message if the path is not safe. 593586Seric */ 594586Seric 595586Seric bool 596586Seric safepath(p) 597586Seric register char *p; 598586Seric { 599586Seric extern char *index(); 600586Seric 601586Seric if (*p != '/') 602586Seric { 603586Seric while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) 604586Seric { 605586Seric p = index(p, '/'); 606586Seric if (p == NULL) 607586Seric return (TRUE); 608586Seric p++; 609586Seric } 610586Seric } 611586Seric 612586Seric printf("You may not use full pathnames or \"..\"\n"); 613586Seric return (FALSE); 614586Seric } 615586Seric /* 616261Seric ** CLEAN -- clean out recreatable files 617261Seric ** 618261Seric ** Any file for which an "s." file exists but no "p." file 619261Seric ** exists in the current directory is purged. 620261Seric ** 621261Seric ** Parameters: 622819Seric ** tells whether this came from a "clean", "info", or 623819Seric ** "check" command. 624261Seric ** 625261Seric ** Returns: 626261Seric ** none. 627261Seric ** 628261Seric ** Side Effects: 629819Seric ** Removes files in the current directory. 630819Seric ** Prints information regarding files being edited. 631819Seric ** Exits if a "check" command. 632261Seric */ 633261Seric 634819Seric clean(mode) 635819Seric int mode; 636261Seric { 637261Seric struct direct dir; 638261Seric struct stat stbuf; 639261Seric char buf[100]; 640394Seric char pline[120]; 641346Seric register FILE *dirfd; 642346Seric register char *basefile; 643351Seric bool gotedit; 644394Seric FILE *pfp; 645261Seric 6461207Seric strcpy(buf, SccsDir); 6471207Seric if (buf[0] != '\0') 6481207Seric strcat(buf, "/"); 6491207Seric strcat(buf, SccsPath); 6501207Seric dirfd = fopen(buf, "r"); 651261Seric if (dirfd == NULL) 652261Seric { 6531207Seric usrerr("cannot open %s", buf); 654261Seric return; 655261Seric } 656261Seric 657261Seric /* 658261Seric ** Scan the SCCS directory looking for s. files. 659261Seric */ 660261Seric 661351Seric gotedit = FALSE; 662261Seric while (fread(&dir, sizeof dir, 1, dirfd) != NULL) 663261Seric { 664568Seric if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) 665261Seric continue; 666261Seric 667261Seric /* got an s. file -- see if the p. file exists */ 6681207Seric strcpy(buf, SccsDir); 6691207Seric if (buf[0] != '\0') 6701207Seric strcat(buf, "/"); 6711207Seric strcat(buf, SccsPath); 672261Seric strcat(buf, "/p."); 673346Seric basefile = &buf[strlen(buf)]; 674568Seric strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); 675346Seric basefile[sizeof dir.d_name - 2] = '\0'; 676394Seric pfp = fopen(buf, "r"); 677394Seric if (pfp != NULL) 678346Seric { 679394Seric while (fgets(pline, sizeof pline, pfp) != NULL) 680416Seric printf("%12s: being edited: %s", basefile, pline); 681394Seric fclose(pfp); 682351Seric gotedit = TRUE; 683261Seric continue; 684346Seric } 685261Seric 686261Seric /* the s. file exists and no p. file exists -- unlink the g-file */ 687819Seric if (mode == CLEANC) 688346Seric { 689568Seric strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); 690346Seric buf[sizeof dir.d_name - 2] = '\0'; 691346Seric unlink(buf); 692346Seric } 693261Seric } 694261Seric 695261Seric fclose(dirfd); 696819Seric if (!gotedit && mode == INFOC) 697416Seric printf("Nothing being edited\n"); 698819Seric if (mode == CHECKC) 699819Seric exit(gotedit); 700261Seric } 701396Seric /* 702396Seric ** UNEDIT -- unedit a file 703396Seric ** 704396Seric ** Checks to see that the current user is actually editting 705396Seric ** the file and arranges that s/he is not editting it. 706396Seric ** 707396Seric ** Parameters: 708416Seric ** fn -- the name of the file to be unedited. 709396Seric ** 710396Seric ** Returns: 711585Seric ** TRUE -- if the file was successfully unedited. 712585Seric ** FALSE -- if the file was not unedited for some 713585Seric ** reason. 714396Seric ** 715396Seric ** Side Effects: 716396Seric ** fn is removed 717396Seric ** entries are removed from pfile. 718396Seric */ 719396Seric 720585Seric bool 721396Seric unedit(fn) 722396Seric char *fn; 723396Seric { 724396Seric register FILE *pfp; 725396Seric char *pfn; 726396Seric static char tfn[] = "/tmp/sccsXXXXX"; 727396Seric FILE *tfp; 728396Seric register char *p; 729396Seric register char *q; 730396Seric bool delete = FALSE; 731396Seric bool others = FALSE; 732396Seric char *myname; 733396Seric extern char *getlogin(); 734396Seric struct pfile *pent; 735396Seric extern struct pfile *getpfile(); 736396Seric char buf[120]; 737828Seric # ifdef UIDUSER 738828Seric struct passwd *pw; 739828Seric extern struct passwd *getpwuid(); 740828Seric # endif UIDUSER 741396Seric 742396Seric /* make "s." filename & find the trailing component */ 743396Seric pfn = makefile(fn); 744586Seric if (pfn == NULL) 745586Seric return (FALSE); 746586Seric q = rindex(pfn, '/'); 747586Seric if (q == NULL) 748586Seric q = &pfn[-1]; 749586Seric if (q[1] != 's' || q[2] != '.') 750396Seric { 7511205Seric usrerr("bad file name \"%s\"", fn); 752585Seric return (FALSE); 753396Seric } 754396Seric 755396Seric /* turn "s." into "p." */ 756396Seric *++q = 'p'; 757396Seric 758396Seric pfp = fopen(pfn, "r"); 759396Seric if (pfp == NULL) 760396Seric { 761416Seric printf("%12s: not being edited\n", fn); 762585Seric return (FALSE); 763396Seric } 764396Seric 765396Seric /* 766396Seric ** Copy p-file to temp file, doing deletions as needed. 767396Seric */ 768396Seric 769396Seric mktemp(tfn); 770396Seric tfp = fopen(tfn, "w"); 771396Seric if (tfp == NULL) 772396Seric { 7731205Seric usrerr("cannot create \"%s\"", tfn); 774396Seric exit(EX_OSERR); 775396Seric } 776396Seric 777828Seric # ifdef UIDUSER 778828Seric pw = getpwuid(getuid()); 779828Seric if (pw == NULL) 780828Seric { 7811205Seric syserr("who are you? (uid=%d)", getuid()); 782828Seric exit(EX_OSERR); 783828Seric } 784828Seric myname = pw->pw_name; 785828Seric # else 786396Seric myname = getlogin(); 787828Seric # endif UIDUSER 788396Seric while ((pent = getpfile(pfp)) != NULL) 789396Seric { 790396Seric if (strcmp(pent->p_user, myname) == 0) 791396Seric { 792396Seric /* a match */ 793396Seric delete++; 794396Seric } 795396Seric else 796396Seric { 797396Seric fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, 798396Seric pent->p_nsid, pent->p_user, pent->p_date, 799396Seric pent->p_time); 800396Seric others++; 801396Seric } 802396Seric } 803396Seric 804396Seric /* do final cleanup */ 805396Seric if (others) 806396Seric { 807396Seric if (freopen(tfn, "r", tfp) == NULL) 808396Seric { 8091205Seric syserr("cannot reopen \"%s\"", tfn); 810396Seric exit(EX_OSERR); 811396Seric } 812396Seric if (freopen(pfn, "w", pfp) == NULL) 813396Seric { 8141205Seric usrerr("cannot create \"%s\"", pfn); 815585Seric return (FALSE); 816396Seric } 817396Seric while (fgets(buf, sizeof buf, tfp) != NULL) 818396Seric fputs(buf, pfp); 819396Seric } 820396Seric else 821396Seric { 822396Seric unlink(pfn); 823396Seric } 824396Seric fclose(tfp); 825396Seric fclose(pfp); 826396Seric unlink(tfn); 827396Seric 828396Seric if (delete) 829396Seric { 830396Seric unlink(fn); 831396Seric printf("%12s: removed\n", fn); 832585Seric return (TRUE); 833396Seric } 834396Seric else 835396Seric { 836416Seric printf("%12s: not being edited by you\n", fn); 837585Seric return (FALSE); 838396Seric } 839396Seric } 840396Seric /* 841396Seric ** GETPFILE -- get an entry from the p-file 842396Seric ** 843396Seric ** Parameters: 844396Seric ** pfp -- p-file file pointer 845396Seric ** 846396Seric ** Returns: 847396Seric ** pointer to p-file struct for next entry 848396Seric ** NULL on EOF or error 849396Seric ** 850396Seric ** Side Effects: 851396Seric ** Each call wipes out results of previous call. 852396Seric */ 853396Seric 854396Seric struct pfile * 855396Seric getpfile(pfp) 856396Seric FILE *pfp; 857396Seric { 858396Seric static struct pfile ent; 859396Seric static char buf[120]; 860396Seric register char *p; 861396Seric extern char *nextfield(); 862396Seric 863396Seric if (fgets(buf, sizeof buf, pfp) == NULL) 864396Seric return (NULL); 865396Seric 866396Seric ent.p_osid = p = buf; 867396Seric ent.p_nsid = p = nextfield(p); 868396Seric ent.p_user = p = nextfield(p); 869396Seric ent.p_date = p = nextfield(p); 870396Seric ent.p_time = p = nextfield(p); 871396Seric if (p == NULL || nextfield(p) != NULL) 872396Seric return (NULL); 873396Seric 874396Seric return (&ent); 875396Seric } 876396Seric 877396Seric 878396Seric char * 879396Seric nextfield(p) 880396Seric register char *p; 881396Seric { 882396Seric if (p == NULL || *p == '\0') 883396Seric return (NULL); 884396Seric while (*p != ' ' && *p != '\n' && *p != '\0') 885396Seric p++; 886396Seric if (*p == '\n' || *p == '\0') 887396Seric { 888396Seric *p = '\0'; 889396Seric return (NULL); 890396Seric } 891396Seric *p++ = '\0'; 892396Seric return (p); 893396Seric } 8941205Seric /* 8951205Seric ** USRERR -- issue user-level error 8961205Seric ** 8971205Seric ** Parameters: 8981205Seric ** f -- format string. 8991205Seric ** p1-p3 -- parameters to a printf. 9001205Seric ** 9011205Seric ** Returns: 9021205Seric ** -1 9031205Seric ** 9041205Seric ** Side Effects: 9051205Seric ** none. 9061205Seric */ 9071205Seric 9081205Seric usrerr(f, p1, p2, p3) 9091205Seric char *f; 9101205Seric { 9111205Seric fprintf(stderr, "\n%s: ", MyName); 9121205Seric fprintf(stderr, f, p1, p2, p3); 9131205Seric fprintf(stderr, "\n"); 9141205Seric 9151205Seric return (-1); 9161205Seric } 9171205Seric /* 9181205Seric ** SYSERR -- print system-generated error. 9191205Seric ** 9201205Seric ** Parameters: 9211205Seric ** f -- format string to a printf. 9221205Seric ** p1, p2, p3 -- parameters to f. 9231205Seric ** 9241205Seric ** Returns: 9251205Seric ** never. 9261205Seric ** 9271205Seric ** Side Effects: 9281205Seric ** none. 9291205Seric */ 9301205Seric 9311205Seric syserr(f, p1, p2, p3) 9321205Seric char *f; 9331205Seric { 9341205Seric extern int errno; 9351205Seric 9361205Seric fprintf(stderr, "\n%s SYSERR: ", MyName); 9371205Seric fprintf(stderr, f, p1, p2, p3); 9381205Seric fprintf(stderr, "\n"); 9391205Seric if (errno == 0) 9401205Seric exit(EX_SOFTWARE); 9411205Seric else 9421205Seric { 9431205Seric perror(0); 9441205Seric exit(EX_OSERR); 9451205Seric } 9461205Seric } 947