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