]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/tag.c
Import cvs-1.10.7. There are a number of nasty bugs that have been fixed.
[FreeBSD/FreeBSD.git] / contrib / cvs / src / tag.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  * Tag
9  * 
10  * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
11  * Uses the checked out revision in the current directory.
12  */
13
14 #include "cvs.h"
15 #include "savecwd.h"
16
17 static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo));
18 static int check_filesdoneproc PROTO ((void *callerdat, int err,
19                                        char *repos, char *update_dir,
20                                        List *entries));
21 static int pretag_proc PROTO((char *repository, char *filter));
22 static void masterlist_delproc PROTO((Node *p));
23 static void tag_delproc PROTO((Node *p));
24 static int pretag_list_proc PROTO((Node *p, void *closure));
25
26 static Dtype tag_dirproc PROTO ((void *callerdat, char *dir,
27                                  char *repos, char *update_dir,
28                                  List *entries));
29 static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo));
30 static int tag_filesdoneproc PROTO ((void *callerdat, int err,
31                                      char *repos, char *update_dir,
32                                      List *entries));
33
34 static char *numtag;
35 static char *date = NULL;
36 static char *symtag;
37 static int delete_flag;                 /* adding a tag by default */
38 static int branch_mode;                 /* make an automagic "branch" tag */
39 static int local;                       /* recursive by default */
40 static int force_tag_match = 1;         /* force tag to match by default */
41 static int force_tag_move;              /* don't force tag to move by default */
42 static int check_uptodate;              /* no uptodate-check by default */
43
44 struct tag_info
45 {
46     Ctype status;
47     char *rev;
48     char *tag;
49     char *options;
50 };
51
52 struct master_lists
53 {
54     List *tlist;
55 };
56
57 static List *mtlist;
58 static List *tlist;
59
60 static const char *const tag_usage[] =
61 {
62     "Usage: %s %s [-lRF] [-b] [-d] [-c] [-r rev|-D date] tag [files...]\n",
63     "\t-l\tLocal directory only, not recursive.\n",
64     "\t-R\tProcess directories recursively.\n",
65     "\t-d\tDelete the given tag.\n",
66     "\t-r rev\tExisting revision/tag.\n",
67     "\t-D\tExisting date.\n",
68     "\t-f\tForce a head revision if specified tag not found.\n",
69     "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
70     "\t-F\tMove tag if it already exists.\n",
71     "\t-c\tCheck that working files are unmodified.\n",
72     "(Specify the --help global option for a list of other help options)\n",
73     NULL
74 };
75
76 int
77 cvstag (argc, argv)
78     int argc;
79     char **argv;
80 {
81     int c;
82     int err = 0;
83
84     if (argc == -1)
85         usage (tag_usage);
86
87     optind = 0;
88     while ((c = getopt (argc, argv, "+FQqlRcdr:D:bf")) != -1)
89     {
90         switch (c)
91         {
92             case 'Q':
93             case 'q':
94 #ifdef SERVER_SUPPORT
95                 /* The CVS 1.5 client sends these options (in addition to
96                    Global_option requests), so we must ignore them.  */
97                 if (!server_active)
98 #endif
99                     error (1, 0,
100                            "-q or -Q must be specified before \"%s\"",
101                            command_name);
102                 break;
103             case 'l':
104                 local = 1;
105                 break;
106             case 'R':
107                 local = 0;
108                 break;
109             case 'd':
110                 delete_flag = 1;
111                 break;
112             case 'c':
113                 check_uptodate = 1;
114                 break;
115             case 'r':
116                 numtag = optarg;
117                 break;
118             case 'D':
119                 if (date)
120                     free (date);
121                 date = Make_Date (optarg);
122                 break;
123             case 'f':
124                 force_tag_match = 0;
125                 break;
126             case 'b':
127                 branch_mode = 1;
128                 break;
129             case 'F':
130                 force_tag_move = 1;
131                 break;
132             case '?':
133             default:
134                 usage (tag_usage);
135                 break;
136         }
137     }
138     argc -= optind;
139     argv += optind;
140
141     if (argc == 0)
142         usage (tag_usage);
143     symtag = argv[0];
144     argc--;
145     argv++;
146
147     if (date && numtag)
148         error (1, 0, "-r and -D options are mutually exclusive");
149     if (delete_flag && branch_mode)
150         error (0, 0, "warning: -b ignored with -d options");
151     RCS_check_tag (symtag);
152
153 #ifdef CLIENT_SUPPORT
154     if (client_active)
155     {
156         /* We're the client side.  Fire up the remote server.  */
157         start_server ();
158         
159         ign_setup ();
160
161         if (!force_tag_match)
162             send_arg ("-f");
163         if (local)
164             send_arg("-l");
165         if (delete_flag)
166             send_arg("-d");
167         if (check_uptodate)
168             send_arg("-c");
169         if (branch_mode)
170             send_arg("-b");
171         if (force_tag_move)
172             send_arg("-F");
173
174         if (numtag)
175             option_with_arg ("-r", numtag);
176         if (date)
177             client_senddate (date);
178
179         send_arg (symtag);
180
181         send_files (argc, argv, local, 0,
182
183                     /* I think the -c case is like "cvs status", in
184                        which we really better be correct rather than
185                        being fast; it is just too confusing otherwise.  */
186                     check_uptodate ? 0 : SEND_NO_CONTENTS);
187         send_file_names (argc, argv, SEND_EXPAND_WILD);
188         send_to_server ("tag\012", 0);
189         return get_responses_and_close ();
190     }
191 #endif
192
193     if (numtag != NULL)
194         tag_check_valid (numtag, argc, argv, local, 0, "");
195
196     /* check to make sure they are authorized to tag all the 
197        specified files in the repository */
198
199     mtlist = getlist();
200     err = start_recursion (check_fileproc, check_filesdoneproc,
201                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
202                            argc, argv, local, W_LOCAL, 0, 1,
203                            (char *) NULL, 1);
204     
205     if (err)
206     {
207        error (1, 0, "correct the above errors first!");
208     }
209      
210     /* start the recursion processor */
211     err = start_recursion (tag_fileproc, tag_filesdoneproc, tag_dirproc,
212                            (DIRLEAVEPROC) NULL, NULL, argc, argv, local,
213                            W_LOCAL, 0, 0, (char *) NULL, 1);
214     dellist(&mtlist);
215     return (err);
216 }
217
218 /* check file that is to be tagged */
219 /* All we do here is add it to our list */
220
221 static int
222 check_fileproc (callerdat, finfo)
223     void *callerdat;
224     struct file_info *finfo;
225 {
226     char *xdir;
227     Node *p;
228     Vers_TS *vers;
229     
230     if (check_uptodate) 
231     {
232         Ctype status = Classify_File (finfo, (char *) NULL, (char *) NULL,
233                                       (char *) NULL, 1, 0, &vers, 0);
234         if ((status != T_UPTODATE) && (status != T_CHECKOUT))
235         {
236             error (0, 0, "%s is locally modified", finfo->fullname);
237             return (1);
238         }
239     }
240     
241     if (finfo->update_dir[0] == '\0')
242         xdir = ".";
243     else
244         xdir = finfo->update_dir;
245     if ((p = findnode (mtlist, xdir)) != NULL)
246     {
247         tlist = ((struct master_lists *) p->data)->tlist;
248     }
249     else
250     {
251         struct master_lists *ml;
252         
253         tlist = getlist ();
254         p = getnode ();
255         p->key = xstrdup (xdir);
256         p->type = UPDATE;
257         ml = (struct master_lists *)
258             xmalloc (sizeof (struct master_lists));
259         ml->tlist = tlist;
260         p->data = (char *) ml;
261         p->delproc = masterlist_delproc;
262         (void) addnode (mtlist, p);
263     }
264     /* do tlist */
265     p = getnode ();
266     p->key = xstrdup (finfo->file);
267     p->type = UPDATE;
268     p->delproc = tag_delproc;
269     vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
270     if (vers->srcfile == NULL)
271     {
272         if (!really_quiet)
273             error (0, 0, "nothing known about %s", finfo->file);
274         return (1);
275     }
276
277     /* Here we duplicate the calculation in tag_fileproc about which
278        version we are going to tag.  There probably are some subtle races
279        (e.g. numtag is "foo" which gets moved between here and
280        tag_fileproc).  */
281     if (numtag == NULL && date == NULL)
282         p->data = xstrdup (vers->vn_user);
283     else
284         p->data = RCS_getversion (vers->srcfile, numtag, date,
285                                   force_tag_match, NULL);
286
287     if (p->data != NULL)
288     {
289         int addit = 1;
290         char *oversion;
291         
292         oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
293                                    (int *) NULL);
294         if (oversion == NULL) 
295         {
296             if (delete_flag)
297             {
298                 /* Deleting a tag which did not exist is a noop and
299                    should not be logged.  */
300                 addit = 0;
301             }
302         }
303         else if (delete_flag)
304         {
305             free (p->data);
306             p->data = xstrdup (oversion);
307         }
308         else if (strcmp(oversion, p->data) == 0)
309         {
310             addit = 0;
311         }
312         else if (!force_tag_move)
313         {
314             addit = 0;
315         }
316         if (oversion != NULL)
317         {
318             free(oversion);
319         }
320         if (!addit)
321         {
322             free(p->data);
323             p->data = NULL;
324         }
325     }
326     freevers_ts(&vers);
327     (void) addnode (tlist, p);
328     return (0);
329 }
330                          
331 static int
332 check_filesdoneproc (callerdat, err, repos, update_dir, entries)
333     void *callerdat;
334     int err;
335     char *repos;
336     char *update_dir;
337     List *entries;
338 {
339     int n;
340     Node *p;
341
342     p = findnode(mtlist, update_dir);
343     if (p != NULL)
344     {
345         tlist = ((struct master_lists *) p->data)->tlist;
346     }
347     else
348     {
349         tlist = (List *) NULL;
350     }
351     if ((tlist == NULL) || (tlist->list->next == tlist->list))
352     {
353         return (err);
354     }
355     if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0)
356     {
357         error (0, 0, "Pre-tag check failed");
358         err += n;
359     }
360     return (err);
361 }
362
363 static int
364 pretag_proc(repository, filter)
365     char *repository;
366     char *filter;
367 {
368     if (filter[0] == '/')
369     {
370         char *s, *cp;
371
372         s = xstrdup(filter);
373         for (cp=s; *cp; cp++)
374         {
375             if (isspace ((unsigned char) *cp))
376             {
377                 *cp = '\0';
378                 break;
379             }
380         }
381         if (!isfile(s))
382         {
383             error (0, errno, "cannot find pre-tag filter '%s'", s);
384             free(s);
385             return (1);
386         }
387         free(s);
388     }
389     run_setup (filter);
390     run_arg (symtag);
391     run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add");
392     run_arg (repository);
393     walklist(tlist, pretag_list_proc, NULL);
394     return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
395 }
396
397 static void
398 masterlist_delproc(p)
399     Node *p;
400 {
401     struct master_lists *ml;
402
403     ml = (struct master_lists *)p->data;
404     dellist(&ml->tlist);
405     free(ml);
406     return;
407 }
408
409 static void
410 tag_delproc(p)
411     Node *p;
412 {
413     if (p->data != NULL)
414     {
415         free(p->data);
416         p->data = NULL;
417     }
418     return;
419 }
420
421 static int
422 pretag_list_proc(p, closure)
423     Node *p;
424     void *closure;
425 {
426     if (p->data != NULL)
427     {
428         run_arg(p->key);
429         run_arg(p->data);
430     }
431     return (0);
432 }
433
434
435 /*
436  * Called to tag a particular file (the currently checked out version is
437  * tagged with the specified tag - or the specified tag is deleted).
438  */
439 /* ARGSUSED */
440 static int
441 tag_fileproc (callerdat, finfo)
442     void *callerdat;
443     struct file_info *finfo;
444 {
445     char *version, *oversion;
446     char *nversion = NULL;
447     char *rev;
448     Vers_TS *vers;
449     int retcode = 0;
450
451     /* Lock the directory if it is not already locked.  We can't rely
452        on tag_dirproc because it won't handle the case where the user
453        specifies a list of files on the command line.  */
454     /* We do not need to acquire a full write lock for the tag operation:
455        the revisions are obtained from the working directory, so we do not
456        require consistency across the entire repository.  However, we do
457        need to prevent simultaneous tag operations from interfering with
458        each other.  Therefore, we write lock each directory as we enter
459        it, and unlock it as we leave it.  */
460     lock_dir_for_write (finfo->repository);
461
462     vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
463
464     if ((numtag != NULL) || (date != NULL))
465     {
466         nversion = RCS_getversion(vers->srcfile,
467                                   numtag,
468                                   date,
469                                   force_tag_match,
470                                   (int *) NULL);
471         if (nversion == NULL)
472         {
473             freevers_ts (&vers);
474             return (0);
475         }
476     }
477     if (delete_flag)
478     {
479
480         /*
481          * If -d is specified, "force_tag_match" is set, so that this call to
482          * RCS_getversion() will return a NULL version string if the symbolic
483          * tag does not exist in the RCS file.
484          * 
485          * This is done here because it's MUCH faster than just blindly calling
486          * "rcs" to remove the tag... trust me.
487          */
488
489         version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
490                                   (int *) NULL);
491         if (version == NULL || vers->srcfile == NULL)
492         {
493             freevers_ts (&vers);
494             return (0);
495         }
496         free (version);
497
498         if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0) 
499         {
500             if (!quiet)
501                 error (0, retcode == -1 ? errno : 0,
502                        "failed to remove tag %s from %s", symtag,
503                        vers->srcfile->path);
504             freevers_ts (&vers);
505             return (1);
506         }
507         RCS_rewrite (vers->srcfile, NULL, NULL);
508
509         /* warm fuzzies */
510         if (!really_quiet)
511         {
512             cvs_output ("D ", 2);
513             cvs_output (finfo->fullname, 0);
514             cvs_output ("\n", 1);
515         }
516
517         freevers_ts (&vers);
518         return (0);
519     }
520
521     /*
522      * If we are adding a tag, we need to know which version we have checked
523      * out and we'll tag that version.
524      */
525     if (nversion == NULL)
526     {
527         version = vers->vn_user;
528     }
529     else
530     {
531         version = nversion;
532     }
533     if (version == NULL)
534     {
535         freevers_ts (&vers);
536         return (0);
537     }
538     else if (strcmp (version, "0") == 0)
539     {
540         if (!quiet)
541             error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file);
542         freevers_ts (&vers);
543         return (0);
544     }
545     else if (version[0] == '-')
546     {
547         if (!quiet)
548             error (0, 0, "skipping removed but un-commited file `%s'", finfo->file);
549         freevers_ts (&vers);
550         return (0);
551     }
552     else if (vers->srcfile == NULL)
553     {
554         if (!quiet)
555             error (0, 0, "cannot find revision control file for `%s'", finfo->file);
556         freevers_ts (&vers);
557         return (0);
558     }
559
560     /*
561      * As an enhancement for the case where a tag is being re-applied to a
562      * large number of files, make one extra call to RCS_getversion to see
563      * if the tag is already set in the RCS file.  If so, check to see if it
564      * needs to be moved.  If not, do nothing.  This will likely save a lot of
565      * time when simply moving the tag to the "current" head revisions of a
566      * module -- which I have found to be a typical tagging operation.
567      */
568     rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
569     oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
570                                (int *) NULL);
571     if (oversion != NULL)
572     {
573         int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
574
575         /*
576          * if versions the same and neither old or new are branches don't have 
577          * to do anything
578          */
579         if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
580         {
581             free (oversion);
582             freevers_ts (&vers);
583             return (0);
584         }
585
586         if (!force_tag_move)
587         {
588             /* we're NOT going to move the tag */
589             cvs_output ("W ", 2);
590             cvs_output (finfo->fullname, 0);
591             cvs_output (" : ", 0);
592             cvs_output (symtag, 0);
593             cvs_output (" already exists on ", 0);
594             cvs_output (isbranch ? "branch" : "version", 0);
595             cvs_output (" ", 0);
596             cvs_output (oversion, 0);
597             cvs_output (" : NOT MOVING tag to ", 0);
598             cvs_output (branch_mode ? "branch" : "version", 0);
599             cvs_output (" ", 0);
600             cvs_output (rev, 0);
601             cvs_output ("\n", 1);
602             free (oversion);
603             freevers_ts (&vers);
604             return (0);
605         }
606         free (oversion);
607     }
608
609     if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
610     {
611         error (1, retcode == -1 ? errno : 0,
612                "failed to set tag %s to revision %s in %s",
613                symtag, rev, vers->srcfile->path);
614         freevers_ts (&vers);
615         return (1);
616     }
617     RCS_rewrite (vers->srcfile, NULL, NULL);
618
619     /* more warm fuzzies */
620     if (!really_quiet)
621     {
622         cvs_output ("T ", 2);
623         cvs_output (finfo->fullname, 0);
624         cvs_output ("\n", 1);
625     }
626
627     if (nversion != NULL)
628     {
629         free (nversion);
630     }
631     freevers_ts (&vers);
632     return (0);
633 }
634
635 /* Clear any lock we may hold on the current directory.  */
636
637 static int
638 tag_filesdoneproc (callerdat, err, repos, update_dir, entries)
639     void *callerdat;
640     int err;
641     char *repos;
642     char *update_dir;
643     List *entries;
644 {
645     Lock_Cleanup ();
646
647     return (err);
648 }
649
650 /*
651  * Print a warm fuzzy message
652  */
653 /* ARGSUSED */
654 static Dtype
655 tag_dirproc (callerdat, dir, repos, update_dir, entries)
656     void *callerdat;
657     char *dir;
658     char *repos;
659     char *update_dir;
660     List *entries;
661 {
662     if (!quiet)
663         error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir);
664     return (R_PROCESS);
665 }
666 \f
667 /* Code relating to the val-tags file.  Note that this file has no way
668    of knowing when a tag has been deleted.  The problem is that there
669    is no way of knowing whether a tag still exists somewhere, when we
670    delete it some places.  Using per-directory val-tags files (in
671    CVSREP) might be better, but that might slow down the process of
672    verifying that a tag is correct (maybe not, for the likely cases,
673    if carefully done), and/or be harder to implement correctly.  */
674
675 struct val_args {
676     char *name;
677     int found;
678 };
679
680 static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo));
681
682 static int
683 val_fileproc (callerdat, finfo)
684     void *callerdat;
685     struct file_info *finfo;
686 {
687     RCSNode *rcsdata;
688     struct val_args *args = (struct val_args *)callerdat;
689     char *tag;
690
691     if ((rcsdata = finfo->rcs) == NULL)
692         /* Not sure this can happen, after all we passed only
693            W_REPOS | W_ATTIC.  */
694         return 0;
695
696     tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL);
697     if (tag != NULL)
698     {
699         /* FIXME: should find out a way to stop the search at this point.  */
700         args->found = 1;
701         free (tag);
702     }
703     return 0;
704 }
705
706 static Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *));
707
708 static Dtype
709 val_direntproc (callerdat, dir, repository, update_dir, entries)
710     void *callerdat;
711     char *dir;
712     char *repository;
713     char *update_dir;
714     List *entries;
715 {
716     /* This is not quite right--it doesn't get right the case of "cvs
717        update -d -r foobar" where foobar is a tag which exists only in
718        files in a directory which does not exist yet, but which is
719        about to be created.  */
720     if (isdir (dir))
721         return 0;
722     return R_SKIP_ALL;
723 }
724
725 /* Check to see whether NAME is a valid tag.  If so, return.  If not
726    print an error message and exit.  ARGC, ARGV, LOCAL, and AFLAG specify
727    which files we will be operating on.
728
729    REPOSITORY is the repository if we need to cd into it, or NULL if
730    we are already there, or "" if we should do a W_LOCAL recursion.
731    Sorry for three cases, but the "" case is needed in case the
732    working directories come from diverse parts of the repository, the
733    NULL case avoids an unneccesary chdir, and the non-NULL, non-""
734    case is needed for checkout, where we don't want to chdir if the
735    tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
736    local directory.  */
737 void
738 tag_check_valid (name, argc, argv, local, aflag, repository)
739     char *name;
740     int argc;
741     char **argv;
742     int local;
743     int aflag;
744     char *repository;
745 {
746     DBM *db;
747     char *valtags_filename;
748     int err;
749     datum mytag;
750     struct val_args the_val_args;
751     struct saved_cwd cwd;
752     int which;
753
754     /* Numeric tags require only a syntactic check.  */
755     if (isdigit ((unsigned char) name[0]))
756     {
757         char *p;
758         for (p = name; *p != '\0'; ++p)
759         {
760             if (!(isdigit ((unsigned char) *p) || *p == '.'))
761                 error (1, 0, "\
762 Numeric tag %s contains characters other than digits and '.'", name);
763         }
764         return;
765     }
766
767     /* Special tags are always valid.  */
768     if (strcmp (name, TAG_BASE) == 0
769         || strcmp (name, TAG_HEAD) == 0)
770         return;
771
772     /* FIXME: This routine doesn't seem to do any locking whatsoever
773        (and it is called from places which don't have locks in place).
774        If two processes try to write val-tags at the same time, it would
775        seem like we are in trouble.  */
776
777     mytag.dptr = name;
778     mytag.dsize = strlen (name);
779
780     valtags_filename = xmalloc (strlen (CVSroot_directory)
781                                 + sizeof CVSROOTADM
782                                 + sizeof CVSROOTADM_VALTAGS + 20);
783     strcpy (valtags_filename, CVSroot_directory);
784     strcat (valtags_filename, "/");
785     strcat (valtags_filename, CVSROOTADM);
786     strcat (valtags_filename, "/");
787     strcat (valtags_filename, CVSROOTADM_VALTAGS);
788     db = dbm_open (valtags_filename, O_RDWR, 0666);
789     if (db == NULL)
790     {
791         if (!existence_error (errno))
792             error (1, errno, "cannot read %s", valtags_filename);
793
794         /* If the file merely fails to exist, we just keep going and create
795            it later if need be.  */
796     }
797     else
798     {
799         datum val;
800
801         val = dbm_fetch (db, mytag);
802         if (val.dptr != NULL)
803         {
804             /* Found.  The tag is valid.  */
805             dbm_close (db);
806             free (valtags_filename);
807             return;
808         }
809         /* FIXME: should check errors somehow (add dbm_error to myndbm.c?).  */
810     }
811
812     /* We didn't find the tag in val-tags, so look through all the RCS files
813        to see whether it exists there.  Yes, this is expensive, but there
814        is no other way to cope with a tag which might have been created
815        by an old version of CVS, from before val-tags was invented.
816
817        Since we need this code anyway, we also use it to create
818        entries in val-tags in general (that is, the val-tags entry
819        will get created the first time the tag is used, not when the
820        tag is created).  */
821
822     the_val_args.name = name;
823     the_val_args.found = 0;
824
825     which = W_REPOS | W_ATTIC;
826
827     if (repository != NULL)
828     {
829         if (repository[0] == '\0')
830             which |= W_LOCAL;
831         else
832         {
833             if (save_cwd (&cwd))
834                 error_exit ();
835             if ( CVS_CHDIR (repository) < 0)
836                 error (1, errno, "cannot change to %s directory", repository);
837         }
838     }
839
840     err = start_recursion (val_fileproc, (FILESDONEPROC) NULL,
841                            val_direntproc, (DIRLEAVEPROC) NULL,
842                            (void *)&the_val_args,
843                            argc, argv, local, which, aflag,
844                            1, NULL, 1);
845     if (repository != NULL && repository[0] != '\0')
846     {
847         if (restore_cwd (&cwd, NULL))
848             exit (EXIT_FAILURE);
849         free_cwd (&cwd);
850     }
851
852     if (!the_val_args.found)
853         error (1, 0, "no such tag %s", name);
854     else
855     {
856         /* The tags is valid but not mentioned in val-tags.  Add it.  */
857         datum value;
858
859         if (noexec)
860         {
861             if (db != NULL)
862                 dbm_close (db);
863             free (valtags_filename);
864             return;
865         }
866
867         if (db == NULL)
868         {
869             mode_t omask;
870             omask = umask (cvsumask);
871             db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
872             (void) umask (omask);
873
874             if (db == NULL)
875             {
876                 error (0, errno, "cannot create %s", valtags_filename);
877                 free (valtags_filename);
878                 return;
879             }
880         }
881         value.dptr = "y";
882         value.dsize = 1;
883         if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
884             error (0, errno, "cannot store %s into %s", name,
885                    valtags_filename);
886         dbm_close (db);
887     }
888     free (valtags_filename);
889 }
890
891 /*
892  * Check whether a join tag is valid.  This is just like
893  * tag_check_valid, but we must stop before the colon if there is one.
894  */
895
896 void
897 tag_check_valid_join (join_tag, argc, argv, local, aflag, repository)
898      char *join_tag;
899      int argc;
900      char **argv;
901      int local;
902      int aflag;
903      char *repository;
904 {
905     char *c, *s;
906
907     c = xstrdup (join_tag);
908     s = strchr (c, ':');
909     if (s != NULL)
910     {
911         if (isdigit ((unsigned char) join_tag[0]))
912             error (1, 0,
913                    "Numeric join tag %s may not contain a date specifier",
914                    join_tag);
915
916         *s = '\0';
917         /* hmmm...  I think it makes sense to allow -j:<date>, but
918          * for now this fixes a bug where CVS just spins and spins (I
919          * think in the RCS code) looking for a zero length tag.
920          */
921         if (!*c)
922             error (1, 0,
923                    "argument to join may not contain a date specifier without a tag");
924     }
925
926     tag_check_valid (c, argc, argv, local, aflag, repository);
927
928     free (c);
929 }