1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may use this file only in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.opensource.org/licenses/cddl1.txt
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23 /* Copyright (c) 1988 AT&T */
24 /* All Rights Reserved */
25 /*
26 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
28 */
29 /*
30 * Copyright 2006-2020 J. Schilling
31 *
32 * @(#)get.c 1.97 20/09/07 J. Schilling
33 */
34 #if defined(sun)
35 #pragma ident "@(#)get.c 1.97 20/09/07 J. Schilling"
36 #endif
37 /*
38 * @(#)get.c 1.59 06/12/12
39 */
40
41 #if defined(sun)
42 #pragma ident "@(#)get.c"
43 #pragma ident "@(#)sccs:cmd/get.c"
44 #endif
45
46 #define SCCS_MAIN /* define global vars */
47 #include <defines.h>
48 #include <version.h>
49 #include <had.h>
50 #include <i18n.h>
51 #include <schily/utsname.h>
52 #include <ccstypes.h>
53 #include <schily/limits.h>
54 #include <schily/sysexits.h>
55
56 #define DATELEN 12
57
58 #if defined(__STDC__) || defined(PROTOTYPES)
59 #define INCPATH INS_BASE "/ccs/include" /* place to find binaries */
60 #else
61 /*
62 * XXX With a K&R compiler, you need to edit the following string in case
63 * XXX you like to change the install path.
64 */
65 #define INCPATH "/usr/ccs/include" /* place to find binaries */
66 #endif
67
68
69
70 /*
71 * Get is now much more careful than before about checking
72 * for write errors when creating the g- l- p- and z-files.
73 * However, it remains deliberately unconcerned about failures
74 * in writing statistical information (e.g., number of lines
75 * retrieved).
76 */
77
78 static struct packet gpkt; /* libcomobj data for get() */
79 static char Zhold[MAXPATHLEN]; /* temporary z-file name */
80
81 static Nparms N; /* Keep -N parameters */
82 static Xparms X; /* Keep -X parameters */
83 static struct sid sid; /* To hold -r parameter */
84 static unsigned Ser; /* To hold -a parameter */
85 static char *ilist; /* To hold -i parameter */
86 static char *elist; /* To hold -x parameter */
87 static char *lfile; /* To hold -l parameter */
88 static char *cutoffstr; /* To hold -c parameter */
89 static char Gfile[PATH_MAX]; /* To hold -G (g. + parameter) */
90 static char gfile[PATH_MAX]; /* To hold -G parameter */
91 static char *Cwd = ""; /* To hold -C parameter */
92
93 /* Beginning of modifications made for CMF system. */
94 #define CMRLIMIT 128
95 static char cmr[CMRLIMIT]; /* To hold -z parameter */
96 /* End of insertion */
97
98 static int num_files; /* arg counter for main()/get() */
99 static char Pfilename[FILESIZE]; /* p.filename used in get() */
100 static time_t cutoff = MAX_TIME; /* cutoff time for main()/get() */
101
102 static struct utsname un; /* uname for lockit() */
103 static char *uuname; /* un.nodename */
104
105
106 static void clean_up __PR((void));
107 static void enter __PR((struct packet *pkt, int ch, int n, struct sid *sidp));
108
109 int main __PR((int argc, char **argv));
110 static void do_get __PR((char *file));
111 static void get __PR((struct packet *pkt, char *file));
112 static void gen_lfile __PR((struct packet *pkt));
113 static void prfx __PR((struct packet *pkt));
114 static void annotate __PR((struct packet *pkt));
115 static void wrtpfile __PR((struct packet *pkt, char *inc, char *exc));
116 static int cmrinsert __PR((struct packet *pkt));
117
118 int
main(argc,argv)119 main(argc,argv)
120 int argc;
121 register char *argv[];
122 {
123 register int i;
124 register char *p;
125 int c;
126 extern int Fcnt;
127 int current_optind;
128 int no_arg;
129 int cmri = 0; /* CMF index for option parsing */
130
131 /*
132 * Set locale for all categories.
133 */
134 setlocale(LC_ALL, NOGETTEXT(""));
135
136 sccs_setinsbase(INS_BASE);
137
138 /*
139 * Set directory to search for general l10n SCCS messages.
140 */
141 #ifdef PROTOTYPES
142 (void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
143 NOGETTEXT(INS_BASE "/" SCCS_BIN_PRE "lib/locale/"));
144 #else
145 (void) bindtextdomain(NOGETTEXT("SUNW_SPRO_SCCS"),
146 NOGETTEXT("/usr/ccs/lib/locale/"));
147 #endif
148 (void) textdomain(NOGETTEXT("SUNW_SPRO_SCCS"));
149
150 tzset(); /* Set up timezome related vars */
151
152 #ifdef SCHILY_BUILD
153 save_args(argc, argv);
154 #endif
155 set_clean_up(clean_up);
156 Fflags = FTLEXIT | FTLMSG | FTLCLN;
157 #ifdef SCCS_FATALHELP
158 Fflags |= FTLFUNC;
159 Ffunc = sccsfatalhelp;
160 #endif
161 #ifdef XPG4
162 sccs_xpg4(TRUE);
163 #endif
164 current_optind = 1;
165 optind = 1;
166 opterr = 0;
167 no_arg = 0;
168 i = 1;
169 /*CONSTCOND*/
170 while (1) {
171 if (current_optind < optind) {
172 current_optind = optind;
173 argv[i] = 0;
174 if (optind > i+1) {
175 if ((argv[i+1][0] != '-') && (no_arg == 0)) {
176 argv[i+1] = NULL;
177 } else {
178 optind = i+1;
179 current_optind = optind;
180 }
181 }
182 }
183 no_arg = 0;
184 i = current_optind;
185 c = getopt(argc, argv, "()-r:c:ebi:x:kl:Lpsmnogta:G:w:zqdC:AFN:X:V(version)");
186 /* this takes care of options given after
187 ** file names.
188 */
189 if (c == EOF) {
190 if (optind < argc) {
191 /* if it's due to -- then break; */
192 if (argv[i][0] == '-' &&
193 argv[i][1] == '-') {
194 argv[i] = 0;
195 break;
196 }
197 optind++;
198 current_optind = optind;
199 continue;
200 } else {
201 break;
202 }
203 }
204 p = optarg;
205 switch (c) {
206 case CMFFLAG: /* 'z' */
207 /* Concatenate the rest of this argument with
208 * the existing CMR list. */
209 if (p) {
210 while (*p) {
211 if (cmri == CMRLIMIT)
212 fatal(gettext("CMR list is too long."));
213 cmr[cmri++] = *p++;
214 }
215 }
216 cmr[cmri] = '\0';
217 break;
218 case 'a':
219 if (*p == 0) continue;
220 Ser = patoi(p);
221 break;
222 case 'r':
223 if (argv[i][2] == '\0') {
224 if (*(omit_sid(p)) != '\0') {
225 no_arg = 1;
226 continue;
227 }
228 }
229 chksid(sid_ab(p, &sid), &sid);
230 if ((sid.s_rel < MINR) || (sid.s_rel > MAXR)) {
231 fatal(gettext("r out of range (ge22)"));
232 }
233 break;
234 case 'w':
235 if (p) whatsetup(p);
236 break;
237 case 'c':
238 if (*p == 0) continue;
239 cutoffstr = p;
240 #if defined(BUG_1205145) || defined(GMT_TIME)
241 if (parse_date(p,&cutoff, 0))
242 #else
243 if (parse_date(p,&cutoff, PF_GMT))
244 #endif
245 fatal(gettext("bad date/time (cm5)"));
246 break;
247 case 'L':
248 /* treat this as if -lp was given */
249 lfile = NOGETTEXT("stdout");
250 c = 'l';
251 break;
252 case 'l':
253 if (argv[i][2] != '\0') {
254 if ((*p == 'p') && (strlen(p) == 1)) {
255 p = NOGETTEXT("stdout");
256 }
257 lfile = p;
258 } else {
259 no_arg = 1;
260 lfile = NULL;
261 }
262 break;
263 case 'i':
264 if (*p == 0) continue;
265 ilist = p;
266 break;
267 case 'x':
268 if (*p == 0) continue;
269 elist = p;
270 break;
271 case 'G':
272 {
273 char *cp;
274
275 if (*p == 0) continue;
276 copy(p, gfile);
277 cp = auxf(p, 'G');
278 copy(cp, Gfile);
279 break;
280 }
281 case 'b':
282 case 'g':
283 case 'e':
284 case 'p':
285 case 'k':
286 case 'm':
287 case 'n':
288 case 'o':
289 case 's':
290 case 't':
291 case 'd':
292 case 'A':
293 case 'F':
294 if (p) {
295 sprintf(SccsError,
296 gettext("value after %c arg (cm8)"),
297 c);
298 fatal(SccsError);
299 }
300 break;
301 case 'q': /* enable NSE mode */
302 if (p) {
303 if (*p) {
304 nsedelim = p;
305 }
306 } else {
307 nsedelim = (char *) 0;
308 }
309 break;
310 case 'C':
311 Cwd = p;
312 break;
313
314 case 'N': /* Bulk names */
315 initN(&N);
316 if (optarg == argv[i+1]) {
317 no_arg = 1;
318 break;
319 }
320 N.n_parm = p;
321 break;
322
323 case 'X': /* -Xtended options */
324 X.x_parm = optarg;
325 X.x_flags = XO_NULLPATH;
326 if (!parseX(&X))
327 goto err;
328 had[NLOWER+c-'A'] = 0; /* Allow mult -X */
329 break;
330
331 case 'V': /* version */
332 printf(gettext(
333 "get %s-SCCS version %s %s (%s-%s-%s)\n"),
334 PROVIDER,
335 VERSION,
336 VDATE,
337 HOST_CPU, HOST_VENDOR, HOST_OS);
338 exit(EX_OK);
339
340 default:
341 err:
342 fatal(gettext("Usage: get [-AbeFgkmLopst] [-l[p]] [-asequence] [-cdate-time] [-Gg-file]\n\t[-isid-list] [-rsid] [-xsid-list][ -N[bulk-spec]][ -Xxopts ] s.filename ..."));
343 }
344
345 /*
346 * Make sure that we only collect option letters from
347 * the range 'a'..'z' and 'A'..'Z'.
348 */
349 if (ALPHA(c) &&
350 (had[LOWER(c)? c-'a' : NLOWER+c-'A']++)) {
351 if (c != 'X')
352 fatal(gettext("key letter twice (cm2)"));
353 }
354 }
355 for (i=1; i<argc; i++) {
356 if (argv[i]) {
357 num_files++;
358 }
359 }
360 if(num_files == 0)
361 fatal(gettext("missing file arg (cm3)"));
362 if (HADE && HADM)
363 fatal(gettext("e not allowed with m (ge3)"));
364 if (HADE)
365 HADK = 1;
366 if (HADE && !logname())
367 fatal(gettext("User ID not in password file (cm9)"));
368
369 setsig();
370 xsethome(NULL);
371 if (HADUCN) { /* Parse -N args */
372 /*
373 * initN() was already called while parsing options.
374 */
375 if (!HADP && !HADG && !HADUCG) /* Create g-file, so */
376 N.n_flags |= N_GETI; /* create subdirs */
377
378 parseN(&N);
379
380 if (N.n_sdot && (sethomestat & SETHOME_OFFTREE))
381 fatal(gettext("-Ns. not supported in off-tree project mode"));
382 }
383
384 /*
385 * Get the name of our machine to be used for the lockfile.
386 */
387 uname(&un);
388 uuname = un.nodename;
389
390 /*
391 * Set up a project global lock on the changeset file.
392 * Since we set FTLJMP, we do not need to unlockchset() from clean_up().
393 */
394 if (HADE && SETHOME_CHSET())
395 lockchset(getppid(), getpid(), uuname);
396 timerchsetlock();
397
398 Fflags &= ~FTLEXIT;
399 Fflags |= FTLJMP;
400 for (i=1; i<argc; i++)
401 if ((p=argv[i]) != NULL)
402 do_file(p, do_get, 1, N.n_sdot, &X);
403
404 /*
405 * Only remove the global lock it it was created by us and not by
406 * our parent.
407 */
408 if (HADE && SETHOME_CHSET()) {
409 if (HADUCN)
410 bulkchdir(&N);
411 unlockchset(getpid(), uuname);
412 }
413
414 return (Fcnt ? 1 : 0);
415 }
416
417 static void
do_get(file)418 do_get(file)
419 char *file;
420 {
421 get(&gpkt, file);
422 }
423
424 static void
get(pkt,file)425 get(pkt, file)
426 struct packet *pkt;
427 char *file;
428 {
429 register char *p;
430 register unsigned ser;
431 extern char had_dir, had_standinp;
432 struct stats stats;
433 char str[SID_STRSIZE]; /* Must fit a SID string */
434 #ifdef PROTOTYPES
435 char template[] = NOGETTEXT("/get.XXXXXX");
436 #else
437 char *template = NOGETTEXT("/get.XXXXXX");
438 #endif
439 char *ifile;
440 char buf1[PATH_MAX];
441 uid_t holduid;
442 gid_t holdgid;
443 static int first = 1;
444
445 if (setjmp(Fjmp))
446 return;
447
448 /*
449 * In order to make the global lock with a potentially long duration
450 * not look as if it was expired, we refresh it for every file in our
451 * task list. This is needed since another SCCS instance on a different
452 * NFS machine cannot use kill() to check for a still active process.
453 */
454 if (HADE && SETHOME_CHSET()) {
455 if (HADUCN)
456 bulkchdir(&N); /* Done by bulkprepare() anyway */
457 refreshchsetlock();
458 }
459
460 if (HADUCN) {
461 #ifdef __needed__
462 char *ofile = file;
463 #endif
464
465 file = bulkprepare(&N, file);
466 if (file == NULL) {
467 #ifdef __needed__
468 if (N.n_ifile)
469 ofile = N.n_ifile;
470 #endif
471 /*
472 * The error is typically
473 * "directory specified as s-file (cm14)"
474 */
475 fatal(gettext(bulkerror(&N)));
476 }
477 ifile = N.n_ifile;
478 if (sid.s_rel == 0 && N.n_sid.s_rel != 0) {
479 sid.s_rel = N.n_sid.s_rel;
480 sid.s_lev = N.n_sid.s_lev;
481 sid.s_br = N.n_sid.s_br;
482 sid.s_seq = N.n_sid.s_seq;
483 }
484 } else {
485 ifile = NULL;
486 }
487
488 if (HADE) {
489
490 /*
491 call `sinit' to check if file is an SCCS file
492 but don't open the SCCS file.
493 If it is, then create lock file.
494 pass both the PID and machine name to lockit
495 */
496 sinit(pkt, file, SI_INIT);
497
498 /*
499 * Lock out any other user who may be trying to process
500 * the same file.
501 */
502 if (!islockchset(copy(auxf(file, 'z'), Zhold)) &&
503 lockit(Zhold, SCCS_LOCK_ATTEMPTS, getpid(), uuname)) {
504 lockfatal(Zhold, getpid(), uuname);
505 } else {
506 timersetlockfile(Zhold);
507 }
508 }
509 /*
510 Open SCCS file and initialize packet
511 */
512 sinit(pkt, file, SI_OPEN);
513 pkt->p_ixuser = (HADI | HADX);
514 pkt->p_reqsid.s_rel = sid.s_rel;
515 pkt->p_reqsid.s_lev = sid.s_lev;
516 pkt->p_reqsid.s_br = sid.s_br;
517 pkt->p_reqsid.s_seq = sid.s_seq;
518 pkt->p_verbose = (HADS) ? 0 : 1;
519 pkt->p_stdout = (HADP||lfile) ? stderr : stdout;
520 pkt->p_cutoff = cutoff;
521 pkt->p_lfile = lfile;
522 pkt->p_enter = enter;
523 #if defined(BUG_1205145) || defined(GMT_TIME)
524 #else
525 if ((pkt->p_flags & PF_V6) == 0) {
526 pkt->p_flags |= PF_GMT;
527 } else if (cutoffstr != NULL) {
528 if (parse_date(cutoffstr, &cutoff, 0))
529 fatal(gettext("bad date/time (cm5)"));
530 pkt->p_cutoff = cutoff;
531 }
532 #endif
533 if (HADUCA)
534 pkt->p_pgmrs = (char **)Null;
535 if (Gfile[0] == 0 || !first) {
536 gfile[0] = '\0';
537 strlcatl(gfile, sizeof (gfile),
538 Cwd,
539 ifile ? ifile :
540 auxf(pkt->p_file, 'g'),
541 (char *)0);
542 Gfile[0] = '\0';
543 strlcatl(Gfile, sizeof (Gfile),
544 Cwd,
545 ifile ? auxf(ifile, 'G') :
546 auxf(pkt->p_file, 'A'),
547 (char *)0);
548 }
549 strlcpy(buf1, dname(Gfile), sizeof (buf1));
550 strlcat(buf1, template, sizeof (buf1));
551 Gfile[0] = '\0'; /* File not yet created */
552 first = 0;
553
554 if (pkt->p_verbose && (num_files > 1 || had_dir || had_standinp))
555 fprintf(pkt->p_stdout, "\n%s:\n", pkt->p_file);
556 if (dodelt(pkt, &stats, (struct sid *) 0, 0) == 0)
557 fmterr(pkt);
558 finduser(pkt);
559 doflags(pkt);
560 donamedflags(pkt);
561 dometa(pkt);
562
563 if (!HADA) {
564 ser = getser(pkt);
565 } else {
566 if ((ser = Ser) > maxser(pkt))
567 fatal(gettext("serial number too large (ge19)"));
568 pkt->p_gotsid = pkt->p_idel[ser].i_sid;
569 if (HADR && sid.s_rel != pkt->p_gotsid.s_rel) {
570 zero((char *) &pkt->p_reqsid, sizeof (pkt->p_reqsid));
571 pkt->p_reqsid.s_rel = sid.s_rel;
572 } else {
573 pkt->p_reqsid = pkt->p_gotsid;
574 }
575 }
576 doie(pkt, ilist, elist, (char *) 0);
577 setup(pkt, (int) ser);
578 if (!(HADP || HADG)) {
579 if (exists(gfile) && (S_IWRITE & Statbuf.st_mode)) {
580 sprintf(SccsError, gettext("writable `%s' exists (ge4)"),
581 gfile);
582 fatal(SccsError);
583 }
584 }
585 if (pkt->p_verbose) {
586 sid_ba(&pkt->p_gotsid, str);
587 fprintf(pkt->p_stdout, "%s\n", str);
588 }
589 if (HADE) {
590 if (HADC || pkt->p_reqsid.s_rel == 0)
591 pkt->p_reqsid = pkt->p_gotsid;
592 newsid(pkt, pkt->p_sflags[BRCHFLAG - 'a'] && HADB);
593 permiss(pkt);
594 wrtpfile(pkt, ilist, elist);
595 }
596 if (!HADG || HADL) {
597 if (pkt->p_stdout) {
598 fflush(pkt->p_stdout);
599 fflush(stderr);
600 }
601 holduid=geteuid();
602 holdgid=getegid();
603 setuid(getuid());
604 setgid(getgid());
605 if (HADL)
606 gen_lfile(pkt);
607 if (HADG)
608 goto unlock;
609
610 if (pkt->p_idel[ser].i_dtype == 'U') { /* unlink delta */
611 unlink(gfile);
612 goto unlock;
613 }
614
615 flushto(pkt, EUSERTXT, FLUSH_NOCOPY);
616 idsetup(pkt, gfile);
617 pkt->p_chkeof = 1;
618 pkt->p_did_id = 0;
619 /*
620 call `xcreate' which deletes the old `g-file' and
621 creates a new one except if the `p' keyletter is set in
622 which case any old `g-file' is not disturbed.
623 The mod of the file depends on whether or not the `k'
624 keyletter has been set.
625 */
626 if (pkt->p_gout == 0) {
627 if (HADP) {
628 pkt->p_gout = stdout;
629 } else {
630 #ifdef HAVE_MKSTEMP
631 close(mkstemp(buf1)); /* safer than mktemp */
632 #else
633 mktemp(buf1);
634 #endif
635 copy(buf1, Gfile);
636 /*
637 * It may be better to use fdopen() and chmod()
638 * instead of xfcreat() in order to avoid an
639 * unlink()/create() chain.
640 */
641 if ((exists(pkt->p_file) && (S_IEXEC & Statbuf.st_mode)) ||
642 (pkt->p_flags & PF_SCOX)) {
643 pkt->p_gout = xfcreat(Gfile, HADK ?
644 ((mode_t)0755) : ((mode_t)0555));
645 } else {
646 pkt->p_gout = xfcreat(Gfile, HADK ?
647 ((mode_t)0644) : ((mode_t)0444));
648 }
649 #ifdef USE_SETVBUF
650 /*
651 * Do not call setvbuf() with stdout as this may result
652 * in a second illegal call in gen_lfile().
653 */
654 setvbuf(pkt->p_gout, NULL, _IOFBF, VBUF_SIZE);
655 #endif
656 }
657 }
658 pkt->p_ghash = 0; /* Reset ghash */
659 if (pkt->p_encoding & EF_UUENCODE) {
660 while (readmod(pkt)) {
661 decode(pkt->p_line, pkt->p_gout);
662 }
663 } else {
664 while (readmod(pkt)) {
665 prfx(pkt);
666 if (pkt->p_flags & PF_NONL) {
667 pkt->p_line[--(pkt->p_line_length)] = '\0';
668 pkt->p_line_length -= 2; /* ^AN */
669 }
670 p = idsubst(pkt, pkt->p_lineptr);
671 if (p != pkt->p_lineptr) {
672 if (fputs(p, pkt->p_gout) == EOF)
673 xmsg(gfile, NOGETTEXT("get"));
674 } else {
675 if (fwrite(p, 1, pkt->p_line_length,
676 pkt->p_gout) != pkt->p_line_length)
677 xmsg(gfile, NOGETTEXT("get"));
678 }
679 if (pkt->p_flags & PF_NONL) {
680 pkt->p_line_length += 2; /* ^AN */
681 pkt->p_line[(pkt->p_line_length)++] = '\n';
682 }
683 }
684 }
685 if ((pkt->p_flags & (PF_V6 | PF_V6TAGS)) && pkt->p_hash) {
686 /*
687 * SCCS v6 is able to check against SID specific
688 * checksums, but this will not work in case that
689 * we use include or exclude lists.
690 */
691 if (pkt->p_hash[ser] != (pkt->p_ghash & 0xFFFF) &&
692 elist == NULL && ilist == NULL && !HADUCF)
693 fatal(gettext("corrupted file version (co27)"));
694 }
695
696 if (pkt->p_gout) {
697 if (fflush(pkt->p_gout) == EOF)
698 xmsg(gfile, NOGETTEXT("get"));
699 fflush (stderr);
700 }
701 if (pkt->p_gout && pkt->p_gout != stdout) {
702 /*
703 * Force g-file to disk and verify
704 * that it actually got there.
705 */
706 #ifdef HAVE_FSYNC
707 if (fsync(fileno(pkt->p_gout)) < 0)
708 xmsg(gfile, NOGETTEXT("get"));
709 #endif
710 if (fclose(pkt->p_gout) == EOF)
711 xmsg(gfile, NOGETTEXT("get"));
712 pkt->p_gout = NULL;
713 }
714 if (pkt->p_verbose) {
715 #ifdef XPG4
716 fprintf(pkt->p_stdout, NOGETTEXT("%d lines\n"), pkt->p_glnno);
717 #else
718 if (HADD == 0)
719 fprintf(pkt->p_stdout, gettext("%d lines\n"), pkt->p_glnno);
720 #endif
721 }
722 if (!pkt->p_did_id && !HADK && !HADQ &&
723 (!pkt->p_sflags[EXPANDFLAG - 'a'] ||
724 *(pkt->p_sflags[EXPANDFLAG - 'a']))) {
725 if (pkt->p_sflags[IDFLAG - 'a']) {
726 if (!(*pkt->p_sflags[IDFLAG - 'a'])) {
727 fatal(gettext("no id keywords (cm6)"));
728 } else {
729 fatal(gettext("invalid id keywords (cm10)"));
730 }
731 } else {
732 if (pkt->p_verbose) {
733 fprintf(stderr, gettext("No id keywords (cm7)\n"));
734 }
735 }
736 }
737 if (Gfile[0] != '\0' && exists(Gfile)) {
738 rename(Gfile, gfile);
739 if (HADO) {
740 #if defined(BUG_1205145) || defined(GMT_TIME)
741 #else
742 struct tm tm;
743 #endif
744 struct timespec ts[2];
745 unsigned int gser;
746 extern dtime_t Timenow;
747
748 gser = sidtoser(&pkt->p_gotsid, pkt);
749
750 ts[0].tv_sec = Timenow.dt_sec;
751 ts[0].tv_nsec = Timenow.dt_nsec;
752 ts[1].tv_sec = pkt->p_idel[gser].i_datetime.tv_sec;
753 ts[1].tv_nsec = pkt->p_idel[gser].i_datetime.tv_nsec;
754 /*
755 * If we did cheat while scanning the delta
756 * table and converted the time stamps assuming
757 * GMT. Fix the resulting error here.
758 */
759 #if defined(BUG_1205145) || defined(GMT_TIME)
760 #else
761 if (pkt->p_flags & PF_GMT) {
762 tm = *gmtime(&ts[1].tv_sec);
763 tm.tm_isdst = -1;
764 ts[1].tv_sec = mktime(&tm);
765 }
766 #endif
767 /*
768 * As SunPro make and gmake call sccs
769 * get when the time if s.file equals
770 * the time stamp of the g-file, make
771 * sure the g-file is a bit younger.
772 */
773 if (!(gpkt.p_flags & PF_V6)) {
774 struct timespec tn;
775
776 getnstimeofday(&tn);
777 ts[1].tv_nsec = tn.tv_nsec;
778 }
779 if (ts[1].tv_nsec <= 500000000)
780 ts[1].tv_nsec += 499999999;
781
782 utimensat(AT_FDCWD, gfile, ts, 0);
783 }
784 }
785 setuid(holduid);
786 setgid(holdgid);
787 }
788 unlock:
789 if (HADE) {
790 copy(auxf(pkt->p_file, 'p'), Pfilename);
791 rename(auxf(pkt->p_file, 'q'), Pfilename);
792 timersetlockfile(NULL);
793 if (!islockchset(Zhold))
794 unlockit(Zhold, getpid(), uuname);
795 }
796 sclose(pkt);
797 sfree(pkt);
798 ffreeall();
799 if (HADUCA) /* ffreeall() killed it */
800 lhash_destroy(); /* need to reset it */
801 }
802
803 static void
enter(pkt,ch,n,sidp)804 enter(pkt,ch,n,sidp)
805 struct packet *pkt;
806 char ch;
807 int n;
808 struct sid *sidp;
809 {
810 char str[SID_STRSIZE]; /* Must fit a SID string */
811 register struct apply *ap;
812
813 sid_ba(sidp,str);
814 if (pkt->p_verbose)
815 fprintf(pkt->p_stdout,"%s\n",str);
816 ap = &pkt->p_apply[n];
817 switch (ap->a_code) {
818
819 case SX_EMPTY:
820 if (ch == INCLUDE)
821 condset(ap,APPLY,INCLUSER);
822 else
823 condset(ap,NOAPPLY,EXCLUSER);
824 break;
825 case APPLY:
826 sid_ba(sidp,str);
827 sprintf(SccsError, gettext("%s already included (ge9)"), str);
828 fatal(SccsError);
829 break;
830 case NOAPPLY:
831 sid_ba(sidp,str);
832 sprintf(SccsError, gettext("%s already excluded (ge10)"), str);
833 fatal(SccsError);
834 break;
835 default:
836 fatal(gettext("internal error in get/enter() (ge11)"));
837 break;
838 }
839 }
840
841 static void
gen_lfile(pkt)842 gen_lfile(pkt)
843 register struct packet *pkt;
844 {
845 char *n;
846 int reason;
847 char str[max(DT_ZSTRSIZE, SID_STRSIZE)]; /* SCCS v6 date or SID str */
848 char line[BUFSIZ];
849 struct deltab dt;
850 FILE *in;
851 FILE *out;
852 char *outname = NOGETTEXT("stdout");
853
854 #define OUTPUTC(c) \
855 if (putc((c), out) == EOF) \
856 xmsg(outname, NOGETTEXT("gen_lfile"));
857
858 in = xfopen(pkt->p_file, O_RDONLY|O_BINARY);
859 if (pkt->p_lfile) {
860 /*
861 * Do not call setvbuf() with stdout as this may result
862 * in a second illegal call in get().
863 */
864 out = stdout;
865 } else {
866 outname = auxf(pkt->p_file, 'l');
867 out = xfcreat(outname,(mode_t)0444);
868 }
869 fgets(line,sizeof(line),in);
870 while (fgets(line,sizeof(line),in) != NULL &&
871 line[0] == CTLCHAR && line[1] == STATS) {
872 fgets(line,sizeof(line),in);
873 del_ab(line,&dt,pkt);
874 if (dt.d_type == 'D' || dt.d_type == 'U') {
875 reason = pkt->p_apply[dt.d_serial].a_reason;
876 if (pkt->p_apply[dt.d_serial].a_code == APPLY) {
877 OUTPUTC(' ');
878 OUTPUTC(' ');
879 } else {
880 OUTPUTC('*');
881 if (reason & IGNR) {
882 OUTPUTC(' ');
883 } else {
884 OUTPUTC('*');
885 }
886 }
887 switch (reason & (INCL | EXCL | CUTOFF)) {
888
889 case INCL:
890 OUTPUTC('I');
891 break;
892 case EXCL:
893 OUTPUTC('X');
894 break;
895 case CUTOFF:
896 OUTPUTC('C');
897 break;
898 default:
899 OUTPUTC(' ');
900 break;
901 }
902 OUTPUTC(' ');
903 sid_ba(&dt.d_sid,str);
904 if (fprintf(out, "%s\t", str) == EOF)
905 xmsg(outname, NOGETTEXT("gen_lfile"));
906 /*
907 * POSIX requires a 2-digit year for the l-file.
908 * We use 4-digits before 1969 and past 2068
909 * which is outside the year range specified by POSIX.
910 */
911 #if SIZEOF_TIME_T == 4
912 if (dt.d_dtime.dt_sec < Y1969)
913 #else
914 if ((dt.d_dtime.dt_sec < Y1969) ||
915 (dt.d_dtime.dt_sec >= Y2069))
916 #endif
917 date_bal(&dt.d_dtime.dt_sec,str, pkt->p_flags); /* 4 digit year */
918 else
919 date_ba(&dt.d_dtime.dt_sec,str, pkt->p_flags); /* 2 digit year */
920 if (fprintf(out, "%s %s\n", str, dt.d_pgmr) == EOF)
921 xmsg(outname, NOGETTEXT("gen_lfile"));
922 }
923 while ((n = fgets(line,sizeof(line),in)) != NULL) {
924 if (line[0] != CTLCHAR) {
925 break;
926 } else {
927 switch (line[1]) {
928
929 case EDELTAB:
930 break;
931 default:
932 continue;
933 case MRNUM:
934 case COMMENTS:
935 if (dt.d_type == 'D' || dt.d_type == 'U') {
936 if (fprintf(out,"\t%s",&line[3]) == EOF)
937 xmsg(outname,
938 NOGETTEXT("gen_lfile"));
939 }
940 continue;
941 }
942 break;
943 }
944 }
945 if (n == NULL || line[0] != CTLCHAR)
946 break;
947 OUTPUTC('\n');
948 }
949 fclose(in);
950 if (out != stdout) {
951 #ifdef HAVE_FSYNC
952 if (fsync(fileno(out)) < 0)
953 xmsg(outname, NOGETTEXT("gen_lfile"));
954 #endif
955 if (fclose(out) == EOF)
956 xmsg(outname, NOGETTEXT("gen_lfile"));
957 }
958
959 #undef OUTPUTC
960 }
961
962 static void
prfx(pkt)963 prfx(pkt)
964 register struct packet *pkt;
965 {
966 char str[SID_STRSIZE]; /* Must fit a SID string */
967
968 if (HADN)
969 if (fprintf(pkt->p_gout, "%s\t", getmodname()) == EOF)
970 xmsg(gfile, NOGETTEXT("prfx"));
971 if (HADM) {
972 sid_ba(&pkt->p_inssid,str);
973 if (fprintf(pkt->p_gout, "%s\t", str) == EOF)
974 xmsg(gfile, NOGETTEXT("prfx"));
975 }
976 if (pkt->p_pgmrs)
977 annotate(pkt);
978 }
979
980 static void
annotate(pkt)981 annotate(pkt)
982 register struct packet *pkt;
983 {
984 char str[SID_STRSIZE]; /* Must fit a SID string */
985 char *p;
986
987 date_ba(&pkt->p_idel[pkt->p_insser].i_datetime.tv_sec,
988 str, pkt->p_flags);
989 p = strchr(str, ' ');
990 if (p)
991 *p = '\0';
992 if (fprintf(pkt->p_gout, "%-8s\t%s\t",
993 pkt->p_pgmrs[pkt->p_insser], str) == EOF)
994 xmsg(gfile, NOGETTEXT("prfx"));
995 }
996
997 static void
clean_up()998 clean_up()
999 {
1000 /*
1001 clean_up is only called from fatal() upon bad termination.
1002 */
1003 if (gpkt.p_gout) {
1004 fflush(gpkt.p_gout);
1005 }
1006 if (gpkt.p_gout && gpkt.p_gout != stdout) {
1007 fclose(gpkt.p_gout);
1008 gpkt.p_gout = NULL;
1009 unlink(Gfile);
1010 }
1011 if (HADE) {
1012 uname(&un);
1013 uuname = un.nodename;
1014 if (mylock(auxf(gpkt.p_file,'z'), getpid(), uuname)) {
1015 unlink(auxf(gpkt.p_file,'q'));
1016 timersetlockfile(NULL);
1017 if (!islockchset(Zhold))
1018 unlockit(Zhold, getpid(), uuname);
1019 }
1020 }
1021 sclose(&gpkt);
1022 sfree(&gpkt);
1023 ffreeall();
1024 if (HADUCA) /* ffreeall() killed it */
1025 lhash_destroy(); /* need to reset it */
1026 }
1027
1028 static char warn[] = NOGETTEXT("WARNING: being edited: `%s' (ge18)\n");
1029
1030 static void
wrtpfile(pkt,inc,exc)1031 wrtpfile(pkt,inc,exc)
1032 register struct packet *pkt;
1033 char *inc, *exc;
1034 {
1035 char line[64];
1036 char str1[SID_STRSIZE], str2[SID_STRSIZE]; /* Must fit a SID string */
1037 char *user, *pfile;
1038 FILE *in, *out;
1039 struct pfile pf;
1040 extern dtime_t Timenow;
1041 int fd;
1042 char **sflags = pkt->p_sflags;
1043
1044 if ((user=logname()) == NULL)
1045 fatal(gettext("User ID not in password file (cm9)"));
1046 if ((fd=open(auxf(pkt->p_file,'q'),O_WRONLY|O_CREAT|O_EXCL|O_BINARY,0444)) < 0) {
1047 efatal(gettext("cannot create lock file (cm4)"));
1048 }
1049 #ifdef HAVE_FCHMOD
1050 fchmod(fd, (mode_t)0644);
1051 #else
1052 chmod(auxf(pkt->p_file,'q'), (mode_t)0644);
1053 #endif
1054 out = fdfopen(fd, O_WRONLY|O_BINARY);
1055 if (exists(pfile = auxf(pkt->p_file,'p'))) {
1056 in = fdfopen(xopen(pfile, O_RDONLY|O_BINARY), O_RDONLY|O_BINARY);
1057 while (fgets(line,sizeof(line),in) != NULL) {
1058 if (fputs(line, out) == EOF)
1059 xmsg(pfile, NOGETTEXT("wrtpfile"));
1060 pf_ab(line,&pf,0);
1061 if (!(sflags[JOINTFLAG - 'a'])) {
1062 if ((pf.pf_gsid.s_rel == pkt->p_gotsid.s_rel &&
1063 pf.pf_gsid.s_lev == pkt->p_gotsid.s_lev &&
1064 pf.pf_gsid.s_br == pkt->p_gotsid.s_br &&
1065 pf.pf_gsid.s_seq == pkt->p_gotsid.s_seq) ||
1066 (pf.pf_nsid.s_rel == pkt->p_reqsid.s_rel &&
1067 pf.pf_nsid.s_lev == pkt->p_reqsid.s_lev &&
1068 pf.pf_nsid.s_br == pkt->p_reqsid.s_br &&
1069 pf.pf_nsid.s_seq == pkt->p_reqsid.s_seq)) {
1070 fclose(in);
1071 sprintf(SccsError,
1072 gettext("being edited: `%s' (ge17)"),
1073 line);
1074 fatal(SccsError);
1075 }
1076 if (!equal(pf.pf_user,user))
1077 fprintf(stderr,warn,line);
1078 } else {
1079 fprintf(stderr,warn,line);
1080 }
1081 }
1082 fclose(in);
1083 }
1084 if (fseek(out, (off_t)0, SEEK_END) == EOF)
1085 xmsg(pfile, NOGETTEXT("wrtpfile"));
1086 sid_ba(&pkt->p_gotsid,str1);
1087 sid_ba(&pkt->p_reqsid,str2);
1088 /*
1089 * POSIX does not explicitly mention a 2-digit year for the p-file but
1090 * refers to "date-time" which is most likely expected to have 2-digits.
1091 * We use 4-digits before 1969 and past 2068
1092 * which is outside the year range specified by POSIX.
1093 */
1094 #if SIZEOF_TIME_T == 4
1095 if (Timenow.dt_sec < Y1969)
1096 #else
1097 if ((Timenow.dt_sec < Y1969) ||
1098 (Timenow.dt_sec >= Y2069))
1099 #endif
1100 date_bal(&Timenow.dt_sec, line, 0); /* 4 digit year */
1101 else
1102 date_ba(&Timenow.dt_sec, line, 0); /* 2 digit year */
1103 if (fprintf(out,"%s %s %s %s",str1,str2,user,line) == EOF)
1104 xmsg(pfile, NOGETTEXT("wrtpfile"));
1105 if (inc)
1106 if (fprintf(out," -i%s",inc) == EOF)
1107 xmsg(pfile, NOGETTEXT("wrtpfile"));
1108 if (exc)
1109 if (fprintf(out," -x%s",exc) == EOF)
1110 xmsg(pfile, NOGETTEXT("wrtpfile"));
1111 if (cmrinsert(pkt) > 0) /* if there are CMRS and they are okay */
1112 if (fprintf (out, " -z%s", cmr) == EOF)
1113 xmsg(pfile, NOGETTEXT("wrtpfile"));
1114 if (fprintf(out, "\n") == EOF)
1115 xmsg(pfile, NOGETTEXT("wrtpfile"));
1116 if (fflush(out) == EOF)
1117 xmsg(pfile, NOGETTEXT("wrtpfile"));
1118 #ifdef HAVE_FSYNC
1119 if (fsync(fileno(out)) < 0)
1120 xmsg(pfile, NOGETTEXT("wrtpfile"));
1121 #endif
1122 if (fclose(out) == EOF)
1123 xmsg(pfile, NOGETTEXT("wrtpfile"));
1124 if (pkt->p_verbose) {
1125 if (HADQ)
1126 (void)fprintf(pkt->p_stdout, gettext("new version %s\n"),
1127 str2);
1128 else
1129 (void)fprintf(pkt->p_stdout, gettext("new delta %s\n"),
1130 str2);
1131 }
1132 }
1133
1134 /* cmrinsert -- insert CMR numbers in the p.file. */
1135
1136 static int
cmrinsert(pkt)1137 cmrinsert(pkt)
1138 register struct packet *pkt;
1139 {
1140 char holdcmr[CMRLIMIT];
1141 char tcmr[CMRLIMIT];
1142 char *p;
1143 int bad;
1144 int isvalid;
1145
1146 if (pkt->p_sflags[CMFFLAG - 'a'] == 0) { /* CMFFLAG was not set. */
1147 return (0);
1148 }
1149 if (HADP && !HADZ) { /* no CMFFLAG and no place to prompt. */
1150 fatal(gettext("Background CASSI get with no CMRs\n"));
1151 }
1152 retry:
1153 if (cmr[0] == '\0') { /* No CMR list. Make one. */
1154 if (HADZ && ((!isatty(0)) || (!isatty(1)))) {
1155 fatal(gettext("Background CASSI get with invalid CMR\n"));
1156 }
1157 fprintf (stdout,
1158 gettext("Input Comma Separated List of CMRs: "));
1159 fgets(cmr, CMRLIMIT, stdin);
1160 p=strend(cmr);
1161 *(--p) = '\0';
1162 if ((int)(p - cmr) == CMRLIMIT) {
1163 fprintf(stdout, gettext("?Too many CMRs.\n"));
1164 cmr[0] = '\0';
1165 goto retry; /* Entry was too long. */
1166 }
1167 }
1168 /* Now, check the comma separated list of CMRs for accuracy. */
1169 bad = 0;
1170 isvalid = 0;
1171 strlcpy(tcmr, cmr, sizeof (tcmr));
1172 while ((p=strrchr(tcmr,',')) != NULL) {
1173 ++p;
1174 if (cmrcheck(p, pkt->p_sflags[CMFFLAG - 'a'])) {
1175 ++bad;
1176 } else {
1177 ++isvalid;
1178 strlcat(holdcmr, ",", sizeof (holdcmr));
1179 strlcat(holdcmr, p, sizeof (holdcmr));
1180 }
1181 *(--p) = '\0';
1182 }
1183 if (*tcmr) {
1184 if (cmrcheck(tcmr, pkt->p_sflags[CMFFLAG - 'a'])) {
1185 ++bad;
1186 } else {
1187 ++isvalid;
1188 strlcat(holdcmr, ",", sizeof (holdcmr));
1189 strlcat(holdcmr, tcmr, sizeof (holdcmr));
1190 }
1191 }
1192 if (!bad && holdcmr[1]) {
1193 strlcpy(cmr, holdcmr+1, sizeof (cmr));
1194 return(1);
1195 } else {
1196 if ((isatty(0)) && (isatty(1))) {
1197 if (!isvalid)
1198 fprintf(stdout,
1199 gettext("Must enter at least one valid CMR.\n"));
1200 else
1201 fprintf(stdout,
1202 gettext("Re-enter invalid CMRs, or press return.\n"));
1203 }
1204 cmr[0] = '\0';
1205 goto retry;
1206 }
1207 }
1208