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