1 /*
2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3 *
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5 * and others.
6 *
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
9 *
10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution.
12 *
13 * Commit Files
14 *
15 * "commit" commits the present version to the RCS repository, AFTER
16 * having done a test on conflicts.
17 *
18 * The call is: cvs commit [options] files...
19 *
20 */
21
22 #include "cvs.h"
23 #include "getline.h"
24 #include "edit.h"
25 #include "fileattr.h"
26 #include "hardlink.h"
27
28 __RCSID("$MirOS: src/gnu/usr.bin/cvs/src/commit.c,v 1.10 2010/09/19 19:43:02 tg Exp $");
29
30 static Dtype check_direntproc (void *callerdat, const char *dir,
31 const char *repos, const char *update_dir,
32 List *entries);
33 static int check_fileproc (void *callerdat, struct file_info *finfo);
34 static int check_filesdoneproc (void *callerdat, int err, const char *repos,
35 const char *update_dir, List *entries);
36 static int checkaddfile (const char *file, const char *repository,
37 const char *tag, const char *options,
38 RCSNode **rcsnode);
39 static Dtype commit_direntproc (void *callerdat, const char *dir,
40 const char *repos, const char *update_dir,
41 List *entries);
42 static int commit_dirleaveproc (void *callerdat, const char *dir, int err,
43 const char *update_dir, List *entries);
44 static int commit_fileproc (void *callerdat, struct file_info *finfo);
45 static int commit_filesdoneproc (void *callerdat, int err,
46 const char *repository,
47 const char *update_dir, List *entries);
48 static int finaladd (struct file_info *finfo, char *revision, char *tag,
49 char *options);
50 static int findmaxrev (Node * p, void *closure);
51 static int lock_RCS (const char *user, RCSNode *rcs, const char *rev,
52 const char *repository);
53 static int precommit_list_to_args_proc (Node * p, void *closure);
54 static int precommit_proc (const char *repository, const char *filter,
55 void *closure);
56 static int remove_file (struct file_info *finfo, char *tag,
57 char *message);
58 static void fixaddfile (const char *rcs);
59 static void fixbranch (RCSNode *, char *branch);
60 static void unlockrcs (RCSNode *rcs);
61 static void ci_delproc (Node *p);
62 static void masterlist_delproc (Node *p);
63
64 struct commit_info
65 {
66 Ctype status; /* as returned from Classify_File() */
67 char *rev; /* a numeric rev, if we know it */
68 char *tag; /* any sticky tag, or -r option */
69 char *options; /* Any sticky -k option */
70 };
71 struct master_lists
72 {
73 List *ulist; /* list for Update_Logfile */
74 List *cilist; /* list with commit_info structs */
75 };
76
77 static int check_valid_edit = 0;
78 static int force_ci = 0;
79 static int got_message;
80 static int aflag;
81 static char *saved_tag;
82 static char *write_dirtag;
83 static int write_dirnonbranch;
84 static char *logfile;
85 static List *mulist;
86 static char *saved_message;
87 static time_t last_register_time;
88
89 static const char *const commit_usage[] =
90 {
91 "Usage: %s %s [-cRlf] [-m msg | -F logfile] [-r rev] files...\n",
92 " -c Check for valid edits before committing.\n",
93 " -R Process directories recursively.\n",
94 " -l Local directory only (not recursive).\n",
95 " -f Force the file to be committed; disables recursion.\n",
96 " -F logfile Read the log message from file.\n",
97 " -m msg Log message.\n",
98 " -r rev Commit to this branch or trunk revision.\n",
99 "(Specify the --help global option for a list of other help options)\n",
100 NULL
101 };
102
103 #ifdef CLIENT_SUPPORT
104 /* Identify a file which needs "? foo" or a Questionable request. */
105 struct question
106 {
107 /* The two fields for the Directory request. */
108 char *dir;
109 char *repos;
110
111 /* The file name. */
112 char *file;
113
114 struct question *next;
115 };
116
117 struct find_data
118 {
119 List *ulist;
120 int argc;
121 char **argv;
122
123 /* This is used from dirent to filesdone time, for each directory,
124 to make a list of files we have already seen. */
125 List *ignlist;
126
127 /* Linked list of files which need "? foo" or a Questionable request. */
128 struct question *questionables;
129
130 /* Only good within functions called from the filesdoneproc. Stores
131 the repository (pointer into storage managed by the recursion
132 processor. */
133 const char *repository;
134
135 /* Non-zero if we should force the commit. This is enabled by
136 either -f or -r options, unlike force_ci which is just -f. */
137 int force;
138 };
139
140
141
142 static Dtype
find_dirent_proc(void * callerdat,const char * dir,const char * repository,const char * update_dir,List * entries)143 find_dirent_proc (void *callerdat, const char *dir, const char *repository,
144 const char *update_dir, List *entries)
145 {
146 struct find_data *find_data = callerdat;
147
148 /* This check seems to slowly be creeping throughout CVS (update
149 and send_dirent_proc by CVS 1.5, diff in 31 Oct 1995. My guess
150 is that it (or some variant thereof) should go in all the
151 dirent procs. Unless someone has some better idea... */
152 if (!isdir (dir))
153 return R_SKIP_ALL;
154
155 /* initialize the ignore list for this directory */
156 find_data->ignlist = getlist ();
157
158 /* Print the same warm fuzzy as in check_direntproc, since that
159 code will never be run during client/server operation and we
160 want the messages to match. */
161 if (!quiet)
162 error (0, 0, "Examining %s", update_dir);
163
164 return R_PROCESS;
165 }
166
167
168
169 /* Here as a static until we get around to fixing ignore_files to pass
170 it along as an argument. */
171 static struct find_data *find_data_static;
172
173
174
175 static void
find_ignproc(const char * file,const char * dir)176 find_ignproc (const char *file, const char *dir)
177 {
178 struct question *p;
179
180 p = xmalloc (sizeof (struct question));
181 p->dir = xstrdup (dir);
182 p->repos = xstrdup (find_data_static->repository);
183 p->file = xstrdup (file);
184 p->next = find_data_static->questionables;
185 find_data_static->questionables = p;
186 }
187
188
189
190 static int
find_filesdoneproc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)191 find_filesdoneproc (void *callerdat, int err, const char *repository,
192 const char *update_dir, List *entries)
193 {
194 struct find_data *find_data = callerdat;
195 find_data->repository = repository;
196
197 /* if this directory has an ignore list, process it then free it */
198 if (find_data->ignlist)
199 {
200 find_data_static = find_data;
201 ignore_files (find_data->ignlist, entries, update_dir, find_ignproc);
202 dellist (&find_data->ignlist);
203 }
204
205 find_data->repository = NULL;
206
207 return err;
208 }
209
210
211
212 /* Machinery to find out what is modified, added, and removed. It is
213 possible this should be broken out into a new client_classify function;
214 merging it with classify_file is almost sure to be a mess, though,
215 because classify_file has all kinds of repository processing. */
216 static int
find_fileproc(void * callerdat,struct file_info * finfo)217 find_fileproc (void *callerdat, struct file_info *finfo)
218 {
219 Vers_TS *vers;
220 enum classify_type status;
221 Node *node;
222 struct find_data *args = callerdat;
223 struct logfile_info *data;
224 struct file_info xfinfo;
225
226 /* if this directory has an ignore list, add this file to it */
227 if (args->ignlist)
228 {
229 Node *p;
230
231 p = getnode ();
232 p->type = FILES;
233 p->key = xstrdup (finfo->file);
234 if (addnode (args->ignlist, p) != 0)
235 freenode (p);
236 }
237
238 xfinfo = *finfo;
239 xfinfo.repository = NULL;
240 xfinfo.rcs = NULL;
241
242 vers = Version_TS (&xfinfo, NULL, saved_tag, NULL, 0, 0);
243 if (vers->vn_user == NULL)
244 {
245 if (vers->ts_user == NULL)
246 error (0, 0, "nothing known about `%s'", finfo->fullname);
247 else
248 error (0, 0, "use `%s add' to create an entry for `%s'",
249 program_name, finfo->fullname);
250 freevers_ts (&vers);
251 return 1;
252 }
253 if (vers->vn_user[0] == '-')
254 {
255 if (vers->ts_user != NULL)
256 {
257 error (0, 0,
258 "`%s' should be removed and is still there (or is back"
259 " again)", finfo->fullname);
260 freevers_ts (&vers);
261 return 1;
262 }
263 /* else */
264 status = T_REMOVED;
265 }
266 else if (strcmp (vers->vn_user, "0") == 0)
267 {
268 if (vers->ts_user == NULL)
269 {
270 /* This happens when one has `cvs add'ed a file, but it no
271 longer exists in the working directory at commit time.
272 FIXME: What classify_file does in this case is print
273 "new-born %s has disappeared" and removes the entry.
274 We probably should do the same. */
275 if (!really_quiet)
276 error (0, 0, "warning: new-born %s has disappeared",
277 finfo->fullname);
278 status = T_REMOVE_ENTRY;
279 }
280 else
281 status = T_ADDED;
282 }
283 else if (vers->ts_user == NULL)
284 {
285 /* FIXME: What classify_file does in this case is print
286 "%s was lost". We probably should do the same. */
287 freevers_ts (&vers);
288 return 0;
289 }
290 else if (vers->ts_rcs != NULL
291 && (args->force || strcmp (vers->ts_user, vers->ts_rcs) != 0))
292 /* If we are forcing commits, pretend that the file is
293 modified. */
294 status = T_MODIFIED;
295 else
296 {
297 /* This covers unmodified files, as well as a variety of other
298 cases. FIXME: we probably should be printing a message and
299 returning 1 for many of those cases (but I'm not sure
300 exactly which ones). */
301 freevers_ts (&vers);
302 return 0;
303 }
304
305 node = getnode ();
306 node->key = xstrdup (finfo->fullname);
307
308 data = xmalloc (sizeof (struct logfile_info));
309 data->type = status;
310 data->tag = xstrdup (vers->tag);
311 data->rev_old = data->rev_new = NULL;
312
313 node->type = UPDATE;
314 node->delproc = update_delproc;
315 node->data = data;
316 (void)addnode (args->ulist, node);
317
318 ++args->argc;
319
320 freevers_ts (&vers);
321 return 0;
322 }
323
324
325
326 static int
copy_ulist(Node * node,void * data)327 copy_ulist (Node *node, void *data)
328 {
329 struct find_data *args = data;
330 args->argv[args->argc++] = node->key;
331 return 0;
332 }
333 #endif /* CLIENT_SUPPORT */
334
335
336
337 #ifdef SERVER_SUPPORT
338 # define COMMIT_OPTIONS "+cnlRm:fF:r:"
339 #else /* !SERVER_SUPPORT */
340 # define COMMIT_OPTIONS "+clRm:fF:r:"
341 #endif /* SERVER_SUPPORT */
342 int
commit(int argc,char ** argv)343 commit (int argc, char **argv)
344 {
345 int c;
346 int err = 0;
347 int local = 0;
348
349 if (argc == -1)
350 usage (commit_usage);
351
352 #ifdef CVS_BADROOT
353 /*
354 * For log purposes, do not allow "root" to commit files. If you look
355 * like root, but are really logged in as a non-root user, it's OK.
356 */
357 /* FIXME: Shouldn't this check be much more closely related to the
358 readonly user stuff (CVSROOT/readers, &c). That is, why should
359 root be able to "cvs init", "cvs import", &c, but not "cvs ci"? */
360 /* Who we are on the client side doesn't affect logging. */
361 if (geteuid () == (uid_t) 0 && !current_parsed_root->isremote)
362 {
363 struct passwd *pw;
364
365 if ((pw = getpwnam (getcaller ())) == NULL)
366 error (1, 0,
367 "your apparent username (%s) is unknown to this system",
368 getcaller ());
369 if (pw->pw_uid == (uid_t) 0)
370 error (1, 0, "'root' is not allowed to commit files");
371 }
372 #endif /* CVS_BADROOT */
373
374 optind = 0;
375 while ((c = getopt (argc, argv, COMMIT_OPTIONS)) != -1)
376 {
377 switch (c)
378 {
379 case 'c':
380 check_valid_edit = 1;
381 break;
382 #ifdef SERVER_SUPPORT
383 case 'n':
384 /* Silently ignore -n for compatibility with old
385 * clients.
386 */
387 break;
388 #endif /* SERVER_SUPPORT */
389 case 'm':
390 #ifdef FORCE_USE_EDITOR
391 use_editor = 1;
392 #else
393 use_editor = 0;
394 #endif
395 if (saved_message)
396 {
397 free (saved_message);
398 saved_message = NULL;
399 }
400
401 saved_message = xstrdup (optarg);
402 break;
403 case 'r':
404 if (saved_tag)
405 free (saved_tag);
406 saved_tag = xstrdup (optarg);
407 break;
408 case 'l':
409 local = 1;
410 break;
411 case 'R':
412 local = 0;
413 break;
414 case 'f':
415 force_ci = 1;
416 check_valid_edit = 0;
417 local = 1; /* also disable recursion */
418 break;
419 case 'F':
420 #ifdef FORCE_USE_EDITOR
421 use_editor = 1;
422 #else
423 use_editor = 0;
424 #endif
425 logfile = optarg;
426 break;
427 case '?':
428 default:
429 usage (commit_usage);
430 break;
431 }
432 }
433 argc -= optind;
434 argv += optind;
435
436 /* numeric specified revision means we ignore sticky tags... */
437 if (saved_tag && isdigit ((unsigned char) *saved_tag))
438 {
439 char *p = saved_tag + strlen (saved_tag);
440 aflag = 1;
441 /* strip trailing dots and leading zeros */
442 while (*--p == '.') ;
443 p[1] = '\0';
444 while (saved_tag[0] == '0' && isdigit ((unsigned char) saved_tag[1]))
445 ++saved_tag;
446 }
447
448 /* some checks related to the "-F logfile" option */
449 if (logfile)
450 {
451 size_t size = 0, len;
452
453 if (saved_message)
454 error (1, 0, "cannot specify both a message and a log file");
455
456 get_file (logfile, logfile, "r", &saved_message, &size, &len);
457 }
458
459 #ifdef CLIENT_SUPPORT
460 if (current_parsed_root->isremote)
461 {
462 struct find_data find_args;
463
464 ign_setup ();
465
466 find_args.ulist = getlist ();
467 find_args.argc = 0;
468 find_args.questionables = NULL;
469 find_args.ignlist = NULL;
470 find_args.repository = NULL;
471
472 /* It is possible that only a numeric tag should set this.
473 I haven't really thought about it much.
474 Anyway, I suspect that setting it unnecessarily only causes
475 a little unneeded network traffic. */
476 find_args.force = force_ci || saved_tag != NULL;
477
478 err = start_recursion
479 (find_fileproc, find_filesdoneproc, find_dirent_proc, NULL,
480 &find_args, argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE,
481 NULL, 0, NULL );
482 if (err)
483 error (1, 0, "correct above errors first!");
484
485 if (find_args.argc == 0)
486 {
487 /* Nothing to commit. Exit now without contacting the
488 server (note that this means that we won't print "?
489 foo" for files which merit it, because we don't know
490 what is in the CVSROOT/cvsignore file). */
491 dellist (&find_args.ulist);
492 return 0;
493 }
494
495 /* Now we keep track of which files we actually are going to
496 operate on, and only work with those files in the future.
497 This saves time--we don't want to search the file system
498 of the working directory twice. */
499 if (size_overflow_p (xtimes (find_args.argc, sizeof (char **))))
500 {
501 find_args.argc = 0;
502 return 0;
503 }
504 find_args.argv = xnmalloc (find_args.argc, sizeof (char **));
505 find_args.argc = 0;
506 walklist (find_args.ulist, copy_ulist, &find_args);
507
508 /* Do this before calling do_editor; don't ask for a log
509 message if we can't talk to the server. But do it after we
510 have made the checks that we can locally (to more quickly
511 catch syntax errors, the case where no files are modified,
512 added or removed, etc.).
513
514 On the other hand, calling start_server before do_editor
515 means that we chew up server resources the whole time that
516 the user has the editor open (hours or days if the user
517 forgets about it), which seems dubious. */
518 start_server ();
519
520 /*
521 * We do this once, not once for each directory as in normal CVS.
522 * The protocol is designed this way. This is a feature.
523 */
524 if (use_editor)
525 do_editor (".", &saved_message, NULL, find_args.ulist);
526
527 /* We always send some sort of message, even if empty. */
528 option_with_arg ("-m", saved_message ? saved_message : "");
529
530 /* OK, now process all the questionable files we have been saving
531 up. */
532 {
533 struct question *p;
534 struct question *q;
535
536 p = find_args.questionables;
537 while (p != NULL)
538 {
539 if (ign_inhibit_server || !supported_request ("Questionable"))
540 {
541 cvs_output ("? ", 2);
542 if (p->dir[0] != '\0')
543 {
544 cvs_output (p->dir, 0);
545 cvs_output ("/", 1);
546 }
547 cvs_output (p->file, 0);
548 cvs_output ("\n", 1);
549 }
550 else
551 {
552 /* This used to send the Directory line of its own accord,
553 * but skipped some of the other processing like checking
554 * for whether the server would accept "Relative-directory"
555 * requests. Relying on send_a_repository() to do this
556 * picks up these checks but also:
557 *
558 * 1. Causes the "Directory" request to be sent only once
559 * per directory.
560 * 2. Causes the global TOPLEVEL_REPOS to be set.
561 * 3. Causes "Static-directory" and "Sticky" requests
562 * to sometimes be sent.
563 *
564 * (1) is almost certainly a plus. (2) & (3) may or may
565 * not be useful sometimes, and will ocassionally cause a
566 * little extra network traffic. The additional network
567 * traffic is probably already saved several times over and
568 * certainly cancelled out via the multiple "Directory"
569 * request suppression of (1).
570 */
571 send_a_repository (p->dir, p->repos, p->dir);
572
573 send_to_server ("Questionable ", 0);
574 send_to_server (p->file, 0);
575 send_to_server ("\012", 1);
576 }
577 free (p->dir);
578 free (p->repos);
579 free (p->file);
580 q = p->next;
581 free (p);
582 p = q;
583 }
584 }
585
586 if (local)
587 send_arg ("-l");
588 if (check_valid_edit)
589 send_arg ("-c");
590 if (force_ci)
591 send_arg ("-f");
592 option_with_arg ("-r", saved_tag);
593 send_arg ("--");
594
595 /* FIXME: This whole find_args.force/SEND_FORCE business is a
596 kludge. It would seem to be a server bug that we have to
597 say that files are modified when they are not. This makes
598 "cvs commit -r 2" across a whole bunch of files a very slow
599 operation (and it isn't documented in cvsclient.texi). I
600 haven't looked at the server code carefully enough to be
601 _sure_ why this is needed, but if it is because the "ci"
602 program, which we used to call, wanted the file to exist,
603 then it would be relatively simple to fix in the server. */
604 send_files (find_args.argc, find_args.argv, local, 0,
605 find_args.force ? SEND_FORCE : 0);
606
607 /* Sending only the names of the files which were modified, added,
608 or removed means that the server will only do an up-to-date
609 check on those files. This is different from local CVS and
610 previous versions of client/server CVS, but it probably is a Good
611 Thing, or at least Not Such A Bad Thing. */
612 send_file_names (find_args.argc, find_args.argv, 0);
613 free (find_args.argv);
614 dellist (&find_args.ulist);
615
616 send_to_server ("ci\012", 0);
617 err = get_responses_and_close ();
618 logmsg_cleanup(err);
619 return err;
620 }
621 #endif
622
623 if (saved_tag != NULL)
624 tag_check_valid (saved_tag, argc, argv, local, aflag, "", false);
625
626 /* XXX - this is not the perfect check for this */
627 if (argc <= 0)
628 write_dirtag = saved_tag;
629
630 wrap_setup ();
631
632 lock_tree_promotably (argc, argv, local, W_LOCAL, aflag);
633
634 /*
635 * Set up the master update list and hard link list
636 */
637 mulist = getlist ();
638
639 #ifdef PRESERVE_PERMISSIONS_SUPPORT
640 if (preserve_perms)
641 {
642 hardlist = getlist ();
643
644 /*
645 * We need to save the working directory so that
646 * check_fileproc can construct a full pathname for each file.
647 */
648 working_dir = xgetcwd ();
649 }
650 #endif
651
652 /*
653 * Run the recursion processor to verify the files are all up-to-date
654 */
655 err = start_recursion (check_fileproc, check_filesdoneproc,
656 check_direntproc, NULL, NULL, argc, argv, local,
657 W_LOCAL, aflag, CVS_LOCK_NONE, NULL, 1, NULL);
658 if (err)
659 error (1, 0, "correct above errors first!");
660
661 /*
662 * Run the recursion processor to commit the files
663 */
664 write_dirnonbranch = 0;
665 if (noexec == 0)
666 err = start_recursion (commit_fileproc, commit_filesdoneproc,
667 commit_direntproc, commit_dirleaveproc, NULL,
668 argc, argv, local, W_LOCAL, aflag,
669 CVS_LOCK_WRITE, NULL, 1, NULL);
670
671 /*
672 * Unlock all the dirs and clean up
673 */
674 Lock_Cleanup ();
675 dellist (&mulist);
676
677 /* see if we need to sleep before returning to avoid time-stamp races */
678 if (!server_active && last_register_time)
679 {
680 sleep_past (last_register_time);
681 }
682
683 logmsg_cleanup(err);
684 return err;
685 }
686
687
688
689 /* This routine determines the status of a given file and retrieves
690 the version information that is associated with that file. */
691
692 static
693 Ctype
classify_file_internal(struct file_info * finfo,Vers_TS ** vers)694 classify_file_internal (struct file_info *finfo, Vers_TS **vers)
695 {
696 int save_noexec, save_quiet, save_really_quiet;
697 Ctype status;
698
699 /* FIXME: Do we need to save quiet as well as really_quiet? Last
700 time I glanced at Classify_File I only saw it looking at really_quiet
701 not quiet. */
702 save_noexec = noexec;
703 save_quiet = quiet;
704 save_really_quiet = really_quiet;
705 noexec = quiet = really_quiet = 1;
706
707 /* handle specified numeric revision specially */
708 if (saved_tag && isdigit ((unsigned char) *saved_tag))
709 {
710 /* If the tag is for the trunk, make sure we're at the head */
711 if (numdots (saved_tag) < 2)
712 {
713 status = Classify_File (finfo, NULL, NULL,
714 NULL, 1, aflag, vers, 0);
715 if (status == T_UPTODATE || status == T_MODIFIED ||
716 status == T_ADDED)
717 {
718 Ctype xstatus;
719
720 freevers_ts (vers);
721 xstatus = Classify_File (finfo, saved_tag, NULL,
722 NULL, 1, aflag, vers, 0);
723 if (xstatus == T_REMOVE_ENTRY)
724 status = T_MODIFIED;
725 else if (status == T_MODIFIED && xstatus == T_CONFLICT)
726 status = T_MODIFIED;
727 else
728 status = xstatus;
729 }
730 }
731 else
732 {
733 char *xtag, *cp;
734
735 /*
736 * The revision is off the main trunk; make sure we're
737 * up-to-date with the head of the specified branch.
738 */
739 xtag = xstrdup (saved_tag);
740 if ((numdots (xtag) & 1) != 0)
741 {
742 cp = strrchr (xtag, '.');
743 *cp = '\0';
744 }
745 status = Classify_File (finfo, xtag, NULL,
746 NULL, 1, aflag, vers, 0);
747 if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
748 && (cp = strrchr (xtag, '.')) != NULL)
749 {
750 /* pluck one more dot off the revision */
751 *cp = '\0';
752 freevers_ts (vers);
753 status = Classify_File (finfo, xtag, NULL,
754 NULL, 1, aflag, vers, 0);
755 if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
756 status = T_MODIFIED;
757 }
758 /* now, muck with vers to make the tag correct */
759 free ((*vers)->tag);
760 (*vers)->tag = xstrdup (saved_tag);
761 free (xtag);
762 }
763 }
764 else
765 status = Classify_File (finfo, saved_tag, NULL, NULL, 1, 0, vers, 0);
766 noexec = save_noexec;
767 quiet = save_quiet;
768 really_quiet = save_really_quiet;
769
770 return status;
771 }
772
773
774
775 /*
776 * Check to see if a file is ok to commit and make sure all files are
777 * up-to-date
778 */
779 /* ARGSUSED */
780 static int
check_fileproc(void * callerdat,struct file_info * finfo)781 check_fileproc (void *callerdat, struct file_info *finfo)
782 {
783 Ctype status;
784 const char *xdir;
785 Node *p;
786 List *ulist, *cilist;
787 Vers_TS *vers;
788 struct commit_info *ci;
789 struct logfile_info *li;
790 int retval = 1;
791
792 size_t cvsroot_len = strlen (current_parsed_root->directory);
793
794 if (!finfo->repository)
795 {
796 error (0, 0, "nothing known about `%s'", finfo->fullname);
797 return 1;
798 }
799
800 if (strncmp (finfo->repository, current_parsed_root->directory,
801 cvsroot_len) == 0
802 && ISSLASH (finfo->repository[cvsroot_len])
803 && strncmp (finfo->repository + cvsroot_len + 1,
804 CVSROOTADM,
805 sizeof (CVSROOTADM) - 1) == 0
806 && ISSLASH (finfo->repository[cvsroot_len + sizeof (CVSROOTADM)])
807 && strcmp (finfo->repository + cvsroot_len + sizeof (CVSROOTADM) + 1,
808 CVSNULLREPOS) == 0
809 )
810 error (1, 0, "cannot check in to %s", finfo->repository);
811
812 status = classify_file_internal (finfo, &vers);
813
814 /*
815 * If the force-commit option is enabled, and the file in question
816 * appears to be up-to-date, just make it look modified so that
817 * it will be committed.
818 */
819 if (force_ci && status == T_UPTODATE)
820 status = T_MODIFIED;
821
822 switch (status)
823 {
824 case T_CHECKOUT:
825 case T_PATCH:
826 case T_NEEDS_MERGE:
827 case T_REMOVE_ENTRY:
828 error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname);
829 goto out;
830 case T_CONFLICT:
831 case T_MODIFIED:
832 case T_ADDED:
833 case T_REMOVED:
834 {
835 char *editor = NULL;
836
837 /*
838 * some quick sanity checks; if no numeric -r option specified:
839 * - can't have a sticky date
840 * - can't have a sticky tag that is not a branch
841 * Also,
842 * - if status is T_REMOVED, file must not exist and its entry
843 * can't have a numeric sticky tag.
844 * - if status is T_ADDED, rcs file must not exist unless on
845 * a branch or head is dead
846 * - if status is T_ADDED, can't have a non-trunk numeric rev
847 * - if status is T_MODIFIED and a Conflict marker exists, don't
848 * allow the commit if timestamp is identical or if we find
849 * an RCS_MERGE_PAT in the file.
850 */
851 if (!saved_tag || !isdigit ((unsigned char) *saved_tag))
852 {
853 if (vers->date)
854 {
855 error (0, 0,
856 "cannot commit with sticky date for file `%s'",
857 finfo->fullname);
858 goto out;
859 }
860 if (status == T_MODIFIED && vers->tag &&
861 !RCS_isbranch (finfo->rcs, vers->tag))
862 {
863 error (0, 0,
864 "sticky tag `%s' for file `%s' is not a branch",
865 vers->tag, finfo->fullname);
866 goto out;
867 }
868 }
869 if (status == T_CONFLICT && !force_ci)
870 {
871 error (0, 0,
872 "file `%s' had a conflict and has not been modified",
873 finfo->fullname);
874 goto out;
875 }
876 if (status == T_MODIFIED && !force_ci && file_has_markers (finfo))
877 {
878 /* Make this a warning, not an error, because we have
879 no way of knowing whether the "conflict indicators"
880 are really from a conflict or whether they are part
881 of the document itself (cvs.texinfo and sanity.sh in
882 CVS itself, for example, tend to want to have strings
883 like ">>>>>>>" at the start of a line). Making people
884 kludge this the way they need to kludge keyword
885 expansion seems undesirable. And it is worse than
886 keyword expansion, because there is no -ko
887 analogue. */
888 error (0, 0,
889 "\
890 warning: file `%s' seems to still contain conflict indicators",
891 finfo->fullname);
892 }
893
894 if (status == T_REMOVED)
895 {
896 if (vers->ts_user != NULL)
897 {
898 error (0, 0,
899 "`%s' should be removed and is still there (or is"
900 " back again)", finfo->fullname);
901 goto out;
902 }
903
904 if (vers->tag && isdigit ((unsigned char) *vers->tag))
905 {
906 /* Remove also tries to forbid this, but we should check
907 here. I'm only _sure_ about somewhat obscure cases
908 (hacking the Entries file, using an old version of
909 CVS for the remove and a new one for the commit), but
910 there might be other cases. */
911 error (0, 0,
912 "cannot remove file `%s' which has a numeric sticky"
913 " tag of `%s'", finfo->fullname, vers->tag);
914 freevers_ts (&vers);
915 goto out;
916 }
917 }
918 if (status == T_ADDED)
919 {
920 if (vers->tag == NULL)
921 {
922 if (finfo->rcs != NULL &&
923 !RCS_isdead (finfo->rcs, finfo->rcs->head))
924 {
925 error (0, 0,
926 "cannot add file `%s' when RCS file `%s' already exists",
927 finfo->fullname, finfo->rcs->path);
928 goto out;
929 }
930 }
931 else if (isdigit ((unsigned char) *vers->tag) &&
932 numdots (vers->tag) > 1)
933 {
934 error (0, 0,
935 "cannot add file `%s' with revision `%s'; must be on trunk",
936 finfo->fullname, vers->tag);
937 goto out;
938 }
939 }
940
941 /* done with consistency checks; now, to get on with the commit */
942 if (finfo->update_dir[0] == '\0')
943 xdir = ".";
944 else
945 xdir = finfo->update_dir;
946 if ((p = findnode (mulist, xdir)) != NULL)
947 {
948 ulist = ((struct master_lists *) p->data)->ulist;
949 cilist = ((struct master_lists *) p->data)->cilist;
950 }
951 else
952 {
953 struct master_lists *ml;
954
955 ml = xmalloc (sizeof (struct master_lists));
956 ulist = ml->ulist = getlist ();
957 cilist = ml->cilist = getlist ();
958
959 p = getnode ();
960 p->key = xstrdup (xdir);
961 p->type = UPDATE;
962 p->data = ml;
963 p->delproc = masterlist_delproc;
964 (void) addnode (mulist, p);
965 }
966
967 /* first do ulist, then cilist */
968 p = getnode ();
969 p->key = xstrdup (finfo->file);
970 p->type = UPDATE;
971 p->delproc = update_delproc;
972 li = xmalloc (sizeof (struct logfile_info));
973 li->type = status;
974
975 if (check_valid_edit)
976 {
977 char *editors = NULL;
978
979 editor = NULL;
980 editors = fileattr_get0 (finfo->file, "_editors");
981 if (editors != NULL)
982 {
983 char *caller = getcaller ();
984 char *p = NULL;
985 char *p0 = NULL;
986
987 p = editors;
988 p0 = p;
989 while (*p != '\0')
990 {
991 p = strchr (p, '>');
992 if (p == NULL)
993 {
994 break;
995 }
996 *p = '\0';
997 if (strcmp (caller, p0) == 0)
998 {
999 break;
1000 }
1001 p = strchr (p + 1, ',');
1002 if (p == NULL)
1003 {
1004 break;
1005 }
1006 ++p;
1007 p0 = p;
1008 }
1009
1010 if (strcmp (caller, p0) == 0)
1011 {
1012 editor = caller;
1013 }
1014
1015 free (editors);
1016 }
1017 }
1018
1019 if (check_valid_edit && editor == NULL)
1020 {
1021 error (0, 0, "Valid edit does not exist for %s",
1022 finfo->fullname);
1023 freevers_ts (&vers);
1024 return 1;
1025 }
1026
1027 li->tag = xstrdup (vers->tag);
1028 li->rev_old = xstrdup (vers->vn_rcs);
1029 li->rev_new = NULL;
1030 p->data = li;
1031 (void) addnode (ulist, p);
1032
1033 p = getnode ();
1034 p->key = xstrdup (finfo->file);
1035 p->type = UPDATE;
1036 p->delproc = ci_delproc;
1037 ci = xmalloc (sizeof (struct commit_info));
1038 ci->status = status;
1039 if (vers->tag)
1040 if (isdigit ((unsigned char) *vers->tag))
1041 ci->rev = xstrdup (vers->tag);
1042 else
1043 ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
1044 else
1045 ci->rev = NULL;
1046 ci->tag = xstrdup (vers->tag);
1047 ci->options = xstrdup (vers->options);
1048 p->data = ci;
1049 (void) addnode (cilist, p);
1050
1051 #ifdef PRESERVE_PERMISSIONS_SUPPORT
1052 if (preserve_perms)
1053 {
1054 /* Add this file to hardlist, indexed on its inode. When
1055 we are done, we can find out what files are hardlinked
1056 to a given file by looking up its inode in hardlist. */
1057 char *fullpath;
1058 Node *linkp;
1059 struct hardlink_info *hlinfo;
1060
1061 /* Get the full pathname of the current file. */
1062 fullpath = Xasprintf ("%s/%s", working_dir, finfo->fullname);
1063
1064 /* To permit following links in subdirectories, files
1065 are keyed on finfo->fullname, not on finfo->name. */
1066 linkp = lookup_file_by_inode (fullpath);
1067
1068 /* If linkp is NULL, the file doesn't exist... maybe
1069 we're doing a remove operation? */
1070 if (linkp != NULL)
1071 {
1072 /* Create a new hardlink_info node, which will record
1073 the current file's status and the links listed in its
1074 `hardlinks' delta field. We will append this
1075 hardlink_info node to the appropriate hardlist entry. */
1076 hlinfo = xmalloc (sizeof (struct hardlink_info));
1077 hlinfo->status = status;
1078 linkp->data = hlinfo;
1079 }
1080 }
1081 #endif
1082
1083 break;
1084 }
1085
1086 case T_UNKNOWN:
1087 error (0, 0, "nothing known about `%s'", finfo->fullname);
1088 goto out;
1089 case T_UPTODATE:
1090 break;
1091 default:
1092 error (0, 0, "CVS internal error: unknown status %d", status);
1093 break;
1094 }
1095
1096 retval = 0;
1097
1098 out:
1099
1100 freevers_ts (&vers);
1101 return retval;
1102 }
1103
1104
1105
1106 /*
1107 * By default, return the code that tells do_recursion to examine all
1108 * directories
1109 */
1110 /* ARGSUSED */
1111 static Dtype
check_direntproc(void * callerdat,const char * dir,const char * repos,const char * update_dir,List * entries)1112 check_direntproc (void *callerdat, const char *dir, const char *repos,
1113 const char *update_dir, List *entries)
1114 {
1115 if (!isdir (dir))
1116 return R_SKIP_ALL;
1117
1118 if (!quiet)
1119 error (0, 0, "Examining %s", update_dir);
1120
1121 return R_PROCESS;
1122 }
1123
1124
1125
1126 /*
1127 * Walklist proc to generate an arg list from the line in commitinfo
1128 */
1129 static int
precommit_list_to_args_proc(p,closure)1130 precommit_list_to_args_proc (p, closure)
1131 Node *p;
1132 void *closure;
1133 {
1134 struct format_cmdline_walklist_closure *c = closure;
1135 struct logfile_info *li;
1136 char *arg = NULL;
1137 const char *f;
1138 char *d;
1139 size_t doff;
1140
1141 if (p->data == NULL) return 1;
1142
1143 f = c->format;
1144 d = *c->d;
1145 /* foreach requested attribute */
1146 while (*f)
1147 {
1148 switch (*f++)
1149 {
1150 case 's':
1151 li = p->data;
1152 if (li->type == T_ADDED
1153 || li->type == T_MODIFIED
1154 || li->type == T_REMOVED)
1155 {
1156 arg = p->key;
1157 }
1158 break;
1159 default:
1160 error (1, 0,
1161 "Unknown format character or not a list attribute: %c",
1162 f[-1]);
1163 /* NOTREACHED */
1164 break;
1165 }
1166 /* copy the attribute into an argument */
1167 if (c->quotes)
1168 {
1169 arg = cmdlineescape (c->quotes, arg);
1170 }
1171 else
1172 {
1173 arg = cmdlinequote ('"', arg);
1174 }
1175 doff = d - *c->buf;
1176 expand_string (c->buf, c->length, doff + strlen (arg));
1177 d = *c->buf + doff;
1178 strncpy (d, arg, strlen (arg));
1179 d += strlen (arg);
1180 free (arg);
1181
1182 /* and always put the extra space on. we'll have to back up a char
1183 * when we're done, but that seems most efficient
1184 */
1185 doff = d - *c->buf;
1186 expand_string (c->buf, c->length, doff + 1);
1187 d = *c->buf + doff;
1188 *d++ = ' ';
1189 }
1190 /* correct our original pointer into the buff */
1191 *c->d = d;
1192 return 0;
1193 }
1194
1195
1196
1197 /*
1198 * Callback proc for pre-commit checking
1199 */
1200 static int
precommit_proc(const char * repository,const char * filter,void * closure)1201 precommit_proc (const char *repository, const char *filter, void *closure)
1202 {
1203 char *newfilter = NULL;
1204 char *cmdline;
1205 const char *srepos = Short_Repository (repository);
1206 List *ulist = closure;
1207
1208 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1209 if (!strchr (filter, '%'))
1210 {
1211 error (0, 0,
1212 "warning: commitinfo line contains no format strings:\n"
1213 " \"%s\"\n"
1214 "Appending defaults (\" %%r/%%p %%s\"), but please be aware that this usage is\n"
1215 "deprecated.", filter);
1216 newfilter = Xasprintf ("%s %%r/%%p %%s", filter);
1217 filter = newfilter;
1218 }
1219 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1220
1221 /*
1222 * Cast any NULL arguments as appropriate pointers as this is an
1223 * stdarg function and we need to be certain the caller gets what
1224 * is expected.
1225 */
1226 cmdline = format_cmdline (
1227 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
1228 false, srepos,
1229 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
1230 filter,
1231 "c", "s", cvs_cmd_name,
1232 "I", "s", global_session_id,
1233 #ifdef SERVER_SUPPORT
1234 "R", "s", referrer ? referrer->original : "NONE",
1235 #endif /* SERVER_SUPPORT */
1236 "p", "s", srepos,
1237 "r", "s", current_parsed_root->directory,
1238 "s", ",", ulist, precommit_list_to_args_proc,
1239 (void *) NULL,
1240 (char *) NULL);
1241
1242 if (newfilter) free (newfilter);
1243
1244 if (!cmdline || !strlen (cmdline))
1245 {
1246 if (cmdline) free (cmdline);
1247 error (0, 0, "precommit proc resolved to the empty string!");
1248 return 1;
1249 }
1250
1251 run_setup (cmdline);
1252 free (cmdline);
1253
1254 return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL | RUN_REALLY);
1255 }
1256
1257
1258
1259 /*
1260 * Run the pre-commit checks for the dir
1261 */
1262 /* ARGSUSED */
1263 static int
check_filesdoneproc(void * callerdat,int err,const char * repos,const char * update_dir,List * entries)1264 check_filesdoneproc (void *callerdat, int err, const char *repos,
1265 const char *update_dir, List *entries)
1266 {
1267 int n;
1268 Node *p;
1269 List *saved_ulist;
1270
1271 /* find the update list for this dir */
1272 p = findnode (mulist, update_dir);
1273 if (p != NULL)
1274 saved_ulist = ((struct master_lists *) p->data)->ulist;
1275 else
1276 saved_ulist = NULL;
1277
1278 /* skip the checks if there's nothing to do */
1279 if (saved_ulist == NULL || saved_ulist->list->next == saved_ulist->list)
1280 return err;
1281
1282 /* run any pre-commit checks */
1283 n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, PIOPT_ALL,
1284 saved_ulist);
1285 if (n > 0)
1286 {
1287 error (0, 0, "Pre-commit check failed");
1288 err += n;
1289 }
1290
1291 return err;
1292 }
1293
1294
1295
1296 /*
1297 * Do the work of committing a file
1298 */
1299 static int maxrev;
1300 static char *sbranch;
1301
1302 /* ARGSUSED */
1303 static int
commit_fileproc(void * callerdat,struct file_info * finfo)1304 commit_fileproc (void *callerdat, struct file_info *finfo)
1305 {
1306 Node *p;
1307 int err = 0;
1308 List *ulist, *cilist;
1309 struct commit_info *ci;
1310
1311 /* Keep track of whether write_dirtag is a branch tag.
1312 Note that if it is a branch tag in some files and a nonbranch tag
1313 in others, treat it as a nonbranch tag. It is possible that case
1314 should elicit a warning or an error. */
1315 if (write_dirtag != NULL
1316 && finfo->rcs != NULL)
1317 {
1318 char *rev = RCS_getversion (finfo->rcs, write_dirtag, NULL, 1, NULL);
1319 if (rev != NULL
1320 && !RCS_nodeisbranch (finfo->rcs, write_dirtag))
1321 write_dirnonbranch = 1;
1322 if (rev != NULL)
1323 free (rev);
1324 }
1325
1326 if (finfo->update_dir[0] == '\0')
1327 p = findnode (mulist, ".");
1328 else
1329 p = findnode (mulist, finfo->update_dir);
1330
1331 /*
1332 * if p is null, there were file type command line args which were
1333 * all up-to-date so nothing really needs to be done
1334 */
1335 if (p == NULL)
1336 return 0;
1337 ulist = ((struct master_lists *) p->data)->ulist;
1338 cilist = ((struct master_lists *) p->data)->cilist;
1339
1340 /*
1341 * At this point, we should have the commit message unless we were called
1342 * with files as args from the command line. In that latter case, we
1343 * need to get the commit message ourselves
1344 */
1345 if (!got_message)
1346 {
1347 got_message = 1;
1348 if (!server_active && use_editor)
1349 do_editor (finfo->update_dir, &saved_message,
1350 finfo->repository, ulist);
1351 do_verify (&saved_message, finfo->repository, ulist);
1352 }
1353
1354 p = findnode (cilist, finfo->file);
1355 if (p == NULL)
1356 return 0;
1357
1358 ci = p->data;
1359 if (ci->status == T_MODIFIED)
1360 {
1361 if (finfo->rcs == NULL)
1362 error (1, 0, "internal error: no parsed RCS file");
1363 if (lock_RCS (finfo->file, finfo->rcs, ci->rev,
1364 finfo->repository) != 0)
1365 {
1366 unlockrcs (finfo->rcs);
1367 err = 1;
1368 goto out;
1369 }
1370 }
1371 else if (ci->status == T_ADDED)
1372 {
1373 if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
1374 &finfo->rcs) != 0)
1375 {
1376 if (finfo->rcs != NULL)
1377 fixaddfile (finfo->rcs->path);
1378 err = 1;
1379 goto out;
1380 }
1381
1382 /* adding files with a tag, now means adding them on a branch.
1383 Since the branch test was done in check_fileproc for
1384 modified files, we need to stub it in again here. */
1385
1386 if (ci->tag
1387
1388 /* If numeric, it is on the trunk; check_fileproc enforced
1389 this. */
1390 && !isdigit ((unsigned char) ci->tag[0]))
1391 {
1392 if (finfo->rcs == NULL)
1393 error (1, 0, "internal error: no parsed RCS file");
1394 if (ci->rev)
1395 free (ci->rev);
1396 ci->rev = RCS_whatbranch (finfo->rcs, ci->tag);
1397 err = Checkin ('A', finfo, ci->rev,
1398 ci->tag, ci->options, saved_message);
1399 if (err != 0)
1400 {
1401 unlockrcs (finfo->rcs);
1402 fixbranch (finfo->rcs, sbranch);
1403 }
1404
1405 (void) time (&last_register_time);
1406
1407 ci->status = T_UPTODATE;
1408 }
1409 }
1410
1411 /*
1412 * Add the file for real
1413 */
1414 if (ci->status == T_ADDED)
1415 {
1416 char *xrev = NULL;
1417
1418 if (ci->rev == NULL)
1419 {
1420 /* find the max major rev number in this directory */
1421 maxrev = 0;
1422 (void) walklist (finfo->entries, findmaxrev, NULL);
1423 if (finfo->rcs->head)
1424 {
1425 /* resurrecting: include dead revision */
1426 int thisrev = atoi (finfo->rcs->head);
1427 if (thisrev > maxrev)
1428 maxrev = thisrev;
1429 }
1430 if (maxrev == 0)
1431 maxrev = 1;
1432 xrev = Xasprintf ("%d", maxrev);
1433 }
1434
1435 /* XXX - an added file with symbolic -r should add tag as well */
1436 err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options);
1437 if (xrev)
1438 free (xrev);
1439 }
1440 else if (ci->status == T_MODIFIED)
1441 {
1442 err = Checkin ('M', finfo, ci->rev, ci->tag,
1443 ci->options, saved_message);
1444
1445 (void) time (&last_register_time);
1446
1447 if (err != 0)
1448 {
1449 unlockrcs (finfo->rcs);
1450 fixbranch (finfo->rcs, sbranch);
1451 }
1452 }
1453 else if (ci->status == T_REMOVED)
1454 {
1455 err = remove_file (finfo, ci->tag, saved_message);
1456 #ifdef SERVER_SUPPORT
1457 if (server_active)
1458 {
1459 server_scratch_entry_only ();
1460 server_updated (finfo,
1461 NULL,
1462
1463 /* Doesn't matter, it won't get checked. */
1464 SERVER_UPDATED,
1465
1466 (mode_t) -1,
1467 NULL,
1468 NULL);
1469 }
1470 #endif
1471 }
1472
1473 /* Clearly this is right for T_MODIFIED. I haven't thought so much
1474 about T_ADDED or T_REMOVED. */
1475 notify_do ('C', finfo->file, finfo->update_dir, getcaller (), NULL, NULL,
1476 finfo->repository);
1477
1478 out:
1479 if (err != 0)
1480 {
1481 /* on failure, remove the file from ulist */
1482 p = findnode (ulist, finfo->file);
1483 if (p)
1484 delnode (p);
1485 }
1486 else
1487 {
1488 /* On success, retrieve the new version number of the file and
1489 copy it into the log information (see logmsg.c
1490 (logfile_write) for more details). We should only update
1491 the version number for files that have been added or
1492 modified but not removed since classify_file_internal
1493 will return the version number of a file even after it has
1494 been removed from the archive, which is not the behavior we
1495 want for our commitlog messages; we want the old version
1496 number and then "NONE." */
1497
1498 if (ci->status != T_REMOVED)
1499 {
1500 p = findnode (ulist, finfo->file);
1501 if (p)
1502 {
1503 Vers_TS *vers;
1504 struct logfile_info *li;
1505
1506 (void) classify_file_internal (finfo, &vers);
1507 li = p->data;
1508 li->rev_new = xstrdup (vers->vn_rcs);
1509 freevers_ts (&vers);
1510 }
1511 }
1512 }
1513 if (SIG_inCrSect ())
1514 SIG_endCrSect ();
1515
1516 return err;
1517 }
1518
1519
1520
1521 /*
1522 * Log the commit and clean up the update list
1523 */
1524 /* ARGSUSED */
1525 static int
commit_filesdoneproc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)1526 commit_filesdoneproc (void *callerdat, int err, const char *repository,
1527 const char *update_dir, List *entries)
1528 {
1529 Node *p;
1530 List *ulist;
1531
1532 assert (repository);
1533
1534 p = findnode (mulist, update_dir);
1535 if (p == NULL)
1536 return err;
1537
1538 ulist = ((struct master_lists *) p->data)->ulist;
1539
1540 got_message = 0;
1541
1542 /* Build the administrative files if necessary. */
1543 {
1544 const char *p;
1545
1546 if (strncmp (current_parsed_root->directory, repository,
1547 strlen (current_parsed_root->directory)) != 0)
1548 error (0, 0,
1549 "internal error: repository (%s) doesn't begin with root (%s)",
1550 repository, current_parsed_root->directory);
1551 p = repository + strlen (current_parsed_root->directory);
1552 if (*p == '/')
1553 ++p;
1554 if (strcmp ("CVSROOT", p) == 0
1555 /* Check for subdirectories because people may want to create
1556 subdirectories and list files therein in checkoutlist. */
1557 || strncmp ("CVSROOT/", p, strlen ("CVSROOT/")) == 0
1558 )
1559 {
1560 /* "Database" might a little bit grandiose and/or vague,
1561 but "checked-out copies of administrative files, unless
1562 in the case of modules and you are using ndbm in which
1563 case modules.{pag,dir,db}" is verbose and excessively
1564 focused on how the database is implemented. */
1565
1566 /* mkmodules requires the absolute name of the CVSROOT directory.
1567 Remove anything after the `CVSROOT' component -- this is
1568 necessary when committing in a subdirectory of CVSROOT. */
1569 char *admin_dir = xstrdup (repository);
1570 int cvsrootlen = strlen ("CVSROOT");
1571 assert (admin_dir[p - repository + cvsrootlen] == '\0'
1572 || admin_dir[p - repository + cvsrootlen] == '/');
1573 admin_dir[p - repository + cvsrootlen] = '\0';
1574
1575 if (!really_quiet)
1576 {
1577 cvs_output (program_name, 0);
1578 cvs_output (" ", 1);
1579 cvs_output (cvs_cmd_name, 0);
1580 cvs_output (": Rebuilding administrative file database\n", 0);
1581 }
1582 mkmodules (admin_dir);
1583 free (admin_dir);
1584 WriteTemplate (".", 1, repository);
1585 }
1586 }
1587
1588 /* FIXME: This used to be above the block above. The advantage of being
1589 * here is that it is not called until after all possible writes from this
1590 * process are complete. The disadvantage is that a fatal error during
1591 * update of CVSROOT can prevent the loginfo script from being called.
1592 *
1593 * A more general solution I have been considering is calling a generic
1594 * "postwrite" hook from the remove write lock routine.
1595 */
1596 Update_Logfile (repository, saved_message, NULL, ulist);
1597
1598 return err;
1599 }
1600
1601
1602
1603 /*
1604 * Get the log message for a dir
1605 */
1606 /* ARGSUSED */
1607 static Dtype
commit_direntproc(void * callerdat,const char * dir,const char * repos,const char * update_dir,List * entries)1608 commit_direntproc (void *callerdat, const char *dir, const char *repos,
1609 const char *update_dir, List *entries)
1610 {
1611 Node *p;
1612 List *ulist;
1613 char *real_repos;
1614
1615 if (!isdir (dir))
1616 return R_SKIP_ALL;
1617
1618 /* find the update list for this dir */
1619 p = findnode (mulist, update_dir);
1620 if (p != NULL)
1621 ulist = ((struct master_lists *) p->data)->ulist;
1622 else
1623 ulist = NULL;
1624
1625 /* skip the files as an optimization */
1626 if (ulist == NULL || ulist->list->next == ulist->list)
1627 return R_SKIP_FILES;
1628
1629 /* get commit message */
1630 got_message = 1;
1631 real_repos = Name_Repository (dir, update_dir);
1632 if (!server_active && use_editor)
1633 do_editor (update_dir, &saved_message, real_repos, ulist);
1634 do_verify (&saved_message, real_repos, ulist);
1635 free (real_repos);
1636 return R_PROCESS;
1637 }
1638
1639
1640
1641 /*
1642 * Process the post-commit proc if necessary
1643 */
1644 /* ARGSUSED */
1645 static int
commit_dirleaveproc(void * callerdat,const char * dir,int err,const char * update_dir,List * entries)1646 commit_dirleaveproc (void *callerdat, const char *dir, int err,
1647 const char *update_dir, List *entries)
1648 {
1649 /* update the per-directory tag info */
1650 /* FIXME? Why? The "commit examples" node of cvs.texinfo briefly
1651 mentions commit -r being sticky, but apparently in the context of
1652 this being a confusing feature! */
1653 if (err == 0 && write_dirtag != NULL)
1654 {
1655 char *repos = Name_Repository (NULL, update_dir);
1656 WriteTag (NULL, write_dirtag, NULL, write_dirnonbranch,
1657 update_dir, repos);
1658 free (repos);
1659 }
1660
1661 return err;
1662 }
1663
1664
1665
1666 /*
1667 * find the maximum major rev number in an entries file
1668 */
1669 static int
findmaxrev(Node * p,void * closure)1670 findmaxrev (Node *p, void *closure)
1671 {
1672 int thisrev;
1673 Entnode *entdata = p->data;
1674
1675 if (entdata->type != ENT_FILE)
1676 return 0;
1677 thisrev = atoi (entdata->version);
1678 if (thisrev > maxrev)
1679 maxrev = thisrev;
1680 return 0;
1681 }
1682
1683 /*
1684 * Actually remove a file by moving it to the attic
1685 * XXX - if removing a ,v file that is a relative symbolic link to
1686 * another ,v file, we probably should add a ".." component to the
1687 * link to keep it relative after we move it into the attic.
1688
1689 Return value is 0 on success, or >0 on error (in which case we have
1690 printed an error message). */
1691 static int
remove_file(struct file_info * finfo,char * tag,char * message)1692 remove_file (struct file_info *finfo, char *tag, char *message)
1693 {
1694 int retcode;
1695
1696 int branch;
1697 int lockflag;
1698 char *corev;
1699 char *rev;
1700 char *prev_rev;
1701 char *old_path;
1702
1703 corev = NULL;
1704 rev = NULL;
1705 prev_rev = NULL;
1706
1707 retcode = 0;
1708
1709 if (finfo->rcs == NULL)
1710 error (1, 0, "internal error: no parsed RCS file");
1711
1712 branch = 0;
1713 if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag)))
1714 {
1715 /* a symbolic tag is specified; just remove the tag from the file */
1716 if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0)
1717 {
1718 if (!quiet)
1719 error (0, retcode == -1 ? errno : 0,
1720 "failed to remove tag `%s' from `%s'", tag,
1721 finfo->fullname);
1722 return 1;
1723 }
1724 RCS_rewrite (finfo->rcs, NULL, NULL);
1725 Scratch_Entry (finfo->entries, finfo->file);
1726 return 0;
1727 }
1728
1729 /* we are removing the file from either the head or a branch */
1730 /* commit a new, dead revision. */
1731
1732 rev = NULL;
1733 lockflag = 1;
1734 if (branch)
1735 {
1736 char *branchname;
1737
1738 rev = RCS_whatbranch (finfo->rcs, tag);
1739 if (rev == NULL)
1740 {
1741 error (0, 0, "cannot find branch \"%s\".", tag);
1742 return 1;
1743 }
1744
1745 branchname = RCS_getbranch (finfo->rcs, rev, 1);
1746 if (branchname == NULL)
1747 {
1748 /* no revision exists on this branch. use the previous
1749 revision but do not lock. */
1750 corev = RCS_gettag (finfo->rcs, tag, 1, NULL);
1751 prev_rev = xstrdup (corev);
1752 lockflag = 0;
1753 } else
1754 {
1755 corev = xstrdup (rev);
1756 prev_rev = xstrdup (branchname);
1757 free (branchname);
1758 }
1759
1760 } else /* Not a branch */
1761 {
1762 /* Get current head revision of file. */
1763 prev_rev = RCS_head (finfo->rcs);
1764 }
1765
1766 /* if removing without a tag or a branch, then make sure the default
1767 branch is the trunk. */
1768 if (!tag && !branch)
1769 {
1770 if (RCS_setbranch (finfo->rcs, NULL) != 0)
1771 {
1772 error (0, 0, "cannot change branch to default for %s",
1773 finfo->fullname);
1774 return 1;
1775 }
1776 RCS_rewrite (finfo->rcs, NULL, NULL);
1777 }
1778
1779 /* check something out. Generally this is the head. If we have a
1780 particular rev, then name it. */
1781 retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL,
1782 NULL, NULL, RUN_TTY, NULL, NULL);
1783 if (retcode != 0)
1784 {
1785 error (0, 0,
1786 "failed to check out `%s'", finfo->fullname);
1787 return 1;
1788 }
1789
1790 /* Except when we are creating a branch, lock the revision so that
1791 we can check in the new revision. */
1792 if (lockflag)
1793 {
1794 if (RCS_lock (finfo->rcs, rev ? corev : NULL, 1) == 0)
1795 RCS_rewrite (finfo->rcs, NULL, NULL);
1796 }
1797
1798 if (corev != NULL)
1799 free (corev);
1800
1801 retcode = RCS_checkin (finfo->rcs, NULL, finfo->file, message,
1802 rev, 0, RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
1803 if (retcode != 0)
1804 {
1805 if (!quiet)
1806 error (0, retcode == -1 ? errno : 0,
1807 "failed to commit dead revision for `%s'", finfo->fullname);
1808 return 1;
1809 }
1810 /* At this point, the file has been committed as removed. We should
1811 probably tell the history file about it */
1812 history_write ('R', NULL, finfo->rcs->head, finfo->file, finfo->repository);
1813
1814 if (rev != NULL)
1815 free (rev);
1816
1817 old_path = xstrdup (finfo->rcs->path);
1818 if (!branch)
1819 RCS_setattic (finfo->rcs, 1);
1820
1821 /* Print message that file was removed. */
1822 if (!really_quiet)
1823 {
1824 cvs_output (old_path, 0);
1825 cvs_output (" <-- ", 0);
1826 if (finfo->update_dir && strlen (finfo->update_dir))
1827 {
1828 cvs_output (finfo->update_dir, 0);
1829 cvs_output ("/", 1);
1830 }
1831 cvs_output (finfo->file, 0);
1832 cvs_output ("\nnew revision: delete; previous revision: ", 0);
1833 cvs_output (prev_rev, 0);
1834 cvs_output ("\n", 0);
1835 }
1836
1837 free (prev_rev);
1838
1839 free (old_path);
1840
1841 Scratch_Entry (finfo->entries, finfo->file);
1842 return 0;
1843 }
1844
1845
1846
1847 /*
1848 * Do the actual checkin for added files
1849 */
1850 static int
finaladd(struct file_info * finfo,char * rev,char * tag,char * options)1851 finaladd (struct file_info *finfo, char *rev, char *tag, char *options)
1852 {
1853 int ret;
1854
1855 ret = Checkin ('A', finfo, rev, tag, options, saved_message);
1856 if (ret == 0)
1857 {
1858 char *tmp = Xasprintf ("%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
1859 if (unlink_file (tmp) < 0
1860 && !existence_error (errno))
1861 error (0, errno, "cannot remove %s", tmp);
1862 free (tmp);
1863 }
1864 else if (finfo->rcs != NULL)
1865 fixaddfile (finfo->rcs->path);
1866
1867 (void) time (&last_register_time);
1868
1869 return ret;
1870 }
1871
1872
1873
1874 /*
1875 * Unlock an rcs file
1876 */
1877 static void
unlockrcs(RCSNode * rcs)1878 unlockrcs (RCSNode *rcs)
1879 {
1880 int retcode;
1881
1882 if ((retcode = RCS_unlock (rcs, NULL, 1)) != 0)
1883 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1884 "could not unlock %s", rcs->path);
1885 else
1886 RCS_rewrite (rcs, NULL, NULL);
1887 }
1888
1889
1890
1891 /*
1892 * remove a partially added file. if we can parse it, leave it alone.
1893 *
1894 * FIXME: Every caller that calls this function can access finfo->rcs (the
1895 * parsed RCSNode data), so we should be able to detect that the file needs
1896 * to be removed without reparsing the file as we do below.
1897 */
1898 static void
fixaddfile(const char * rcs)1899 fixaddfile (const char *rcs)
1900 {
1901 RCSNode *rcsfile;
1902 int save_really_quiet;
1903
1904 save_really_quiet = really_quiet;
1905 really_quiet = 1;
1906 if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
1907 {
1908 if (unlink_file (rcs) < 0)
1909 error (0, errno, "cannot remove %s", rcs);
1910 }
1911 else
1912 freercsnode (&rcsfile);
1913 really_quiet = save_really_quiet;
1914 }
1915
1916
1917
1918 /*
1919 * put the branch back on an rcs file
1920 */
1921 static void
fixbranch(RCSNode * rcs,char * branch)1922 fixbranch (RCSNode *rcs, char *branch)
1923 {
1924 int retcode;
1925
1926 if (branch != NULL)
1927 {
1928 if ((retcode = RCS_setbranch (rcs, branch)) != 0)
1929 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
1930 "cannot restore branch to %s for %s", branch, rcs->path);
1931 RCS_rewrite (rcs, NULL, NULL);
1932 }
1933 }
1934
1935
1936
1937 /*
1938 * do the initial part of a file add for the named file. if adding
1939 * with a tag, put the file in the Attic and point the symbolic tag
1940 * at the committed revision.
1941 *
1942 * INPUTS
1943 * file The name of the file in the workspace.
1944 * repository The repository directory to expect to find FILE,v in.
1945 * tag The name or rev num of the branch being added to, if any.
1946 * options Any RCS keyword expansion options specified by the user.
1947 * rcsnode A pointer to the pre-parsed RCSNode for this file, if the file
1948 * exists in the repository. If this is NULL, assume the file
1949 * does not yet exist.
1950 *
1951 * RETURNS
1952 * 0 on success.
1953 * 1 on errors, after printing any appropriate error messages.
1954 *
1955 * ERRORS
1956 * This function will return an error when any of the following functions do:
1957 * add_rcs_file
1958 * RCS_setattic
1959 * lock_RCS
1960 * RCS_checkin
1961 * RCS_parse (called to verify the newly created archive file)
1962 * RCS_settag
1963 */
1964
1965 static int
checkaddfile(const char * file,const char * repository,const char * tag,const char * options,RCSNode ** rcsnode)1966 checkaddfile (const char *file, const char *repository, const char *tag,
1967 const char *options, RCSNode **rcsnode)
1968 {
1969 RCSNode *rcs;
1970 char *fname;
1971 int newfile = 0; /* Set to 1 if we created a new RCS archive. */
1972 int retval = 1;
1973 int adding_on_branch;
1974
1975 assert (rcsnode != NULL);
1976
1977 /* Callers expect to be able to use either "" or NULL to mean the
1978 default keyword expansion. */
1979 if (options != NULL && options[0] == '\0')
1980 options = NULL;
1981 if (options != NULL)
1982 assert (options[0] == '-' && options[1] == 'k');
1983
1984 /* If numeric, it is on the trunk; check_fileproc enforced
1985 this. */
1986 adding_on_branch = tag != NULL && !isdigit ((unsigned char) tag[0]);
1987
1988 if (*rcsnode == NULL)
1989 {
1990 char *rcsname;
1991 char *desc = NULL;
1992 size_t descalloc = 0;
1993 size_t desclen = 0;
1994 const char *opt;
1995
1996 if (adding_on_branch)
1997 {
1998 mode_t omask;
1999 rcsname = xmalloc (strlen (repository)
2000 + sizeof (CVSATTIC)
2001 + strlen (file)
2002 + sizeof (RCSEXT)
2003 + 3);
2004 (void) sprintf (rcsname, "%s/%s", repository, CVSATTIC);
2005 omask = umask (cvsumask);
2006 if (CVS_MKDIR (rcsname, 0777) != 0 && errno != EEXIST)
2007 error (1, errno, "cannot make directory `%s'", rcsname);
2008 (void) umask (omask);
2009 (void) sprintf (rcsname,
2010 "%s/%s/%s%s",
2011 repository,
2012 CVSATTIC,
2013 file,
2014 RCSEXT);
2015 }
2016 else
2017 rcsname = Xasprintf ("%s/%s%s", repository, file, RCSEXT);
2018
2019 /* this is the first time we have ever seen this file; create
2020 an RCS file. */
2021 fname = Xasprintf ("%s/%s%s", CVSADM, file, CVSEXT_LOG);
2022 /* If the file does not exist, no big deal. In particular, the
2023 server does not (yet at least) create CVSEXT_LOG files. */
2024 if (isfile (fname))
2025 /* FIXME: Should be including update_dir in the appropriate
2026 place here. */
2027 get_file (fname, fname, "r", &desc, &descalloc, &desclen);
2028 free (fname);
2029
2030 /* From reading the RCS 5.7 source, "rcs -i" adds a newline to the
2031 end of the log message if the message is nonempty.
2032 Do it. RCS also deletes certain whitespace, in cleanlogmsg,
2033 which we don't try to do here. */
2034 if (desclen > 0)
2035 {
2036 expand_string (&desc, &descalloc, desclen + 1);
2037 desc[desclen++] = '\012';
2038 }
2039
2040 /* Set RCS keyword expansion options. */
2041 if (options != NULL)
2042 opt = options + 2;
2043 else
2044 opt = NULL;
2045
2046 if (add_rcs_file (NULL, rcsname, file, NULL, opt,
2047 NULL, NULL, 0, NULL,
2048 desc, desclen, NULL, 0) != 0)
2049 {
2050 if (rcsname != NULL)
2051 free (rcsname);
2052 goto out;
2053 }
2054 rcs = RCS_parsercsfile (rcsname);
2055 newfile = 1;
2056 if (rcsname != NULL)
2057 free (rcsname);
2058 if (desc != NULL)
2059 free (desc);
2060 *rcsnode = rcs;
2061 }
2062 else
2063 {
2064 /* file has existed in the past. Prepare to resurrect. */
2065 char *rev;
2066 char *oldexpand;
2067
2068 rcs = *rcsnode;
2069
2070 oldexpand = RCS_getexpand (rcs);
2071 if ((oldexpand != NULL
2072 && options != NULL
2073 && strcmp (options + 2, oldexpand) != 0)
2074 || (oldexpand == NULL && options != NULL))
2075 {
2076 /* We tell the user about this, because it means that the
2077 old revisions will no longer retrieve the way that they
2078 used to. */
2079 error (0, 0, "changing keyword expansion mode to %s", options);
2080 RCS_setexpand (rcs, options + 2);
2081 }
2082
2083 if (!adding_on_branch)
2084 {
2085 /* We are adding on the trunk, so move the file out of the
2086 Attic. */
2087 if (!(rcs->flags & INATTIC))
2088 {
2089 error (0, 0, "warning: expected %s to be in Attic",
2090 rcs->path);
2091 }
2092
2093 /* Begin a critical section around the code that spans the
2094 first commit on the trunk of a file that's already been
2095 committed on a branch. */
2096 SIG_beginCrSect ();
2097
2098 if (RCS_setattic (rcs, 0))
2099 {
2100 goto out;
2101 }
2102 }
2103
2104 rev = RCS_getversion (rcs, tag, NULL, 1, NULL);
2105 /* and lock it */
2106 if (lock_RCS (file, rcs, rev, repository))
2107 {
2108 error (0, 0, "cannot lock revision %s in `%s'.",
2109 rev ? rev : tag ? tag : "HEAD", rcs->path);
2110 if (rev != NULL)
2111 free (rev);
2112 goto out;
2113 }
2114
2115 if (rev != NULL)
2116 free (rev);
2117 }
2118
2119 /* when adding a file for the first time, and using a tag, we need
2120 to create a dead revision on the trunk. */
2121 if (adding_on_branch)
2122 {
2123 if (newfile)
2124 {
2125 char *tmp;
2126 FILE *fp;
2127 int retcode;
2128
2129 /* move the new file out of the way. */
2130 fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
2131 rename_file (file, fname);
2132
2133 /* Create empty FILE. Can't use copy_file with a DEVNULL
2134 argument -- copy_file now ignores device files. */
2135 fp = fopen (file, "w");
2136 if (fp == NULL)
2137 error (1, errno, "cannot open %s for writing", file);
2138 if (fclose (fp) < 0)
2139 error (0, errno, "cannot close %s", file);
2140
2141 tmp = Xasprintf ("file %s was initially added on branch %s.",
2142 file, tag);
2143 /* commit a dead revision. */
2144 retcode = RCS_checkin (rcs, NULL, NULL, tmp, NULL, 0,
2145 RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
2146 free (tmp);
2147 if (retcode != 0)
2148 {
2149 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2150 "could not create initial dead revision %s", rcs->path);
2151 free (fname);
2152 goto out;
2153 }
2154
2155 /* put the new file back where it was */
2156 rename_file (fname, file);
2157 free (fname);
2158
2159 /* double-check that the file was written correctly */
2160 freercsnode (&rcs);
2161 rcs = RCS_parse (file, repository);
2162 if (rcs == NULL)
2163 {
2164 error (0, 0, "could not read %s", rcs->path);
2165 goto out;
2166 }
2167 *rcsnode = rcs;
2168
2169 /* and lock it once again. */
2170 if (lock_RCS (file, rcs, NULL, repository))
2171 {
2172 error (0, 0, "cannot lock initial revision in `%s'.",
2173 rcs->path);
2174 goto out;
2175 }
2176 }
2177
2178 /* when adding with a tag, we need to stub a branch, if it
2179 doesn't already exist. */
2180 if (!RCS_nodeisbranch (rcs, tag))
2181 {
2182 /* branch does not exist. Stub it. */
2183 char *head;
2184 char *magicrev;
2185 int retcode;
2186 time_t headtime = -1;
2187 char *revnum, *tmp;
2188 FILE *fp;
2189 time_t t = -1;
2190 struct tm *ct;
2191
2192 fixbranch (rcs, sbranch);
2193
2194 head = RCS_getversion (rcs, NULL, NULL, 0, NULL);
2195 if (!head)
2196 error (1, 0, "No head revision in archive file `%s'.",
2197 rcs->print_path);
2198 magicrev = RCS_magicrev (rcs, head);
2199
2200 /* If this is not a new branch, then we will want a dead
2201 version created before this one. */
2202 if (!newfile)
2203 headtime = RCS_getrevtime (rcs, head, 0, 0);
2204
2205 retcode = RCS_settag (rcs, tag, magicrev);
2206 RCS_rewrite (rcs, NULL, NULL);
2207
2208 free (head);
2209 free (magicrev);
2210
2211 if (retcode != 0)
2212 {
2213 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2214 "could not stub branch %s for %s", tag, rcs->path);
2215 goto out;
2216 }
2217 /* We need to add a dead version here to avoid -rtag -Dtime
2218 checkout problems between when the head version was
2219 created and now. */
2220 if (!newfile && headtime != -1)
2221 {
2222 /* move the new file out of the way. */
2223 fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file);
2224 rename_file (file, fname);
2225
2226 /* Create empty FILE. Can't use copy_file with a DEVNULL
2227 argument -- copy_file now ignores device files. */
2228 fp = fopen (file, "w");
2229 if (fp == NULL)
2230 error (1, errno, "cannot open %s for writing", file);
2231 if (fclose (fp) < 0)
2232 error (0, errno, "cannot close %s", file);
2233
2234 /* As we will be hacking the delta date, put the time
2235 this was added into the log message. */
2236 t = time (NULL);
2237 ct = gmtime (&t);
2238 tmp = Xasprintf ("file %s was added on branch %s on %ld-%02d-%02d %02d:%02d:%02d +0000",
2239 file, tag,
2240 (long)ct->tm_year
2241 + (ct->tm_year < 100 ? 0 : 1900),
2242 ct->tm_mon + 1, ct->tm_mday,
2243 ct->tm_hour, ct->tm_min, ct->tm_sec);
2244
2245 /* commit a dead revision. */
2246 revnum = RCS_whatbranch (rcs, tag);
2247 retcode = RCS_checkin (rcs, NULL, NULL, tmp, revnum, headtime,
2248 RCS_FLAGS_DEAD |
2249 RCS_FLAGS_QUIET |
2250 RCS_FLAGS_USETIME);
2251 free (revnum);
2252 free (tmp);
2253
2254 if (retcode != 0)
2255 {
2256 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
2257 "could not created dead stub %s for %s", tag,
2258 rcs->path);
2259 goto out;
2260 }
2261
2262 /* put the new file back where it was */
2263 rename_file (fname, file);
2264 free (fname);
2265
2266 /* double-check that the file was written correctly */
2267 freercsnode (&rcs);
2268 rcs = RCS_parse (file, repository);
2269 if (rcs == NULL)
2270 {
2271 error (0, 0, "could not read %s", rcs->path);
2272 goto out;
2273 }
2274 *rcsnode = rcs;
2275 }
2276 }
2277 else
2278 {
2279 /* lock the branch. (stubbed branches need not be locked.) */
2280 if (lock_RCS (file, rcs, NULL, repository))
2281 {
2282 error (0, 0, "cannot lock head revision in `%s'.", rcs->path);
2283 goto out;
2284 }
2285 }
2286
2287 if (*rcsnode != rcs)
2288 {
2289 freercsnode (rcsnode);
2290 *rcsnode = rcs;
2291 }
2292 }
2293
2294 fileattr_newfile (file);
2295
2296 /* At this point, we used to set the file mode of the RCS file
2297 based on the mode of the file in the working directory. If we
2298 are creating the RCS file for the first time, add_rcs_file does
2299 this already. If we are re-adding the file, then perhaps it is
2300 consistent to preserve the old file mode, just as we preserve
2301 the old keyword expansion mode.
2302
2303 If we decide that we should change the modes, then we can't do
2304 it here anyhow. At this point, the RCS file may be owned by
2305 somebody else, so a chmod will fail. We need to instead do the
2306 chmod after rewriting it.
2307
2308 FIXME: In general, I think the file mode (and the keyword
2309 expansion mode) should be associated with a particular revision
2310 of the file, so that it is possible to have different revisions
2311 of a file have different modes. */
2312
2313 retval = 0;
2314
2315 out:
2316 if (retval != 0 && SIG_inCrSect ())
2317 SIG_endCrSect ();
2318 return retval;
2319 }
2320
2321
2322
2323 /*
2324 * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
2325 * couldn't. If the RCS file currently has a branch as the head, we must
2326 * move the head back to the trunk before locking the file, and be sure to
2327 * put the branch back as the head if there are any errors.
2328 */
2329 static int
lock_RCS(const char * user,RCSNode * rcs,const char * rev,const char * repository)2330 lock_RCS (const char *user, RCSNode *rcs, const char *rev,
2331 const char *repository)
2332 {
2333 char *branch = NULL;
2334 int err = 0;
2335
2336 /*
2337 * For a specified, numeric revision of the form "1" or "1.1", (or when
2338 * no revision is specified ""), definitely move the branch to the trunk
2339 * before locking the RCS file.
2340 *
2341 * The assumption is that if there is more than one revision on the trunk,
2342 * the head points to the trunk, not a branch... and as such, it's not
2343 * necessary to move the head in this case.
2344 */
2345 if (rev == NULL
2346 || (rev && isdigit ((unsigned char) *rev) && numdots (rev) < 2))
2347 {
2348 branch = xstrdup (rcs->branch);
2349 if (branch != NULL)
2350 {
2351 if (RCS_setbranch (rcs, NULL) != 0)
2352 {
2353 error (0, 0, "cannot change branch to default for %s",
2354 rcs->path);
2355 if (branch)
2356 free (branch);
2357 return 1;
2358 }
2359 }
2360 err = RCS_lock (rcs, NULL, 1);
2361 }
2362 else
2363 {
2364 RCS_lock (rcs, rev, 1);
2365 }
2366
2367 /* We used to call RCS_rewrite here, and that might seem
2368 appropriate in order to write out the locked revision
2369 information. However, such a call would actually serve no
2370 purpose. CVS locks will prevent any interference from other
2371 CVS processes. The comment above rcs_internal_lockfile
2372 explains that it is already unsafe to use RCS and CVS
2373 simultaneously. It follows that writing out the locked
2374 revision information here would add no additional security.
2375
2376 If we ever do care about it, the proper fix is to create the
2377 RCS lock file before calling this function, and maintain it
2378 until the checkin is complete.
2379
2380 The call to RCS_lock is still required at present, since in
2381 some cases RCS_checkin will determine which revision to check
2382 in by looking for a lock. FIXME: This is rather roundabout,
2383 and a more straightforward approach would probably be easier to
2384 understand. */
2385
2386 if (err == 0)
2387 {
2388 if (sbranch != NULL)
2389 free (sbranch);
2390 sbranch = branch;
2391 return 0;
2392 }
2393
2394 /* try to restore the branch if we can on error */
2395 if (branch != NULL)
2396 fixbranch (rcs, branch);
2397
2398 if (branch)
2399 free (branch);
2400 return 1;
2401 }
2402
2403
2404
2405 /*
2406 * free an UPDATE node's data
2407 */
2408 void
update_delproc(Node * p)2409 update_delproc (Node *p)
2410 {
2411 struct logfile_info *li = p->data;
2412
2413 if (li->tag)
2414 free (li->tag);
2415 if (li->rev_old)
2416 free (li->rev_old);
2417 if (li->rev_new)
2418 free (li->rev_new);
2419 free (li);
2420 }
2421
2422 /*
2423 * Free the commit_info structure in p.
2424 */
2425 static void
ci_delproc(Node * p)2426 ci_delproc (Node *p)
2427 {
2428 struct commit_info *ci = p->data;
2429
2430 if (ci->rev)
2431 free (ci->rev);
2432 if (ci->tag)
2433 free (ci->tag);
2434 if (ci->options)
2435 free (ci->options);
2436 free (ci);
2437 }
2438
2439 /*
2440 * Free the commit_info structure in p.
2441 */
2442 static void
masterlist_delproc(Node * p)2443 masterlist_delproc (Node *p)
2444 {
2445 struct master_lists *ml = p->data;
2446
2447 dellist (&ml->ulist);
2448 dellist (&ml->cilist);
2449 free (ml);
2450 }
2451