#include "tweak.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#if defined(unix) && !defined(GO32)
#define RCNAME ".tweakrc"
#elif defined(MSDOS)
#define RCNAME "tweak.rc"
#endif

static char *default_rc[] = {
    "# Default "RCNAME" generated by `tweak -D'.",
    "#",
    "# Key bindings: movement keys",
    "bind top-of-file ^[<",
#if defined(unix) && !defined(GO32)
    "bind page-up ^[[5~",
#elif defined(MSDOS)
    "bind page-up ^@I",
    "bind page-up ^@/",
#endif
    "bind page-up ^[V",
    "bind page-up ^[v",
    "bind move-up ^P",
#if defined(unix) && !defined(GO32)
    "bind move-up ^[[A",
#elif defined(MSDOS)
    "bind move-up ^@H",
#endif
    "bind begin-line ^A",
#if defined(unix) && !defined(GO32)
    "bind begin-line ^[[H",
    "bind begin-line ^[[1~",
#elif defined(MSDOS)
    "bind begin-line ^@G",
#endif
    "bind move-left ^B",
#if defined(unix) && !defined(GO32)
    "bind move-left ^[[D",
#elif defined(MSDOS)
    "bind move-left ^@K",
#endif
    "bind move-right ^F",
#if defined(unix) && !defined(GO32)
    "bind move-right ^[[C",
#elif defined(MSDOS)
    "bind move-right ^@M",
#endif
    "bind end-line ^E",
#if defined(unix) && !defined(GO32)
    "bind end-line ^[Ow",
    "bind end-line ^[[4~",
#elif defined(MSDOS)
    "bind end-line ^@O",
#endif
    "bind move-down ^N",
#if defined(unix) && !defined(GO32)
    "bind move-down ^[[B",
#elif defined(MSDOS)
    "bind move-down ^@P",
#endif
    "bind page-down ^V",
#if defined(unix) && !defined(GO32)
    "bind page-down ^[[6~",
#elif defined(MSDOS)
    "bind page-down ^@Q",
#endif
    "bind bottom-of-file ^[>",
    "",
    "# Key bindings: miscellaneous editing keys",
    "bind toggle-insert ^X^I",
#if defined(unix) && !defined(GO32)
    "bind toggle-insert ^[[2~",
#elif defined(MSDOS)
    "bind toggle-insert ^@R",
#endif
    "bind change-mode ^M",
    "bind change-mode ^J",
    "bind quote-next ^Q",
    "bind toggle-status ^XH",
    "bind toggle-status ^Xh",
    "bind toggle-status ^XX",
    "bind toggle-status ^Xx",
    "",
    "# Key bindings: deletion keys",
    "bind delete-left ^?",
    "bind delete-left ^H",
    "bind delete-right ^D",
#if defined(unix) && !defined(GO32)
    "bind delete-right ^[[3~",
#elif defined(MSDOS)
    "bind delete-right ^@S",
#endif
    "",
    "# Key bindings: cut and paste keys",
#if defined(unix) && !defined(GO32)
    "bind mark-place ^@",
#elif defined(MSDOS)
    "bind mark-place ^@^C",
#endif
    "bind cut ^W",
    "bind copy ^[W",
    "bind copy ^[w",
#ifdef MSDOS
    "bind copy ^@^Q",
#endif
    "bind paste ^Y",
    "",
    "# Key bindings: additional movement keys",
    "bind search ^S",
    "bind search-back ^R",
    "bind goto-position ^XG",
    "bind goto-position ^Xg",
    "bind screen-recentre ^L",
    "",
    "# Standard screen size parameters, plus keybindings to alter them",
    "width 16",
    "offset 0",
    "bind new-width ^XW",
    "bind new-width ^Xw",
    "bind new-offset ^XO",
    "bind new-offset ^Xo",
    "",
    "# Key bindings: overall program/file control",
    "bind suspend ^Z",
    "bind exit ^X^C",
    "bind save-file ^X^S",
    "# unbound by default: exit-and-save",
    "",
#ifdef TEST_BUFFER
    "bind diagnostics ^X^D",
    "",
#endif
    "# End of default "RCNAME,
    NULL
};

extern char *pname;

