/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
 */

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

#define	import_stdio
#define	import_libc
#define	import_fset
#define	import_spp
#include <iraf.h>

#include "config.h"
#include "operand.h"
#include "param.h"
#include "task.h"
#include "eparam.h"
#include "proto.h"


#define	COLWIDTH	40		/* column width for showhelp	*/


/*-------------------------------------------------------------------------
 * EDCAP.C -- Tools to support the edcap utility, used to define the input
 * language of screen editors.
 *
 * External procedures:
 *    edtinit		initialize the editor database and terminal
 *    edtexit		terminate edit mode (may send output to terminal)
 *    host_editor	return host command used to call the named editor
 *    what_cmd		convert escape sequence into editor command
 *    show_editorhelp	print a list of editor keystrokes
 *
 * Internal procedures:
 *    get_editor	open and scan the EDCAP file
 *    cmd_match		search the editor command list for an escape sequence
 *    map_escapes	map encoded escape sequence from EDCAP file
 *
 *-------------------------------------------------------------------------
 */

static	char ed_editorcmd[SZ_LINE+1];
static	void map_escapes(char *input, char *output);


/* EDTINIT -- Initialize the editor.
 */
void
edtinit (void)
{
	register int	i;
	char 	editor[SZ_FNAME];	/* the name of the editor	 */

	/* See if the current editor is the one to use.  If not, get the
	 * editor.ed definitions.
	 */
	if (c_envgets ("editor", editor, SZ_FNAME) > 0)
	    if (strcmp (editor, command[EDITOR_ID].keystroke) != 0)
		get_editor (editor);

	/* Count the number of editor commands.
	 */
	numcommands = FIRST_CMD;
	for (i=FIRST_CMD;  command[i].cmd < NOMORE_COMMANDS;  i++)
	    numcommands++;

	/* Send the initial edit sequence (to enable keypad, if any).
	 */
	if (*(command[EDIT_INIT].escape) != '\0')
	    printf ("%s",command[EDIT_INIT].escape); 

	/* Enable transmission of the screen repaint sequence, to be returned
	 * by the terminal driver if the process is suspended and later
	 * resumed while in raw mode.
	 */
	for (i=FIRST_CMD;  command[i].cmd < NOMORE_COMMANDS;  i++)
	    if (command[i].cmd == REPAINT && strlen(command[i].escape)==1)
		c_fseti ((XINT)STDOUT, F_SETREDRAW, command[i].escape[0]);
}


/* EDTEXIT -- Terminate the editor.  Send an escape sequence to the terminal
 * if necessary.
 */
void
edtexit (void)
{
	c_fseti ((XINT)STDOUT, F_SETREDRAW, 0);
	if (*(command[EDIT_TERM].escape) != '\0')
	    printf ("%s",command[EDIT_TERM].escape); 
}


/* HOST_EDITOR -- Return a pointer to the command string to be sent to the
 * host system to run an editor, given the user name for the editor.
 */
char *
host_editor (char *editor)
{
	get_editor (editor);
	return (ed_editorcmd);
}


/* GET_EDITOR -- Redefine the editor keystrokes from the editor.ed file.
 * Search for that file first in the users home directory.  If not found
 * there, look in the standard device directory.
 */
