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