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 / import.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  32KB  |  1,248 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.  * "import" checks in the vendor release located in the current directory into
  9.  * the CVS source repository.  The CVS vendor branch support is utilized.
  10.  * 
  11.  * At least three arguments are expected to follow the options:
  12.  *    repository    Where the source belongs relative to the CVSROOT
  13.  *    VendorTag    Vendor's major tag
  14.  *    VendorReleTag    Tag for this particular release
  15.  *
  16.  * Additional arguments specify more Vendor Release Tags.
  17.  */
  18.  
  19. #include "cvs.h"
  20. #include "savecwd.h"
  21.  
  22. #define    FILE_HOLDER    ".#cvsxxx"
  23.  
  24. static char *get_comment PROTO((char *user));
  25. static int add_rcs_file PROTO((char *message, char *rcs, char *user, char *vtag,
  26.                  int targc, char *targv[]));
  27. static int expand_at_signs PROTO((char *buf, off_t size, FILE *fp));
  28. static int add_rev PROTO((char *message, char *rcs, char *vfile, char *vers));
  29. static int add_tags PROTO((char *rcs, char *vfile, char *vtag, int targc,
  30.              char *targv[]));
  31. static int import_descend PROTO((char *message, char *vtag, int targc, char *targv[]));
  32. static int import_descend_dir PROTO((char *message, char *dir, char *vtag,
  33.                    int targc, char *targv[]));
  34. static int process_import_file PROTO((char *message, char *vfile, char *vtag,
  35.                 int targc, char *targv[]));
  36. static int update_rcs_file PROTO((char *message, char *vfile, char *vtag, int targc,
  37.                 char *targv[], int inattic));
  38. static void add_log PROTO((int ch, char *fname));
  39.  
  40. static int repos_len;
  41. static char vhead[50];
  42. static char vbranch[50];
  43. static FILE *logfp;
  44. static char repository[PATH_MAX];
  45. static int conflicts;
  46. static int use_file_modtime;
  47. static char *keyword_opt = NULL;
  48.  
  49. static const char *const import_usage[] =
  50. {
  51.     "Usage: %s %s [-d] [-k subst] [-I ign] [-m msg] [-b branch]\n",
  52.     "    [-W spec] repository vendor-tag release-tags...\n",
  53.     "\t-d\tUse the file's modification time as the time of import.\n",
  54.     "\t-k sub\tSet default RCS keyword substitution mode.\n",
  55.     "\t-I ign\tMore files to ignore (! to reset).\n",
  56.     "\t-b bra\tVendor branch id.\n",
  57.     "\t-m msg\tLog message.\n",
  58.     "\t-W spec\tWrappers specification line.\n",
  59.     NULL
  60. };
  61.  
  62. int
  63. import (argc, argv)
  64.     int argc;
  65.     char **argv;
  66. {
  67.     char *message = NULL;
  68.     char *tmpfile;
  69.     char *cp;
  70.     int i, c, msglen, err;
  71.     List *ulist;
  72.     Node *p;
  73.     struct logfile_info *li;
  74.  
  75.     if (argc == -1)
  76.     usage (import_usage);
  77.  
  78.     ign_setup ();
  79.     wrap_setup ();
  80.  
  81.     (void) strcpy (vbranch, CVSBRANCH);
  82.     optind = 1;
  83.     while ((c = getopt (argc, argv, "Qqdb:m:I:k:W:")) != -1)
  84.     {
  85.     switch (c)
  86.     {
  87.         case 'Q':
  88.         case 'q':
  89. #ifdef SERVER_SUPPORT
  90.         /* The CVS 1.5 client sends these options (in addition to
  91.            Global_option requests), so we must ignore them.  */
  92.         if (!server_active)
  93. #endif
  94.             error (1, 0,
  95.                "-q or -Q must be specified before \"%s\"",
  96.                command_name);
  97.         break;
  98.         case 'd':
  99.         use_file_modtime = 1;
  100.         break;
  101.         case 'b':
  102.         (void) strcpy (vbranch, optarg);
  103.         break;
  104.         case 'm':
  105. #ifdef FORCE_USE_EDITOR
  106.         use_editor = TRUE;
  107. #else
  108.         use_editor = FALSE;
  109. #endif
  110.         message = xstrdup(optarg);
  111.         break;
  112.         case 'I':
  113.         ign_add (optarg, 0);
  114.         break;
  115.             case 'k':
  116.         /* RCS_check_kflag returns strings of the form -kxx.  We
  117.            only use it for validation, so we can free the value
  118.            as soon as it is returned. */
  119.         free (RCS_check_kflag (optarg));
  120.         keyword_opt = optarg;
  121.         break;
  122.         case 'W':
  123.         wrap_add (optarg, 0);
  124.         break;
  125.         case '?':
  126.         default:
  127.         usage (import_usage);
  128.         break;
  129.     }
  130.     }
  131.     argc -= optind;
  132.     argv += optind;
  133.     if (argc < 3)
  134.     usage (import_usage);
  135.  
  136.     for (i = 1; i < argc; i++)        /* check the tags for validity */
  137.     RCS_check_tag (argv[i]);
  138.  
  139.     /* XXX - this should be a module, not just a pathname */
  140.     if (! isabsolute (argv[0]))
  141.     {
  142.     if (CVSroot_directory == NULL)
  143.     {
  144.         error (0, 0, "missing CVSROOT environment variable\n");
  145.         error (1, 0, "Set it or specify the '-d' option to %s.",
  146.            program_name);
  147.     }
  148.     (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]);
  149.     repos_len = strlen (CVSroot_directory);
  150.     }
  151.     else
  152.     {
  153.     (void) strcpy (repository, argv[0]);
  154.     repos_len = 0;
  155.     }
  156.  
  157.     /*
  158.      * Consistency checks on the specified vendor branch.  It must be
  159.      * composed of only numbers and dots ('.').  Also, for now we only
  160.      * support branching to a single level, so the specified vendor branch
  161.      * must only have two dots in it (like "1.1.1").
  162.      */
  163.     for (cp = vbranch; *cp != '\0'; cp++)
  164.     if (!isdigit (*cp) && *cp != '.')
  165.         error (1, 0, "%s is not a numeric branch", vbranch);
  166.     if (numdots (vbranch) != 2)
  167.     error (1, 0, "Only branches with two dots are supported: %s", vbranch);
  168.     (void) strcpy (vhead, vbranch);
  169.     cp = strrchr (vhead, '.');
  170.     *cp = '\0';
  171.  
  172. #ifdef CLIENT_SUPPORT
  173.     if (client_active)
  174.     {
  175.     /* For rationale behind calling start_server before do_editor, see
  176.        commit.c  */
  177.     start_server ();
  178.     }
  179. #endif
  180.  
  181.     if (use_editor)
  182.     {
  183.     do_editor ((char *) NULL, &message, repository,
  184.            (List *) NULL);
  185.     }
  186.  
  187.     msglen = message == NULL ? 0 : strlen (message);
  188.     if (msglen == 0 || message[msglen - 1] != '\n')
  189.     {
  190.     char *nm = xmalloc (msglen + 2);
  191.     if (message != NULL)
  192.     {
  193.         (void) strcpy (nm, message);
  194.         free (message);
  195.     }
  196.     (void) strcat (nm + msglen, "\n");
  197.     message = nm;
  198.     }
  199.  
  200. #ifdef CLIENT_SUPPORT
  201.     if (client_active)
  202.     {
  203.     int err;
  204.  
  205.     if (use_file_modtime)
  206.         send_arg("-d");
  207.  
  208.     if (vbranch[0] != '\0')
  209.         option_with_arg ("-b", vbranch);
  210.     if (message)
  211.         option_with_arg ("-m", message);
  212.     if (keyword_opt != NULL)
  213.         option_with_arg ("-k", keyword_opt);
  214.     /* The only ignore processing which takes place on the server side
  215.        is the CVSROOT/cvsignore file.  But if the user specified -I !,
  216.        the documented behavior is to not process said file.  */
  217.     if (ign_inhibit_server)
  218.     {
  219.         send_arg ("-I");
  220.         send_arg ("!");
  221.     }
  222.     wrap_send ();
  223.  
  224.     {
  225.         int i;
  226.         for (i = 0; i < argc; ++i)
  227.         send_arg (argv[i]);
  228.     }
  229.  
  230.     logfp = stdin;
  231.     client_import_setup (repository);
  232.     err = import_descend (message, argv[1], argc - 2, argv + 2);
  233.     client_import_done ();
  234.     send_to_server ("import\012", 0);
  235.     err += get_responses_and_close ();
  236.     return err;
  237.     }
  238. #endif
  239.  
  240.     /*
  241.      * Make all newly created directories writable.  Should really use a more
  242.      * sophisticated security mechanism here.
  243.      */
  244.     (void) umask (cvsumask);
  245.     make_directories (repository);
  246.  
  247.     /* Create the logfile that will be logged upon completion */
  248.     tmpfile = cvs_temp_name ();
  249.     if ((logfp = CVS_FOPEN (tmpfile, "w+")) == NULL)
  250.     error (1, errno, "cannot create temporary file `%s'", tmpfile);
  251.     (void) CVS_UNLINK (tmpfile);        /* to be sure it goes away */
  252.     (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
  253.     (void) fprintf (logfp, "Release Tags:\t");
  254.     for (i = 2; i < argc; i++)
  255.     (void) fprintf (logfp, "%s\n\t\t", argv[i]);
  256.     (void) fprintf (logfp, "\n");
  257.  
  258.     /* Just Do It.  */
  259.     err = import_descend (message, argv[1], argc - 2, argv + 2);
  260.     if (conflicts)
  261.     {
  262.     if (!really_quiet)
  263.     {
  264.         char buf[80];
  265.         sprintf (buf, "\n%d conflicts created by this import.\n",
  266.              conflicts);
  267.         cvs_output (buf, 0);
  268.         cvs_output ("Use the following command to help the merge:\n\n",
  269.             0);
  270.         cvs_output ("\t", 1);
  271.         cvs_output (program_name, 0);
  272.         cvs_output (" checkout -j", 0);
  273.         cvs_output (argv[1], 0);
  274.         cvs_output (":yesterday -j", 0);
  275.         cvs_output (argv[1], 0);
  276.         cvs_output (" ", 1);
  277.         cvs_output (argv[0], 0);
  278.         cvs_output ("\n\n", 0);
  279.     }
  280.  
  281.     (void) fprintf (logfp, "\n%d conflicts created by this import.\n",
  282.             conflicts);
  283.     (void) fprintf (logfp,
  284.             "Use the following command to help the merge:\n\n");
  285.     (void) fprintf (logfp, "\t%s checkout -j%s:yesterday -j%s %s\n\n",
  286.             program_name, argv[1], argv[1], argv[0]);
  287.     }
  288.     else
  289.     {
  290.     if (!really_quiet)
  291.         cvs_output ("\nNo conflicts created by this import\n\n", 0);
  292.     (void) fprintf (logfp, "\nNo conflicts created by this import\n\n");
  293.     }
  294.  
  295.     /*
  296.      * Write out the logfile and clean up.
  297.      */
  298.     ulist = getlist ();
  299.     p = getnode ();
  300.     p->type = UPDATE;
  301.     p->delproc = update_delproc;
  302.     p->key = xstrdup ("- Imported sources");
  303.     li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
  304.     li->type = T_TITLE;
  305.     li->tag = xstrdup (vbranch);
  306.     p->data = (char *) li;
  307.     (void) addnode (ulist, p);
  308.     Update_Logfile (repository, message, logfp, ulist);
  309.     dellist (&ulist);
  310.     (void) fclose (logfp);
  311.  
  312.     /* Make sure the temporary file goes away, even on systems that don't let
  313.        you delete a file that's in use.  */
  314.     CVS_UNLINK (tmpfile);
  315.     free (tmpfile);
  316.  
  317.     if (message)
  318.     free (message);
  319.  
  320.     return (err);
  321. }
  322.  
  323. /*
  324.  * process all the files in ".", then descend into other directories.
  325.  */
  326. static int
  327. import_descend (message, vtag, targc, targv)
  328.     char *message;
  329.     char *vtag;
  330.     int targc;
  331.     char *targv[];
  332. {
  333.     DIR *dirp;
  334.     struct dirent *dp;
  335.     int err = 0;
  336.     List *dirlist = NULL;
  337.  
  338.     /* first, load up any per-directory ignore lists */
  339.     ign_add_file (CVSDOTIGNORE, 1);
  340.     wrap_add_file (CVSDOTWRAPPER, 1);
  341.  
  342.     if ((dirp = CVS_OPENDIR (".")) == NULL)
  343.     {
  344.     err++;
  345.     }
  346.     else
  347.     {
  348.     while ((dp = readdir (dirp)) != NULL)
  349.     {
  350.         if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
  351.         continue;
  352. #ifdef SERVER_SUPPORT
  353.         /* CVS directories are created in the temp directory by
  354.            server.c because it doesn't special-case import.  So
  355.            don't print a message about them, regardless of -I!.  */
  356.         if (server_active && strcmp (dp->d_name, CVSADM) == 0)
  357.         continue;
  358. #endif
  359.         if (ign_name (dp->d_name))
  360.         {
  361.         add_log ('I', dp->d_name);
  362.         continue;
  363.         }
  364.  
  365.         if (
  366. #ifdef DT_DIR
  367.         (dp->d_type == DT_DIR
  368.          || (dp->d_type == DT_UNKNOWN && isdir (dp->d_name)))
  369. #else
  370.         isdir (dp->d_name)
  371. #endif
  372.         && !wrap_name_has (dp->d_name, WRAP_TOCVS)
  373.         )
  374.         {
  375.         Node *n;
  376.  
  377.         if (dirlist == NULL)
  378.             dirlist = getlist();
  379.  
  380.         n = getnode();
  381.         n->key = xstrdup (dp->d_name);
  382.         addnode(dirlist, n);
  383.         }
  384.         else if (
  385. #ifdef DT_DIR
  386.         dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN &&
  387. #endif
  388.         islink (dp->d_name))
  389.         {
  390.         add_log ('L', dp->d_name);
  391.         err++;
  392.         }
  393.         else
  394.         {
  395. #ifdef CLIENT_SUPPORT
  396.         if (client_active)
  397.             err += client_process_import_file (message, dp->d_name,
  398.                                vtag, targc, targv,
  399.                                repository);
  400.         else
  401. #endif
  402.             err += process_import_file (message, dp->d_name,
  403.                         vtag, targc, targv);
  404.         }
  405.     }
  406.     (void) closedir (dirp);
  407.     }
  408.  
  409.     if (dirlist != NULL)
  410.     {
  411.     Node *head, *p;
  412.  
  413.     head = dirlist->list;
  414.     for (p = head->next; p != head; p = p->next)
  415.     {
  416.         err += import_descend_dir (message, p->key, vtag, targc, targv);
  417.     }
  418.  
  419.     dellist(&dirlist);
  420.     }
  421.  
  422.     return (err);
  423. }
  424.  
  425. /*
  426.  * Process the argument import file.
  427.  */
  428. static int
  429. process_import_file (message, vfile, vtag, targc, targv)
  430.     char *message;
  431.     char *vfile;
  432.     char *vtag;
  433.     int targc;
  434.     char *targv[];
  435. {
  436.     char attic_name[PATH_MAX];
  437.     char rcs[PATH_MAX];
  438.     int inattic = 0;
  439.  
  440.     (void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT);
  441.     if (!isfile (rcs))
  442.     {
  443.     (void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC,
  444.             vfile, RCSEXT);
  445.     if (!isfile (attic_name))
  446.     {
  447.  
  448.         /*
  449.          * A new import source file; it doesn't exist as a ,v within the
  450.          * repository nor in the Attic -- create it anew.
  451.          */
  452.         add_log ('N', vfile);
  453.         return (add_rcs_file (message, rcs, vfile, vtag, targc, targv));
  454.     }
  455.     inattic = 1;
  456.     }
  457.  
  458.     /*
  459.      * an rcs file exists. have to do things the official, slow, way.
  460.      */
  461.     return (update_rcs_file (message, vfile, vtag, targc, targv, inattic));
  462. }
  463.  
  464. /*
  465.  * The RCS file exists; update it by adding the new import file to the
  466.  * (possibly already existing) vendor branch.
  467.  */
  468. static int
  469. update_rcs_file (message, vfile, vtag, targc, targv, inattic)
  470.     char *message;
  471.     char *vfile;
  472.     char *vtag;
  473.     int targc;
  474.     char *targv[];
  475.     int inattic;
  476. {
  477.     Vers_TS *vers;
  478.     int letter;
  479.     int ierrno;
  480.     char *tmpdir;
  481.     char *tocvsPath;
  482.     struct file_info finfo;
  483.  
  484.     memset (&finfo, 0, sizeof finfo);
  485.     finfo.file = vfile;
  486.     /* Not used, so don't worry about it.  */
  487.     finfo.update_dir = NULL;
  488.     finfo.fullname = finfo.file;
  489.     finfo.repository = repository;
  490.     finfo.entries = NULL;
  491.     finfo.rcs = NULL;
  492.     vers = Version_TS (&finfo, (char *) NULL, vbranch, (char *) NULL,
  493.                1, 0);
  494.     if (vers->vn_rcs != NULL
  495.     && !RCS_isdead(vers->srcfile, vers->vn_rcs))
  496.     {
  497.     char xtmpfile[PATH_MAX];
  498.     int different;
  499.     int retcode = 0;
  500.  
  501.     tmpdir = getenv ("TMPDIR");
  502.     if (tmpdir == NULL || tmpdir[0] == '\0')
  503.       tmpdir = "/tmp";
  504.  
  505.     (void) sprintf (xtmpfile, "%s/cvs-imp%ld", tmpdir, (long) getpid());
  506.  
  507.     /*
  508.      * The rcs file does have a revision on the vendor branch. Compare
  509.      * this revision with the import file; if they match exactly, there
  510.      * is no need to install the new import file as a new revision to the
  511.      * branch.  Just tag the revision with the new import tags.
  512.      * 
  513.      * This is to try to cut down the number of "C" conflict messages for
  514.      * locally modified import source files.
  515.      */
  516.     /* Why is RCS_FLAGS_FORCE here?  I wouldn't think that it would have any
  517.        effect in conjunction with passing NULL for workfile (i.e. to stdout).  */
  518.     retcode = RCS_fast_checkout (vers->srcfile, NULL, vers->vn_rcs,
  519. #ifdef HAVE_RCS5
  520.                      "-ko",
  521. #else
  522.                      NULL,
  523. #endif
  524.                      xtmpfile, RCS_FLAGS_FORCE);
  525.     if (retcode != 0)
  526.     {
  527.         ierrno = errno;
  528.         fperror (logfp, 0, retcode == -1 ? ierrno : 0,
  529.              "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
  530.              vers->srcfile->path);
  531.         error (0, retcode == -1 ? ierrno : 0,
  532.            "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
  533.            vers->srcfile->path);
  534.         (void) unlink_file (xtmpfile);
  535.         return (1);
  536.     }
  537.  
  538.     tocvsPath = wrap_tocvs_process_file (vfile);
  539.     different = xcmp (xtmpfile, vfile);
  540.     if (tocvsPath)
  541.         if (unlink_file_dir (tocvsPath) < 0)
  542.         error (0, errno, "cannot remove %s", tocvsPath);
  543.  
  544.     (void) unlink_file (xtmpfile);
  545.     if (!different)
  546.     {
  547.         int retval = 0;
  548.  
  549.         /*
  550.          * The two files are identical.  Just update the tags, print the
  551.          * "U", signifying that the file has changed, but needs no
  552.          * attention, and we're done.
  553.          */
  554.         if (add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
  555.         retval = 1;
  556.         add_log ('U', vfile);
  557.         freevers_ts (&vers);
  558.         return (retval);
  559.     }
  560.     }
  561.  
  562.     /* We may have failed to parse the RCS file; check just in case */
  563.     if (vers->srcfile == NULL ||
  564.     add_rev (message, vers->srcfile->path, vfile, vers->vn_rcs) ||
  565.     add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
  566.     {
  567.     freevers_ts (&vers);
  568.     return (1);
  569.     }
  570.  
  571.     if (vers->srcfile->branch == NULL || inattic ||
  572.     strcmp (vers->srcfile->branch, vbranch) != 0)
  573.     {
  574.     conflicts++;
  575.     letter = 'C';
  576.     }
  577.     else
  578.     letter = 'U';
  579.     add_log (letter, vfile);
  580.  
  581.     freevers_ts (&vers);
  582.     return (0);
  583. }
  584.  
  585. /*
  586.  * Add the revision to the vendor branch
  587.  */
  588. static int
  589. add_rev (message, rcs, vfile, vers)
  590.     char *message;
  591.     char *rcs;
  592.     char *vfile;
  593.     char *vers;
  594. {
  595.     int locked, status, ierrno;
  596.     char *tocvsPath;
  597.  
  598.     if (noexec)
  599.     return (0);
  600.  
  601.     locked = 0;
  602.     if (vers != NULL)
  603.     {
  604.     /* Before RCS_lock existed, we were directing stdout, as well as
  605.        stderr, from the RCS command, to DEVNULL.  I wouldn't guess that
  606.        was necessary, but I don't know for sure.  */
  607.         if (RCS_lock (rcs, vbranch, 1) != 0)
  608.     {
  609.         error (0, errno, "fork failed");
  610.         return (1);
  611.     }
  612.     locked = 1;
  613.     }
  614.     tocvsPath = wrap_tocvs_process_file (vfile);
  615.     if (tocvsPath == NULL)
  616.     {
  617.     /* We play with hard links rather than passing -u to ci to avoid
  618.        expanding RCS keywords (see test 106.5 in sanity.sh).  */
  619.     if (link_file (vfile, FILE_HOLDER) < 0)
  620.     {
  621.         if (errno == EEXIST)
  622.         {
  623.         (void) unlink_file (FILE_HOLDER);
  624.         (void) link_file (vfile, FILE_HOLDER);
  625.         }
  626.         else
  627.         {
  628.         ierrno = errno;
  629.         fperror (logfp, 0, ierrno,
  630.              "ERROR: cannot create link to %s", vfile);
  631.         error (0, ierrno, "ERROR: cannot create link to %s", vfile);
  632.         return (1);
  633.         }
  634.     }
  635.     }
  636.  
  637.     status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath,
  638.               message, vbranch,
  639.               (RCS_FLAGS_QUIET
  640.                | (use_file_modtime ? RCS_FLAGS_MODTIME : 0)));
  641.     ierrno = errno;
  642.  
  643.     if (tocvsPath == NULL)
  644.     rename_file (FILE_HOLDER, vfile);
  645.     else
  646.     if (unlink_file_dir (tocvsPath) < 0)
  647.         error (0, errno, "cannot remove %s", tocvsPath);
  648.  
  649.     if (status)
  650.     {
  651.     if (!noexec)
  652.     {
  653.         fperror (logfp, 0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
  654.         error (0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
  655.     }
  656.     if (locked)
  657.     {
  658.         (void) RCS_unlock(rcs, vbranch, 0);
  659.     }
  660.     return (1);
  661.     }
  662.     return (0);
  663. }
  664.  
  665. /*
  666.  * Add the vendor branch tag and all the specified import release tags to the
  667.  * RCS file.  The vendor branch tag goes on the branch root (1.1.1) while the
  668.  * vendor release tags go on the newly added leaf of the branch (1.1.1.1,
  669.  * 1.1.1.2, ...).
  670.  */
  671. static int
  672. add_tags (rcs, vfile, vtag, targc, targv)
  673.     char *rcs;
  674.     char *vfile;
  675.     char *vtag;
  676.     int targc;
  677.     char *targv[];
  678. {
  679.     int i, ierrno;
  680.     Vers_TS *vers;
  681.     int retcode = 0;
  682.     struct file_info finfo;
  683.  
  684.     if (noexec)
  685.     return (0);
  686.  
  687.     if ((retcode = RCS_settag(rcs, vtag, vbranch)) != 0)
  688.     {
  689.     ierrno = errno;
  690.     fperror (logfp, 0, retcode == -1 ? ierrno : 0,
  691.          "ERROR: Failed to set tag %s in %s", vtag, rcs);
  692.     error (0, retcode == -1 ? ierrno : 0,
  693.            "ERROR: Failed to set tag %s in %s", vtag, rcs);
  694.     return (1);
  695.     }
  696.  
  697.     memset (&finfo, 0, sizeof finfo);
  698.     finfo.file = vfile;
  699.     /* Not used, so don't worry about it.  */
  700.     finfo.update_dir = NULL;
  701.     finfo.fullname = finfo.file;
  702.     finfo.repository = repository;
  703.     finfo.entries = NULL;
  704.     finfo.rcs = NULL;
  705.     vers = Version_TS (&finfo, NULL, vtag, NULL, 1, 0);
  706.     for (i = 0; i < targc; i++)
  707.     {
  708.     if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) != 0)
  709.     {
  710.         ierrno = errno;
  711.         fperror (logfp, 0, retcode == -1 ? ierrno : 0,
  712.              "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
  713.         error (0, retcode == -1 ? ierrno : 0,
  714.            "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
  715.     }
  716.     }
  717.     freevers_ts (&vers);
  718.     return (0);
  719. }
  720.  
  721. /*
  722.  * Stolen from rcs/src/rcsfnms.c, and adapted/extended.
  723.  */
  724. struct compair
  725. {
  726.     char *suffix, *comlead;
  727. };
  728.  
  729. static const struct compair comtable[] =
  730. {
  731.  
  732. /*
  733.  * comtable pairs each filename suffix with a comment leader. The comment
  734.  * leader is placed before each line generated by the $Log keyword. This
  735.  * table is used to guess the proper comment leader from the working file's
  736.  * suffix during initial ci (see InitAdmin()). Comment leaders are needed for
  737.  * languages without multiline comments; for others they are optional.
  738.  *
  739.  * I believe that the comment leader is unused if you are using RCS 5.7, which
  740.  * decides what leader to use based on the text surrounding the $Log keyword
  741.  * rather than a specified comment leader.
  742.  */
  743.     {"a", "-- "},            /* Ada         */
  744.     {"ada", "-- "},
  745.     {"adb", "-- "},
  746.     {"asm", ";; "},            /* assembler (MS-DOS) */
  747.     {"ads", "-- "},            /* Ada         */
  748.     {"bas", "' "},                /* Visual Basic code */
  749.     {"bat", ":: "},            /* batch (MS-DOS) */
  750.     {"body", "-- "},            /* Ada         */
  751.     {"c", " * "},            /* C         */
  752.     {"c++", "// "},            /* C++ in all its infinite guises */
  753.     {"cc", "// "},
  754.     {"cpp", "// "},
  755.     {"cxx", "// "},
  756.     {"m", "// "},            /* Objective-C */
  757.     {"cl", ";;; "},            /* Common Lisp     */
  758.     {"cmd", ":: "},            /* command (OS/2) */
  759.     {"cmf", "c "},            /* CM Fortran     */
  760.     {"cs", " * "},            /* C*         */
  761.     {"csh", "# "},            /* shell     */
  762.     {"dlg", " * "},               /* MS Windows dialog file */
  763.     {"e", "# "},            /* efl         */
  764.     {"epsf", "% "},            /* encapsulated postscript */
  765.     {"epsi", "% "},            /* encapsulated postscript */
  766.     {"el", "; "},            /* Emacs Lisp     */
  767.     {"f", "c "},            /* Fortran     */
  768.     {"for", "c "},
  769.     {"frm", "' "},                /* Visual Basic form */
  770.     {"h", " * "},            /* C-header     */
  771.     {"hh", "// "},            /* C++ header     */
  772.     {"hpp", "// "},
  773.     {"hxx", "// "},
  774.     {"in", "# "},            /* for Makefile.in */
  775.     {"l", " * "},            /* lex (conflict between lex and
  776.                      * franzlisp) */
  777.     {"mac", ";; "},            /* macro (DEC-10, MS-DOS, PDP-11,
  778.                      * VMS, etc) */
  779.     {"mak", "# "},                /* makefile, e.g. Visual C++ */
  780.     {"me", ".\\\" "},            /* me-macros    t/nroff     */
  781.     {"ml", "; "},            /* mocklisp     */
  782.     {"mm", ".\\\" "},            /* mm-macros    t/nroff     */
  783.     {"ms", ".\\\" "},            /* ms-macros    t/nroff     */
  784.     {"man", ".\\\" "},            /* man-macros    t/nroff     */
  785.     {"1", ".\\\" "},            /* feeble attempt at man pages... */
  786.     {"2", ".\\\" "},
  787.     {"3", ".\\\" "},
  788.     {"4", ".\\\" "},
  789.     {"5", ".\\\" "},
  790.     {"6", ".\\\" "},
  791.     {"7", ".\\\" "},
  792.     {"8", ".\\\" "},
  793.     {"9", ".\\\" "},
  794.     {"p", " * "},            /* pascal     */
  795.     {"pas", " * "},
  796.     {"pl", "# "},            /* perl    (conflict with Prolog) */
  797.     {"ps", "% "},            /* postscript     */
  798.     {"psw", "% "},            /* postscript wrap */
  799.     {"pswm", "% "},            /* postscript wrap */
  800.     {"r", "# "},            /* ratfor     */
  801.     {"rc", " * "},            /* Microsoft Windows resource file */
  802.     {"red", "% "},            /* psl/rlisp     */
  803. #ifdef sparc
  804.     {"s", "! "},            /* assembler     */
  805. #endif
  806. #ifdef mc68000
  807.     {"s", "| "},            /* assembler     */
  808. #endif
  809. #ifdef pdp11
  810.     {"s", "/ "},            /* assembler     */
  811. #endif
  812. #ifdef vax
  813.     {"s", "# "},            /* assembler     */
  814. #endif
  815. #ifdef __ksr__
  816.     {"s", "# "},            /* assembler     */
  817.     {"S", "# "},            /* Macro assembler */
  818. #endif
  819.     {"sh", "# "},            /* shell     */
  820.     {"sl", "% "},            /* psl         */
  821.     {"spec", "-- "},            /* Ada         */
  822.     {"tex", "% "},            /* tex         */
  823.     {"y", " * "},            /* yacc         */
  824.     {"ye", " * "},            /* yacc-efl     */
  825.     {"yr", " * "},            /* yacc-ratfor     */
  826.     {"", "# "},                /* default for empty suffix     */
  827.     {NULL, "# "}            /* default for unknown suffix;     */
  828. /* must always be last         */
  829. };
  830.  
  831. static char *
  832. get_comment (user)
  833.     char *user;
  834. {
  835.     char *cp, *suffix;
  836.     char suffix_path[PATH_MAX];
  837.     int i;
  838.  
  839.     cp = strrchr (user, '.');
  840.     if (cp != NULL)
  841.     {
  842.     cp++;
  843.  
  844.     /*
  845.      * Convert to lower-case, since we are not concerned about the
  846.      * case-ness of the suffix.
  847.      */
  848.     (void) strcpy (suffix_path, cp);
  849.     for (cp = suffix_path; *cp; cp++)
  850.         if (isupper (*cp))
  851.         *cp = tolower (*cp);
  852.     suffix = suffix_path;
  853.     }
  854.     else
  855.     suffix = "";            /* will use the default */
  856.     for (i = 0;; i++)
  857.     {
  858.     if (comtable[i].suffix == NULL)    /* default */
  859.         return (comtable[i].comlead);
  860.     if (strcmp (suffix, comtable[i].suffix) == 0)
  861.         return (comtable[i].comlead);
  862.     }
  863. }
  864.  
  865. static int
  866. add_rcs_file (message, rcs, user, vtag, targc, targv)
  867.     char *message;
  868.     char *rcs;
  869.     char *user;
  870.     char *vtag;
  871.     int targc;
  872.     char *targv[];
  873. {
  874.     FILE *fprcs, *fpuser;
  875.     struct stat sb;
  876.     struct tm *ftm;
  877.     time_t now;
  878.     char altdate1[50];
  879. #ifndef HAVE_RCS5
  880.     char altdate2[50];
  881. #endif
  882.     char *author;
  883.     int i, ierrno, err = 0;
  884.     mode_t mode;
  885.     char *tocvsPath;
  886.     char *userfile;
  887.     char *local_opt = keyword_opt;
  888.     char *free_opt = NULL;
  889.  
  890.     if (noexec)
  891.     return (0);
  892.  
  893.     if (local_opt == NULL)
  894.     {
  895.     if (wrap_name_has (user, WRAP_RCSOPTION))
  896.     {
  897.         local_opt = free_opt = wrap_rcsoption (user, 0);
  898.     }
  899.     }
  900.  
  901.     tocvsPath = wrap_tocvs_process_file (user);
  902.     userfile = (tocvsPath == NULL ? user : tocvsPath);
  903.     fpuser = CVS_FOPEN (userfile, "r");
  904.     if (fpuser == NULL)
  905.     {
  906.     /* not fatal, continue import */
  907.     fperror (logfp, 0, errno, "ERROR: cannot read file %s", userfile);
  908.     error (0, errno, "ERROR: cannot read file %s", userfile);
  909.     goto read_error;
  910.     }
  911.     fprcs = CVS_FOPEN (rcs, "w+b");
  912.     if (fprcs == NULL)
  913.     {
  914.     ierrno = errno;
  915.     goto write_error_noclose;
  916.     }
  917.  
  918.     /*
  919.      * putadmin()
  920.      */
  921.     if (fprintf (fprcs, "head     %s;\012", vhead) < 0 ||
  922.     fprintf (fprcs, "branch   %s;\012", vbranch) < 0 ||
  923.     fprintf (fprcs, "access   ;\012") < 0 ||
  924.     fprintf (fprcs, "symbols  ") < 0)
  925.     {
  926.     goto write_error;
  927.     }
  928.  
  929.     for (i = targc - 1; i >= 0; i--)    /* RCS writes the symbols backwards */
  930.     if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) < 0)
  931.         goto write_error;
  932.  
  933.     if (fprintf (fprcs, "%s:%s;\012", vtag, vbranch) < 0 ||
  934.     fprintf (fprcs, "locks    ; strict;\012") < 0 ||
  935.     /* XXX - make sure @@ processing works in the RCS file */
  936.     fprintf (fprcs, "comment  @%s@;\012", get_comment (user)) < 0)
  937.     {
  938.     goto write_error;
  939.     }
  940.  
  941.     if (local_opt != NULL)
  942.     {
  943.     if (fprintf (fprcs, "expand   @%s@;\012", local_opt) < 0)
  944.     {
  945.         goto write_error;
  946.     }
  947.     }
  948.  
  949.     if (fprintf (fprcs, "\012") < 0)
  950.       goto write_error;
  951.  
  952.     /*
  953.      * puttree()
  954.      */
  955.     if (fstat (fileno (fpuser), &sb) < 0)
  956.     error (1, errno, "cannot fstat %s", user);
  957.     if (use_file_modtime)
  958.     now = sb.st_mtime;
  959.     else
  960.     (void) time (&now);
  961. #ifdef HAVE_RCS5
  962.     ftm = gmtime (&now);
  963. #else
  964.     ftm = localtime (&now);
  965. #endif
  966.     (void) sprintf (altdate1, DATEFORM,
  967.             ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
  968.             ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
  969.             ftm->tm_min, ftm->tm_sec);
  970. #ifdef HAVE_RCS5
  971. #define    altdate2 altdate1
  972. #else
  973.     /*
  974.      * If you don't have RCS V5 or later, you need to lie about the ci
  975.      * time, since RCS V4 and earlier insist that the times differ.
  976.      */
  977.     now++;
  978.     ftm = localtime (&now);
  979.     (void) sprintf (altdate2, DATEFORM,
  980.             ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
  981.             ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
  982.             ftm->tm_min, ftm->tm_sec);
  983. #endif
  984.     author = getcaller ();
  985.  
  986.     if (fprintf (fprcs, "\012%s\012", vhead) < 0 ||
  987.     fprintf (fprcs, "date     %s;  author %s;  state Exp;\012",
  988.          altdate1, author) < 0 ||
  989.     fprintf (fprcs, "branches %s.1;\012", vbranch) < 0 ||
  990.     fprintf (fprcs, "next     ;\012") < 0 ||
  991.     fprintf (fprcs, "\012%s.1\012", vbranch) < 0 ||
  992.     fprintf (fprcs, "date     %s;  author %s;  state Exp;\012",
  993.          altdate2, author) < 0 ||
  994.     fprintf (fprcs, "branches ;\012") < 0 ||
  995.     fprintf (fprcs, "next     ;\012\012") < 0 ||
  996.     /*
  997.      * putdesc()
  998.      */
  999.     fprintf (fprcs, "\012desc\012") < 0 ||
  1000.     fprintf (fprcs, "@@\012\012\012") < 0 ||
  1001.     /*
  1002.      * putdelta()
  1003.      */
  1004.     fprintf (fprcs, "\012%s\012", vhead) < 0 ||
  1005.     fprintf (fprcs, "log\012") < 0 ||
  1006.     fprintf (fprcs, "@Initial revision\012@\012") < 0 ||
  1007.     fprintf (fprcs, "text\012@") < 0)
  1008.     {
  1009.     goto write_error;
  1010.     }
  1011.  
  1012.     /* Now copy over the contents of the file, expanding at signs.  */
  1013.     {
  1014.     char buf[8192];
  1015.     unsigned int len;
  1016.  
  1017.     while (1)
  1018.     {
  1019.         len = fread (buf, 1, sizeof buf, fpuser);
  1020.         if (len == 0)
  1021.         {
  1022.         if (ferror (fpuser))
  1023.             error (1, errno, "cannot read file %s for copying", user);
  1024.         break;
  1025.         }
  1026.         if (expand_at_signs (buf, len, fprcs) < 0)
  1027.         goto write_error;
  1028.     }
  1029.     }
  1030.     if (fprintf (fprcs, "@\012\012") < 0 ||
  1031.     fprintf (fprcs, "\012%s.1\012", vbranch) < 0 ||
  1032.     fprintf (fprcs, "log\012@") < 0 ||
  1033.     expand_at_signs (message, (off_t) strlen (message), fprcs) < 0 ||
  1034.     fprintf (fprcs, "@\012text\012") < 0 ||
  1035.     fprintf (fprcs, "@@\012") < 0)
  1036.     {
  1037.     goto write_error;
  1038.     }
  1039.     if (fclose (fprcs) == EOF)
  1040.     {
  1041.     ierrno = errno;
  1042.     goto write_error_noclose;
  1043.     }
  1044.     (void) fclose (fpuser);
  1045.  
  1046.     /*
  1047.      * Fix the modes on the RCS files.  The user modes of the original
  1048.      * user file are propagated to the group and other modes as allowed
  1049.      * by the repository umask, except that all write permissions are
  1050.      * turned off.
  1051.      */
  1052.     mode = (sb.st_mode |
  1053.         (sb.st_mode & S_IRWXU) >> 3 |
  1054.         (sb.st_mode & S_IRWXU) >> 6) &
  1055.        ~cvsumask &
  1056.        ~(S_IWRITE | S_IWGRP | S_IWOTH);
  1057.     if (chmod (rcs, mode) < 0)
  1058.     {
  1059.     ierrno = errno;
  1060.     fperror (logfp, 0, ierrno,
  1061.          "WARNING: cannot change mode of file %s", rcs);
  1062.     error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
  1063.     err++;
  1064.     }
  1065.     if (tocvsPath)
  1066.     if (unlink_file_dir (tocvsPath) < 0)
  1067.         error (0, errno, "cannot remove %s", tocvsPath);
  1068.     if (free_opt != NULL)
  1069.     free (free_opt);
  1070.     return (err);
  1071.  
  1072. write_error:
  1073.     ierrno = errno;
  1074.     (void) fclose (fprcs);
  1075. write_error_noclose:
  1076.     (void) fclose (fpuser);
  1077.     fperror (logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
  1078.     error (0, ierrno, "ERROR: cannot write file %s", rcs);
  1079.     if (ierrno == ENOSPC)
  1080.     {
  1081.     (void) CVS_UNLINK (rcs);
  1082.     fperror (logfp, 0, 0, "ERROR: out of space - aborting");
  1083.     error (1, 0, "ERROR: out of space - aborting");
  1084.     }
  1085. read_error:
  1086.     if (tocvsPath)
  1087.     if (unlink_file_dir (tocvsPath) < 0)
  1088.         error (0, errno, "cannot remove %s", tocvsPath);
  1089.  
  1090.     if (free_opt != NULL)
  1091.     free (free_opt);
  1092.  
  1093.     return (err + 1);
  1094. }
  1095.  
  1096. /*
  1097.  * Write SIZE bytes at BUF to FP, expanding @ signs into double @
  1098.  * signs.  If an error occurs, return a negative value and set errno
  1099.  * to indicate the error.  If not, return a nonnegative value.
  1100.  */
  1101. static int
  1102. expand_at_signs (buf, size, fp)
  1103.     char *buf;
  1104.     off_t size;
  1105.     FILE *fp;
  1106. {
  1107.     char *cp, *end;
  1108.  
  1109.     errno = 0;
  1110.     for (cp = buf, end = buf + size; cp < end; cp++)
  1111.     {
  1112.     if (*cp == '@')
  1113.     {
  1114.         if (putc ('@', fp) == EOF && errno != 0)
  1115.         return EOF;
  1116.     }
  1117.     if (putc (*cp, fp) == EOF && errno != 0)
  1118.         return (EOF);
  1119.     }
  1120.     return (1);
  1121. }
  1122.  
  1123. /*
  1124.  * Write an update message to (potentially) the screen and the log file.
  1125.  */
  1126. static void
  1127. add_log (ch, fname)
  1128.     int ch;
  1129.     char *fname;
  1130. {
  1131.     if (!really_quiet)            /* write to terminal */
  1132.     {
  1133.     char buf[2];
  1134.     buf[0] = ch;
  1135.     buf[1] = ' ';
  1136.     cvs_output (buf, 2);
  1137.     if (repos_len)
  1138.     {
  1139.         cvs_output (repository + repos_len + 1, 0);
  1140.         cvs_output ("/", 1);
  1141.     }
  1142.     else if (repository[0] != '\0')
  1143.     {
  1144.         cvs_output (repository, 0);
  1145.         cvs_output ("/", 1);
  1146.     }
  1147.     cvs_output (fname, 0);
  1148.     cvs_output ("\n", 1);
  1149.     }
  1150.  
  1151.     if (repos_len)            /* write to logfile */
  1152.     (void) fprintf (logfp, "%c %s/%s\n", ch,
  1153.             repository + repos_len + 1, fname);
  1154.     else if (repository[0])
  1155.     (void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname);
  1156.     else
  1157.     (void) fprintf (logfp, "%c %s\n", ch, fname);
  1158. }
  1159.  
  1160. /*
  1161.  * This is the recursive function that walks the argument directory looking
  1162.  * for sub-directories that have CVS administration files in them and updates
  1163.  * them recursively.
  1164.  * 
  1165.  * Note that we do not follow symbolic links here, which is a feature!
  1166.  */
  1167. static int
  1168. import_descend_dir (message, dir, vtag, targc, targv)
  1169.     char *message;
  1170.     char *dir;
  1171.     char *vtag;
  1172.     int targc;
  1173.     char *targv[];
  1174. {
  1175.     struct saved_cwd cwd;
  1176.     char *cp;
  1177.     int ierrno, err;
  1178.  
  1179.     if (islink (dir))
  1180.     return (0);
  1181.     if (save_cwd (&cwd))
  1182.     {
  1183.     fperror (logfp, 0, 0, "ERROR: cannot get working directory");
  1184.     return (1);
  1185.     }
  1186.     if (repository[0] == '\0')
  1187.     (void) strcpy (repository, dir);
  1188.     else
  1189.     {
  1190.     (void) strcat (repository, "/");
  1191.     (void) strcat (repository, dir);
  1192.     }
  1193. #ifdef CLIENT_SUPPORT
  1194.     if (!quiet && !client_active)
  1195. #else
  1196.     if (!quiet)
  1197. #endif
  1198.     error (0, 0, "Importing %s", repository);
  1199.  
  1200.     if ( CVS_CHDIR (dir) < 0)
  1201.     {
  1202.     ierrno = errno;
  1203.     fperror (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository);
  1204.     error (0, ierrno, "ERROR: cannot chdir to %s", repository);
  1205.     err = 1;
  1206.     goto out;
  1207.     }
  1208. #ifdef CLIENT_SUPPORT
  1209.     if (!client_active && !isdir (repository))
  1210. #else
  1211.     if (!isdir (repository))
  1212. #endif
  1213.     {
  1214.     char rcs[PATH_MAX];
  1215.  
  1216.     (void) sprintf (rcs, "%s%s", repository, RCSEXT);
  1217.     if (isfile (repository) || isfile(rcs))
  1218.     {
  1219.         fperror (logfp, 0, 0, "ERROR: %s is a file, should be a directory!",
  1220.              repository);
  1221.         error (0, 0, "ERROR: %s is a file, should be a directory!",
  1222.            repository);
  1223.         err = 1;
  1224.         goto out;
  1225.     }
  1226.     if (noexec == 0 && CVS_MKDIR (repository, 0777) < 0)
  1227.     {
  1228.         ierrno = errno;
  1229.         fperror (logfp, 0, ierrno,
  1230.              "ERROR: cannot mkdir %s -- not added", repository);
  1231.         error (0, ierrno,
  1232.            "ERROR: cannot mkdir %s -- not added", repository);
  1233.         err = 1;
  1234.         goto out;
  1235.     }
  1236.     }
  1237.     err = import_descend (message, vtag, targc, targv);
  1238.   out:
  1239.     if ((cp = strrchr (repository, '/')) != NULL)
  1240.     *cp = '\0';
  1241.     else
  1242.     repository[0] = '\0';
  1243.     if (restore_cwd (&cwd, NULL))
  1244.       exit (EXIT_FAILURE);
  1245.     free_cwd (&cwd);
  1246.     return (err);
  1247. }
  1248.