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