/* ARGSUSED */ static int release_delete_dirleaveproc(void *callerdat, char *dir, int err, char *update_dir, List *entries) { if (strchr (dir, '/') == NULL) { /* FIXME: chdir ("..") loses with symlinks. */ /* Prune empty dirs on the way out - if necessary */ unlink_file_dir(CVSADM); (void) CVS_CHDIR (".."); if (force_delete || isemptydir (dir, 0)) { List *list; /* I'm not sure the existence_error is actually possible (except in cases where we really should print a message), but since this code used to ignore all errors, I'll play it safe. */ if (unlink_file_dir (dir) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s directory", dir); if(isdir(CVSADM)) { list = Entries_Open(0,NULL); Subdir_Deregister (list, (char *) NULL, dir); Entries_Close(list); } } } return (err); }
/* * Unlink a file or dir, if possible. If it is a directory do a deep * removal of all of the files in the directory. Return -1 on error * (in which case errno is set). */ int unlink_file_dir (const char *f) { struct stat sb; TRACE(1,"unlink_file_dir(%s)",PATCH_NULL(f)); if (noexec) return (0); /* For at least some unices, if root tries to unlink() a directory, instead of doing something rational like returning EISDIR, the system will gleefully go ahead and corrupt the filesystem. So we first call stat() to see if it is OK to call unlink(). This doesn't quite work--if someone creates a directory between the call to stat() and the call to unlink(), we'll still corrupt the filesystem. Where is the Unix Haters Handbook when you need it? */ if (stat (f, &sb) < 0) { if (existence_error (errno)) { /* The file or directory doesn't exist anyhow. */ return -1; } } else if (S_ISDIR (sb.st_mode)) return deep_remove_dir (f); return CVS_UNLINK (f); }
/* ARGSUSED */ DBM * mydbm_open (char *file, int flags, int mode) { FILE *fp; DBM *db; fp = CVS_FOPEN (file, (flags & O_ACCMODE) != O_RDONLY ? FOPEN_BINARY_READWRITE : FOPEN_BINARY_READ); if (fp == NULL && !(existence_error (errno) && (flags & O_CREAT))) return NULL; db = xmalloc (sizeof (*db)); db->dbm_list = getlist (); db->modified = 0; db->name = xstrdup (file); if (fp != NULL) { mydbm_load_file (fp, db->dbm_list, file); if (fclose (fp) < 0) error (0, errno, "cannot close %s", primary_root_inverse_translate (file)); } return db; }
/* * Unlink a file or dir, if possible. If it is a directory do a deep * removal of all of the files in the directory. Return -1 on error * (in which case errno is set). */ int unlink_file_dir (const char *f) { struct stat sb; /* This is called by the server parent process in contexts where it is not OK to send output (e.g. after we sent "ok" to the client). */ if (!server_active) TRACE (TRACE_FUNCTION, "unlink_file_dir(%s)", f); if (noexec) return 0; /* For at least some unices, if root tries to unlink() a directory, instead of doing something rational like returning EISDIR, the system will gleefully go ahead and corrupt the filesystem. So we first call stat() to see if it is OK to call unlink(). This doesn't quite work--if someone creates a directory between the call to stat() and the call to unlink(), we'll still corrupt the filesystem. Where is the Unix Haters Handbook when you need it? */ if (stat (f, &sb) < 0) { if (existence_error (errno)) { /* The file or directory doesn't exist anyhow. */ return -1; } } else if (S_ISDIR (sb.st_mode)) return deep_remove_dir (f); return CVS_UNLINK (f); }
/* * Open a file and read lines, feeding each line to a line parser. Arrange * for keeping a temporary list of wildcards at the end, if the "hold" * argument is set. */ void ign_add_file (char *file, int hold) { FILE *fp; char *line = NULL; size_t line_allocated = 0; /* restore the saved list (if any) */ if (s_ign_list != NULL) { int i; for (i = 0; i < s_ign_count; i++) ign_list[i] = s_ign_list[i]; ign_count = s_ign_count; ign_list[ign_count] = NULL; s_ign_count = 0; free (s_ign_list); s_ign_list = NULL; } /* is this a temporary ignore file? */ if (hold) { /* re-set if we had already done a temporary file */ if (ign_hold >= 0) { int i; for (i = ign_hold; i < ign_count; i++) free (ign_list[i]); ign_count = ign_hold; ign_list[ign_count] = NULL; } else { ign_hold = ign_count; } } /* load the file */ fp = CVS_FOPEN (file, "r"); if (fp == NULL) { if (! existence_error (errno)) error (0, errno, "cannot open %s", file); return; } while (getline (&line, &line_allocated, fp) >= 0) ign_add (line, hold); if (ferror (fp)) error (0, errno, "cannot read %s", file); if (fclose (fp) < 0) error (0, errno, "cannot close %s", file); free (line); }
static int get_table_ex(term_t handle, Table *table) { int64_t l; if ( PL_get_int64(handle, &l) ) { Table t = (Table)(intptr_t)l; if ( t->magic == TABLE_MAGIC ) { *table = t; return TRUE; } return existence_error(handle, "table"); } return type_error(handle, "table"); }
/* * Make a path to the argument directory, printing a message if something * goes wrong. */ void make_directories (const char *name) { char *cp; if (noexec) return; if (mkdir (name, 0777) == 0 || errno == EEXIST) return; if (! existence_error (errno)) { error (0, errno, "cannot make path to %s", name); return; } if ((cp = strrchr (name, '/')) == NULL) return; *cp = '\0'; make_directories (name); *cp++ = '/'; if (*cp == '\0') return; (void) mkdir (name, 0777); }
/* * Builds a temporary file using setup_tmpfile() and invokes the user's * editor on the file. The header garbage in the resultant file is then * stripped and the log message is stored in the "message" argument. * * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it * is NULL, use the CVSADM_TEMPLATE file instead. REPOSITORY should be * NULL when running in client mode. * * GLOBALS * Editor Set to a default value by configure and overridable using the * -e option to the CVS executable. */ void do_editor (const char *dir, char **messagep, const char *repository, List *changes) { static int reuse_log_message = 0; char *line; int line_length; size_t line_chars_allocated; char *fname; struct stat pre_stbuf, post_stbuf; int retcode = 0; assert (!current_parsed_root->isremote != !repository); if (noexec || reuse_log_message) return; /* Abort before creation of the temp file if no editor is defined. */ if (strcmp (Editor, "") == 0) error(1, 0, "no editor defined, must use -e or -m"); again: /* Create a temporary file. */ if( ( fp = cvs_temp_file( &fname ) ) == NULL ) error( 1, errno, "cannot create temporary file" ); if (*messagep) { (void) fputs (*messagep, fp); if ((*messagep)[0] == '\0' || (*messagep)[strlen (*messagep) - 1] != '\n') (void) fprintf (fp, "\n"); } else (void) fprintf (fp, "\n"); if (repository != NULL) /* tack templates on if necessary */ (void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, PIOPT_ALL, NULL); else { FILE *tfp; char buf[1024]; size_t n; size_t nwrite; /* Why "b"? */ tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb"); if (tfp == NULL) { if (!existence_error (errno)) error (1, errno, "cannot read %s", CVSADM_TEMPLATE); } else { while (!feof (tfp)) { char *p = buf; n = fread (buf, 1, sizeof buf, tfp); nwrite = n; while (nwrite > 0) { n = fwrite (p, 1, nwrite, fp); nwrite -= n; p += n; } if (ferror (tfp)) error (1, errno, "cannot read %s", CVSADM_TEMPLATE); } if (fclose (tfp) < 0) error (0, errno, "cannot close %s", CVSADM_TEMPLATE); } } (void) fprintf (fp, "%s----------------------------------------------------------------------\n", CVSEDITPREFIX); (void) fprintf (fp, "%sEnter Log. Lines beginning with `%.*s' are removed automatically\n%s\n", CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX, CVSEDITPREFIX); if (dir != NULL && *dir) (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX, dir, CVSEDITPREFIX); if (changes != NULL) setup_tmpfile (fp, CVSEDITPREFIX, changes); (void) fprintf (fp, "%s----------------------------------------------------------------------\n", CVSEDITPREFIX); /* finish off the temp file */ if (fclose (fp) == EOF) error (1, errno, "%s", fname); if (stat (fname, &pre_stbuf) == -1) pre_stbuf.st_mtime = 0; /* run the editor */ run_setup (Editor); run_add_arg (fname); if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL | RUN_SIGIGNORE | RUN_UNSETXID)) != 0) error (0, retcode == -1 ? errno : 0, "warning: editor session failed"); /* put the entire message back into the *messagep variable */ fp = xfopen (fname, "r"); if (*messagep) free (*messagep); if (stat (fname, &post_stbuf) != 0) error (1, errno, "cannot find size of temp file %s", fname); if (post_stbuf.st_size == 0) *messagep = NULL; else { /* On NT, we might read less than st_size bytes, but we won't read more. So this works. */ *messagep = (char *) xmalloc (post_stbuf.st_size + 1); (*messagep)[0] = '\0'; } line = NULL; line_chars_allocated = 0; if (*messagep) { size_t message_len = post_stbuf.st_size + 1; size_t offset = 0; while (1) { line_length = getline (&line, &line_chars_allocated, fp); if (line_length == -1) { if (ferror (fp)) error (0, errno, "warning: cannot read %s", fname); break; } if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0) continue; if (offset + line_length >= message_len) expand_string (messagep, &message_len, offset + line_length + 1); (void) strcpy (*messagep + offset, line); offset += line_length; } } if (fclose (fp) < 0) error (0, errno, "warning: cannot close %s", fname); /* canonicalize emply messages */ if (*messagep != NULL && (**messagep == '\0' || strcmp (*messagep, "\n") == 0)) { free (*messagep); *messagep = NULL; } if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL) { for (;;) { (void) printf ("\nLog message unchanged or not specified\n"); (void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n"); (void) printf ("Action: (abort) "); (void) fflush (stdout); line_length = getline (&line, &line_chars_allocated, stdin); if (line_length < 0) { error (0, errno, "cannot read from stdin"); if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); error (1, 0, "aborting"); } else if (line_length == 0 || *line == '\n' || *line == 'a' || *line == 'A') { if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); error (1, 0, "aborted by user"); } if (*line == 'c' || *line == 'C') break; if (*line == 'e' || *line == 'E') goto again; if (*line == '!') { reuse_log_message = 1; break; } (void) printf ("Unknown input\n"); } } if (line) free (line); if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); free (fname); }
static void time_stamp_server (const char *file, Vers_TS *vers_ts, Entnode *entdata) { struct stat sb; char *cp; TRACE (TRACE_FUNCTION, "time_stamp_server (%s, %s, %s, %s)", file, entdata && entdata->version ? entdata->version : "(null)", entdata && entdata->timestamp ? entdata->timestamp : "(null)", entdata && entdata->conflict ? entdata->conflict : "(null)"); if (lstat (file, &sb) < 0) { if (! existence_error (errno)) error (1, errno, "cannot stat temp file"); /* Missing file means lost or unmodified; check entries file to see which. XXX FIXME - If there's no entries file line, we wouldn't be getting the file at all, so consider it lost. I don't know that that's right, but it's not clear to me that either choice is. Besides, would we have an RCS string in that case anyways? */ if (entdata == NULL) mark_lost (vers_ts); else if (entdata->timestamp && entdata->timestamp[0] == '=' && entdata->timestamp[1] == '\0') mark_unchanged (vers_ts); else if (entdata->conflict && entdata->conflict[0] == '=') { /* These just need matching content. Might as well minimize it. */ vers_ts->ts_user = xstrdup (""); vers_ts->ts_conflict = xstrdup (""); } else if (entdata->timestamp && (entdata->timestamp[0] == 'M' || entdata->timestamp[0] == 'D') && entdata->timestamp[1] == '\0') vers_ts->ts_user = xstrdup ("Is-modified"); else mark_lost (vers_ts); } else if (sb.st_mtime == 0) { /* We shouldn't reach this case any more! */ abort (); } else { struct tm *tm_p; vers_ts->ts_user = xmalloc (25); /* We want to use the same timestamp format as is stored in the st_mtime. For unix (and NT I think) this *must* be universal time (UT), so that files don't appear to be modified merely because the timezone has changed. For VMS, or hopefully other systems where gmtime returns NULL, the modification time is stored in local time, and therefore it is not possible to cause st_mtime to be out of sync by changing the timezone. */ tm_p = gmtime (&sb.st_mtime); cp = tm_p ? asctime (tm_p) : ctime (&sb.st_mtime); cp[24] = 0; /* Fix non-standard format. */ if (cp[8] == '0') cp[8] = ' '; (void) strcpy (vers_ts->ts_user, cp); } }
List *Find_Names (const char *repository, int which, int aflag, List **optentries, const char *virtual_repository) { List *entries; List *files; const char *regex = NULL; if(!current_parsed_root->isremote) regex = lookup_regex(virtual_repository); /* make a list for the files */ files = filelist = getlist (); /* look at entries (if necessary) */ if (which & W_LOCAL) { /* parse the entries file (if it exists) */ entries = Entries_Open (aflag, NULL); if (entries != NULL) { /* walk the entries file adding elements to the files list */ (void) walklist (entries, add_entries_proc, NULL); /* if our caller wanted the entries list, return it; else free it */ if (optentries != NULL) *optentries = entries; else Entries_Close (entries); } } if ((which & W_REPOS) && repository && !isreadable (CVSADM_ENTSTAT)) { /* search the repository */ if (find_rcs (repository, files, regex) != 0) { error (0, errno, "cannot open directory %s", fn_root(repository)); goto error_exit; } /* search the attic too */ { char *dir; dir = (char*)xmalloc (strlen (repository) + sizeof (CVSATTIC) + 10); (void) sprintf (dir, "%s/%s", repository, CVSATTIC); if (find_rcs (dir, files, regex) != 0 && !existence_error (errno)) /* For now keep this a fatal error, seems less useful for access control than the case above. */ error (1, errno, "cannot open directory %s", fn_root(dir)); xfree (dir); } if(find_virtual_rcs (virtual_repository, files) != 0) { error(1, errno, "find_virtual_rcs failed"); } if(find_rename_rcs(virtual_repository, files) != 0) { error(1,errno, "find_renames failed"); } } xfree(regex); /* sort the list into alphabetical order and return it */ sortlist (files, fsortcmp); return (files); error_exit: xfree(regex); dellist (&files); return NULL; }
/* * Copies "from" to "to", decompressing "from" on the fly */ int copy_and_unzip_file (const char *from, const char *to, int force_overwrite, int must_exist) { struct stat sb; struct utimbuf t; int fdin, fdout; z_stream zstr = {0}; int zstatus; char buf[BUFSIZ*10]; char zbuf[BUFSIZ*20]; int n; #ifdef UTIME_EXPECTS_WRITABLE int change_it_back = 0; #endif TRACE(1,"copy_and_unzip(%s,%s)",PATCH_NULL(from),PATCH_NULL(to)); if (noexec) return 0; /* If the file to be copied is a link or a device, then just create the new link or device appropriately. */ if (islink (from)) { char *source = xreadlink (from); symlink (source, to); xfree (source); return 0; } if (isdevice (from)) { #if defined(HAVE_MKNOD) && defined(HAVE_STRUCT_STAT_ST_RDEV) if (stat (from, &sb) < 0) error (1, errno, "cannot stat %s", fn_root(from)); mknod (to, sb.st_mode, sb.st_rdev); #else error (1, 0, "cannot copy device files on this system (%s)", fn_root(from)); #endif } else { /* Not a link or a device... probably a regular file. */ if ((fdin = CVS_OPEN (from, O_RDONLY)) < 0) { if(must_exist) error (1, errno, "cannot open %s for copying", fn_root(from)); else return -1; } if (fstat (fdin, &sb) < 0) { if(must_exist) error (1, errno, "cannot fstat %s", fn_root(from)); else { close(fdin); return -1; } } if (force_overwrite && unlink_file (to) && !existence_error (errno)) error (1, errno, "unable to remove %s", to); if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0) error (1, errno, "cannot create %s for copying", to); zstatus = inflateInit2 (&zstr, 47); if(zstatus != Z_OK) error(1, 0, "expansion error (INIT): (%d)%s", zstatus,zstr.msg); if (sb.st_size > 0) { for (;;) { n = read (fdin, buf, sizeof(buf)); if (n == -1) { #ifdef EINTR if (errno == EINTR) continue; #endif error (1, errno, "cannot read file %s for copying", fn_root(from)); } else if (n == 0) break; zstr.next_in = (Bytef*)buf; zstr.avail_in = n; while(zstr.avail_in) { zstr.next_out = (Bytef*)zbuf; zstr.avail_out = sizeof(zbuf); zstatus = inflate (&zstr, 0); if(zstatus != Z_OK && zstatus != Z_STREAM_END) error(1,0, "expansion error (inflate): (%d)%s", zstatus,zstr.msg); n = sizeof(zbuf)-zstr.avail_out; if (n && write(fdout, zbuf, n) != n) { error (1, errno, "cannot write file %s for copying", to); } } if(zstatus == Z_STREAM_END) break; } #ifdef HAVE_FSYNC if (fsync (fdout)) error (1, errno, "cannot fsync file %s after copying", fn_root(to)); #endif } zstr.next_out = (Bytef*)zbuf; zstr.avail_out = sizeof(zbuf); zstatus = inflate (&zstr, Z_FINISH); if(zstatus != Z_OK && zstatus != Z_STREAM_END) error(1,0, "expansion error (Z_FINISH): (%d)%s", zstatus,zstr.msg); n = sizeof(zbuf)-zstr.avail_out; if (n && write(fdout, zbuf, n) != n) { error (1, errno, "cannot write file %s for copying", fn_root(to)); } zstr.next_in = (Bytef*)buf; zstr.avail_in = 0; zstr.next_out = (Bytef*)zbuf; zstr.avail_out = sizeof(zbuf); zstatus = inflateEnd(&zstr); if(zstatus != Z_OK) error(1,0, "expansion error: %s", zstr.msg); if (close (fdin) < 0) error (0, errno, "cannot close %s", fn_root(from)); if (close (fdout) < 0) error (1, errno, "cannot close %s", fn_root(to)); } #ifdef UTIME_EXPECTS_WRITABLE if (!iswritable (to)) { xchmod (to, 1); change_it_back = 1; } #endif /* UTIME_EXPECTS_WRITABLE */ /* now, set the times for the copied file to match those of the original */ memset ((char *) &t, 0, sizeof (t)); t.actime = sb.st_atime; t.modtime = sb.st_mtime; (void) utime (to, &t); #ifdef UTIME_EXPECTS_WRITABLE if (change_it_back) xchmod (to, 0); #endif /* UTIME_EXPECTS_WRITABLE */ return 0; }
/* Diff revisions and/or files. OPTS controls the format of the diff (it contains options such as "-w -c", &c), or "" for the default. OPTIONS controls keyword expansion, as a string starting with "-k", or "" to use the default. REV1 is the first revision to compare against; it must be non-NULL. If REV2 is non-NULL, compare REV1 and REV2; if REV2 is NULL compare REV1 with the file in the working directory, whose name is WORKFILE. LABEL1 and LABEL2 are default file labels, and (if non-NULL) should be added as -L options to diff. Output goes to stdout. Return value is 0 for success, -1 for a failure which set errno, or positive for a failure which printed a message on stderr. This used to exec rcsdiff, but now calls RCS_checkout and diff_exec. An issue is what timezone is used for the dates which appear in the diff output. rcsdiff uses the -z flag, which is not presently processed by CVS diff, but I'm not sure exactly how hard to worry about this--any such features are undocumented in the context of CVS, and I'm not sure how important to users. */ int RCS_exec_rcsdiff (RCSNode *rcsfile, int diff_argc, char * const *diff_argv, const char *options, const char *rev1, const char *rev1_cache, const char *rev2, const char *label1, const char *label2, const char *workfile) { char *tmpfile1 = NULL; char *tmpfile2 = NULL; const char *use_file1, *use_file2; int status, retval; cvs_output ("\ ===================================================================\n\ RCS file: ", 0); cvs_output (rcsfile->print_path, 0); cvs_output ("\n", 1); /* Historically, `cvs diff' has expanded the $Name keyword to the empty string when checking out revisions. This is an accident, but no one has considered the issue thoroughly enough to determine what the best behavior is. Passing NULL for the `nametag' argument preserves the existing behavior. */ cvs_output ("retrieving revision ", 0); cvs_output (rev1, 0); cvs_output ("\n", 1); if (rev1_cache != NULL) use_file1 = rev1_cache; else { tmpfile1 = cvs_temp_name(); status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1, NULL, NULL); if (status > 0) { retval = status; goto error_return; } else if (status < 0) { error( 0, errno, "cannot check out revision %s of %s", rev1, rcsfile->path ); retval = 1; goto error_return; } use_file1 = tmpfile1; } if (rev2 == NULL) { assert (workfile != NULL); use_file2 = workfile; } else { tmpfile2 = cvs_temp_name (); cvs_output ("retrieving revision ", 0); cvs_output (rev2, 0); cvs_output ("\n", 1); status = RCS_checkout (rcsfile, NULL, rev2, NULL, options, tmpfile2, NULL, NULL); if (status > 0) { retval = status; goto error_return; } else if (status < 0) { error (0, errno, "cannot check out revision %s of %s", rev2, rcsfile->path); return 1; } use_file2 = tmpfile2; } RCS_output_diff_options (diff_argc, diff_argv, rev1, rev2, workfile); status = diff_exec (use_file1, use_file2, label1, label2, diff_argc, diff_argv, RUN_TTY); if (status >= 0) { retval = status; goto error_return; } else if (status < 0) { error (0, errno, "cannot diff %s and %s", use_file1, use_file2); retval = 1; goto error_return; } error_return: { /* Call CVS_UNLINK() below rather than unlink_file to avoid the check * for noexec. */ if( tmpfile1 != NULL ) { if( CVS_UNLINK( tmpfile1 ) < 0 ) { if( !existence_error( errno ) ) error( 0, errno, "cannot remove temp file %s", tmpfile1 ); } free( tmpfile1 ); } if( tmpfile2 != NULL ) { if( CVS_UNLINK( tmpfile2 ) < 0 ) { if( !existence_error( errno ) ) error( 0, errno, "cannot remove temp file %s", tmpfile2 ); } free (tmpfile2); } } return retval; }
int RCS_merge (RCSNode *rcs, const char *path, const char *workfile, const char *options, const char *rev1, const char *rev2) { char *xrev1, *xrev2; char *tmp1, *tmp2; char *diffout = NULL; int retval; if (options != NULL && options[0] != '\0') assert (options[0] == '-' && options[1] == 'k'); cvs_output ("RCS file: ", 0); cvs_output (rcs->print_path, 0); cvs_output ("\n", 1); /* Calculate numeric revision numbers from rev1 and rev2 (may be symbolic). FIXME - No they can't. Both calls to RCS_merge are passing in numeric revisions. */ xrev1 = RCS_gettag (rcs, rev1, 0, NULL); xrev2 = RCS_gettag (rcs, rev2, 0, NULL); assert (xrev1 && xrev2); /* Check out chosen revisions. The error message when RCS_checkout fails is not very informative -- it is taken verbatim from RCS 5.7, and relies on RCS_checkout saying something intelligent upon failure. */ cvs_output ("retrieving revision ", 0); cvs_output (xrev1, 0); cvs_output ("\n", 1); tmp1 = cvs_temp_name(); if (RCS_checkout (rcs, NULL, xrev1, rev1, options, tmp1, NULL, NULL)) { cvs_outerr ("rcsmerge: co failed\n", 0); exit (EXIT_FAILURE); } cvs_output ("retrieving revision ", 0); cvs_output (xrev2, 0); cvs_output ("\n", 1); tmp2 = cvs_temp_name(); if (RCS_checkout (rcs, NULL, xrev2, rev2, options, tmp2, NULL, NULL)) { cvs_outerr ("rcsmerge: co failed\n", 0); exit (EXIT_FAILURE); } /* Merge changes. */ cvs_output ("Merging differences between ", 0); cvs_output (xrev1, 0); cvs_output (" and ", 0); cvs_output (xrev2, 0); cvs_output (" into ", 0); cvs_output (workfile, 0); cvs_output ("\n", 1); /* Remember that the first word in the `call_diff_setup' string is used now only for diagnostic messages -- CVS no longer forks to run diff3. */ diffout = cvs_temp_name(); call_diff_setup ("diff3", 0, NULL); call_diff_add_arg ("-E"); call_diff_add_arg ("-am"); call_diff_add_arg ("-L"); call_diff_add_arg (workfile); call_diff_add_arg ("-L"); call_diff_add_arg (xrev1); call_diff_add_arg ("-L"); call_diff_add_arg (xrev2); call_diff_add_arg ("--"); call_diff_add_arg (workfile); call_diff_add_arg (tmp1); call_diff_add_arg (tmp2); retval = call_diff3 (diffout); if (retval == 1) cvs_outerr ("rcsmerge: warning: conflicts during merge\n", 0); else if (retval == 2) exit (EXIT_FAILURE); if (diffout) copy_file (diffout, workfile); /* Clean up. */ { int save_noexec = noexec; noexec = 0; if (unlink_file (tmp1) < 0) { if (!existence_error (errno)) error (0, errno, "cannot remove temp file %s", tmp1); } free (tmp1); if (unlink_file (tmp2) < 0) { if (!existence_error (errno)) error (0, errno, "cannot remove temp file %s", tmp2); } free (tmp2); if (diffout) { if (unlink_file (diffout) < 0) { if (!existence_error (errno)) error (0, errno, "cannot remove temp file %s", diffout); } free (diffout); } free (xrev1); free (xrev2); noexec = save_noexec; } return retval; }
/* Rebuild the checked out administrative files in directory DIR. */ int mkmodules (char *dir) { struct saved_cwd cwd; char *temp; char *cp, *last, *fname; #ifdef MY_NDBM DBM *db; #endif FILE *fp; char *line = NULL; size_t line_allocated = 0; const struct admin_file *fileptr; mode_t mode; if (noexec) return 0; if (save_cwd (&cwd)) error_exit (); if ( CVS_CHDIR (dir) < 0) error (1, errno, "cannot chdir to %s", dir); /* * First, do the work necessary to update the "modules" database. */ temp = make_tempfile (); switch (checkout_file (CVSROOTADM_MODULES, dir, temp, NULL)) { case 0: /* everything ok */ #ifdef MY_NDBM /* open it, to generate any duplicate errors */ if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL) dbm_close (db); #else write_dbmfile (temp); rename_dbmfile (temp); #endif rename_rcsfile (temp, CVSROOTADM_MODULES); break; default: error (0, 0, "'cvs checkout' is less functional without a %s file", CVSROOTADM_MODULES); break; } /* switch on checkout_file() */ if (unlink_file (temp) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", temp); xfree (temp); /* Checkout the files that need it in CVSROOT dir */ for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) { if (fileptr->errormsg == NULL) continue; temp = make_tempfile (); if (checkout_file (fileptr->filename, dir, temp, NULL) == 0) rename_rcsfile (temp, fileptr->filename); if (unlink_file (temp) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", temp); xfree (temp); } fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r"); if (fp) { /* * File format: * [<whitespace>]<filename><whitespace><error message><end-of-line> * * comment lines begin with '#' */ while (getline (&line, &line_allocated, fp) >= 0) { /* skip lines starting with # */ if (line[0] == '#') continue; if ((last = strrchr (line, '\n')) != NULL) *last = '\0'; /* strip the newline */ /* Skip leading white space. */ for (fname = line; *fname && isspace ((unsigned char) *fname); fname++) ; /* Find end of filename. */ for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++) ; *cp = '\0'; if(isabsolute(fname) || pathname_levels(fname)>0 || !fncmp(fname,"checkoutlist")) { error(0,0,"Invalid filename '%s' in checkoutlist", fname); continue; } temp = make_tempfile (); if (checkout_file (fname, dir, temp, &mode) == 0) { chmod(temp,mode); rename_rcsfile (temp, fname); } else { for (cp++; cp < last && *last && isspace ((unsigned char) *last); cp++) ; if (cp < last && *cp) error (0, 0, cp, fname); } if (unlink_file (temp) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", temp); xfree (temp); } if (line) xfree (line); if (ferror (fp)) error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST); if (fclose (fp) < 0) error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST); } else { /* Error from CVS_FOPEN. */ if (!existence_error (errno)) error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST); } if (restore_cwd (&cwd, NULL)) error_exit (); free_cwd (&cwd); return (0); }
/* * Copies "from" to "to", decompressing "from" on the fly */ int copy_and_unzip_file (const char *from, const char *to, int force_overwrite, int must_exist) { struct stat sb; struct utimbuf t; int fdin, fdout; z_stream zstr = {0}; int zstatus; char buf[BUFSIZ*10]; char zbuf[BUFSIZ*20]; int n; #ifdef UTIME_EXPECTS_WRITABLE int change_it_back = 0; #endif TRACE(1,"copy_and_unzip(%s,%s)",from,to); if (noexec) return 0; if ((fdin = open (from, O_RDONLY | O_BINARY,0)) < 0) { if(must_exist) error (1, errno, "cannot open %s for copying", from); else return -1; } if (fstat (fdin, &sb) < 0) { if(must_exist) error (1, errno, "cannot fstat %s", from); else { close(fdin); return -1; } } if (force_overwrite && unlink_file (to) && !existence_error (errno)) error (1, errno, "unable to remove %s", to); if ((fdout = open (to, O_CREAT | O_TRUNC | O_RDWR | O_BINARY, 0600))<0) error (1, errno, "cannot create %s for copying", to); zstatus = inflateInit2 (&zstr, 47); if(zstatus != Z_OK) error(1, 0, "expansion error (INIT): (%d)%s", zstatus,zstr.msg); if (sb.st_size > 0) { for (;;) { n = read (fdin, buf, sizeof(buf)); if (n == -1) { #ifdef EINTR if (errno == EINTR) continue; #endif error (1, errno, "cannot read file %s for copying", from); } else if (n == 0) break; zstr.next_in = buf; zstr.avail_in = n; while(zstr.avail_in) { zstr.next_out = zbuf; zstr.avail_out = sizeof(zbuf); zstatus = inflate (&zstr, 0); if(zstatus != Z_OK && zstatus != Z_STREAM_END) error(1,0, "expansion error (inflate): (%d)%s", zstatus,zstr.msg); n = sizeof(zbuf)-zstr.avail_out; if (n && write(fdout, zbuf, n) != n) { error (1, errno, "cannot write file %s for copying", to); } } if(zstatus == Z_STREAM_END) break; } #ifdef HAVE_FSYNC if (fsync (fdout)) error (1, errno, "cannot fsync file %s after copying", to); #endif } zstr.next_out = zbuf; zstr.avail_out = sizeof(zbuf); zstatus = inflate (&zstr, Z_FINISH); if(zstatus != Z_OK && zstatus != Z_STREAM_END) error(1,0, "expansion error (Z_FINISH): (%d)%s", zstatus,zstr.msg); n = sizeof(zbuf)-zstr.avail_out; if (n && write(fdout, zbuf, n) != n) { error (1, errno, "cannot write file %s for copying", to); } zstr.next_in = buf; zstr.avail_in = 0; zstr.next_out = zbuf; zstr.avail_out = sizeof(zbuf); zstatus = inflateEnd(&zstr); if(zstatus != Z_OK) error(1,0, "expansion error: %s", zstr.msg); if (close (fdin) < 0) error (0, errno, "cannot close %s", from); if (close (fdout) < 0) error (1, errno, "cannot close %s", to); #ifdef UTIME_EXPECTS_WRITABLE if (!iswritable (to)) { xchmod (to, 1); change_it_back = 1; } #endif /* UTIME_EXPECTS_WRITABLE */ /* now, set the times for the copied file to match those of the original */ memset ((char *) &t, 0, sizeof (t)); t.actime = sb.st_atime; t.modtime = sb.st_mtime; utime (to, &t); chmod(to,sb.st_mode); #ifdef UTIME_EXPECTS_WRITABLE if (change_it_back) xchmod (to, 0); #endif /* UTIME_EXPECTS_WRITABLE */ return 0; }
/* ARGSUSED */ static int remove_fileproc (void *callerdat, struct file_info *finfo) { Vers_TS *vers; if (force) { if (!noexec) { if ( CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno)) { error (0, errno, "unable to remove %s", fn_root(finfo->fullname)); } } /* else FIXME should probably act as if the file doesn't exist in doing the following checks. */ } vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0, 0); if (vers->ts_user && (!vers->vn_user || strcmp(vers->vn_user,"0"))) { existing_files++; if (!quiet) error (0, 0, "file `%s' still in working directory", fn_root(finfo->fullname)); } else if (vers->vn_user == NULL) { if (!quiet) error (0, 0, "nothing known about `%s'", fn_root(finfo->fullname)); } else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') { char *fname; /* * It's a file that has been added, but not commited yet. So, * remove the ,t file for it and scratch it from the * entries file. */ Scratch_Entry (finfo->entries, finfo->file); fname = (char*)xmalloc (strlen (finfo->file) + sizeof (CVSADM) + sizeof (CVSEXT_LOG) + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); if (unlink_file (fname) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s", CVSEXT_LOG); if (!quiet) error (0, 0, "removed `%s'", fn_root(finfo->fullname)); #ifdef SERVER_SUPPORT if (server_active) server_checked_in (finfo->file, finfo->update_dir, finfo->repository); #endif xfree (fname); } else if (vers->vn_user[0] == '-') { if (!quiet) error (0, 0, "file `%s' already scheduled for removal", fn_root(finfo->fullname)); } else if (vers->tag != NULL && ((isdigit ((unsigned char) *vers->tag)) || !RCS_isbranch(finfo->rcs, vers->tag))) { /* Commit will just give an error, and so there seems to be little reason to allow the remove. I mean, conflicts that arise out of parallel development are one thing, but conflicts that arise from sticky tags are quite another. */ error (0, 0, "\ cannot remove file `%s' which has a sticky tag of `%s'", fn_root(finfo->fullname), vers->tag); bad_files++; } else { char *fname; /* Re-register it with a negative version number. */ fname = (char*)xmalloc (strlen (vers->vn_user) + 5); (void) strcpy (fname, "-"); (void) strcat (fname, vers->vn_user); Register (finfo->entries, finfo->file, fname, vers->ts_rcs, vers->options, vers->tag, vers->date, vers->ts_conflict, NULL, NULL, vers->tt_rcs, vers->edit_revision, vers->edit_tag, vers->edit_bugid, NULL); if (!quiet) error (0, 0, "scheduling `%s' for removal", fn_root(finfo->fullname)); removed_files++; #ifdef SERVER_SUPPORT if (server_active) server_checked_in (finfo->file, finfo->update_dir, finfo->repository); #endif xfree (fname); } freevers_ts (&vers); return (0); }
/*ARGSUSED*/ static int remove_force_fileproc (void *callerdat, struct file_info *finfo) { if (CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno)) error (0, errno, "unable to remove %s", fn_root(finfo->fullname)); return 0; }
/* * Copies "from" to "to". */ int copy_file (const char *from, const char *to, int force_overwrite, int must_exist) { struct stat sb; struct utimbuf t; int fdin, fdout; #ifdef UTIME_EXPECTS_WRITABLE int change_it_back = 0; #endif TRACE(1,"copy(%s,%s)",PATCH_NULL(from),PATCH_NULL(to)); if (noexec) return 0; /* If the file to be copied is a link or a device, then just create the new link or device appropriately. */ if (islink (from)) { char *source = xreadlink (from); symlink (source, to); xfree (source); return 0; } if (isdevice (from)) { #if defined(HAVE_MKNOD) && defined(HAVE_STRUCT_STAT_ST_RDEV) if (stat (from, &sb) < 0) error (1, errno, "cannot stat %s", fn_root(from)); mknod (to, sb.st_mode, sb.st_rdev); #else error (1, 0, "cannot copy device files on this system (%s)", fn_root(from)); #endif } else { #ifdef MAC_HFS_STUFF /* Only use the mac specific copying routine in the client, since the server shouldn't have any need to handle resource forks (all the resource fork handling is done in the client - the server only handles flat files) */ if ( !server_active ) { if (stat (from, &sb) < 0) { if(must_exist) error (1, errno, "cannot stat %s", fn_root(from)); else return -1; } mac_copy_file(from, to, force_overwrite, must_exist); } else { #endif /* Not a link or a device... probably a regular file. */ if ((fdin = CVS_OPEN (from, O_RDONLY)) < 0) { if(must_exist) error (1, errno, "cannot open %s for copying", fn_root(from)); else return -1; } if (fstat (fdin, &sb) < 0) { if(must_exist) error (1, errno, "cannot fstat %s", fn_root(from)); else { close(fdin); return -1; } } if (force_overwrite && unlink_file (to) && !existence_error (errno)) error (1, errno, "unable to remove %s", to); if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0) error (1, errno, "cannot create %s for copying", fn_root(to)); if (sb.st_size > 0) { char buf[BUFSIZ]; int n; for (;;) { n = read (fdin, buf, sizeof(buf)); if (n == -1) { #ifdef EINTR if (errno == EINTR) continue; #endif error (1, errno, "cannot read file %s for copying", fn_root(from)); } else if (n == 0) break; if (write(fdout, buf, n) != n) { error (1, errno, "cannot write file %s for copying", fn_root(to)); } } #ifdef HAVE_FSYNC if (fsync (fdout)) error (1, errno, "cannot fsync file %s after copying", fn_root(to)); #endif } if (close (fdin) < 0) error (0, errno, "cannot close %s", fn_root(from)); if (close (fdout) < 0) error (1, errno, "cannot close %s", fn_root(to)); #ifdef MAC_HFS_STUFF } #endif } #ifdef UTIME_EXPECTS_WRITABLE if (!iswritable (to)) { xchmod (to, 1); change_it_back = 1; } #endif /* UTIME_EXPECTS_WRITABLE */ /* now, set the times for the copied file to match those of the original */ memset ((char *) &t, 0, sizeof (t)); t.actime = sb.st_atime; t.modtime = sb.st_mtime; (void) utime (to, &t); #ifdef UTIME_EXPECTS_WRITABLE if (change_it_back) xchmod (to, 0); #endif /* UTIME_EXPECTS_WRITABLE */ return 0; }
char *Name_Repository (const char *dir, const char *update_dir) { FILE *fpin; const char *xupdate_dir; char *repos = NULL; size_t repos_allocated = 0; char *tmp=NULL; char *cp; TRACE(3,"Name_Repository(%s,%s)",PATCH_NULL(dir),PATCH_NULL(update_dir)); if (update_dir && *update_dir) xupdate_dir = update_dir; else xupdate_dir = "."; if (dir != NULL) { size_t tmpsz=strlen (dir) + sizeof (CVSADM_REP) + 10; tmp = (char*)xmalloc (tmpsz+sizeof(CVSADM_VIRTREPOS)); sprintf (tmp, "%s/%s", dir, CVSADM_VIRTREPOS); size_t tmplen1 = strlen(tmp); if(!isfile(tmp)) sprintf(tmp, "%s/%s", dir, CVSADM_REP); size_t tmplen2 = strlen(tmp); TRACE(3,"Name_Repository allocate tmp of size %d, len1=%d, len2=%d",(int)tmpsz,(int)tmplen1,(int)tmplen2); } else { tmp = xstrdup (CVSADM_VIRTREPOS); TRACE(3,"Name_Repository dup tmp is len",(int)strlen(tmp)); if(!isfile(tmp)) { xfree(tmp); tmp=NULL; tmp=xstrdup(CVSADM_REP); TRACE(3,"Name_Repository dup tmp is now len",(int)strlen(tmp)); } } /* * The assumption here is that the repository is always contained in the * first line of the "Repository" file. */ TRACE(3,"Name_Repository open %s",tmp); fpin = CVS_FOPEN (tmp, "r"); if (fpin == NULL) { int save_errno = errno; char *cvsadm=NULL; if (dir != NULL) { cvsadm = (char*)xmalloc (strlen (dir) + sizeof (CVSADM) + 100); (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); } else cvsadm = xstrdup (CVSADM); TRACE(3,"Name_Repository failed to open %s so try %s",tmp,cvsadm); if (!isdir (cvsadm)) { error (0, 0, "in directory %s:", xupdate_dir); error (1, 0, "there is no version here; do '%s checkout' first", program_name); } xfree (cvsadm); cvsadm=NULL; if (existence_error (save_errno)) { /* FIXME: This is a very poorly worded error message. It occurs at least in the case where the user manually creates a directory named CVS, so the error message should be more along the lines of "CVS directory found without administrative files; use CVS to create the CVS directory, or rename it to something else if the intention is to store something besides CVS administrative files". */ error (0, 0, "in directory %s:", xupdate_dir); error (1, 0, "CVS directory without administration files present. Cannot continue until this directory is deleted or renamed."); } error (1, save_errno, "cannot open %s", tmp); } TRACE(3,"Name_Repository opened %s ok so read a line",tmp); if (getline (&repos, &repos_allocated, fpin) < 0) { /* FIXME: should be checking for end of file separately. */ error (0, 0, "in directory %s:", xupdate_dir); error (1, errno, "cannot read %s", tmp); } if (fclose (fpin) < 0) error (0, errno, "cannot close %s", tmp); TRACE(3,"Name_Repository closed %s",tmp); xfree (tmp); tmp=NULL; TRACE(3,"Name_Repository read 1 %s",repos); if ((cp = strrchr (repos, '\n')) != NULL) *cp = '\0'; /* strip the newline */ TRACE(3,"Name_Repository read 2 %s",repos); /* * If this is a relative repository pathname, turn it into an absolute * one by tacking on the CVSROOT environment variable. If the CVSROOT * environment variable is not set, die now. */ TRACE(3,"Name_Repository isabsolute( %s )?",repos); char *newrepos=NULL; if (! isabsolute(repos)) { TRACE(3,"Name_Repository isabsolute( %s )!",repos); if (current_parsed_root == NULL) { error (0, 0, "in directory %s:", xupdate_dir); error (0, 0, "must set the CVSROOT environment variable\n"); error (0, 0, "or specify the '-d' option to %s.", program_name); error (1, 0, "illegal repository setting"); } if (pathname_levels (repos) > 0) { error (0, 0, "in directory %s:", xupdate_dir); error (0, 0, "`..'-relative repositories are not supported."); error (1, 0, "illegal source repository"); } newrepos = (char*)xmalloc (strlen (current_parsed_root->directory) + strlen (repos) + 2); (void) sprintf (newrepos, "%s/%s", current_parsed_root->directory, repos); #ifndef HAVE_GETLINE xfree (repos); #else free(repos); #endif repos = newrepos; } else { TRACE(3,"Name_Repository not isabsolute( %s )",repos); newrepos = (char*)xmalloc (strlen (repos) + 2); (void) sprintf (newrepos, "%s", repos); #ifndef HAVE_GETLINE xfree (repos); #else free(repos); #endif repos = newrepos; } TRACE(3,"Name_Repository Sanitize_Repository_Name( %s )!",repos); Sanitize_Repository_Name (&repos); TRACE(3,"Name_Repository return ( %s )!",repos); return repos; }
static foreign_t cgi_property(term_t cgi, term_t prop) { IOSTREAM *s; cgi_context *ctx; term_t arg = PL_new_term_ref(); atom_t name; int arity; int rc = TRUE; if ( !get_cgi_stream(cgi, &s, &ctx) ) return FALSE; if ( !PL_get_name_arity(prop, &name, &arity) || arity != 1 ) { rc = type_error(prop, "cgi_property"); goto out; } _PL_get_arg(1, prop, arg); if ( name == ATOM_request ) { if ( ctx->request ) rc = unify_record(arg, ctx->request); else rc = PL_unify_nil(arg); } else if ( name == ATOM_header ) { if ( ctx->header ) rc = unify_record(arg, ctx->header); else rc = PL_unify_nil(arg); } else if ( name == ATOM_id ) { rc = PL_unify_int64(arg, ctx->id); } else if ( name == ATOM_client ) { rc = PL_unify_stream(arg, ctx->stream); } else if ( name == ATOM_transfer_encoding ) { rc = PL_unify_atom(arg, ctx->transfer_encoding); } else if ( name == ATOM_connection ) { rc = PL_unify_atom(arg, ctx->connection ? ctx->connection : ATOM_close); } else if ( name == ATOM_content_length ) { if ( ctx->transfer_encoding == ATOM_chunked ) rc = PL_unify_int64(arg, ctx->chunked_written); else rc = PL_unify_int64(arg, ctx->datasize - ctx->data_offset); } else if ( name == ATOM_header_codes ) { if ( ctx->data_offset > 0 ) rc = PL_unify_chars(arg, PL_CODE_LIST, ctx->data_offset, ctx->data); else /* incomplete header */ rc = PL_unify_chars(arg, PL_CODE_LIST, ctx->datasize, ctx->data); } else if ( name == ATOM_state ) { atom_t state; switch(ctx->state) { case CGI_HDR: state = ATOM_header; break; case CGI_DATA: state = ATOM_data; break; case CGI_DISCARDED: state = ATOM_discarded; break; default: assert(0); } rc = PL_unify_atom(arg, state); } else { rc = existence_error(prop, "cgi_property"); } out: if ( !PL_release_stream(s) ) { if ( PL_exception(0) ) PL_clear_exception(); } return rc; }
/* Copies "from" to "to". Note that the functionality here is similar to the win32 function CopyFile, but (1) we copy LastAccessTime and CopyFile doesn't, (2) we set file attributes to the default set by the C library and CopyFile copies them. Neither #1 nor #2 was intentional as far as I know, but changing them could be confusing, unless there is some reason they should be changed (this would need more investigation). */ int copy_file (const char *from, const char *to, int force_overwrite, int must_exist) { struct stat sb; struct utimbuf t; int fdin, fdout; #ifdef UTIME_EXPECTS_WRITABLE int change_it_back = 0; #endif TRACE(1,"copy(%s,%s)",from,to); if (noexec) return 0; if ((fdin = open (from, O_RDONLY | O_BINARY,0)) < 0) { if(must_exist) error (1, errno, "cannot open %s for copying", from); else return -1; } if (fstat (fdin, &sb) < 0) { if(must_exist) error (1, errno, "cannot fstat %s", from); else { close(fdin); return -1; } } if (force_overwrite && unlink_file (to) && !existence_error (errno)) error (1, errno, "unable to remove %s", to); if ((fdout = open (to, O_CREAT | O_TRUNC | O_RDWR | O_BINARY, 0600)) < 0) error (1, errno, "cannot create %s for copying", to); if (sb.st_size > 0) { char buf[BUFSIZ]; int n; for (;;) { n = read (fdin, buf, sizeof(buf)); if (n == -1) { #ifdef EINTR if (errno == EINTR) continue; #endif error (1, errno, "cannot read file %s for copying", from); } else if (n == 0) break; if (write(fdout, buf, n) != n) { error (1, errno, "cannot write file %s for copying", to); } } #ifdef HAVE_FSYNC if (fsync (fdout)) error (1, errno, "cannot fsync file %s after copying", to); #endif } if (close (fdin) < 0) error (0, errno, "cannot close %s", from); if (close (fdout) < 0) error (1, errno, "cannot close %s", to); #ifdef UTIME_EXPECTS_WRITABLE if (!iswritable (to)) { xchmod (to, 1); change_it_back = 1; } #endif /* UTIME_EXPECTS_WRITABLE */ /* now, set the times for the copied file to match those of the original */ memset ((char *) &t, 0, sizeof (t)); t.actime = sb.st_atime; t.modtime = sb.st_mtime; utime (to, &t); chmod(to,sb.st_mode); #ifdef UTIME_EXPECTS_WRITABLE if (change_it_back) xchmod (to, 0); #endif /* UTIME_EXPECTS_WRITABLE */ return 0; }
/* * Builds a temporary file using setup_tmpfile() and invokes the user's * editor on the file. The header garbage in the resultant file is then * stripped and the log message is stored in the "message" argument. * * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it * is NULL, use the CVSADM_TEMPLATE file instead. */ void do_editor (const char *dir, char **messagep, const char *repository, List *changes) { static int reuse_log_message = 0; char *line; int line_length; size_t line_chars_allocated; char *fname; struct stat pre_stbuf, post_stbuf; int retcode = 0; if (noexec || reuse_log_message) return; /* Abort creation of temp file if no editor is defined */ if (strcmp (Editor, "") == 0) error(1, 0, "no editor defined, must use -e or -m"); /* Create a temporary file */ /* FIXME - It's possible we should be relying on cvs_temp_file to open * the file here - we get race conditions otherwise. */ fname = cvs_temp_name (); again: if ((fp = CVS_FOPEN (fname, "w+")) == NULL) error (1, 0, "cannot create temporary file %s", fname); if (*messagep) { fprintf (fp, "%s", *messagep); if ((*messagep)[0] == '\0' || (*messagep)[strlen (*messagep) - 1] != '\n') fprintf (fp, "\n"); } if (repository != NULL) { rcsinfo_param_t args; args.directory = Short_Repository(repository); args.message=NULL; /* tack templates on if necessary */ TRACE(3,"run rcsinfo trigger"); if(!run_trigger(&args,rcsinfo_proc) && args.message) { fprintf(fp,"%s",args.message); if (args.message[0] == '\0' || args.message[strlen(args.message) - 1] != '\n') fprintf (fp, "\n"); } } else { FILE *tfp; char buf[1024]; size_t n; size_t nwrite; /* Why "b"? */ tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb"); if (tfp == NULL) { if (!existence_error (errno)) error (1, errno, "cannot read %s", CVSADM_TEMPLATE); } else { while (!feof (tfp)) { char *p = buf; n = fread (buf, 1, sizeof buf, tfp); nwrite = n; while (nwrite > 0) { n = fwrite (p, 1, nwrite, fp); nwrite -= n; p += n; } if (ferror (tfp)) error (1, errno, "cannot read %s", CVSADM_TEMPLATE); } if (fclose (tfp) < 0) error (0, errno, "cannot close %s", CVSADM_TEMPLATE); } } fprintf (fp,"%s----------------------------------------------------------------------\n",CVSEDITPREFIX); #ifdef _WIN32 #if (CVSNT_SPECIAL_BUILD_FLAG != 0) if (strcasecmp(CVSNT_SPECIAL_BUILD,"Suite")!=0) #endif { fprintf (fp,"%s Committed on the Free edition of March Hare Software CVSNT Server\n",CVSEDITPREFIX); fprintf (fp,"%s Upgrade to CVS Suite for more features and support:\n",CVSEDITPREFIX); fprintf (fp,"%s http://march-hare.com/cvsnt/\n",CVSEDITPREFIX); fprintf (fp,"%s----------------------------------------------------------------------\n",CVSEDITPREFIX); } #endif fprintf (fp,"%sEnter Log. Lines beginning with `%.*s' are removed automatically\n%s\n", CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX,CVSEDITPREFIX); if (dir != NULL && *dir) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX, dir, CVSEDITPREFIX); if (changes != NULL) setup_tmpfile (fp, CVSEDITPREFIX, changes); fprintf (fp,"%s----------------------------------------------------------------------\n", CVSEDITPREFIX); /* finish off the temp file */ if (fclose (fp) == EOF) error (1, errno, "%s", fname); if ( CVS_STAT (fname, &pre_stbuf) == -1) pre_stbuf.st_mtime = 0; /* run the editor */ run_setup (Editor); run_arg (fname); if ((retcode = run_exec (true)) != 0) error (0, retcode == -1 ? errno : 0, "warning: editor session failed"); /* put the entire message back into the *messagep variable */ fp = open_file (fname, "r"); if (*messagep) xfree (*messagep); if ( CVS_STAT (fname, &post_stbuf) != 0) error (1, errno, "cannot find size of temp file %s", fname); if (post_stbuf.st_size == 0) *messagep = NULL; else { /* On NT, we might read less than st_size bytes, but we won't read more. So this works. */ *messagep = (char *) xmalloc (post_stbuf.st_size + 1); *messagep[0] = '\0'; } line = NULL; line_chars_allocated = 0; if (*messagep) { size_t message_len = post_stbuf.st_size + 1; size_t offset = 0; while (1) { line_length = getline (&line, &line_chars_allocated, fp); if (line_length == -1) { if (ferror (fp)) error (0, errno, "warning: cannot read %s", fname); break; } if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0) continue; if (offset + line_length >= message_len) expand_string (messagep, &message_len, offset + line_length + 1); strcpy (*messagep + offset, line); offset += line_length; } } if (fclose (fp) < 0) error (0, errno, "warning: cannot close %s", fname); if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL || strcmp (*messagep, "\n") == 0) { for (;;) { printf ("\nLog message unchanged or not specified\n"); printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n"); printf ("Action: (continue) "); fflush (stderr); fflush (stdout); line_length = getline (&line, &line_chars_allocated, stdin); if (line_length < 0) { error (0, errno, "cannot read from stdin"); if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); error (1, 0, "aborting"); } else if (line_length == 0 || *line == '\n' || *line == 'c' || *line == 'C') break; if (*line == 'a' || *line == 'A') { if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); error (1, 0, "aborted by user"); } if (*line == 'e' || *line == 'E') goto again; if (*line == '!') { reuse_log_message = 1; break; } printf ("Unknown input\n"); } } if (line) xfree (line); if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); xfree (fname); }