Bug Summary

File:src/gnu/usr.bin/cvs/src/commit.c
Warning:line 1639, column 5
Value stored to 'retcode' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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