update from MirBSD; for us relevant:
[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 __RCSID("$MirOS: src/gnu/usr.bin/cvs/src/add.c,v 1.5 2016/10/22 00:54:33 tg Exp $");
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
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     optind = 0;
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                         /* There is a user file, so build the entry for it */
407                         if (build_entry (repository, finfo.file, vers->options,
408                                          message, entries, vers->tag) != 0)
409                             err++;
410                         else
411                         {
412                             added_files++;
413                             if (!quiet)
414                             {
415                                 if (vers->tag)
416                                     error (0, 0, "scheduling %s `%s' for"
417                                            " addition on branch `%s'",
418                                            (wrap_name_has (finfo.file,
419                                                            WRAP_TOCVS)
420                                             ? "wrapper"
421                                             : "file"),
422                                            finfo.fullname, vers->tag);
423                                 else
424                                     error (0, 0,
425                                            "scheduling %s `%s' for addition",
426                                            (wrap_name_has (finfo.file,
427                                                            WRAP_TOCVS)
428                                             ? "wrapper"
429                                             : "file"),
430                                            finfo.fullname);
431                             }
432                         }
433                     }
434                 }
435             }
436             else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
437             {
438                 if (isdir (finfo.file)
439                     && !wrap_name_has (finfo.file, WRAP_TOCVS))
440                 {
441                     error (0, 0,
442                            "the directory `%s' cannot be added because a file"
443                            " of the", finfo.fullname);
444                     error (1, 0, "same name already exists in the repository.");
445                 }
446                 else
447                 {
448                     if (vers->nonbranch)
449                     {
450                         error (0, 0,
451                                "cannot add file on non-branch tag `%s'",
452                                vers->tag);
453                         ++err;
454                     }
455                     else
456                     {
457                         char *timestamp = NULL;
458                         if (vers->ts_user == NULL)
459                         {
460                             /* If this file does not exist locally, assume that
461                              * the last version on the branch is being
462                              * resurrected.
463                              *
464                              * Compute previous revision.  We assume that it
465                              * exists and that it is not a revision on the
466                              * trunk of the form X.1 (1.1, 2.1, 3.1, ...).  We
467                              * also assume that it is not dead, which seems
468                              * fair since we know vers->vn_rcs is dead
469                              * and we shouldn't see two dead revisions in a
470                              * row.
471                              */
472                             char *prev = previous_rev (vers->srcfile,
473                                                        vers->vn_rcs);
474                             int status;
475                             if (prev == NULL)
476                             {
477                                 /* There is no previous revision.  Either:
478                                  *
479                                  *  * Revision 1.1 was dead, as when a file was
480                                  *    inititially added on a branch, 
481                                  *
482                                  * or
483                                  *
484                                  *  * All previous revisions have been deleted.
485                                  *    For instance, via `admin -o'.
486                                  */
487                                 if (!really_quiet)
488                                     error (0, 0,
489 "File `%s' has no previous revision to resurrect.",
490                                            finfo.fullname);
491                                 free (prev);
492                                 goto skip_this_file;
493                             }
494                             if (!quiet)
495                                 error (0, 0,
496 "Resurrecting file `%s' from revision %s.",
497                                        finfo.fullname, prev);
498                             status = RCS_checkout (vers->srcfile, finfo.file,
499                                                    prev, vers->tag,
500                                                    vers->options, RUN_TTY,
501                                                    NULL, NULL);
502                             xchmod (finfo.file, 1);
503                             if (status != 0)
504                             {
505                                 error (0, 0, "Failed to resurrect revision %s",
506                                        prev);
507                                 err++;
508                             }
509                             else
510                             {
511                                 /* I don't actually set vers->ts_user here
512                                  * because it would confuse server_update().
513                                  */
514                                 timestamp = time_stamp (finfo.file);
515                                 if (!really_quiet)
516                                     write_letter (&finfo, 'U');
517                             }
518                             free (prev);
519                         }
520                         if (!quiet)
521                         {
522                             char *bbuf;
523                             if (vers->tag)
524                             {
525                                 bbuf = Xasprintf (" on branch `%s'",
526                                                   vers->tag);
527                             }
528                             else
529                                 bbuf = "";
530                             error (0, 0,
531 "Re-adding file `%s'%s after dead revision %s.",
532                                    finfo.fullname, bbuf, vers->vn_rcs);
533                             if (vers->tag)
534                                 free (bbuf);
535                         }
536                         Register (entries, finfo.file, "0",
537                                   timestamp ? timestamp :
538                                    vers->ts_user_ists ? "locally added" : vers->ts_user,
539                                   vers->options, vers->tag, vers->date, NULL);
540                         if (timestamp) free (timestamp);
541 #ifdef SERVER_SUPPORT
542                         if (server_active && vers->ts_user == NULL)
543                         {
544                             /* If we resurrected the file from the archive, we
545                              * need to tell the client about it.
546                              */
547                             server_updated (&finfo, vers,
548                                             SERVER_UPDATED,
549                                             (mode_t) -1, NULL, NULL);
550                             /* This is kinda hacky or, at least, it renders the
551                              * name "begin_added_files" obsolete, but we want
552                              * the added_files to be counted without triggering
553                              * the check that causes server_checked_in() to be
554                              * called below since we have already called
555                              * server_updated() to complete the resurrection.
556                              */
557                             ++begin_added_files;
558                         }
559 #endif
560                         ++added_files;
561                     }
562                 }
563             }
564             else
565             {
566                 /*
567                  * There is an RCS file already, so somebody else must've
568                  * added it
569                  */
570                 error (0, 0, "`%s' added independently by second party",
571                        finfo.fullname);
572                 err++;
573             }
574         }
575         else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
576         {
577
578             /*
579              * An entry for a new-born file, ts_rcs is dummy, but that is
580              * inappropriate here
581              */
582             if (!quiet)
583                 error (0, 0, "`%s' has already been entered", finfo.fullname);
584             err++;
585         }
586         else if (vers->vn_user[0] == '-')
587         {
588             /* An entry for a removed file, ts_rcs is invalid */
589             if (vers->ts_user == NULL)
590             {
591                 /* There is no user file (as it should be) */
592                 if (vers->vn_rcs == NULL)
593                 {
594
595                     /*
596                      * There is no RCS file, so somebody else must've removed
597                      * it from under us
598                      */
599                     error (0, 0,
600                            "cannot resurrect `%s'; RCS file removed by"
601                            " second party", finfo.fullname);
602                     err++;
603                 }
604                 else
605                 {
606                     int status;
607                     /*
608                      * There is an RCS file, so remove the "-" from the
609                      * version number and restore the file
610                      */
611                     char *tmp = xstrdup (vers->vn_user + 1);
612                     (void) strcpy (vers->vn_user, tmp);
613                     free (tmp);
614                     status = RCS_checkout (vers->srcfile, finfo.file,
615                                            vers->vn_user, vers->tag,
616                                            vers->options, RUN_TTY,
617                                            NULL, NULL);
618                     xchmod (finfo.file, cvswrite);
619                     if (status != 0)
620                     {
621                         error (0, 0, "Failed to resurrect revision %s.",
622                                vers->vn_user);
623                         err++;
624                         tmp = NULL;
625                     }
626                     else
627                     {
628                         /* I don't actually set vers->ts_user here because it
629                          * would confuse server_update().
630                          */
631                         tmp = time_stamp (finfo.file);
632                         write_letter (&finfo, 'U');
633                         if (!quiet)
634                              error (0, 0, "`%s', version %s, resurrected",
635                                     finfo.fullname, vers->vn_user);
636                     }
637                     Register (entries, finfo.file, vers->vn_user,
638                               tmp, vers->options,
639                               vers->tag, vers->date, NULL);
640                     if (tmp) free (tmp);
641 #ifdef SERVER_SUPPORT
642                     if (server_active)
643                     {
644                         /* If we resurrected the file from the archive, we
645                          * need to tell the client about it.
646                          */
647                         server_updated (&finfo, vers,
648                                         SERVER_UPDATED,
649                                         (mode_t) -1, NULL, NULL);
650                     }
651                    /* We don't increment added_files here because this isn't
652                     * a change that needs to be committed.
653                     */
654 #endif
655                 }
656             }
657             else
658             {
659                 /* The user file shouldn't be there */
660                 error (0, 0, "\
661 `%s' should be removed and is still there (or is back again)", finfo.fullname);
662                 err++;
663             }
664         }
665         else
666         {
667             /* A normal entry, ts_rcs is valid, so it must already be there */
668             if (!quiet)
669                 error (0, 0, "`%s' already exists, with version number %s",
670                         finfo.fullname,
671                         vers->vn_user);
672             err++;
673         }
674         freevers_ts (&vers);
675
676         /* passed all the checks.  Go ahead and add it if its a directory */
677         if (begin_err == err
678             && isdir (finfo.file)
679             && !wrap_name_has (finfo.file, WRAP_TOCVS))
680         {
681             err += add_directory (&finfo);
682         }
683         else
684         {
685 #ifdef SERVER_SUPPORT
686             if (server_active && begin_added_files != added_files)
687                 server_checked_in (finfo.file, finfo.update_dir, repository);
688 #endif
689         }
690
691 skip_this_file:
692         free (repository);
693         Entries_Close (entries);
694
695         if (restore_cwd (&cwd))
696             error (1, errno, "Failed to restore current directory, `%s'.",
697                    cwd.name);
698         free_cwd (&cwd);
699
700         /* It's okay to discard the const to free this - we allocated this
701          * above.  The const is for everybody else.
702          */
703         free ((char *) finfo.fullname);
704         free (filename);
705     }
706     if (added_files && !really_quiet)
707         error (0, 0, "use `%s commit' to add %s permanently",
708                program_name,
709                (added_files == 1) ? "this file" : "these files");
710
711     if (message)
712         free (message);
713     if (options)
714         free (options);
715
716     return err;
717 }
718
719
720
721 /*
722  * The specified user file is really a directory.  So, let's make sure that
723  * it is created in the RCS source repository, and that the user's directory
724  * is updated to include a CVS directory.
725  * 
726  * Returns 1 on failure, 0 on success.
727  */
728 static int
729 add_directory (struct file_info *finfo)
730 {
731     const char *repository = finfo->repository;
732     List *entries = finfo->entries;
733     const char *dir = finfo->file;
734
735     char *rcsdir = NULL;
736     struct saved_cwd cwd;
737     char *message = NULL;
738     char *tag, *date;
739     int nonbranch;
740     char *attrs;
741
742     if (strchr (dir, '/') != NULL)
743     {
744         /* "Can't happen".  */
745         error (0, 0,
746                "directory %s not added; must be a direct sub-directory", dir);
747         return 1;
748     }
749     if (fncmp (dir, CVSADM) == 0)
750     {
751         error (0, 0, "cannot add a `%s' directory", CVSADM);
752         return 1;
753     }
754
755     /* before we do anything else, see if we have any per-directory tags */
756     ParseTag (&tag, &date, &nonbranch);
757
758     /* Remember the default attributes from this directory, so we can apply
759        them to the new directory.  */
760     fileattr_startdir (repository);
761     attrs = fileattr_getall (NULL);
762     fileattr_free ();
763
764     /* now, remember where we were, so we can get back */
765     if (save_cwd (&cwd))
766     {
767         error (0, errno, "Failed to save current directory.");
768         return 1;
769     }
770     if (CVS_CHDIR (dir) < 0)
771     {
772         error (0, errno, "cannot chdir to %s", finfo->fullname);
773         return 1;
774     }
775     if (!server_active && isfile (CVSADM))
776     {
777         error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM);
778         goto out;
779     }
780
781     rcsdir = Xasprintf ("%s/%s", repository, dir);
782     if (isfile (rcsdir) && !isdir (rcsdir))
783     {
784         error (0, 0, "%s is not a directory; %s not added", rcsdir,
785                finfo->fullname);
786         goto out;
787     }
788
789     /* setup the log message */
790     message = Xasprintf ("Directory %s put under version control\n%s%s%s%s%s%s",
791                          rcsdir,
792                          tag ? "--> Using per-directory sticky tag `" : "",
793                          tag ? tag : "", tag ? "'\n" : "",
794                          date ? "--> Using per-directory sticky date `" : "",
795                          date ? date : "", date ? "'\n" : "");
796
797     if (!isdir (rcsdir))
798     {
799         mode_t omask;
800         Node *p;
801         List *ulist;
802         struct logfile_info *li;
803
804         /* There used to be some code here which would prompt for
805            whether to add the directory.  The details of that code had
806            bitrotted, but more to the point it can't work
807            client/server, doesn't ask in the right way for GUIs, etc.
808            A better way of making it harder to accidentally add
809            directories would be to have to add and commit directories
810            like for files.  The code was #if 0'd at least since CVS 1.5.  */
811
812         if (!noexec)
813         {
814             omask = umask (cvsumask);
815             if (CVS_MKDIR (rcsdir, 0777) < 0)
816             {
817                 error (0, errno, "cannot mkdir %s", rcsdir);
818                 (void) umask (omask);
819                 goto out;
820             }
821             (void) umask (omask);
822         }
823
824         /* Now set the default file attributes to the ones we inherited
825            from the parent directory.  */
826         fileattr_startdir (rcsdir);
827         fileattr_setall (NULL, attrs);
828         fileattr_write ();
829         fileattr_free ();
830         if (attrs != NULL)
831             free (attrs);
832
833         /*
834          * Set up an update list with a single title node for Update_Logfile
835          */
836         ulist = getlist ();
837         p = getnode ();
838         p->type = UPDATE;
839         p->delproc = update_delproc;
840         p->key = xstrdup ("- New directory");
841         li = xmalloc (sizeof (struct logfile_info));
842         li->type = T_TITLE;
843         li->tag = xstrdup (tag);
844         li->rev_old = li->rev_new = NULL;
845         p->data = li;
846         (void) addnode (ulist, p);
847         Update_Logfile (rcsdir, message, NULL, ulist);
848         dellist (&ulist);
849     }
850
851     if (server_active)
852         WriteTemplate (finfo->fullname, 1, rcsdir);
853     else
854         Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1);
855
856     if (tag)
857         free (tag);
858     if (date)
859         free (date);
860
861     if (restore_cwd (&cwd))
862         error (1, errno, "Failed to restore current directory, `%s'.",
863                cwd.name);
864     free_cwd (&cwd);
865
866     Subdir_Register (entries, NULL, dir);
867
868     if (!really_quiet)
869         cvs_output (message, 0);
870
871     free (rcsdir);
872     free (message);
873
874     return 0;
875
876 out:
877     if (restore_cwd (&cwd))
878         error (1, errno, "Failed to restore current directory, `%s'.",
879                cwd.name);
880     free_cwd (&cwd);
881     if (message) free (message);
882     if (rcsdir != NULL)
883         free (rcsdir);
884     return 0;
885 }
886
887
888
889 /*
890  * Builds an entry for a new file and sets up "CVS/file",[pt] by
891  * interrogating the user.  Returns non-zero on error.
892  */
893 static int
894 build_entry (const char *repository, const char *user, const char *options,
895              const char *message, List *entries, const char *tag)
896 {
897     char *fname;
898     char *line;
899     FILE *fp;
900
901     if (noexec)
902         return 0;
903
904     /*
905      * The requested log is read directly from the user and stored in the
906      * file user,t.  If the "message" argument is set, use it as the
907      * initial creation log (which typically describes the file).
908      */
909     fname = Xasprintf ("%s/%s%s", CVSADM, user, CVSEXT_LOG);
910     fp = xfopen (fname, "w+");
911     if (message && fputs (message, fp) == EOF)
912             error (1, errno, "cannot write to %s", fname);
913     if (fclose (fp) == EOF)
914         error (1, errno, "cannot close %s", fname);
915     free (fname);
916
917     /*
918      * Create the entry now, since this allows the user to interrupt us above
919      * without needing to clean anything up (well, we could clean up the
920      * ,t file, but who cares).
921      */
922     line = Xasprintf ("Initial %s", user);
923     Register (entries, user, "0", line, options, tag, NULL, NULL);
924     free (line);
925     return 0;
926 }