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