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