diff options
author | Marc Espie <espie@cvs.openbsd.org> | 1999-06-16 14:02:21 +0000 |
---|---|---|
committer | Marc Espie <espie@cvs.openbsd.org> | 1999-06-16 14:02:21 +0000 |
commit | 2810ed462ee6cd7f96afc43b988e660da74da548 (patch) | |
tree | ad31db4c3190f540434775d9e724dc1dc2125c9e | |
parent | 2e0f656a249424dac2245971ac28096ec191dd69 (diff) |
ARCH -> __ARCH_
-rw-r--r-- | gnu/usr.bin/cvs/src/import.c | 1057 |
1 files changed, 736 insertions, 321 deletions
diff --git a/gnu/usr.bin/cvs/src/import.c b/gnu/usr.bin/cvs/src/import.c index 95d3c4338d0..dcb35d4b818 100644 --- a/gnu/usr.bin/cvs/src/import.c +++ b/gnu/usr.bin/cvs/src/import.c @@ -3,7 +3,7 @@ * 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.4 kit. + * specified in the README file that comes with the CVS source distribution. * * "import" checks in the vendor release located in the current directory into * the CVS source repository. The CVS vendor branch support is utilized. @@ -17,21 +17,13 @@ */ #include "cvs.h" -#include "save-cwd.h" - -#ifndef lint -static const char rcsid[] = "$CVSid: @(#)import.c 1.63 94/09/30 $"; -USE(rcsid); -#endif - -#define FILE_HOLDER ".#cvsxxx" +#include "savecwd.h" +#include <assert.h> static char *get_comment PROTO((char *user)); -static int add_rcs_file PROTO((char *message, char *rcs, char *user, char *vtag, - int targc, char *targv[])); -static int expand_at_signs PROTO((char *buf, off_t size, FILE *fp)); -static int add_rev PROTO((char *message, char *rcs, char *vfile, char *vers)); -static int add_tags PROTO((char *rcs, char *vfile, char *vtag, int targc, +static int add_rev PROTO((char *message, RCSNode *rcs, char *vfile, + char *vers)); +static int add_tags PROTO((RCSNode *rcs, char *vfile, char *vtag, int targc, char *targv[])); static int import_descend PROTO((char *message, char *vtag, int targc, char *targv[])); static int import_descend_dir PROTO((char *message, char *dir, char *vtag, @@ -43,10 +35,10 @@ static int update_rcs_file PROTO((char *message, char *vfile, char *vtag, int ta static void add_log PROTO((int ch, char *fname)); static int repos_len; -static char vhead[50]; -static char vbranch[50]; +static char *vhead; +static char *vbranch; static FILE *logfp; -static char repository[PATH_MAX]; +static char *repository; static int conflicts; static int use_file_modtime; static char *keyword_opt = NULL; @@ -61,6 +53,7 @@ static const char *const import_usage[] = "\t-b bra\tVendor branch id.\n", "\t-m msg\tLog message.\n", "\t-W spec\tWrappers specification line.\n", + "(Specify the --help global option for a list of other help options)\n", NULL }; @@ -70,11 +63,12 @@ import (argc, argv) char **argv; { char *message = NULL; - char tmpfile[L_tmpnam+1]; + char *tmpfile; char *cp; int i, c, msglen, err; List *ulist; Node *p; + struct logfile_info *li; if (argc == -1) usage (import_usage); @@ -82,9 +76,9 @@ import (argc, argv) ign_setup (); wrap_setup (); - (void) strcpy (vbranch, CVSBRANCH); - optind = 1; - while ((c = getopt (argc, argv, "Qqdb:m:I:k:W:")) != -1) + vbranch = xstrdup (CVSBRANCH); + optind = 0; + while ((c = getopt (argc, argv, "+Qqdb:m:I:k:W:")) != -1) { switch (c) { @@ -100,16 +94,28 @@ import (argc, argv) command_name); break; case 'd': +#ifdef SERVER_SUPPORT + if (server_active) + { + /* CVS 1.10 and older clients will send this, but it + doesn't do any good. So tell the user we can't + cope, rather than silently losing. */ + error (0, 0, + "warning: not setting the time of import from the file"); + error (0, 0, "due to client limitations"); + } +#endif use_file_modtime = 1; break; case 'b': - (void) strcpy (vbranch, optarg); + free (vbranch); + vbranch = xstrdup (optarg); break; case 'm': #ifdef FORCE_USE_EDITOR - use_editor = TRUE; + use_editor = 1; #else - use_editor = FALSE; + use_editor = 0; #endif message = xstrdup(optarg); break; @@ -120,7 +126,7 @@ import (argc, argv) /* RCS_check_kflag returns strings of the form -kxx. We only use it for validation, so we can free the value as soon as it is returned. */ - free (RCS_check_kflag(optarg)); + free (RCS_check_kflag (optarg)); keyword_opt = optarg; break; case 'W': @@ -137,23 +143,47 @@ import (argc, argv) if (argc < 3) usage (import_usage); +#ifdef SERVER_SUPPORT + /* This is for handling the Checkin-time request. It might seem a + bit odd to enable the use_file_modtime code even in the case + where Checkin-time was not sent for a particular file. The + effect is that we use the time of upload, rather than the time + when we call RCS_checkin. Since those times are both during + CVS's run, that seems OK, and it is easier to implement than + putting the "was Checkin-time sent" flag in CVS/Entries or some + such place. */ + + if (server_active) + use_file_modtime = 1; +#endif + for (i = 1; i < argc; i++) /* check the tags for validity */ + { + int j; + RCS_check_tag (argv[i]); + for (j = 1; j < i; j++) + if (strcmp (argv[j], argv[i]) == 0) + error (1, 0, "tag `%s' was specified more than once", argv[i]); + } /* XXX - this should be a module, not just a pathname */ if (! isabsolute (argv[0])) { - if (CVSroot == NULL) + if (CVSroot_directory == 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); + repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0]) + + 10); + (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); + repos_len = strlen (CVSroot_directory); } else { + repository = xmalloc (strlen (argv[0]) + 5); (void) strcpy (repository, argv[0]); repos_len = 0; } @@ -165,20 +195,19 @@ import (argc, argv) * must only have two dots in it (like "1.1.1"). */ for (cp = vbranch; *cp != '\0'; cp++) - if (!isdigit (*cp) && *cp != '.') + if (!isdigit ((unsigned char) *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); + vhead = xstrdup (vbranch); cp = strrchr (vhead, '.'); *cp = '\0'; #ifdef CLIENT_SUPPORT if (client_active) { - /* Do this now; don't ask for a log message if we can't talk to the - server. But if there is a syntax error in the options, give - an error message without connecting. */ + /* For rationale behind calling start_server before do_editor, see + commit.c */ start_server (); } #endif @@ -186,13 +215,14 @@ import (argc, argv) if (use_editor) { do_editor ((char *) NULL, &message, repository, - (List *) NULL); + (List *) NULL); } - + do_verify (message, repository); msglen = message == NULL ? 0 : strlen (message); if (msglen == 0 || message[msglen - 1] != '\n') { char *nm = xmalloc (msglen + 2); + *nm = '\0'; if (message != NULL) { (void) strcpy (nm, message); @@ -207,17 +237,21 @@ import (argc, argv) { int err; - ign_setup (); - - if (use_file_modtime) - send_arg("-d"); - if (vbranch[0] != '\0') option_with_arg ("-b", vbranch); if (message) option_with_arg ("-m", message); if (keyword_opt != NULL) option_with_arg ("-k", keyword_opt); + /* The only ignore processing which takes place on the server side + is the CVSROOT/cvsignore file. But if the user specified -I !, + the documented behavior is to not process said file. */ + if (ign_inhibit_server) + { + send_arg ("-I"); + send_arg ("!"); + } + wrap_send (); { int i; @@ -229,8 +263,7 @@ import (argc, argv) client_import_setup (repository); err = import_descend (message, argv[1], argc - 2, argv + 2); client_import_done (); - if (fprintf (to_server, "import\n") < 0) - error (1, errno, "writing to server"); + send_to_server ("import\012", 0); err += get_responses_and_close (); return err; } @@ -240,13 +273,18 @@ import (argc, argv) * Make all newly created directories writable. Should really use a more * sophisticated security mechanism here. */ - (void) umask (2); + (void) umask (cvsumask); make_directories (repository); /* Create the logfile that will be logged upon completion */ - if ((logfp = fopen (tmpnam (tmpfile), "w+")) == NULL) + tmpfile = cvs_temp_name (); + if ((logfp = CVS_FOPEN (tmpfile, "w+")) == NULL) error (1, errno, "cannot create temporary file `%s'", tmpfile); - (void) unlink (tmpfile); /* to be sure it goes away */ + /* On systems where we can unlink an open file, do so, so it will go + away no matter how we exit. FIXME-maybe: Should be checking for + errors but I'm not sure which error(s) we get if we are on a system + where one can't unlink open files. */ + (void) CVS_UNLINK (tmpfile); (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]); (void) fprintf (logfp, "Release Tags:\t"); for (i = 2; i < argc; i++) @@ -259,24 +297,57 @@ import (argc, argv) { 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]); + char buf[20]; + char *buf2; + + cvs_output_tagged ("+importmergecmd", NULL); + cvs_output_tagged ("newline", NULL); + sprintf (buf, "%d", conflicts); + cvs_output_tagged ("conflicts", buf); + cvs_output_tagged ("text", " conflicts created by this import."); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("text", + "Use the following command to help the merge:"); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("text", "\t"); + cvs_output_tagged ("text", program_name); + if (CVSroot_cmdline != NULL) + { + cvs_output_tagged ("text", " -d "); + cvs_output_tagged ("text", CVSroot_cmdline); + } + cvs_output_tagged ("text", " checkout -j"); + buf2 = xmalloc (strlen (argv[1]) + 20); + sprintf (buf2, "%s:yesterday", argv[1]); + cvs_output_tagged ("mergetag1", buf2); + free (buf2); + cvs_output_tagged ("text", " -j"); + cvs_output_tagged ("mergetag2", argv[1]); + cvs_output_tagged ("text", " "); + cvs_output_tagged ("repository", argv[0]); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("newline", NULL); + cvs_output_tagged ("-importmergecmd", NULL); } + /* FIXME: I'm not sure whether we need to put this information + into the loginfo. If we do, then note that it does not + report any required -d option. There is no particularly + clean way to tell the server about the -d option used by + the client. */ (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]); + (void) fprintf (logfp, "\t%s checkout ", program_name); + (void) fprintf (logfp, "-j%s:yesterday -j%s %s\n\n", + argv[1], argv[1], argv[0]); } else { if (!really_quiet) - (void) printf ("\nNo conflicts created by this import\n\n"); + cvs_output ("\nNo conflicts created by this import\n\n", 0); (void) fprintf (logfp, "\nNo conflicts created by this import\n\n"); } @@ -288,25 +359,35 @@ import (argc, argv) p->type = UPDATE; p->delproc = update_delproc; p->key = xstrdup ("- Imported sources"); - p->data = (char *) T_TITLE; + li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); + li->type = T_TITLE; + li->tag = xstrdup (vbranch); + li->rev_old = li->rev_new = NULL; + p->data = (char *) li; (void) addnode (ulist, p); - Update_Logfile (repository, message, vbranch, logfp, ulist); + Update_Logfile (repository, message, logfp, ulist); dellist (&ulist); - (void) fclose (logfp); + if (fclose (logfp) < 0) + error (0, errno, "error closing %s", tmpfile); /* Make sure the temporary file goes away, even on systems that don't let you delete a file that's in use. */ - unlink (tmpfile); + if (CVS_UNLINK (tmpfile) < 0 && !existence_error (errno)) + error (0, errno, "cannot remove %s", tmpfile); + free (tmpfile); if (message) free (message); + free (repository); + free (vbranch); + free (vhead); return (err); } -/* - * process all the files in ".", then descend into other directories. - */ +/* Process all the files in ".", then descend into other directories. + Returns 0 for success, or >0 on error (in which case a message + will have been printed). */ static int import_descend (message, vtag, targc, targv) char *message; @@ -323,28 +404,29 @@ import_descend (message, vtag, targc, targv) ign_add_file (CVSDOTIGNORE, 1); wrap_add_file (CVSDOTWRAPPER, 1); - if ((dirp = opendir (".")) == NULL) + if ((dirp = CVS_OPENDIR (".")) == NULL) { + error (0, errno, "cannot open directory"); err++; } else { + errno = 0; while ((dp = readdir (dirp)) != NULL) { if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0) - continue; - if (ign_name (dp->d_name)) - { + goto one_more_time_boys; #ifdef SERVER_SUPPORT - /* CVS directories are created by server.c because it doesn't - special-case import. So don't print a message about them. - Do print a message about other ignored files (although - most of these will get ignored on the client side). */ - if (server_active && strcmp (dp->d_name, CVSADM) == 0) - continue; + /* CVS directories are created in the temp directory by + server.c because it doesn't special-case import. So + don't print a message about them, regardless of -I!. */ + if (server_active && strcmp (dp->d_name, CVSADM) == 0) + goto one_more_time_boys; #endif + if (ign_name (dp->d_name)) + { add_log ('I', dp->d_name); - continue; + goto one_more_time_boys; } if ( @@ -356,7 +438,7 @@ import_descend (message, vtag, targc, targv) #endif && !wrap_name_has (dp->d_name, WRAP_TOCVS) ) - { + { Node *n; if (dirlist == NULL) @@ -368,7 +450,7 @@ import_descend (message, vtag, targc, targv) } else if ( #ifdef DT_DIR - dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && + dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && #endif islink (dp->d_name)) { @@ -380,18 +462,28 @@ import_descend (message, vtag, targc, targv) #ifdef CLIENT_SUPPORT if (client_active) err += client_process_import_file (message, dp->d_name, - vtag, targc, targv, - repository); + vtag, targc, targv, + repository, + keyword_opt != NULL && + keyword_opt[0] == 'b', + use_file_modtime); else #endif err += process_import_file (message, dp->d_name, vtag, targc, targv); } + one_more_time_boys: + errno = 0; + } + if (errno != 0) + { + error (0, errno, "cannot read directory"); + ++err; } (void) closedir (dirp); } - if (dirlist != NULL) + if (dirlist != NULL) { Node *head, *p; @@ -418,28 +510,76 @@ process_import_file (message, vfile, vtag, targc, targv) int targc; char *targv[]; { - char attic_name[PATH_MAX]; - char rcs[PATH_MAX]; + char *rcs; int inattic = 0; + rcs = xmalloc (strlen (repository) + strlen (vfile) + sizeof (RCSEXT) + + 5); (void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT); if (!isfile (rcs)) { + char *attic_name; + + attic_name = xmalloc (strlen (repository) + strlen (vfile) + + sizeof (CVSATTIC) + sizeof (RCSEXT) + 10); (void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC, vfile, RCSEXT); if (!isfile (attic_name)) { + int retval; + char *free_opt = NULL; + char *our_opt = keyword_opt; + free (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)); + +#ifdef SERVER_SUPPORT + /* The most reliable information on whether the file is binary + is what the client told us. That is because if the client had + the wrong idea about binaryness, it corrupted the file, so + we might as well believe the client. */ + if (server_active) + { + Node *node; + List *entries; + + /* Reading all the entries for each file is fairly silly, and + probably slow. But I am too lazy at the moment to do + anything else. */ + entries = Entries_Open (0, NULL); + node = findnode_fn (entries, vfile); + if (node != NULL) + { + Entnode *entdata = (Entnode *) node->data; + if (entdata->type == ENT_FILE) + { + assert (entdata->options[0] == '-' + && entdata->options[1] == 'k'); + our_opt = xstrdup (entdata->options + 2); + free_opt = our_opt; + } + } + Entries_Close (entries); + } +#endif + + retval = add_rcs_file (message, rcs, vfile, vhead, our_opt, + vbranch, vtag, targc, targv, + NULL, 0, logfp); + if (free_opt != NULL) + free (free_opt); + free (rcs); + return retval; } + free (attic_name); inattic = 1; } + free (rcs); /* * an rcs file exists. have to do things the official, slow, way. */ @@ -461,28 +601,23 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) { Vers_TS *vers; int letter; - int ierrno; - char *tmpdir; char *tocvsPath; - - vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile, - 1, 0, (List *) NULL, (List *) NULL); -#ifdef DEATH_SUPPORT + struct file_info finfo; + + memset (&finfo, 0, sizeof finfo); + finfo.file = vfile; + /* Not used, so don't worry about it. */ + finfo.update_dir = NULL; + finfo.fullname = finfo.file; + finfo.repository = repository; + finfo.entries = NULL; + finfo.rcs = NULL; + vers = Version_TS (&finfo, (char *) NULL, vbranch, (char *) NULL, + 1, 0); if (vers->vn_rcs != NULL && !RCS_isdead(vers->srcfile, vers->vn_rcs)) -#else - if (vers->vn_rcs != NULL) -#endif { - char xtmpfile[PATH_MAX]; int different; - int retcode = 0; - - tmpdir = getenv ("TMPDIR"); - if (tmpdir == NULL || tmpdir[0] == '\0') - tmpdir = "/tmp"; - - (void) sprintf (xtmpfile, "%s/cvs-imp%d", tmpdir, getpid()); /* * The rcs file does have a revision on the vendor branch. Compare @@ -493,33 +628,14 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) * 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); - } - tocvsPath = wrap_tocvs_process_file (vfile); - different = xcmp (xtmpfile, vfile); + /* FIXME: Why don't we pass tocvsPath to RCS_cmp_file if it is + not NULL? */ + different = RCS_cmp_file (vers->srcfile, vers->vn_rcs, "-ko", vfile); if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); - (void) unlink_file (xtmpfile); if (!different) { int retval = 0; @@ -529,7 +645,7 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) * "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)) + if (add_tags (vers->srcfile, vfile, vtag, targc, targv)) retval = 1; add_log ('U', vfile); freevers_ts (&vers); @@ -539,8 +655,8 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) /* 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)) + add_rev (message, vers->srcfile, vfile, vers->vn_rcs) || + add_tags (vers->srcfile, vfile, vtag, targc, targv)) { freevers_ts (&vers); return (1); @@ -566,13 +682,12 @@ update_rcs_file (message, vfile, vtag, targc, targv, inattic) static int add_rev (message, rcs, vfile, vers) char *message; - char *rcs; + RCSNode *rcs; char *vfile; char *vers; { int locked, status, ierrno; char *tocvsPath; - struct stat vfile_stat; if (noexec) return (0); @@ -583,50 +698,39 @@ add_rev (message, rcs, vfile, vers) /* Before RCS_lock existed, we were directing stdout, as well as stderr, from the RCS command, to DEVNULL. I wouldn't guess that was necessary, but I don't know for sure. */ - if (RCS_lock (rcs, vbranch, 1) != 0) - { - error (0, errno, "fork failed"); - return (1); - } + /* Earlier versions of this function printed a `fork failed' error + when RCS_lock returned an error code. That's not appropriate + now that RCS_lock is librarified, but should the error text be + preserved? */ + if (RCS_lock (rcs, vbranch, 1) != 0) + return 1; locked = 1; + RCS_rewrite (rcs, NULL, NULL); } tocvsPath = wrap_tocvs_process_file (vfile); - /* We used to deposit the revision with -r; RCS would delete the - working file, but we'd keep a hard link to it, and rename it - back after running RCS (ooh, atomicity). However, that - strategy doesn't work on operating systems without hard links - (like Windows NT). Instead, let's deposit it using -u, and - restore its permission bits afterwards. This also means the - file always exists under its own name. */ - if (! tocvsPath) - stat (vfile, &vfile_stat); - - run_setup ("%s%s -q -f %s%s", Rcsbin, RCS_CI, - (tocvsPath ? "-r" : "-u"), - vbranch); - run_args ("-m%s", make_message_rcslegal (message)); - if (use_file_modtime) - run_arg ("-d"); - run_arg (tocvsPath == NULL ? vfile : tocvsPath); - run_arg (rcs); - status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath, + message, vbranch, + (RCS_FLAGS_QUIET | RCS_FLAGS_KEEPFILE + | (use_file_modtime ? RCS_FLAGS_MODTIME : 0))); ierrno = errno; - /* Restore the permissions on vfile. */ - if (! tocvsPath) - chmod (vfile, vfile_stat.st_mode); + if ((tocvsPath != NULL) && (unlink_file_dir (tocvsPath) < 0)) + error (0, errno, "cannot remove %s", tocvsPath); 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); + fperror (logfp, 0, status == -1 ? ierrno : 0, + "ERROR: Check-in of %s failed", rcs->path); + error (0, status == -1 ? ierrno : 0, + "ERROR: Check-in of %s failed", rcs->path); } if (locked) { (void) RCS_unlock(rcs, vbranch, 0); + RCS_rewrite (rcs, NULL, NULL); } return (1); } @@ -641,7 +745,7 @@ add_rev (message, rcs, vfile, vers) */ static int add_tags (rcs, vfile, vtag, targc, targv) - char *rcs; + RCSNode *rcs; char *vfile; char *vtag; int targc; @@ -650,6 +754,7 @@ add_tags (rcs, vfile, vtag, targc, targv) int i, ierrno; Vers_TS *vers; int retcode = 0; + struct file_info finfo; if (noexec) return (0); @@ -657,23 +762,36 @@ add_tags (rcs, vfile, vtag, targc, targv) if ((retcode = RCS_settag(rcs, vtag, vbranch)) != 0) { ierrno = errno; - fperror (logfp, 0, retcode == -1 ? ierrno : 0, - "ERROR: Failed to set tag %s in %s", vtag, rcs); + fperror (logfp, 0, retcode == -1 ? ierrno : 0, + "ERROR: Failed to set tag %s in %s", vtag, rcs->path); error (0, retcode == -1 ? ierrno : 0, - "ERROR: Failed to set tag %s in %s", vtag, rcs); + "ERROR: Failed to set tag %s in %s", vtag, rcs->path); return (1); } - vers = Version_TS (repository, (char *) NULL, vtag, (char *) NULL, vfile, - 1, 0, (List *) NULL, (List *) NULL); + RCS_rewrite (rcs, NULL, NULL); + + memset (&finfo, 0, sizeof finfo); + finfo.file = vfile; + /* Not used, so don't worry about it. */ + finfo.update_dir = NULL; + finfo.fullname = finfo.file; + finfo.repository = repository; + finfo.entries = NULL; + finfo.rcs = NULL; + vers = Version_TS (&finfo, NULL, vtag, NULL, 1, 0); for (i = 0; i < targc; i++) { - if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) != 0) + if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) == 0) + RCS_rewrite (rcs, NULL, NULL); + else { ierrno = errno; - fperror (logfp, 0, retcode == -1 ? ierrno : 0, - "WARNING: Couldn't add tag %s to %s", targv[i], rcs); + fperror (logfp, 0, retcode == -1 ? ierrno : 0, + "WARNING: Couldn't add tag %s to %s", targv[i], + rcs->path); error (0, retcode == -1 ? ierrno : 0, - "WARNING: Couldn't add tag %s to %s", targv[i], rcs); + "WARNING: Couldn't add tag %s to %s", targv[i], + rcs->path); } } freevers_ts (&vers); @@ -697,12 +815,17 @@ static const struct compair comtable[] = * 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. + * + * I believe that the comment leader is unused if you are using RCS 5.7, which + * decides what leader to use based on the text surrounding the $Log keyword + * rather than a specified comment leader. */ {"a", "-- "}, /* Ada */ {"ada", "-- "}, {"adb", "-- "}, {"asm", ";; "}, /* assembler (MS-DOS) */ {"ads", "-- "}, /* Ada */ + {"bas", "' "}, /* Visual Basic code */ {"bat", ":: "}, /* batch (MS-DOS) */ {"body", "-- "}, /* Ada */ {"c", " * "}, /* C */ @@ -716,12 +839,14 @@ static const struct compair comtable[] = {"cmf", "c "}, /* CM Fortran */ {"cs", " * "}, /* C* */ {"csh", "# "}, /* shell */ + {"dlg", " * "}, /* MS Windows dialog file */ {"e", "# "}, /* efl */ {"epsf", "% "}, /* encapsulated postscript */ {"epsi", "% "}, /* encapsulated postscript */ {"el", "; "}, /* Emacs Lisp */ {"f", "c "}, /* Fortran */ {"for", "c "}, + {"frm", "' "}, /* Visual Basic form */ {"h", " * "}, /* C-header */ {"hh", "// "}, /* C++ header */ {"hpp", "// "}, @@ -731,6 +856,7 @@ static const struct compair comtable[] = * franzlisp) */ {"mac", ";; "}, /* macro (DEC-10, MS-DOS, PDP-11, * VMS, etc) */ + {"mak", "# "}, /* makefile, e.g. Visual C++ */ {"me", ".\\\" "}, /* me-macros t/nroff */ {"ml", "; "}, /* mocklisp */ {"mm", ".\\\" "}, /* mm-macros t/nroff */ @@ -752,17 +878,18 @@ static const struct compair comtable[] = {"psw", "% "}, /* postscript wrap */ {"pswm", "% "}, /* postscript wrap */ {"r", "# "}, /* ratfor */ + {"rc", " * "}, /* Microsoft Windows resource file */ {"red", "% "}, /* psl/rlisp */ -#ifdef sparc +#ifdef __sparc__ {"s", "! "}, /* assembler */ #endif -#ifdef mc68000 +#ifdef __mc68000__ {"s", "| "}, /* assembler */ #endif -#ifdef pdp11 +#ifdef __pdp11__ {"s", "/ "}, /* assembler */ #endif -#ifdef vax +#ifdef __vax__ {"s", "# "}, /* assembler */ #endif #ifdef __ksr__ @@ -776,9 +903,6 @@ static const struct compair comtable[] = {"y", " * "}, /* yacc */ {"ye", " * "}, /* yacc-efl */ {"yr", " * "}, /* yacc-ratfor */ -#ifdef SYSTEM_COMMENT_TABLE - SYSTEM_COMMENT_TABLE -#endif {"", "# "}, /* default for empty suffix */ {NULL, "# "} /* default for unknown suffix; */ /* must always be last */ @@ -789,9 +913,11 @@ get_comment (user) char *user; { char *cp, *suffix; - char suffix_path[PATH_MAX]; + char *suffix_path; int i; + char *retval; + suffix_path = xmalloc (strlen (user) + 5); cp = strrchr (user, '.'); if (cp != NULL) { @@ -803,7 +929,7 @@ get_comment (user) */ (void) strcpy (suffix_path, cp); for (cp = suffix_path; *cp; cp++) - if (isupper (*cp)) + if (isupper ((unsigned char) *cp)) *cp = tolower (*cp); suffix = suffix_path; } @@ -811,56 +937,145 @@ get_comment (user) suffix = ""; /* will use the default */ for (i = 0;; i++) { - if (comtable[i].suffix == NULL) /* default */ - return (comtable[i].comlead); + if (comtable[i].suffix == NULL) + { + /* Default. Note we'll always hit this case before we + ever return NULL. */ + retval = comtable[i].comlead; + break; + } if (strcmp (suffix, comtable[i].suffix) == 0) - return (comtable[i].comlead); + { + retval = comtable[i].comlead; + break; + } } + free (suffix_path); + return retval; } -static int -add_rcs_file (message, rcs, user, vtag, targc, targv) +/* Create a new RCS file from scratch. + + This probably should be moved to rcs.c now that it is called from + places outside import.c. + + Return value is 0 for success, or nonzero for failure (in which + case an error message will have already been printed). */ +int +add_rcs_file (message, rcs, user, add_vhead, key_opt, + add_vbranch, vtag, targc, targv, + desctext, desclen, add_logfp) + /* Log message for the addition. Not used if add_vhead == NULL. */ char *message; + /* Filename of the RCS file to create. */ char *rcs; + /* Filename of the file to serve as the contents of the initial + revision. Even if add_vhead is NULL, we use this to determine + the modes to give the new RCS file. */ char *user; + + /* Revision number of head that we are adding. Normally 1.1 but + could be another revision as long as ADD_VBRANCH is a branch + from it. If NULL, then just add an empty file without any + revisions (similar to the one created by "rcs -i"). */ + char *add_vhead; + + /* Keyword expansion mode, e.g., "b" for binary. NULL means the + default behavior. */ + char *key_opt; + + /* Vendor branch to import to, or NULL if none. If non-NULL, then + vtag should also be non-NULL. */ + char *add_vbranch; char *vtag; int targc; char *targv[]; + + /* If non-NULL, description for the file. If NULL, the description + will be empty. */ + char *desctext; + size_t desclen; + + /* Write errors to here as well as via error (), or NULL if we should + use only error (). */ + FILE *add_logfp; { FILE *fprcs, *fpuser; struct stat sb; struct tm *ftm; time_t now; - char altdate1[50]; -#ifndef HAVE_RCS5 - char altdate2[50]; -#endif - char *author, *buf; + char altdate1[MAXDATELEN]; + char *author; int i, ierrno, err = 0; mode_t mode; char *tocvsPath; char *userfile; + char *local_opt = key_opt; + char *free_opt = NULL; + mode_t file_type; if (noexec) return (0); -#ifdef LINES_CRLF_TERMINATED - /* There exits a port of RCS to such a system that stores files with - straight newlines. If we ever reach this point on such a system, - we'll need to decide what to do with the open_file call below. */ - abort (); -#endif + /* Note that as the code stands now, the -k option overrides any + settings in wrappers (whether CVSROOT/cvswrappers, -W, or + whatever). Some have suggested this should be the other way + around. As far as I know the documentation doesn't say one way + or the other. Before making a change of this sort, should think + about what is best, document it (in cvs.texinfo and NEWS), &c. */ + + if (local_opt == NULL) + { + if (wrap_name_has (user, WRAP_RCSOPTION)) + { + local_opt = free_opt = wrap_rcsoption (user, 0); + } + } + tocvsPath = wrap_tocvs_process_file (user); userfile = (tocvsPath == NULL ? user : tocvsPath); - fpuser = fopen (userfile, "r"); - if (fpuser == NULL) { - /* not fatal, continue import */ - fperror (logfp, 0, errno, "ERROR: cannot read file %s", userfile); - error (0, errno, "ERROR: cannot read file %s", userfile); - goto read_error; + + /* Opening in text mode is probably never the right thing for the + server (because the protocol encodes text files in a fashion + which does not depend on what the client or server OS is, as + documented in cvsclient.texi), but as long as the server just + runs on unix it is a moot point. */ + + /* If PreservePermissions is set, then make sure that the file + is a plain file before trying to open it. Longstanding (although + often unpopular) CVS behavior has been to follow symlinks, so we + maintain that behavior if PreservePermissions is not on. + + NOTE: this error message used to be `cannot fstat', but is now + `cannot lstat'. I don't see a way around this, since we must + stat the file before opening it. -twp */ + + if (CVS_LSTAT (userfile, &sb) < 0) + error (1, errno, "cannot lstat %s", user); + file_type = sb.st_mode & S_IFMT; + + fpuser = NULL; + if (!preserve_perms || file_type == S_IFREG) + { + fpuser = CVS_FOPEN (userfile, + ((local_opt != NULL && strcmp (local_opt, "b") == 0) + ? "rb" + : "r") + ); + if (fpuser == NULL) + { + /* not fatal, continue import */ + if (add_logfp != NULL) + fperror (add_logfp, 0, errno, + "ERROR: cannot read file %s", userfile); + error (0, errno, "ERROR: cannot read file %s", userfile); + goto read_error; + } } - fprcs = fopen (rcs, "w+"); - if (fprcs == NULL) { + + fprcs = CVS_FOPEN (rcs, "w+b"); + if (fprcs == NULL) + { ierrno = errno; goto write_error_noclose; } @@ -868,156 +1083,325 @@ add_rcs_file (message, rcs, user, vtag, targc, targv) /* * putadmin() */ - if (fprintf (fprcs, "head %s;\n", vhead) < 0 || - fprintf (fprcs, "branch %s;\n", vbranch) < 0 || - fprintf (fprcs, "access ;\n") < 0 || + if (add_vhead != NULL) + { + if (fprintf (fprcs, "head %s;\012", add_vhead) < 0) + goto write_error; + } + else + { + if (fprintf (fprcs, "head ;\012") < 0) + goto write_error; + } + + if (add_vbranch != NULL) + { + if (fprintf (fprcs, "branch %s;\012", add_vbranch) < 0) + goto write_error; + } + if (fprintf (fprcs, "access ;\012") < 0 || fprintf (fprcs, "symbols ") < 0) { goto write_error; } - for (i = targc - 1; i >= 0; i--) /* RCS writes the symbols backwards */ - if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) < 0) + for (i = targc - 1; i >= 0; i--) + { + /* RCS writes the symbols backwards */ + assert (add_vbranch != NULL); + if (fprintf (fprcs, "%s:%s.1 ", targv[i], add_vbranch) < 0) + goto write_error; + } + + if (add_vbranch != NULL) + { + if (fprintf (fprcs, "%s:%s", vtag, add_vbranch) < 0) goto write_error; + } + if (fprintf (fprcs, ";\012") < 0) + goto write_error; - if (fprintf (fprcs, "%s:%s;\n", vtag, vbranch) < 0 || - fprintf (fprcs, "locks ; strict;\n") < 0 || + if (fprintf (fprcs, "locks ; strict;\012") < 0 || /* XXX - make sure @@ processing works in the RCS file */ - fprintf (fprcs, "comment @%s@;\n", get_comment (user)) < 0) + fprintf (fprcs, "comment @%s@;\012", get_comment (user)) < 0) { goto write_error; } - if (keyword_opt != NULL) - if (fprintf (fprcs, "expand @%s@;\n", keyword_opt) < 0) + if (local_opt != NULL) + { + if (fprintf (fprcs, "expand @%s@;\012", local_opt) < 0) { - goto write_error; + goto write_error; } + } - if (fprintf (fprcs, "\n") < 0) + if (fprintf (fprcs, "\012") < 0) goto write_error; - /* - * puttree() - */ - if (fstat (fileno (fpuser), &sb) < 0) - error (1, errno, "cannot fstat %s", user); - if (use_file_modtime) - now = sb.st_mtime; - else - (void) time (&now); -#ifdef HAVE_RCS5 - ftm = gmtime (&now); -#else - ftm = localtime (&now); + /* Write the revision(s), with the date and author and so on + (that is "delta" rather than "deltatext" from rcsfile(5)). */ + if (add_vhead != NULL) + { + if (use_file_modtime) + now = sb.st_mtime; + else + (void) time (&now); + ftm = gmtime (&now); + (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); + author = getcaller (); + + if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 || + fprintf (fprcs, "date %s; author %s; state Exp;\012", + altdate1, author) < 0) + goto write_error; + + if (fprintf (fprcs, "branches") < 0) + goto write_error; + if (add_vbranch != NULL) + { + if (fprintf (fprcs, " %s.1", add_vbranch) < 0) + goto write_error; + } + if (fprintf (fprcs, ";\012") < 0) + goto write_error; + + if (fprintf (fprcs, "next ;\012") < 0) + goto write_error; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Store initial permissions if necessary. */ + if (preserve_perms) + { + if (file_type == S_IFLNK) + { + char *link = xreadlink (userfile); + if (fprintf (fprcs, "symlink\t@") < 0 || + expand_at_signs (link, strlen (link), fprcs) < 0 || + fprintf (fprcs, "@;\012") < 0) + goto write_error; + free (link); + } + else + { + if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0) + goto write_error; + if (fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0) + goto write_error; + if (fprintf (fprcs, "permissions\t%o;\012", + sb.st_mode & 07777) < 0) + goto write_error; + switch (file_type) + { + case S_IFREG: break; + case S_IFCHR: + case S_IFBLK: + if (fprintf (fprcs, "special\t%s %lu;\012", + (file_type == S_IFCHR + ? "character" + : "block"), + (unsigned long) sb.st_rdev) < 0) + goto write_error; + break; + default: + error (0, 0, + "can't import %s: unknown kind of special file", + userfile); + } + } + } #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); -#ifdef HAVE_RCS5 -#define altdate2 altdate1 -#else - /* - * If you don't have RCS V5 or later, you need to lie about the ci - * time, since RCS V4 and earlier insist that the times differ. - */ - now++; - ftm = localtime (&now); - (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); + + if (add_vbranch != NULL) + { + if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 || + fprintf (fprcs, "date %s; author %s; state Exp;\012", + altdate1, author) < 0 || + fprintf (fprcs, "branches ;\012") < 0 || + fprintf (fprcs, "next ;\012") < 0) + goto write_error; + +#ifdef PRESERVE_PERMISSIONS_SUPPORT + /* Store initial permissions if necessary. */ + if (preserve_perms) + { + if (file_type == S_IFLNK) + { + char *link = xreadlink (userfile); + if (fprintf (fprcs, "symlink\t@") < 0 || + expand_at_signs (link, strlen (link), fprcs) < 0 || + fprintf (fprcs, "@;\012") < 0) + goto write_error; + free (link); + } + else + { + if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0 || + fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0 || + fprintf (fprcs, "permissions\t%o;\012", + sb.st_mode & 07777) < 0) + goto write_error; + + switch (file_type) + { + case S_IFREG: break; + case S_IFCHR: + case S_IFBLK: + if (fprintf (fprcs, "special\t%s %lu;\012", + (file_type == S_IFCHR + ? "character" + : "block"), + (unsigned long) sb.st_rdev) < 0) + goto write_error; + break; + default: + error (0, 0, + "cannot import %s: special file of unknown type", + userfile); + } + } + } #endif - author = getcaller (); - - if (fprintf (fprcs, "\n%s\n", vhead) < 0 || - fprintf (fprcs, "date %s; author %s; state Exp;\n", - altdate1, author) < 0 || - fprintf (fprcs, "branches %s.1;\n", vbranch) < 0 || - fprintf (fprcs, "next ;\n") < 0 || - fprintf (fprcs, "\n%s.1\n", vbranch) < 0 || - fprintf (fprcs, "date %s; author %s; state Exp;\n", - altdate2, author) < 0 || - fprintf (fprcs, "branches ;\n") < 0 || - fprintf (fprcs, "next ;\n\n") < 0 || - /* - * putdesc() - */ - fprintf (fprcs, "\ndesc\n") < 0 || - fprintf (fprcs, "@@\n\n\n") < 0 || - /* - * putdelta() - */ - fprintf (fprcs, "\n%s\n", vhead) < 0 || - fprintf (fprcs, "log\n") < 0 || - fprintf (fprcs, "@Initial revision\n@\n") < 0 || - fprintf (fprcs, "text\n@") < 0) - { + + if (fprintf (fprcs, "\012") < 0) + goto write_error; + } + } + + /* Now write the description (possibly empty). */ + if (fprintf (fprcs, "\012desc\012") < 0 || + fprintf (fprcs, "@") < 0) goto write_error; + if (desctext != NULL) + { + /* The use of off_t not size_t for the second argument is very + strange, since we are dealing with something which definitely + fits in memory. */ + if (expand_at_signs (desctext, (off_t) desclen, fprcs) < 0) + goto write_error; } + if (fprintf (fprcs, "@\012\012\012") < 0) + goto write_error; - if (sb.st_size > 0) + /* Now write the log messages and contents for the revision(s) (that + is, "deltatext" rather than "delta" from rcsfile(5)). */ + if (add_vhead != NULL) { - off_t size; + if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 || + fprintf (fprcs, "log\012@") < 0) + goto write_error; + if (add_vbranch != NULL) + { + /* We are going to put the log message in the revision on the + branch. So putting it here too seems kind of redundant, I + guess (and that is what CVS has always done, anyway). */ + if (fprintf (fprcs, "Initial revision\012") < 0) + goto write_error; + } + else + { + if (expand_at_signs (message, (off_t) strlen (message), fprcs) < 0) + goto write_error; + } + if (fprintf (fprcs, "@\012") < 0 || + fprintf (fprcs, "text\012@") < 0) + { + goto write_error; + } - 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) < 0) + /* Now copy over the contents of the file, expanding at signs. + If preserve_perms is set, do this only for regular files. */ + if (!preserve_perms || file_type == S_IFREG) { - free (buf); + char buf[8192]; + unsigned int len; + + while (1) + { + len = fread (buf, 1, sizeof buf, fpuser); + if (len == 0) + { + if (ferror (fpuser)) + error (1, errno, "cannot read file %s for copying", + user); + break; + } + if (expand_at_signs (buf, len, fprcs) < 0) + goto write_error; + } + } + if (fprintf (fprcs, "@\012\012") < 0) goto write_error; + if (add_vbranch != NULL) + { + if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 || + fprintf (fprcs, "log\012@") < 0 || + expand_at_signs (message, + (off_t) strlen (message), fprcs) < 0 || + fprintf (fprcs, "@\012text\012") < 0 || + fprintf (fprcs, "@@\012") < 0) + goto write_error; } - free (buf); - } - if (fprintf (fprcs, "@\n\n") < 0 || - fprintf (fprcs, "\n%s.1\n", vbranch) < 0 || - fprintf (fprcs, "log\n@") < 0 || - expand_at_signs (message, (off_t) strlen (message), fprcs) < 0 || - fprintf (fprcs, "@\ntext\n") < 0 || - fprintf (fprcs, "@@\n") < 0) - { - goto write_error; } + if (fclose (fprcs) == EOF) { ierrno = errno; goto write_error_noclose; } - (void) fclose (fpuser); + /* Close fpuser only if we opened it to begin with. */ + if (fpuser != NULL) + { + if (fclose (fpuser) < 0) + error (0, errno, "cannot close %s", user); + } /* - * 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 + * Fix the modes on the RCS files. The user modes of the original + * user file are propagated to the group and other modes as allowed + * by the repository umask, except that all write permissions are * turned off. */ - mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH); + mode = (sb.st_mode | + (sb.st_mode & S_IRWXU) >> 3 | + (sb.st_mode & S_IRWXU) >> 6) & + ~cvsumask & + ~(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); + if (add_logfp != NULL) + fperror (add_logfp, 0, ierrno, + "WARNING: cannot change mode of file %s", rcs); error (0, ierrno, "WARNING: cannot change mode of file %s", rcs); err++; } if (tocvsPath) if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); + if (free_opt != NULL) + free (free_opt); return (err); write_error: ierrno = errno; - (void) fclose (fprcs); + if (fclose (fprcs) < 0) + error (0, errno, "cannot close %s", rcs); write_error_noclose: - (void) fclose (fpuser); - fperror (logfp, 0, ierrno, "ERROR: cannot write file %s", rcs); + if (fclose (fpuser) < 0) + error (0, errno, "cannot close %s", user); + if (add_logfp != NULL) + fperror (add_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"); + if (CVS_UNLINK (rcs) < 0) + error (0, errno, "cannot remove %s", rcs); + if (add_logfp != NULL) + fperror (add_logfp, 0, 0, "ERROR: out of space - aborting"); error (1, 0, "ERROR: out of space - aborting"); } read_error: @@ -1025,6 +1409,9 @@ read_error: if (unlink_file_dir (tocvsPath) < 0) error (0, errno, "cannot remove %s", tocvsPath); + if (free_opt != NULL) + free (free_opt); + return (err + 1); } @@ -1033,25 +1420,33 @@ read_error: * signs. If an error occurs, return a negative value and set errno * to indicate the error. If not, return a nonnegative value. */ -static int +int expand_at_signs (buf, size, fp) char *buf; off_t size; FILE *fp; { - char *cp, *end; + register char *cp, *next; - for (cp = buf, end = buf + size; cp < end; cp++) + cp = buf; + while ((next = memchr (cp, '@', size)) != NULL) { - if (*cp == '@') - { - if (putc ('@', fp) == EOF) - return EOF; - } - if (putc (*cp, fp) == EOF) - return (EOF); + int len; + + ++next; + len = next - cp; + if (fwrite (cp, 1, len, fp) != len) + return EOF; + if (putc ('@', fp) == EOF) + return EOF; + cp = next; + size -= len; } - return (1); + + if (fwrite (cp, 1, size, fp) != size) + return EOF; + + return 1; } /* @@ -1064,12 +1459,22 @@ add_log (ch, fname) { if (!really_quiet) /* write to terminal */ { + char buf[2]; + buf[0] = ch; + buf[1] = ' '; + cvs_output (buf, 2); 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); + { + cvs_output (repository + repos_len + 1, 0); + cvs_output ("/", 1); + } + else if (repository[0] != '\0') + { + cvs_output (repository, 0); + cvs_output ("/", 1); + } + cvs_output (fname, 0); + cvs_output ("\n", 1); } if (repos_len) /* write to logfile */ @@ -1099,6 +1504,7 @@ import_descend_dir (message, dir, vtag, targc, targv) struct saved_cwd cwd; char *cp; int ierrno, err; + char *rcs = NULL; if (islink (dir)) return (0); @@ -1107,28 +1513,32 @@ import_descend_dir (message, dir, vtag, targc, targv) fperror (logfp, 0, 0, "ERROR: cannot get working directory"); return (1); } + + /* Concatenate DIR to the end of REPOSITORY. */ if (repository[0] == '\0') - (void) strcpy (repository, dir); + { + char *new = xstrdup (dir); + free (repository); + repository = new; + } else { - (void) strcat (repository, "/"); - (void) strcat (repository, dir); + char *new = xmalloc (strlen (repository) + strlen (dir) + 10); + strcpy (new, repository); + (void) strcat (new, "/"); + (void) strcat (new, dir); + free (repository); + repository = new; } + #ifdef CLIENT_SUPPORT if (!quiet && !client_active) #else if (!quiet) #endif -#ifdef SERVER_SUPPORT - /* Needs to go on stdout, not stderr, to avoid being interspersed - with the add_log messages. */ - printf ("%s %s: Importing %s\n", - program_name, command_name, repository); -#else error (0, 0, "Importing %s", repository); -#endif - if (chdir (dir) < 0) + if ( CVS_CHDIR (dir) < 0) { ierrno = errno; fperror (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository); @@ -1142,9 +1552,12 @@ import_descend_dir (message, dir, vtag, targc, targv) if (!isdir (repository)) #endif { - if (isfile (repository)) + rcs = xmalloc (strlen (repository) + sizeof (RCSEXT) + 5); + (void) sprintf (rcs, "%s%s", repository, RCSEXT); + if (isfile (repository) || isfile(rcs)) { - fperror (logfp, 0, 0, "ERROR: %s is a file, should be a directory!", + 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); @@ -1164,12 +1577,14 @@ import_descend_dir (message, dir, vtag, targc, targv) } err = import_descend (message, vtag, targc, targv); out: + if (rcs != NULL) + free (rcs); if ((cp = strrchr (repository, '/')) != NULL) *cp = '\0'; else repository[0] = '\0'; if (restore_cwd (&cwd, NULL)) - exit (1); + error_exit (); free_cwd (&cwd); return (err); } |