/* editfile.c - run the editor on an entire file
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif				/* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <utime.h>
#include <fcntl.h>
#include <errno.h>
#include "terminal.h"
#include "hook.h"

extern int ed_main(char *, long, char *, char *);


/* Transfer all data from "rfd" to "wfd". The file descriptors are not closed.
 * Returns nonzero on error.
 */
int mview_ef_transfer(int rfd, int wfd)
{
	char buf[1024];
	int r, w, o;

	for (w = 0;;) {
		t_bored(1);
		r = read(rfd, buf, 1024);
		if (r <= 0)
			break;
		for (o = 0; r > 0;) {
			w = write(wfd, buf + o, r);
			if (w < 0)
				break;
			o += w;
			r -= w;
		}
	}

	t_bored(0);

	if ((r < 0) || (w < 0))
		return (1);
	return (0);
}


/* Run the editor specified by the environment variable EDITOR or VISUAL,
 * or "joe" if none is given, on "file" which is currently at line "pos".
 * However if "ext" is 0, the internal editor is used instead.
 *
 * This is done by copying "file" to a temporary file, dropping privileges,
 * and running the editor. On exiting the editor, the parent process copies
 * the contents of the temporary file back to "file". Therefore "file" must
 * have been locked by the calling function before this function is called.
 *
 * If the internal editor is used, the title "title" is displayed on the top
 * line, and any lines in "hdr" (or none if it's 0) are shown.
 *
 * NB if "file" is owned by the current user then it is not copied first -
 * the editor is run on "file" itself.
 *
 * Returns nonzero if the editor did.
 */
int mview_edit_file(char *file, long pos, int ext, char *title, char *hdr)
{
	struct utimbuf ub;
	struct stat sb;
	char buf[128];
	char *editor;
	char *temp = 0;
	pid_t child;
	pid_t result;
	int status;
	int es = 0;
	int notmp = 0;
	char *a;
	int rfd = -1;
	int wfd = -1;

	if (stat(file, &sb))
		return (1);
	notmp = (sb.st_uid == getuid())? 1 : 0;

	if (!notmp) {

		temp = (char *) malloc(strlen(P_tmpdir) + 32);
		if (!temp)
			return (1);
		strcpy(temp, P_tmpdir);
		strcat(temp, "/mteXXXXXX");

		rfd = open(file, O_RDONLY);
		if (rfd < 0)
			return (1);

		wfd = mkstemp(temp);
		if (wfd < 0) {
			close(rfd);
			remove(temp);
			free(temp);
			return (1);
		}

	}

	if (ext) {			    /* use external editor */

		t_echo_on();
		t_canon_on();

		if ((!(editor = getenv("EDITOR")))
		    && (!(editor = getenv("VISUAL"))))
			editor = JOE_PATH;

		sprintf(buf, "+%ld", (pos > 0) ? pos : 1);

		child = fork();
		if (child == 0) {

			a = strrchr(file, '/');
			if (a) {
				a[0] = 0;
				chdir(file);
				a[0] = '/';
			}
			bbs_hook(HOOK_DROP_PRIVS, editor, 0);
			if (!notmp) {
				if (mview_ef_transfer(rfd, wfd))
					exit(1);
				close(rfd);
				close(wfd);
				ub.actime = 0;
				ub.modtime = 0;
				utime(temp, &ub);	/* set last-modification to 0 */
			}
			if (pos > 1) {
				execlp(editor, editor, buf,
				       (notmp) ? file : temp, 0);
			} else {	    /* don't pass line number if it's <2 */
				execlp(editor, editor,
				       (notmp) ? file : temp, 0);
			}
			exit(1);

		} else if (child > 0) {

			do {

				alarm(10);  /* update status every 10 seconds */
				result = KEY_NONE;
				bbs_hook(HOOK_KEY_PRESSED, &child,
					 &result);

				result = waitpid(child, &status, 0);
				if ((result <= 0)
				    && ((errno == EAGAIN)
					|| (errno == EINTR)))
					result = 1;

			} while (result > 0);

		}

	} else {			    /* use internal editor */

		if (!notmp) {
			if (mview_ef_transfer(rfd, wfd)) {
				close(rfd);
				close(wfd);
				remove(temp);
				free(temp);
				return (1);
			}
			close(wfd);
			ub.actime = 0;
			ub.modtime = 0;
			utime(temp, &ub);   /* set last-modification to 0 */
		}

		es = ed_main((notmp) ? file : temp, pos, title, hdr);
	}

	if (!notmp) {
		close(rfd);

		wfd = -1;
		rfd = -1;

		if (ext)
			es = WEXITSTATUS(status);

		if ((es == 0) &&	    /* copy back only if editor worked */
		    (!stat(temp, &sb))	    /* and temp exists */
		    &&(sb.st_mtime > 0))    /* and it has been modified */
			rfd = open(temp, O_RDONLY);

		if (rfd >= 0)
			wfd = open(file, O_WRONLY | O_TRUNC);

		if ((rfd >= 0) && (wfd >= 0))
			mview_ef_transfer(rfd, wfd);

		if (rfd >= 0)
			close(rfd);
		if (wfd >= 0)
			close(wfd);

		remove(temp);
		free(temp);
	}

	if (ext) {
		t_init(getenv("TERM"));
		t_echo_off();
		t_canon_off();
	}

	return (es);
}

/* EOF */
