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