Bring cvs-1.12.9 into the CVS repository
[dragonfly.git] / contrib / cvs-1.12.9 / 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 and Rtag
9  *
10  * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
11  * Tag uses the checked out revision in the current directory, rtag uses
12  * the modules database, if necessary.
13  */
14
15 #include "cvs.h"
16 #include "savecwd.h"
17
18 static int rtag_proc (int argc, char **argv, char *xwhere,
19                       char *mwhere, char *mfile, int shorten,
20                       int local_specified, char *mname, char *msg);
21 static int check_fileproc (void *callerdat, struct file_info *finfo);
22 static int check_filesdoneproc (void *callerdat, int err,
23                                 const char *repos, const char *update_dir,
24                                 List *entries);
25 static int pretag_proc (const char *_repository, const char *_filter,
26                         void *_closure);
27 static void masterlist_delproc (Node *_p);
28 static void tag_delproc (Node *_p);
29 static int pretag_list_to_args_proc (Node *_p, void *_closure);
30
31 static Dtype tag_dirproc (void *callerdat, const char *dir,
32                           const char *repos, const char *update_dir,
33                           List *entries);
34 static int rtag_fileproc (void *callerdat, struct file_info *finfo);
35 static int rtag_delete (RCSNode *rcsfile);
36 static int tag_fileproc (void *callerdat, struct file_info *finfo);
37
38 static char *numtag;                    /* specific revision to tag */
39 static bool numtag_validated = false;
40 static char *date = NULL;
41 static char *symtag;                    /* tag to add or delete */
42 static bool delete_flag;                /* adding a tag by default */
43 static bool branch_mode;                /* make an automagic "branch" tag */
44 static bool disturb_branch_tags = false;/* allow -F,-d to disturb branch tags */
45 static bool force_tag_match = true;     /* force tag to match by default */
46 static bool force_tag_move;             /* don't force tag to move by default */
47 static bool check_uptodate;             /* no uptodate-check by default */
48 static bool attic_too;                  /* remove tag from Attic files */
49 static bool is_rtag;
50
51 struct tag_info
52 {
53     Ctype status;
54     char *oldrev;
55     char *rev;
56     char *tag;
57     char *options;
58 };
59
60 struct master_lists
61 {
62     List *tlist;
63 };
64
65 static List *mtlist;
66
67 static const char rtag_opts[] = "+aBbdFflnQqRr:D:";
68 static const char *const rtag_usage[] =
69 {
70     "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n",
71     "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
72     "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
73     "\t-B\tAllows -F and -d to disturb branch tags.  Use with extreme care.\n",
74     "\t-d\tDelete the given tag.\n",
75     "\t-F\tMove tag if it already exists.\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-n\tNo execution of 'tag program'.\n",
79     "\t-R\tProcess directories recursively.\n",
80     "\t-r rev\tExisting revision/tag.\n",
81     "\t-D\tExisting date.\n",
82     "(Specify the --help global option for a list of other help options)\n",
83     NULL
84 };
85
86 static const char tag_opts[] = "+BbcdFflQqRr:D:";
87 static const char *const tag_usage[] =
88 {
89     "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n",
90     "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
91     "\t-B\tAllows -F and -d to disturb branch tags.  Use with extreme care.\n",
92     "\t-c\tCheck that working files are unmodified.\n",
93     "\t-d\tDelete the given tag.\n",
94     "\t-F\tMove tag if it already exists.\n",
95     "\t-f\tForce a head revision match if tag/date not found.\n",
96     "\t-l\tLocal directory only, not recursive.\n",
97     "\t-R\tProcess directories recursively.\n",
98     "\t-r rev\tExisting revision/tag.\n",
99     "\t-D\tExisting date.\n",
100     "(Specify the --help global option for a list of other help options)\n",
101     NULL
102 };
103
104
105
106 int
107 cvstag (int argc, char **argv)
108 {
109     bool local = false;                 /* recursive by default */
110     int c;
111     int err = 0;
112     bool run_module_prog = true;
113
114     is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0);
115
116     if (argc == -1)
117         usage (is_rtag ? rtag_usage : tag_usage);
118
119     optind = 0;
120     while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1)
121     {
122         switch (c)
123         {
124             case 'a':
125                 attic_too = true;
126                 break;
127             case 'b':
128                 branch_mode = true;
129                 break;
130             case 'B':
131                 disturb_branch_tags = true;
132                 break;
133             case 'c':
134                 check_uptodate = true;
135                 break;
136             case 'd':
137                 delete_flag = true;
138                 break;
139             case 'F':
140                 force_tag_move = true;
141                 break;
142             case 'f':
143                 force_tag_match = false;
144                 break;
145             case 'l':
146                 local = true;
147                 break;
148             case 'n':
149                 run_module_prog = false;
150                 break;
151             case 'Q':
152             case 'q':
153 #ifdef SERVER_SUPPORT
154                 /* The CVS 1.5 client sends these options (in addition to
155                    Global_option requests), so we must ignore them.  */
156                 if (!server_active)
157 #endif
158                     error (1, 0,
159                            "-q or -Q must be specified before \"%s\"",
160                            cvs_cmd_name);
161                 break;
162             case 'R':
163                 local = false;
164                 break;
165             case 'r':
166                 numtag = optarg;
167                 break;
168             case 'D':
169                 if (date)
170                     free (date);
171                 date = Make_Date (optarg);
172                 break;
173             case '?':
174             default:
175                 usage (is_rtag ? rtag_usage : tag_usage);
176                 break;
177         }
178     }
179     argc -= optind;
180     argv += optind;
181
182     if (argc < (is_rtag ? 2 : 1))
183         usage (is_rtag ? rtag_usage : tag_usage);
184     symtag = argv[0];
185     argc--;
186     argv++;
187
188     if (date && numtag)
189         error (1, 0, "-r and -D options are mutually exclusive");
190     if (delete_flag && branch_mode)
191         error (0, 0, "warning: -b ignored with -d options");
192     RCS_check_tag (symtag);
193
194 #ifdef CLIENT_SUPPORT
195     if (current_parsed_root->isremote)
196     {
197         /* We're the client side.  Fire up the remote server.  */
198         start_server ();
199         
200         ign_setup ();
201
202         if (attic_too)
203             send_arg ("-a");
204         if (branch_mode)
205             send_arg ("-b");
206         if (disturb_branch_tags)
207             send_arg ("-B");
208         if (check_uptodate)
209             send_arg ("-c");
210         if (delete_flag)
211             send_arg ("-d");
212         if (force_tag_move)
213             send_arg ("-F");
214         if (!force_tag_match)
215             send_arg ("-f");
216         if (local)
217             send_arg ("-l");
218         if (!run_module_prog)
219             send_arg ("-n");
220
221         if (numtag)
222             option_with_arg ("-r", numtag);
223         if (date)
224             client_senddate (date);
225
226         send_arg ("--");
227
228         send_arg (symtag);
229
230         if (is_rtag)
231         {
232             int i;
233             for (i = 0; i < argc; ++i)
234                 send_arg (argv[i]);
235             send_to_server ("rtag\012", 0);
236         }
237         else
238         {
239             send_files (argc, argv, local, 0,
240
241                     /* I think the -c case is like "cvs status", in
242                        which we really better be correct rather than
243                        being fast; it is just too confusing otherwise.  */
244                         check_uptodate ? 0 : SEND_NO_CONTENTS);
245             send_file_names (argc, argv, SEND_EXPAND_WILD);
246             send_to_server ("tag\012", 0);
247         }
248
249         return get_responses_and_close ();
250     }
251 #endif
252
253     if (is_rtag)
254     {
255         DBM *db;
256         int i;
257         db = open_module ();
258         for (i = 0; i < argc; i++)
259         {
260             /* XXX last arg should be repository, but doesn't make sense here */
261             history_write ('T', (delete_flag ? "D" : (numtag ? numtag :
262                            (date ? date : "A"))), symtag, argv[i], "");
263             err += do_module (db, argv[i], TAG,
264                               delete_flag ? "Untagging" : "Tagging",
265                               rtag_proc, NULL, 0, local, run_module_prog,
266                               0, symtag);
267         }
268         close_module (db);
269     }
270     else
271     {
272         err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
273                          NULL);
274     }
275
276     return err;
277 }
278
279
280
281 /*
282  * callback proc for doing the real work of tagging
283  */
284 /* ARGSUSED */
285 static int
286 rtag_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
287            int shorten, int local_specified, char *mname, char *msg)
288 {
289     /* Begin section which is identical to patch_proc--should this
290        be abstracted out somehow?  */
291     char *myargv[2];
292     int err = 0;
293     int which;
294     char *repository;
295     char *where;
296
297 #ifdef HAVE_PRINTF_PTR
298     TRACE (TRACE_FUNCTION,
299            "rtag_proc (argc=%d, argv=%p, xwhere=%s,\n"
300       "                mwhere=%s, mfile=%s, shorten=%d,\n"
301       "                local_specified=%d, mname=%s, msg=%s)",
302             argc, (void *)argv, xwhere ? xwhere : "(null)",
303             mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
304             shorten, local_specified,
305             mname ? mname : "(null)", msg ? msg : "(null)" );
306 #else
307     TRACE (TRACE_FUNCTION,
308            "rtag_proc (argc=%d, argv=%lx, xwhere=%s,\n"
309       "                mwhere=%s, mfile=%s, shorten=%d,\n"
310       "                local_specified=%d, mname=%s, msg=%s )",
311             argc, (unsigned long)argv, xwhere ? xwhere : "(null)",
312             mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
313             shorten, local_specified,
314             mname ? mname : "(null)", msg ? msg : "(null)" );
315 #endif
316
317     if (is_rtag)
318     {
319         repository = xmalloc (strlen (current_parsed_root->directory)
320                               + strlen (argv[0])
321                               + (mfile == NULL ? 0 : strlen (mfile) + 1)
322                               + 2);
323         (void) sprintf (repository, "%s/%s", current_parsed_root->directory,
324                         argv[0]);
325         where = xmalloc (strlen (argv[0])
326                          + (mfile == NULL ? 0 : strlen (mfile) + 1)
327                          + 1);
328         (void) strcpy (where, argv[0]);
329
330         /* If MFILE isn't null, we need to set up to do only part of the
331          * module.
332          */
333         if (mfile != NULL)
334         {
335             char *cp;
336             char *path;
337
338             /* If the portion of the module is a path, put the dir part on
339              * REPOS.
340              */
341             if ((cp = strrchr (mfile, '/')) != NULL)
342             {
343                 *cp = '\0';
344                 (void) strcat (repository, "/");
345                 (void) strcat (repository, mfile);
346                 (void) strcat (where, "/");
347                 (void) strcat (where, mfile);
348                 mfile = cp + 1;
349             }
350
351             /* take care of the rest */
352             path = xmalloc (strlen (repository) + strlen (mfile) + 5);
353             (void) sprintf (path, "%s/%s", repository, mfile);
354             if (isdir (path))
355             {
356                 /* directory means repository gets the dir tacked on */
357                 (void) strcpy (repository, path);
358                 (void) strcat (where, "/");
359                 (void) strcat (where, mfile);
360             }
361             else
362             {
363                 myargv[0] = argv[0];
364                 myargv[1] = mfile;
365                 argc = 2;
366                 argv = myargv;
367             }
368             free (path);
369         }
370
371         /* cd to the starting repository */
372         if (CVS_CHDIR (repository) < 0)
373         {
374             error (0, errno, "cannot chdir to %s", repository);
375             free (repository);
376             return 1;
377         }
378         /* End section which is identical to patch_proc.  */
379
380         if (delete_flag || attic_too || (force_tag_match && numtag))
381             which = W_REPOS | W_ATTIC;
382         else
383             which = W_REPOS;
384     }
385     else
386     {
387         where = NULL;
388         which = W_LOCAL;
389         repository = "";
390     }
391
392     if (numtag != NULL && !numtag_validated)
393     {
394         tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0,
395                          repository );
396         numtag_validated = true;
397     }
398
399     /* check to make sure they are authorized to tag all the
400        specified files in the repository */
401
402     mtlist = getlist();
403     err = start_recursion (check_fileproc, check_filesdoneproc,
404                            NULL, NULL, NULL,
405                            argc - 1, argv + 1, local_specified, which, 0,
406                            CVS_LOCK_READ, where, 1, repository);
407
408     if (err)
409     {
410        error (1, 0, "correct the above errors first!");
411     }
412
413     /* It would be nice to provide consistency with respect to
414        commits; however CVS lacks the infrastructure to do that (see
415        Concurrency in cvs.texinfo and comment in do_recursion).  */
416
417     /* start the recursion processor */
418     err = start_recursion
419         (is_rtag ? rtag_fileproc : tag_fileproc,
420          NULL, tag_dirproc, NULL, NULL, argc - 1, argv + 1,
421          local_specified, which, 0, CVS_LOCK_WRITE, where, 1,
422          repository);
423     dellist (&mtlist);
424     if (which & W_REPOS) free (repository);
425     if (where != NULL)
426         free (where);
427     return err;
428 }
429
430
431
432 /* check file that is to be tagged */
433 /* All we do here is add it to our list */
434 static int
435 check_fileproc (void *callerdat, struct file_info *finfo)
436 {
437     const char *xdir;
438     Node *p;
439     Vers_TS *vers;
440     List *tlist;
441     struct tag_info *ti;
442     int addit = 1;
443
444     TRACE (TRACE_FUNCTION, "check_fileproc (%s, %s, %s)",
445            finfo->repository ? finfo->repository : "(null)",
446            finfo->fullname ? finfo->fullname : "(null)",
447            finfo->rcs ? (finfo->rcs->path ? finfo->rcs->path : "(null)")
448            : "NULL");
449
450     if (check_uptodate)
451     {
452         switch (Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0))
453         {
454         case T_UPTODATE:
455         case T_CHECKOUT:
456         case T_PATCH:
457         case T_REMOVE_ENTRY:
458             break;
459         case T_UNKNOWN:
460         case T_CONFLICT:
461         case T_NEEDS_MERGE:
462         case T_MODIFIED:
463         case T_ADDED:
464         case T_REMOVED:
465         default:
466             error (0, 0, "%s is locally modified", finfo->fullname);
467             freevers_ts (&vers);
468             return 1;
469         }
470     }
471     else
472         vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
473
474     if (finfo->update_dir[0] == '\0')
475         xdir = ".";
476     else
477         xdir = finfo->update_dir;
478     if ((p = findnode (mtlist, xdir)) != NULL)
479     {
480         tlist = ((struct master_lists *) p->data)->tlist;
481     }
482     else
483     {
484         struct master_lists *ml;
485
486         tlist = getlist ();
487         p = getnode ();
488         p->key = xstrdup (xdir);
489         p->type = UPDATE;
490         ml = xmalloc (sizeof (struct master_lists));
491         ml->tlist = tlist;
492         p->data = ml;
493         p->delproc = masterlist_delproc;
494         (void) addnode (mtlist, p);
495     }
496     /* do tlist */
497     p = getnode ();
498     p->key = xstrdup (finfo->file);
499     p->type = UPDATE;
500     p->delproc = tag_delproc;
501     if (vers->srcfile == NULL)
502     {
503         if (!really_quiet)
504             error (0, 0, "nothing known about %s", finfo->file);
505         freevers_ts (&vers);
506         freenode (p);
507         return 1;
508     }
509
510     /* Here we duplicate the calculation in tag_fileproc about which
511        version we are going to tag.  There probably are some subtle races
512        (e.g. numtag is "foo" which gets moved between here and
513        tag_fileproc).  */
514     p->data = ti = xmalloc (sizeof (struct tag_info));
515     if (!is_rtag && numtag == NULL && date == NULL)
516         ti->rev = xstrdup (vers->vn_user);
517     else
518         ti->rev = RCS_getversion (vers->srcfile, numtag, date,
519                                   force_tag_match, NULL);
520
521     if (ti->rev != NULL)
522     {
523         ti->oldrev = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
524
525         if (ti->oldrev == NULL)
526         {
527             if (delete_flag)
528             {
529                 /* Deleting a tag which did not exist is a noop and
530                    should not be logged.  */
531                 addit = 0;
532             }
533         }
534         else if (delete_flag)
535         {
536             free (ti->rev);
537 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
538             /* a hack since %v used to mean old or new rev */
539             ti->rev = xstrdup (ti->oldrev);
540 #else /* SUPPORT_OLD_INFO_FMT_STRINGS */
541             ti->rev = NULL;
542 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
543         }
544         else if (strcmp(ti->oldrev, p->data) == 0)
545             addit = 0;
546         else if (!force_tag_move)
547             addit = 0;
548     }
549     else
550         addit = 0;
551     if (!addit)
552     {
553         free(p->data);
554         p->data = NULL;
555     }
556     freevers_ts (&vers);
557     (void)addnode (tlist, p);
558     return 0;
559 }
560
561
562
563 struct pretag_proc_data {
564      List *tlist;
565      bool delete_flag;
566      bool force_tag_move;
567      char *symtag;
568 };
569
570 static int
571 check_filesdoneproc (void *callerdat, int err, const char *repos,
572                      const char *update_dir, List *entries)
573 {
574     int n;
575     Node *p;
576     List *tlist;
577     struct pretag_proc_data ppd;
578
579     p = findnode(mtlist, update_dir);
580     if (p != NULL)
581     {
582         tlist = ((struct master_lists *) p->data)->tlist;
583     }
584     else
585     {
586         tlist = (List *) NULL;
587     }
588     if ((tlist == NULL) || (tlist->list->next == tlist->list))
589     {
590         return (err);
591     }
592
593     ppd.tlist = tlist;
594     ppd.delete_flag = delete_flag;
595     ppd.force_tag_move = force_tag_move;
596     ppd.symtag = symtag;
597     if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, PIOPT_ALL, &ppd)) > 0)
598     {
599         error (0, 0, "Pre-tag check failed");
600         err += n;
601     }
602     return err;
603 }
604
605
606
607 /*
608  * called from Parse_Info, this routine processes a line that came out
609  * of a taginfo file and turns it into a command and executes it.
610  *
611  * RETURNS
612  *    the absolute value of the return value of run_exec, which may or
613  *    may not be the return value of the child process.  this is
614  *    contrained to return positive values because Parse_Info is adding up
615  *    return values and testing for non-zeroness to signify one or more
616  *    of its callbacks having returned an error.
617  */
618 static int
619 pretag_proc (const char *repository, const char *filter, void *closure)
620 {
621     char *newfilter = NULL;
622     char *cmdline;
623     const char *srepos = Short_Repository (repository);
624     struct pretag_proc_data *ppd = (struct pretag_proc_data *)closure;
625
626 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
627     if (!strchr(filter, '%'))
628     {
629         error(0,0,
630               "warning: taginfo line contains no format strings:\n"
631               "    \"%s\"\n"
632               "Filling in old defaults ('%%t %%o %%p %%{sv}'), but please be aware that this\n"
633               "usage is deprecated.", filter);
634         newfilter = xmalloc (strlen(filter) + 16);
635         strcpy (newfilter, filter);
636         strcat (newfilter, " %t %o %p %{sv}");
637         filter = newfilter;
638     }
639 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
640
641     /* %t = tag being added/moved/removed
642      * %o = operation = "add" | "mov" | "del"
643      * %b = branch mode = "?" (delete ops - unknown) | "T" (branch) | "N" (not branch)
644      * %p = path from $CVSROOT
645      * %r = path from root
646      * %{sVv} = attribute list = file name, old version tag will be deleted from,
647      *             new version tag will be added to (or deleted from until
648      *             SUPPORT_OLD_INFO_FMT_STRINGS is undefined)
649      */
650     cmdline = format_cmdline(
651 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
652         0, srepos,
653 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
654         filter,
655         "t", "s", ppd->symtag,
656         "o", "s", ppd->delete_flag ? "del" :
657                   ppd->force_tag_move ? "mov" : "add",
658         "b", "c", delete_flag ? '?' : branch_mode ? 'T' : 'N',
659         "p", "s", srepos,
660         "r", "s", current_parsed_root->directory,
661         "sVv", ",", ppd->tlist, pretag_list_to_args_proc, (void *) NULL,
662         (char *)NULL
663         );
664
665     if (newfilter) free (newfilter);
666
667     if (!cmdline || !strlen (cmdline))
668     {
669         if (cmdline) free (cmdline);
670         error(0, 0, "pretag proc resolved to the empty string!");
671         return 1;
672     }
673
674     run_setup (cmdline);
675
676     /* FIXME - the old code used to run the following here:
677      *
678      * if (!isfile(s))
679      * {
680      *     error (0, errno, "cannot find pre-tag filter '%s'", s);
681      *     free(s);
682      *     return (1);
683      * }
684      *
685      * not sure this is really necessary.  it might give a little finer grained
686      * error than letting the execution attempt fail but i'm not sure.  in any
687      * case it should be easy enough to add a function in run.c to test its
688      * first arg for fileness & executability.
689      */
690
691     free (cmdline);
692     return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
693 }
694
695 static void
696 masterlist_delproc(Node *p)
697 {
698     struct master_lists *ml = p->data;
699
700     dellist(&ml->tlist);
701     free(ml);
702     return;
703 }
704
705 static void
706 tag_delproc(Node *p)
707 {
708     struct tag_info *ti;
709     if (p->data != NULL)
710     {
711         ti = (struct tag_info *) p->data;
712         if (ti->oldrev) free (ti->oldrev);
713         if (ti->rev) free (ti->rev);
714         free(p->data);
715         p->data = NULL;
716     }
717     return;
718 }
719
720 /* to be passed into walklist with a list of tags
721  * p->key = tagname
722  * p->data = struct tag_info *
723  * p->data->oldrev = rev tag will be deleted from
724  * p->data->rev = rev tag will be added to
725  *
726  * closure will be a struct format_cmdline_walklist_closure
727  * where closure is undefined
728  */
729 static int
730 pretag_list_to_args_proc(Node *p, void *closure)
731 {
732     struct tag_info *taginfo = (struct tag_info *)p->data;
733     struct format_cmdline_walklist_closure *c =
734             (struct format_cmdline_walklist_closure *)closure;
735     char *arg = NULL;
736     const char *f;
737     char *d;
738     size_t doff;
739
740     if (p->data == NULL) return 1;
741
742     f = c->format;
743     d = *c->d;
744     /* foreach requested attribute */
745     while (*f)
746     {
747         switch (*f++)
748         {
749             case 's':
750                 arg = p->key;
751                 break;
752             case 'v':
753                 arg = taginfo->rev ? taginfo->rev : "NONE";
754                 break;
755             case 'V':
756                 arg = taginfo->oldrev ? taginfo->oldrev : "NONE";
757                 break;
758             default:
759                 error(1,0,
760                       "Unknown format character or not a list attribute: %c",
761                       f[-1]);
762                 break;
763         }
764         /* copy the attribute into an argument */
765         if (c->quotes)
766         {
767             arg = cmdlineescape (c->quotes, arg);
768         }
769         else
770         {
771             arg = cmdlinequote ('"', arg);
772         }
773
774         doff = d - *c->buf;
775         expand_string (c->buf, c->length, doff + strlen (arg));
776         d = *c->buf + doff;
777         strncpy (d, arg, strlen (arg));
778         d += strlen (arg);
779
780         free (arg);
781
782         /* and always put the extra space on.  we'll have to back up a char when we're
783          * done, but that seems most efficient
784          */
785         doff = d - *c->buf;
786         expand_string (c->buf, c->length, doff + 1);
787         d = *c->buf + doff;
788         *d++ = ' ';
789     }
790     /* correct our original pointer into the buff */
791     *c->d = d;
792     return 0;
793 }
794
795
796 /*
797  * Called to rtag a particular file, as appropriate with the options that were
798  * set above.
799  */
800 /* ARGSUSED */
801 static int
802 rtag_fileproc (void *callerdat, struct file_info *finfo)
803 {
804     RCSNode *rcsfile;
805     char *version, *rev;
806     int retcode = 0;
807
808     /* find the parsed RCS data */
809     if ((rcsfile = finfo->rcs) == NULL)
810         return 1;
811
812     /*
813      * For tagging an RCS file which is a symbolic link, you'd best be
814      * running with RCS 5.6, since it knows how to handle symbolic links
815      * correctly without breaking your link!
816      */
817
818     if (delete_flag)
819         return rtag_delete (rcsfile);
820
821     /*
822      * If we get here, we are adding a tag.  But, if -a was specified, we
823      * need to check to see if a -r or -D option was specified.  If neither
824      * was specified and the file is in the Attic, remove the tag.
825      */
826     if (attic_too && (!numtag && !date))
827     {
828         if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
829             return rtag_delete (rcsfile);
830     }
831
832     version = RCS_getversion (rcsfile, numtag, date, force_tag_match, NULL);
833     if (version == NULL)
834     {
835         /* If -a specified, clean up any old tags */
836         if (attic_too)
837             (void)rtag_delete (rcsfile);
838
839         if (!quiet && !force_tag_match)
840         {
841             error (0, 0, "cannot find tag `%s' in `%s'",
842                    numtag ? numtag : "head", rcsfile->path);
843             return 1;
844         }
845         return 0;
846     }
847     if (numtag
848         && isdigit ((unsigned char)*numtag)
849         && strcmp (numtag, version) != 0)
850     {
851
852         /*
853          * We didn't find a match for the numeric tag that was specified, but
854          * that's OK.  just pass the numeric tag on to rcs, to be tagged as
855          * specified.  Could get here if one tried to tag "1.1.1" and there
856          * was a 1.1.1 branch with some head revision.  In this case, we want
857          * the tag to reference "1.1.1" and not the revision at the head of
858          * the branch.  Use a symbolic tag for that.
859          */
860         rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
861         retcode = RCS_settag(rcsfile, symtag, numtag);
862         if (retcode == 0)
863             RCS_rewrite (rcsfile, NULL, NULL);
864     }
865     else
866     {
867         char *oversion;
868
869         /*
870          * As an enhancement for the case where a tag is being re-applied to
871          * a large body of a module, make one extra call to RCS_getversion to
872          * see if the tag is already set in the RCS file.  If so, check to
873          * see if it needs to be moved.  If not, do nothing.  This will
874          * likely save a lot of time when simply moving the tag to the
875          * "current" head revisions of a module -- which I have found to be a
876          * typical tagging operation.
877          */
878         rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
879         oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1,
880                                    (int *) NULL);
881         if (oversion != NULL)
882         {
883             int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
884
885             /*
886              * if versions the same and neither old or new are branches don't
887              * have to do anything
888              */
889             if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
890             {
891                 free (oversion);
892                 free (version);
893                 return (0);
894             }
895
896             if (!force_tag_move)
897             {
898                 /* we're NOT going to move the tag */
899                 (void)printf ("W %s", finfo->fullname);
900
901                 (void)printf (" : %s already exists on %s %s",
902                               symtag, isbranch ? "branch" : "version",
903                               oversion);
904                 (void)printf (" : NOT MOVING tag to %s %s\n",
905                               branch_mode ? "branch" : "version", rev);
906                 free (oversion);
907                 free (version);
908                 if (branch_mode) free (rev);
909                 return 0;
910             }
911             else /* force_tag_move is set and... */
912                 if ((isbranch && !disturb_branch_tags) ||
913                     (!isbranch && disturb_branch_tags))
914             {
915                 error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
916                         finfo->fullname,
917                         isbranch ? "branch" : "non-branch",
918                         symtag, oversion, rev,
919                         isbranch ? "" : " due to `-B' option");
920                 if (branch_mode) free(rev);
921                 free (oversion);
922                 free (version);
923                 return 0;
924             }
925             free (oversion);
926         }
927         retcode = RCS_settag(rcsfile, symtag, rev);
928         if (retcode == 0)
929             RCS_rewrite (rcsfile, NULL, NULL);
930     }
931
932     if (retcode != 0)
933     {
934         error (1, retcode == -1 ? errno : 0,
935                "failed to set tag `%s' to revision `%s' in `%s'",
936                symtag, rev, rcsfile->path);
937         if (branch_mode)
938             free (rev);
939         free (version);
940         return 1;
941     }
942     if (branch_mode)
943         free (rev);
944     free (version);
945     return 0;
946 }
947
948
949
950 /*
951  * If -d is specified, "force_tag_match" is set, so that this call to
952  * RCS_getversion() will return a NULL version string if the symbolic
953  * tag does not exist in the RCS file.
954  *
955  * If the -r flag was used, numtag is set, and we only delete the
956  * symtag from files that have numtag.
957  *
958  * This is done here because it's MUCH faster than just blindly calling
959  * "rcs" to remove the tag... trust me.
960  */
961 static int
962 rtag_delete (RCSNode *rcsfile)
963 {
964     char *version;
965     int retcode, isbranch;
966
967     if (numtag)
968     {
969         version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1,
970                                   (int *) NULL);
971         if (version == NULL)
972             return (0);
973         free (version);
974     }
975
976     version = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
977     if (version == NULL)
978         return 0;
979     free (version);
980
981
982     isbranch = RCS_nodeisbranch (rcsfile, symtag);
983     if ((isbranch && !disturb_branch_tags) ||
984         (!isbranch && disturb_branch_tags))
985     {
986         if (!quiet)
987             error (0, 0,
988                    "Not removing %s tag `%s' from `%s'%s.",
989                    isbranch ? "branch" : "non-branch",
990                    symtag, rcsfile->path,
991                    isbranch ? "" : " due to `-B' option");
992         return 1;
993     }
994
995     if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
996     {
997         if (!quiet)
998             error (0, retcode == -1 ? errno : 0,
999                    "failed to remove tag `%s' from `%s'", symtag,
1000                    rcsfile->path);
1001         return 1;
1002     }
1003     RCS_rewrite (rcsfile, NULL, NULL);
1004     return 0;
1005 }
1006
1007
1008
1009 /*
1010  * Called to tag a particular file (the currently checked out version is
1011  * tagged with the specified tag - or the specified tag is deleted).
1012  */
1013 /* ARGSUSED */
1014 static int
1015 tag_fileproc (void *callerdat, struct file_info *finfo)
1016 {
1017     char *version, *oversion;
1018     char *nversion = NULL;
1019     char *rev;
1020     Vers_TS *vers;
1021     int retcode = 0;
1022     int retval = 0;
1023
1024     vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
1025
1026     if ((numtag != NULL) || (date != NULL))
1027     {
1028         nversion = RCS_getversion(vers->srcfile,
1029                                   numtag,
1030                                   date,
1031                                   force_tag_match,
1032                                   NULL);
1033         if (nversion == NULL)
1034             goto free_vars_and_return;
1035     }
1036     if (delete_flag)
1037     {
1038
1039         int isbranch;
1040         /*
1041          * If -d is specified, "force_tag_match" is set, so that this call to
1042          * RCS_getversion() will return a NULL version string if the symbolic
1043          * tag does not exist in the RCS file.
1044          *
1045          * This is done here because it's MUCH faster than just blindly calling
1046          * "rcs" to remove the tag... trust me.
1047          */
1048
1049         version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
1050                                   (int *) NULL);
1051         if (version == NULL || vers->srcfile == NULL)
1052             goto free_vars_and_return;
1053
1054         free (version);
1055
1056         isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1057         if ((isbranch && !disturb_branch_tags) ||
1058             (!isbranch && disturb_branch_tags))
1059         {
1060             if (!quiet)
1061                 error(0, 0,
1062                        "Not removing %s tag `%s' from `%s'%s.",
1063                         isbranch ? "branch" : "non-branch",
1064                         symtag, vers->srcfile->path,
1065                         isbranch ? "" : " due to `-B' option");
1066             retval = 1;
1067             goto free_vars_and_return;
1068         }
1069
1070         if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0)
1071         {
1072             if (!quiet)
1073                 error (0, retcode == -1 ? errno : 0,
1074                        "failed to remove tag %s from %s", symtag,
1075                        vers->srcfile->path);
1076             retval = 1;
1077             goto free_vars_and_return;
1078         }
1079         RCS_rewrite (vers->srcfile, NULL, NULL);
1080
1081         /* warm fuzzies */
1082         if (!really_quiet)
1083         {
1084             cvs_output ("D ", 2);
1085             cvs_output (finfo->fullname, 0);
1086             cvs_output ("\n", 1);
1087         }
1088
1089         goto free_vars_and_return;
1090     }
1091
1092     /*
1093      * If we are adding a tag, we need to know which version we have checked
1094      * out and we'll tag that version.
1095      */
1096     if (nversion == NULL)
1097     {
1098         version = vers->vn_user;
1099     }
1100     else
1101     {
1102         version = nversion;
1103     }
1104     if (version == NULL)
1105     {
1106         goto free_vars_and_return;
1107     }
1108     else if (strcmp (version, "0") == 0)
1109     {
1110         if (!quiet)
1111             error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file);
1112         goto free_vars_and_return;
1113     }
1114     else if (version[0] == '-')
1115     {
1116         if (!quiet)
1117             error (0, 0, "skipping removed but un-commited file `%s'", finfo->file);
1118         goto free_vars_and_return;
1119     }
1120     else if (vers->srcfile == NULL)
1121     {
1122         if (!quiet)
1123             error (0, 0, "cannot find revision control file for `%s'", finfo->file);
1124         goto free_vars_and_return;
1125     }
1126
1127     /*
1128      * As an enhancement for the case where a tag is being re-applied to a
1129      * large number of files, make one extra call to RCS_getversion to see
1130      * if the tag is already set in the RCS file.  If so, check to see if it
1131      * needs to be moved.  If not, do nothing.  This will likely save a lot of
1132      * time when simply moving the tag to the "current" head revisions of a
1133      * module -- which I have found to be a typical tagging operation.
1134      */
1135     rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
1136     oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
1137                                (int *) NULL);
1138     if (oversion != NULL)
1139     {
1140         int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1141
1142         /*
1143          * if versions the same and neither old or new are branches don't have
1144          * to do anything
1145          */
1146         if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
1147         {
1148             free (oversion);
1149             if (branch_mode)
1150                 free (rev);
1151             goto free_vars_and_return;
1152         }
1153
1154         if (!force_tag_move)
1155         {
1156             /* we're NOT going to move the tag */
1157             cvs_output ("W ", 2);
1158             cvs_output (finfo->fullname, 0);
1159             cvs_output (" : ", 0);
1160             cvs_output (symtag, 0);
1161             cvs_output (" already exists on ", 0);
1162             cvs_output (isbranch ? "branch" : "version", 0);
1163             cvs_output (" ", 0);
1164             cvs_output (oversion, 0);
1165             cvs_output (" : NOT MOVING tag to ", 0);
1166             cvs_output (branch_mode ? "branch" : "version", 0);
1167             cvs_output (" ", 0);
1168             cvs_output (rev, 0);
1169             cvs_output ("\n", 1);
1170             free (oversion);
1171             if (branch_mode)
1172                 free (rev);
1173             goto free_vars_and_return;
1174         }
1175         else    /* force_tag_move == 1 and... */
1176                 if ((isbranch && !disturb_branch_tags) ||
1177                     (!isbranch && disturb_branch_tags))
1178         {
1179             error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
1180                     finfo->fullname,
1181                     isbranch ? "branch" : "non-branch",
1182                     symtag, oversion, rev,
1183                     isbranch ? "" : " due to `-B' option");
1184             free (oversion);
1185             if (branch_mode)
1186                 free (rev);
1187             goto free_vars_and_return;
1188         }
1189         free (oversion);
1190     }
1191
1192     if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
1193     {
1194         error (1, retcode == -1 ? errno : 0,
1195                "failed to set tag %s to revision %s in %s",
1196                symtag, rev, vers->srcfile->path);
1197         if (branch_mode)
1198             free (rev);
1199         retval = 1;
1200         goto free_vars_and_return;
1201     }
1202     if (branch_mode)
1203         free (rev);
1204     RCS_rewrite (vers->srcfile, NULL, NULL);
1205
1206     /* more warm fuzzies */
1207     if (!really_quiet)
1208     {
1209         cvs_output ("T ", 2);
1210         cvs_output (finfo->fullname, 0);
1211         cvs_output ("\n", 1);
1212     }
1213
1214  free_vars_and_return:
1215     if (nversion != NULL)
1216         free (nversion);
1217     freevers_ts (&vers);
1218     return (retval);
1219 }
1220
1221
1222
1223 /*
1224  * Print a warm fuzzy message
1225  */
1226 /* ARGSUSED */
1227 static Dtype
1228 tag_dirproc (void *callerdat, const char *dir, const char *repos,
1229              const char *update_dir, List *entries)
1230 {
1231
1232     if (ignore_directory (update_dir))
1233     {
1234         /* print the warm fuzzy message */
1235         if (!quiet)
1236           error (0, 0, "Ignoring %s", update_dir);
1237         return R_SKIP_ALL;
1238     }
1239
1240     if (!quiet)
1241         error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging",
1242                update_dir);
1243     return R_PROCESS;
1244 }
1245
1246
1247
1248 /* Code relating to the val-tags file.  Note that this file has no way
1249    of knowing when a tag has been deleted.  The problem is that there
1250    is no way of knowing whether a tag still exists somewhere, when we
1251    delete it some places.  Using per-directory val-tags files (in
1252    CVSREP) might be better, but that might slow down the process of
1253    verifying that a tag is correct (maybe not, for the likely cases,
1254    if carefully done), and/or be harder to implement correctly.  */
1255
1256 struct val_args {
1257     char *name;
1258     int found;
1259 };
1260
1261 static int val_fileproc (void *callerdat, struct file_info *finfo);
1262
1263 static int
1264 val_fileproc (void *callerdat, struct file_info *finfo)
1265 {
1266     RCSNode *rcsdata;
1267     struct val_args *args = (struct val_args *)callerdat;
1268     char *tag;
1269
1270     if ((rcsdata = finfo->rcs) == NULL)
1271         /* Not sure this can happen, after all we passed only
1272            W_REPOS | W_ATTIC.  */
1273         return 0;
1274
1275     tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL);
1276     if (tag != NULL)
1277     {
1278         /* FIXME: should find out a way to stop the search at this point.  */
1279         args->found = 1;
1280         free (tag);
1281     }
1282     return 0;
1283 }
1284
1285
1286
1287 static Dtype
1288 val_direntproc (void *callerdat, const char *dir, const char *repository,
1289                 const char *update_dir, List *entries)
1290 {
1291     /* This is not quite right--it doesn't get right the case of "cvs
1292        update -d -r foobar" where foobar is a tag which exists only in
1293        files in a directory which does not exist yet, but which is
1294        about to be created.  */
1295     if (isdir (dir))
1296         return R_PROCESS;
1297     return R_SKIP_ALL;
1298 }
1299
1300 /* Check to see whether NAME is a valid tag.  If so, return.  If not
1301    print an error message and exit.  ARGC, ARGV, LOCAL, and AFLAG specify
1302    which files we will be operating on.
1303
1304    REPOSITORY is the repository if we need to cd into it, or NULL if
1305    we are already there, or "" if we should do a W_LOCAL recursion.
1306    Sorry for three cases, but the "" case is needed in case the
1307    working directories come from diverse parts of the repository, the
1308    NULL case avoids an unneccesary chdir, and the non-NULL, non-""
1309    case is needed for checkout, where we don't want to chdir if the
1310    tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
1311    local directory.  */
1312 void
1313 tag_check_valid (char *name, int argc, char **argv, int local, int aflag,
1314                  char *repository)
1315 {
1316     DBM *db;
1317     char *valtags_filename;
1318     int nowrite = 0;
1319     datum mytag;
1320     struct val_args the_val_args;
1321     struct saved_cwd cwd;
1322     int which;
1323
1324 #ifdef HAVE_PRINTF_PTR
1325     TRACE ( TRACE_FUNCTION,
1326             "tag_check_valid ( name=%s, argc=%d, argv=%p, local=%d,\n"
1327        "                       aflag=%d, repository=%s )",
1328             name ? name : "(name)", argc, (void *)argv, local, aflag,
1329             repository ? repository : "(null)" );
1330 #else
1331     TRACE ( TRACE_FUNCTION,
1332             "tag_check_valid ( name=%s, argc=%d, argv=%lx, local=%d,\n"
1333        "                       aflag=%d, repository=%s )",
1334             name ? name : "(name)", argc, (unsigned long)argv, local, aflag,
1335             repository ? repository : "(null)" );
1336 #endif
1337
1338     /* Numeric tags require only a syntactic check.  */
1339     if (isdigit ((unsigned char) name[0]))
1340     {
1341         char *p;
1342         for (p = name; *p != '\0'; ++p)
1343         {
1344             if (!(isdigit ((unsigned char) *p) || *p == '.'))
1345                 error (1, 0, "\
1346 Numeric tag %s contains characters other than digits and '.'", name);
1347         }
1348         return;
1349     }
1350
1351     /* Special tags are always valid.  */
1352     if (strcmp (name, TAG_BASE) == 0
1353         || strcmp (name, TAG_HEAD) == 0)
1354         return;
1355
1356     /* FIXME: This routine doesn't seem to do any locking whatsoever
1357        (and it is called from places which don't have locks in place).
1358        If two processes try to write val-tags at the same time, it would
1359        seem like we are in trouble.  */
1360
1361     mytag.dptr = name;
1362     mytag.dsize = strlen (name);
1363
1364     valtags_filename = xmalloc (strlen (current_parsed_root->directory)
1365                                 + sizeof CVSROOTADM
1366                                 + sizeof CVSROOTADM_VALTAGS + 3);
1367     sprintf (valtags_filename, "%s/%s/%s", current_parsed_root->directory,
1368                                            CVSROOTADM, CVSROOTADM_VALTAGS);
1369     db = dbm_open (valtags_filename, O_RDWR, 0666);
1370     if (db == NULL)
1371     {
1372         if (!existence_error (errno))
1373         {
1374             error (0, errno, "warning: cannot open %s read/write",
1375                    valtags_filename);
1376             db = dbm_open (valtags_filename, O_RDONLY, 0666);
1377             if (db != NULL)
1378                 nowrite = 1;
1379             else if (!existence_error (errno))
1380                 error (1, errno, "cannot read %s", valtags_filename);
1381         }
1382         /* If the file merely fails to exist, we just keep going and create
1383            it later if need be.  */
1384     }
1385     if (db != NULL)
1386     {
1387         datum val;
1388
1389         val = dbm_fetch (db, mytag);
1390         if (val.dptr != NULL)
1391         {
1392             /* Found.  The tag is valid.  */
1393             dbm_close (db);
1394             free (valtags_filename);
1395             return;
1396         }
1397         /* FIXME: should check errors somehow (add dbm_error to myndbm.c?).  */
1398     }
1399
1400     /* We didn't find the tag in val-tags, so look through all the RCS files
1401        to see whether it exists there.  Yes, this is expensive, but there
1402        is no other way to cope with a tag which might have been created
1403        by an old version of CVS, from before val-tags was invented.
1404
1405        Since we need this code anyway, we also use it to create
1406        entries in val-tags in general (that is, the val-tags entry
1407        will get created the first time the tag is used, not when the
1408        tag is created).  */
1409
1410     the_val_args.name = name;
1411     the_val_args.found = 0;
1412
1413     which = W_REPOS | W_ATTIC;
1414
1415     if (repository == NULL || repository[0] == '\0')
1416         which |= W_LOCAL;
1417     else
1418     {
1419         if (save_cwd (&cwd))
1420             exit (EXIT_FAILURE);
1421         if (CVS_CHDIR (repository) < 0)
1422             error (1, errno, "cannot change to %s directory", repository);
1423     }
1424
1425     start_recursion
1426         (val_fileproc, NULL, val_direntproc, NULL,
1427          &the_val_args, argc, argv, local, which, aflag,
1428          CVS_LOCK_READ, NULL, 1, repository);
1429     if (repository != NULL && repository[0] != '\0')
1430     {
1431         if (restore_cwd (&cwd, NULL))
1432             exit (EXIT_FAILURE);
1433         free_cwd (&cwd);
1434     }
1435
1436     if (!the_val_args.found)
1437         error (1, 0, "no such tag %s", name);
1438     else
1439     {
1440         /* The tags is valid but not mentioned in val-tags.  Add it.  */
1441         datum value;
1442
1443         if (noexec || nowrite)
1444         {
1445             if (db != NULL)
1446                 dbm_close (db);
1447             free (valtags_filename);
1448             return;
1449         }
1450
1451         if (db == NULL)
1452         {
1453             mode_t omask;
1454             omask = umask (cvsumask);
1455             db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
1456             (void)umask (omask);
1457
1458             if (db == NULL)
1459             {
1460                 error (0, errno, "warning: cannot create %s", valtags_filename);
1461                 free (valtags_filename);
1462                 return;
1463             }
1464         }
1465         value.dptr = "y";
1466         value.dsize = 1;
1467         if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
1468             error (0, errno, "cannot store %s into %s", name,
1469                    valtags_filename);
1470         dbm_close (db);
1471     }
1472     free (valtags_filename);
1473 }
1474
1475 /*
1476  * Check whether a join tag is valid.  This is just like
1477  * tag_check_valid, but we must stop before the colon if there is one.
1478  */
1479
1480 void
1481 tag_check_valid_join (char *join_tag, int argc, char **argv, int local, int aflag, char *repository)
1482 {
1483     char *c, *s;
1484
1485     c = xstrdup (join_tag);
1486     s = strchr (c, ':');
1487     if (s != NULL)
1488     {
1489         if (isdigit ((unsigned char) join_tag[0]))
1490             error (1, 0,
1491                    "Numeric join tag %s may not contain a date specifier",
1492                    join_tag);
1493
1494         *s = '\0';
1495         /* hmmm...  I think it makes sense to allow -j:<date>, but
1496          * for now this fixes a bug where CVS just spins and spins (I
1497          * think in the RCS code) looking for a zero length tag.
1498          */
1499         if (!*c)
1500             error (1, 0,
1501                    "argument to join may not contain a date specifier without a tag");
1502     }
1503
1504     tag_check_valid (c, argc, argv, local, aflag, repository);
1505
1506     free (c);
1507 }