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 / rtag.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  17KB  |  715 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.  * Rtag
  9.  * 
  10.  * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
  11.  * Uses the modules database, if necessary.
  12.  */
  13.  
  14. #include "cvs.h"
  15.  
  16. static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  17. static int check_filesdoneproc PROTO ((void *callerdat, int err,
  18.                        char *repos, char *update_dir,
  19.                        List *entries));
  20. static int pretag_proc PROTO((char *repository, char *filter));
  21. static void masterlist_delproc PROTO((Node *p));
  22. static void tag_delproc PROTO((Node *p));
  23. static int pretag_list_proc PROTO((Node *p, void *closure));
  24.  
  25. static Dtype rtag_dirproc PROTO ((void *callerdat, char *dir,
  26.                   char *repos, char *update_dir,
  27.                   List *entries));
  28. static int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  29. static int rtag_filesdoneproc PROTO ((void *callerdat, int err,
  30.                       char *repos, char *update_dir,
  31.                       List *entries));
  32. static int rtag_proc PROTO((int *pargc, char **argv, char *xwhere,
  33.               char *mwhere, char *mfile, int shorten,
  34.               int local_specified, char *mname, char *msg));
  35. static int rtag_delete PROTO((RCSNode *rcsfile));
  36.  
  37.  
  38. struct tag_info
  39. {
  40.     Ctype status;
  41.     char *rev;
  42.     char *tag;
  43.     char *options;
  44. };
  45.  
  46. struct master_lists
  47. {
  48.     List *tlist;
  49. };
  50.  
  51. static List *mtlist;
  52. static List *tlist;
  53.  
  54. static char *symtag;
  55. static char *numtag;
  56. static int numtag_validated = 0;
  57. static int delete_flag;            /* adding a tag by default */
  58. static int attic_too;            /* remove tag from Attic files */
  59. static int branch_mode;            /* make an automagic "branch" tag */
  60. static char *date;
  61. static int local;            /* recursive by default */
  62. static int force_tag_match = 1;        /* force by default */
  63. static int force_tag_move;              /* don't move existing tags by default */
  64.  
  65. static const char *const rtag_usage[] =
  66. {
  67.     "Usage: %s %s [-aflRnF] [-b] [-d] [-r tag|-D date] tag modules...\n",
  68.     "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
  69.     "\t-f\tForce a head revision match if tag/date not found.\n",
  70.     "\t-l\tLocal directory only, not recursive\n",
  71.     "\t-R\tProcess directories recursively.\n",
  72.     "\t-n\tNo execution of 'tag program'\n",
  73.     "\t-d\tDelete the given Tag.\n",
  74.     "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
  75.     "\t-[rD]\tExisting tag or Date.\n",
  76.     "\t-F\tMove tag if it already exists\n",    
  77.     NULL
  78. };
  79.  
  80. int
  81. rtag (argc, argv)
  82.     int argc;
  83.     char **argv;
  84. {
  85.     register int i;
  86.     int c;
  87.     DBM *db;
  88.     int run_module_prog = 1;
  89.     int err = 0;
  90.  
  91.     if (argc == -1)
  92.     usage (rtag_usage);
  93.  
  94.     optind = 1;
  95.     while ((c = getopt (argc, argv, "FanfQqlRdbr:D:")) != -1)
  96.     {
  97.     switch (c)
  98.     {
  99.         case 'a':
  100.         attic_too = 1;
  101.         break;
  102.         case 'n':
  103.         run_module_prog = 0;
  104.         break;
  105.         case 'Q':
  106.         case 'q':
  107. #ifdef SERVER_SUPPORT
  108.         /* The CVS 1.5 client sends these options (in addition to
  109.            Global_option requests), so we must ignore them.  */
  110.         if (!server_active)
  111. #endif
  112.             error (1, 0,
  113.                "-q or -Q must be specified before \"%s\"",
  114.                command_name);
  115.         break;
  116.         case 'l':
  117.         local = 1;
  118.         break;
  119.         case 'R':
  120.         local = 0;
  121.         break;
  122.         case 'd':
  123.         delete_flag = 1;
  124.         break;
  125.         case 'f':
  126.         force_tag_match = 0;
  127.         break;
  128.         case 'b':
  129.         branch_mode = 1;
  130.         break;
  131.         case 'r':
  132.         numtag = optarg;
  133.         break;
  134.         case 'D':
  135.         if (date)
  136.             free (date);
  137.         date = Make_Date (optarg);
  138.         break;
  139.         case 'F':
  140.         force_tag_move = 1;
  141.         break;
  142.         case '?':
  143.         default:
  144.         usage (rtag_usage);
  145.         break;
  146.     }
  147.     }
  148.     argc -= optind;
  149.     argv += optind;
  150.     if (argc < 2)
  151.     usage (rtag_usage);
  152.     symtag = argv[0];
  153.     argc--;
  154.     argv++;
  155.  
  156.     if (date && numtag)
  157.     error (1, 0, "-r and -D options are mutually exclusive");
  158.     if (delete_flag && branch_mode)
  159.     error (0, 0, "warning: -b ignored with -d options");
  160.     RCS_check_tag (symtag);
  161.  
  162. #ifdef CLIENT_SUPPORT
  163.     if (client_active)
  164.     {
  165.     /* We're the client side.  Fire up the remote server.  */
  166.     start_server ();
  167.     
  168.     ign_setup ();
  169.  
  170.     if (local)
  171.         send_arg("-l");
  172.     if (delete_flag)
  173.         send_arg("-d");
  174.     if (branch_mode)
  175.         send_arg("-b");
  176.     if (force_tag_move)
  177.         send_arg("-F");
  178.     if (run_module_prog)
  179.         send_arg("-n");
  180.     if (attic_too)
  181.         send_arg("-a");
  182.  
  183.     if (numtag)
  184.         option_with_arg ("-r", numtag);
  185.     if (date)
  186.         client_senddate (date);
  187.  
  188.     send_arg (symtag);
  189.  
  190.     {
  191.         int i;
  192.         for (i = 0; i < argc; ++i)
  193.         send_arg (argv[i]);
  194.     }
  195.  
  196.     send_to_server ("rtag\012", 0);
  197.         return get_responses_and_close ();
  198.     }
  199. #endif
  200.  
  201.     db = open_module ();
  202.     for (i = 0; i < argc; i++)
  203.     {
  204.     /* XXX last arg should be repository, but doesn't make sense here */
  205.     history_write ('T', (delete_flag ? "D" : (numtag ? numtag : 
  206.                (date ? date : "A"))), symtag, argv[i], "");
  207.     err += do_module (db, argv[i], TAG, delete_flag ? "Untagging" : "Tagging",
  208.               rtag_proc, (char *) NULL, 0, 0, run_module_prog,
  209.               symtag);
  210.     }
  211.     close_module (db);
  212.     return (err);
  213. }
  214.  
  215. /*
  216.  * callback proc for doing the real work of tagging
  217.  */
  218. /* ARGSUSED */
  219. static int
  220. rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
  221.        mname, msg)
  222.     int *pargc;
  223.     char **argv;
  224.     char *xwhere;
  225.     char *mwhere;
  226.     char *mfile;
  227.     int shorten;
  228.     int local_specified;
  229.     char *mname;
  230.     char *msg;
  231. {
  232.     int err = 0;
  233.     int which;
  234.     char repository[PATH_MAX];
  235.     char where[PATH_MAX];
  236.  
  237.     (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]);
  238.     (void) strcpy (where, argv[0]);
  239.  
  240.     /* if mfile isn't null, we need to set up to do only part of the module */
  241.     if (mfile != NULL)
  242.     {
  243.     char *cp;
  244.     char path[PATH_MAX];
  245.  
  246.     /* if the portion of the module is a path, put the dir part on repos */
  247.     if ((cp = strrchr (mfile, '/')) != NULL)
  248.     {
  249.         *cp = '\0';
  250.         (void) strcat (repository, "/");
  251.         (void) strcat (repository, mfile);
  252.         (void) strcat (where, "/");
  253.         (void) strcat (where, mfile);
  254.         mfile = cp + 1;
  255.     }
  256.  
  257.     /* take care of the rest */
  258.     (void) sprintf (path, "%s/%s", repository, mfile);
  259.     if (isdir (path))
  260.     {
  261.         /* directory means repository gets the dir tacked on */
  262.         (void) strcpy (repository, path);
  263.         (void) strcat (where, "/");
  264.         (void) strcat (where, mfile);
  265.     }
  266.     else
  267.     {
  268.         int i;
  269.  
  270.         /* a file means muck argv */
  271.         for (i = 1; i < *pargc; i++)
  272.         free (argv[i]);
  273.         argv[1] = xstrdup (mfile);
  274.         (*pargc) = 2;
  275.     }
  276.     }
  277.  
  278.     /* chdir to the starting directory */
  279.     if ( CVS_CHDIR (repository) < 0)
  280.     {
  281.     error (0, errno, "cannot chdir to %s", repository);
  282.     return (1);
  283.     }
  284.  
  285.     if (delete_flag || attic_too || (force_tag_match && numtag))
  286.     which = W_REPOS | W_ATTIC;
  287.     else
  288.     which = W_REPOS;
  289.  
  290.     if (numtag != NULL && !numtag_validated)
  291.     {
  292.     tag_check_valid (numtag, *pargc - 1, argv + 1, local, 0, NULL);
  293.     numtag_validated = 1;
  294.     }
  295.  
  296.     /* check to make sure they are authorized to tag all the 
  297.        specified files in the repository */
  298.  
  299.     mtlist = getlist();
  300.     err = start_recursion (check_fileproc, check_filesdoneproc,
  301.                            (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
  302.                            *pargc - 1, argv + 1, local, which, 0, 1,
  303.                            where, 1);
  304.     
  305.     if (err)
  306.     {
  307.        error (1, 0, "correct the above errors first!");
  308.     }
  309.      
  310.     /* start the recursion processor */
  311.     err = start_recursion (rtag_fileproc, rtag_filesdoneproc, rtag_dirproc,
  312.                (DIRLEAVEPROC) NULL, NULL,
  313.                *pargc - 1, argv + 1, local,
  314.                which, 0, 0, where, 1);
  315.  
  316.     dellist(&mtlist);
  317.  
  318.     return (err);
  319. }
  320.  
  321. /* check file that is to be tagged */
  322. /* All we do here is add it to our list */
  323.  
  324. static int
  325. check_fileproc (callerdat, finfo)
  326.     void *callerdat;
  327.     struct file_info *finfo;
  328. {
  329.     char *xdir;
  330.     Node *p;
  331.     Vers_TS *vers;
  332.     
  333.     if (finfo->update_dir[0] == '\0')
  334.     xdir = ".";
  335.     else
  336.     xdir = finfo->update_dir;
  337.     if ((p = findnode (mtlist, xdir)) != NULL)
  338.     {
  339.     tlist = ((struct master_lists *) p->data)->tlist;
  340.     }
  341.     else
  342.     {
  343.     struct master_lists *ml;
  344.         
  345.     tlist = getlist ();
  346.     p = getnode ();
  347.     p->key = xstrdup (xdir);
  348.     p->type = UPDATE;
  349.     ml = (struct master_lists *)
  350.         xmalloc (sizeof (struct master_lists));
  351.     ml->tlist = tlist;
  352.     p->data = (char *) ml;
  353.     p->delproc = masterlist_delproc;
  354.     (void) addnode (mtlist, p);
  355.     }
  356.     /* do tlist */
  357.     p = getnode ();
  358.     p->key = xstrdup (finfo->file);
  359.     p->type = UPDATE;
  360.     p->delproc = tag_delproc;
  361.     vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
  362.     p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0);
  363.     if (p->data != NULL)
  364.     {
  365.         int addit = 1;
  366.         char *oversion;
  367.         
  368.         oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
  369.         if (oversion == NULL) 
  370.         {
  371.             if (delete_flag)
  372.             {
  373.                 addit = 0;
  374.             }
  375.         }
  376.         else if (strcmp(oversion, p->data) == 0)
  377.         {
  378.             addit = 0;
  379.         }
  380.         else if (!force_tag_move)
  381.         {
  382.             addit = 0;
  383.         }
  384.         if (oversion != NULL)
  385.         {
  386.             free(oversion);
  387.         }
  388.         if (!addit)
  389.         {
  390.             free(p->data);
  391.             p->data = NULL;
  392.         }
  393.     }
  394.     freevers_ts (&vers);
  395.     (void) addnode (tlist, p);
  396.     return (0);
  397. }
  398.                          
  399. static int
  400. check_filesdoneproc (callerdat, err, repos, update_dir, entries)
  401.     void *callerdat;
  402.     int err;
  403.     char *repos;
  404.     char *update_dir;
  405.     List *entries;
  406. {
  407.     int n;
  408.     Node *p;
  409.  
  410.     p = findnode(mtlist, update_dir);
  411.     if (p != NULL)
  412.     {
  413.         tlist = ((struct master_lists *) p->data)->tlist;
  414.     }
  415.     else
  416.     {
  417.         tlist = (List *) NULL;
  418.     }
  419.     if ((tlist == NULL) || (tlist->list->next == tlist->list))
  420.     {
  421.         return (err);
  422.     }
  423.     if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0)
  424.     {
  425.         error (0, 0, "Pre-tag check failed");
  426.         err += n;
  427.     }
  428.     return (err);
  429. }
  430.  
  431. static int
  432. pretag_proc(repository, filter)
  433.     char *repository;
  434.     char *filter;
  435. {
  436.     if (filter[0] == '/')
  437.     {
  438.         char *s, *cp;
  439.  
  440.         s = xstrdup(filter);
  441.         for (cp=s; *cp; cp++)
  442.         {
  443.             if (isspace(*cp))
  444.             {
  445.                 *cp = '\0';
  446.                 break;
  447.             }
  448.         }
  449.         if (!isfile(s))
  450.         {
  451.             error (0, errno, "cannot find pre-tag filter '%s'", s);
  452.             free(s);
  453.             return (1);
  454.         }
  455.         free(s);
  456.     }
  457.     run_setup("%s %s %s %s",
  458.               filter,
  459.               symtag,
  460.               delete_flag ? "del" : force_tag_move ? "mov" : "add",
  461.               repository);
  462.     walklist(tlist, pretag_list_proc, NULL);
  463.     return (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
  464. }
  465.  
  466. static void
  467. masterlist_delproc(p)
  468.     Node *p;
  469. {
  470.     struct master_lists *ml;
  471.  
  472.     ml = (struct master_lists *)p->data;
  473.     dellist(&ml->tlist);
  474.     free(ml);
  475.     return;
  476. }
  477.  
  478. static void
  479. tag_delproc(p)
  480.     Node *p;
  481. {
  482.     if (p->data != NULL)
  483.     {
  484.         free(p->data);
  485.         p->data = NULL;
  486.     }
  487.     return;
  488. }
  489.  
  490. static int
  491. pretag_list_proc(p, closure)
  492.     Node *p;
  493.     void *closure;
  494. {
  495.     if (p->data != NULL)
  496.     {
  497.         run_arg(p->key);
  498.         run_arg(p->data);
  499.     }
  500.     return (0);
  501. }
  502.  
  503. /*
  504.  * Called to tag a particular file, as appropriate with the options that were
  505.  * set above.
  506.  */
  507. /* ARGSUSED */
  508. static int
  509. rtag_fileproc (callerdat, finfo)
  510.     void *callerdat;
  511.     struct file_info *finfo;
  512. {
  513.     RCSNode *rcsfile;
  514.     char *version, *rev;
  515.     int retcode = 0;
  516.  
  517.     /* Lock the directory if it is not already locked.  We might be
  518.        able to rely on rtag_dirproc for this.  */
  519.     tag_lockdir (finfo->repository);
  520.  
  521.     /* find the parsed RCS data */
  522.     if ((rcsfile = finfo->rcs) == NULL)
  523.     return (1);
  524.  
  525.     /*
  526.      * For tagging an RCS file which is a symbolic link, you'd best be
  527.      * running with RCS 5.6, since it knows how to handle symbolic links
  528.      * correctly without breaking your link!
  529.      */
  530.  
  531.     if (delete_flag)
  532.     return (rtag_delete (rcsfile));
  533.  
  534.     /*
  535.      * If we get here, we are adding a tag.  But, if -a was specified, we
  536.      * need to check to see if a -r or -D option was specified.  If neither
  537.      * was specified and the file is in the Attic, remove the tag.
  538.      */
  539.     if (attic_too && (!numtag && !date))
  540.     {
  541.     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
  542.         return (rtag_delete (rcsfile));
  543.     }
  544.  
  545.     version = RCS_getversion (rcsfile, numtag, date, force_tag_match, 0);
  546.     if (version == NULL)
  547.     {
  548.     /* If -a specified, clean up any old tags */
  549.     if (attic_too)
  550.         (void) rtag_delete (rcsfile);
  551.  
  552.     if (!quiet && !force_tag_match)
  553.     {
  554.         error (0, 0, "cannot find tag `%s' in `%s'",
  555.            numtag ? numtag : "head", rcsfile->path);
  556.         return (1);
  557.     }
  558.     return (0);
  559.     }
  560.     if (numtag && isdigit (*numtag) && strcmp (numtag, version) != 0)
  561.     {
  562.  
  563.     /*
  564.      * We didn't find a match for the numeric tag that was specified, but
  565.      * that's OK.  just pass the numeric tag on to rcs, to be tagged as
  566.      * specified.  Could get here if one tried to tag "1.1.1" and there
  567.      * was a 1.1.1 branch with some head revision.  In this case, we want
  568.      * the tag to reference "1.1.1" and not the revision at the head of
  569.      * the branch.  Use a symbolic tag for that.
  570.      */
  571.     rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
  572.     retcode = RCS_settag(rcsfile->path, symtag, numtag);
  573.     }
  574.     else
  575.     {
  576.     char *oversion;
  577.        
  578.     /*
  579.      * As an enhancement for the case where a tag is being re-applied to
  580.      * a large body of a module, make one extra call to RCS_getversion to
  581.      * see if the tag is already set in the RCS file.  If so, check to
  582.      * see if it needs to be moved.  If not, do nothing.  This will
  583.      * likely save a lot of time when simply moving the tag to the
  584.      * "current" head revisions of a module -- which I have found to be a
  585.      * typical tagging operation.
  586.      */
  587.     rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
  588.     oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0);
  589.     if (oversion != NULL)
  590.     {
  591.         int isbranch = RCS_isbranch (finfo->rcs, symtag);
  592.  
  593.         /*
  594.          * if versions the same and neither old or new are branches don't
  595.          * have to do anything
  596.          */
  597.         if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
  598.         {
  599.         free (oversion);
  600.         free (version);
  601.         return (0);
  602.         }
  603.       
  604.         if (!force_tag_move)
  605.         {
  606.         /* we're NOT going to move the tag */
  607.         (void) printf ("W %s", finfo->fullname);
  608.  
  609.         (void) printf (" : %s already exists on %s %s", 
  610.                    symtag, isbranch ? "branch" : "version",
  611.                    oversion);
  612.         (void) printf (" : NOT MOVING tag to %s %s\n", 
  613.                    branch_mode ? "branch" : "version", rev);
  614.         free (oversion);
  615.         free (version);
  616.         return (0);
  617.         }
  618.         free (oversion);
  619.     }
  620.     retcode = RCS_settag(rcsfile->path, symtag, rev);
  621.     }
  622.  
  623.     if (retcode != 0)
  624.     {
  625.     error (1, retcode == -1 ? errno : 0,
  626.            "failed to set tag `%s' to revision `%s' in `%s'",
  627.            symtag, rev, rcsfile->path);
  628.         if (branch_mode)
  629.         free (rev);
  630.         free (version);
  631.         return (1);
  632.     }
  633.     if (branch_mode)
  634.     free (rev);
  635.     free (version);
  636.     return (0);
  637. }
  638.  
  639. /*
  640.  * If -d is specified, "force_tag_match" is set, so that this call to
  641.  * RCS_getversion() will return a NULL version string if the symbolic
  642.  * tag does not exist in the RCS file.
  643.  * 
  644.  * If the -r flag was used, numtag is set, and we only delete the
  645.  * symtag from files that have numtag.
  646.  * 
  647.  * This is done here because it's MUCH faster than just blindly calling
  648.  * "rcs" to remove the tag... trust me.
  649.  */
  650. static int
  651. rtag_delete (rcsfile)
  652.     RCSNode *rcsfile;
  653. {
  654.     char *version;
  655.     int retcode;
  656.  
  657.     if (numtag)
  658.     {
  659.     version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, 0);
  660.     if (version == NULL)
  661.         return (0);
  662.     free (version);
  663.     }
  664.  
  665.     version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0);
  666.     if (version == NULL)
  667.     return (0);
  668.     free (version);
  669.  
  670.     if ((retcode = RCS_deltag(rcsfile->path, symtag, 1)) != 0)
  671.     {
  672.     if (!quiet)
  673.         error (0, retcode == -1 ? errno : 0,
  674.            "failed to remove tag `%s' from `%s'", symtag,
  675.            rcsfile->path);
  676.     return (1);
  677.     }
  678.     return (0);
  679. }
  680.  
  681. /* Clear any lock we may hold on the current directory.  */
  682.  
  683. static int
  684. rtag_filesdoneproc (callerdat, err, repos, update_dir, entries)
  685.     void *callerdat;
  686.     int err;
  687.     char *repos;
  688.     char *update_dir;
  689.     List *entries;
  690. {
  691.     tag_unlockdir ();
  692.  
  693.     return (err);
  694. }
  695.  
  696. /*
  697.  * Print a warm fuzzy message
  698.  */
  699. /* ARGSUSED */
  700. static Dtype
  701. rtag_dirproc (callerdat, dir, repos, update_dir, entries)
  702.     void *callerdat;
  703.     char *dir;
  704.     char *repos;
  705.     char *update_dir;
  706.     List *entries;
  707. {
  708.     if (!quiet)
  709.     error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir);
  710.     return (R_PROCESS);
  711. }
  712.  
  713.  
  714.  
  715.