386BSD 0.1 development
[unix-history] / usr / othersrc / public / cvs-1.3 / src / checkout.c
CommitLineData
210114b7
WJ
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 1.3 kit.
7 *
8 * Create Version
9 *
10 * "checkout" creates a "version" of an RCS repository. This version is owned
11 * totally by the user and is actually an independent copy, to be dealt with
12 * as seen fit. Once "checkout" has been called in a given directory, it
13 * never needs to be called again. The user can keep up-to-date by calling
14 * "update" when he feels like it; this will supply him with a merge of his
15 * own modifications and the changes made in the RCS original. See "update"
16 * for details.
17 *
18 * "checkout" can be given a list of directories or files to be updated and in
19 * the case of a directory, will recursivley create any sub-directories that
20 * exist in the repository.
21 *
22 * When the user is satisfied with his own modifications, the present version
23 * can be committed by "commit"; this keeps the present version in tact,
24 * usually.
25 *
26 * The call is cvs checkout [options] <module-name>...
27 *
28 * "checkout" creates a directory ./CVS, in which it keeps its administration,
29 * in two files, Repository and Entries. The first contains the name of the
30 * repository. The second contains one line for each registered file,
31 * consisting of the version number it derives from, its time stamp at
32 * derivation time and its name. Both files are normal files and can be
33 * edited by the user, if necessary (when the repository is moved, e.g.)
34 */
35
36#include "cvs.h"
37
38#ifndef lint
39static char rcsid[] = "@(#)checkout.c 1.67 92/04/10";
40#endif
41
42#if __STDC__
43static char *findslash (char *start, char *p);
44static int build_dirs_and_chdir (char *dir, char *prepath, char *realdir,
45 int sticky);
46static int checkout_proc (int *pargc, char *argv[], char *where,
47 char *mwhere, char *mfile, int shorten,
48 int local_specified, char *omodule,
49 char *msg);
50#else
51static int checkout_proc ();
52static char *findslash ();
53static int build_dirs_and_chdir ();
54#endif /* __STDC__ */
55
56static char *checkout_usage[] =
57{
58 "Usage:\n %s %s [-ANPQcflnpqs] [-r rev | -D date] [-d dir] [-k kopt] modules...\n",
59 "\t-A\tReset any sticky tags/date/kopts.\n",
60 "\t-N\tDon't shorten module paths if -d specified.\n",
61 "\t-P\tPrune empty directories.\n",
62 "\t-Q\tReally quiet.\n",
63 "\t-c\t\"cat\" the module database.\n",
64 "\t-f\tForce a head revision match if tag/date not found.\n",
65 "\t-l\tLocal directory only, not recursive\n",
66 "\t-n\tDo not run module program (if any).\n",
67 "\t-p\tCheck out files to standard output.\n",
68 "\t-q\tSomewhat quiet.\n",
69 "\t-s\tLike -c, but include module status.\n",
70 "\t-r rev\tCheck out revision or tag. (implies -P)\n",
71 "\t-D date\tCheck out revisions as of date. (implies -P)\n",
72 "\t-d dir\tCheck out into dir instead of module name.\n",
73 "\t-k kopt\tUse RCS kopt -k option on checkout.\n",
74 "\t-j rev\tMerge in changes made between current revision and rev.\n",
75 NULL
76};
77
78static char *export_usage[] =
79{
80 "Usage: %s %s [-NPQflnq] [-r rev | -D date] [-d dir] module...\n",
81 "\t-N\tDon't shorten module paths if -d specified.\n",
82 "\t-Q\tReally quiet.\n",
83 "\t-f\tForce a head revision match if tag/date not found.\n",
84 "\t-l\tLocal directory only, not recursive\n",
85 "\t-n\tDo not run module program (if any).\n",
86 "\t-q\tSomewhat quiet.\n",
87 "\t-r rev\tCheck out revision or tag. (implies -P)\n",
88 "\t-D date\tCheck out revisions as of date. (implies -P)\n",
89 "\t-d dir\tCheck out into dir instead of module name.\n",
90 NULL
91};
92
93static int checkout_prune_dirs;
94static int force_tag_match = 1;
95static int pipeout;
96static int aflag;
97static char *options = NULL;
98static char *tag = NULL;
99static char *date = NULL;
100static char *join_rev1 = NULL;
101static char *join_rev2 = NULL;
102static char *preload_update_dir = NULL;
103
104int
105checkout (argc, argv)
106 int argc;
107 char *argv[];
108{
109 register int i;
110 int c;
111 DBM *db;
112 int cat = 0, err = 0, status = 0;
113 int run_module_prog = 1;
114 int local = 0;
115 int shorten = -1;
116 char *where = NULL;
117 char *valid_options, **valid_usage;
118
119 /*
120 * A smaller subset of options are allowed for the export command, which
121 * is essentially like checkout, except that it hard-codes certain
122 * options to be on (like -kv) and takes care to remove the CVS directory
123 * when it has done its duty
124 */
125 if (strcmp (command_name, "export") == 0)
126 {
127 valid_options = "Nnd:flRQqr:D:";
128 valid_usage = export_usage;
129 }
130 else
131 {
132 valid_options = "ANnk:d:flRpQqcsr:D:j:P";
133 valid_usage = checkout_usage;
134 }
135
136 if (argc == -1)
137 usage (valid_usage);
138
139 ign_setup ();
140
141 optind = 1;
142 while ((c = gnu_getopt (argc, argv, valid_options)) != -1)
143 {
144 switch (c)
145 {
146 case 'A':
147 aflag = 1;
148 break;
149 case 'N':
150 shorten = 0;
151 break;
152 case 'k':
153 if (options)
154 free (options);
155 options = RCS_check_kflag (optarg);
156 break;
157 case 'n':
158 run_module_prog = 0;
159 break;
160 case 'Q':
161 really_quiet = 1;
162 /* FALL THROUGH */
163 case 'q':
164 quiet = 1;
165 break;
166 case 'l':
167 local = 1;
168 break;
169 case 'R':
170 local = 0;
171 break;
172 case 'P':
173 checkout_prune_dirs = 1;
174 break;
175 case 'p':
176 pipeout = 1;
177 run_module_prog = 0; /* don't run module prog when piping */
178 noexec = 1; /* so no locks will be created */
179 break;
180 case 'c':
181 cat = 1;
182 break;
183 case 'd':
184 where = optarg;
185 if (shorten == -1)
186 shorten = 1;
187 break;
188 case 's':
189 status = 1;
190 break;
191 case 'f':
192 force_tag_match = 0;
193 break;
194 case 'r':
195 tag = optarg;
196 checkout_prune_dirs = 1;
197 break;
198 case 'D':
199 date = Make_Date (optarg);
200 checkout_prune_dirs = 1;
201 break;
202 case 'j':
203 if (join_rev2)
204 error (1, 0, "only two -j options can be specified");
205 if (join_rev1)
206 join_rev2 = optarg;
207 else
208 join_rev1 = optarg;
209 break;
210 case '?':
211 default:
212 usage (valid_usage);
213 break;
214 }
215 }
216 argc -= optind;
217 argv += optind;
218
219 if (shorten == -1)
220 shorten = 0;
221
222 if ((!(cat + status) && argc == 0) || ((cat + status) && argc != 0)
223 || (tag && date))
224 usage (valid_usage);
225
226 if (where && pipeout)
227 error (1, 0, "-d and -p are mutually exclusive");
228
229 if (strcmp (command_name, "export") == 0)
230 {
231 if (!tag && !date)
232 {
233 error (0, 0, "must specify a tag or date");
234 usage (valid_usage);
235 }
236 if (tag && isdigit (tag[0]))
237 error (1, 0, "tag `%s' must be a symbolic tag", tag);
238 options = RCS_check_kflag ("v");/* -kv must be on */
239 }
240
241 if (cat || status)
242 {
243 cat_module (status);
244 return (0);
245 }
246 db = open_module ();
247
248 /*
249 * if we have more than one argument and where was specified, we make the
250 * where, cd into it, and try to shorten names as much as possible.
251 * Otherwise, we pass the where as a single argument to do_module.
252 */
253 if (argc > 1 && where != NULL)
254 {
255 char repository[PATH_MAX];
256
257 (void) mkdir (where, 0777);
258 if (chdir (where) < 0)
259 error (1, errno, "cannot chdir to %s", where);
260 preload_update_dir = xstrdup (where);
261 where = (char *) NULL;
262 if (!isfile (CVSADM) && !isfile (OCVSADM))
263 {
264 (void) sprintf (repository, "%s/%s", CVSroot, CVSNULLREPOS);
265 if (!isfile (repository))
266 (void) mkdir (repository, 0777);
267 Create_Admin (".", repository, (char *) NULL, (char *) NULL);
268 if (!noexec)
269 {
270 FILE *fp;
271
272 fp = open_file (CVSADM_ENTSTAT, "w+");
273 if (fclose(fp) == EOF)
274 error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
275 }
276 }
277 }
278
279 /*
280 * if where was specified (-d) and we have not taken care of it already
281 * with the multiple arg stuff, and it was not a simple directory name
282 * but rather a path, we strip off everything but the last component and
283 * attempt to cd to the indicated place. where then becomes simply the
284 * last component
285 */
286 if (where != NULL && index (where, '/') != NULL)
287 {
288 char *slash;
289
290 slash = rindex (where, '/');
291 *slash = '\0';
292
293 if (chdir (where) < 0)
294 error (1, errno, "cannot chdir to %s", where);
295
296 preload_update_dir = xstrdup (where);
297
298 where = slash + 1;
299 if (*where == '\0')
300 where = NULL;
301 }
302
303 for (i = 0; i < argc; i++)
304 err += do_module (db, argv[i], CHECKOUT, "Updating", checkout_proc,
305 where, shorten, local, run_module_prog,
306 (char *) NULL);
307 close_module (db);
308 return (err);
309}
310
311/*
312 * process_module calls us back here so we do the actual checkout stuff
313 */
314/* ARGSUSED */
315static int
316checkout_proc (pargc, argv, where, mwhere, mfile, shorten,
317 local_specified, omodule, msg)
318 int *pargc;
319 char *argv[];
320 char *where;
321 char *mwhere;
322 char *mfile;
323 int shorten;
324 int local_specified;
325 char *omodule;
326 char *msg;
327{
328 int err = 0;
329 int which;
330 char *cp;
331 char *cp2;
332 char repository[PATH_MAX];
333 char xwhere[PATH_MAX];
334 char *oldupdate = NULL;
335 char *prepath;
336 char *realdirs;
337
338 /*
339 * OK, so we're doing the checkout! Our args are as follows:
340 * argc,argv contain either dir or dir followed by a list of files
341 * where contains where to put it (if supplied by checkout)
342 * mwhere contains the module name or -d from module file
343 * mfile says do only that part of the module
344 * shorten = TRUE says shorten as much as possible
345 * omodule is the original arg to do_module()
346 */
347
348 /* set up the repository (maybe) for the bottom directory */
349 (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
350
351 /* save the original value of preload_update_dir */
352 if (preload_update_dir != NULL)
353 oldupdate = xstrdup (preload_update_dir);
354
355 /* fix up argv[] for the case of partial modules */
356 if (mfile != NULL)
357 {
358 char file[PATH_MAX];
359
360 /* if mfile is really a path, straighten it out first */
361 if ((cp = rindex (mfile, '/')) != NULL)
362 {
363 *cp = 0;
364 (void) strcat (repository, "/");
365 (void) strcat (repository, mfile);
366
367 /*
368 * Now we need to fill in the where correctly. if !shorten, tack
369 * the rest of the path onto where if where is filled in
370 * otherwise tack the rest of the path onto mwhere and make that
371 * the where
372 *
373 * If shorten is enabled, we might use mwhere to set where if
374 * nobody set it yet, so we'll need to setup mwhere as the last
375 * component of the path we are tacking onto repository
376 */
377 if (!shorten)
378 {
379 if (where != NULL)
380 (void) sprintf (xwhere, "%s/%s", where, mfile);
381 else
382 (void) sprintf (xwhere, "%s/%s", mwhere, mfile);
383 where = xwhere;
384 }
385 else
386 {
387 char *slash;
388
389 if ((slash = rindex (mfile, '/')) != NULL)
390 mwhere = slash + 1;
391 else
392 mwhere = mfile;
393 }
394 mfile = cp + 1;
395 }
396
397 (void) sprintf (file, "%s/%s", repository, mfile);
398 if (isdir (file))
399 {
400
401 /*
402 * The portion of a module was a directory, so kludge up where to
403 * be the subdir, and fix up repository
404 */
405 (void) strcpy (repository, file);
406
407 /*
408 * At this point, if shorten is not enabled, we make where either
409 * where with mfile concatenated, or if where hadn't been set we
410 * set it to mwhere with mfile concatenated.
411 *
412 * If shorten is enabled and where hasn't been set yet, then where
413 * becomes mfile
414 */
415 if (!shorten)
416 {
417 if (where != NULL)
418 (void) sprintf (xwhere, "%s/%s", where, mfile);
419 else
420 (void) sprintf (xwhere, "%s/%s", mwhere, mfile);
421 where = xwhere;
422 }
423 else if (where == NULL)
424 where = mfile;
425 }
426 else
427 {
428 int i;
429
430 /*
431 * The portion of a module was a file, so kludge up argv to be
432 * correct
433 */
434 for (i = 1; i < *pargc; i++)/* free the old ones */
435 free (argv[i]);
436 argv[1] = xstrdup (mfile); /* set up the new one */
437 *pargc = 2;
438
439 /* where gets mwhere if where isn't set */
440 if (where == NULL)
441 where = mwhere;
442 }
443 }
444
445 /*
446 * if shorten is enabled and where isn't specified yet, we pluck the last
447 * directory component of argv[0] and make it the where
448 */
449 if (shorten && where == NULL)
450 {
451 if ((cp = rindex (argv[0], '/')) != NULL)
452 {
453 (void) strcpy (xwhere, cp + 1);
454 where = xwhere;
455 }
456 }
457
458 /* if where is still NULL, use mwhere if set or the argv[0] dir */
459 if (where == NULL)
460 {
461 if (mwhere)
462 where = mwhere;
463 else
464 {
465 (void) strcpy (xwhere, argv[0]);
466 where = xwhere;
467 }
468 }
469
470 if (preload_update_dir != NULL)
471 {
472 char tmp[PATH_MAX];
473
474 (void) sprintf (tmp, "%s/%s", preload_update_dir, where);
475 free (preload_update_dir);
476 preload_update_dir = xstrdup (tmp);
477 }
478 else
479 preload_update_dir = xstrdup (where);
480
481 /*
482 * At this point, where is the directory we want to build, repository is
483 * the repository for the lowest level of the path.
484 */
485
486 /*
487 * If we are sending everything to stdout, we can skip a whole bunch of
488 * work from here
489 */
490 if (!pipeout)
491 {
492
493 /*
494 * We need to tell build_dirs not only the path we want it to build,
495 * but also the repositories we want it to populate the path with. To
496 * accomplish this, we pass build_dirs a ``real path'' with valid
497 * repositories and a string to pre-pend based on how many path
498 * elements exist in where. Big Black Magic
499 */
500 prepath = xstrdup (repository);
501 cp = rindex (where, '/');
502 cp2 = rindex (prepath, '/');
503 while (cp != NULL)
504 {
505 cp = findslash (where, cp - 1);
506 cp2 = findslash (prepath, cp2 - 1);
507 }
508 *cp2 = '\0';
509 realdirs = cp2 + 1;
510
511 /*
512 * build dirs on the path if necessary and leave us in the bottom
513 * directory (where if where was specified) doesn't contain a CVS
514 * subdir yet, but all the others contain CVS and Entries.Static
515 * files
516 */
517 if (build_dirs_and_chdir (where, prepath, realdirs, *pargc <= 1) != 0)
518 {
519 error (0, 0, "ignoring module %s", omodule);
520 free (prepath);
521 free (preload_update_dir);
522 preload_update_dir = oldupdate;
523 return (1);
524 }
525
526 /* clean up */
527 free (prepath);
528
529 /* set up the repository (or make sure the old one matches) */
530 if (!isfile (CVSADM) && !isfile (OCVSADM))
531 {
532 FILE *fp;
533
534 if (!noexec && *pargc > 1)
535 {
536 Create_Admin (".", repository, (char *) NULL, (char *) NULL);
537 fp = open_file (CVSADM_ENTSTAT, "w+");
538 if (fclose(fp) == EOF)
539 error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
540 }
541 else
542 Create_Admin (".", repository, tag, date);
543 }
544 else
545 {
546 char *repos;
547
548 /* get the contents of the previously existing repository */
549 repos = Name_Repository ((char *) NULL, preload_update_dir);
550 if (strcmp (repository, repos) != 0)
551 {
552 error (0, 0, "existing repository %s does not match %s",
553 repos, repository);
554 error (0, 0, "ignoring module %s", omodule);
555 free (repos);
556 free (preload_update_dir);
557 preload_update_dir = oldupdate;
558 return (1);
559 }
560 free (repos);
561 }
562 }
563
564 /*
565 * If we are going to be updating to stdout, we need to cd to the
566 * repository directory so the recursion processor can use the current
567 * directory as the place to find repository information
568 */
569 if (pipeout)
570 {
571 if (chdir (repository) < 0)
572 {
573 error (0, errno, "cannot chdir to %s", repository);
574 free (preload_update_dir);
575 preload_update_dir = oldupdate;
576 return (1);
577 }
578 which = W_REPOS;
579 }
580 else
581 which = W_LOCAL | W_REPOS;
582
583 if (tag != NULL || date != NULL)
584 which |= W_ATTIC;
585
586 /*
587 * if we are going to be recursive (building dirs), go ahead and call the
588 * update recursion processor. We will be recursive unless either local
589 * only was specified, or we were passed arguments
590 */
591 if (!(local_specified || *pargc > 1))
592 {
593 if (strcmp (command_name, "export") != 0 && !pipeout)
594 history_write ('O', preload_update_dir, tag ? tag : date, where,
595 repository);
596 err += do_update (0, (char **) NULL, options, tag, date,
597 force_tag_match, 0 /* !local */ ,
598 1 /* update -d */ , aflag, checkout_prune_dirs,
599 pipeout, which, join_rev1, join_rev2,
600 preload_update_dir);
601 free (preload_update_dir);
602 preload_update_dir = oldupdate;
603 return (err);
604 }
605
606 if (!pipeout)
607 {
608 int i;
609 List *entries;
610
611 /* we are only doing files, so register them */
612 entries = ParseEntries (0);
613 for (i = 1; i < *pargc; i++)
614 {
615 char line[MAXLINELEN];
616 char *user;
617 Vers_TS *vers;
618
619 user = argv[i];
620 vers = Version_TS (repository, options, tag, date, user,
621 force_tag_match, 0, entries, (List *) NULL);
622 if (vers->ts_user == NULL)
623 {
624 (void) sprintf (line, "Initial %s", user);
625 Register (entries, user, vers->vn_rcs, line, vers->options,
626 vers->tag, vers->date);
627 }
628 freevers_ts (&vers);
629 }
630 dellist (&entries);
631 }
632
633 /* Don't log "export", just regular "checkouts" */
634 if (strcmp (command_name, "export") != 0 && !pipeout)
635 history_write ('O', preload_update_dir, (tag ? tag : date), where,
636 repository);
637
638 /* go ahead and call update now that everything is set */
639 err += do_update (*pargc - 1, argv + 1, options, tag, date,
640 force_tag_match, local_specified, 1 /* update -d */,
641 aflag, checkout_prune_dirs, pipeout, which, join_rev1,
642 join_rev2, preload_update_dir);
643 free (preload_update_dir);
644 preload_update_dir = oldupdate;
645 return (err);
646}
647
648static char *
649findslash (start, p)
650 char *start;
651 char *p;
652{
653 while ((int) p >= (int) start && *p != '/')
654 p--;
655 if ((int) p < (int) start)
656 return (NULL);
657 else
658 return (p);
659}
660
661/*
662 * build all the dirs along the path to dir with CVS subdirs with appropriate
663 * repositories and Entries.Static files
664 */
665static int
666build_dirs_and_chdir (dir, prepath, realdir, sticky)
667 char *dir;
668 char *prepath;
669 char *realdir;
670 int sticky;
671{
672 FILE *fp;
673 char repository[PATH_MAX];
674 char path[PATH_MAX];
675 char path2[PATH_MAX];
676 char *slash;
677 char *slash2;
678 char *cp;
679 char *cp2;
680
681 (void) strcpy (path, dir);
682 (void) strcpy (path2, realdir);
683 for (cp = path, cp2 = path2;
684 (slash = index (cp, '/')) != NULL && (slash2 = index (cp2, '/')) != NULL;
685 cp = slash + 1, cp2 = slash2 + 1)
686 {
687 *slash = '\0';
688 *slash2 = '\0';
689 (void) mkdir (cp, 0777);
690 if (chdir (cp) < 0)
691 {
692 error (0, errno, "cannot chdir to %s", cp);
693 return (1);
694 }
695 if (!isfile (CVSADM) && !isfile (OCVSADM) &&
696 strcmp (command_name, "export") != 0)
697 {
698 (void) sprintf (repository, "%s/%s", prepath, path2);
699 Create_Admin (".", repository, sticky ? (char *) NULL : tag,
700 sticky ? (char *) NULL : date);
701 if (!noexec)
702 {
703 fp = open_file (CVSADM_ENTSTAT, "w+");
704 if (fclose(fp) == EOF)
705 error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
706 }
707 }
708 *slash = '/';
709 *slash2 = '/';
710 }
711 (void) mkdir (cp, 0777);
712 if (chdir (cp) < 0)
713 {
714 error (0, errno, "cannot chdir to %s", cp);
715 return (1);
716 }
717 return (0);
718}