void
get_editor (
    char *editor		/* the name of the editor		*/
)
{
	FILE	*fp;		/* pointer to the editor.ed file	*/
	char	string[SZ_LINE];/* an edcap string from the .ed file	*/
	char	label[SZ_LINE];	/* the key-sequence label (keyword)	*/
	char	escape[SZ_LINE];/* the escape sequence in c octal	*/
	char	name[SZ_LINE];	/* the keystroke name, for HELP		*/
	char	fname[SZ_PATHNAME];
	int	i, num, n;

	/* Search the directories for the edcap file editor.ed.
	 */
	sprintf (fname, "home$%s.ed", editor);
	fp = fopen (fname, "r");

	if (fp == NULL) { 
	    sprintf (fname, "dev$%s.ed", editor);
	    fp = fopen (fname, "r");

	    if (fp == NULL) {
		eprintf ("cannot find edcap file for `%s'\n", editor);
		eprintf ("editor language defaults to `%s'\n",
		    command[EDITOR_ID].keystroke);
		return;
	    }
	}

	/* Parse the edcap file and initialize the command list and the host
	 * editor command string (default `irafvi', `irafemacs', etc.).
	 */
	sprintf (ed_editorcmd, "iraf%s", editor);
	num = 0;

	while (fgets (string, SZ_LINE, fp) != NULL) {
	    /* Check for the EDITOR_CMD field, the command to be sent to the
	     * host system to run the editor.  This is a special case since
	     * the edcap format does not support anything but keystrokes.
	     * A termcap format file should have been used for this
	     * database, rather than defining a new format file, then this
	     * would not have been necessary.
	     */
	    if (strncmp (string, "EDITOR_CMD", 10) == 0) {
		char    *ip, *op;
		char    delim;
		int	isformat;

		/* Extract the optionally quoted host command format string.
		 * This is either the editor command name (prefix), e.g.,
		 * "irafemacs", or an SPRINTF format string containing a %s
		 * where the filename(s) are to go.
		 */
		for (ip=string+10;  isspace(*ip);  ip++)
		    ;
		delim = (*ip == '"' || *ip == '\'') ? *ip++ : 0;
		for (op=ed_editorcmd, isformat=NO;  (*op = *ip++);  op++) {
		    if ((delim && *op == delim) || (!delim && isspace(*op)))
			break;
		    else if (*op == '%' && *ip == 's')
			isformat++;
		}

		/* If the command string did not contain an embedded %s to
		 * indicate where the file names(s) are to go, add one at
		 * the end, i.e., "... %s".
		 */
		if (!isformat) {
		    *op++ = ' ';
		    *op++ = '%';
		    *op++ = 's';
		}

		*op = EOS;
		continue;
	    }

	    /* Process a normal editor command into the command table.
	     * Each line must contain three tokens, the internal command
	     * name, the terminal escape sequence, and the keystroke name.
	     */
	    n = sscanf (string, "%s %s %s", label, escape, name);

	    if (n == 3) {
		/* Determine which legitimate editor command this is.
		 */
		for (i=0;  i < NUM_COMMANDS;  i++)
		    if (strcmp (label, cmdnames[i]) == 0)
			break;

		/* Stuff the command into the static command buffer.
		 */
		if (i < NUM_COMMANDS) {
		    command[num].cmd = i;
		    map_escapes (escape, label);
		    strncpy (command[num].escape, label, SZ_ESCAPE);
		    strncpy (command[num].keystroke, name, SZ_KEYSTROKE);
		    num++;
		}
	    }
	}

	/* Make sure the command buffer terminates here.
	 */
	command[num].cmd = NOMORE_COMMANDS;
	strcpy (command[num].escape, "");
	strcpy (command[num].keystroke, " ");

	strncpy (command[EDITOR_ID].keystroke, editor, SZ_KEYSTROKE);
	fclose (fp);
}


/* MAP_ESCAPES -- Take an ASCII string which may have escape sequences
 * encoded as octal (\nnn).  Copy the string to the output, replacing
 * the encoded values with the binary character value.  The output
 * string may be the same as the input string.
 *
 * Control codes may be represented in the input in any of the following ways:
 *
 *	^X		control-X
 *	\[befnrt]	backspace, escape, formfeed, newline, return, tab
 *	\nnn		octal constant
 *	\^		the character ^
 *	\\		the character \
 *
 * Ordinary characters are copied to the output.
 */