void read_rc (void) {
    FILE *fp;
    char **p, *q, *r, *s, *keyseq;
    char rcbuffer[256];
    char rcname[FILENAME_MAX];
    int lineno = 0;
    int errors = FALSE, errors_here;

#if defined(unix) && !defined(GO32)
    rcname[0] = '\0';
    if (getenv("HOME"))
	strcpy (rcname, getenv("HOME"));
    strcat (rcname, "/.tweakrc");
#elif defined(MSDOS)
    /*
     * Use environment variable TWEAKRC if set. Otherwise, look for
     * TWEAK.RC in the same directory as TWEAK.EXE, if _that_ exists,
     * and failing everything else, try C:\TWEAK\TWEAK.RC.
     */
    if (getenv("TWEAKRC"))
	strcpy (rcname, getenv("TWEAKRC"));
    else {
	if ( (q = strrchr(pname, '\\')) != NULL) {
	    FILE *tempfp;

	    strncpy (rcname, pname, q+1-pname);
	    strcpy (rcname+(q+1-pname), "TWEAK.RC");
	    if ( (tempfp = fopen(rcname, "r")) != NULL)
		fclose (tempfp);
	    else
		strcpy (rcname, "C:\\TWEAK\\TWEAK.RC");
	} else
	    strcpy (rcname, "C:\\TWEAK\\TWEAK.RC");
    }
#endif

    {				       /* easy keybindings: self inserts */
	int i;
	char c;
	for (i=32; i<127; i++) {
	    c = i;
	    bind_key (&c, 1, act_self_ins);
	}
    }

    fp = fopen(rcname, "r");
    p = default_rc;
    for (EVER) {
	if (fp) {
	    if (!fgets(rcbuffer, sizeof(rcbuffer), fp)) {
		fclose (fp);
		break;
	    }
	    rcbuffer[strcspn(rcbuffer, "\r\n")] = '\0';
	} else {
	    if (!*p)
		break;
	    strcpy (rcbuffer, *p++);
	}
	lineno++;
	errors_here = FALSE;

	/*
	 * Now we have a line from the .rc file, wherever it's
	 * really come from. Process it.
	 */
	q = rcbuffer;
	while (*q && isspace((unsigned char)*q))
	    q++;

	if (!*q || *q == '#')
	    continue;		       /* comment or blank line */

	r = q;
	while (*r && !isspace((unsigned char)*r))
	    r++;
	if (*r)
	    *r++ = '\0';

	/*
	 * Now "q" points to the command word, "r" to the rest of
	 * the line.
	 */
	if (!strcmp(q, "bind")) {
	    /*
	     * It's a "bind" directive. The rest of the line should
	     * consist of an action name, then a single whitespace
	     * character, then a key sequence.
	     */
	    keyact action;

	    while (*r && isspace((unsigned char)*r))
		r++;

	    q = r;
	    while (*q && !isspace((unsigned char)*q))
		q++;
	    if (*q)
		*q++ = '\0';
	    else {
		fprintf(stderr, "%s: no key sequence after \"bind\" command"
			" on line %d of "RCNAME, pname, lineno);
		errors = TRUE;
		continue;
	    }

	    /*
	     * "r" points to the action name; "q" to the key sequence.
	     */
	    keyseq = s = q;
	    while (*q) {
		if (*q == '^') {
		    if (!*++q) {
			fprintf(stderr, "%s: nothing follows `^' on line %d"
				" of "RCNAME, pname, lineno);
			errors = TRUE;
			errors_here = TRUE;
		    } else {
			*s++ = *q++ ^ 0x40;
		    }
		} else if (*q == '\\') {
		    if (!*++q) {
			fprintf(stderr, "%s: nothing follows `\\' on line %d"
				" of "RCNAME, pname, lineno);
			errors = TRUE;
			errors_here = TRUE;
		    } else if (*q == '\\' || *q == '^') {
			*s++ = *q++;
		    } else if (isxdigit((unsigned char)*q) &&
			       q[1] && isxdigit((unsigned char)q[1])) {
			char buf[3];
			buf[0] = *q++;
			buf[1] = *q++;
			buf[2] = '\0';
			*s++ = strtol (buf, NULL, 16);
		    } else {
			fprintf(stderr, "%s: badly formed `\\' sequence on"
				" line %d of "RCNAME, pname, lineno);
			errors = TRUE;
			errors_here = TRUE;
		    }
		} else
		    *s++ = *q++;
	    }
	    if (errors_here)
		continue;

	    if (!strcmp(r, "quote-next")) {
		/*
		 * The "quote next" sequence requires special
		 * treatment.
		 */
		int i;

		for (i=0; i<256; i++) {
		    *s = i;
		    bind_key (keyseq, s-keyseq+1, act_self_ins);
		}
	    } else if ( (action = parse_action (r)) ) {
		/*
		 * An ordinary action, requiring ordinary treatment.
		 */
		bind_key (keyseq, s-keyseq, action);
	    } else {
		fprintf(stderr, "%s: unrecognised key action \"%s\""
			" at line %d of "RCNAME"\n",
			pname, r, lineno);
		errors = TRUE;
	    }
	} else if (!strcmp(q, "width")) {
	    width = atoi(r);
	} else if (!strcmp(q, "offset")) {
	    realoffset = atoi(r);
	} else {
	    fprintf(stderr, "%s: unrecognised "RCNAME" directive \"%s\""
		    " at line %d of "RCNAME"\n",
		    pname, q, lineno);
	    errors = TRUE;
	}
    }
    if (errors)
	exit(1);
}

void write_default_rc (void) {
    char **p;

    for (p = default_rc; *p; p++)
	puts (*p);
}
