/*
** prompter.c -- simple prompting editor front-end
**
** This code is Copyright (c) 2002, by the authors of nmh.  See the
** COPYRIGHT file in the root directory of the nmh distribution for
** complete copyright information.
*/

#include <h/mh.h>
#include <fcntl.h>
#include <h/signals.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
#include <locale.h>
#include <sysexits.h>

static struct swit switches[] = {
#define PREPSW  0
	{ "prepend", 0 },
#define NPREPSW  1
	{ "noprepend", 2 },
#define RAPDSW  2
	{ "rapid", 0 },
#define NRAPDSW  3
	{ "norapid", 2 },
#define BODYSW  4
	{ "body", 0 },
#define NBODYSW  5
	{ "nobody", 2 },
#define VERSIONSW 6
	{ "Version", 0 },
#define HELPSW  7
	{ "help", 0 },
	{ NULL, 0 }
};

char *version=VERSION;

volatile sig_atomic_t wtuser = 0;
volatile sig_atomic_t sigint = 0;
static jmp_buf sigenv;

/*
** prototypes
*/
int getln(char *, int);
static void intrser(int);


int
main(int argc, char **argv)
{
	int qbody = 1, prepend = 1, rapid = 0;
	int fdi, fdo, i;
	char *cp, *drft = NULL;
	enum state state;
	struct field f = {{0}};
	char buffer[BUFSIZ], tmpfil[BUFSIZ];
	char **arguments, **argp;
	FILE *in, *out;
	char *tfile = NULL;

	setlocale(LC_ALL, "");
	invo_name = mhbasename(argv[0]);

	/* read user profile/context */
	context_read();

	arguments = getarguments(invo_name, argc, argv, 1);
	argp = arguments;

	while ((cp = *argp++)) {
		if (*cp == '-') {
			switch (smatch(++cp, switches)) {
			case AMBIGSW:
				ambigsw(cp, switches);
				exit(EX_USAGE);
			case UNKWNSW:
				adios(EX_USAGE, NULL, "-%s unknown", cp);

			case HELPSW:
				snprintf(buffer, sizeof(buffer),
						"%s [switches] file",
						invo_name);
				print_help(buffer, switches, 1);
				exit(argc == 2 ? EX_OK : EX_USAGE);

			case VERSIONSW:
				print_version(invo_name);
				exit(argc == 2 ? EX_OK : EX_USAGE);

			case PREPSW:
				prepend++;
				continue;
			case NPREPSW:
				prepend = 0;
				continue;

			case RAPDSW:
				rapid++;
				continue;
			case NRAPDSW:
				rapid = 0;
				continue;

			case BODYSW:
				qbody++;
				continue;
			case NBODYSW:
				qbody = 0;
				continue;
			}
		} else if (!drft) {
			drft = cp;
		}
	}

	if (!drft) {
		adios(EX_USAGE, NULL, "usage: %s [switches] file", invo_name);
	}
	if ((in = fopen(drft, "r")) == NULL) {
		adios(EX_IOERR, drft, "unable to open");
	}

	tfile = m_mktemp2(NULL, invo_name, NULL, &out);
	if (tfile == NULL) {
		adios(EX_CANTCREAT, "prompter", "unable to create temporary file");
	}
	chmod(tmpfil, 0600);
	strncpy(tmpfil, tfile, sizeof(tmpfil));

	sigint = 0;
	SIGNAL2(SIGINT, intrser);

	/*
	** Loop through the lines of the draft skeleton.
	*/
	for (state = FLD2;;) {
		switch (state = m_getfld2(state, &f, in)) {
		case LENERR2:
			state = FLD2;
			/* FALL */

		case FLD2:
			/*
			** Check if the value of field contains
			** anything other than space or tab.
			*/
			for (cp = f.value; *cp; cp++) {
				if (*cp != ' ' && *cp != '\t') {
					break;
				}
			}

			/* If so, just add header line to draft */
			if (*cp++ != '\n' || *cp) {
				printf("%s:%s", f.name, f.value);
				fprintf(out, "%s:%s", f.name, f.value);
			} else {
				/* Else, get value of header field */
				printf("%s: ", f.name);
				fflush(stdout);
				i = getln(buffer, sizeof(buffer));
				if (i == -1) {
abort:
					unlink(tmpfil);
					exit(EX_DATAERR);
				}
				if (i || (buffer[0]!='\n' && buffer[0]!='\0')) {
					fprintf(out, "%s:", f.name);
					do {
						if (buffer[0] != ' ' && buffer[0] != '\t') {
							putc(' ', out);
						}
						fprintf(out, "%s", buffer);
					} while (i == 1 && (i = getln(buffer, sizeof(buffer))) >= 0);
					if (i == -1) {
						goto abort;
					}
				}
			}
			continue;

		case BODY2:
		case FILEEOF2:
			fprintf(out, "--------\n");
			if (qbody) {
				if (f.value == NULL) {
					printf("--------\n");
					goto has_no_body;
				}

				if (prepend) {
					printf("--------Enter initial text\n");
					fflush(stdout);
					for (;;) {
						getln(buffer, sizeof(buffer));
						if (!*buffer) {
							break;
						}
						fprintf(out, "%s", buffer);
					}
				} else {
					printf("--------\n");
				}
			}

			if (state == BODY2) {
				do {
					fprintf(out, "%s", f.value);
					if (!rapid && !sigint) {
						printf("%s", f.value);
					}
				} while ((state = m_getfld2(state, &f, in))
						==BODY2);
				if (state != FILEEOF2) {
					adios(EX_IOERR, "m_getfld2", "io error");
				}
			}

			if (prepend || !qbody) {
				break;
			}

			printf("--------Enter additional text\n");
has_no_body:
			fflush(stdout);
			for (;;) {
				getln(buffer, sizeof(buffer));
				if (!*buffer) {
					break;
				}
				fprintf(out, "%s", buffer);
			}
			break;

		case FMTERR2:
			advise(NULL, "skeleton is poorly formatted");
			continue;
		default:
			adios(EX_IOERR, "m_getfld2", "io error");
		}
		break;
	}

	if (qbody) {
		printf("--------\n");
	}

	fflush(stdout);
	fclose(in);
	fclose(out);
	SIGNAL(SIGINT, SIG_IGN);

	if ((fdi = open(tmpfil, O_RDONLY)) == NOTOK) {
		adios(EX_IOERR, tmpfil, "unable to re-open");
	}
	if ((fdo = creat(drft, m_gmprot())) == NOTOK) {
		adios(EX_IOERR, drft, "unable to write");
	}
	cpydata(fdi, fdo, tmpfil, drft);
	close(fdi);
	close(fdo);
	unlink(tmpfil);

	context_save();
	return EX_OK;
}


int
getln(char *buffer, int n)
{
	int c;
	sig_atomic_t psigint = sigint;
	char *cp;

	cp = buffer;
	*cp = '\0';

	switch (setjmp(sigenv)) {
	case 0:
		wtuser = 1;
		break;

	default:
		wtuser = 0;
		if (sigint == psigint) {
			return 0;
		} else {
			sigint = psigint;
			return NOTOK;
		}
	}

	for (;;) {
		switch (c = getchar()) {
		case EOF:
			clearerr(stdin);
			longjmp(sigenv, DONE);

		case '\n':
			if (cp[-1] == '\\') {
				cp[-1] = c;
				wtuser = 0;
				return 1;
			}
			*cp++ = c;
			*cp = '\0';
			wtuser = 0;
			return 0;

		default:
			if (cp < buffer + n)
				*cp++ = c;
			*cp = '\0';
		}
	}
}


static void
intrser(int i)
{
	if (wtuser) {
		close(STDIN_FILENO);
	}
	sigint++;
}
