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