xref: /dragonfly/contrib/cvs-1.12/src/checkout.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1 /*
2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3  *
4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5  *                                  and others.
6  *
7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8  * Portions Copyright (C) 1989-1992, Brian Berliner
9  *
10  * You may distribute under the terms of the GNU General Public License as
11  * specified in the README file that comes with the CVS source distribution.
12  *
13  * Create Version
14  *
15  * "checkout" creates a "version" of an RCS repository.  This version is owned
16  * totally by the user and is actually an independent copy, to be dealt with
17  * as seen fit.  Once "checkout" has been called in a given directory, it
18  * never needs to be called again.  The user can keep up-to-date by calling
19  * "update" when he feels like it; this will supply him with a merge of his
20  * own modifications and the changes made in the RCS original.  See "update"
21  * for details.
22  *
23  * "checkout" can be given a list of directories or files to be updated and in
24  * the case of a directory, will recursivley create any sub-directories that
25  * exist in the repository.
26  *
27  * When the user is satisfied with his own modifications, the present version
28  * can be committed by "commit"; this keeps the present version in tact,
29  * usually.
30  *
31  * The call is cvs checkout [options] <module-name>...
32  *
33  * "checkout" creates a directory ./CVS, in which it keeps its administration,
34  * in two files, Repository and Entries. The first contains the name of the
35  * repository.  The second contains one line for each registered file,
36  * consisting of the version number it derives from, its time stamp at
37  * derivation time and its name.  Both files are normal files and can be
38  * edited by the user, if necessary (when the repository is moved, e.g.)
39  */
40 
41 #include "cvs.h"
42 
43 static char *findslash (char *start, char *p);
44 static int checkout_proc (int argc, char **argv, char *where,
45                               char *mwhere, char *mfile, int shorten,
46                               int local_specified, char *omodule,
47                               char *msg);
48 
49 static const char *const checkout_usage[] =
50 {
51     "Usage:\n  %s %s [-ANPRcflnps] [-r rev] [-D date] [-d dir]\n",
52     "    [-j rev1] [-j rev2] [-k kopt] modules...\n",
53     "\t-A\tReset any sticky tags/date/kopts.\n",
54     "\t-N\tDon't shorten module paths if -d specified.\n",
55     "\t-P\tPrune empty directories.\n",
56     "\t-R\tProcess directories recursively.\n",
57     "\t-c\t\"cat\" the module database.\n",
58     "\t-f\tForce a head revision match if tag/date not found.\n",
59     "\t-l\tLocal directory only, not recursive\n",
60     "\t-n\tDo not run module program (if any).\n",
61     "\t-p\tCheck out files to standard output (avoids stickiness).\n",
62     "\t-s\tLike -c, but include module status.\n",
63     "\t-r rev\tCheck out revision or tag. (implies -P) (is sticky)\n",
64     "\t-D date\tCheck out revisions as of date. (implies -P) (is sticky)\n",
65     "\t-d dir\tCheck out into dir instead of module name.\n",
66     "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n",
67     "\t-j rev\tMerge in changes made between current revision and rev.\n",
68     "(Specify the --help global option for a list of other help options)\n",
69     NULL
70 };
71 
72 static const char *const export_usage[] =
73 {
74     "Usage: %s %s [-NRfln] [-r tag] [-D date] [-d dir] [-k kopt] module...\n",
75     "\t-N\tDon't shorten module paths if -d specified.\n",
76     "\t-f\tForce a head revision match if tag/date not found.\n",
77     "\t-l\tLocal directory only, not recursive\n",
78     "\t-R\tProcess directories recursively (default).\n",
79     "\t-n\tDo not run module program (if any).\n",
80     "\t-r tag\tExport tagged revisions.\n",
81     "\t-D date\tExport revisions as of date.\n",
82     "\t-d dir\tExport into dir instead of module name.\n",
83     "\t-k kopt\tUse RCS kopt -k option on checkout.\n",
84     "(Specify the --help global option for a list of other help options)\n",
85     NULL
86 };
87 
88 static int checkout_prune_dirs;
89 static int force_tag_match;
90 static int pipeout;
91 static int aflag;
92 static char *options;
93 static char *tag;
94 static bool tag_validated;
95 static char *date;
96 static char *join_rev1, *join_date1;
97 static char *join_rev2, *join_date2;
98 static bool join_tags_validated;
99 static char *preload_update_dir;
100 static char *history_name;
101 static enum mtype m_type;
102 
103 int
checkout(int argc,char ** argv)104 checkout (int argc, char **argv)
105 {
106     int i;
107     int c;
108     DBM *db;
109     int cat = 0, err = 0, status = 0;
110     int run_module_prog = 1;
111     int local = 0;
112     int shorten = -1;
113     char *where = NULL;
114     const char *valid_options;
115     const char *const *valid_usage;
116     char *join_orig1, *join_orig2;
117 
118     /* initialize static options */
119     force_tag_match = 1;
120     if (options)
121     {
122           free (options);
123           options = NULL;
124     }
125     tag = date = join_rev1 = join_date1 = join_rev2 = join_date2 =
126             join_orig1 = join_orig2 = preload_update_dir = NULL;
127     history_name = NULL;
128     tag_validated = join_tags_validated = false;
129 
130 
131     /*
132      * A smaller subset of options are allowed for the export command, which
133      * is essentially like checkout, except that it hard-codes certain
134      * options to be default (like -kv) and takes care to remove the CVS
135      * directory when it has done its duty
136      */
137     if (strcmp (cvs_cmd_name, "export") == 0)
138     {
139         m_type = EXPORT;
140           valid_options = "+Nnk:d:flRQqr:D:";
141           valid_usage = export_usage;
142     }
143     else
144     {
145         m_type = CHECKOUT;
146           valid_options = "+ANnk:d:flRpQqcsr:D:j:P";
147           valid_usage = checkout_usage;
148     }
149 
150     if (argc == -1)
151           usage (valid_usage);
152 
153     ign_setup ();
154     wrap_setup ();
155 
156     optind = 0;
157     while ((c = getopt (argc, argv, valid_options)) != -1)
158     {
159           switch (c)
160           {
161               case 'A':
162                     aflag = 1;
163                     break;
164               case 'N':
165                     shorten = 0;
166                     break;
167               case 'k':
168                     if (options)
169                         free (options);
170                     options = RCS_check_kflag (optarg);
171                     break;
172               case 'n':
173                     run_module_prog = 0;
174                     break;
175               case 'Q':
176               case 'q':
177                     /* The CVS 1.5 client sends these options (in addition to
178                        Global_option requests), so we must ignore them.  */
179                     if (!server_active)
180                         error (1, 0,
181                                  "-q or -Q must be specified before \"%s\"",
182                                  cvs_cmd_name);
183                     break;
184               case 'l':
185                     local = 1;
186                     break;
187               case 'R':
188                     local = 0;
189                     break;
190               case 'P':
191                     checkout_prune_dirs = 1;
192                     break;
193               case 'p':
194                     pipeout = 1;
195                     run_module_prog = 0;          /* don't run module prog when piping */
196                     noexec = 1;                   /* so no locks will be created */
197                     break;
198               case 'c':
199                     cat = 1;
200                     break;
201               case 'd':
202                     where = optarg;
203                     if (shorten == -1)
204                         shorten = 1;
205                     break;
206               case 's':
207                     cat = status = 1;
208                     break;
209               case 'f':
210                     force_tag_match = 0;
211                     break;
212               case 'r':
213                     parse_tagdate (&tag, &date, optarg);
214                     checkout_prune_dirs = 1;
215                     break;
216               case 'D':
217                     if (date) free (date);
218                     date = Make_Date (optarg);
219                     checkout_prune_dirs = 1;
220                     break;
221               case 'j':
222                     if (join_rev2 || join_date2)
223                         error (1, 0, "only two -j options can be specified");
224                     if (join_rev1 || join_date1)
225                     {
226                         if (join_orig2) free (join_orig2);
227                         join_orig2 = xstrdup (optarg);
228                         parse_tagdate (&join_rev2, &join_date2, optarg);
229                     }
230                     else
231                     {
232                         if (join_orig1) free (join_orig1);
233                         join_orig1 = xstrdup (optarg);
234                         parse_tagdate (&join_rev1, &join_date1, optarg);
235                     }
236                     break;
237               case '?':
238               default:
239                     usage (valid_usage);
240                     break;
241           }
242     }
243     argc -= optind;
244     argv += optind;
245 
246     if (shorten == -1)
247           shorten = 0;
248 
249     if (cat && argc != 0)
250           error (1, 0, "-c and -s must not get any arguments");
251 
252     if (!cat && argc == 0)
253           error (1, 0, "must specify at least one module or directory");
254 
255     if (where && pipeout)
256           error (1, 0, "-d and -p are mutually exclusive");
257 
258     if (m_type == EXPORT)
259     {
260           if (!tag && !date)
261               error (1, 0, "must specify a tag or date");
262 
263           if (tag && isdigit (tag[0]))
264               error (1, 0, "tag `%s' must be a symbolic tag", tag);
265     }
266 
267 #ifdef SERVER_SUPPORT
268     if (server_active && where != NULL)
269     {
270           server_pathname_check (where);
271     }
272 #endif
273 
274     if (!cat && !pipeout && !safe_location (where))
275     {
276         error (1, 0, "Cannot check out files into the repository itself");
277     }
278 
279 #ifdef CLIENT_SUPPORT
280     if (current_parsed_root->isremote)
281     {
282           int expand_modules;
283 
284           start_server ();
285 
286           ign_setup ();
287 
288           expand_modules = (!cat && !pipeout
289                                 && supported_request ("expand-modules"));
290 
291           if (expand_modules)
292           {
293               /* This is done here because we need to read responses
294                from the server before we send the command checkout or
295                export files. */
296 
297               client_expand_modules (argc, argv, local);
298           }
299 
300           if (!run_module_prog)
301               send_arg ("-n");
302           if (local)
303               send_arg ("-l");
304           if (pipeout)
305               send_arg ("-p");
306           if (!force_tag_match)
307               send_arg ("-f");
308           if (aflag)
309               send_arg ("-A");
310           if (!shorten)
311               send_arg ("-N");
312           if (checkout_prune_dirs && m_type == CHECKOUT)
313               send_arg ("-P");
314           client_prune_dirs = checkout_prune_dirs;
315           if (cat && !status)
316               send_arg ("-c");
317           if (where != NULL)
318               option_with_arg ("-d", where);
319           if (status)
320               send_arg ("-s");
321           if (options != NULL && options[0] != '\0')
322               send_arg (options);
323           option_with_arg ("-r", tag);
324           if (date)
325               client_senddate (date);
326           if (join_orig1)
327               option_with_arg ("-j", join_orig1);
328           if (join_orig2)
329               option_with_arg ("-j", join_orig2);
330           send_arg ("--");
331 
332           if (expand_modules)
333           {
334               client_send_expansions (local, where, 1);
335           }
336           else
337           {
338               int i;
339               for (i = 0; i < argc; ++i)
340                     send_arg (argv[i]);
341               client_nonexpanded_setup ();
342           }
343 
344           send_to_server (m_type == EXPORT ? "export\012" : "co\012", 0);
345           return get_responses_and_close ();
346     }
347 #endif /* CLIENT_SUPPORT */
348 
349     if (cat)
350     {
351           cat_module (status);
352           if (options)
353           {
354               free (options);
355               options = NULL;
356           }
357           return 0;
358     }
359     db = open_module ();
360 
361 
362     /* If we've specified something like "cvs co foo/bar baz/quux"
363        don't try to shorten names.  There are a few cases in which we
364        could shorten (e.g. "cvs co foo/bar foo/baz"), but we don't
365        handle those yet.  Better to have an extra directory created
366        than the thing checked out under the wrong directory name. */
367 
368     if (argc > 1)
369           shorten = 0;
370 
371 
372     /* If we will be calling history_write, work out the name to pass
373        it.  */
374     if (!pipeout)
375     {
376           if (!date)
377               history_name = tag;
378           else if (!tag)
379               history_name = date;
380           else
381               history_name = Xasprintf ("%s:%s", tag, date);
382     }
383 
384 
385     for (i = 0; i < argc; i++)
386           err += do_module (db, argv[i], m_type, "Updating", checkout_proc,
387                                 where, shorten, local, run_module_prog, !pipeout,
388                                 NULL);
389     close_module (db);
390     if (options)
391     {
392           free (options);
393           options = NULL;
394     }
395     if (history_name != tag && history_name != date && history_name != NULL)
396           free (history_name);
397     return err;
398 }
399 
400 
401 
402 /* FIXME: This is and emptydir_name are in checkout.c for historical
403    reasons, probably want to move them.  */
404 
405 /* int
406  * safe_location ( char *where )
407  *
408  * Return true if where is a safe destination for a checkout.
409  *
410  * INPUTS
411  *  where The requested destination directory.
412  *
413  * GLOBALS
414  *  current_parsed_root->directory
415  *  current_parsed_root->isremote
416  *                  Used to locate our CVSROOT.
417  *
418  * RETURNS
419  *  true  If we are running in client mode or if where is not located
420  *                  within the CVSROOT.
421  *  false Otherwise.
422  *
423  * ERRORS
424  *  Exits with a fatal error message when various events occur, such as not
425  *  being able to resolve a path or failing ot chdir to a path.
426  */
427 int
safe_location(char * where)428 safe_location (char *where)
429 {
430     char *current;
431     char *hardpath;
432     size_t hardpath_len;
433     int retval;
434 
435     TRACE (TRACE_FUNCTION, "safe_location( where=%s )",
436            where ? where : "(null)");
437 
438     /* Don't compare remote CVSROOTs to our destination directory. */
439     if (current_parsed_root->isremote) return 1;
440 
441     /* set current - even if where is set we'll need to cd back... */
442     current = xgetcwd ();
443     if (current == NULL)
444           error (1, errno, "could not get working directory");
445 
446     hardpath = xcanonicalize_file_name (current_parsed_root->directory);
447 
448     /* if where is set, set current to as much of where as exists,
449      * or fail.
450      */
451     if (where != NULL)
452     {
453           char *where_this_pass = xstrdup (where);
454           while (1)
455           {
456               if (CVS_CHDIR (where_this_pass) != -1)
457               {
458                     /* where */
459                     free (where_this_pass);
460                     where_this_pass = xgetcwd ();
461                     if (where_this_pass == NULL)
462                         error (1, errno, "could not get working directory");
463 
464                     if (CVS_CHDIR (current) == -1)
465                         error (1, errno,
466                                  "could not restore directory to `%s'", current);
467 
468                     free (current);
469                     current = where_this_pass;
470                     break;
471               }
472               else if (errno == ENOENT)
473               {
474                     /* where_this_pass - last_component (where_this_pass) */
475                     char *parent;
476 
477                     /* It's okay to cast out the const below since we know we
478                      * allocated where_this_pass and have control of it.
479                      */
480                     if ((parent = (char *)last_component (where_this_pass))
481                             != where_this_pass)
482                     {
483                         /* strip the last_component */
484                         parent[-1] = '\0';
485                         /* continue */
486                     }
487                     else
488                     {
489                         /* ERRNO == ENOENT
490                          *   && last_component (where_this_pass) == where_this_pass
491                          * means we've tried all the parent diretories and not one
492                          * exists, so there is no need to test any portion of where
493                          * - it is all being created.
494                          */
495                         free (where_this_pass);
496                         break;
497                     }
498               }
499               else
500                     /* we don't know how to handle other errors, so fail */
501                     error (1, errno, "\
502 could not change directory to requested checkout directory `%s'",
503                            where_this_pass);
504           } /* while (1) */
505     } /* where != NULL */
506 
507     hardpath_len = strlen (hardpath);
508     if (strlen (current) >= hardpath_len
509           && strncmp (current, hardpath, hardpath_len) == 0)
510     {
511           if (/* Current is a subdirectory of hardpath.  */
512               current[hardpath_len] == '/'
513 
514               /* Current is hardpath itself.  */
515               || current[hardpath_len] == '\0')
516               retval = 0;
517           else
518               /* It isn't a problem.  For example, current is
519                  "/foo/cvsroot-bar" and hardpath is "/foo/cvsroot".  */
520               retval = 1;
521     }
522     else
523           retval = 1;
524     free (current);
525     free (hardpath);
526     return retval;
527 }
528 
529 
530 
531 struct dir_to_build
532 {
533     /* What to put in CVS/Repository.  */
534     char *repository;
535     /* The path to the directory.  */
536     char *dirpath;
537 
538     struct dir_to_build *next;
539 };
540 
541 
542 
543 static int build_dirs_and_chdir (struct dir_to_build *list,
544                                                   int sticky);
545 
546 static void
build_one_dir(char * repository,char * dirpath,int sticky)547 build_one_dir (char *repository, char *dirpath, int sticky)
548 {
549     FILE *fp;
550 
551     if (isfile (CVSADM))
552     {
553           if (m_type == EXPORT)
554               error (1, 0, "cannot export into a working directory");
555     }
556     else if (m_type == CHECKOUT)
557     {
558           /* I suspect that this check could be omitted.  */
559           if (!isdir (repository))
560               error (1, 0, "there is no repository %s", repository);
561 
562           if (Create_Admin (".", dirpath, repository,
563                                 sticky ? tag : NULL,
564                                 sticky ? date : NULL,
565 
566                                 /* FIXME?  This is a guess.  If it is important
567                                    for nonbranch to be set correctly here I
568                                    think we need to write it one way now and
569                                    then rewrite it later via WriteTag, once
570                                    we've had a chance to call RCS_nodeisbranch
571                                    on each file.  */
572                                 0, 1, 1))
573               return;
574 
575           if (!noexec)
576           {
577               fp = xfopen (CVSADM_ENTSTAT, "w+");
578               if (fclose (fp) == EOF)
579                     error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
580 #ifdef SERVER_SUPPORT
581               if (server_active)
582                     server_set_entstat (dirpath, repository);
583 #endif
584           }
585     }
586 }
587 
588 
589 
590 /*
591  * process_module calls us back here so we do the actual checkout stuff
592  */
593 /* ARGSUSED */
594 static int
checkout_proc(int argc,char ** argv,char * where_orig,char * mwhere,char * mfile,int shorten,int local_specified,char * omodule,char * msg)595 checkout_proc (int argc, char **argv, char *where_orig, char *mwhere,
596                  char *mfile, int shorten, int local_specified, char *omodule,
597                  char *msg)
598 {
599     char *myargv[2];
600     int err = 0;
601     int which;
602     char *cp;
603     char *repository;
604     char *oldupdate = NULL;
605     char *where;
606 
607     TRACE (TRACE_FUNCTION, "checkout_proc (%s, %s, %s, %d, %d, %s, %s)\n",
608              where_orig ? where_orig : "(null)",
609              mwhere ? mwhere : "(null)",
610              mfile ? mfile : "(null)",
611              shorten, local_specified,
612              omodule ? omodule : "(null)",
613              msg ? msg : "(null)"
614             );
615 
616     /*
617      * OK, so we're doing the checkout! Our args are as follows:
618      *  argc,argv contain either dir or dir followed by a list of files
619      *  where contains where to put it (if supplied by checkout)
620      *  mwhere contains the module name or -d from module file
621      *  mfile says do only that part of the module
622      *  shorten = 1 says shorten as much as possible
623      *  omodule is the original arg to do_module()
624      */
625 
626     /* Set up the repository (maybe) for the bottom directory.
627        Allocate more space than we need so we don't need to keep
628        reallocating this string. */
629     repository = xmalloc (strlen (current_parsed_root->directory)
630                                 + strlen (argv[0])
631                                 + (mfile == NULL ? 0 : strlen (mfile))
632                                 + 10);
633     (void) sprintf (repository, "%s/%s",
634                     current_parsed_root->directory, argv[0]);
635     Sanitize_Repository_Name (repository);
636 
637 
638     /* save the original value of preload_update_dir */
639     if (preload_update_dir != NULL)
640           oldupdate = xstrdup (preload_update_dir);
641 
642 
643     /* Allocate space and set up the where variable.  We allocate more
644        space than necessary here so that we don't have to keep
645        reallocaing it later on. */
646 
647     where = xmalloc (strlen (argv[0])
648                          + (mfile == NULL ? 0 : strlen (mfile))
649                          + (mwhere == NULL ? 0 : strlen (mwhere))
650                          + (where_orig == NULL ? 0 : strlen (where_orig))
651                          + 10);
652 
653     /* Yes, this could be written in a less verbose way, but in this
654        form it is quite easy to read.
655 
656        FIXME?  The following code that sets should probably be moved
657        to do_module in modules.c, since there is similar code in
658        patch.c and rtag.c. */
659 
660     if (shorten)
661     {
662           if (where_orig != NULL)
663           {
664               /* If the user has specified a directory with `-d' on the
665                  command line, use it preferentially, even over the `-d'
666                  flag in the modules file. */
667 
668               (void) strcpy (where, where_orig);
669           }
670           else if (mwhere != NULL)
671           {
672               /* Second preference is the value of mwhere, which is from
673                  the `-d' flag in the modules file. */
674 
675               (void) strcpy (where, mwhere);
676           }
677           else
678           {
679               /* Third preference is the directory specified in argv[0]
680                  which is this module'e directory in the repository. */
681 
682               (void) strcpy (where, argv[0]);
683           }
684     }
685     else
686     {
687           /* Use the same preferences here, bug don't shorten -- that
688            is, tack on where_orig if it exists. */
689 
690           *where = '\0';
691 
692           if (where_orig != NULL)
693           {
694               (void) strcat (where, where_orig);
695               (void) strcat (where, "/");
696           }
697 
698           /* If the -d flag in the modules file specified an absolute
699            directory, let the user override it with the command-line
700            -d option. */
701 
702           if (mwhere && !ISABSOLUTE (mwhere))
703               (void) strcat (where, mwhere);
704           else
705               (void) strcat (where, argv[0]);
706     }
707     strip_trailing_slashes (where); /* necessary? */
708 
709 
710     /* At this point, the user may have asked for a single file or
711        directory from within a module.  In that case, we should modify
712        where, repository, and argv as appropriate. */
713 
714     if (mfile != NULL)
715     {
716           /* The mfile variable can have one or more path elements.  If
717              it has multiple elements, we want to tack those onto both
718              repository and where.  The last element may refer to either
719              a file or directory.  Here's what to do:
720 
721              it refers to a directory
722                -> simply tack it on to where and repository
723              it refers to a file
724                -> munge argv to contain `basename mfile` */
725 
726           char *cp;
727           char *path;
728 
729 
730           /* Paranoia check. */
731 
732           if (mfile[strlen (mfile) - 1] == '/')
733           {
734               error (0, 0, "checkout_proc: trailing slash on mfile (%s)!",
735                        mfile);
736           }
737 
738 
739           /* Does mfile have multiple path elements? */
740 
741           cp = strrchr (mfile, '/');
742           if (cp != NULL)
743           {
744               *cp = '\0';
745               (void) strcat (repository, "/");
746               (void) strcat (repository, mfile);
747               (void) strcat (where, "/");
748               (void) strcat (where, mfile);
749               mfile = cp + 1;
750           }
751 
752 
753           /* Now mfile is a single path element. */
754 
755           path = Xasprintf ("%s/%s", repository, mfile);
756           if (isdir (path))
757           {
758               /* It's a directory, so tack it on to repository and
759                where, as we did above. */
760 
761               (void) strcat (repository, "/");
762               (void) strcat (repository, mfile);
763               (void) strcat (where, "/");
764               (void) strcat (where, mfile);
765           }
766           else
767           {
768               /* It's a file, which means we have to screw around with
769                argv. */
770               myargv[0] = argv[0];
771               myargv[1] = mfile;
772               argc = 2;
773               argv = myargv;
774           }
775           free (path);
776     }
777 
778     if (preload_update_dir != NULL)
779     {
780           preload_update_dir =
781               xrealloc (preload_update_dir,
782                           strlen (preload_update_dir) + strlen (where) + 5);
783           strcat (preload_update_dir, "/");
784           strcat (preload_update_dir, where);
785     }
786     else
787           preload_update_dir = xstrdup (where);
788 
789     /*
790      * At this point, where is the directory we want to build, repository is
791      * the repository for the lowest level of the path.
792      *
793      * We need to tell build_dirs not only the path we want it to
794      * build, but also the repositories we want it to populate the
795      * path with.  To accomplish this, we walk the path backwards, one
796      * pathname component at a time, constucting a linked list of
797      * struct dir_to_build.
798      */
799 
800     /*
801      * If we are sending everything to stdout, we can skip a whole bunch of
802      * work from here
803      */
804     if (!pipeout)
805     {
806           struct dir_to_build *head;
807           char *reposcopy;
808 
809           if (strncmp (repository, current_parsed_root->directory,
810                          strlen (current_parsed_root->directory)) != 0)
811               error (1, 0, "\
812 internal error: %s doesn't start with %s in checkout_proc",
813                        repository, current_parsed_root->directory);
814 
815           /* We always create at least one directory, which corresponds to
816              the entire strings for WHERE and REPOSITORY.  */
817           head = xmalloc (sizeof (struct dir_to_build));
818           /* Special marker to indicate that we don't want build_dirs_and_chdir
819              to create the CVSADM directory for us.  */
820           head->repository = NULL;
821           head->dirpath = xstrdup (where);
822           head->next = NULL;
823 
824           /* Make a copy of the repository name to play with. */
825           reposcopy = xstrdup (repository);
826 
827           /* FIXME: this should be written in terms of last_component
828              instead of hardcoding '/'.  This presumably affects OS/2,
829              NT, &c, if the user specifies '\'.  Likewise for the call
830              to findslash.  */
831           cp = where + strlen (where);
832           while (cp > where)
833           {
834               struct dir_to_build *new;
835 
836               cp = findslash (where, cp - 1);
837               if (cp == NULL)
838                     break;              /* we're done */
839 
840               new = xmalloc (sizeof (struct dir_to_build));
841               new->dirpath = xmalloc (strlen (where));
842 
843               /* If the user specified an absolute path for where, the
844                last path element we create should be the top-level
845                directory. */
846 
847               if (cp > where)
848               {
849                     strncpy (new->dirpath, where, cp - where);
850                     new->dirpath[cp - where] = '\0';
851               }
852               else
853               {
854                     /* where should always be at least one character long. */
855                     assert (where[0] != '\0');
856                     strcpy (new->dirpath, "/");
857               }
858               new->next = head;
859               head = new;
860 
861               /* Now figure out what repository directory to generate.
862                The most complete case would be something like this:
863 
864                  The modules file contains
865                    foo -d bar/baz quux
866 
867                  The command issued was:
868                    cvs co -d what/ever -N foo
869 
870                  The results in the CVS/Repository files should be:
871                    .     -> (don't touch CVS/Repository)
872                                 (I think this case might be buggy currently)
873                      what  -> (don't touch CVS/Repository)
874                      ever  -> .          (same as "cd what/ever; cvs co -N foo")
875                      bar   -> Emptydir   (generated dir -- not in repos)
876                      baz   -> quux       (finally!) */
877 
878               if (strcmp (reposcopy, current_parsed_root->directory) == 0)
879               {
880                     /* We can't walk up past CVSROOT.  Instead, the
881                    repository should be Emptydir. */
882                     new->repository = emptydir_name ();
883               }
884               else
885               {
886                     /* It's a directory in the repository! */
887 
888                     char *rp;
889 
890                     /* We'll always be below CVSROOT, but check for
891                        paranoia's sake. */
892                     rp = strrchr (reposcopy, '/');
893                     if (rp == NULL)
894                         error (1, 0,
895                                  "internal error: %s doesn't contain a slash",
896                                  reposcopy);
897 
898                     *rp = '\0';
899 
900                     if (strcmp (reposcopy, current_parsed_root->directory) == 0)
901                     {
902                         /* Special case -- the repository name needs
903                            to be "/path/to/repos/." (the trailing dot
904                            is important).  We might be able to get rid
905                            of this after the we check out the other
906                            code that handles repository names. */
907                         new-> repository = Xasprintf ("%s/.", reposcopy);
908                     }
909                     else
910                         new->repository = xstrdup (reposcopy);
911               }
912           }
913 
914           /* clean up */
915           free (reposcopy);
916 
917           /* The top-level CVSADM directory should always be
918              current_parsed_root->directory.  Create it, but only if WHERE is
919              relative.  If WHERE is absolute, our current directory
920              may not have a thing to do with where the sources are
921              being checked out.  If it does, build_dirs_and_chdir
922              will take care of creating adm files here. */
923           /* FIXME: checking where_is_absolute is a horrid kludge;
924              I suspect we probably can just skip the call to
925              build_one_dir whenever the -d command option was specified
926              to checkout.  */
927 
928           if (!ISABSOLUTE (where) && config->top_level_admin
929               && m_type == CHECKOUT)
930           {
931               /* It may be argued that we shouldn't set any sticky
932                  bits for the top-level repository.  FIXME?  */
933               build_one_dir (current_parsed_root->directory, ".", argc <= 1);
934 
935 #ifdef SERVER_SUPPORT
936               /* We _always_ want to have a top-level admin
937                  directory.  If we're running in client/server mode,
938                  send a "Clear-static-directory" command to make
939                  sure it is created on the client side.  (See 5.10
940                  in cvsclient.dvi to convince yourself that this is
941                  OK.)  If this is a duplicate command being sent, it
942                  will be ignored on the client side.  */
943 
944               if (server_active)
945                     server_clear_entstat (".", current_parsed_root->directory);
946 #endif
947           }
948 
949 
950           /* Build dirs on the path if necessary and leave us in the
951              bottom directory (where if where was specified) doesn't
952              contain a CVS subdir yet, but all the others contain
953              CVS and Entries.Static files */
954 
955           if (build_dirs_and_chdir (head, argc <= 1) != 0)
956           {
957               error (0, 0, "ignoring module %s", omodule);
958               err = 1;
959               goto out;
960           }
961 
962           /* set up the repository (or make sure the old one matches) */
963           if (!isfile (CVSADM))
964           {
965               FILE *fp;
966 
967               if (!noexec && argc > 1)
968               {
969                     /* I'm not sure whether this check is redundant.  */
970                     if (!isdir (repository))
971                         error (1, 0, "there is no repository %s", repository);
972 
973                     Create_Admin (".", preload_update_dir, repository,
974                                     NULL, NULL, 0, 0, m_type == CHECKOUT);
975                     fp = xfopen (CVSADM_ENTSTAT, "w+");
976                     if (fclose (fp) == EOF)
977                         error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
978 #ifdef SERVER_SUPPORT
979                     if (server_active)
980                         server_set_entstat (where, repository);
981 #endif
982               }
983               else
984               {
985                     /* I'm not sure whether this check is redundant.  */
986                     if (!isdir (repository))
987                         error (1, 0, "there is no repository %s", repository);
988 
989                     Create_Admin (".", preload_update_dir, repository, tag, date,
990 
991                                     /* FIXME?  This is a guess.  If it is important
992                                          for nonbranch to be set correctly here I
993                                          think we need to write it one way now and
994                                          then rewrite it later via WriteTag, once
995                                          we've had a chance to call RCS_nodeisbranch
996                                          on each file.  */
997                                     0, 0, m_type == CHECKOUT);
998               }
999           }
1000           else
1001           {
1002               char *repos;
1003 
1004               if (m_type == EXPORT)
1005                     error (1, 0, "cannot export into working directory");
1006 
1007               /* get the contents of the previously existing repository */
1008               repos = Name_Repository (NULL, preload_update_dir);
1009               if (fncmp (repository, repos) != 0)
1010               {
1011                     char *prepos = xstrdup (primary_root_inverse_translate (repos));
1012                     char *prepository =
1013                         xstrdup (primary_root_inverse_translate (repository));
1014                     error (0, 0, "existing repository %s does not match %s",
1015                            prepos, prepository);
1016                     error (0, 0, "ignoring module %s", omodule);
1017                     free (repos);
1018                     free (prepos);
1019                     free (prepository);
1020                     err = 1;
1021                     goto out;
1022               }
1023               free (repos);
1024           }
1025     }
1026 
1027     /*
1028      * If we are going to be updating to stdout, we need to cd to the
1029      * repository directory so the recursion processor can use the current
1030      * directory as the place to find repository information
1031      */
1032     if (pipeout)
1033     {
1034           if (CVS_CHDIR (repository) < 0)
1035           {
1036               error (0, errno, "cannot chdir to %s", repository);
1037               err = 1;
1038               goto out;
1039           }
1040           which = W_REPOS;
1041           if (tag && !tag_validated)
1042           {
1043               tag_check_valid (tag, argc - 1, argv + 1, 0, aflag,
1044                                    repository, false);
1045               tag_validated = true;
1046           }
1047     }
1048     else
1049     {
1050           which = W_LOCAL | W_REPOS;
1051           if (tag && !tag_validated)
1052           {
1053               tag_check_valid (tag, argc - 1, argv + 1, 0, aflag,
1054                                    repository, false);
1055               tag_validated = true;
1056           }
1057     }
1058 
1059     if (tag || date || join_rev1 || join_date2)
1060           which |= W_ATTIC;
1061 
1062     if (!join_tags_validated)
1063     {
1064         if (join_rev1)
1065               tag_check_valid (join_rev1, argc - 1, argv + 1, 0, aflag,
1066                                    repository, false);
1067           if (join_rev2)
1068               tag_check_valid (join_rev2, argc - 1, argv + 1, 0, aflag,
1069                                    repository, false);
1070           join_tags_validated = true;
1071     }
1072 
1073     /*
1074      * if we are going to be recursive (building dirs), go ahead and call the
1075      * update recursion processor.  We will be recursive unless either local
1076      * only was specified, or we were passed arguments
1077      */
1078     if (!(local_specified || argc > 1))
1079     {
1080           if (!pipeout)
1081               history_write (m_type == CHECKOUT ? 'O' : 'E', preload_update_dir,
1082                                  history_name, where, repository);
1083           err += do_update (0, NULL, options, tag, date,
1084                                 force_tag_match, false /* !local */ ,
1085                                 true /* update -d */ , aflag, checkout_prune_dirs,
1086                                 pipeout, which, join_rev1, join_date1,
1087                                 join_rev2, join_date2,
1088                                 preload_update_dir, m_type == CHECKOUT,
1089                                 repository);
1090           goto out;
1091     }
1092 
1093     if (!pipeout)
1094     {
1095           int i;
1096           List *entries;
1097 
1098           /* we are only doing files, so register them */
1099           entries = Entries_Open (0, NULL);
1100           for (i = 1; i < argc; i++)
1101           {
1102               char *line;
1103               Vers_TS *vers;
1104               struct file_info finfo;
1105 
1106               memset (&finfo, 0, sizeof finfo);
1107               finfo.file = argv[i];
1108               /* Shouldn't be used, so set to arbitrary value.  */
1109               finfo.update_dir = NULL;
1110               finfo.fullname = argv[i];
1111               finfo.repository = repository;
1112               finfo.entries = entries;
1113               /* The rcs slot is needed to get the options from the RCS
1114                file */
1115               finfo.rcs = RCS_parse (finfo.file, repository);
1116 
1117               vers = Version_TS (&finfo, options, tag, date,
1118                                      force_tag_match, 0);
1119               if (vers->ts_user == NULL)
1120               {
1121                     line = Xasprintf ("Initial %s", finfo.file);
1122                     Register (entries, finfo.file,
1123                                 vers->vn_rcs ? vers->vn_rcs : "0",
1124                                 line, vers->options, vers->tag,
1125                                 vers->date, NULL);
1126                     free (line);
1127               }
1128               freevers_ts (&vers);
1129               freercsnode (&finfo.rcs);
1130           }
1131 
1132           Entries_Close (entries);
1133     }
1134 
1135     /* Don't log "export", just regular "checkouts" */
1136     if (m_type == CHECKOUT && !pipeout)
1137           history_write ('O', preload_update_dir, history_name, where,
1138                            repository);
1139 
1140     /* go ahead and call update now that everything is set */
1141     err += do_update (argc - 1, argv + 1, options, tag, date,
1142                           force_tag_match, local_specified, true /* update -d */,
1143                           aflag, checkout_prune_dirs, pipeout, which, join_rev1,
1144                           join_date1, join_rev2, join_date2, preload_update_dir,
1145                           m_type == CHECKOUT, repository);
1146 out:
1147     free (preload_update_dir);
1148     preload_update_dir = oldupdate;
1149     free (where);
1150     free (repository);
1151     return err;
1152 }
1153 
1154 
1155 
1156 static char *
findslash(char * start,char * p)1157 findslash (char *start, char *p)
1158 {
1159     for (;;)
1160     {
1161           if (*p == '/') return p;
1162           if (p == start) break;
1163           --p;
1164     }
1165     return NULL;
1166 }
1167 
1168 
1169 
1170 /* Return a newly malloc'd string containing a pathname for CVSNULLREPOS,
1171    and make sure that it exists.  If there is an error creating the
1172    directory, give a fatal error.  Otherwise, the directory is guaranteed
1173    to exist when we return.  */
1174 char *
emptydir_name(void)1175 emptydir_name (void)
1176 {
1177     char *repository;
1178 
1179     repository = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
1180                                   CVSROOTADM, CVSNULLREPOS);
1181     if (!isfile (repository))
1182     {
1183           mode_t omask;
1184           omask = umask (cvsumask);
1185           if (CVS_MKDIR (repository, 0777) < 0)
1186               error (1, errno, "cannot create %s", repository);
1187           (void) umask (omask);
1188     }
1189     return repository;
1190 }
1191 
1192 
1193 
1194 /* Build all the dirs along the path to DIRS with CVS subdirs with appropriate
1195  * repositories.  If DIRS->repository is NULL or the directory already exists,
1196  * do not create a CVSADM directory for that subdirectory; just CVS_CHDIR into
1197  * it.  Frees all storage used by DIRS.
1198  *
1199  * ASSUMPTIONS
1200  *   1. Parent directories will be listed in DIRS before their children.
1201  *   2. At most a single directory will need to be changed at one time.  In
1202  *      other words, if we are in /a/b/c, and our final destination is
1203  *      /a/b/c/d/e/f, then we will build d, then d/e, then d/e/f.
1204  *
1205  * INPUTS
1206  *   dirs Simple list composed of dir_to_build structures, listing
1207  *                  information about directories to build.
1208  *   sticky         Passed to build_one_dir to tell it whether there are any sticky
1209  *                  tags or dates to be concerned with.
1210  *
1211  * RETURNS
1212  *   1 on error, 0 otherwise.
1213  *
1214  * ERRORS
1215  *  The only nonfatal error this function may return is if the CHDIR fails.
1216  */
1217 static int
build_dirs_and_chdir(struct dir_to_build * dirs,int sticky)1218 build_dirs_and_chdir (struct dir_to_build *dirs, int sticky)
1219 {
1220     int retval = 0;
1221     struct dir_to_build *nextdir;
1222 
1223     while (dirs != NULL)
1224     {
1225           const char *dir = last_component (dirs->dirpath);
1226           int made_dir = 0;
1227 
1228           made_dir = !mkdir_if_needed (dir);
1229           if (made_dir) Subdir_Register (NULL, NULL, dir);
1230 
1231           if (CVS_CHDIR (dir) < 0)
1232           {
1233               error (0, errno, "cannot chdir to %s", dir);
1234               retval = 1;
1235               goto out;
1236           }
1237           if (dirs->repository != NULL)
1238           {
1239               if (made_dir)
1240                     build_one_dir (dirs->repository, dirs->dirpath, sticky);
1241               free (dirs->repository);
1242           }
1243           nextdir = dirs->next;
1244           free (dirs->dirpath);
1245           free (dirs);
1246           dirs = nextdir;
1247     }
1248 
1249  out:
1250     while (dirs != NULL)
1251     {
1252           if (dirs->repository != NULL)
1253               free (dirs->repository);
1254           nextdir = dirs->next;
1255           free (dirs->dirpath);
1256           free (dirs);
1257           dirs = nextdir;
1258     }
1259     return retval;
1260 }
1261