Bug Summary

File:src/gnu/usr.bin/cvs/src/add.c
Warning:line 646, column 29
The left operand of '!=' is a garbage value

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 add.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/add.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 * Add
9 *
10 * Adds a file or directory to the RCS source repository. For a file,
11 * the entry is marked as "needing to be added" in the user's own CVS
12 * directory, and really added to the repository when it is committed.
13 * For a directory, it is added at the appropriate place in the source
14 * repository and a CVS directory is generated within the directory.
15 *
16 * The -m option is currently the only supported option. Some may wish to
17 * supply standard "rcs" options here, but I've found that this causes more
18 * trouble than anything else.
19 *
20 * The user files or directories must already exist. For a directory, it must
21 * not already have a CVS file in it.
22 *
23 * An "add" on a file that has been "remove"d but not committed will cause the
24 * file to be resurrected.
25 */
26
27#include "cvs.h"
28#include "savecwd.h"
29#include "fileattr.h"
30
31static int add_directory PROTO ((struct file_info *finfo))(struct file_info *finfo);
32static int build_entry PROTO((char *repository, char *user, char *options,(char *repository, char *user, char *options, char *message, List
* entries, char *tag)
33 char *message, List * entries, char *tag))(char *repository, char *user, char *options, char *message, List
* entries, char *tag)
;
34
35static const char *const add_usage[] =
36{
37 "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
38 "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
39 "\t-m\tUse \"message\" for the creation log.\n",
40 "(Specify the --help global option for a list of other help options)\n",
41 NULL((void*)0)
42};
43
44int
45add (argc, argv)
46 int argc;
47 char **argv;
48{
49 char *message = NULL((void*)0);
50 int i;
51 char *repository;
52 int c;
53 int err = 0;
54 int added_files = 0;
55 char *options = NULL((void*)0);
56 List *entries;
57 Vers_TS *vers;
58 struct saved_cwd cwd;
59 /* Nonzero if we found a slash, and are thus adding files in a
60 subdirectory. */
61 int found_slash = 0;
62 size_t cvsroot_len;
63
64 if (argc == 1 || argc == -1)
1
Assuming 'argc' is not equal to 1
2
Assuming the condition is false
3
Taking false branch
65 usage (add_usage);
66
67 wrap_setup ();
68
69 /* parse args */
70 optind = 0;
71 while ((c = getopt (argc, argv, "+k:m:")) != -1)
4
Assuming the condition is false
5
Loop condition is false. Execution continues on line 90
72 {
73 switch (c)
74 {
75 case 'k':
76 if (options)
77 free (options);
78 options = RCS_check_kflag (optarg);
79 break;
80
81 case 'm':
82 message = xstrdup (optarg);
83 break;
84 case '?':
85 default:
86 usage (add_usage);
87 break;
88 }
89 }
90 argc -= optind;
91 argv += optind;
92
93 if (argc <= 0)
6
Assuming 'argc' is > 0
7
Taking false branch
94 usage (add_usage);
95
96 cvsroot_len = strlen (current_parsed_root->directory);
97
98 /* First some sanity checks. I know that the CVS case is (sort of)
99 also handled by add_directory, but we need to check here so the
100 client won't get all confused in send_file_names. */
101 for (i = 0; i
7.1
'i' is < 'argc'
< argc
; i++)
8
Loop condition is true. Entering loop body
16
Assuming 'i' is >= 'argc'
17
Loop condition is false. Execution continues on line 151
102 {
103 int skip_file = 0;
104
105 /* If it were up to me I'd probably make this a fatal error.
106 But some people are really fond of their "cvs add *", and
107 don't seem to object to the warnings.
108 Whatever. */
109 strip_trailing_slashes (argv[i]);
110 if (strcmp (argv[i], ".") == 0
9
Assuming the condition is false
12
Taking false branch
111 || strcmp (argv[i], "..") == 0
10
Assuming the condition is false
112 || fncmpstrcmp (argv[i], CVSADM"CVS") == 0)
11
Assuming the condition is false
113 {
114 if (!quiet)
115 error (0, 0, "cannot add special file `%s'; skipping", argv[i]);
116 skip_file = 1;
117 }
118 else
119 {
120 char *p;
121 p = argv[i];
122 while (*p != '\0')
13
Assuming the condition is false
14
Loop condition is false. Execution continues on line 133
123 {
124 if (ISDIRSEP (*p)((*p) == '/'))
125 {
126 found_slash = 1;
127 break;
128 }
129 ++p;
130 }
131 }
132
133 if (skip_file
14.1
'skip_file' is 0
)
15
Taking false branch
134 {
135 int j;
136
137 /* FIXME: We don't do anything about free'ing argv[i]. But
138 the problem is that it is only sometimes allocated (see
139 cvsrc.c). */
140
141 for (j = i; j < argc - 1; ++j)
142 argv[j] = argv[j + 1];
143 --argc;
144 /* Check the new argv[i] again. */
145 --i;
146 ++err;
147 }
148 }
149
150#ifdef CLIENT_SUPPORT1
151 if (current_parsed_root->isremote)
18
Assuming field 'isremote' is 0
19
Taking false branch
152 {
153 int i;
154
155 if (argc == 0)
156 /* We snipped out all the arguments in the above sanity
157 check. We can just forget the whole thing (and we
158 better, because if we fired up the server and passed it
159 nothing, it would spit back a usage message). */
160 return err;
161
162 start_server ();
163 ign_setup ();
164 if (options)
165 {
166 send_arg (options);
167 free (options);
168 }
169 option_with_arg ("-m", message);
170
171 /* If !found_slash, refrain from sending "Directory", for
172 CVS 1.9 compatibility. If we only tried to deal with servers
173 which are at least CVS 1.9.26 or so, we wouldn't have to
174 special-case this. */
175 if (found_slash)
176 {
177 repository = Name_Repository (NULL((void*)0), NULL((void*)0));
178 send_a_repository ("", repository, "");
179 free (repository);
180 }
181
182 for (i = 0; i < argc; ++i)
183 {
184 /* FIXME: Does this erroneously call Create_Admin in error
185 conditions which are only detected once the server gets its
186 hands on things? */
187 /* FIXME-also: if filenames are case-insensitive on the
188 client, and the directory in the repository already
189 exists and is named "foo", and the command is "cvs add
190 FOO", this call to Create_Admin puts the wrong thing in
191 CVS/Repository and so a subsequent "cvs update" will
192 give an error. The fix will be to have the server report
193 back what it actually did (e.g. use tagged text for the
194 "Directory %s added" message), and then Create_Admin,
195 which should also fix the error handling concerns. */
196
197 if (isdir (argv[i]))
198 {
199 char *tag;
200 char *date;
201 int nonbranch;
202 char *rcsdir;
203 char *p;
204 char *update_dir;
205 /* This is some mungeable storage into which we can point
206 with p and/or update_dir. */
207 char *filedir;
208
209 if (save_cwd (&cwd))
210 error_exit ();
211
212 filedir = xstrdup (argv[i]);
213 p = last_component (filedir);
214 if (p == filedir)
215 {
216 update_dir = "";
217 }
218 else
219 {
220 p[-1] = '\0';
221 update_dir = filedir;
222 if (CVS_CHDIRchdir (update_dir) < 0)
223 error (1, errno(*__errno()),
224 "could not chdir to %s", update_dir);
225 }
226
227 /* find the repository associated with our current dir */
228 repository = Name_Repository (NULL((void*)0), update_dir);
229
230 /* don't add stuff to Emptydir */
231 if (strncmp (repository, current_parsed_root->directory, cvsroot_len) == 0
232 && ISDIRSEP (repository[cvsroot_len])((repository[cvsroot_len]) == '/')
233 && strncmp (repository + cvsroot_len + 1,
234 CVSROOTADM"CVSROOT",
235 sizeof CVSROOTADM"CVSROOT" - 1) == 0
236 && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM])((repository[cvsroot_len + sizeof "CVSROOT"]) == '/')
237 && strcmp (repository + cvsroot_len + sizeof CVSROOTADM"CVSROOT" + 1,
238 CVSNULLREPOS"Emptydir") == 0)
239 error (1, 0, "cannot add to %s", repository);
240
241 /* before we do anything else, see if we have any
242 per-directory tags */
243 ParseTag (&tag, &date, &nonbranch);
244
245 rcsdir = xmalloc (strlen (repository) + strlen (p) + 5);
246 sprintf (rcsdir, "%s/%s", repository, p);
247
248 Create_Admin (p, argv[i], rcsdir, tag, date,
249 nonbranch, 0, 1);
250
251 if (found_slash)
252 send_a_repository ("", repository, update_dir);
253
254 if (restore_cwd (&cwd, NULL((void*)0)))
255 error_exit ();
256 free_cwd (&cwd);
257
258 if (tag)
259 free (tag);
260 if (date)
261 free (date);
262 free (rcsdir);
263
264 if (p == filedir)
265 Subdir_Register ((List *) NULL((void*)0), (char *) NULL((void*)0), argv[i]);
266 else
267 {
268 Subdir_Register ((List *) NULL((void*)0), update_dir, p);
269 }
270 free (repository);
271 free (filedir);
272 }
273 }
274 send_files (argc, argv, 0, 0, SEND_BUILD_DIRS1 | SEND_NO_CONTENTS4);
275 send_file_names (argc, argv, SEND_EXPAND_WILD1);
276 send_to_server ("add\012", 0);
277 if (message)
278 free (message);
279 return err + get_responses_and_close ();
280 }
281#endif
282
283 /* walk the arg list adding files/dirs */
284 for (i = 0; i < argc; i++)
20
Loop condition is true. Entering loop body
285 {
286 int begin_err = err;
287#ifdef SERVER_SUPPORT1
288 int begin_added_files = added_files;
289#endif
290 struct file_info finfo;
291 char *p;
292#if defined (SERVER_SUPPORT1) && !defined (FILENAMES_CASE_INSENSITIVE)
293 char *found_name;
21
'found_name' declared without an initial value
294#endif
295
296 memset (&finfo, 0, sizeof finfo);
297
298 if (save_cwd (&cwd))
22
Assuming the condition is false
23
Taking false branch
299 error_exit ();
300
301 finfo.fullname = xstrdup (argv[i]);
302 p = last_component (argv[i]);
303 if (p == argv[i])
24
Assuming the condition is true
25
Taking true branch
304 {
305 finfo.update_dir = "";
306 finfo.file = p;
307 }
308 else
309 {
310 p[-1] = '\0';
311 finfo.update_dir = argv[i];
312 finfo.file = p;
313 if (CVS_CHDIRchdir (finfo.update_dir) < 0)
314 error (1, errno(*__errno()), "could not chdir to %s", finfo.update_dir);
315 }
316
317 /* Add wrappers for this directory. They exist only until
318 the next call to wrap_add_file. */
319 wrap_add_file (CVSDOTWRAPPER".cvswrappers", 1);
320
321 finfo.rcs = NULL((void*)0);
322
323 /* Find the repository associated with our current dir. */
324 repository = Name_Repository (NULL((void*)0), finfo.update_dir);
325
326 /* don't add stuff to Emptydir */
327 if (strncmp (repository, current_parsed_root->directory, cvsroot_len) == 0
26
Assuming the condition is false
328 && ISDIRSEP (repository[cvsroot_len])((repository[cvsroot_len]) == '/')
329 && strncmp (repository + cvsroot_len + 1,
330 CVSROOTADM"CVSROOT",
331 sizeof CVSROOTADM"CVSROOT" - 1) == 0
332 && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM])((repository[cvsroot_len + sizeof "CVSROOT"]) == '/')
333 && strcmp (repository + cvsroot_len + sizeof CVSROOTADM"CVSROOT" + 1,
334 CVSNULLREPOS"Emptydir") == 0)
335 error (1, 0, "cannot add to %s", repository);
336
337 entries = Entries_Open (0, NULL((void*)0));
338
339 finfo.repository = repository;
340 finfo.entries = entries;
341
342#if defined (SERVER_SUPPORT1) && !defined (FILENAMES_CASE_INSENSITIVE)
343 if (ign_case)
27
Assuming 'ign_case' is 0
28
Taking false branch
344 {
345 /* Need to check whether there is a directory with the
346 same name but different case. We'll check for files
347 with the same name later (when Version_TS calls
348 RCS_parse which calls fopen_case). If CVS some day
349 records directories in the RCS files, then we should be
350 able to skip the separate check here, which would be
351 cleaner. */
352 DIR *dirp;
353 struct dirent *dp;
354
355 dirp = CVS_OPENDIRopendir (finfo.repository);
356 if (dirp == NULL((void*)0))
357 error (1, errno(*__errno()), "cannot read directory %s", finfo.repository);
358 found_name = NULL((void*)0);
359 errno(*__errno()) = 0;
360 while ((dp = CVS_READDIRreaddir (dirp)) != NULL((void*)0))
361 {
362 if (cvs_casecmp (dp->d_name, finfo.file) == 0)
363 {
364 if (found_name != NULL((void*)0))
365 error (1, 0, "%s is ambiguous; could mean %s or %s",
366 finfo.file, dp->d_name, found_name);
367 found_name = xstrdup (dp->d_name);
368 }
369 }
370 if (errno(*__errno()) != 0)
371 error (1, errno(*__errno()), "cannot read directory %s", finfo.repository);
372 CVS_CLOSEDIRclosedir (dirp);
373
374 if (found_name != NULL((void*)0))
375 {
376 /* OK, we are about to patch up the name, so patch up
377 the temporary directory too to match. The isdir
378 should "always" be true (since files have ,v), but
379 I guess we might as well make some attempt to not
380 get confused by stray files in the repository. */
381 if (isdir (finfo.file))
382 {
383 if (CVS_MKDIRmkdir (found_name, 0777) < 0
384 && errno(*__errno()) != EEXIST17)
385 error (0, errno(*__errno()), "cannot create %s", finfo.file);
386 }
387
388 /* OK, we found a directory with the same name, maybe in
389 a different case. Treat it as if the name were the
390 same. */
391 finfo.file = found_name;
392 }
393 }
394#endif
395
396 /* We pass force_tag_match as 1. If the directory has a
397 sticky branch tag, and there is already an RCS file which
398 does not have that tag, then the head revision is
399 meaningless to us. */
400 vers = Version_TS (&finfo, options, NULL((void*)0), NULL((void*)0), 1, 0);
401 if (vers->vn_user == NULL((void*)0))
29
Assuming field 'vn_user' is not equal to NULL
30
Taking false branch
402 {
403 /* No entry available, ts_rcs is invalid */
404 if (vers->vn_rcs == NULL((void*)0))
405 {
406 /* There is no RCS file either */
407 if (vers->ts_user == NULL((void*)0))
408 {
409 /* There is no user file either */
410 error (0, 0, "nothing known about %s", finfo.fullname);
411 err++;
412 }
413 else if (!isdir (finfo.file)
414 || wrap_name_has (finfo.file, WRAP_TOCVS))
415 {
416 /*
417 * See if a directory exists in the repository with
418 * the same name. If so, blow this request off.
419 */
420 char *dname = xmalloc (strlen (repository)
421 + strlen (finfo.file)
422 + 10);
423 (void) sprintf (dname, "%s/%s", repository, finfo.file);
424 if (isdir (dname))
425 {
426 error (0, 0,
427 "cannot add file `%s' since the directory",
428 finfo.fullname);
429 error (0, 0, "`%s' already exists in the repository",
430 dname);
431 error (1, 0, "illegal filename overlap");
432 }
433 free (dname);
434
435 if (vers->options == NULL((void*)0) || *vers->options == '\0')
436 {
437 /* No options specified on command line (or in
438 rcs file if it existed, e.g. the file exists
439 on another branch). Check for a value from
440 the wrapper stuff. */
441 if (wrap_name_has (finfo.file, WRAP_RCSOPTION))
442 {
443 if (vers->options)
444 free (vers->options);
445 vers->options = wrap_rcsoption (finfo.file, 1);
446 }
447 }
448
449 if (vers->nonbranch)
450 {
451 error (0, 0,
452 "cannot add file on non-branch tag %s",
453 vers->tag);
454 ++err;
455 }
456 else
457 {
458 /* There is a user file, so build the entry for it */
459 if (build_entry (repository, finfo.file, vers->options,
460 message, entries, vers->tag) != 0)
461 err++;
462 else
463 {
464 added_files++;
465 if (!quiet)
466 {
467 if (vers->tag)
468 error (0, 0, "\
469scheduling %s `%s' for addition on branch `%s'",
470 (wrap_name_has (finfo.file,
471 WRAP_TOCVS)
472 ? "wrapper"
473 : "file"),
474 finfo.fullname, vers->tag);
475 else
476 error (0, 0,
477 "scheduling %s `%s' for addition",
478 (wrap_name_has (finfo.file,
479 WRAP_TOCVS)
480 ? "wrapper"
481 : "file"),
482 finfo.fullname);
483 }
484 }
485 }
486 }
487 }
488 else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
489 {
490 if (isdir (finfo.file)
491 && !wrap_name_has (finfo.file, WRAP_TOCVS))
492 {
493 error (0, 0, "\
494the directory `%s' cannot be added because a file of the", finfo.fullname);
495 error (1, 0, "\
496same name already exists in the repository.");
497 }
498 else
499 {
500 if (vers->nonbranch)
501 {
502 error (0, 0,
503 "cannot add file on non-branch tag %s",
504 vers->tag);
505 ++err;
506 }
507 else
508 {
509 if (!quiet)
510 {
511 if (vers->tag)
512 error (0, 0, "\
513file `%s' will be added on branch `%s' from version %s",
514 finfo.fullname, vers->tag, vers->vn_rcs);
515 else
516 /* I'm not sure that mentioning
517 vers->vn_rcs makes any sense here; I
518 can't think of a way to word the
519 message which is not confusing. */
520 error (0, 0, "\
521re-adding file %s (in place of dead revision %s)",
522 finfo.fullname, vers->vn_rcs);
523 }
524 Register (entries, finfo.file, "0", vers->ts_user,
525 vers->options,
526 vers->tag, NULL((void*)0), NULL((void*)0));
527 ++added_files;
528 }
529 }
530 }
531 else
532 {
533 /*
534 * There is an RCS file already, so somebody else must've
535 * added it
536 */
537 error (0, 0, "%s added independently by second party",
538 finfo.fullname);
539 err++;
540 }
541 }
542 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
31
Assuming the condition is false
543 {
544
545 /*
546 * An entry for a new-born file, ts_rcs is dummy, but that is
547 * inappropriate here
548 */
549 if (!quiet)
550 error (0, 0, "%s has already been entered", finfo.fullname);
551 err++;
552 }
553 else if (vers->vn_user[0] == '-')
32
Assuming the condition is false
33
Taking false branch
554 {
555 /* An entry for a removed file, ts_rcs is invalid */
556 if (vers->ts_user == NULL((void*)0))
557 {
558 /* There is no user file (as it should be) */
559 if (vers->vn_rcs == NULL((void*)0))
560 {
561
562 /*
563 * There is no RCS file, so somebody else must've removed
564 * it from under us
565 */
566 error (0, 0, "\
567cannot resurrect %s; RCS file removed by second party", finfo.fullname);
568 err++;
569 }
570 else
571 {
572
573 /*
574 * There is an RCS file, so remove the "-" from the
575 * version number and restore the file
576 */
577 char *tmp = xmalloc (strlen (finfo.file) + 50);
578
579 (void) strcpy (tmp, vers->vn_user + 1);
580 (void) strcpy (vers->vn_user, tmp);
581 (void) sprintf (tmp, "Resurrected %s", finfo.file);
582 Register (entries, finfo.file, vers->vn_user, tmp,
583 vers->options,
584 vers->tag, vers->date, vers->ts_conflict);
585 free (tmp);
586
587 /* XXX - bugs here; this really resurrect the head */
588 /* Note that this depends on the Register above actually
589 having written Entries, or else it won't really
590 check the file out. */
591 if (update (2, argv + i - 1) == 0)
592 {
593 error (0, 0, "%s, version %s, resurrected",
594 finfo.fullname,
595 vers->vn_user);
596 }
597 else
598 {
599 error (0, 0, "could not resurrect %s", finfo.fullname);
600 err++;
601 }
602 }
603 }
604 else
605 {
606 /* The user file shouldn't be there */
607 error (0, 0, "\
608%s should be removed and is still there (or is back again)", finfo.fullname);
609 err++;
610 }
611 }
612 else
613 {
614 /* A normal entry, ts_rcs is valid, so it must already be there */
615 if (!quiet)
34
Assuming 'quiet' is not equal to 0
35
Taking false branch
616 error (0, 0, "%s already exists, with version number %s",
617 finfo.fullname,
618 vers->vn_user);
619 err++;
620 }
621 freevers_ts (&vers);
622
623 /* passed all the checks. Go ahead and add it if its a directory */
624 if (begin_err
35.1
'begin_err' is not equal to 'err'
== err
625 && isdir (finfo.file)
626 && !wrap_name_has (finfo.file, WRAP_TOCVS))
627 {
628 err += add_directory (&finfo);
629 }
630 else
631 {
632#ifdef SERVER_SUPPORT1
633 if (server_active && begin_added_files != added_files)
36
Assuming 'server_active' is 0
634 server_checked_in (finfo.file, finfo.update_dir, repository);
635#endif
636 }
637 free (repository);
638 Entries_Close (entries);
639
640 if (restore_cwd (&cwd, NULL((void*)0)))
37
Assuming the condition is false
38
Taking false branch
641 error_exit ();
642 free_cwd (&cwd);
643
644 free (finfo.fullname);
645#if defined (SERVER_SUPPORT1) && !defined (FILENAMES_CASE_INSENSITIVE)
646 if (ign_case && found_name != NULL((void*)0))
39
Assuming 'ign_case' is not equal to 0
40
The left operand of '!=' is a garbage value
647 free (found_name);
648#endif
649 }
650 if (added_files && !really_quiet)
651 error (0, 0, "use '%s commit' to add %s permanently",
652 program_name,
653 (added_files == 1) ? "this file" : "these files");
654
655 if (message)
656 free (message);
657 if (options)
658 free (options);
659
660 return (err);
661}
662
663/*
664 * The specified user file is really a directory. So, let's make sure that
665 * it is created in the RCS source repository, and that the user's directory
666 * is updated to include a CVS directory.
667 *
668 * Returns 1 on failure, 0 on success.
669 */
670static int
671add_directory (finfo)
672 struct file_info *finfo;
673{
674 char *repository = finfo->repository;
675 List *entries = finfo->entries;
676 char *dir = finfo->file;
677
678 char *rcsdir = NULL((void*)0);
679 struct saved_cwd cwd;
680 char *message = NULL((void*)0);
681 char *tag, *date;
682 int nonbranch;
683 char *attrs;
684
685 if (strchr (dir, '/') != NULL((void*)0))
686 {
687 /* "Can't happen". */
688 error (0, 0,
689 "directory %s not added; must be a direct sub-directory", dir);
690 return (1);
691 }
692 if (fncmpstrcmp (dir, CVSADM"CVS") == 0)
693 {
694 error (0, 0, "cannot add a `%s' directory", CVSADM"CVS");
695 return (1);
696 }
697
698 /* before we do anything else, see if we have any per-directory tags */
699 ParseTag (&tag, &date, &nonbranch);
700
701 /* Remember the default attributes from this directory, so we can apply
702 them to the new directory. */
703 fileattr_startdir (repository);
704 attrs = fileattr_getall (NULL((void*)0));
705 fileattr_free ();
706
707 /* now, remember where we were, so we can get back */
708 if (save_cwd (&cwd))
709 return (1);
710 if ( CVS_CHDIRchdir (dir) < 0)
711 {
712 error (0, errno(*__errno()), "cannot chdir to %s", finfo->fullname);
713 return (1);
714 }
715#ifdef SERVER_SUPPORT1
716 if (!server_active && isfile (CVSADM"CVS"))
717#else
718 if (isfile (CVSADM"CVS"))
719#endif
720 {
721 error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM"CVS");
722 goto out;
723 }
724
725 rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5);
726 sprintf (rcsdir, "%s/%s", repository, dir);
727 if (isfile (rcsdir) && !isdir (rcsdir))
728 {
729 error (0, 0, "%s is not a directory; %s not added", rcsdir,
730 finfo->fullname);
731 goto out;
732 }
733
734 /* setup the log message */
735 message = xmalloc (strlen (rcsdir)
736 + 80
737 + (tag == NULL((void*)0) ? 0 : strlen (tag) + 80)
738 + (date == NULL((void*)0) ? 0 : strlen (date) + 80));
739 (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
740 if (tag)
741 {
742 (void) strcat (message, "--> Using per-directory sticky tag `");
743 (void) strcat (message, tag);
744 (void) strcat (message, "'\n");
745 }
746 if (date)
747 {
748 (void) strcat (message, "--> Using per-directory sticky date `");
749 (void) strcat (message, date);
750 (void) strcat (message, "'\n");
751 }
752
753 if (!isdir (rcsdir))
754 {
755 mode_t omask;
756 Node *p;
757 List *ulist;
758 struct logfile_info *li;
759
760 /* There used to be some code here which would prompt for
761 whether to add the directory. The details of that code had
762 bitrotted, but more to the point it can't work
763 client/server, doesn't ask in the right way for GUIs, etc.
764 A better way of making it harder to accidentally add
765 directories would be to have to add and commit directories
766 like for files. The code was #if 0'd at least since CVS 1.5. */
767
768 if (!noexec)
769 {
770 omask = umask (cvsumask);
771 if (CVS_MKDIRmkdir (rcsdir, 0777) < 0)
772 {
773 error (0, errno(*__errno()), "cannot mkdir %s", rcsdir);
774 (void) umask (omask);
775 goto out;
776 }
777 (void) umask (omask);
778 }
779
780 /* Now set the default file attributes to the ones we inherited
781 from the parent directory. */
782 fileattr_startdir (rcsdir);
783 fileattr_setall (NULL((void*)0), attrs);
784 fileattr_write ();
785 fileattr_free ();
786 if (attrs != NULL((void*)0))
787 free (attrs);
788
789 /*
790 * Set up an update list with a single title node for Update_Logfile
791 */
792 ulist = getlist ();
793 p = getnode ();
794 p->type = UPDATE;
795 p->delproc = update_delproc;
796 p->key = xstrdup ("- New directory");
797 li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
798 li->type = T_TITLE;
799 li->tag = xstrdup (tag);
800 li->rev_old = li->rev_new = NULL((void*)0);
801 p->data = (char *) li;
802 (void) addnode (ulist, p);
803 Update_Logfile (rcsdir, message, (FILE *) NULL((void*)0), ulist);
804 dellist (&ulist);
805 }
806
807#ifdef SERVER_SUPPORT1
808 if (!server_active)
809#endif
810 Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1);
811 if (tag)
812 free (tag);
813 if (date)
814 free (date);
815
816 if (restore_cwd (&cwd, NULL((void*)0)))
817 error_exit ();
818 free_cwd (&cwd);
819
820 Subdir_Register (entries, (char *) NULL((void*)0), dir);
821
822 cvs_output (message, 0);
823
824 free (rcsdir);
825 free (message);
826
827 return (0);
828
829out:
830 if (restore_cwd (&cwd, NULL((void*)0)))
831 error_exit ();
832 free_cwd (&cwd);
833 if (rcsdir != NULL((void*)0))
834 free (rcsdir);
835 return (0);
836}
837
838/*
839 * Builds an entry for a new file and sets up "CVS/file",[pt] by
840 * interrogating the user. Returns non-zero on error.
841 */
842static int
843build_entry (repository, user, options, message, entries, tag)
844 char *repository;
845 char *user;
846 char *options;
847 char *message;
848 List *entries;
849 char *tag;
850{
851 char *fname;
852 char *line;
853 FILE *fp;
854
855 if (noexec)
856 return (0);
857
858 /*
859 * The requested log is read directly from the user and stored in the
860 * file user,t. If the "message" argument is set, use it as the
861 * initial creation log (which typically describes the file).
862 */
863 fname = xmalloc (strlen (user) + 80);
864 (void) sprintf (fname, "%s/%s%s", CVSADM"CVS", user, CVSEXT_LOG",t");
865 fp = open_file (fname, "w+");
866 if (message && fputs (message, fp) == EOF(-1))
867 error (1, errno(*__errno()), "cannot write to %s", fname);
868 if (fclose(fp) == EOF(-1))
869 error(1, errno(*__errno()), "cannot close %s", fname);
870 free (fname);
871
872 /*
873 * Create the entry now, since this allows the user to interrupt us above
874 * without needing to clean anything up (well, we could clean up the
875 * ,t file, but who cares).
876 */
877 line = xmalloc (strlen (user) + 20);
878 (void) sprintf (line, "Initial %s", user);
879 Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
880 free (line);
881 return (0);
882}