1 /* Check in revisions of RCS files from working files.
2
3 Copyright (C) 2010-2020 Thien-Thi Nguyen
4 Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5 Copyright (C) 1982, 1988, 1989 Walter Tichy
6
7 This file is part of GNU RCS.
8
9 GNU RCS is free software: you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 GNU RCS is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty
16 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "base.h"
24 #include <string.h>
25 #include <errno.h>
26 #include <ctype.h> /* isdigit */
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include "same-inode.h"
30 #include "stat-time.h"
31 #include "timespec.h"
32 #include "ci.help"
33 #include "b-complain.h"
34 #include "b-divvy.h"
35 #include "b-esds.h"
36 #include "b-excwho.h"
37 #include "b-fb.h"
38 #include "b-feph.h"
39 #include "b-fro.h"
40 #include "b-grok.h"
41 #include "b-isr.h"
42 #include "b-kwxout.h"
43
44 struct reason
45 {
46 struct cbuf upfront; /* from -mMSG */
47 struct cbuf delayed; /* when the user is lazy */
48 };
49
50 struct work
51 {
52 struct stat st;
53 struct fro *fro;
54 FILE *ex; /* expansion */
55 };
56
57 struct bud /* new growth */
58 {
59 struct cbuf num; /* wip revision number */
60 struct delta d; /* to be inserted */
61 struct wlink br; /* branch to be inserted */
62 bool keep;
63 struct delta *target;
64 char getcurdate_buffer[DATESIZE];
65 struct timespec work_mtime;
66 };
67
68 static void
cleanup(int * exitstatus,struct work * work)69 cleanup (int *exitstatus, struct work *work)
70 {
71 if (FLOW (erroneous))
72 *exitstatus = exit_failure;
73 fro_zclose (&FLOW (from));
74 fro_zclose (&work->fro);
75 Ozclose (&work->ex);
76 Ozclose (&FLOW (res));
77 ORCSclose ();
78 dirtempunlink ();
79 }
80
81 #define ACCF(...) accf (PLEXUS, __VA_ARGS__)
82
83 #define OK(x) (x)->string = finish_string (PLEXUS, &((x)->size))
84 #define JAM(x,s) do { ACCF ("%s", s); OK (x); } while (0)
85
86 #define ADD(x,s) do \
87 { \
88 accumulate_nbytes \
89 (PLEXUS, (x)->string, (x)->size); \
90 JAM (x, s); \
91 } \
92 while (0)
93
94 static void
incnum(char const * onum,struct cbuf * nnum)95 incnum (char const *onum, struct cbuf *nnum)
96 /* Increment the last field of revision number ‘onum’
97 by one into a ‘PLEXUS’ string and point ‘nnum’ at it. */
98 {
99 register char *tp, *np;
100 register size_t l;
101
102 ACCF ("%s%c", onum, '\0');
103 np = finish_string (PLEXUS, &nnum->size);
104 nnum->string = np;
105 l = nnum->size - 1;
106 for (tp = np + l; np != tp;)
107 if (isdigit (*--tp))
108 {
109 if (*tp != '9')
110 {
111 ++*tp;
112 nnum->size--;
113 return;
114 }
115 *tp = '0';
116 }
117 else
118 {
119 tp++;
120 break;
121 }
122 /* We changed 999 to 000; now change it to 1000. */
123 *tp = '1';
124 tp = np + l;
125 *tp++ = '0';
126 *tp = '\0';
127 }
128
129 static int
removelock(struct delta * delta)130 removelock (struct delta *delta)
131 /* Find the lock held by caller on ‘delta’,
132 remove it, and return nonzero if successful.
133 Print an error message and return -1 if there is no such lock.
134 An exception is if ‘!strictly_locking’, and caller is the owner of
135 the RCS file. If caller does not have a lock in this case,
136 return 0; return 1 if a lock is actually removed. */
137 {
138 struct link box, *tp;
139 struct rcslock const *rl;
140 char const *num;
141
142 num = delta->num;
143 box.next = GROK (locks);
144 if (! (tp = lock_delta_memq (&box, delta)))
145 {
146 if (!BE (strictly_locking) && stat_mine_p (&REPO (stat)))
147 return 0;
148 RERR ("no lock set by %s for revision %s", getcaller (), num);
149 return -1;
150 }
151 rl = tp->next->entry;
152 if (! caller_login_p (rl->login))
153 {
154 RERR ("revision %s locked by %s", num, rl->login);
155 return -1;
156 }
157 /* We found a lock on ‘delta’ by caller; delete it. */
158 lock_drop (&box, tp);
159 return 1;
160 }
161
162 static int
addbranch(struct delta * branchpoint,struct bud * bud,int removedlock,struct wlink ** tp_deltas)163 addbranch (struct delta *branchpoint, struct bud *bud,
164 int removedlock, struct wlink **tp_deltas)
165 /* Add a new branch and branch delta at ‘branchpoint’.
166 If ‘num’ is the null string, append the new branch, incrementing
167 the highest branch number (initially 1), and setting the level number to 1.
168 The new delta and branchhead are in ‘bud->d’ and ‘bud->br’, respectively.
169 The new number is placed into a ‘PLEXUS’ string with ‘num’ pointing to it.
170 Return -1 on error, 1 if a lock is removed, 0 otherwise.
171 If ‘removedlock’ is 1, a lock was already removed. */
172 {
173 struct cbuf *num = &bud->num;
174 struct wlink **btrail;
175 struct delta *d;
176 int result;
177 int field, numlength;
178
179 numlength = countnumflds (num->string);
180
181 if (!branchpoint->branches)
182 {
183 /* Start first branch. */
184 branchpoint->branches = &bud->br;
185 if (numlength == 0)
186 {
187 JAM (num, branchpoint->num);
188 ADD (num, ".1.1");
189 }
190 else if (ODDP (numlength))
191 ADD (num, ".1");
192 bud->br.next = NULL;
193 }
194 else if (numlength == 0)
195 {
196 struct wlink *bhead = branchpoint->branches;
197
198 /* Append new branch to the end. */
199 while (bhead->next)
200 bhead = bhead->next;
201 bhead->next = &bud->br;
202 d = bhead->entry;
203 incnum (BRANCHNO (d->num), num);
204 ADD (num, ".1");
205 bud->br.next = NULL;
206 }
207 else
208 {
209 /* Place the branch properly. */
210 field = numlength - EVENP (numlength);
211 /* Field of branch number. */
212 btrail = &branchpoint->branches;
213 while (d = (*btrail)->entry,
214 0 < (result = cmpnumfld (num->string, d->num, field)))
215 {
216 btrail = &(*btrail)->next;
217 if (!*btrail)
218 {
219 result = -1;
220 break;
221 }
222 }
223 if (result < 0)
224 {
225 /* Insert/append new branchhead. */
226 bud->br.next = *btrail;
227 *btrail = &bud->br;
228 if (ODDP (numlength))
229 ADD (num, ".1");
230 }
231 else
232 {
233 /* Branch exists; append to end. */
234 bud->target = gr_revno (BRANCHNO (num->string), tp_deltas);
235 if (!bud->target)
236 return -1;
237 if (!NUM_GT (num->string, bud->target->num))
238 {
239 RERR ("revision %s too low; must be higher than %s",
240 num->string, bud->target->num);
241 return -1;
242 }
243 if (!removedlock && 0 <= (removedlock = removelock (bud->target)))
244 {
245 if (ODDP (numlength))
246 incnum (bud->target->num, num);
247 bud->target->ilk = &bud->d;
248 bud->d.ilk = NULL;
249 }
250 return removedlock;
251 /* Don't do anything to ‘bud->br’. */
252 }
253 }
254 bud->br.entry = &bud->d;
255 bud->d.ilk = NULL;
256 if (branchpoint->lockedby)
257 if (caller_login_p (branchpoint->lockedby))
258 return removelock (branchpoint); /* This returns 1. */
259 return removedlock;
260 }
261
262 static void
prune(struct delta * wrong,struct delta * bp)263 prune (struct delta *wrong, struct delta *bp)
264 /* Remove reference to ‘wrong’ from the tree, starting the
265 search at ‘bp’. As a side effect, clear ‘wrong->selector’. */
266 {
267 struct wlink box, *tp;
268 struct delta *d;
269 int same = countnumflds (wrong->num) - 2;
270
271 /* Unselecting ‘wrong’ is not strictly necessary,
272 but doing so is cleaner. */
273 wrong->selector = false;
274
275 /* On the trunk, no one points to ‘wrong’, so we are finished. */
276 if (0 >= same)
277 return;
278
279 /* If ‘wrong’ is the ‘bp’ successor, simply forget it. */
280 if (wrong == bp->ilk)
281 {
282 bp->ilk = NULL;
283 return;
284 }
285
286 /* If ‘wrong’ is the only revision on a branch, delete that branch. */
287 box.next = bp->branches;
288 for (tp = &box; tp->next; tp = tp->next)
289 if (wrong == (d = tp->next->entry))
290 {
291 tp->next = tp->next->next;
292 bp->branches = box.next;
293 return;
294 }
295
296 /* Otherwise, it must be on normal chain. */
297 for (tp = bp->branches; tp; tp = tp->next)
298 {
299 d = tp->entry;
300 if (0 == compartial (wrong->num, d->num, same))
301 {
302 while (d->ilk != wrong)
303 d = d->ilk;
304 d->ilk = NULL;
305 return;
306 }
307 }
308
309 /* Should never get here. */
310 abort ();
311 }
312
313 static int
addelta(struct wlink ** tp_deltas,struct bud * bud,bool rcsinitflag)314 addelta (struct wlink **tp_deltas, struct bud *bud, bool rcsinitflag)
315 /* Append a delta to the delta tree, whose number is given by ‘bud->num’.
316 Update ‘REPO (tip)’, ‘bud->num’ and the links in ‘bud->d’.
317 Return -1 on error, 1 if a lock is removed, 0 otherwise. */
318 {
319 register char const *tp;
320 register int i;
321 int removedlock;
322 int newdnumlength; /* actual length of new rev. num. */
323 struct delta *tip = REPO (tip);
324 char const *defbr = GROK (branch);
325
326 newdnumlength = countnumflds (bud->num.string);
327
328 if (rcsinitflag)
329 {
330 /* This covers non-existing RCS file,
331 and a file initialized with ‘rcs -i’. */
332 if (newdnumlength == 0 && defbr)
333 {
334 JAM (&bud->num, defbr);
335 newdnumlength = countnumflds (defbr);
336 }
337 if (newdnumlength == 0)
338 JAM (&bud->num, "1.1");
339 else if (newdnumlength == 1)
340 ADD (&bud->num, ".1");
341 else if (newdnumlength > 2)
342 {
343 RERR ("Branch point doesn't exist for revision %s.",
344 bud->num.string);
345 return -1;
346 }
347 /* (‘newdnumlength’ == 2 is OK.) */
348 tip = REPO (tip) = &bud->d;
349 bud->d.ilk = NULL;
350 return 0;
351 }
352 if (newdnumlength == 0)
353 {
354 /* Derive new revision number from locks. */
355 switch (findlock (true, &bud->target))
356 {
357
358 default:
359 /* Found two or more old locks. */
360 return -1;
361
362 case 1:
363 /* Found an old lock. Check whether locked revision exists. */
364 if (!gr_revno (bud->target->num, tp_deltas))
365 return -1;
366 if (bud->target == tip)
367 {
368 /* Make new head. */
369 bud->d.ilk = tip;
370 tip = REPO (tip) = &bud->d;
371 }
372 else if (!bud->target->ilk && countnumflds (bud->target->num) > 2)
373 {
374 /* New tip revision on side branch. */
375 bud->target->ilk = &bud->d;
376 bud->d.ilk = NULL;
377 }
378 else
379 {
380 /* Middle revision; start a new branch. */
381 JAM (&bud->num, "");
382 return addbranch (bud->target, bud, true, tp_deltas);
383 }
384 incnum (bud->target->num, &bud->num);
385 /* Successful use of existing lock. */
386 return 1;
387
388 case 0:
389 /* No existing lock; try ‘defbr’. Update ‘bud->num’. */
390 if (BE (strictly_locking) || !stat_mine_p (&REPO (stat)))
391 {
392 RERR ("no lock set by %s", getcaller ());
393 return -1;
394 }
395 if (defbr)
396 JAM (&bud->num, defbr);
397 else
398 {
399 incnum (tip->num, &bud->num);
400 }
401 newdnumlength = countnumflds (bud->num.string);
402 /* Now fall into next statement. */
403 }
404 }
405 if (newdnumlength <= 2)
406 {
407 /* Add new head per given number. */
408 if (newdnumlength == 1)
409 {
410 /* Make a two-field number out of it. */
411 if (NUMF_EQ (1, bud->num.string, tip->num))
412 incnum (tip->num, &bud->num);
413 else
414 ADD (&bud->num, ".1");
415 }
416 if (!NUM_GT (bud->num.string, tip->num))
417 {
418 RERR ("revision %s too low; must be higher than %s",
419 bud->num.string, tip->num);
420 return -1;
421 }
422 bud->target = tip;
423 if (0 <= (removedlock = removelock (tip)))
424 {
425 if (!gr_revno (tip->num, tp_deltas))
426 return -1;
427 bud->d.ilk = tip;
428 tip = REPO (tip) = &bud->d;
429 }
430 return removedlock;
431 }
432 else
433 {
434 struct cbuf old = bud->num; /* sigh */
435
436 /* Put new revision on side branch. First, get branch point. */
437 tp = old.string;
438 for (i = newdnumlength - EVENP (newdnumlength); --i;)
439 while (*tp++ != '.')
440 continue;
441 /* Ignore rest to get old delta. */
442 old.string = SHSNIP (&old.size, old.string, tp - 1);
443 if (! (bud->target = gr_revno (old.string, tp_deltas)))
444 return -1;
445 if (!NUM_EQ (bud->target->num, old.string))
446 {
447 RERR ("can't find branch point %s", old.string);
448 return -1;
449 }
450 return addbranch (bud->target, bud, false, tp_deltas);
451 }
452 }
453
454 static bool
addsyms(char const * num,struct link * ls)455 addsyms (char const *num, struct link *ls)
456 {
457 struct u_symdef const *ud;
458
459 for (; ls; ls = ls->next)
460 {
461 ud = ls->entry;
462
463 if (addsymbol (num, ud->u.meaningful, ud->override) < 0)
464 return false;
465 }
466 return true;
467 }
468
469 static char const *
getcurdate(struct bud * bud)470 getcurdate (struct bud *bud)
471 /* Return a pointer to the current date. */
472 {
473 if (!bud->getcurdate_buffer[0])
474 time2date (BE (now.tv_sec), bud->getcurdate_buffer);
475 return bud->getcurdate_buffer;
476 }
477
478 static int
fixwork(mode_t newworkmode,const struct timespec mtime,struct work * work)479 fixwork (mode_t newworkmode, const struct timespec mtime, struct work *work)
480 {
481 char const *mani_filename = MANI (filename);
482 struct stat *st = &work->st;
483
484 return
485 (1 < st->st_nlink
486 || (newworkmode & S_IWUSR && !stat_mine_p (st))
487 || PROB (setmtime (mani_filename, mtime)))
488 ? -1
489 : (st->st_mode == newworkmode
490 ? 0
491 : (!PROB (change_mode (work->fro->fd, newworkmode))
492 ? 0
493 : chmod (mani_filename, newworkmode)));
494 }
495
496 static int
xpandfile(struct work * work,struct delta const * delta,char const ** exname,bool dolog)497 xpandfile (struct work *work, struct delta const *delta,
498 char const **exname, bool dolog)
499 /* Read ‘work->fro’ and copy it to a file, performing keyword
500 substitution with data from ‘delta’.
501 Return -1 if unsuccessful, 1 if expansion occurred, 0 otherwise.
502 If successful, store the name into ‘*exname’. */
503 {
504 char const *targetname;
505 int e, r;
506
507 targetname = makedirtemp (true);
508 if (!(work->ex = fopen_safer (targetname, FOPEN_W_WORK)))
509 {
510 syserror_errno (targetname);
511 MERR ("can't build working file");
512 return -1;
513 }
514 r = 0;
515 if (MIN_UNEXPAND <= BE (kws))
516 fro_spew (work->fro, work->ex);
517 else
518 {
519 struct expctx ctx = EXPCTX_1OUT (work->ex, work->fro, false, dolog);
520
521 for (;;)
522 {
523 e = expandline (&ctx);
524 if (e < 0)
525 break;
526 r |= e;
527 if (e <= 1)
528 break;
529 }
530 FINISH_EXPCTX (&ctx);
531 }
532 *exname = targetname;
533 return ODDP (r);
534 }
535
536 /* --------------------- G E T L O G M S G --------------------------------*/
537
538 #define FIRST "Initial revision"
539
540 static struct cbuf
getlogmsg(struct reason * reason,struct bud * bud)541 getlogmsg (struct reason *reason, struct bud *bud)
542 /* Obtain and return a log message.
543 If a log message is given with ‘-m’, return that message.
544 If this is the initial revision, return a standard log message.
545 Otherwise, read a character string from the terminal.
546 Stop after reading EOF or a single '.' on a line.
547 Prompt the first time called for the log message; during all
548 later calls ask whether the previous log message can be reused. */
549 {
550 const char *num;
551
552 if (reason->upfront.size)
553 return reason->upfront;
554
555 if (bud->keep)
556 {
557 char datebuf[FULLDATESIZE];
558
559 /* Generate standard log message. */
560 date2str (getcurdate (bud), datebuf);
561 ACCF ("%s%s at %s", TINYKS (ciklog), getcaller (), datebuf);
562 OK (&reason->delayed);
563 return reason->delayed;
564 }
565
566 if (!bud->target
567 && bud->num.size
568 && (num = bud->num.string)
569 && (NUM_EQ (num, "1.1")
570 || NUM_EQ (num, "1.0")))
571 {
572 struct cbuf const initiallog =
573 {
574 .string = FIRST,
575 .size = sizeof (FIRST) - 1
576 };
577
578 return initiallog;
579 }
580
581 if (reason->delayed.size)
582 {
583 /*Previous log available. */
584 if (yesorno (true, "reuse log message of previous file"))
585 return reason->delayed;
586 }
587
588 /* Now read string from stdin. */
589 reason->delayed = getsstdin ("m", "log message", "");
590
591 /* Now check whether the log message is not empty. */
592 if (!reason->delayed.size)
593 set_empty_log_message (&reason->delayed);
594 return reason->delayed;
595 }
596
597 static char const *
first_meaningful_symbolic_name(struct link * ls)598 first_meaningful_symbolic_name (struct link *ls)
599 {
600 struct u_symdef const *ud;
601
602 /* Find last link so that, e.g., "-nA -nB -nC" yields "A".
603 See: (search-forward "symbolic_names = prepend") */
604 while (ls && ls->next)
605 ls = ls->next;
606
607 ud = ls->entry;
608 return ud->u.meaningful;
609 }
610
611 DECLARE_PROGRAM (ci, BOG_FULL);
612
613 static int
ci_main(const char * cmd,int argc,char ** argv)614 ci_main (const char *cmd, int argc, char **argv)
615 {
616 int exitstatus = EXIT_SUCCESS;
617 struct reason reason;
618 char altdate[DATESIZE];
619 char olddate[DATESIZE];
620 char newdatebuf[FULLDATESIZE];
621 char targetdatebuf[FULLDATESIZE];
622 char *a, **newargv, *textfile;
623 char const *author, *krev, *rev, *state;
624 char const *diffname, *expname;
625 char const *newworkname;
626 struct work work = { .ex = NULL };
627 bool forceciflag = false;
628 bool keepworkingfile = false;
629 bool rcsinitflag = false;
630 bool initflag, mustread;
631 bool lockflag, lockthis, mtimeflag;
632 int removedlock;
633 bool Ttimeflag;
634 int r;
635 int changedRCS, changework;
636 bool dolog, newhead;
637 bool usestatdate; /* Use mod time of file for -d. */
638 mode_t newworkmode; /* mode for working file */
639 struct timespec mtime, wtime;
640 struct bud bud;
641 struct delta *workdelta;
642 struct link *symbolic_names = NULL;
643 struct wlink *deltas; /* Deltas to be generated. */
644
645 CHECK_HV (cmd);
646 gnurcs_init (&program);
647
648 /* This lameness is because constructing a proper initialization form for
649 ‘struct bud’ is too much hassle. We do it here, after the ‘gnurcs_init’
650 instead of before, closer to the declaration (as would be more indicative
651 of its role) because perhaps Real Soon Now But Not Quite Yet ‘bud’ will
652 be changed to be heap-allocated (probably in ‘PLEXUS’), and this is the
653 place to do that. */
654 memset (&bud, 0, sizeof (struct bud));
655 /* Likewise. */
656 memset (&reason, 0, sizeof (reason));
657
658 setrid ();
659
660 author = rev = state = textfile = NULL;
661 initflag = lockflag = mustread = false;
662 mtimeflag = false;
663 Ttimeflag = false;
664 altdate[0] = '\0'; /* empty alternate date for -d */
665 usestatdate = false;
666
667 argc = getRCSINIT (argc, argv, &newargv);
668 argv = newargv;
669 while (a = *++argv, 0 < --argc && *a++ == '-')
670 {
671 switch (*a++)
672 {
673
674 case 'r':
675 if (*a)
676 goto revno;
677 keepworkingfile = lockflag = false;
678 break;
679
680 case 'l':
681 keepworkingfile = lockflag = true;
682 revno:
683 chk_set_rev (&rev, a);
684 break;
685
686 case 'u':
687 keepworkingfile = true;
688 lockflag = false;
689 goto revno;
690
691 case 'i':
692 initflag = true;
693 goto revno;
694
695 case 'j':
696 mustread = true;
697 goto revno;
698
699 case 'I':
700 BE (interactive) = true;
701 goto revno;
702
703 case 'q':
704 BE (quiet) = true;
705 goto revno;
706
707 case 'f':
708 forceciflag = true;
709 goto revno;
710
711 case 'k':
712 bud.keep = true;
713 goto revno;
714
715 case 'm':
716 if (reason.upfront.size)
717 redefined ('m');
718 reason.upfront = cleanlogmsg (a, strlen (a));
719 if (!reason.upfront.size)
720 set_empty_log_message (&reason.upfront);
721 break;
722
723 case 'n':
724 case 'N':
725 {
726 char option = a[-1];
727 struct u_symdef *ud;
728
729 if (!*a)
730 {
731 PERR ("missing symbolic name after -%c", option);
732 break;
733 }
734 checkssym (a);
735 ud = ZLLOC (1, struct u_symdef);
736 ud->override = ('N' == option);
737 ud->u.meaningful = a;
738 symbolic_names = prepend (ud, symbolic_names, PLEXUS);
739 }
740 break;
741
742 case 's':
743 if (*a)
744 {
745 if (state)
746 redefined ('s');
747 checksid (a);
748 state = a;
749 }
750 else
751 PERR ("missing state for -s option");
752 break;
753
754 case 't':
755 if (*a)
756 {
757 if (textfile)
758 redefined ('t');
759 textfile = a;
760 }
761 break;
762
763 case 'd':
764 if (altdate[0] || usestatdate)
765 redefined ('d');
766 altdate[0] = '\0';
767 if (!(usestatdate = !*a))
768 str2date (a, altdate);
769 break;
770
771 case 'M':
772 mtimeflag = true;
773 goto revno;
774
775 case 'w':
776 if (*a)
777 {
778 if (author)
779 redefined ('w');
780 checksid (a);
781 author = a;
782 }
783 else
784 PERR ("missing author for -w option");
785 break;
786
787 case 'x':
788 BE (pe) = a;
789 break;
790
791 case 'V':
792 setRCSversion (*argv);
793 break;
794
795 case 'z':
796 zone_set (a);
797 break;
798
799 case 'T':
800 if (!*a)
801 {
802 Ttimeflag = true;
803 break;
804 }
805 /* fall into */
806 default:
807 bad_option (*argv);
808 };
809 }
810 /* (End processing of options.) */
811
812 /* Handle all filenames. */
813 if (FLOW (erroneous))
814 cleanup (&exitstatus, &work);
815 else if (argc < 1)
816 PFATAL ("no input file");
817 else
818 for (; 0 < argc; cleanup (&exitstatus, &work), ++argv, --argc)
819 {
820 /* Use var instead of simple #define for fast identity compare. */
821 char const *default_state = DEFAULTSTATE;
822 char const *mani_filename, *pv;
823 struct fro *from;
824 struct stat *repo_stat;
825 struct timespec fs_mtime;
826 FILE *frew;
827 struct delta *tip;
828 int kws;
829 struct cbuf newdesc =
830 {
831 .string = NULL,
832 .size = 0
833 };
834
835 bud.target = NULL;
836 ffree ();
837
838 switch (pairnames (argc, argv, rcswriteopen, mustread, false))
839 {
840
841 case -1:
842 /* New RCS file. */
843 if (currently_setuid_p ())
844 {
845 MERR
846 ("setuid initial checkin prohibited; use `rcs -i -a' first");
847 continue;
848 }
849 rcsinitflag = true;
850 break;
851
852 case 0:
853 /* Error. */
854 continue;
855
856 case 1:
857 /* Normal checkin with previous RCS file. */
858 if (initflag)
859 {
860 RERR ("already exists");
861 continue;
862 }
863 rcsinitflag = !(tip = REPO (tip));
864 }
865
866 /* ‘REPO (filename)’ contains the name of the RCS file,
867 and ‘MANI (filename)’ contains the name of the working file.
868 If the RCS file exists, ‘FLOW (from)’ contains the file
869 descriptor for the RCS file, and ‘REPO (stat)’ is set.
870 The admin node is initialized. */
871 mani_filename = MANI (filename);
872 from = FLOW (from);
873 repo_stat = &REPO (stat);
874 kws = BE (kws);
875
876 diagnose ("%s <-- %s", REPO (filename), mani_filename);
877
878 if (!(work.fro = fro_open (mani_filename, FOPEN_R_WORK, &work.st)))
879 {
880 syserror_errno (mani_filename);
881 continue;
882 }
883
884 if (from)
885 {
886 if (SAME_INODE (REPO (stat), work.st))
887 {
888 RERR ("RCS file is the same as working file %s.",
889 mani_filename);
890 continue;
891 }
892 if (!checkaccesslist ())
893 continue;
894 }
895
896 krev = rev;
897 if (bud.keep)
898 {
899 /* Get keyword values from working file. */
900 if (!getoldkeys (work.fro))
901 continue;
902 if (!rev && !(krev = PREV (rev)))
903 {
904 MERR ("can't find a %s", ks_revno);
905 continue;
906 }
907 if (!PREV (date) && *altdate == '\0' && usestatdate == false)
908 MWARN ("can't find a date");
909 if (!PREV (author) && !author)
910 MWARN ("can't find an author");
911 if (!PREV (state) && !state)
912 MWARN ("can't find a state");
913 }
914
915 /* Expand symbolic revision number. */
916 if (!fully_numeric (&bud.num, krev, work.fro))
917 continue;
918
919 /* Splice new delta into tree. */
920 if (PROB (removedlock = addelta (&deltas, &bud, rcsinitflag)))
921 continue;
922 tip = REPO (tip);
923
924 bud.d.num = bud.num.string;
925 bud.d.branches = NULL;
926 /* This might be changed by ‘addlock’. */
927 bud.d.lockedby = NULL;
928 bud.d.selector = true;
929 bud.d.name = NULL;
930
931 /* Set author. */
932 bud.d.author =
933 /* Given by ‘-w’. */
934 (author
935 ? author
936 /* Preserve old author if possible. */
937 : (bud.keep && (pv = PREV (author))
938 ? pv
939 /* Otherwise use caller's id. */
940 : getcaller ()));
941
942 /* Set state. */
943 bud.d.state =
944 /* Given by ‘-s’. */
945 (state
946 ? state
947 /* Preserve old state if possible. */
948 : (bud.keep && (pv = PREV (state))
949 ? pv
950 /* default */
951 : default_state));
952
953 /* Compute date. */
954 bud.work_mtime = get_stat_mtime (&work.st);
955 if (usestatdate)
956 {
957 time2date (work.st.st_mtime, altdate);
958 }
959 if (*altdate != '\0')
960 /* Given by ‘-d’. */
961 bud.d.date = altdate;
962 else if (bud.keep && (pv = PREV (date)))
963 {
964 /* Preserve old date if possible. */
965 str2date (pv, olddate);
966 bud.d.date = olddate;
967 }
968 else
969 /* Use current date. */
970 bud.d.date = getcurdate (&bud);
971 /* Now check validity of date -- needed because of ‘-d’ and ‘-k’. */
972 if (bud.target && DATE_LT (bud.d.date, bud.target->date))
973 {
974 RERR ("Date %s precedes %s in revision %s.",
975 date2str (bud.d.date, newdatebuf),
976 date2str (bud.target->date, targetdatebuf),
977 bud.target->num);
978 continue;
979 }
980
981 if (lockflag && addlock (&bud.d, true) < 0)
982 continue;
983
984 if (bud.keep && (pv = PREV (name)))
985 if (addsymbol (bud.d.num, pv, false) < 0)
986 continue;
987 if (!addsyms (bud.d.num, symbolic_names))
988 continue;
989
990 putadmin ();
991 frew = FLOW (rewr);
992 puttree (tip, frew);
993 putdesc (&newdesc, false, textfile);
994
995 changework = kws < MIN_UNCHANGED_EXPAND;
996 dolog = true;
997 lockthis = lockflag;
998 workdelta = &bud.d;
999
1000 /* Build rest of file. */
1001 if (rcsinitflag)
1002 {
1003 diagnose ("initial revision: %s", bud.d.num);
1004 /* Get logmessage. */
1005 bud.d.pretty_log = getlogmsg (&reason, &bud);
1006 putdftext (&bud.d, work.fro, frew, false);
1007 repo_stat->st_mode = work.st.st_mode;
1008 repo_stat->st_nlink = 0;
1009 changedRCS = true;
1010 if (from)
1011 IGNORE_REST (from);
1012 }
1013 else
1014 {
1015 diffname = maketemp (0);
1016 newhead = tip == &bud.d;
1017 if (!newhead)
1018 FLOW (to) = frew;
1019 expname = buildrevision (deltas, bud.target, NULL, false);
1020 if (!forceciflag
1021 && STR_SAME (bud.d.state, bud.target->state)
1022 && ((changework = rcsfcmp (work.fro, &work.st, expname,
1023 bud.target))
1024 <= 0))
1025 {
1026 diagnose
1027 ("file is unchanged; reverting to previous revision %s",
1028 bud.target->num);
1029 if (removedlock < lockflag)
1030 {
1031 diagnose
1032 ("previous revision was not locked; ignoring -l option");
1033 lockthis = 0;
1034 }
1035 dolog = false;
1036 if (!(changedRCS = lockflag < removedlock || symbolic_names))
1037 {
1038 workdelta = bud.target;
1039 SAME_AFTER (from, bud.target->text);
1040 }
1041 else
1042 /* We have started to build the wrong new RCS file.
1043 Start over from the beginning. */
1044 {
1045 off_t hwm = ftello (frew);
1046 bool bad_truncate;
1047
1048 rewind (frew);
1049 bad_truncate = PROB (ftruncate (fileno (frew), (off_t) 0));
1050 grok_resynch (REPO (r));
1051
1052 /* The ‘bud.d’ might still be linked in the tree,
1053 so prune it now. (Unfortunately, ‘grok_resynch’
1054 did not restore the tree completely, as its name
1055 might imply.) */
1056 prune (&bud.d, bud.target);
1057
1058 if (! (workdelta = gr_revno (bud.target->num, &deltas)))
1059 continue;
1060 workdelta->pretty_log = bud.target->pretty_log;
1061 if (bud.d.state != default_state)
1062 workdelta->state = bud.d.state;
1063 if (lockthis < removedlock && removelock (workdelta) < 0)
1064 continue;
1065 if (!addsyms (workdelta->num, symbolic_names))
1066 continue;
1067 if (PROB (dorewrite (true, true)))
1068 continue;
1069 VERBATIM (from, GROK (neck));
1070 fro_spew (from, frew);
1071 if (bad_truncate)
1072 while (ftello (frew) < hwm)
1073 /* White out any earlier mistake with '\n's.
1074 This is unlikely. */
1075 newline (frew);
1076 }
1077 }
1078 else
1079 {
1080 int wfd = work.fro->fd;
1081 struct stat checkworkstat;
1082 char const *diffv[6 + !!OPEN_O_BINARY], **diffp;
1083
1084 diagnose ("new revision: %s; previous revision: %s",
1085 bud.d.num, bud.target->num);
1086 SAME_AFTER (from, bud.target->text);
1087 bud.d.pretty_log = getlogmsg (&reason, &bud);
1088
1089 /* Make sure diff(1) reads from the beginning. */
1090 if (PROB (lseek (wfd, 0, SEEK_SET)))
1091 Ierror ();
1092
1093 diffp = diffv;
1094 *++diffp = prog_diff;
1095 *++diffp = diff_flags;
1096 if (OPEN_O_BINARY
1097 && kws == kwsub_b)
1098 *++diffp = "--binary";
1099 *++diffp = newhead ? "-" : expname;
1100 *++diffp = newhead ? expname : "-";
1101 *++diffp = NULL;
1102 if (DIFF_TROUBLE == runv (wfd, diffname, diffv))
1103 RFATAL ("diff failed");
1104
1105 /* "Rewind" ‘work.fro’ only after feeding it to
1106 diff(1). This is needed to keep the stream
1107 buffer state in sync with the fd. */
1108 fro_bob (work.fro);
1109
1110 if (newhead)
1111 {
1112 fro_bob (work.fro);
1113 putdftext (&bud.d, work.fro, frew, false);
1114 if (!putdtext (bud.target, diffname, frew, true))
1115 continue;
1116 }
1117 else if (!putdtext (&bud.d, diffname, frew, true))
1118 continue;
1119
1120 /* Check whether the working file changed during checkin,
1121 to avoid producing an inconsistent RCS file. */
1122 if (PROB (fstat (wfd, &checkworkstat))
1123 || 0 != timespec_cmp (get_stat_mtime (&checkworkstat),
1124 bud.work_mtime)
1125 || work.st.st_size != checkworkstat.st_size)
1126 {
1127 MERR ("file changed during checkin");
1128 continue;
1129 }
1130
1131 changedRCS = true;
1132 }
1133 }
1134
1135 /* Deduce timestamp of new revision if it is needed later. */
1136 wtime = (mtimeflag | Ttimeflag)
1137 ? (usestatdate
1138 ? bud.work_mtime
1139 : make_timespec (date2time (workdelta->date),
1140 ZERO_NANOSECONDS))
1141 : unspecified_timespec ();
1142
1143 if (Ttimeflag)
1144 fs_mtime = file_mtime (from, repo_stat);
1145
1146 if (PROB (donerewrite (changedRCS, !Ttimeflag
1147 ? unspecified_timespec ()
1148 : (PROB (timespec_cmp (wtime, fs_mtime))
1149 /* File is newer. */
1150 ? fs_mtime
1151 /* Delta is newer. */
1152 : wtime))))
1153 continue;
1154
1155 if (!keepworkingfile)
1156 {
1157 fro_zclose (&work.fro);
1158 /* Get rid of old file. */
1159 r = un_link (mani_filename);
1160 }
1161 else
1162 {
1163 newworkmode = WORKMODE (repo_stat->st_mode,
1164 !(kws == kwsub_v
1165 || lockthis < BE (strictly_locking)));
1166 mtime = mtimeflag
1167 ? wtime
1168 : unspecified_timespec ();
1169
1170 /* Expand if it might change or if we can't fix mode, time. */
1171 if (changework || PROB (r = fixwork (newworkmode, mtime, &work)))
1172 {
1173 fro_bob (work.fro);
1174 /* Expand keywords in file. */
1175 BE (inclusive_of_Locker_in_Id_val) = lockthis;
1176 workdelta->name =
1177 namedrev (symbolic_names
1178 ? first_meaningful_symbolic_name (symbolic_names)
1179 : (bud.keep && (pv = PREV (name))
1180 ? pv
1181 : rev),
1182 workdelta);
1183 switch (xpandfile (&work, workdelta, &newworkname, dolog))
1184 {
1185 default:
1186 continue;
1187
1188 case 0:
1189 /* No expansion occurred; try to reuse working file
1190 unless we already tried and failed. */
1191 if (changework)
1192 if ((r = fixwork (newworkmode, mtime, &work)) == 0)
1193 break;
1194 /* fall into */
1195 case 1:
1196 fro_zclose (&work.fro);
1197 aflush (work.ex);
1198 IGNOREINTS ();
1199 r = chnamemod (&work.ex, newworkname, mani_filename,
1200 1, newworkmode, mtime);
1201 keepdirtemp (newworkname);
1202 RESTOREINTS ();
1203 }
1204 }
1205 }
1206 if (PROB (r))
1207 {
1208 syserror_errno (mani_filename);
1209 continue;
1210 }
1211 diagnose ("done");
1212
1213 }
1214
1215 tempunlink ();
1216 gnurcs_goodbye ();
1217 return exitstatus;
1218 }
1219
1220 static const uint8_t ci_aka[19] =
1221 {
1222 3 /* count */,
1223 2,'c','i',
1224 7,'c','h','e','c','k','i','n',
1225 6,'c','o','m','m','i','t'
1226 };
1227
1228 YET_ANOTHER_COMMAND (ci);
1229
1230 /*:help
1231 [options] file...
1232 Options:
1233 -f[REV] Force new entry, even if no content changed.
1234 -I[REV] Interactive.
1235 -i[REV] Initial checkin; error if RCS file already exists.
1236 -j[REV] Just checkin, don't init; error if RCS file does not exist.
1237 -k[REV] Compute revision from working file keywords.
1238 -q[REV] Quiet mode.
1239 -r[REV] Do normal checkin, if REV is specified;
1240 otherwise, release lock and delete working file.
1241 -l[REV] Like -r, but immediately checkout locked (co -l) afterwards.
1242 -u[REV] Like -l, but checkout unlocked (co -u).
1243 -M[REV] Reset working file mtime (relevant for -l, -u).
1244 -d[DATE] Use DATE (or working file mtime).
1245 -mMSG Use MSG as the log message.
1246 -nNAME Assign symbolic NAME to the entry; NAME must be new.
1247 -NNAME Like -n, but overwrite any previous assignment.
1248 -sSTATE Set state to STATE.
1249 -t-TEXT Set description to TEXT.
1250 -tFILENAME Set description from text read from FILENAME.
1251 -T Set the RCS file's modification time to the new
1252 revision's time if the former precedes the latter and there
1253 is a new revision; preserve the RCS file's modification
1254 time otherwise.
1255 -V Obsolete; do not use.
1256 -VN Emulate RCS version N.
1257 -wWHO Use WHO as the author.
1258 -xSUFF Specify SUFF as a slash-separated list of suffixes
1259 used to identify RCS file names.
1260 -zZONE Specify date output format in keyword-substitution
1261 and also the default timezone for -dDATE.
1262
1263 Multiple flags in {fiIjklMqru} may be used, except for -r, -l, -u, which are
1264 mutually exclusive. If specified, REV can be symbolic, numeric, or mixed:
1265 symbolic Must have been defined previously (see -n, -N).
1266 $ Determine from keyword values in the working file.
1267 .N Prepend default branch => DEFBR.N
1268 BR.N Use this, but N must be greater than any existing
1269 on BR, or BR must be new.
1270 BR Latest rev on branch BR + 1 => BR.(L+1), or BR.1 if new branch.
1271 If REV is omitted, compute it from the last lock (co -l), perhaps
1272 starting a new branch. If there is no lock, use DEFBR.(L+1).
1273 */
1274
1275 /* ci.c ends here */
1276