static void
map_escapes (
    char *input,		/* pointer into input string	*/
    char *output		/* pointer into output string	*/
)
{
	static char	*echars = "befnrt";
	static char	*ecodes = "\b\033\f\n\r\t";
	register char	*ip = input;
	register char	*op = output;
	register int	n;

	while (*ip != '\0') {
	    if (*ip == '\\') { 
		switch (*++ip) {
		case 'b': case 'e': case 'f':
		case 'n': case 'r': case 't':
		    *op++ = ecodes[strchr(echars,*ip++)-echars];
		    break;
		default:
		    if (isdigit (*ip)) {
			for (n=0;  isdigit(*ip) != 0;  ip++)
			     n = n * 8 + (*ip - '0');
			*op++ = n;
		    } else
			*op++ = *ip++;
		}
	    } else if (*ip == '^') {
		ip++;
		*op++ = (*ip++ % 040);
	    } else
		*op++ = *ip++;
	}

	*op = '\0';
}


/* WHAT_CMD -- Determine which editing command has been sent.  Such commands
 * must begin with a non-printable character.  Return the command number or
 * zero if unrecognized.  We are called with the first character of the
 * command (some control code).  Additional keystrokes are read from the
 * standard input until an editor command is recognized.
 */
int
what_cmd (
  int	first_char		/* the first unprintable character */
)
{
	register int	nchars, k;
	char	cmd_string[9];
	char	*cmd;

	cmd = cmd_string;
	*cmd = first_char;

	/* Loop until we get an exact match or until we get no match.
	 * A character is read from the standard input in each pass
	 * through the loop.
	 */
	for (nchars=1;  nchars < 9;  nchars++)
	    if ((k = cmd_match (cmd_string, nchars)) < 0)
		return (0);
	    else if (nchars == strlen (command[k].escape))
		return (command[k].cmd);
	    else
		*(++cmd) = fgetc(stdin);

	return (0);
}


/* CMD_MATCH -- Scan the first nchars of the available commands to see if
 * any match the command string.  Return -1 if the command string does not
 * match any editor escape sequence, else return the index of the first
 * command code matched.
 */
int
cmd_match (
    char *cstring,		/* command string	*/
    int nchars			/* nchars to compare	*/
)
{
	int	k;

	for (k=FIRST_CMD;  k <= numcommands;  k++)
	    if (strncmp (cstring, command[k].escape, nchars) == 0)
		return (k);

	return (-1);
}


/* SHOW_EDITORHELP -- Display the edit commands and their keystroke
 * equivalences.
 */
void
show_editorhelp (void)
{
	char	sbuf[MAX_COMMANDS*COLWIDTH];
	char	*strp[MAX_COMMANDS];
	int	center, maxcols, firstcol, lastcol, nstrs, i;
	int	save_raw;

	maxcols = c_envgeti ("ttyncols");
	center = maxcols / 2;

	/* Disable raw mode output so that output processing will be enabled,
	 * e.g., to map newlines into crlfs.
	 */
	save_raw = c_fstati ((XINT)STDOUT, F_RAW);
	c_fseti ((XINT)STDOUT, F_RAW, NO);

	/* Format the help strings for the individual keystrokes.
	 */
	for (i=FIRST_CMD, nstrs=0;  i <= numcommands;  i++) {
	    if (*(command[i].escape) != '\0') {
		strp[nstrs] = &sbuf[nstrs*COLWIDTH];
		sprintf (strp[nstrs], "%8w%-10.10s = %-11.11s%2w",
		    cmdnames[command[i].cmd], command[i].keystroke);
		nstrs++;
	    }
	}

	e_clear();
	e_goto (center - 7, 1);
	e_putline ("EDIT COMMANDS (");
	e_putline (command[EDITOR_ID].keystroke);
	e_putline (")\n\n");

	/* Sort and output the string table.
	 */
	if (nstrs) {
	    strsort (strp, nstrs);
	    i = strlen (strp[0]);
	    firstcol = center - i - 2;
	    lastcol  = center + i + 2;
	    strtable (stdout, strp, nstrs, firstcol, lastcol, COLWIDTH, 2);
	}

	e_putline ("\n");
	e_ctrl ("so");
	e_putline ("[hit any key to continue]");
	e_ctrl ("se");

	/* Restore raw mode.
	 */
	c_fseti ((XINT)STDOUT, F_RAW, save_raw);

	fflush (stdout);

	/* Pause. */
	fgetc (stdin);
}
