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