home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / cvs-1.8.7-src.tgz / tar.out / fsf / cvs / src / tag.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  23KB  |  933 lines

  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 1.4 kit.
  7.  * 
  8.  * Tag
  9.  * 
  10.  * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
  11.  * Uses the checked out revision in the current directory.
  12.  */
  13.  
  14. #include "cvs.h"
  15. #include "savecwd.h"
  16.  
  17. static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  18. static int check_filesdoneproc PROTO ((void *callerdat, int err,
  19.                        char *repos, char *update_dir,
  20.                        List *entries));
  21. static int pretag_proc PROTO((char *repository, char *filter));
  22. static void masterlist_delproc PROTO((Node *p));
  23. static void tag_delproc PROTO((Node *p));
  24. static int pretag_list_proc PROTO((Node *p, void *closure));
  25.  
  26. static Dtype tag_dirproc PROTO ((void *callerdat, char *dir,
  27.                  char *repos, char *update_dir,
  28.                  List *entries));
  29. static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  30. static int tag_filesdoneproc PROTO ((void *callerdat, int err,
  31.                      char *repos, char *update_dir,
  32.                      List *entries));
  33.  
  34. static char *numtag;
  35. static char *date = NULL;
  36. static char *symtag;
  37. static int delete_flag;            /* adding a tag by default */
  38. static int branch_mode;            /* make an automagic "branch" tag */
  39. static int local;            /* recursive by default */
  40. static int force_tag_match = 1;         /* force tag to match by default */
  41. static int force_tag_move;        /* don't force tag to move by default */
  42. static int check_uptodate;        /* no uptodate-check by default */
  43.  
  44. struct tag_info
  45. {
  46.     Ctype status;
  47.     char *rev;
  48.     char *tag;
  49.     char *options;
  50. };
  51.  
  52. struct master_lists
  53. {
  54.     List *tlist;
  55. };
  56.  
  57. static List *mtlist;
  58. static List *tlist;
  59.  
  60. static const char *const tag_usage[] =
  61. {
  62.     "Usage: %s %s [-lRF] [-b] [-d] [-c] [-r tag|-D date] tag [files...]\n",
  63.     "\t-l\tLocal directory only, not recursive.\n",
  64.     "\t-R\tProcess directories recursively.\n",
  65.     "\t-d\tDelete the given tag.\n",
  66.     "\t-[rD]\tExisting tag or date.\n",
  67.     "\t-f\tForce a head revision if specified tag not found.\n",
  68.     "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
  69.     "\t-F\tMove tag if it already exists.\n",
  70.     "\t-c\tCheck that working files are unmodified.\n",
  71.     NULL
  72. };
  73.  
  74. int
  75. tag (argc, argv)
  76.     int argc;
  77.     char **argv;
  78. {
  79.     int c;
  80.     int err = 0;
  81.  
  82.     if (argc == -1)
  83.     usage (tag_usage);
  84.  
  85.     optind = 1;
  86.     while ((c = getopt (argc, argv, "FQqlRcdr:D:bf")) != -1)
  87.     {
  88.     switch (c)
  89.     {
  90.         case 'Q':
  91.         case 'q':
  92. #ifdef SERVER_SUPPORT
  93.         /* The CVS 1.5 client sends these options (in addition to
  94.            Global_option requests), so we must ignore them.  */
  95.         if (!server_active)
  96. #endif
  97.             error (1, 0,
  98.                "-q or -Q must be specified before \"%s\"",
  99.                command_name);
  100.         break;
  101.         case 'l':
  102.         local = 1;
  103.         break;
  104.         case 'R':
  105.         local = 0;
  106.         break;
  107.         case 'd':
  108.         delete_flag = 1;
  109.         break;
  110.         case 'c':
  111.         check_uptodate = 1;
  112.         break;
  113.             case 'r':
  114.                 numtag = optarg;
  115.                 break;
  116.             case 'D':
  117.                 if (date)
  118.                     free (date);
  119.                 date = Make_Date (optarg);
  120.                 break;
  121.         case 'f':
  122.         force_tag_match = 0;
  123.         break;
  124.         case 'b':
  125.         branch_mode = 1;
  126.         break;
  127.             case 'F':
  128.         force_tag_move = 1;
  129.         break;
  130.         case '?':
  131.         default:
  132.         usage (tag_usage);
  133.         break;
  134.     }
  135.     }
  136.     argc -= optind;
  137.     argv += optind;
  138.  
  139.     if (argc == 0)
  140.     usage (tag_usage);
  141.     symtag = argv[0];
  142.     argc--;
  143.     argv++;
  144.  
  145.     if (date && numtag)
  146.     error (1, 0, "-r and -D options are mutually exclusive");
  147.     if (delete_flag && branch_mode)
  148.     error (0, 0, "warning: -b ignored with -d options");
  149.     RCS_check_tag (symtag);
  150.  
  151. #ifdef CLIENT_SUPPORT
  152.     if (client_active)
  153.     {
  154.     /* We're the client side.  Fire up the remote server.  */
  155.     start_server ();
  156.     
  157.     ign_setup ();
  158.  
  159.     if (local)
  160.         send_arg("-l");
  161.     if (delete_flag)
  162.         send_arg("-d");
  163.     if (check_uptodate)
  164.         send_arg("-c");
  165.     if (branch_mode)
  166.         send_arg("-b");
  167.     if (force_tag_move)
  168.         send_arg("-F");
  169.  
  170.     if (numtag)
  171.         option_with_arg ("-r", numtag);
  172.     if (date)
  173.         client_senddate (date);
  174.  
  175.     send_arg (symtag);
  176.  
  177.     send_file_names (argc, argv, SEND_EXPAND_WILD);
  178.     /* FIXME:  We shouldn't have to send current files, but I'm not sure
  179.        whether it works.  So send the files --
  180.        it's slower but it works.  */
  181.     send_files (argc, argv, local, 0);
  182.     send_to_server ("tag\012", 0);
  183.         return get_responses_and_close ();
  184.     }
  185. #endif
  186.  
  187.     if (numtag != NULL)
  188.     tag_check_valid (numtag, argc, argv, local, 0, "");
  189.  
  190.     /* check to make sure they are authorized to tag all the 
  191.        specified files in the repository */
  192.  
  193.     mtlist = getlist();
  194.     err = start_recursion (check_fileproc, check_filesdoneproc,
  195.                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
  196.                            argc, argv, local, W_LOCAL, 0, 1,
  197.                            (char *) NULL, 1);
  198.     
  199.     if (err)
  200.     {
  201.        error (1, 0, "correct the above errors first!");
  202.     }
  203.      
  204.     /* start the recursion processor */
  205.     err = start_recursion (tag_fileproc, tag_filesdoneproc, tag_dirproc,
  206.                (DIRLEAVEPROC) NULL, NULL, argc, argv, local,
  207.                W_LOCAL, 0, 0, (char *) NULL, 1);
  208.     dellist(&mtlist);
  209.     return (err);
  210. }
  211.  
  212. /* check file that is to be tagged */
  213. /* All we do here is add it to our list */
  214.  
  215. static int
  216. check_fileproc (callerdat, finfo)
  217.     void *callerdat;
  218.     struct file_info *finfo;
  219. {
  220.     char *xdir;
  221.     Node *p;
  222.     Vers_TS *vers;
  223.     
  224.     if (check_uptodate) 
  225.     {
  226.     Ctype status = Classify_File (finfo, (char *) NULL, (char *) NULL,
  227.                       (char *) NULL, 1, 0, &vers, 0);
  228.     if ((status != T_UPTODATE) && (status != T_CHECKOUT))
  229.     {
  230.         error (0, 0, "%s is locally modified", finfo->fullname);
  231.         return (1);
  232.     }
  233.     }
  234.     
  235.     if (finfo->update_dir[0] == '\0')
  236.     xdir = ".";
  237.     else
  238.     xdir = finfo->update_dir;
  239.     if ((p = findnode (mtlist, xdir)) != NULL)
  240.     {
  241.     tlist = ((struct master_lists *) p->data)->tlist;
  242.     }
  243.     else
  244.     {
  245.     struct master_lists *ml;
  246.         
  247.     tlist = getlist ();
  248.     p = getnode ();
  249.     p->key = xstrdup (xdir);
  250.     p->type = UPDATE;
  251.     ml = (struct master_lists *)
  252.         xmalloc (sizeof (struct master_lists));
  253.     ml->tlist = tlist;
  254.     p->data = (char *) ml;
  255.     p->delproc = masterlist_delproc;
  256.     (void) addnode (mtlist, p);
  257.     }
  258.     /* do tlist */
  259.     p = getnode ();
  260.     p->key = xstrdup (finfo->file);
  261.     p->type = UPDATE;
  262.     p->delproc = tag_delproc;
  263.     vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
  264.     if (vers->srcfile == NULL)
  265.     {
  266.         if (!really_quiet)
  267.         error (0, 0, "nothing known about %s", finfo->file);
  268.     return (1);
  269.     }
  270.     p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0);
  271.     if (p->data != NULL)
  272.     {
  273.         int addit = 1;
  274.         char *oversion;
  275.         
  276.         oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
  277.         if (oversion == NULL) 
  278.         {
  279.             if (delete_flag)
  280.             {
  281.                 addit = 0;
  282.             }
  283.         }
  284.         else if (strcmp(oversion, p->data) == 0)
  285.         {
  286.             addit = 0;
  287.         }
  288.         else if (!force_tag_move)
  289.         {
  290.             addit = 0;
  291.         }
  292.         if (oversion != NULL)
  293.         {
  294.             free(oversion);
  295.         }
  296.         if (!addit)
  297.         {
  298.             free(p->data);
  299.             p->data = NULL;
  300.         }
  301.     }
  302.     freevers_ts(&vers);
  303.     (void) addnode (tlist, p);
  304.     return (0);
  305. }
  306.                          
  307. static int
  308. check_filesdoneproc (callerdat, err, repos, update_dir, entries)
  309.     void *callerdat;
  310.     int err;
  311.     char *repos;
  312.     char *update_dir;
  313.     List *entries;
  314. {
  315.     int n;
  316.     Node *p;
  317.  
  318.     p = findnode(mtlist, update_dir);
  319.     if (p != NULL)
  320.     {
  321.         tlist = ((struct master_lists *) p->data)->tlist;
  322.     }
  323.     else
  324.     {
  325.         tlist = (List *) NULL;
  326.     }
  327.     if ((tlist == NULL) || (tlist->list->next == tlist->list))
  328.     {
  329.         return (err);
  330.     }
  331.     if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0)
  332.     {
  333.         error (0, 0, "Pre-tag check failed");
  334.         err += n;
  335.     }
  336.     return (err);
  337. }
  338.  
  339. static int
  340. pretag_proc(repository, filter)
  341.     char *repository;
  342.     char *filter;
  343. {
  344.     if (filter[0] == '/')
  345.     {
  346.         char *s, *cp;
  347.  
  348.         s = xstrdup(filter);
  349.         for (cp=s; *cp; cp++)
  350.         {
  351.             if (isspace(*cp))
  352.             {
  353.                 *cp = '\0';
  354.                 break;
  355.             }
  356.         }
  357.         if (!isfile(s))
  358.         {
  359.             error (0, errno, "cannot find pre-tag filter '%s'", s);
  360.             free(s);
  361.             return (1);
  362.         }
  363.         free(s);
  364.     }
  365.     run_setup("%s %s %s %s",
  366.               filter,
  367.               symtag,
  368.               delete_flag ? "del" : force_tag_move ? "mov" : "add",
  369.               repository);
  370.     walklist(tlist, pretag_list_proc, NULL);
  371.     return (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
  372. }
  373.  
  374. static void
  375. masterlist_delproc(p)
  376.     Node *p;
  377. {
  378.     struct master_lists *ml;
  379.  
  380.     ml = (struct master_lists *)p->data;
  381.     dellist(&ml->tlist);
  382.     free(ml);
  383.     return;
  384. }
  385.  
  386. static void
  387. tag_delproc(p)
  388.     Node *p;
  389. {
  390.     if (p->data != NULL)
  391.     {
  392.         free(p->data);
  393.         p->data = NULL;
  394.     }
  395.     return;
  396. }
  397.  
  398. static int
  399. pretag_list_proc(p, closure)
  400.     Node *p;
  401.     void *closure;
  402. {
  403.     if (p->data != NULL)
  404.     {
  405.         run_arg(p->key);
  406.         run_arg(p->data);
  407.     }
  408.     return (0);
  409. }
  410.  
  411.  
  412. /*
  413.  * Called to tag a particular file (the currently checked out version is
  414.  * tagged with the specified tag - or the specified tag is deleted).
  415.  */
  416. /* ARGSUSED */
  417. static int
  418. tag_fileproc (callerdat, finfo)
  419.     void *callerdat;
  420.     struct file_info *finfo;
  421. {
  422.     char *version, *oversion;
  423.     char *nversion = NULL;
  424.     char *rev;
  425.     Vers_TS *vers;
  426.     int retcode = 0;
  427.  
  428.     /* Lock the directory if it is not already locked.  We can't rely
  429.        on tag_dirproc because it won't handle the case where the user
  430.        specifies a list of files on the command line.  */
  431.     tag_lockdir (finfo->repository);
  432.  
  433.     vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
  434.  
  435.     if ((numtag != NULL) || (date != NULL))
  436.     {
  437.         nversion = RCS_getversion(vers->srcfile,
  438.                                   numtag,
  439.                                   date,
  440.                                   force_tag_match, 0);
  441.         if (nversion == NULL)
  442.         {
  443.         freevers_ts (&vers);
  444.             return (0);
  445.         }
  446.     }
  447.     if (delete_flag)
  448.     {
  449.  
  450.     /*
  451.      * If -d is specified, "force_tag_match" is set, so that this call to
  452.      * RCS_getversion() will return a NULL version string if the symbolic
  453.      * tag does not exist in the RCS file.
  454.      * 
  455.      * This is done here because it's MUCH faster than just blindly calling
  456.      * "rcs" to remove the tag... trust me.
  457.      */
  458.  
  459.     version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
  460.     if (version == NULL || vers->srcfile == NULL)
  461.     {
  462.         freevers_ts (&vers);
  463.         return (0);
  464.     }
  465.     free (version);
  466.  
  467.     if ((retcode = RCS_deltag(vers->srcfile->path, symtag, 1)) != 0) 
  468.     {
  469.         if (!quiet)
  470.         error (0, retcode == -1 ? errno : 0,
  471.                "failed to remove tag %s from %s", symtag,
  472.                vers->srcfile->path);
  473.         freevers_ts (&vers);
  474.         return (1);
  475.     }
  476.  
  477.     /* warm fuzzies */
  478.     if (!really_quiet)
  479.     {
  480.         (void) printf ("D %s\n", finfo->fullname);
  481.     }
  482.  
  483.     freevers_ts (&vers);
  484.     return (0);
  485.     }
  486.  
  487.     /*
  488.      * If we are adding a tag, we need to know which version we have checked
  489.      * out and we'll tag that version.
  490.      */
  491.     if (nversion == NULL)
  492.     {
  493.         version = vers->vn_user;
  494.     }
  495.     else
  496.     {
  497.         version = nversion;
  498.     }
  499.     if (version == NULL)
  500.     {
  501.     freevers_ts (&vers);
  502.     return (0);
  503.     }
  504.     else if (strcmp (version, "0") == 0)
  505.     {
  506.     if (!quiet)
  507.         error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file);
  508.     freevers_ts (&vers);
  509.     return (0);
  510.     }
  511.     else if (version[0] == '-')
  512.     {
  513.     if (!quiet)
  514.         error (0, 0, "skipping removed but un-commited file `%s'", finfo->file);
  515.     freevers_ts (&vers);
  516.     return (0);
  517.     }
  518.     else if (vers->srcfile == NULL)
  519.     {
  520.     if (!quiet)
  521.         error (0, 0, "cannot find revision control file for `%s'", finfo->file);
  522.     freevers_ts (&vers);
  523.     return (0);
  524.     }
  525.  
  526.     /*
  527.      * As an enhancement for the case where a tag is being re-applied to a
  528.      * large number of files, make one extra call to RCS_getversion to see
  529.      * if the tag is already set in the RCS file.  If so, check to see if it
  530.      * needs to be moved.  If not, do nothing.  This will likely save a lot of
  531.      * time when simply moving the tag to the "current" head revisions of a
  532.      * module -- which I have found to be a typical tagging operation.
  533.      */
  534.     rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
  535.     oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
  536.     if (oversion != NULL)
  537.     {
  538.     int isbranch = RCS_isbranch (finfo->rcs, symtag);
  539.  
  540.     /*
  541.      * if versions the same and neither old or new are branches don't have 
  542.      * to do anything
  543.      */
  544.     if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
  545.     {
  546.         free (oversion);
  547.         freevers_ts (&vers);
  548.         return (0);
  549.     }
  550.  
  551.     if (!force_tag_move)
  552.     {
  553.         /* we're NOT going to move the tag */
  554.         (void) printf ("W %s", finfo->fullname);
  555.  
  556.         (void) printf (" : %s already exists on %s %s", 
  557.                symtag, isbranch ? "branch" : "version", oversion);
  558.         (void) printf (" : NOT MOVING tag to %s %s\n", 
  559.                branch_mode ? "branch" : "version", rev);
  560.         free (oversion);
  561.         freevers_ts (&vers);
  562.         return (0);
  563.     }
  564.     free (oversion);
  565.     }
  566.  
  567.     if ((retcode = RCS_settag(vers->srcfile->path, symtag, rev)) != 0)
  568.     {
  569.     error (1, retcode == -1 ? errno : 0,
  570.            "failed to set tag %s to revision %s in %s",
  571.            symtag, rev, vers->srcfile->path);
  572.     freevers_ts (&vers);
  573.     return (1);
  574.     }
  575.  
  576.     /* more warm fuzzies */
  577.     if (!really_quiet)
  578.     {
  579.     (void) printf ("T %s\n", finfo->fullname);
  580.     }
  581.  
  582.     if (nversion != NULL)
  583.     {
  584.         free (nversion);
  585.     }
  586.     freevers_ts (&vers);
  587.     return (0);
  588. }
  589.  
  590. /* Clear any lock we may hold on the current directory.  */
  591.  
  592. static int
  593. tag_filesdoneproc (callerdat, err, repos, update_dir, entries)
  594.     void *callerdat;
  595.     int err;
  596.     char *repos;
  597.     char *update_dir;
  598.     List *entries;
  599. {
  600.     tag_unlockdir ();
  601.  
  602.     return (err);
  603. }
  604.  
  605. /*
  606.  * Print a warm fuzzy message
  607.  */
  608. /* ARGSUSED */
  609. static Dtype
  610. tag_dirproc (callerdat, dir, repos, update_dir, entries)
  611.     void *callerdat;
  612.     char *dir;
  613.     char *repos;
  614.     char *update_dir;
  615.     List *entries;
  616. {
  617.     if (!quiet)
  618.     error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir);
  619.     return (R_PROCESS);
  620. }
  621.  
  622. /* We do not need to acquire a full write lock for the tag operation:
  623.    the revisions are obtained from the working directory, so we do not
  624.    require consistency across the entire repository.  However, we do
  625.    need to prevent simultaneous tag operations from interfering with
  626.    each other.  Therefore, we write lock each directory as we enter
  627.    it, and unlock it as we leave it.
  628.  
  629.    In the rtag case, it would be nice to provide consistency with
  630.    respect to commits; however CVS lacks the infrastructure to do that
  631.    (see Concurrency in cvs.texinfo and comment in do_recursion).  We
  632.    can and will prevent simultaneous tag operations from interfering
  633.    with each other, by write locking each directory as we enter it,
  634.    and unlocking it as we leave it.  */
  635. static char *locked_dir;
  636. static List *locked_list;
  637.  
  638. /*
  639.  * Lock the directory for a tag operation.  This is also called by the
  640.  * rtag code.
  641.  */
  642. void
  643. tag_lockdir (repository)
  644.      char *repository;
  645. {
  646.     if (repository != NULL
  647.     && (locked_dir == NULL
  648.         || strcmp (locked_dir, repository) != 0))
  649.     {
  650.         Node *node;
  651.  
  652.     if (locked_dir != NULL)
  653.         tag_unlockdir ();
  654.  
  655.     locked_dir = xstrdup (repository);
  656.     locked_list = getlist ();
  657.     node = getnode ();
  658.     node->type = LOCK;
  659.     node->key = xstrdup (repository);
  660.     (void) addnode (locked_list, node);
  661.     Writer_Lock (locked_list);
  662.     }
  663. }
  664.  
  665. /*
  666.  * Unlock the directory for a tag operation.  This is also called by
  667.  * the rtag code.
  668.  */
  669. void
  670. tag_unlockdir ()
  671. {
  672.     if (locked_dir != NULL)
  673.     {
  674.         Lock_Cleanup ();
  675.     dellist (&locked_list);
  676.     free (locked_dir);
  677.     locked_dir = NULL;
  678.     locked_list = NULL;
  679.     }
  680. }
  681.  
  682. /* Code relating to the val-tags file.  Note that this file has no way
  683.    of knowing when a tag has been deleted.  The problem is that there
  684.    is no way of knowing whether a tag still exists somewhere, when we
  685.    delete it some places.  Using per-directory val-tags files (in
  686.    CVSREP) might be better, but that might slow down the process of
  687.    verifying that a tag is correct (maybe not, for the likely cases,
  688.    if carefully done), and/or be harder to implement correctly.  */
  689.  
  690. struct val_args {
  691.     char *name;
  692.     int found;
  693. };
  694.  
  695. static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  696.  
  697. static int
  698. val_fileproc (callerdat, finfo)
  699.     void *callerdat;
  700.     struct file_info *finfo;
  701. {
  702.     RCSNode *rcsdata;
  703.     struct val_args *args = (struct val_args *)callerdat;
  704.     char *tag;
  705.  
  706.     if ((rcsdata = finfo->rcs) == NULL)
  707.     /* Not sure this can happen, after all we passed only
  708.        W_REPOS | W_ATTIC.  */
  709.     return 0;
  710.  
  711.     tag = RCS_gettag (rcsdata, args->name, 1, 0);
  712.     if (tag != NULL)
  713.     {
  714.     /* FIXME: should find out a way to stop the search at this point.  */
  715.     args->found = 1;
  716.     free (tag);
  717.     }
  718.     return 0;
  719. }
  720.  
  721. static Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *));
  722.  
  723. static Dtype
  724. val_direntproc (callerdat, dir, repository, update_dir, entries)
  725.     void *callerdat;
  726.     char *dir;
  727.     char *repository;
  728.     char *update_dir;
  729.     List *entries;
  730. {
  731.     /* This is not quite right--it doesn't get right the case of "cvs
  732.        update -d -r foobar" where foobar is a tag which exists only in
  733.        files in a directory which does not exist yet, but which is
  734.        about to be created.  */
  735.     if (isdir (dir))
  736.     return 0;
  737.     return R_SKIP_ALL;
  738. }
  739.  
  740. /* Check to see whether NAME is a valid tag.  If so, return.  If not
  741.    print an error message and exit.  ARGC, ARGV, LOCAL, and AFLAG specify
  742.    which files we will be operating on.
  743.  
  744.    REPOSITORY is the repository if we need to cd into it, or NULL if
  745.    we are already there, or "" if we should do a W_LOCAL recursion.
  746.    Sorry for three cases, but the "" case is needed in case the
  747.    working directories come from diverse parts of the repository, the
  748.    NULL case avoids an unneccesary chdir, and the non-NULL, non-""
  749.    case is needed for checkout, where we don't want to chdir if the
  750.    tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
  751.    local directory.  */
  752. void
  753. tag_check_valid (name, argc, argv, local, aflag, repository)
  754.     char *name;
  755.     int argc;
  756.     char **argv;
  757.     int local;
  758.     int aflag;
  759.     char *repository;
  760. {
  761.     DBM *db;
  762.     char *valtags_filename;
  763.     int err;
  764.     datum mytag;
  765.     struct val_args the_val_args;
  766.     struct saved_cwd cwd;
  767.     int which;
  768.  
  769.     /* Numeric tags require only a syntactic check.  */
  770.     if (isdigit (name[0]))
  771.     {
  772.     char *p;
  773.     for (p = name; *p != '\0'; ++p)
  774.     {
  775.         if (!(isdigit (*p) || *p == '.'))
  776.         error (1, 0, "\
  777. Numeric tag %s contains characters other than digits and '.'", name);
  778.     }
  779.     return;
  780.     }
  781.  
  782.     /* Special tags are always valid.  */
  783.     if (strcmp (name, TAG_BASE) == 0
  784.     || strcmp (name, TAG_HEAD) == 0)
  785.     return;
  786.  
  787.     mytag.dptr = name;
  788.     mytag.dsize = strlen (name);
  789.  
  790.     valtags_filename = xmalloc (strlen (CVSroot_directory)
  791.                 + sizeof CVSROOTADM
  792.                 + sizeof CVSROOTADM_VALTAGS + 20);
  793.     strcpy (valtags_filename, CVSroot_directory);
  794.     strcat (valtags_filename, "/");
  795.     strcat (valtags_filename, CVSROOTADM);
  796.     strcat (valtags_filename, "/");
  797.     strcat (valtags_filename, CVSROOTADM_VALTAGS);
  798.     db = dbm_open (valtags_filename, O_RDWR, 0666);
  799.     if (db == NULL)
  800.     {
  801.     if (!existence_error (errno))
  802.         error (1, errno, "cannot read %s", valtags_filename);
  803.  
  804.     /* If the file merely fails to exist, we just keep going and create
  805.        it later if need be.  */
  806.     }
  807.     else
  808.     {
  809.     datum val;
  810.  
  811.     val = dbm_fetch (db, mytag);
  812.     if (val.dptr != NULL)
  813.     {
  814.         /* Found.  The tag is valid.  */
  815.         dbm_close (db);
  816.         free (valtags_filename);
  817.         return;
  818.     }
  819.     /* FIXME: should check errors somehow (add dbm_error to myndbm.c?).  */
  820.     }
  821.  
  822.     /* We didn't find the tag in val-tags, so look through all the RCS files
  823.        to see whether it exists there.  Yes, this is expensive, but there
  824.        is no other way to cope with a tag which might have been created
  825.        by an old version of CVS, from before val-tags was invented.
  826.  
  827.        Since we need this code anyway, we also use it to create
  828.        entries in val-tags in general (that is, the val-tags entry
  829.        will get created the first time the tag is used, not when the
  830.        tag is created).  */
  831.  
  832.     the_val_args.name = name;
  833.     the_val_args.found = 0;
  834.  
  835.     which = W_REPOS | W_ATTIC;
  836.  
  837.     if (repository != NULL)
  838.     {
  839.     if (repository[0] == '\0')
  840.         which |= W_LOCAL;
  841.     else
  842.     {
  843.         if (save_cwd (&cwd))
  844.         exit (EXIT_FAILURE);
  845.         if ( CVS_CHDIR (repository) < 0)
  846.         error (1, errno, "cannot change to %s directory", repository);
  847.     }
  848.     }
  849.  
  850.     err = start_recursion (val_fileproc, (FILESDONEPROC) NULL,
  851.                val_direntproc, (DIRLEAVEPROC) NULL,
  852.                (void *)&the_val_args,
  853.                argc, argv, local, which, aflag,
  854.                1, NULL, 1);
  855.     if (repository != NULL && repository[0] != '\0')
  856.     {
  857.     if (restore_cwd (&cwd, NULL))
  858.         exit (EXIT_FAILURE);
  859.     free_cwd (&cwd);
  860.     }
  861.  
  862.     if (!the_val_args.found)
  863.     error (1, 0, "no such tag %s", name);
  864.     else
  865.     {
  866.     /* The tags is valid but not mentioned in val-tags.  Add it.  */
  867.     datum value;
  868.  
  869.     if (noexec)
  870.     {
  871.         if (db != NULL)
  872.         dbm_close (db);
  873.         free (valtags_filename);
  874.         return;
  875.     }
  876.  
  877.     if (db == NULL)
  878.     {
  879.         mode_t omask;
  880.         omask = umask (cvsumask);
  881.         db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
  882.         (void) umask (omask);
  883.  
  884.         if (db == NULL)
  885.         {
  886.         error (0, errno, "cannot create %s", valtags_filename);
  887.         free (valtags_filename);
  888.         return;
  889.         }
  890.     }
  891.     value.dptr = "y";
  892.     value.dsize = 1;
  893.     if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
  894.         error (0, errno, "cannot store %s into %s", name,
  895.            valtags_filename);
  896.     dbm_close (db);
  897.     }
  898.     free (valtags_filename);
  899. }
  900.  
  901. /*
  902.  * Check whether a join tag is valid.  This is just like
  903.  * tag_check_valid, but we must stop before the colon if there is one.
  904.  */
  905.  
  906. void
  907. tag_check_valid_join (join_tag, argc, argv, local, aflag, repository)
  908.      char *join_tag;
  909.      int argc;
  910.      char **argv;
  911.      int local;
  912.      int aflag;
  913.      char *repository;
  914. {
  915.     char *c, *s;
  916.  
  917.     c = xstrdup (join_tag);
  918.     s = strchr (c, ':');
  919.     if (s != NULL)
  920.     {
  921.         if (isdigit (join_tag[0]))
  922.         error (1, 0,
  923.            "Numeric join tag %s may not contain a date specifier",
  924.            join_tag);
  925.  
  926.         *s = '\0';
  927.     }
  928.  
  929.     tag_check_valid (c, argc, argv, local, aflag, repository);
  930.  
  931.     free (c);
  932. }
  933.