AMD64 - Fix format conversions and other warnings.
[dragonfly.git] / contrib / cvs-1.12 / src / tag.c
blobcb138561f631fa95e87faeef57791e9733cc39d8
1 /*
2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5 * and others.
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
9 *
10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution.
13 * Tag and Rtag
15 * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
16 * Tag uses the checked out revision in the current directory, rtag uses
17 * the modules database, if necessary.
20 #include "cvs.h"
21 #include "save-cwd.h"
23 static int rtag_proc (int argc, char **argv, char *xwhere,
24 char *mwhere, char *mfile, int shorten,
25 int local_specified, char *mname, char *msg);
26 static int check_fileproc (void *callerdat, struct file_info *finfo);
27 static int check_filesdoneproc (void *callerdat, int err,
28 const char *repos, const char *update_dir,
29 List *entries);
30 static int pretag_proc (const char *_repository, const char *_filter,
31 void *_closure);
32 static void masterlist_delproc (Node *_p);
33 static void tag_delproc (Node *_p);
34 static int pretag_list_to_args_proc (Node *_p, void *_closure);
36 static Dtype tag_dirproc (void *callerdat, const char *dir,
37 const char *repos, const char *update_dir,
38 List *entries);
39 static int rtag_fileproc (void *callerdat, struct file_info *finfo);
40 static int rtag_delete (RCSNode *rcsfile);
41 static int tag_fileproc (void *callerdat, struct file_info *finfo);
43 static char *numtag; /* specific revision to tag */
44 static bool numtag_validated = false;
45 static char *date = NULL;
46 static char *symtag; /* tag to add or delete */
47 static bool delete_flag; /* adding a tag by default */
48 static bool branch_mode; /* make an automagic "branch" tag */
49 static bool disturb_branch_tags = false;/* allow -F,-d to disturb branch tags */
50 static bool force_tag_match = true; /* force tag to match by default */
51 static bool force_tag_move; /* don't force tag to move by default */
52 static bool check_uptodate; /* no uptodate-check by default */
53 static bool attic_too; /* remove tag from Attic files */
54 static bool is_rtag;
56 struct tag_info
58 Ctype status;
59 char *oldrev;
60 char *rev;
61 char *tag;
62 char *options;
65 struct master_lists
67 List *tlist;
70 static List *mtlist;
72 static const char rtag_opts[] = "+aBbdFflnQqRr:D:";
73 static const char *const rtag_usage[] =
75 "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n",
76 "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
77 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
78 "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
79 "\t-d\tDelete the given tag.\n",
80 "\t-F\tMove tag if it already exists.\n",
81 "\t-f\tForce a head revision match if tag/date not found.\n",
82 "\t-l\tLocal directory only, not recursive.\n",
83 "\t-n\tNo execution of 'tag program'.\n",
84 "\t-R\tProcess directories recursively.\n",
85 "\t-r rev\tExisting revision/tag.\n",
86 "\t-D\tExisting date.\n",
87 "(Specify the --help global option for a list of other help options)\n",
88 NULL
91 static const char tag_opts[] = "+BbcdFflQqRr:D:";
92 static const char *const tag_usage[] =
94 "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n",
95 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
96 "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
97 "\t-c\tCheck that working files are unmodified.\n",
98 "\t-d\tDelete the given tag.\n",
99 "\t-F\tMove tag if it already exists.\n",
100 "\t-f\tForce a head revision match if tag/date not found.\n",
101 "\t-l\tLocal directory only, not recursive.\n",
102 "\t-R\tProcess directories recursively.\n",
103 "\t-r rev\tExisting revision/tag.\n",
104 "\t-D\tExisting date.\n",
105 "(Specify the --help global option for a list of other help options)\n",
106 NULL
112 cvstag (int argc, char **argv)
114 bool local = false; /* recursive by default */
115 int c;
116 int err = 0;
117 bool run_module_prog = true;
119 is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0);
121 if (argc == -1)
122 usage (is_rtag ? rtag_usage : tag_usage);
124 optind = 0;
125 while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1)
127 switch (c)
129 case 'a':
130 attic_too = true;
131 break;
132 case 'b':
133 branch_mode = true;
134 break;
135 case 'B':
136 disturb_branch_tags = true;
137 break;
138 case 'c':
139 check_uptodate = true;
140 break;
141 case 'd':
142 delete_flag = true;
143 break;
144 case 'F':
145 force_tag_move = true;
146 break;
147 case 'f':
148 force_tag_match = false;
149 break;
150 case 'l':
151 local = true;
152 break;
153 case 'n':
154 run_module_prog = false;
155 break;
156 case 'Q':
157 case 'q':
158 /* The CVS 1.5 client sends these options (in addition to
159 Global_option requests), so we must ignore them. */
160 if (!server_active)
161 error (1, 0,
162 "-q or -Q must be specified before \"%s\"",
163 cvs_cmd_name);
164 break;
165 case 'R':
166 local = false;
167 break;
168 case 'r':
169 parse_tagdate (&numtag, &date, optarg);
170 break;
171 case 'D':
172 if (date) free (date);
173 date = Make_Date (optarg);
174 break;
175 case '?':
176 default:
177 usage (is_rtag ? rtag_usage : tag_usage);
178 break;
181 argc -= optind;
182 argv += optind;
184 if (argc < (is_rtag ? 2 : 1))
185 usage (is_rtag ? rtag_usage : tag_usage);
186 symtag = argv[0];
187 argc--;
188 argv++;
190 if (date && delete_flag)
191 error (1, 0, "-d makes no sense with a date specification.");
192 if (delete_flag && branch_mode)
193 error (0, 0, "warning: -b ignored with -d options");
194 RCS_check_tag (symtag);
196 #ifdef CLIENT_SUPPORT
197 if (current_parsed_root->isremote)
199 /* We're the client side. Fire up the remote server. */
200 start_server ();
202 ign_setup ();
204 if (attic_too)
205 send_arg ("-a");
206 if (branch_mode)
207 send_arg ("-b");
208 if (disturb_branch_tags)
209 send_arg ("-B");
210 if (check_uptodate)
211 send_arg ("-c");
212 if (delete_flag)
213 send_arg ("-d");
214 if (force_tag_move)
215 send_arg ("-F");
216 if (!force_tag_match)
217 send_arg ("-f");
218 if (local)
219 send_arg ("-l");
220 if (!run_module_prog)
221 send_arg ("-n");
223 if (numtag)
224 option_with_arg ("-r", numtag);
225 if (date)
226 client_senddate (date);
228 send_arg ("--");
230 send_arg (symtag);
232 if (is_rtag)
234 int i;
235 for (i = 0; i < argc; ++i)
236 send_arg (argv[i]);
237 send_to_server ("rtag\012", 0);
239 else
241 send_files (argc, argv, local, 0,
243 /* I think the -c case is like "cvs status", in
244 which we really better be correct rather than
245 being fast; it is just too confusing otherwise. */
246 check_uptodate ? 0 : SEND_NO_CONTENTS);
247 send_file_names (argc, argv, SEND_EXPAND_WILD);
248 send_to_server ("tag\012", 0);
251 return get_responses_and_close ();
253 #endif
255 if (is_rtag)
257 DBM *db;
258 int i;
259 db = open_module ();
260 for (i = 0; i < argc; i++)
262 /* XXX last arg should be repository, but doesn't make sense here */
263 history_write ('T', (delete_flag ? "D" : (numtag ? numtag :
264 (date ? date : "A"))), symtag, argv[i], "");
265 err += do_module (db, argv[i], TAG,
266 delete_flag ? "Untagging" : "Tagging",
267 rtag_proc, NULL, 0, local, run_module_prog,
268 0, symtag);
270 close_module (db);
272 else
274 err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
275 NULL);
278 return err;
283 struct pretag_proc_data {
284 List *tlist;
285 bool delete_flag;
286 bool force_tag_move;
287 char *symtag;
291 * called from Parse_Info, this routine processes a line that came out
292 * of the posttag file and turns it into a command and executes it.
294 * RETURNS
295 * the absolute value of the return value of run_exec, which may or
296 * may not be the return value of the child process. this is
297 * contrained to return positive values because Parse_Info is summing
298 * return values and testing for non-zeroness to signify one or more
299 * of its callbacks having returned an error.
301 static int
302 posttag_proc (const char *repository, const char *filter, void *closure)
304 char *cmdline;
305 const char *srepos = Short_Repository (repository);
306 struct pretag_proc_data *ppd = closure;
308 /* %t = tag being added/moved/removed
309 * %o = operation = "add" | "mov" | "del"
310 * %b = branch mode = "?" (delete ops - unknown) | "T" (branch)
311 * | "N" (not branch)
312 * %c = cvs_cmd_name
313 * %p = path from $CVSROOT
314 * %r = path from root
315 * %{sVv} = attribute list = file name, old version tag will be deleted
316 * from, new version tag will be added to (or
317 * deleted from until
318 * SUPPORT_OLD_INFO_FMT_STRINGS is undefined).
321 * Cast any NULL arguments as appropriate pointers as this is an
322 * stdarg function and we need to be certain the caller gets what
323 * is expected.
325 cmdline = format_cmdline (
326 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
327 false, srepos,
328 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
329 filter,
330 "t", "s", ppd->symtag,
331 "o", "s", ppd->delete_flag
332 ? "del" : ppd->force_tag_move ? "mov" : "add",
333 "b", "c", delete_flag
334 ? '?' : branch_mode ? 'T' : 'N',
335 "c", "s", cvs_cmd_name,
336 #ifdef SERVER_SUPPORT
337 "R", "s", referrer ? referrer->original : "NONE",
338 #endif /* SERVER_SUPPORT */
339 "p", "s", srepos,
340 "r", "s", current_parsed_root->directory,
341 "sVv", ",", ppd->tlist,
342 pretag_list_to_args_proc, (void *) NULL,
343 (char *) NULL);
345 if (!cmdline || !strlen (cmdline))
347 if (cmdline) free (cmdline);
348 error (0, 0, "pretag proc resolved to the empty string!");
349 return 1;
352 run_setup (cmdline);
354 free (cmdline);
355 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
361 * Call any postadmin procs.
363 static int
364 tag_filesdoneproc (void *callerdat, int err, const char *repository,
365 const char *update_dir, List *entries)
367 Node *p;
368 List *mtlist, *tlist;
369 struct pretag_proc_data ppd;
371 TRACE (TRACE_FUNCTION, "tag_filesdoneproc (%d, %s, %s)", err, repository,
372 update_dir);
374 mtlist = callerdat;
375 p = findnode (mtlist, update_dir);
376 if (p != NULL)
377 tlist = ((struct master_lists *) p->data)->tlist;
378 else
379 tlist = NULL;
380 if (tlist == NULL || tlist->list->next == tlist->list)
381 return err;
383 ppd.tlist = tlist;
384 ppd.delete_flag = delete_flag;
385 ppd.force_tag_move = force_tag_move;
386 ppd.symtag = symtag;
387 Parse_Info (CVSROOTADM_POSTTAG, repository, posttag_proc,
388 PIOPT_ALL, &ppd);
390 return err;
396 * callback proc for doing the real work of tagging
398 /* ARGSUSED */
399 static int
400 rtag_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
401 int shorten, int local_specified, char *mname, char *msg)
403 /* Begin section which is identical to patch_proc--should this
404 be abstracted out somehow? */
405 char *myargv[2];
406 int err = 0;
407 int which;
408 char *repository;
409 char *where;
411 #ifdef HAVE_PRINTF_PTR
412 TRACE (TRACE_FUNCTION,
413 "rtag_proc (argc=%d, argv=%p, xwhere=%s,\n"
414 " mwhere=%s, mfile=%s, shorten=%d,\n"
415 " local_specified=%d, mname=%s, msg=%s)",
416 argc, (void *)argv, xwhere ? xwhere : "(null)",
417 mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
418 shorten, local_specified,
419 mname ? mname : "(null)", msg ? msg : "(null)" );
420 #else
421 TRACE (TRACE_FUNCTION,
422 "rtag_proc (argc=%d, argv=%lx, xwhere=%s,\n"
423 " mwhere=%s, mfile=%s, shorten=%d,\n"
424 " local_specified=%d, mname=%s, msg=%s )",
425 argc, (unsigned long)argv, xwhere ? xwhere : "(null)",
426 mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
427 shorten, local_specified,
428 mname ? mname : "(null)", msg ? msg : "(null)" );
429 #endif
431 if (is_rtag)
433 repository = xmalloc (strlen (current_parsed_root->directory)
434 + strlen (argv[0])
435 + (mfile == NULL ? 0 : strlen (mfile) + 1)
436 + 2);
437 (void) sprintf (repository, "%s/%s", current_parsed_root->directory,
438 argv[0]);
439 where = xmalloc (strlen (argv[0])
440 + (mfile == NULL ? 0 : strlen (mfile) + 1)
441 + 1);
442 (void) strcpy (where, argv[0]);
444 /* If MFILE isn't null, we need to set up to do only part of the
445 * module.
447 if (mfile != NULL)
449 char *cp;
450 char *path;
452 /* If the portion of the module is a path, put the dir part on
453 * REPOS.
455 if ((cp = strrchr (mfile, '/')) != NULL)
457 *cp = '\0';
458 (void) strcat (repository, "/");
459 (void) strcat (repository, mfile);
460 (void) strcat (where, "/");
461 (void) strcat (where, mfile);
462 mfile = cp + 1;
465 /* take care of the rest */
466 path = xmalloc (strlen (repository) + strlen (mfile) + 5);
467 (void) sprintf (path, "%s/%s", repository, mfile);
468 if (isdir (path))
470 /* directory means repository gets the dir tacked on */
471 (void) strcpy (repository, path);
472 (void) strcat (where, "/");
473 (void) strcat (where, mfile);
475 else
477 myargv[0] = argv[0];
478 myargv[1] = mfile;
479 argc = 2;
480 argv = myargv;
482 free (path);
485 /* cd to the starting repository */
486 if (CVS_CHDIR (repository) < 0)
488 error (0, errno, "cannot chdir to %s", repository);
489 free (repository);
490 free (where);
491 return 1;
493 /* End section which is identical to patch_proc. */
495 if (delete_flag || attic_too || (force_tag_match && numtag))
496 which = W_REPOS | W_ATTIC;
497 else
498 which = W_REPOS;
500 else
502 where = NULL;
503 which = W_LOCAL;
504 repository = "";
507 if (numtag != NULL && !numtag_validated)
509 tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0,
510 repository, false);
511 numtag_validated = true;
514 /* check to make sure they are authorized to tag all the
515 specified files in the repository */
517 mtlist = getlist ();
518 err = start_recursion (check_fileproc, check_filesdoneproc,
519 NULL, NULL, NULL,
520 argc - 1, argv + 1, local_specified, which, 0,
521 CVS_LOCK_READ, where, 1, repository);
523 if (err)
525 error (1, 0, "correct the above errors first!");
528 /* It would be nice to provide consistency with respect to
529 commits; however CVS lacks the infrastructure to do that (see
530 Concurrency in cvs.texinfo and comment in do_recursion). */
532 /* start the recursion processor */
533 err = start_recursion
534 (is_rtag ? rtag_fileproc : tag_fileproc,
535 tag_filesdoneproc, tag_dirproc, NULL, mtlist, argc - 1, argv + 1,
536 local_specified, which, 0, CVS_LOCK_WRITE, where, 1,
537 repository);
538 dellist (&mtlist);
539 if (which & W_REPOS) free (repository);
540 if (where != NULL)
541 free (where);
542 return err;
547 /* check file that is to be tagged */
548 /* All we do here is add it to our list */
549 static int
550 check_fileproc (void *callerdat, struct file_info *finfo)
552 const char *xdir;
553 Node *p;
554 Vers_TS *vers;
555 List *tlist;
556 struct tag_info *ti;
557 int addit = 1;
559 TRACE (TRACE_FUNCTION, "check_fileproc (%s, %s, %s)",
560 finfo->repository ? finfo->repository : "(null)",
561 finfo->fullname ? finfo->fullname : "(null)",
562 finfo->rcs ? (finfo->rcs->path ? finfo->rcs->path : "(null)")
563 : "NULL");
565 if (check_uptodate)
567 switch (Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0))
569 case T_UPTODATE:
570 case T_CHECKOUT:
571 case T_PATCH:
572 case T_REMOVE_ENTRY:
573 break;
574 case T_UNKNOWN:
575 case T_CONFLICT:
576 case T_NEEDS_MERGE:
577 case T_MODIFIED:
578 case T_ADDED:
579 case T_REMOVED:
580 default:
581 error (0, 0, "%s is locally modified", finfo->fullname);
582 freevers_ts (&vers);
583 return 1;
586 else
587 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
589 if (finfo->update_dir[0] == '\0')
590 xdir = ".";
591 else
592 xdir = finfo->update_dir;
593 if ((p = findnode (mtlist, xdir)) != NULL)
595 tlist = ((struct master_lists *) p->data)->tlist;
597 else
599 struct master_lists *ml;
601 tlist = getlist ();
602 p = getnode ();
603 p->key = xstrdup (xdir);
604 p->type = UPDATE;
605 ml = xmalloc (sizeof (struct master_lists));
606 ml->tlist = tlist;
607 p->data = ml;
608 p->delproc = masterlist_delproc;
609 (void) addnode (mtlist, p);
611 /* do tlist */
612 p = getnode ();
613 p->key = xstrdup (finfo->file);
614 p->type = UPDATE;
615 p->delproc = tag_delproc;
616 if (vers->srcfile == NULL)
618 if (!really_quiet)
619 error (0, 0, "nothing known about %s", finfo->file);
620 freevers_ts (&vers);
621 freenode (p);
622 return 1;
625 /* Here we duplicate the calculation in tag_fileproc about which
626 version we are going to tag. There probably are some subtle races
627 (e.g. numtag is "foo" which gets moved between here and
628 tag_fileproc). */
629 p->data = ti = xmalloc (sizeof (struct tag_info));
630 ti->tag = xstrdup (numtag ? numtag : vers->tag);
631 if (!is_rtag && numtag == NULL && date == NULL)
632 ti->rev = xstrdup (vers->vn_user);
633 else
634 ti->rev = RCS_getversion (vers->srcfile, numtag, date,
635 force_tag_match, NULL);
637 if (ti->rev != NULL)
639 ti->oldrev = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
641 if (ti->oldrev == NULL)
643 if (delete_flag)
645 /* Deleting a tag which did not exist is a noop and
646 should not be logged. */
647 addit = 0;
650 else if (delete_flag)
652 free (ti->rev);
653 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
654 /* a hack since %v used to mean old or new rev */
655 ti->rev = xstrdup (ti->oldrev);
656 #else /* SUPPORT_OLD_INFO_FMT_STRINGS */
657 ti->rev = NULL;
658 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
660 else if (strcmp(ti->oldrev, p->data) == 0)
661 addit = 0;
662 else if (!force_tag_move)
663 addit = 0;
665 else
666 addit = 0;
667 if (!addit)
669 free(p->data);
670 p->data = NULL;
672 freevers_ts (&vers);
673 (void)addnode (tlist, p);
674 return 0;
679 static int
680 check_filesdoneproc (void *callerdat, int err, const char *repos,
681 const char *update_dir, List *entries)
683 int n;
684 Node *p;
685 List *tlist;
686 struct pretag_proc_data ppd;
688 p = findnode (mtlist, update_dir);
689 if (p != NULL)
690 tlist = ((struct master_lists *) p->data)->tlist;
691 else
692 tlist = NULL;
693 if (tlist == NULL || tlist->list->next == tlist->list)
694 return err;
696 ppd.tlist = tlist;
697 ppd.delete_flag = delete_flag;
698 ppd.force_tag_move = force_tag_move;
699 ppd.symtag = symtag;
700 if ((n = Parse_Info (CVSROOTADM_TAGINFO, repos, pretag_proc, PIOPT_ALL,
701 &ppd)) > 0)
703 error (0, 0, "Pre-tag check failed");
704 err += n;
706 return err;
712 * called from Parse_Info, this routine processes a line that came out
713 * of a taginfo file and turns it into a command and executes it.
715 * RETURNS
716 * the absolute value of the return value of run_exec, which may or
717 * may not be the return value of the child process. this is
718 * contrained to return positive values because Parse_Info is adding up
719 * return values and testing for non-zeroness to signify one or more
720 * of its callbacks having returned an error.
722 static int
723 pretag_proc (const char *repository, const char *filter, void *closure)
725 char *newfilter = NULL;
726 char *cmdline;
727 const char *srepos = Short_Repository (repository);
728 struct pretag_proc_data *ppd = closure;
730 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
731 if (!strchr (filter, '%'))
733 error (0,0,
734 "warning: taginfo line contains no format strings:\n"
735 " \"%s\"\n"
736 "Filling in old defaults ('%%t %%o %%p %%{sv}'), but please be aware that this\n"
737 "usage is deprecated.", filter);
738 newfilter = xmalloc (strlen (filter) + 16);
739 strcpy (newfilter, filter);
740 strcat (newfilter, " %t %o %p %{sv}");
741 filter = newfilter;
743 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
745 /* %t = tag being added/moved/removed
746 * %o = operation = "add" | "mov" | "del"
747 * %b = branch mode = "?" (delete ops - unknown) | "T" (branch)
748 * | "N" (not branch)
749 * %c = cvs_cmd_name
750 * %p = path from $CVSROOT
751 * %r = path from root
752 * %{sVv} = attribute list = file name, old version tag will be deleted
753 * from, new version tag will be added to (or
754 * deleted from until
755 * SUPPORT_OLD_INFO_FMT_STRINGS is undefined)
758 * Cast any NULL arguments as appropriate pointers as this is an
759 * stdarg function and we need to be certain the caller gets what
760 * is expected.
762 cmdline = format_cmdline (
763 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
764 false, srepos,
765 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
766 filter,
767 "t", "s", ppd->symtag,
768 "o", "s", ppd->delete_flag ? "del" :
769 ppd->force_tag_move ? "mov" : "add",
770 "b", "c", delete_flag
771 ? '?' : branch_mode ? 'T' : 'N',
772 "c", "s", cvs_cmd_name,
773 #ifdef SERVER_SUPPORT
774 "R", "s", referrer ? referrer->original : "NONE",
775 #endif /* SERVER_SUPPORT */
776 "p", "s", srepos,
777 "r", "s", current_parsed_root->directory,
778 "sVv", ",", ppd->tlist,
779 pretag_list_to_args_proc, (void *) NULL,
780 (char *) NULL);
782 if (newfilter) free (newfilter);
784 if (!cmdline || !strlen (cmdline))
786 if (cmdline) free (cmdline);
787 error (0, 0, "pretag proc resolved to the empty string!");
788 return 1;
791 run_setup (cmdline);
793 /* FIXME - the old code used to run the following here:
795 * if (!isfile(s))
797 * error (0, errno, "cannot find pre-tag filter '%s'", s);
798 * free(s);
799 * return (1);
802 * not sure this is really necessary. it might give a little finer grained
803 * error than letting the execution attempt fail but i'm not sure. in any
804 * case it should be easy enough to add a function in run.c to test its
805 * first arg for fileness & executability.
808 free (cmdline);
809 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
814 static void
815 masterlist_delproc (Node *p)
817 struct master_lists *ml = p->data;
819 dellist (&ml->tlist);
820 free (ml);
821 return;
826 static void
827 tag_delproc (Node *p)
829 struct tag_info *ti;
830 if (p->data)
832 ti = (struct tag_info *) p->data;
833 if (ti->oldrev) free (ti->oldrev);
834 if (ti->rev) free (ti->rev);
835 free (ti->tag);
836 free (p->data);
837 p->data = NULL;
839 return;
844 /* to be passed into walklist with a list of tags
845 * p->key = tagname
846 * p->data = struct tag_info *
847 * p->data->oldrev = rev tag will be deleted from
848 * p->data->rev = rev tag will be added to
849 * p->data->tag = tag oldrev is attached to, if any
851 * closure will be a struct format_cmdline_walklist_closure
852 * where closure is undefined
854 static int
855 pretag_list_to_args_proc (Node *p, void *closure)
857 struct tag_info *taginfo = (struct tag_info *)p->data;
858 struct format_cmdline_walklist_closure *c =
859 (struct format_cmdline_walklist_closure *)closure;
860 char *arg = NULL;
861 const char *f;
862 char *d;
863 size_t doff;
865 if (!p->data) return 1;
867 f = c->format;
868 d = *c->d;
869 /* foreach requested attribute */
870 while (*f)
872 switch (*f++)
874 case 's':
875 arg = p->key;
876 break;
877 case 'T':
878 arg = taginfo->tag ? taginfo->tag : "";
879 break;
880 case 'v':
881 arg = taginfo->rev ? taginfo->rev : "NONE";
882 break;
883 case 'V':
884 arg = taginfo->oldrev ? taginfo->oldrev : "NONE";
885 break;
886 default:
887 error(1,0,
888 "Unknown format character or not a list attribute: %c",
889 f[-1]);
890 break;
892 /* copy the attribute into an argument */
893 if (c->quotes)
895 arg = cmdlineescape (c->quotes, arg);
897 else
899 arg = cmdlinequote ('"', arg);
902 doff = d - *c->buf;
903 expand_string (c->buf, c->length, doff + strlen (arg));
904 d = *c->buf + doff;
905 strncpy (d, arg, strlen (arg));
906 d += strlen (arg);
908 free (arg);
910 /* and always put the extra space on. we'll have to back up a char when we're
911 * done, but that seems most efficient
913 doff = d - *c->buf;
914 expand_string (c->buf, c->length, doff + 1);
915 d = *c->buf + doff;
916 *d++ = ' ';
918 /* correct our original pointer into the buff */
919 *c->d = d;
920 return 0;
925 * Called to rtag a particular file, as appropriate with the options that were
926 * set above.
928 /* ARGSUSED */
929 static int
930 rtag_fileproc (void *callerdat, struct file_info *finfo)
932 RCSNode *rcsfile;
933 char *version = NULL, *rev = NULL;
934 int retcode = 0;
935 int retval = 0;
936 static bool valtagged = false;
938 /* find the parsed RCS data */
939 if ((rcsfile = finfo->rcs) == NULL)
941 retval = 1;
942 goto free_vars_and_return;
946 * For tagging an RCS file which is a symbolic link, you'd best be
947 * running with RCS 5.6, since it knows how to handle symbolic links
948 * correctly without breaking your link!
951 if (delete_flag)
953 retval = rtag_delete (rcsfile);
954 goto free_vars_and_return;
958 * If we get here, we are adding a tag. But, if -a was specified, we
959 * need to check to see if a -r or -D option was specified. If neither
960 * was specified and the file is in the Attic, remove the tag.
962 if (attic_too && (!numtag && !date))
964 if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
966 retval = rtag_delete (rcsfile);
967 goto free_vars_and_return;
971 version = RCS_getversion (rcsfile, numtag, date, force_tag_match, NULL);
972 if (version == NULL)
974 /* If -a specified, clean up any old tags */
975 if (attic_too)
976 (void)rtag_delete (rcsfile);
978 if (!quiet && !force_tag_match)
980 error (0, 0, "cannot find tag `%s' in `%s'",
981 numtag ? numtag : "head", rcsfile->path);
982 retval = 1;
984 goto free_vars_and_return;
986 if (numtag
987 && isdigit ((unsigned char)*numtag)
988 && strcmp (numtag, version) != 0)
992 * We didn't find a match for the numeric tag that was specified, but
993 * that's OK. just pass the numeric tag on to rcs, to be tagged as
994 * specified. Could get here if one tried to tag "1.1.1" and there
995 * was a 1.1.1 branch with some head revision. In this case, we want
996 * the tag to reference "1.1.1" and not the revision at the head of
997 * the branch. Use a symbolic tag for that.
999 rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
1000 retcode = RCS_settag(rcsfile, symtag, numtag);
1001 if (retcode == 0)
1002 RCS_rewrite (rcsfile, NULL, NULL);
1004 else
1006 char *oversion;
1009 * As an enhancement for the case where a tag is being re-applied to
1010 * a large body of a module, make one extra call to RCS_getversion to
1011 * see if the tag is already set in the RCS file. If so, check to
1012 * see if it needs to be moved. If not, do nothing. This will
1013 * likely save a lot of time when simply moving the tag to the
1014 * "current" head revisions of a module -- which I have found to be a
1015 * typical tagging operation.
1017 rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
1018 oversion = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
1019 if (oversion != NULL)
1021 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1024 * if versions the same and neither old or new are branches don't
1025 * have to do anything
1027 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
1029 free (oversion);
1030 goto free_vars_and_return;
1033 if (!force_tag_move)
1035 /* we're NOT going to move the tag */
1036 (void)printf ("W %s", finfo->fullname);
1038 (void)printf (" : %s already exists on %s %s",
1039 symtag, isbranch ? "branch" : "version",
1040 oversion);
1041 (void)printf (" : NOT MOVING tag to %s %s\n",
1042 branch_mode ? "branch" : "version", rev);
1043 free (oversion);
1044 goto free_vars_and_return;
1046 else /* force_tag_move is set and... */
1047 if ((isbranch && !disturb_branch_tags) ||
1048 (!isbranch && disturb_branch_tags))
1050 error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
1051 finfo->fullname,
1052 isbranch ? "branch" : "non-branch",
1053 symtag, oversion, rev,
1054 isbranch ? "" : " due to `-B' option");
1055 free (oversion);
1056 goto free_vars_and_return;
1058 free (oversion);
1060 retcode = RCS_settag (rcsfile, symtag, rev);
1061 if (retcode == 0)
1062 RCS_rewrite (rcsfile, NULL, NULL);
1065 if (retcode != 0)
1067 error (1, retcode == -1 ? errno : 0,
1068 "failed to set tag `%s' to revision `%s' in `%s'",
1069 symtag, rev, rcsfile->path);
1070 retval = 1;
1071 goto free_vars_and_return;
1074 free_vars_and_return:
1075 if (branch_mode && rev) free (rev);
1076 if (version) free (version);
1077 if (!delete_flag && !retval && !valtagged)
1079 tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
1080 valtagged = true;
1082 return retval;
1088 * If -d is specified, "force_tag_match" is set, so that this call to
1089 * RCS_getversion() will return a NULL version string if the symbolic
1090 * tag does not exist in the RCS file.
1092 * If the -r flag was used, numtag is set, and we only delete the
1093 * symtag from files that have numtag.
1095 * This is done here because it's MUCH faster than just blindly calling
1096 * "rcs" to remove the tag... trust me.
1098 static int
1099 rtag_delete (RCSNode *rcsfile)
1101 char *version;
1102 int retcode, isbranch;
1104 if (numtag)
1106 version = RCS_getversion (rcsfile, numtag, NULL, 1, NULL);
1107 if (version == NULL)
1108 return (0);
1109 free (version);
1112 version = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
1113 if (version == NULL)
1114 return 0;
1115 free (version);
1118 isbranch = RCS_nodeisbranch (rcsfile, symtag);
1119 if ((isbranch && !disturb_branch_tags) ||
1120 (!isbranch && disturb_branch_tags))
1122 if (!quiet)
1123 error (0, 0,
1124 "Not removing %s tag `%s' from `%s'%s.",
1125 isbranch ? "branch" : "non-branch",
1126 symtag, rcsfile->path,
1127 isbranch ? "" : " due to `-B' option");
1128 return 1;
1131 if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
1133 if (!quiet)
1134 error (0, retcode == -1 ? errno : 0,
1135 "failed to remove tag `%s' from `%s'", symtag,
1136 rcsfile->path);
1137 return 1;
1139 RCS_rewrite (rcsfile, NULL, NULL);
1140 return 0;
1146 * Called to tag a particular file (the currently checked out version is
1147 * tagged with the specified tag - or the specified tag is deleted).
1149 /* ARGSUSED */
1150 static int
1151 tag_fileproc (void *callerdat, struct file_info *finfo)
1153 char *version, *oversion;
1154 char *nversion = NULL;
1155 char *rev;
1156 Vers_TS *vers;
1157 int retcode = 0;
1158 int retval = 0;
1159 static bool valtagged = false;
1161 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
1163 if (numtag || date)
1165 nversion = RCS_getversion (vers->srcfile, numtag, date,
1166 force_tag_match, NULL);
1167 if (!nversion)
1168 goto free_vars_and_return;
1170 if (delete_flag)
1173 int isbranch;
1175 * If -d is specified, "force_tag_match" is set, so that this call to
1176 * RCS_getversion() will return a NULL version string if the symbolic
1177 * tag does not exist in the RCS file.
1179 * This is done here because it's MUCH faster than just blindly calling
1180 * "rcs" to remove the tag... trust me.
1183 version = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
1184 if (version == NULL || vers->srcfile == NULL)
1185 goto free_vars_and_return;
1187 free (version);
1189 isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1190 if ((isbranch && !disturb_branch_tags) ||
1191 (!isbranch && disturb_branch_tags))
1193 if (!quiet)
1194 error(0, 0,
1195 "Not removing %s tag `%s' from `%s'%s.",
1196 isbranch ? "branch" : "non-branch",
1197 symtag, vers->srcfile->path,
1198 isbranch ? "" : " due to `-B' option");
1199 retval = 1;
1200 goto free_vars_and_return;
1203 if ((retcode = RCS_deltag (vers->srcfile, symtag)) != 0)
1205 if (!quiet)
1206 error (0, retcode == -1 ? errno : 0,
1207 "failed to remove tag %s from %s", symtag,
1208 vers->srcfile->path);
1209 retval = 1;
1210 goto free_vars_and_return;
1212 RCS_rewrite (vers->srcfile, NULL, NULL);
1214 /* warm fuzzies */
1215 if (!really_quiet)
1217 cvs_output ("D ", 2);
1218 cvs_output (finfo->fullname, 0);
1219 cvs_output ("\n", 1);
1222 goto free_vars_and_return;
1226 * If we are adding a tag, we need to know which version we have checked
1227 * out and we'll tag that version.
1229 if (!nversion)
1230 version = vers->vn_user;
1231 else
1232 version = nversion;
1233 if (!version)
1234 goto free_vars_and_return;
1235 else if (strcmp (version, "0") == 0)
1237 if (!quiet)
1238 error (0, 0, "couldn't tag added but un-commited file `%s'",
1239 finfo->file);
1240 goto free_vars_and_return;
1242 else if (version[0] == '-')
1244 if (!quiet)
1245 error (0, 0, "skipping removed but un-commited file `%s'",
1246 finfo->file);
1247 goto free_vars_and_return;
1249 else if (vers->srcfile == NULL)
1251 if (!quiet)
1252 error (0, 0, "cannot find revision control file for `%s'",
1253 finfo->file);
1254 goto free_vars_and_return;
1258 * As an enhancement for the case where a tag is being re-applied to a
1259 * large number of files, make one extra call to RCS_getversion to see
1260 * if the tag is already set in the RCS file. If so, check to see if it
1261 * needs to be moved. If not, do nothing. This will likely save a lot of
1262 * time when simply moving the tag to the "current" head revisions of a
1263 * module -- which I have found to be a typical tagging operation.
1265 rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
1266 oversion = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
1267 if (oversion != NULL)
1269 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1272 * if versions the same and neither old or new are branches don't have
1273 * to do anything
1275 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
1277 free (oversion);
1278 if (branch_mode)
1279 free (rev);
1280 goto free_vars_and_return;
1283 if (!force_tag_move)
1285 /* we're NOT going to move the tag */
1286 cvs_output ("W ", 2);
1287 cvs_output (finfo->fullname, 0);
1288 cvs_output (" : ", 0);
1289 cvs_output (symtag, 0);
1290 cvs_output (" already exists on ", 0);
1291 cvs_output (isbranch ? "branch" : "version", 0);
1292 cvs_output (" ", 0);
1293 cvs_output (oversion, 0);
1294 cvs_output (" : NOT MOVING tag to ", 0);
1295 cvs_output (branch_mode ? "branch" : "version", 0);
1296 cvs_output (" ", 0);
1297 cvs_output (rev, 0);
1298 cvs_output ("\n", 1);
1299 free (oversion);
1300 if (branch_mode)
1301 free (rev);
1302 goto free_vars_and_return;
1304 else /* force_tag_move == 1 and... */
1305 if ((isbranch && !disturb_branch_tags) ||
1306 (!isbranch && disturb_branch_tags))
1308 error (0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
1309 finfo->fullname,
1310 isbranch ? "branch" : "non-branch",
1311 symtag, oversion, rev,
1312 isbranch ? "" : " due to `-B' option");
1313 free (oversion);
1314 if (branch_mode)
1315 free (rev);
1316 goto free_vars_and_return;
1318 free (oversion);
1321 if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
1323 error (1, retcode == -1 ? errno : 0,
1324 "failed to set tag %s to revision %s in %s",
1325 symtag, rev, vers->srcfile->path);
1326 if (branch_mode)
1327 free (rev);
1328 retval = 1;
1329 goto free_vars_and_return;
1331 if (branch_mode)
1332 free (rev);
1333 RCS_rewrite (vers->srcfile, NULL, NULL);
1335 /* more warm fuzzies */
1336 if (!really_quiet)
1338 cvs_output ("T ", 2);
1339 cvs_output (finfo->fullname, 0);
1340 cvs_output ("\n", 1);
1343 free_vars_and_return:
1344 if (nversion != NULL)
1345 free (nversion);
1346 freevers_ts (&vers);
1347 if (!delete_flag && !retval && !valtagged)
1349 tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
1350 valtagged = true;
1352 return retval;
1358 * Print a warm fuzzy message
1360 /* ARGSUSED */
1361 static Dtype
1362 tag_dirproc (void *callerdat, const char *dir, const char *repos,
1363 const char *update_dir, List *entries)
1366 if (ignore_directory (update_dir))
1368 /* print the warm fuzzy message */
1369 if (!quiet)
1370 error (0, 0, "Ignoring %s", update_dir);
1371 return R_SKIP_ALL;
1374 if (!quiet)
1375 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging",
1376 update_dir);
1377 return R_PROCESS;
1382 /* Code relating to the val-tags file. Note that this file has no way
1383 of knowing when a tag has been deleted. The problem is that there
1384 is no way of knowing whether a tag still exists somewhere, when we
1385 delete it some places. Using per-directory val-tags files (in
1386 CVSREP) might be better, but that might slow down the process of
1387 verifying that a tag is correct (maybe not, for the likely cases,
1388 if carefully done), and/or be harder to implement correctly. */
1390 struct val_args {
1391 const char *name;
1392 int found;
1395 static int
1396 val_fileproc (void *callerdat, struct file_info *finfo)
1398 RCSNode *rcsdata;
1399 struct val_args *args = callerdat;
1400 char *tag;
1402 if ((rcsdata = finfo->rcs) == NULL)
1403 /* Not sure this can happen, after all we passed only
1404 W_REPOS | W_ATTIC. */
1405 return 0;
1407 tag = RCS_gettag (rcsdata, args->name, 1, NULL);
1408 if (tag != NULL)
1410 /* FIXME: should find out a way to stop the search at this point. */
1411 args->found = 1;
1412 free (tag);
1414 return 0;
1419 /* This routine determines whether a tag appears in CVSROOT/val-tags.
1421 * The val-tags file will be open read-only when IDB is NULL. Since writes to
1422 * val-tags always append to it, the lack of locking is okay. The worst case
1423 * race condition might misinterpret a partially written "foobar" matched, for
1424 * instance, a request for "f", "foo", of "foob". Such a mismatch would be
1425 * caught harmlessly later.
1427 * Before CVS adds a tag to val-tags, it will lock val-tags for write and
1428 * verify that the tag is still not present to avoid adding it twice.
1430 * NOTES
1431 * This function expects its parent to handle any necessary locking of the
1432 * val-tags file.
1434 * INPUTS
1435 * idb When this value is NULL, the val-tags file is opened in
1436 * in read-only mode. When present, the val-tags file is opened
1437 * in read-write mode and the DBM handle is stored in *IDB.
1438 * name The tag to search for.
1440 * OUTPUTS
1441 * *idb The val-tags file opened for read/write, or NULL if it couldn't
1442 * be opened.
1444 * ERRORS
1445 * Exits with an error message if the val-tags file cannot be opened for
1446 * read (failure to open val-tags read/write is harmless - see below).
1448 * RETURNS
1449 * true 1. If NAME exists in val-tags.
1450 * 2. If IDB is non-NULL and val-tags cannot be opened for write.
1451 * This allows callers to ignore the harmless inability to
1452 * update the val-tags cache.
1453 * false If the file could be opened and the tag is not present.
1455 static int is_in_val_tags (DBM **idb, const char *name)
1457 DBM *db = NULL;
1458 char *valtags_filename;
1459 datum mytag;
1460 int status;
1462 /* Casting out const should be safe here - input datums are not
1463 * written to by the myndbm functions.
1465 mytag.dptr = (char *)name;
1466 mytag.dsize = strlen (name);
1468 valtags_filename = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
1469 CVSROOTADM, CVSROOTADM_VALTAGS);
1471 if (idb)
1473 mode_t omask;
1475 omask = umask (cvsumask);
1476 db = dbm_open (valtags_filename, O_RDWR | O_CREAT, 0666);
1477 umask (omask);
1479 if (!db)
1482 error (0, errno, "warning: cannot open `%s' read/write",
1483 valtags_filename);
1484 *idb = NULL;
1485 return 1;
1488 *idb = db;
1490 else
1492 db = dbm_open (valtags_filename, O_RDONLY, 0444);
1493 if (!db && !existence_error (errno))
1494 error (1, errno, "cannot read %s", valtags_filename);
1497 /* If the file merely fails to exist, we just keep going and create
1498 it later if need be. */
1500 status = 0;
1501 if (db)
1503 datum val;
1505 val = dbm_fetch (db, mytag);
1506 if (val.dptr != NULL)
1507 /* Found. The tag is valid. */
1508 status = 1;
1510 /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */
1512 if (!idb) dbm_close (db);
1515 free (valtags_filename);
1516 return status;
1521 /* Add a tag to the CVSROOT/val-tags cache. Establishes a write lock and
1522 * reverifies that the tag does not exist before adding it.
1524 static void add_to_val_tags (const char *name)
1526 DBM *db;
1527 datum mytag;
1528 datum value;
1530 if (noexec || readonlyfs) return;
1532 val_tags_lock (current_parsed_root->directory);
1534 /* Check for presence again since we have a lock now. */
1535 if (is_in_val_tags (&db, name)) return;
1537 /* Casting out const should be safe here - input datums are not
1538 * written to by the myndbm functions.
1540 mytag.dptr = (char *)name;
1541 mytag.dsize = strlen (name);
1542 value.dptr = "y";
1543 value.dsize = 1;
1545 if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
1546 error (0, errno, "failed to store %s into val-tags", name);
1547 dbm_close (db);
1549 clear_val_tags_lock ();
1554 static Dtype
1555 val_direntproc (void *callerdat, const char *dir, const char *repository,
1556 const char *update_dir, List *entries)
1558 /* This is not quite right--it doesn't get right the case of "cvs
1559 update -d -r foobar" where foobar is a tag which exists only in
1560 files in a directory which does not exist yet, but which is
1561 about to be created. */
1562 if (isdir (dir))
1563 return R_PROCESS;
1564 return R_SKIP_ALL;
1569 /* With VALID set, insert NAME into val-tags if it is not already present
1570 * there.
1572 * Without VALID set, check to see whether NAME is a valid tag. If so, return.
1573 * If not print an error message and exit.
1575 * INPUTS
1577 * ARGC, ARGV, LOCAL, and AFLAG specify which files we will be operating on.
1579 * REPOSITORY is the repository if we need to cd into it, or NULL if
1580 * we are already there, or "" if we should do a W_LOCAL recursion.
1581 * Sorry for three cases, but the "" case is needed in case the
1582 * working directories come from diverse parts of the repository, the
1583 * NULL case avoids an unneccesary chdir, and the non-NULL, non-""
1584 * case is needed for checkout, where we don't want to chdir if the
1585 * tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
1586 * local directory.
1588 * ERRORS
1589 * Errors may be encountered opening and accessing the DBM file. Write
1590 * errors generate warnings and read errors are fatal. When !VALID and NAME
1591 * is not in val-tags, errors may also be generated as per start_recursion.
1592 * When !VALID, non-existance of tags both in val-tags and in the archive
1593 * files also causes a fatal error.
1595 * RETURNS
1596 * Nothing.
1598 void
1599 tag_check_valid (const char *name, int argc, char **argv, int local, int aflag,
1600 char *repository, bool valid)
1602 struct val_args the_val_args;
1603 struct saved_cwd cwd;
1604 int which;
1606 #ifdef HAVE_PRINTF_PTR
1607 TRACE (TRACE_FUNCTION,
1608 "tag_check_valid (name=%s, argc=%d, argv=%p, local=%d,\n"
1609 " aflag=%d, repository=%s, valid=%s)",
1610 name ? name : "(name)", argc, (void *)argv, local, aflag,
1611 repository ? repository : "(null)",
1612 valid ? "true" : "false");
1613 #else
1614 TRACE (TRACE_FUNCTION,
1615 "tag_check_valid (name=%s, argc=%d, argv=%lx, local=%d,\n"
1616 " aflag=%d, repository=%s, valid=%s)",
1617 name ? name : "(name)", argc, (unsigned long)argv, local, aflag,
1618 repository ? repository : "(null)",
1619 valid ? "true" : "false");
1620 #endif
1622 /* Numeric tags require only a syntactic check. */
1623 if (isdigit ((unsigned char) name[0]))
1625 /* insert is not possible for numeric revisions */
1626 assert (!valid);
1627 if (RCS_valid_rev (name)) return;
1628 else
1629 error (1, 0, "\
1630 Numeric tag %s invalid. Numeric tags should be of the form X[.X]...", name);
1633 /* Special tags are always valid. */
1634 if (strcmp (name, TAG_BASE) == 0
1635 || strcmp (name, TAG_HEAD) == 0)
1637 /* insert is not possible for numeric revisions */
1638 assert (!valid);
1639 return;
1642 /* Verify that the tag is valid syntactically. Some later code once made
1643 * assumptions about this.
1645 RCS_check_tag (name);
1647 if (is_in_val_tags (NULL, name)) return;
1649 if (!valid)
1651 /* We didn't find the tag in val-tags, so look through all the RCS files
1652 * to see whether it exists there. Yes, this is expensive, but there
1653 * is no other way to cope with a tag which might have been created
1654 * by an old version of CVS, from before val-tags was invented
1657 the_val_args.name = name;
1658 the_val_args.found = 0;
1659 which = W_REPOS | W_ATTIC;
1661 if (repository == NULL || repository[0] == '\0')
1662 which |= W_LOCAL;
1663 else
1665 if (save_cwd (&cwd))
1666 error (1, errno, "Failed to save current directory.");
1667 if (CVS_CHDIR (repository) < 0)
1668 error (1, errno, "cannot change to %s directory", repository);
1671 start_recursion
1672 (val_fileproc, NULL, val_direntproc, NULL,
1673 &the_val_args, argc, argv, local, which, aflag,
1674 CVS_LOCK_READ, NULL, 1, repository);
1675 if (repository != NULL && repository[0] != '\0')
1677 if (restore_cwd (&cwd))
1678 error (1, errno, "Failed to restore current directory, `%s'.",
1679 cwd.name);
1680 free_cwd (&cwd);
1683 if (!the_val_args.found)
1684 error (1, 0, "no such tag `%s'", name);
1687 /* The tags is valid but not mentioned in val-tags. Add it. */
1688 add_to_val_tags (name);