summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/cvs/cvs/import.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/cvs/cvs/import.c')
-rw-r--r--gnu/usr.bin/cvs/cvs/import.c974
1 files changed, 974 insertions, 0 deletions
diff --git a/gnu/usr.bin/cvs/cvs/import.c b/gnu/usr.bin/cvs/cvs/import.c
new file mode 100644
index 0000000..bf66dcb
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/import.c
@@ -0,0 +1,974 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * "import" checks in the vendor release located in the current directory into
+ * the CVS source repository. The CVS vendor branch support is utilized.
+ *
+ * At least three arguments are expected to follow the options:
+ * repository Where the source belongs relative to the CVSROOT
+ * VendorTag Vendor's major tag
+ * VendorReleTag Tag for this particular release
+ *
+ * Additional arguments specify more Vendor Release Tags.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)import.c 1.52 92/03/31";
+#endif
+
+#define FILE_HOLDER ".#cvsxxx"
+
+#if __STDC__
+static char *get_comment (char *user);
+static int add_rcs_file (char *message, char *rcs, char *user, char *vtag,
+ int targc, char *targv[]);
+static int expand_at_signs (char *buf, off_t size, FILE *fp);
+static int add_rev (char *message, char *rcs, char *vfile, char *vers);
+static int add_tags (char *rcs, char *vfile, char *vtag, int targc,
+ char *targv[]);
+static int import_descend (char *message, char *vtag, int targc, char *targv[]);
+static int import_descend_dir (char *message, char *dir, char *vtag,
+ int targc, char *targv[]);
+static int process_import_file (char *message, char *vfile, char *vtag,
+ int targc, char *targv[]);
+static int update_rcs_file (char *message, char *vfile, char *vtag, int targc,
+ char *targv[]);
+static void add_log (int ch, char *fname);
+#else
+static int import_descend ();
+static int process_import_file ();
+static int update_rcs_file ();
+static int add_rev ();
+static int add_tags ();
+static char *get_comment ();
+static int add_rcs_file ();
+static int expand_at_signs ();
+static void add_log ();
+static int import_descend_dir ();
+#endif /* __STDC__ */
+
+static int repos_len;
+static char vhead[50];
+static char vbranch[50];
+static FILE *logfp;
+static char repository[PATH_MAX];
+static int conflicts;
+
+static char *import_usage[] =
+{
+ "Usage: %s %s [-Qq] [-I ign] [-m msg] [-b branch]\n",
+ " repository vendor-tag release-tags...\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-q\tSomewhat quiet.\n",
+ "\t-I ign\tMore files to ignore (! to reset).\n",
+ "\t-b bra\tVendor branch id.\n",
+ "\t-m msg\tLog message.\n",
+ NULL
+};
+
+int
+import (argc, argv)
+ int argc;
+ char *argv[];
+{
+ char message[MAXMESGLEN];
+ char tmpfile[L_tmpnam+1];
+ char *cp;
+ int i, c, msglen, err;
+ List *ulist;
+ Node *p;
+
+ if (argc == -1)
+ usage (import_usage);
+
+ ign_setup ();
+
+ (void) strcpy (vbranch, CVSBRANCH);
+ message[0] = '\0';
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "Qqb:m:I:")) != -1)
+ {
+ switch (c)
+ {
+ case 'Q':
+ really_quiet = 1;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = 1;
+ break;
+ case 'b':
+ (void) strcpy (vbranch, optarg);
+ break;
+ case 'm':
+#ifdef FORCE_USE_EDITOR
+ use_editor = TRUE;
+#else
+ use_editor = FALSE;
+#endif
+ if (strlen (optarg) >= (sizeof (message) - 1))
+ {
+ error (0, 0, "warning: message too long; truncated!");
+ (void) strncpy (message, optarg, sizeof (message));
+ message[sizeof (message) - 2] = '\0';
+ }
+ else
+ (void) strcpy (message, optarg);
+ break;
+ case 'I':
+ ign_add (optarg, 0);
+ break;
+ case '?':
+ default:
+ usage (import_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 3)
+ usage (import_usage);
+
+ for (i = 1; i < argc; i++) /* check the tags for validity */
+ RCS_check_tag (argv[i]);
+
+ /* XXX - this should be a module, not just a pathname */
+ if (argv[0][0] != '/')
+ {
+ if (CVSroot == NULL)
+ {
+ error (0, 0, "missing CVSROOT environment variable\n");
+ error (1, 0, "Set it or specify the '-d' option to %s.",
+ program_name);
+ }
+ (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
+ repos_len = strlen (CVSroot);
+ }
+ else
+ {
+ (void) strcpy (repository, argv[0]);
+ repos_len = 0;
+ }
+
+ /*
+ * Consistency checks on the specified vendor branch. It must be
+ * composed of only numbers and dots ('.'). Also, for now we only
+ * support branching to a single level, so the specified vendor branch
+ * must only have two dots in it (like "1.1.1").
+ */
+ for (cp = vbranch; *cp != '\0'; cp++)
+ if (!isdigit (*cp) && *cp != '.')
+ error (1, 0, "%s is not a numeric branch", vbranch);
+ if (numdots (vbranch) != 2)
+ error (1, 0, "Only branches with two dots are supported: %s", vbranch);
+ (void) strcpy (vhead, vbranch);
+ cp = rindex (vhead, '.');
+ *cp = '\0';
+ if (use_editor)
+ do_editor ((char *) NULL, message, repository, (List *) NULL);
+ msglen = strlen (message);
+ if (msglen == 0 || message[msglen - 1] != '\n')
+ {
+ message[msglen] = '\n';
+ message[msglen + 1] = '\0';
+ }
+
+ /*
+ * Make all newly created directories writable. Should really use a more
+ * sophisticated security mechanism here.
+ */
+ (void) umask (2);
+ make_directories (repository);
+
+ /* Create the logfile that will be logged upon completion */
+ if ((logfp = fopen (tmpnam (tmpfile), "w+")) == NULL)
+ error (1, errno, "cannot create temporary file `%s'", tmpfile);
+ (void) unlink (tmpfile); /* to be sure it goes away */
+ (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
+ (void) fprintf (logfp, "Release Tags:\t");
+ for (i = 2; i < argc; i++)
+ (void) fprintf (logfp, "%s\n\t\t", argv[i]);
+ (void) fprintf (logfp, "\n");
+
+ /* Just Do It. */
+ err = import_descend (message, argv[1], argc - 2, argv + 2);
+ if (conflicts)
+ {
+ if (!really_quiet)
+ {
+ (void) printf ("\n%d conflicts created by this import.\n",
+ conflicts);
+ (void) printf ("Use the following command to help the merge:\n\n");
+ (void) printf ("\t%s checkout -j%s:yesterday -j%s %s\n\n",
+ program_name, argv[1], argv[1], argv[0]);
+ }
+
+ (void) fprintf (logfp, "\n%d conflicts created by this import.\n",
+ conflicts);
+ (void) fprintf (logfp,
+ "Use the following command to help the merge:\n\n");
+ (void) fprintf (logfp, "\t%s checkout -j%s:yesterday -j%s %s\n\n",
+ program_name, argv[1], argv[1], argv[0]);
+ }
+ else
+ {
+ if (!really_quiet)
+ (void) printf ("\nNo conflicts created by this import\n\n");
+ (void) fprintf (logfp, "\nNo conflicts created by this import\n\n");
+ }
+
+ /*
+ * Write out the logfile and clean up.
+ */
+ ulist = getlist ();
+ p = getnode ();
+ p->type = UPDATE;
+ p->delproc = update_delproc;
+ p->key = xstrdup ("- Imported sources");
+ p->data = (char *) T_TITLE;
+ (void) addnode (ulist, p);
+ Update_Logfile (repository, message, vbranch, logfp, ulist);
+ dellist (&ulist);
+ (void) fclose (logfp);
+ return (err);
+}
+
+/*
+ * process all the files in ".", then descend into other directories.
+ */
+static int
+import_descend (message, vtag, targc, targv)
+ char *message;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ DIR *dirp;
+ struct direct *dp;
+ int err = 0;
+ int has_dirs = 0;
+
+ /* first, load up any per-directory ignore lists */
+ ign_add_file (CVSDOTIGNORE, 1);
+
+ if ((dirp = opendir (".")) == NULL)
+ {
+ err++;
+ }
+ else
+ {
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
+ continue;
+ if (ign_name (dp->d_name))
+ {
+ add_log ('I', dp->d_name);
+ continue;
+ }
+ if (isdir (dp->d_name))
+ {
+ has_dirs = 1;
+ }
+ else
+ {
+ if (islink (dp->d_name))
+ {
+ add_log ('L', dp->d_name);
+ err++;
+ }
+ else
+ {
+ err += process_import_file (message, dp->d_name,
+ vtag, targc, targv);
+ }
+ }
+ }
+ (void) closedir (dirp);
+ }
+ if (has_dirs)
+ {
+ if ((dirp = opendir (".")) == NULL)
+ err++;
+ else
+ {
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (ign_name (dp->d_name) || !isdir (dp->d_name))
+ continue;
+ err += import_descend_dir (message, dp->d_name,
+ vtag, targc, targv);
+ }
+ (void) closedir (dirp);
+ }
+ }
+ return (err);
+}
+
+/*
+ * Process the argument import file.
+ */
+static int
+process_import_file (message, vfile, vtag, targc, targv)
+ char *message;
+ char *vfile;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ char attic_name[PATH_MAX];
+ char rcs[PATH_MAX];
+
+ (void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT);
+ if (!isfile (rcs))
+ {
+ (void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC,
+ vfile, RCSEXT);
+ if (!isfile (attic_name))
+ {
+
+ /*
+ * A new import source file; it doesn't exist as a ,v within the
+ * repository nor in the Attic -- create it anew.
+ */
+ add_log ('N', vfile);
+ return (add_rcs_file (message, rcs, vfile, vtag, targc, targv));
+ }
+ }
+
+ /*
+ * an rcs file exists. have to do things the official, slow, way.
+ */
+ return (update_rcs_file (message, vfile, vtag, targc, targv));
+}
+
+/*
+ * The RCS file exists; update it by adding the new import file to the
+ * (possibly already existing) vendor branch.
+ */
+static int
+update_rcs_file (message, vfile, vtag, targc, targv)
+ char *message;
+ char *vfile;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ Vers_TS *vers;
+ char letter;
+ int ierrno;
+
+ vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile,
+ 1, 0, (List *) NULL, (List *) NULL);
+ if (vers->vn_rcs != NULL)
+ {
+ char xtmpfile[50];
+ int different;
+ int retcode = 0;
+
+ /* XXX - should be more unique */
+ (void) sprintf (xtmpfile, "/tmp/%s", FILE_HOLDER);
+
+ /*
+ * The rcs file does have a revision on the vendor branch. Compare
+ * this revision with the import file; if they match exactly, there
+ * is no need to install the new import file as a new revision to the
+ * branch. Just tag the revision with the new import tags.
+ *
+ * This is to try to cut down the number of "C" conflict messages for
+ * locally modified import source files.
+ */
+#ifdef HAVE_RCS5
+ run_setup ("%s%s -q -f -r%s -p -ko", Rcsbin, RCS_CO, vers->vn_rcs);
+#else
+ run_setup ("%s%s -q -f -r%s -p", Rcsbin, RCS_CO, vers->vn_rcs);
+#endif
+ run_arg (vers->srcfile->path);
+ if ((retcode = run_exec (RUN_TTY, xtmpfile, RUN_TTY,
+ RUN_NORMAL|RUN_REALLY)) != 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, retcode == -1 ? ierrno : 0,
+ "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
+ vers->srcfile->path);
+ error (0, retcode == -1 ? ierrno : 0,
+ "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
+ vers->srcfile->path);
+ (void) unlink_file (xtmpfile);
+ return (1);
+ }
+ different = xcmp (xtmpfile, vfile);
+ (void) unlink_file (xtmpfile);
+ if (!different)
+ {
+ int retval = 0;
+
+ /*
+ * The two files are identical. Just update the tags, print the
+ * "U", signifying that the file has changed, but needs no
+ * attention, and we're done.
+ */
+ if (add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
+ retval = 1;
+ add_log ('U', vfile);
+ freevers_ts (&vers);
+ return (retval);
+ }
+ }
+
+ /* We may have failed to parse the RCS file; check just in case */
+ if (vers->srcfile == NULL || add_rev (message, vers->srcfile->path,
+ vfile, vers->vn_rcs) ||
+ add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
+ {
+ freevers_ts (&vers);
+ return (1);
+ }
+
+ if (vers->srcfile->branch == NULL ||
+ strcmp (vers->srcfile->branch, vbranch) != 0)
+ {
+ conflicts++;
+ letter = 'C';
+ }
+ else
+ letter = 'U';
+ add_log (letter, vfile);
+
+ freevers_ts (&vers);
+ return (0);
+}
+
+/*
+ * Add the revision to the vendor branch
+ */
+static int
+add_rev (message, rcs, vfile, vers)
+ char *message;
+ char *rcs;
+ char *vfile;
+ char *vers;
+{
+ int locked, status, ierrno;
+ int retcode = 0;
+
+ if (noexec)
+ return (0);
+
+ locked = 0;
+ if (vers != NULL)
+ {
+ run_setup ("%s%s -q -l%s", Rcsbin, RCS, vbranch);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, DEVNULL, DEVNULL, RUN_NORMAL)) == 0)
+ locked = 1;
+ else if (retcode == -1)
+ {
+ error (0, errno, "fork failed");
+ return (1);
+ }
+ }
+ if (link_file (vfile, FILE_HOLDER) < 0)
+ {
+ if (errno == EEXIST)
+ {
+ (void) unlink_file (FILE_HOLDER);
+ (void) link_file (vfile, FILE_HOLDER);
+ }
+ else
+ {
+ ierrno = errno;
+ fperror (logfp, 0, ierrno, "ERROR: cannot create link to %s", vfile);
+ error (0, ierrno, "ERROR: cannot create link to %s", vfile);
+ return (1);
+ }
+ }
+ run_setup ("%s%s -q -f -r%s", Rcsbin, RCS_CI, vbranch);
+ run_args ("-m%s", message);
+ run_arg (rcs);
+ status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ ierrno = errno;
+ rename_file (FILE_HOLDER, vfile);
+ if (status)
+ {
+ if (!noexec)
+ {
+ fperror (logfp, 0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
+ error (0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
+ }
+ if (locked)
+ {
+ run_setup ("%s%s -q -u%s", Rcsbin, RCS, vbranch);
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Add the vendor branch tag and all the specified import release tags to the
+ * RCS file. The vendor branch tag goes on the branch root (1.1.1) while the
+ * vendor release tags go on the newly added leaf of the branch (1.1.1.1,
+ * 1.1.1.2, ...).
+ */
+static int
+add_tags (rcs, vfile, vtag, targc, targv)
+ char *rcs;
+ char *vfile;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ int i, ierrno;
+ Vers_TS *vers;
+ int retcode = 0;
+
+ if (noexec)
+ return (0);
+
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, vtag, vbranch);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, retcode == -1 ? ierrno : 0,
+ "ERROR: Failed to set tag %s in %s", vtag, rcs);
+ error (0, retcode == -1 ? ierrno : 0,
+ "ERROR: Failed to set tag %s in %s", vtag, rcs);
+ return (1);
+ }
+ vers = Version_TS (repository, (char *) NULL, vtag, (char *) NULL, vfile,
+ 1, 0, (List *) NULL, (List *) NULL);
+ for (i = 0; i < targc; i++)
+ {
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, targv[i], vers->vn_rcs);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, retcode == -1 ? ierrno : 0,
+ "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
+ error (0, retcode == -1 ? ierrno : 0,
+ "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
+ }
+ }
+ freevers_ts (&vers);
+ return (0);
+}
+
+/*
+ * Stolen from rcs/src/rcsfnms.c, and adapted/extended.
+ */
+struct compair
+{
+ char *suffix, *comlead;
+};
+
+struct compair comtable[] =
+{
+
+/*
+ * comtable pairs each filename suffix with a comment leader. The comment
+ * leader is placed before each line generated by the $Log keyword. This
+ * table is used to guess the proper comment leader from the working file's
+ * suffix during initial ci (see InitAdmin()). Comment leaders are needed for
+ * languages without multiline comments; for others they are optional.
+ */
+ "a", "-- ", /* Ada */
+ "ada", "-- ",
+ "asm", ";; ", /* assembler (MS-DOS) */
+ "bat", ":: ", /* batch (MS-DOS) */
+ "c", " * ", /* C */
+ "c++", "// ", /* C++ in all its infinite guises */
+ "cc", "// ",
+ "cpp", "// ",
+ "cxx", "// ",
+ "cl", ";;; ", /* Common Lisp */
+ "cmd", ":: ", /* command (OS/2) */
+ "cmf", "c ", /* CM Fortran */
+ "cs", " * ", /* C* */
+ "csh", "# ", /* shell */
+ "e", "# ", /* efl */
+ "el", "; ", /* Emacs Lisp */
+ "f", "c ", /* Fortran */
+ "for", "c ",
+ "h", " * ", /* C-header */
+ "hh", "// ", /* C++ header */
+ "hpp", "// ",
+ "hxx", "// ",
+ "in", "# ", /* for Makefile.in */
+ "l", " * ", /* lex (conflict between lex and
+ * franzlisp) */
+ "mac", ";; ", /* macro (DEC-10, MS-DOS, PDP-11,
+ * VMS, etc) */
+ "me", ".\\\" ", /* me-macros t/nroff */
+ "ml", "; ", /* mocklisp */
+ "mm", ".\\\" ", /* mm-macros t/nroff */
+ "ms", ".\\\" ", /* ms-macros t/nroff */
+ "man", ".\\\" ", /* man-macros t/nroff */
+ "1", ".\\\" ", /* feeble attempt at man pages... */
+ "2", ".\\\" ",
+ "3", ".\\\" ",
+ "4", ".\\\" ",
+ "5", ".\\\" ",
+ "6", ".\\\" ",
+ "7", ".\\\" ",
+ "8", ".\\\" ",
+ "9", ".\\\" ",
+ "p", " * ", /* pascal */
+ "pas", " * ",
+ "pl", "# ", /* perl (conflict with Prolog) */
+ "ps", "% ", /* postscript */
+ "r", "# ", /* ratfor */
+ "red", "% ", /* psl/rlisp */
+#ifdef sparc
+ "s", "! ", /* assembler */
+#endif
+#ifdef mc68000
+ "s", "| ", /* assembler */
+#endif
+#ifdef pdp11
+ "s", "/ ", /* assembler */
+#endif
+#ifdef vax
+ "s", "# ", /* assembler */
+#endif
+#ifdef __ksr__
+ "s", "# ", /* assembler */
+ "S", "# ", /* Macro assembler */
+#endif
+ "sh", "# ", /* shell */
+ "sl", "% ", /* psl */
+ "tex", "% ", /* tex */
+ "y", " * ", /* yacc */
+ "ye", " * ", /* yacc-efl */
+ "yr", " * ", /* yacc-ratfor */
+ "", "# ", /* default for empty suffix */
+ NULL, "# " /* default for unknown suffix; */
+/* must always be last */
+};
+
+static char *
+get_comment (user)
+ char *user;
+{
+ char *cp, *suffix;
+ char suffix_path[PATH_MAX];
+ int i;
+
+ cp = rindex (user, '.');
+ if (cp != NULL)
+ {
+ cp++;
+
+ /*
+ * Convert to lower-case, since we are not concerned about the
+ * case-ness of the suffix.
+ */
+ (void) strcpy (suffix_path, cp);
+ for (cp = suffix_path; *cp; cp++)
+ if (isupper (*cp))
+ *cp = tolower (*cp);
+ suffix = suffix_path;
+ }
+ else
+ suffix = ""; /* will use the default */
+ for (i = 0;; i++)
+ {
+ if (comtable[i].suffix == NULL) /* default */
+ return (comtable[i].comlead);
+ if (strcmp (suffix, comtable[i].suffix) == 0)
+ return (comtable[i].comlead);
+ }
+}
+
+static int
+add_rcs_file (message, rcs, user, vtag, targc, targv)
+ char *message;
+ char *rcs;
+ char *user;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ FILE *fprcs, *fpuser;
+ struct stat sb;
+ struct tm *ftm;
+ time_t now;
+ char altdate1[50], altdate2[50];
+ char *author, *buf;
+ int i, mode, ierrno, err = 0;
+
+ if (noexec)
+ return (0);
+
+ fprcs = open_file (rcs, "w+");
+ fpuser = open_file (user, "r");
+
+ /*
+ * putadmin()
+ */
+ if (fprintf (fprcs, "head %s;\n", vhead) == EOF ||
+ fprintf (fprcs, "branch %s;\n", vbranch) == EOF ||
+ fprintf (fprcs, "access ;\n") == EOF ||
+ fprintf (fprcs, "symbols ") == EOF)
+ {
+ goto write_error;
+ }
+
+ for (i = targc - 1; i >= 0; i--) /* RCS writes the symbols backwards */
+ if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) == EOF)
+ goto write_error;
+
+ if (fprintf (fprcs, "%s:%s;\n", vtag, vbranch) == EOF ||
+ fprintf (fprcs, "locks ; strict;\n") == EOF ||
+ /* XXX - make sure @@ processing works in the RCS file */
+ fprintf (fprcs, "comment @%s@;\n\n", get_comment (user)) == EOF)
+ {
+ goto write_error;
+ }
+
+ /*
+ * puttree()
+ */
+ (void) time (&now);
+#ifdef HAVE_RCS5
+ ftm = gmtime (&now);
+#else
+ ftm = localtime (&now);
+#endif
+ (void) sprintf (altdate1, DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ now++;
+#ifdef HAVE_RCS5
+ ftm = gmtime (&now);
+#else
+ ftm = localtime (&now);
+#endif
+ (void) sprintf (altdate2, DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ author = getcaller ();
+
+ if (fprintf (fprcs, "\n%s\n", vhead) == EOF ||
+ fprintf (fprcs, "date %s; author %s; state Exp;\n",
+ altdate1, author) == EOF ||
+ fprintf (fprcs, "branches %s.1;\n", vbranch) == EOF ||
+ fprintf (fprcs, "next ;\n") == EOF ||
+ fprintf (fprcs, "\n%s.1\n", vbranch) == EOF ||
+ fprintf (fprcs, "date %s; author %s; state Exp;\n",
+ altdate2, author) == EOF ||
+ fprintf (fprcs, "branches ;\n") == EOF ||
+ fprintf (fprcs, "next ;\n\n") == EOF ||
+ /*
+ * putdesc()
+ */
+ fprintf (fprcs, "\ndesc\n") == EOF ||
+ fprintf (fprcs, "@@\n\n\n") == EOF ||
+ /*
+ * putdelta()
+ */
+ fprintf (fprcs, "\n%s\n", vhead) == EOF ||
+ fprintf (fprcs, "log\n") == EOF ||
+ fprintf (fprcs, "@Initial revision\n@\n") == EOF ||
+ fprintf (fprcs, "text\n@") == EOF)
+ {
+ goto write_error;
+ }
+
+ if (fstat (fileno (fpuser), &sb) < 0)
+ error (1, errno, "cannot fstat %s", user);
+ if (sb.st_size > 0)
+ {
+ off_t size;
+
+ size = sb.st_size;
+ buf = xmalloc ((int) size);
+ if (fread (buf, (int) size, 1, fpuser) != 1)
+ error (1, errno, "cannot read file %s for copying", user);
+ if (expand_at_signs (buf, size, fprcs) == EOF)
+ goto write_error;
+ free (buf);
+ }
+ if (fprintf (fprcs, "@\n\n") == EOF ||
+ fprintf (fprcs, "\n%s.1\n", vbranch) == EOF ||
+ fprintf (fprcs, "log\n@") == EOF ||
+ expand_at_signs (message, (off_t) strlen (message), fprcs) == EOF ||
+ fprintf (fprcs, "@\ntext\n") == EOF ||
+ fprintf (fprcs, "@@\n") == EOF)
+ {
+ goto write_error;
+ }
+ if (fclose (fprcs) == EOF)
+ {
+ ierrno = errno;
+ goto write_error_noclose;
+ }
+ (void) fclose (fpuser);
+
+ /*
+ * Fix the modes on the RCS files. They must maintain the same modes as
+ * the original user file, except that all write permissions must be
+ * turned off.
+ */
+ mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
+ if (chmod (rcs, mode) < 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, ierrno,
+ "WARNING: cannot change mode of file %s", rcs);
+ error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
+ err++;
+ }
+ return (err);
+
+write_error:
+ ierrno = errno;
+ (void) fclose (fprcs);
+write_error_noclose:
+ (void) fclose (fpuser);
+ fperror (logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
+ error (0, ierrno, "ERROR: cannot write file %s", rcs);
+ if (ierrno == ENOSPC)
+ {
+ (void) unlink (rcs);
+ fperror (logfp, 0, 0, "ERROR: out of space - aborting");
+ error (1, 0, "ERROR: out of space - aborting");
+ }
+ return (err + 1);
+}
+
+/*
+ * Sigh.. need to expand @ signs into double @ signs
+ */
+static int
+expand_at_signs (buf, size, fp)
+ char *buf;
+ off_t size;
+ FILE *fp;
+{
+ char *cp, *end;
+
+ for (cp = buf, end = buf + size; cp < end; cp++)
+ {
+ if (*cp == '@')
+ (void) putc ('@', fp);
+ if (putc (*cp, fp) == EOF)
+ return (EOF);
+ }
+ return (1);
+}
+
+/*
+ * Write an update message to (potentially) the screen and the log file.
+ */
+static void
+add_log (ch, fname)
+ char ch;
+ char *fname;
+{
+ if (!really_quiet) /* write to terminal */
+ {
+ if (repos_len)
+ (void) printf ("%c %s/%s\n", ch, repository + repos_len + 1, fname);
+ else if (repository[0])
+ (void) printf ("%c %s/%s\n", ch, repository, fname);
+ else
+ (void) printf ("%c %s\n", ch, fname);
+ }
+
+ if (repos_len) /* write to logfile */
+ (void) fprintf (logfp, "%c %s/%s\n", ch,
+ repository + repos_len + 1, fname);
+ else if (repository[0])
+ (void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname);
+ else
+ (void) fprintf (logfp, "%c %s\n", ch, fname);
+}
+
+/*
+ * This is the recursive function that walks the argument directory looking
+ * for sub-directories that have CVS administration files in them and updates
+ * them recursively.
+ *
+ * Note that we do not follow symbolic links here, which is a feature!
+ */
+static int
+import_descend_dir (message, dir, vtag, targc, targv)
+ char *message;
+ char *dir;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ char cwd[PATH_MAX];
+ char *cp;
+ int ierrno, err;
+
+ if (islink (dir))
+ return (0);
+ if (getwd (cwd) == NULL)
+ {
+ fperror (logfp, 0, 0, "ERROR: cannot get working directory: %s", cwd);
+ error (0, 0, "ERROR: cannot get working directory: %s", cwd);
+ return (1);
+ }
+ if (repository[0] == '\0')
+ (void) strcpy (repository, dir);
+ else
+ {
+ (void) strcat (repository, "/");
+ (void) strcat (repository, dir);
+ }
+ if (!quiet)
+ error (0, 0, "Importing %s", repository);
+ if (chdir (dir) < 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository);
+ error (0, ierrno, "ERROR: cannot chdir to %s", repository);
+ err = 1;
+ goto out;
+ }
+ if (!isdir (repository))
+ {
+ if (isfile (repository))
+ {
+ fperror (logfp, 0, 0, "ERROR: %s is a file, should be a directory!",
+ repository);
+ error (0, 0, "ERROR: %s is a file, should be a directory!",
+ repository);
+ err = 1;
+ goto out;
+ }
+ if (noexec == 0 && mkdir (repository, 0777) < 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, ierrno,
+ "ERROR: cannot mkdir %s -- not added", repository);
+ error (0, ierrno,
+ "ERROR: cannot mkdir %s -- not added", repository);
+ err = 1;
+ goto out;
+ }
+ }
+ err = import_descend (message, vtag, targc, targv);
+ out:
+ if ((cp = rindex (repository, '/')) != NULL)
+ *cp = '\0';
+ else
+ repository[0] = '\0';
+ if (chdir (cwd) < 0)
+ error (1, errno, "cannot chdir to %s", cwd);
+ return (err);
+}
OpenPOWER on IntegriCloud