]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/tag.c
Import cvs-1.11.17 onto vendor branch.
[FreeBSD/FreeBSD.git] / contrib / cvs / src / tag.c
1 /*
2  * Copyright (c) 1992, Brian Berliner and Jeff Polk
3  * Copyright (c) 1989-1992, Brian Berliner
4  * 
5  * You may distribute under the terms of the GNU General Public License as
6  * specified in the README file that comes with the CVS source distribution.
7  * 
8  * Tag and Rtag
9  * 
10  * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
11  * Tag uses the checked out revision in the current directory, rtag uses
12  * the modules database, if necessary.
13  */
14
15 #include "cvs.h"
16 #include "savecwd.h"
17
18 static int rtag_proc PROTO((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 PROTO ((void *callerdat, struct file_info *finfo));
22 static int check_filesdoneproc PROTO ((void *callerdat, int err,
23                                        const char *repos,
24                                        const char *update_dir,
25                                        List *entries));
26 static int pretag_proc PROTO((const char *repository, const char *filter));
27 static void masterlist_delproc PROTO((Node *p));
28 static void tag_delproc PROTO((Node *p));
29 static int pretag_list_proc PROTO((Node *p, void *closure));
30
31 static Dtype tag_dirproc PROTO ((void *callerdat, const char *dir,
32                                  const char *repos, const char *update_dir,
33                                  List *entries));
34 static int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo));
35 static int rtag_delete PROTO((RCSNode *rcsfile));
36 static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo));
37
38 static char *numtag;                    /* specific revision to tag */
39 static int numtag_validated = 0;
40 static char *date = NULL;
41 static char *symtag;                    /* tag to add or delete */
42 static int delete_flag;                 /* adding a tag by default */
43 static int branch_mode;                 /* make an automagic "branch" tag */
44 static int disturb_branch_tags = 0;     /* allow -F,-d to disturb branch tags */
45 static int force_tag_match = 1;         /* force tag to match by default */
46 static int force_tag_move;              /* don't force tag to move by default */
47 static int check_uptodate;              /* no uptodate-check by default */
48 static int attic_too;                   /* remove tag from Attic files */
49 static int is_rtag;
50
51 struct tag_info
52 {
53     Ctype status;
54     char *rev;
55     char *tag;
56     char *options;
57 };
58
59 struct master_lists
60 {
61     List *tlist;
62 };
63
64 static List *mtlist;
65 static List *tlist;
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 int
105 cvstag (argc, argv)
106     int argc;
107     char **argv;
108 {
109     int local = 0;                      /* recursive by default */
110     int c;
111     int err = 0;
112     int run_module_prog = 1;
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 = 1;
126                 break;
127             case 'b':
128                 branch_mode = 1;
129                 break;
130             case 'B':
131                 disturb_branch_tags = 1;
132                 break;
133             case 'c':
134                 check_uptodate = 1;
135                 break;
136             case 'd':
137                 delete_flag = 1;
138                 break;
139             case 'F':
140                 force_tag_move = 1;
141                 break;
142             case 'f':
143                 force_tag_match = 0;
144                 break;
145             case 'l':
146                 local = 1;
147                 break;
148             case 'n':
149                 run_module_prog = 0;
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 = 0;
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, (char *) 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  * callback proc for doing the real work of tagging
281  */
282 /* ARGSUSED */
283 static int
284 rtag_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified,
285            mname, msg)
286     int argc;
287     char **argv;
288     char *xwhere;
289     char *mwhere;
290     char *mfile;
291     int shorten;
292     int local_specified;
293     char *mname;
294     char *msg;
295 {
296     /* Begin section which is identical to patch_proc--should this
297        be abstracted out somehow?  */
298     char *myargv[2];
299     int err = 0;
300     int which;
301     char *repository;
302     char *where;
303
304     if (is_rtag)
305     {
306         repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
307                               + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
308         (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
309         where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
310                          + 1);
311         (void) strcpy (where, argv[0]);
312
313         /* if mfile isn't null, we need to set up to do only part of the module */
314         if (mfile != NULL)
315         {
316             char *cp;
317             char *path;
318
319             /* if the portion of the module is a path, put the dir part on repos */
320             if ((cp = strrchr (mfile, '/')) != NULL)
321             {
322                 *cp = '\0';
323                 (void) strcat (repository, "/");
324                 (void) strcat (repository, mfile);
325                 (void) strcat (where, "/");
326                 (void) strcat (where, mfile);
327                 mfile = cp + 1;
328             }
329
330             /* take care of the rest */
331             path = xmalloc (strlen (repository) + strlen (mfile) + 5);
332             (void) sprintf (path, "%s/%s", repository, mfile);
333             if (isdir (path))
334             {
335                 /* directory means repository gets the dir tacked on */
336                 (void) strcpy (repository, path);
337                 (void) strcat (where, "/");
338                 (void) strcat (where, mfile);
339             }
340             else
341             {
342                 myargv[0] = argv[0];
343                 myargv[1] = mfile;
344                 argc = 2;
345                 argv = myargv;
346             }
347             free (path);
348         }
349
350         /* cd to the starting repository */
351         if ( CVS_CHDIR (repository) < 0)
352         {
353             error (0, errno, "cannot chdir to %s", repository);
354             free (repository);
355             return (1);
356         }
357         /* End section which is identical to patch_proc.  */
358
359         if (delete_flag || attic_too || (force_tag_match && numtag))
360             which = W_REPOS | W_ATTIC;
361         else
362             which = W_REPOS;
363     }
364     else
365     {
366         where = NULL;
367         which = W_LOCAL;
368         repository = "";
369     }
370
371     if (numtag != NULL && !numtag_validated)
372     {
373         tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0, repository);
374         numtag_validated = 1;
375     }
376
377     /* check to make sure they are authorized to tag all the 
378        specified files in the repository */
379
380     mtlist = getlist();
381     err = start_recursion (check_fileproc, check_filesdoneproc,
382                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
383                            argc - 1, argv + 1, local_specified, which, 0,
384                            CVS_LOCK_READ, where, 1, repository);
385     
386     if (err)
387     {
388        error (1, 0, "correct the above errors first!");
389     }
390      
391     /* It would be nice to provide consistency with respect to
392        commits; however CVS lacks the infrastructure to do that (see
393        Concurrency in cvs.texinfo and comment in do_recursion).  */
394
395     /* start the recursion processor */
396     err = start_recursion (is_rtag ? rtag_fileproc : tag_fileproc,
397                            (FILESDONEPROC) NULL, tag_dirproc,
398                            (DIRLEAVEPROC) NULL, NULL, argc - 1, argv + 1,
399                            local_specified, which, 0, CVS_LOCK_WRITE, where, 1,
400                            repository);
401     if ( which & W_REPOS ) free ( repository );
402     dellist (&mtlist);
403     if (where != NULL)
404         free (where);
405     return (err);
406 }
407
408 /* check file that is to be tagged */
409 /* All we do here is add it to our list */
410
411 static int
412 check_fileproc (callerdat, finfo)
413     void *callerdat;
414     struct file_info *finfo;
415 {
416     const char *xdir;
417     Node *p;
418     Vers_TS *vers;
419     
420     if (check_uptodate) 
421     {
422         switch (Classify_File (finfo, (char *) NULL, (char *) NULL,
423                                       (char *) NULL, 1, 0, &vers, 0))
424         {
425         case T_UPTODATE:
426         case T_CHECKOUT:
427         case T_PATCH:
428         case T_REMOVE_ENTRY:
429             break;
430         case T_UNKNOWN:
431         case T_CONFLICT:
432         case T_NEEDS_MERGE:
433         case T_MODIFIED:
434         case T_ADDED:
435         case T_REMOVED:
436         default:
437             error (0, 0, "%s is locally modified", finfo->fullname);
438             freevers_ts (&vers);
439             return (1);
440         }
441     }
442     else
443         vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
444     
445     if (finfo->update_dir[0] == '\0')
446         xdir = ".";
447     else
448         xdir = finfo->update_dir;
449     if ((p = findnode (mtlist, xdir)) != NULL)
450     {
451         tlist = ((struct master_lists *) p->data)->tlist;
452     }
453     else
454     {
455         struct master_lists *ml;
456         
457         tlist = getlist ();
458         p = getnode ();
459         p->key = xstrdup (xdir);
460         p->type = UPDATE;
461         ml = (struct master_lists *)
462             xmalloc (sizeof (struct master_lists));
463         ml->tlist = tlist;
464         p->data = ml;
465         p->delproc = masterlist_delproc;
466         (void) addnode (mtlist, p);
467     }
468     /* do tlist */
469     p = getnode ();
470     p->key = xstrdup (finfo->file);
471     p->type = UPDATE;
472     p->delproc = tag_delproc;
473     if (vers->srcfile == NULL)
474     {
475         if (!really_quiet)
476             error (0, 0, "nothing known about %s", finfo->file);
477         freevers_ts (&vers);
478         freenode (p);
479         return (1);
480     }
481
482     /* Here we duplicate the calculation in tag_fileproc about which
483        version we are going to tag.  There probably are some subtle races
484        (e.g. numtag is "foo" which gets moved between here and
485        tag_fileproc).  */
486     if (!is_rtag && numtag == NULL && date == NULL)
487         p->data = xstrdup (vers->vn_user);
488     else
489         p->data = RCS_getversion (vers->srcfile, numtag, date,
490                                   force_tag_match, NULL);
491
492     if (p->data != NULL)
493     {
494         int addit = 1;
495         char *oversion;
496         
497         oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
498                                    (int *) NULL);
499         if (oversion == NULL) 
500         {
501             if (delete_flag)
502             {
503                 /* Deleting a tag which did not exist is a noop and
504                    should not be logged.  */
505                 addit = 0;
506             }
507         }
508         else if (delete_flag)
509         {
510             free (p->data);
511             p->data = xstrdup (oversion);
512         }
513         else if (strcmp(oversion, p->data) == 0)
514         {
515             addit = 0;
516         }
517         else if (!force_tag_move)
518         {
519             addit = 0;
520         }
521         if (oversion != NULL)
522         {
523             free(oversion);
524         }
525         if (!addit)
526         {
527             free(p->data);
528             p->data = NULL;
529         }
530     }
531     freevers_ts (&vers);
532     (void) addnode (tlist, p);
533     return (0);
534 }
535                          
536 static int
537 check_filesdoneproc (callerdat, err, repos, update_dir, entries)
538     void *callerdat;
539     int err;
540     const char *repos;
541     const char *update_dir;
542     List *entries;
543 {
544     int n;
545     Node *p;
546
547     p = findnode(mtlist, update_dir);
548     if (p != NULL)
549     {
550         tlist = ((struct master_lists *) p->data)->tlist;
551     }
552     else
553     {
554         tlist = (List *) NULL;
555     }
556     if ((tlist == NULL) || (tlist->list->next == tlist->list))
557     {
558         return (err);
559     }
560     if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0)
561     {
562         error (0, 0, "Pre-tag check failed");
563         err += n;
564     }
565     return (err);
566 }
567
568 static int
569 pretag_proc (repository, filter)
570     const char *repository;
571     const char *filter;
572 {
573     if (filter[0] == '/')
574     {
575         char *s, *cp;
576
577         s = xstrdup(filter);
578         for (cp=s; *cp; cp++)
579         {
580             if (isspace ((unsigned char) *cp))
581             {
582                 *cp = '\0';
583                 break;
584             }
585         }
586         if (!isfile(s))
587         {
588             error (0, errno, "cannot find pre-tag filter '%s'", s);
589             free(s);
590             return (1);
591         }
592         free(s);
593     }
594     run_setup (filter);
595     run_arg (symtag);
596     run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add");
597     run_arg (repository);
598     walklist(tlist, pretag_list_proc, NULL);
599     return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
600 }
601
602 static void
603 masterlist_delproc(p)
604     Node *p;
605 {
606     struct master_lists *ml = p->data;
607
608     dellist(&ml->tlist);
609     free(ml);
610     return;
611 }
612
613 static void
614 tag_delproc(p)
615     Node *p;
616 {
617     if (p->data != NULL)
618     {
619         free(p->data);
620         p->data = NULL;
621     }
622     return;
623 }
624
625 static int
626 pretag_list_proc(p, closure)
627     Node *p;
628     void *closure;
629 {
630     if (p->data != NULL)
631     {
632         run_arg(p->key);
633         run_arg(p->data);
634     }
635     return (0);
636 }
637
638
639 /*
640  * Called to rtag a particular file, as appropriate with the options that were
641  * set above.
642  */
643 /* ARGSUSED */
644 static int
645 rtag_fileproc (callerdat, finfo)
646     void *callerdat;
647     struct file_info *finfo;
648 {
649     RCSNode *rcsfile;
650     char *version, *rev;
651     int retcode = 0;
652
653     /* find the parsed RCS data */
654     if ((rcsfile = finfo->rcs) == NULL)
655         return (1);
656
657     /*
658      * For tagging an RCS file which is a symbolic link, you'd best be
659      * running with RCS 5.6, since it knows how to handle symbolic links
660      * correctly without breaking your link!
661      */
662
663     if (delete_flag)
664         return (rtag_delete (rcsfile));
665
666     /*
667      * If we get here, we are adding a tag.  But, if -a was specified, we
668      * need to check to see if a -r or -D option was specified.  If neither
669      * was specified and the file is in the Attic, remove the tag.
670      */
671     if (attic_too && (!numtag && !date))
672     {
673         if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
674             return (rtag_delete (rcsfile));
675     }
676
677     version = RCS_getversion (rcsfile, numtag, date, force_tag_match,
678                               (int *) NULL);
679     if (version == NULL)
680     {
681         /* If -a specified, clean up any old tags */
682         if (attic_too)
683             (void) rtag_delete (rcsfile);
684
685         if (!quiet && !force_tag_match)
686         {
687             error (0, 0, "cannot find tag `%s' in `%s'",
688                    numtag ? numtag : "head", rcsfile->path);
689             return (1);
690         }
691         return (0);
692     }
693     if (numtag
694         && isdigit ((unsigned char) *numtag)
695         && strcmp (numtag, version) != 0)
696     {
697
698         /*
699          * We didn't find a match for the numeric tag that was specified, but
700          * that's OK.  just pass the numeric tag on to rcs, to be tagged as
701          * specified.  Could get here if one tried to tag "1.1.1" and there
702          * was a 1.1.1 branch with some head revision.  In this case, we want
703          * the tag to reference "1.1.1" and not the revision at the head of
704          * the branch.  Use a symbolic tag for that.
705          */
706         rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
707         retcode = RCS_settag(rcsfile, symtag, numtag);
708         if (retcode == 0)
709             RCS_rewrite (rcsfile, NULL, NULL);
710     }
711     else
712     {
713         char *oversion;
714        
715         /*
716          * As an enhancement for the case where a tag is being re-applied to
717          * a large body of a module, make one extra call to RCS_getversion to
718          * see if the tag is already set in the RCS file.  If so, check to
719          * see if it needs to be moved.  If not, do nothing.  This will
720          * likely save a lot of time when simply moving the tag to the
721          * "current" head revisions of a module -- which I have found to be a
722          * typical tagging operation.
723          */
724         rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
725         oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1,
726                                    (int *) NULL);
727         if (oversion != NULL)
728         {
729             int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
730
731             /*
732              * if versions the same and neither old or new are branches don't
733              * have to do anything
734              */
735             if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
736             {
737                 free (oversion);
738                 free (version);
739                 return (0);
740             }
741           
742             if (!force_tag_move)
743             {
744                 /* we're NOT going to move the tag */
745                 (void) printf ("W %s", finfo->fullname);
746
747                 (void) printf (" : %s already exists on %s %s", 
748                                symtag, isbranch ? "branch" : "version",
749                                oversion);
750                 (void) printf (" : NOT MOVING tag to %s %s\n", 
751                                branch_mode ? "branch" : "version", rev);
752                 free (oversion);
753                 free (version);
754                 if (branch_mode) free(rev);
755                 return (0);
756             }
757             else /* force_tag_move is set and... */
758                 if ((isbranch && !disturb_branch_tags) ||
759                     (!isbranch && disturb_branch_tags))
760             {
761                 error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.", 
762                         finfo->fullname,
763                         isbranch ? "branch" : "non-branch",
764                         symtag, oversion, rev,
765                         isbranch ? "" : " due to `-B' option"); 
766                 if (branch_mode) free(rev);
767                 free (oversion);
768                 free (version);
769                 return (0);
770             }
771             free (oversion);
772         }
773         retcode = RCS_settag(rcsfile, symtag, rev);
774         if (retcode == 0)
775             RCS_rewrite (rcsfile, NULL, NULL);
776     }
777
778     if (retcode != 0)
779     {
780         error (1, retcode == -1 ? errno : 0,
781                "failed to set tag `%s' to revision `%s' in `%s'",
782                symtag, rev, rcsfile->path);
783         if (branch_mode)
784             free (rev);
785         free (version);
786         return (1);
787     }
788     if (branch_mode)
789         free (rev);
790     free (version);
791     return (0);
792 }
793
794 /*
795  * If -d is specified, "force_tag_match" is set, so that this call to
796  * RCS_getversion() will return a NULL version string if the symbolic
797  * tag does not exist in the RCS file.
798  * 
799  * If the -r flag was used, numtag is set, and we only delete the
800  * symtag from files that have numtag.
801  * 
802  * This is done here because it's MUCH faster than just blindly calling
803  * "rcs" to remove the tag... trust me.
804  */
805 static int
806 rtag_delete (rcsfile)
807     RCSNode *rcsfile;
808 {
809     char *version;
810     int retcode, isbranch;
811
812     if (numtag)
813     {
814         version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1,
815                                   (int *) NULL);
816         if (version == NULL)
817             return (0);
818         free (version);
819     }
820
821     version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1,
822                               (int *) NULL);
823     if (version == NULL)
824         return (0);
825     free (version);
826
827
828     isbranch = RCS_nodeisbranch (rcsfile, symtag);
829     if ((isbranch && !disturb_branch_tags) || 
830         (!isbranch && disturb_branch_tags))
831     {
832         if (!quiet)
833             error(0, 0,
834                 "Not removing %s tag `%s' from `%s'%s.", 
835                 isbranch ? "branch" : "non-branch",
836                 symtag, rcsfile->path,
837                 isbranch ? "" : " due to `-B' option"); 
838         return (1);
839     }
840
841     if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
842     {
843         if (!quiet)
844             error (0, retcode == -1 ? errno : 0,
845                    "failed to remove tag `%s' from `%s'", symtag,
846                    rcsfile->path);
847         return (1);
848     }
849     RCS_rewrite (rcsfile, NULL, NULL);
850     return (0);
851 }
852
853
854 /*
855  * Called to tag a particular file (the currently checked out version is
856  * tagged with the specified tag - or the specified tag is deleted).
857  */
858 /* ARGSUSED */
859 static int
860 tag_fileproc (callerdat, finfo)
861     void *callerdat;
862     struct file_info *finfo;
863 {
864     char *version, *oversion;
865     char *nversion = NULL;
866     char *rev;
867     Vers_TS *vers;
868     int retcode = 0;
869     int retval = 0;
870
871     vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
872
873     if ((numtag != NULL) || (date != NULL))
874     {
875         nversion = RCS_getversion(vers->srcfile,
876                                   numtag,
877                                   date,
878                                   force_tag_match,
879                                   (int *) NULL);
880         if (nversion == NULL)
881             goto free_vars_and_return;
882     }
883     if (delete_flag)
884     {
885
886         int isbranch;
887         /*
888          * If -d is specified, "force_tag_match" is set, so that this call to
889          * RCS_getversion() will return a NULL version string if the symbolic
890          * tag does not exist in the RCS file.
891          * 
892          * This is done here because it's MUCH faster than just blindly calling
893          * "rcs" to remove the tag... trust me.
894          */
895
896         version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
897                                   (int *) NULL);
898         if (version == NULL || vers->srcfile == NULL)
899             goto free_vars_and_return;
900
901         free (version);
902
903         isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
904         if ((isbranch && !disturb_branch_tags) || 
905             (!isbranch && disturb_branch_tags))
906         {
907             if (!quiet)
908                 error(0, 0,
909                        "Not removing %s tag `%s' from `%s'%s.", 
910                         isbranch ? "branch" : "non-branch",
911                         symtag, vers->srcfile->path,
912                         isbranch ? "" : " due to `-B' option"); 
913             retval = 1;
914             goto free_vars_and_return;
915         }
916
917         if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0) 
918         {
919             if (!quiet)
920                 error (0, retcode == -1 ? errno : 0,
921                        "failed to remove tag %s from %s", symtag,
922                        vers->srcfile->path);
923             retval = 1;
924             goto free_vars_and_return;
925         }
926         RCS_rewrite (vers->srcfile, NULL, NULL);
927
928         /* warm fuzzies */
929         if (!really_quiet)
930         {
931             cvs_output ("D ", 2);
932             cvs_output (finfo->fullname, 0);
933             cvs_output ("\n", 1);
934         }
935
936         goto free_vars_and_return;
937     }
938
939     /*
940      * If we are adding a tag, we need to know which version we have checked
941      * out and we'll tag that version.
942      */
943     if (nversion == NULL)
944     {
945         version = vers->vn_user;
946     }
947     else
948     {
949         version = nversion;
950     }
951     if (version == NULL)
952     {
953         goto free_vars_and_return;
954     }
955     else if (strcmp (version, "0") == 0)
956     {
957         if (!quiet)
958             error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file);
959         goto free_vars_and_return;
960     }
961     else if (version[0] == '-')
962     {
963         if (!quiet)
964             error (0, 0, "skipping removed but un-commited file `%s'", finfo->file);
965         goto free_vars_and_return;
966     }
967     else if (vers->srcfile == NULL)
968     {
969         if (!quiet)
970             error (0, 0, "cannot find revision control file for `%s'", finfo->file);
971         goto free_vars_and_return;
972     }
973
974     /*
975      * As an enhancement for the case where a tag is being re-applied to a
976      * large number of files, make one extra call to RCS_getversion to see
977      * if the tag is already set in the RCS file.  If so, check to see if it
978      * needs to be moved.  If not, do nothing.  This will likely save a lot of
979      * time when simply moving the tag to the "current" head revisions of a
980      * module -- which I have found to be a typical tagging operation.
981      */
982     rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
983     oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
984                                (int *) NULL);
985     if (oversion != NULL)
986     {
987         int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
988
989         /*
990          * if versions the same and neither old or new are branches don't have 
991          * to do anything
992          */
993         if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
994         {
995             free (oversion);
996             if (branch_mode)
997                 free (rev);
998             goto free_vars_and_return;
999         }
1000
1001         if (!force_tag_move)
1002         {
1003             /* we're NOT going to move the tag */
1004             cvs_output ("W ", 2);
1005             cvs_output (finfo->fullname, 0);
1006             cvs_output (" : ", 0);
1007             cvs_output (symtag, 0);
1008             cvs_output (" already exists on ", 0);
1009             cvs_output (isbranch ? "branch" : "version", 0);
1010             cvs_output (" ", 0);
1011             cvs_output (oversion, 0);
1012             cvs_output (" : NOT MOVING tag to ", 0);
1013             cvs_output (branch_mode ? "branch" : "version", 0);
1014             cvs_output (" ", 0);
1015             cvs_output (rev, 0);
1016             cvs_output ("\n", 1);
1017             free (oversion);
1018             if (branch_mode)
1019                 free (rev);
1020             goto free_vars_and_return;
1021         }
1022         else    /* force_tag_move == 1 and... */
1023                 if ((isbranch && !disturb_branch_tags) ||
1024                     (!isbranch && disturb_branch_tags))
1025         {
1026             error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.", 
1027                     finfo->fullname,
1028                     isbranch ? "branch" : "non-branch",
1029                     symtag, oversion, rev,
1030                     isbranch ? "" : " due to `-B' option"); 
1031             free (oversion);
1032             if (branch_mode)
1033                 free (rev);
1034             goto free_vars_and_return;
1035         }
1036         free (oversion);
1037     }
1038
1039     if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
1040     {
1041         error (1, retcode == -1 ? errno : 0,
1042                "failed to set tag %s to revision %s in %s",
1043                symtag, rev, vers->srcfile->path);
1044         if (branch_mode)
1045             free (rev);
1046         retval = 1;
1047         goto free_vars_and_return;
1048     }
1049     if (branch_mode)
1050         free (rev);
1051     RCS_rewrite (vers->srcfile, NULL, NULL);
1052
1053     /* more warm fuzzies */
1054     if (!really_quiet)
1055     {
1056         cvs_output ("T ", 2);
1057         cvs_output (finfo->fullname, 0);
1058         cvs_output ("\n", 1);
1059     }
1060
1061  free_vars_and_return:
1062     if (nversion != NULL)
1063         free (nversion);
1064     freevers_ts (&vers);
1065     return (retval);
1066 }
1067
1068 /*
1069  * Print a warm fuzzy message
1070  */
1071 /* ARGSUSED */
1072 static Dtype
1073 tag_dirproc (callerdat, dir, repos, update_dir, entries)
1074     void *callerdat;
1075     const char *dir;
1076     const char *repos;
1077     const char *update_dir;
1078     List *entries;
1079 {
1080
1081     if (ignore_directory (update_dir))
1082     {
1083         /* print the warm fuzzy message */
1084         if (!quiet)
1085           error (0, 0, "Ignoring %s", update_dir);
1086         return R_SKIP_ALL;
1087     }
1088
1089     if (!quiet)
1090         error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir);
1091     return (R_PROCESS);
1092 }
1093 \f
1094 /* Code relating to the val-tags file.  Note that this file has no way
1095    of knowing when a tag has been deleted.  The problem is that there
1096    is no way of knowing whether a tag still exists somewhere, when we
1097    delete it some places.  Using per-directory val-tags files (in
1098    CVSREP) might be better, but that might slow down the process of
1099    verifying that a tag is correct (maybe not, for the likely cases,
1100    if carefully done), and/or be harder to implement correctly.  */
1101
1102 struct val_args {
1103     char *name;
1104     int found;
1105 };
1106
1107 static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo));
1108
1109 static int
1110 val_fileproc (callerdat, finfo)
1111     void *callerdat;
1112     struct file_info *finfo;
1113 {
1114     RCSNode *rcsdata;
1115     struct val_args *args = (struct val_args *)callerdat;
1116     char *tag;
1117
1118     if ((rcsdata = finfo->rcs) == NULL)
1119         /* Not sure this can happen, after all we passed only
1120            W_REPOS | W_ATTIC.  */
1121         return 0;
1122
1123     tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL);
1124     if (tag != NULL)
1125     {
1126         /* FIXME: should find out a way to stop the search at this point.  */
1127         args->found = 1;
1128         free (tag);
1129     }
1130     return 0;
1131 }
1132
1133
1134
1135 static Dtype val_direntproc PROTO ((void *, const char *, const char *,
1136                                     const char *, List *));
1137
1138 static Dtype
1139 val_direntproc (callerdat, dir, repository, update_dir, entries)
1140     void *callerdat;
1141     const char *dir;
1142     const char *repository;
1143     const char *update_dir;
1144     List *entries;
1145 {
1146     /* This is not quite right--it doesn't get right the case of "cvs
1147        update -d -r foobar" where foobar is a tag which exists only in
1148        files in a directory which does not exist yet, but which is
1149        about to be created.  */
1150     if (isdir (dir))
1151         return R_PROCESS;
1152     return R_SKIP_ALL;
1153 }
1154
1155 /* Check to see whether NAME is a valid tag.  If so, return.  If not
1156    print an error message and exit.  ARGC, ARGV, LOCAL, and AFLAG specify
1157    which files we will be operating on.
1158
1159    REPOSITORY is the repository if we need to cd into it, or NULL if
1160    we are already there, or "" if we should do a W_LOCAL recursion.
1161    Sorry for three cases, but the "" case is needed in case the
1162    working directories come from diverse parts of the repository, the
1163    NULL case avoids an unneccesary chdir, and the non-NULL, non-""
1164    case is needed for checkout, where we don't want to chdir if the
1165    tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
1166    local directory.  */
1167 void
1168 tag_check_valid (name, argc, argv, local, aflag, repository)
1169     char *name;
1170     int argc;
1171     char **argv;
1172     int local;
1173     int aflag;
1174     char *repository;
1175 {
1176     DBM *db;
1177     char *valtags_filename;
1178     int nowrite = 0;
1179     datum mytag;
1180     struct val_args the_val_args;
1181     struct saved_cwd cwd;
1182     int which;
1183
1184     /* Numeric tags require only a syntactic check.  */
1185     if (isdigit ((unsigned char) name[0]))
1186     {
1187         char *p;
1188         for (p = name; *p != '\0'; ++p)
1189         {
1190             if (!(isdigit ((unsigned char) *p) || *p == '.'))
1191                 error (1, 0, "\
1192 Numeric tag %s contains characters other than digits and '.'", name);
1193         }
1194         return;
1195     }
1196
1197     /* Special tags are always valid.  */
1198     if (strcmp (name, TAG_BASE) == 0
1199         || strcmp (name, TAG_HEAD) == 0)
1200         return;
1201
1202     /* FIXME: This routine doesn't seem to do any locking whatsoever
1203        (and it is called from places which don't have locks in place).
1204        If two processes try to write val-tags at the same time, it would
1205        seem like we are in trouble.  */
1206
1207     mytag.dptr = name;
1208     mytag.dsize = strlen (name);
1209
1210     valtags_filename = xmalloc (strlen (current_parsed_root->directory)
1211                                 + sizeof CVSROOTADM
1212                                 + sizeof CVSROOTADM_VALTAGS + 3);
1213     sprintf (valtags_filename, "%s/%s/%s", current_parsed_root->directory,
1214                                            CVSROOTADM, CVSROOTADM_VALTAGS);
1215     db = dbm_open (valtags_filename, O_RDWR, 0666);
1216     if (db == NULL)
1217     {
1218         if (!existence_error (errno))
1219         {
1220             error (0, errno, "warning: cannot open %s read/write",
1221                    valtags_filename);
1222             db = dbm_open (valtags_filename, O_RDONLY, 0666);
1223             if (db != NULL)
1224                 nowrite = 1;
1225             else if (!existence_error (errno))
1226                 error (1, errno, "cannot read %s", valtags_filename);
1227         }
1228         /* If the file merely fails to exist, we just keep going and create
1229            it later if need be.  */
1230     }
1231     if (db != NULL)
1232     {
1233         datum val;
1234
1235         val = dbm_fetch (db, mytag);
1236         if (val.dptr != NULL)
1237         {
1238             /* Found.  The tag is valid.  */
1239             dbm_close (db);
1240             free (valtags_filename);
1241             return;
1242         }
1243         /* FIXME: should check errors somehow (add dbm_error to myndbm.c?).  */
1244     }
1245
1246     /* We didn't find the tag in val-tags, so look through all the RCS files
1247        to see whether it exists there.  Yes, this is expensive, but there
1248        is no other way to cope with a tag which might have been created
1249        by an old version of CVS, from before val-tags was invented.
1250
1251        Since we need this code anyway, we also use it to create
1252        entries in val-tags in general (that is, the val-tags entry
1253        will get created the first time the tag is used, not when the
1254        tag is created).  */
1255
1256     the_val_args.name = name;
1257     the_val_args.found = 0;
1258
1259     which = W_REPOS | W_ATTIC;
1260
1261     if (repository != NULL)
1262     {
1263         if (repository[0] == '\0')
1264             which |= W_LOCAL;
1265         else
1266         {
1267             if (save_cwd (&cwd))
1268                 error_exit ();
1269             if (CVS_CHDIR (repository) < 0)
1270                 error (1, errno, "cannot change to %s directory", repository);
1271         }
1272     }
1273
1274     start_recursion (val_fileproc, (FILESDONEPROC) NULL,
1275                      val_direntproc, (DIRLEAVEPROC) NULL,
1276                      (void *)&the_val_args,
1277                      argc, argv, local, which, aflag,
1278                      CVS_LOCK_READ, NULL, 1, repository);
1279     if (repository != NULL && repository[0] != '\0')
1280     {
1281         if (restore_cwd (&cwd, NULL))
1282             error_exit ();
1283         free_cwd (&cwd);
1284     }
1285
1286     if (!the_val_args.found)
1287         error (1, 0, "no such tag %s", name);
1288     else
1289     {
1290         /* The tags is valid but not mentioned in val-tags.  Add it.  */
1291         datum value;
1292
1293         if (noexec || nowrite)
1294         {
1295             if (db != NULL)
1296                 dbm_close (db);
1297             free (valtags_filename);
1298             return;
1299         }
1300
1301         if (db == NULL)
1302         {
1303             mode_t omask;
1304             omask = umask (cvsumask);
1305             db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
1306             (void)umask (omask);
1307
1308             if (db == NULL)
1309             {
1310                 error (0, errno, "warning: cannot create %s", valtags_filename);
1311                 free (valtags_filename);
1312                 return;
1313             }
1314         }
1315         value.dptr = "y";
1316         value.dsize = 1;
1317         if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
1318             error (0, errno, "cannot store %s into %s", name,
1319                    valtags_filename);
1320         dbm_close (db);
1321     }
1322     free (valtags_filename);
1323 }
1324
1325 /*
1326  * Check whether a join tag is valid.  This is just like
1327  * tag_check_valid, but we must stop before the colon if there is one.
1328  */
1329
1330 void
1331 tag_check_valid_join (join_tag, argc, argv, local, aflag, repository)
1332      char *join_tag;
1333      int argc;
1334      char **argv;
1335      int local;
1336      int aflag;
1337      char *repository;
1338 {
1339     char *c, *s;
1340
1341     c = xstrdup (join_tag);
1342     s = strchr (c, ':');
1343     if (s != NULL)
1344     {
1345         if (isdigit ((unsigned char) join_tag[0]))
1346             error (1, 0,
1347                    "Numeric join tag %s may not contain a date specifier",
1348                    join_tag);
1349
1350         *s = '\0';
1351         /* hmmm...  I think it makes sense to allow -j:<date>, but
1352          * for now this fixes a bug where CVS just spins and spins (I
1353          * think in the RCS code) looking for a zero length tag.
1354          */
1355         if (!*c)
1356             error (1, 0,
1357                    "argument to join may not contain a date specifier without a tag");
1358     }
1359
1360     tag_check_valid (c, argc, argv, local, aflag, repository);
1361
1362     free (c);
1363 }