/* * 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; struct dir { struct dir *dir_next; char *dir_name; } *dir_list = NULL, *dir_last; /* 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)) { struct dir *dep = (struct dir *) xmalloc (sizeof (struct dir)); dep->dir_name = xstrdup(dp->d_name); dep->dir_next = NULL; if (dir_list) { dir_last->dir_next = dep; } else { dir_list = dep; } dir_last = dep; } 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); } while (dir_list) { err += import_descend_dir (message, dir_list->dir_name, vtag, targc, targv); dir_last = dir_list->dir_next; free (dir_list->dir_name); free (dir_list); dir_list = dir_last; } 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); }