Commit | Line | Data |
---|---|---|
1cc2dcec EA |
1 | # include <stdio.h> |
2 | # include <sys/types.h> | |
3 | # include <sys/stat.h> | |
399058e3 | 4 | # include <sys/dir.h> |
1cc2dcec | 5 | # include <sysexits.h> |
7a8ba074 | 6 | # include <whoami.h> |
1cc2dcec | 7 | |
02ec3e87 | 8 | static char SccsId[] = "@(#)sccs.c 1.24 %G%"; |
2a0234bf EA |
9 | |
10 | # define bitset(bit, word) ((bit) & (word)) | |
11 | ||
12 | typedef char bool; | |
a97ecd0d EA |
13 | # define TRUE 1 |
14 | # define FALSE 0 | |
81dc6403 | 15 | |
1cc2dcec EA |
16 | struct sccsprog |
17 | { | |
18 | char *sccsname; /* name of SCCS routine */ | |
a97ecd0d EA |
19 | short sccsoper; /* opcode, see below */ |
20 | short sccsflags; /* flags, see below */ | |
1cc2dcec EA |
21 | char *sccspath; /* pathname of binary implementing */ |
22 | }; | |
23 | ||
a97ecd0d EA |
24 | /* values for sccsoper */ |
25 | # define PROG 0 /* call a program */ | |
e672376b | 26 | # define CMACRO 1 /* command substitution macro */ |
4535945e | 27 | # define FIX 2 /* fix a delta */ |
399058e3 | 28 | # define CLEAN 3 /* clean out recreatable files */ |
61886f2a | 29 | # define UNEDIT 4 /* unedit a file */ |
a97ecd0d | 30 | |
2a0234bf | 31 | /* bits for sccsflags */ |
a97ecd0d EA |
32 | # define NO_SDOT 0001 /* no s. on front of args */ |
33 | # define REALUSER 0002 /* protected (e.g., admin) */ | |
1cc2dcec | 34 | |
7a8ba074 EA |
35 | # ifdef CSVAX |
36 | # define PROGPATH(name) "/usr/local/name" | |
37 | # endif CSVAX | |
38 | ||
39 | # ifndef PROGPATH | |
40 | # define PROGPATH(name) "/usr/sccs/name" | |
41 | # endif PROGPATH | |
42 | ||
1cc2dcec EA |
43 | struct sccsprog SccsProg[] = |
44 | { | |
7a8ba074 EA |
45 | "admin", PROG, REALUSER, PROGPATH(admin), |
46 | "chghist", PROG, 0, PROGPATH(rmdel), | |
47 | "comb", PROG, 0, PROGPATH(comb), | |
48 | "delta", PROG, 0, PROGPATH(delta), | |
49 | "get", PROG, 0, PROGPATH(get), | |
50 | "help", PROG, NO_SDOT, PROGPATH(help), | |
51 | "prt", PROG, 0, PROGPATH(prt), | |
52 | "rmdel", PROG, REALUSER, PROGPATH(rmdel), | |
53 | "what", PROG, NO_SDOT, PROGPATH(what), | |
27e36d02 EA |
54 | "edit", CMACRO, 0, "get -e", |
55 | "delget", CMACRO, 0, "delta/get", | |
b7991d06 | 56 | "deledit", CMACRO, 0, "delta/get -e", |
e672376b | 57 | "del", CMACRO, 0, "delta/get", |
4535945e EA |
58 | "delt", CMACRO, 0, "delta/get", |
59 | "fix", FIX, 0, NULL, | |
db4904e0 EA |
60 | "clean", CLEAN, REALUSER, (char *) TRUE, |
61 | "info", CLEAN, REALUSER, (char *) FALSE, | |
61886f2a | 62 | "unedit", UNEDIT, 0, NULL, |
a97ecd0d | 63 | NULL, -1, 0, NULL |
1cc2dcec EA |
64 | }; |
65 | ||
61886f2a EA |
66 | struct pfile |
67 | { | |
68 | char *p_osid; /* old SID */ | |
69 | char *p_nsid; /* new SID */ | |
70 | char *p_user; /* user who did edit */ | |
71 | char *p_date; /* date of get */ | |
72 | char *p_time; /* time of get */ | |
73 | }; | |
74 | ||
2a0234bf | 75 | char *SccsPath = "SCCS"; /* pathname of SCCS files */ |
02ec3e87 | 76 | char *SccsDir = ""; /* directory to begin search from */ |
2a0234bf | 77 | bool RealUser; /* if set, running as real user */ |
27e36d02 EA |
78 | # ifdef DEBUG |
79 | bool Debug; /* turn on tracing */ | |
80 | # endif | |
1cc2dcec EA |
81 | |
82 | main(argc, argv) | |
83 | int argc; | |
84 | char **argv; | |
85 | { | |
86 | register char *p; | |
1045e3ba | 87 | extern struct sccsprog *lookup(); |
1cc2dcec EA |
88 | |
89 | /* | |
90 | ** Detect and decode flags intended for this program. | |
91 | */ | |
92 | ||
a97ecd0d EA |
93 | if (argc < 2) |
94 | { | |
95 | fprintf(stderr, "Usage: sccs [flags] command [flags]\n"); | |
96 | exit(EX_USAGE); | |
97 | } | |
98 | argv[argc] = NULL; | |
99 | ||
1045e3ba | 100 | if (lookup(argv[0]) == NULL) |
1cc2dcec | 101 | { |
1045e3ba | 102 | while ((p = *++argv) != NULL) |
1cc2dcec | 103 | { |
1045e3ba EA |
104 | if (*p != '-') |
105 | break; | |
106 | switch (*++p) | |
107 | { | |
108 | case 'r': /* run as real user */ | |
109 | setuid(getuid()); | |
110 | RealUser++; | |
111 | break; | |
112 | ||
113 | case 'p': /* path of sccs files */ | |
114 | SccsPath = ++p; | |
115 | break; | |
116 | ||
02ec3e87 EA |
117 | case 'd': /* directory to search from */ |
118 | SccsDir = ++p; | |
119 | break; | |
120 | ||
27e36d02 EA |
121 | # ifdef DEBUG |
122 | case 'T': /* trace */ | |
123 | Debug++; | |
124 | break; | |
125 | # endif | |
126 | ||
1045e3ba EA |
127 | default: |
128 | fprintf(stderr, "Sccs: unknown option -%s\n", p); | |
129 | break; | |
130 | } | |
1cc2dcec | 131 | } |
1045e3ba EA |
132 | if (SccsPath[0] == '\0') |
133 | SccsPath = "."; | |
1cc2dcec EA |
134 | } |
135 | ||
e672376b | 136 | command(argv, FALSE); |
a97ecd0d EA |
137 | exit(EX_OK); |
138 | } | |
139 | ||
e672376b | 140 | command(argv, forkflag) |
a97ecd0d | 141 | char **argv; |
e672376b | 142 | bool forkflag; |
a97ecd0d EA |
143 | { |
144 | register struct sccsprog *cmd; | |
145 | register char *p; | |
e672376b EA |
146 | register char *q; |
147 | char buf[40]; | |
1045e3ba | 148 | extern struct sccsprog *lookup(); |
9c3bf134 | 149 | char *nav[200]; |
27e36d02 | 150 | char **avp; |
9c3bf134 EA |
151 | register int i; |
152 | extern bool unedit(); | |
27e36d02 EA |
153 | |
154 | # ifdef DEBUG | |
155 | if (Debug) | |
156 | { | |
157 | printf("command:\n"); | |
158 | for (avp = argv; *avp != NULL; avp++) | |
159 | printf(" \"%s\"\n", *avp); | |
160 | } | |
161 | # endif | |
2a0234bf | 162 | |
1cc2dcec EA |
163 | /* |
164 | ** Look up command. | |
a97ecd0d | 165 | ** At this point, argv points to the command name. |
1cc2dcec EA |
166 | */ |
167 | ||
61886f2a | 168 | cmd = lookup(argv[0]); |
1045e3ba | 169 | if (cmd == NULL) |
1cc2dcec | 170 | { |
61886f2a | 171 | fprintf(stderr, "Sccs: Unknown command \"%s\"\n", argv[0]); |
1cc2dcec EA |
172 | exit(EX_USAGE); |
173 | } | |
174 | ||
2a0234bf | 175 | /* |
a97ecd0d | 176 | ** Interpret operation associated with this command. |
2a0234bf EA |
177 | */ |
178 | ||
a97ecd0d EA |
179 | switch (cmd->sccsoper) |
180 | { | |
181 | case PROG: /* call an sccs prog */ | |
e672376b EA |
182 | callprog(cmd->sccspath, cmd->sccsflags, argv, forkflag); |
183 | break; | |
184 | ||
185 | case CMACRO: /* command macro */ | |
186 | for (p = cmd->sccspath; *p != '\0'; p++) | |
187 | { | |
27e36d02 EA |
188 | avp = nav; |
189 | *avp++ = buf; | |
e672376b | 190 | for (q = buf; *p != '/' && *p != '\0'; p++, q++) |
27e36d02 EA |
191 | { |
192 | if (*p == ' ') | |
193 | { | |
194 | *q = '\0'; | |
195 | *avp++ = &q[1]; | |
196 | } | |
197 | else | |
198 | *q = *p; | |
199 | } | |
e672376b | 200 | *q = '\0'; |
27e36d02 EA |
201 | *avp = NULL; |
202 | xcommand(&argv[1], *p != '\0', nav[0], nav[1], nav[2], | |
203 | nav[3], nav[4], nav[5], nav[6]); | |
e672376b EA |
204 | } |
205 | fprintf(stderr, "Sccs internal error: CMACRO\n"); | |
a97ecd0d EA |
206 | exit(EX_SOFTWARE); |
207 | ||
4535945e | 208 | case FIX: /* fix a delta */ |
b8d1a34d | 209 | if (strncmp(argv[1], "-r", 2) != 0) |
4535945e EA |
210 | { |
211 | fprintf(stderr, "Sccs: -r flag needed for fix command\n"); | |
212 | break; | |
213 | } | |
214 | xcommand(&argv[1], TRUE, "get", "-k", NULL); | |
215 | xcommand(&argv[1], TRUE, "rmdel", NULL); | |
216 | xcommand(&argv[2], FALSE, "get", "-e", "-g", NULL); | |
217 | fprintf(stderr, "Sccs internal error: FIX\n"); | |
218 | exit(EX_SOFTWARE); | |
219 | ||
399058e3 | 220 | case CLEAN: |
db4904e0 | 221 | clean((bool) cmd->sccspath); |
399058e3 EA |
222 | break; |
223 | ||
61886f2a | 224 | case UNEDIT: |
9c3bf134 | 225 | i = 0; |
61886f2a | 226 | for (avp = &argv[1]; *avp != NULL; avp++) |
9c3bf134 EA |
227 | { |
228 | if (unedit(*avp)) | |
229 | nav[i++] = *avp; | |
230 | } | |
231 | nav[i] = NULL; | |
232 | if (i > 0) | |
233 | xcommand(nav, FALSE, "get", NULL); | |
61886f2a EA |
234 | break; |
235 | ||
a97ecd0d EA |
236 | default: |
237 | fprintf(stderr, "Sccs internal error: oper %d\n", cmd->sccsoper); | |
238 | exit(EX_SOFTWARE); | |
239 | } | |
240 | } | |
1045e3ba EA |
241 | \f/* |
242 | ** LOOKUP -- look up an SCCS command name. | |
243 | ** | |
244 | ** Parameters: | |
245 | ** name -- the name of the command to look up. | |
246 | ** | |
247 | ** Returns: | |
248 | ** ptr to command descriptor for this command. | |
249 | ** NULL if no such entry. | |
250 | ** | |
251 | ** Side Effects: | |
252 | ** none. | |
253 | */ | |
254 | ||
255 | struct sccsprog * | |
256 | lookup(name) | |
257 | char *name; | |
258 | { | |
259 | register struct sccsprog *cmd; | |
260 | ||
261 | for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) | |
262 | { | |
263 | if (strcmp(cmd->sccsname, name) == 0) | |
264 | return (cmd); | |
265 | } | |
266 | return (NULL); | |
267 | } | |
a97ecd0d | 268 | |
4535945e EA |
269 | |
270 | xcommand(argv, forkflag, arg0) | |
271 | char **argv; | |
272 | bool forkflag; | |
273 | char *arg0; | |
274 | { | |
275 | register char **av; | |
276 | char *newargv[1000]; | |
277 | register char **np; | |
278 | ||
279 | np = newargv; | |
280 | for (av = &arg0; *av != NULL; av++) | |
281 | *np++ = *av; | |
282 | for (av = argv; *av != NULL; av++) | |
283 | *np++ = *av; | |
284 | *np = NULL; | |
285 | command(newargv, forkflag); | |
286 | } | |
287 | ||
a97ecd0d EA |
288 | callprog(progpath, flags, argv, forkflag) |
289 | char *progpath; | |
290 | short flags; | |
291 | char **argv; | |
292 | bool forkflag; | |
293 | { | |
294 | register char *p; | |
295 | register char **av; | |
a97ecd0d EA |
296 | extern char *makefile(); |
297 | register int i; | |
e672376b | 298 | auto int st; |
e2758dc3 | 299 | register char **nav; |
a97ecd0d EA |
300 | |
301 | if (*argv == NULL) | |
302 | return (-1); | |
2a0234bf | 303 | |
1cc2dcec | 304 | /* |
4535945e | 305 | ** Fork if appropriate. |
1cc2dcec EA |
306 | */ |
307 | ||
a97ecd0d EA |
308 | if (forkflag) |
309 | { | |
27e36d02 EA |
310 | # ifdef DEBUG |
311 | if (Debug) | |
312 | printf("Forking\n"); | |
313 | # endif | |
a97ecd0d EA |
314 | i = fork(); |
315 | if (i < 0) | |
316 | { | |
317 | fprintf(stderr, "Sccs: cannot fork"); | |
318 | exit(EX_OSERR); | |
319 | } | |
320 | else if (i > 0) | |
e672376b EA |
321 | { |
322 | wait(&st); | |
323 | return (st); | |
324 | } | |
a97ecd0d EA |
325 | } |
326 | ||
4535945e EA |
327 | /* |
328 | ** Build new argument vector. | |
329 | */ | |
330 | ||
331 | /* copy program filename arguments and flags */ | |
e2758dc3 | 332 | nav = &argv[1]; |
4535945e EA |
333 | av = argv; |
334 | while ((p = *++av) != NULL) | |
335 | { | |
336 | if (!bitset(NO_SDOT, flags) && *p != '-') | |
e2758dc3 EA |
337 | *nav = makefile(p); |
338 | else | |
339 | *nav = p; | |
340 | if (*nav != NULL) | |
341 | nav++; | |
4535945e | 342 | } |
e2758dc3 | 343 | *nav = NULL; |
4535945e | 344 | |
a97ecd0d EA |
345 | /* |
346 | ** Set protection as appropriate. | |
347 | */ | |
348 | ||
349 | if (bitset(REALUSER, flags)) | |
350 | setuid(getuid()); | |
4535945e | 351 | |
a97ecd0d | 352 | /* |
4535945e | 353 | ** Call real SCCS program. |
a97ecd0d EA |
354 | */ |
355 | ||
4535945e | 356 | execv(progpath, argv); |
1cc2dcec | 357 | fprintf(stderr, "Sccs: cannot execute "); |
a97ecd0d | 358 | perror(progpath); |
1cc2dcec EA |
359 | exit(EX_UNAVAILABLE); |
360 | } | |
e2758dc3 EA |
361 | \f/* |
362 | ** MAKEFILE -- make filename of SCCS file | |
363 | ** | |
364 | ** If the name passed is already the name of an SCCS file, | |
365 | ** just return it. Otherwise, munge the name into the name | |
366 | ** of the actual SCCS file. | |
367 | ** | |
368 | ** There are cases when it is not clear what you want to | |
369 | ** do. For example, if SccsPath is an absolute pathname | |
370 | ** and the name given is also an absolute pathname, we go | |
371 | ** for SccsPath (& only use the last component of the name | |
372 | ** passed) -- this is important for security reasons (if | |
373 | ** sccs is being used as a setuid front end), but not | |
374 | ** particularly intuitive. | |
375 | ** | |
376 | ** Parameters: | |
377 | ** name -- the file name to be munged. | |
378 | ** | |
379 | ** Returns: | |
380 | ** The pathname of the sccs file. | |
381 | ** NULL on error. | |
382 | ** | |
383 | ** Side Effects: | |
384 | ** none. | |
385 | */ | |
1cc2dcec EA |
386 | |
387 | char * | |
388 | makefile(name) | |
389 | char *name; | |
390 | { | |
391 | register char *p; | |
392 | register char c; | |
393 | char buf[512]; | |
1cc2dcec | 394 | extern char *malloc(); |
e2758dc3 | 395 | extern char *rindex(); |
8e7b9e7e EA |
396 | extern bool isdir(); |
397 | register char *q; | |
e2758dc3 EA |
398 | |
399 | p = rindex(name, '/'); | |
400 | if (p == NULL) | |
401 | p = name; | |
402 | else | |
403 | p++; | |
1cc2dcec EA |
404 | |
405 | /* | |
8e7b9e7e | 406 | ** See if the name can be used as-is. |
1cc2dcec EA |
407 | */ |
408 | ||
8e7b9e7e EA |
409 | if (SccsPath[0] != '/' || name[0] == '/' || strncmp(name, "./", 2) == 0) |
410 | { | |
411 | if (strncmp(p, "s.", 2) == 0) | |
412 | return (name); | |
413 | if (isdir(name)) | |
414 | return (name); | |
415 | } | |
e2758dc3 EA |
416 | |
417 | /* | |
8e7b9e7e | 418 | ** Create the actual pathname. |
e2758dc3 EA |
419 | */ |
420 | ||
8e7b9e7e | 421 | if (name[0] != '/') |
1cc2dcec | 422 | { |
02ec3e87 | 423 | strcpy(buf, SccsDir); |
e2758dc3 EA |
424 | strcat(buf, "/"); |
425 | } | |
426 | else | |
427 | strcpy(buf, ""); | |
8e7b9e7e EA |
428 | strncat(buf, name, p - name); |
429 | q = &buf[strlen(buf)]; | |
430 | strcpy(q, p); | |
431 | if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) | |
e2758dc3 | 432 | { |
02ec3e87 EA |
433 | strcpy(q, SccsPath); |
434 | strcat(buf, "/s."); | |
e2758dc3 EA |
435 | strcat(buf, p); |
436 | } | |
1cc2dcec | 437 | |
02ec3e87 EA |
438 | if (strcmp(buf, name) == 0) |
439 | p = name; | |
440 | ||
441 | return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); | |
1cc2dcec | 442 | } |
399058e3 | 443 | \f/* |
8e7b9e7e EA |
444 | ** ISDIR -- return true if the argument is a directory. |
445 | ** | |
446 | ** Parameters: | |
447 | ** name -- the pathname of the file to check. | |
448 | ** | |
449 | ** Returns: | |
450 | ** TRUE if 'name' is a directory, FALSE otherwise. | |
451 | ** | |
452 | ** Side Effects: | |
453 | ** none. | |
454 | */ | |
455 | ||
456 | bool | |
457 | isdir(name) | |
458 | char *name; | |
459 | { | |
460 | struct stat stbuf; | |
461 | ||
462 | return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); | |
463 | } | |
464 | \f/* | |
e2758dc3 EA |
465 | ** SAFEPATH -- determine whether a pathname is "safe" |
466 | ** | |
467 | ** "Safe" pathnames only allow you to get deeper into the | |
468 | ** directory structure, i.e., full pathnames and ".." are | |
469 | ** not allowed. | |
470 | ** | |
471 | ** Parameters: | |
472 | ** p -- the name to check. | |
473 | ** | |
474 | ** Returns: | |
475 | ** TRUE -- if the path is safe. | |
476 | ** FALSE -- if the path is not safe. | |
477 | ** | |
478 | ** Side Effects: | |
479 | ** Prints a message if the path is not safe. | |
480 | */ | |
481 | ||
482 | bool | |
483 | safepath(p) | |
484 | register char *p; | |
485 | { | |
486 | extern char *index(); | |
487 | ||
488 | if (*p != '/') | |
489 | { | |
490 | while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) | |
491 | { | |
492 | p = index(p, '/'); | |
493 | if (p == NULL) | |
494 | return (TRUE); | |
495 | p++; | |
496 | } | |
497 | } | |
498 | ||
499 | printf("You may not use full pathnames or \"..\"\n"); | |
500 | return (FALSE); | |
501 | } | |
502 | \f/* | |
399058e3 EA |
503 | ** CLEAN -- clean out recreatable files |
504 | ** | |
505 | ** Any file for which an "s." file exists but no "p." file | |
506 | ** exists in the current directory is purged. | |
507 | ** | |
508 | ** Parameters: | |
db4904e0 EA |
509 | ** really -- if TRUE, remove everything. |
510 | ** else, just report status. | |
399058e3 EA |
511 | ** |
512 | ** Returns: | |
513 | ** none. | |
514 | ** | |
515 | ** Side Effects: | |
516 | ** removes files in the current directory. | |
517 | */ | |
518 | ||
db4904e0 EA |
519 | clean(really) |
520 | bool really; | |
399058e3 EA |
521 | { |
522 | struct direct dir; | |
523 | struct stat stbuf; | |
524 | char buf[100]; | |
bb9b2f29 | 525 | char pline[120]; |
db4904e0 EA |
526 | register FILE *dirfd; |
527 | register char *basefile; | |
d7f27f60 | 528 | bool gotedit; |
bb9b2f29 | 529 | FILE *pfp; |
399058e3 EA |
530 | |
531 | dirfd = fopen(SccsPath, "r"); | |
532 | if (dirfd == NULL) | |
533 | { | |
534 | fprintf(stderr, "Sccs: cannot open %s\n", SccsPath); | |
535 | return; | |
536 | } | |
537 | ||
538 | /* | |
539 | ** Scan the SCCS directory looking for s. files. | |
540 | */ | |
541 | ||
d7f27f60 | 542 | gotedit = FALSE; |
399058e3 EA |
543 | while (fread(&dir, sizeof dir, 1, dirfd) != NULL) |
544 | { | |
b8d1a34d | 545 | if (dir.d_ino == 0 || strncmp(dir.d_name, "s.", 2) != 0) |
399058e3 EA |
546 | continue; |
547 | ||
548 | /* got an s. file -- see if the p. file exists */ | |
549 | strcpy(buf, SccsPath); | |
550 | strcat(buf, "/p."); | |
db4904e0 | 551 | basefile = &buf[strlen(buf)]; |
b8d1a34d | 552 | strncpy(basefile, &dir.d_name[2], sizeof dir.d_name - 2); |
bb9b2f29 EA |
553 | basefile[sizeof dir.d_name - 2] = '\0'; |
554 | pfp = fopen(buf, "r"); | |
555 | if (pfp != NULL) | |
db4904e0 | 556 | { |
bb9b2f29 | 557 | while (fgets(pline, sizeof pline, pfp) != NULL) |
fdca7108 | 558 | printf("%12s: being edited: %s", basefile, pline); |
bb9b2f29 | 559 | fclose(pfp); |
d7f27f60 | 560 | gotedit = TRUE; |
399058e3 | 561 | continue; |
db4904e0 | 562 | } |
399058e3 EA |
563 | |
564 | /* the s. file exists and no p. file exists -- unlink the g-file */ | |
db4904e0 EA |
565 | if (really) |
566 | { | |
b8d1a34d | 567 | strncpy(buf, &dir.d_name[2], sizeof dir.d_name - 2); |
db4904e0 EA |
568 | buf[sizeof dir.d_name - 2] = '\0'; |
569 | unlink(buf); | |
570 | } | |
399058e3 EA |
571 | } |
572 | ||
573 | fclose(dirfd); | |
d7f27f60 | 574 | if (!gotedit && !really) |
fdca7108 | 575 | printf("Nothing being edited\n"); |
399058e3 | 576 | } |
61886f2a EA |
577 | \f/* |
578 | ** UNEDIT -- unedit a file | |
579 | ** | |
580 | ** Checks to see that the current user is actually editting | |
581 | ** the file and arranges that s/he is not editting it. | |
582 | ** | |
583 | ** Parameters: | |
fdca7108 | 584 | ** fn -- the name of the file to be unedited. |
61886f2a EA |
585 | ** |
586 | ** Returns: | |
9c3bf134 EA |
587 | ** TRUE -- if the file was successfully unedited. |
588 | ** FALSE -- if the file was not unedited for some | |
589 | ** reason. | |
61886f2a EA |
590 | ** |
591 | ** Side Effects: | |
592 | ** fn is removed | |
593 | ** entries are removed from pfile. | |
594 | */ | |
595 | ||
9c3bf134 | 596 | bool |
61886f2a EA |
597 | unedit(fn) |
598 | char *fn; | |
599 | { | |
600 | register FILE *pfp; | |
601 | char *pfn; | |
602 | static char tfn[] = "/tmp/sccsXXXXX"; | |
603 | FILE *tfp; | |
604 | register char *p; | |
605 | register char *q; | |
606 | bool delete = FALSE; | |
607 | bool others = FALSE; | |
608 | char *myname; | |
609 | extern char *getlogin(); | |
610 | struct pfile *pent; | |
611 | extern struct pfile *getpfile(); | |
612 | char buf[120]; | |
613 | ||
614 | /* make "s." filename & find the trailing component */ | |
615 | pfn = makefile(fn); | |
e2758dc3 EA |
616 | if (pfn == NULL) |
617 | return (FALSE); | |
618 | q = rindex(pfn, '/'); | |
619 | if (q == NULL) | |
620 | q = &pfn[-1]; | |
621 | if (q[1] != 's' || q[2] != '.') | |
61886f2a EA |
622 | { |
623 | fprintf(stderr, "Sccs: bad file name \"%s\"\n", fn); | |
9c3bf134 | 624 | return (FALSE); |
61886f2a EA |
625 | } |
626 | ||
627 | /* turn "s." into "p." */ | |
628 | *++q = 'p'; | |
629 | ||
630 | pfp = fopen(pfn, "r"); | |
631 | if (pfp == NULL) | |
632 | { | |
fdca7108 | 633 | printf("%12s: not being edited\n", fn); |
9c3bf134 | 634 | return (FALSE); |
61886f2a EA |
635 | } |
636 | ||
637 | /* | |
638 | ** Copy p-file to temp file, doing deletions as needed. | |
639 | */ | |
640 | ||
641 | mktemp(tfn); | |
642 | tfp = fopen(tfn, "w"); | |
643 | if (tfp == NULL) | |
644 | { | |
645 | fprintf(stderr, "Sccs: cannot create \"%s\"\n", tfn); | |
646 | exit(EX_OSERR); | |
647 | } | |
648 | ||
649 | myname = getlogin(); | |
650 | while ((pent = getpfile(pfp)) != NULL) | |
651 | { | |
652 | if (strcmp(pent->p_user, myname) == 0) | |
653 | { | |
654 | /* a match */ | |
655 | delete++; | |
656 | } | |
657 | else | |
658 | { | |
659 | fprintf(tfp, "%s %s %s %s %s\n", pent->p_osid, | |
660 | pent->p_nsid, pent->p_user, pent->p_date, | |
661 | pent->p_time); | |
662 | others++; | |
663 | } | |
664 | } | |
665 | ||
666 | /* do final cleanup */ | |
667 | if (others) | |
668 | { | |
669 | if (freopen(tfn, "r", tfp) == NULL) | |
670 | { | |
671 | fprintf(stderr, "Sccs: cannot reopen \"%s\"\n", tfn); | |
672 | exit(EX_OSERR); | |
673 | } | |
674 | if (freopen(pfn, "w", pfp) == NULL) | |
675 | { | |
676 | fprintf(stderr, "Sccs: cannot create \"%s\"\n", pfn); | |
9c3bf134 | 677 | return (FALSE); |
61886f2a EA |
678 | } |
679 | while (fgets(buf, sizeof buf, tfp) != NULL) | |
680 | fputs(buf, pfp); | |
681 | } | |
682 | else | |
683 | { | |
684 | unlink(pfn); | |
685 | } | |
686 | fclose(tfp); | |
687 | fclose(pfp); | |
688 | unlink(tfn); | |
689 | ||
690 | if (delete) | |
691 | { | |
692 | unlink(fn); | |
693 | printf("%12s: removed\n", fn); | |
9c3bf134 | 694 | return (TRUE); |
61886f2a EA |
695 | } |
696 | else | |
697 | { | |
fdca7108 | 698 | printf("%12s: not being edited by you\n", fn); |
9c3bf134 | 699 | return (FALSE); |
61886f2a EA |
700 | } |
701 | } | |
702 | \f/* | |
703 | ** GETPFILE -- get an entry from the p-file | |
704 | ** | |
705 | ** Parameters: | |
706 | ** pfp -- p-file file pointer | |
707 | ** | |
708 | ** Returns: | |
709 | ** pointer to p-file struct for next entry | |
710 | ** NULL on EOF or error | |
711 | ** | |
712 | ** Side Effects: | |
713 | ** Each call wipes out results of previous call. | |
714 | */ | |
715 | ||
716 | struct pfile * | |
717 | getpfile(pfp) | |
718 | FILE *pfp; | |
719 | { | |
720 | static struct pfile ent; | |
721 | static char buf[120]; | |
722 | register char *p; | |
723 | extern char *nextfield(); | |
724 | ||
725 | if (fgets(buf, sizeof buf, pfp) == NULL) | |
726 | return (NULL); | |
727 | ||
728 | ent.p_osid = p = buf; | |
729 | ent.p_nsid = p = nextfield(p); | |
730 | ent.p_user = p = nextfield(p); | |
731 | ent.p_date = p = nextfield(p); | |
732 | ent.p_time = p = nextfield(p); | |
733 | if (p == NULL || nextfield(p) != NULL) | |
734 | return (NULL); | |
735 | ||
736 | return (&ent); | |
737 | } | |
738 | ||
739 | ||
740 | char * | |
741 | nextfield(p) | |
742 | register char *p; | |
743 | { | |
744 | if (p == NULL || *p == '\0') | |
745 | return (NULL); | |
746 | while (*p != ' ' && *p != '\n' && *p != '\0') | |
747 | p++; | |
748 | if (*p == '\n' || *p == '\0') | |
749 | { | |
750 | *p = '\0'; | |
751 | return (NULL); | |
752 | } | |
753 | *p++ = '\0'; | |
754 | return (p); | |
755 | } |