/************************************************************************
 * This program is Copyright (C) 1986 by Jonathan Payne.  JOVE is	*
 * provided to you without charge, and with no warranty.  You may give	*
 * away copies of JOVE, including sources, provided that this notice is *
 * included in all the files.						*
 ************************************************************************/
/*+++*
 *  title:	atari_st.c
 *  abstract:	Atari-ST specific stuff for JOVE
 *  author:	T.R.Hageman, Groningen, The Netherlands.
 *  created:	may 1989
 *  modified:
 *		30-Jul-89, add <shift>Fn keys
 *		11-Sep-89, get vdi handle the "official" way
 *		14-Oct-89, revision of getchar(), Fkey assignments
 *		18-Feb-90, initial delay in DoMouse; add COLOR option;
 *			interchange Fn and kn keys; add window resize
 *			at Alt-mouse drag of mode line.
 *		26-Feb-90, interchange K2 and K3 to follow terminfo def.
 *		 8-Mar-90, change keypad key bindings considerably.
 *		17-Mar-90, Oops! GEMDOS input does not work for TOS 1.4
 *			so switch to BIOS input.
 *		24-Mar-90, move Mouse stuff to separate module.
 *		14-Oct-90, fix bug in SIGINT handling
 *		11-Nov-90, optional GEMINI console-window support.
 *		11-Jan-91, add PROC_TYPEOUT support,
 *			revised NATIONAL keyboard support.
 *		07-May-91, revise I/O redirection in ShellToBuf
 *		04-Jun-91, add `dense' screen mode setting.
 *		18-Jun-91, add FILESELECTOR option.
 *		20-Jun-91, fix bug in ShellToBuf that ate up file descriptors.
 *		26-Jun-91, replace ShellToBuf with UnixToBuf to let proc.c
 *			handle the system-independent part.
 *		15-Jul-91, get colors right (at last).
 *		01-Aug-91, redefine F10 as "k;" instead of "k0"
 *  description:
 *	this file complements jove.c, proc.c, misc.c and tty.c
 *	and completely replaces fkeys.c and term.c
 *---*/

#include "tune.h"

#ifdef ATARIST

RCS("$Id: atari_st.c,v 14.32.0.9 1994/01/06 00:13:15 tom Exp tom $")

#if __TURBOC__
#   include <vdi.h>
#   include <aes.h>
    #pragma warn -par
#   define GEM_VERSION		(_GemParBlk.global[0])
#else
# ifdef __GNUC__
#   include <vdibind.h>
#   define __NO_MFDB__		/* MFDB definition conflicts with <linea.h> */
#   include <aesbind.h>
    extern unsigned short _global[];	/* This was missing from <gemfast.h> */
#   define GEM_VERSION		(_global[0])
# else /* assume Lattice C */
#   include <gemlib.h>
#   define Int	short
# endif
#endif

#ifndef Int			/* Int type for vdi arrays */
#   define Int	int
#endif

#ifndef GEM_VERSION
#   define GEM_VERSION	0
#endif

#define FSEL_EXINPUT_AVAILABLE	(GEM_VERSION >= 0x130)

#include <linea.h>

#ifndef __GNUC__
#   if __TURBOC__ /* adapt to Gnu-C naming conventions for variables we need. */
#	define linea0		linea_init
#	define M_HID_CT		(Vdiesc->m_hid_ct)
#	define V_CEL_HT		(Vdiesc->v_cel_ht)
#	define V_CEL_WR		(Vdiesc->v_cel_wr)
#	define V_CEL_MX		(Vdiesc->v_cel_mx)
#	define V_CEL_MY		(Vdiesc->v_cel_my)
#	define V_X_MAX		(Vdiesc->v_rez_hz)
#	define V_Y_MAX		(Vdiesc->v_rez_vt)
#	define V_FNT_WR		(Vdiesc->v_fnt_wd)
#	define V_FNT_ST		(Vdiesc->v_fnt_st)
#	define V_FNT_ND		(Vdiesc->v_fnt_nd)
#	define V_OFF_AD		(Vdiesc->v_off_ad)
#	define V_FNT_AD		(Vdiesc->v_fnt_ad)
#	define FONT_RING	(Vdiesc->font_ring)
#	define DEF_FONT		(Vdiesc->def_font)
	/* Line-A variables */
#	define VPLANES		(Linea->v_planes)
#	define VWRAP		(Linea->v_lin_wr)
	/* FONT fields */
#	define __FONT		FONT_HDR
#	define font_id		id
#	define first_ade	ade_lo
#	define last_ade		ade_hi
#	define max_cell_width	wcel_wdt
#	define off_table	ch_ofst
#	define dat_table	fnt_dta
#	define form_width	frm_wdt
#	define form_height	frm_hgt
#   else /* assume Lattice C */
#	define M_HID_CT		(((short *)la_info.li_a0)[-299])
	/* etc... I don't really support Lattice C anymore. */
#   endif
#endif

#if (!__TURBOC__)
/* {{[TRH] This really should be in a system header file!}} */

/* system variable _sysbase (0x4F2L) points to next structure         */
typedef struct _syshdr
{
    unsigned short os_entry;   /* $00 BRA to reset handler             */
    unsigned short os_version; /* $02 TOS version number               */
    void           *os_start;  /* $04 -> reset handler                 */
    struct _syshdr *os_base;   /* $08 -> baseof OS                     */
    void           *os_membot; /* $0c -> end BIOS/GEMDOS/VDI ram usage */
    void           *os_rsv1;   /* $10 << unused,reserved >>            */
    long           *os_magic;  /* $14 -> GEM memoryusage parm. block   */
    long           os_gendat;  /* $18 Date of system build($MMDDYYYY)  */
    short          os_palmode; /* $1c OS configuration bits            */
    short          os_gendatg; /* $1e DOS-format date of systembuild   */
/*
    The next three fields are only available in TOS versions 1.2 and
    greater
*/
    void           *_root;     /* $20 -> base of OS pool               */
    long           *kbshift;   /* $24 -> keyboard shift state variable */
    long           **_run;     /* $28 -> GEMDOS PID of current process */
    void           *p_rsv2;    /* $2c << unused, reserved >>           */
} SYSHDR;
#endif

/* the following definitions conflict with GEM stuff. */
#undef BLACK
#undef RED
#undef GREEN
#undef YELLOW
#undef BLUE
#undef MAGENTA
#undef CYAN
#undef WHITE

#undef UPARROW

#include "jove.h"
#include "ctype.h"
#include "io.h"
#include "process.h"
#include "screen.h"
#include "termcap.h"
#include <osbind.h>
#undef CLK_TCK
#include <time.h>

#ifndef SIG_SYS
#   define SIG_SYS	SIG_DFL
#endif

#ifdef RESHAPING
private int	set_font __(( __FONT *_(font) ));
private __FONT	*org_font;

#   define FONT_SELECT(dense)	((dense) ? ((__FONT **)FONT_RING)[1] : DEF_FONT)
	/* Stupid cast necessary due to type mismatch in gnu's <linea.h> */

#   define FONT_INIT()	org_font = FONT_SELECT(DEF_FONT->form_height != V_CEL_HT)
#   define FONT_EXIT()	{ if (set_font(org_font)) ttsize(), curstoLL(), printf("\n"); }
#else
#   define FONT_INIT()
#   define FONT_EXIT()
#endif

#ifdef MOUSE
private int	mouse_initially_hidden;
#endif

#define CONSOLE	2		/* BIOS console device */

DEF_INT( "alternate-path-separator", AltSlash, V_CHAR) _IF(def ATARIST) ZERO;

/*======================================================================*/
/*				SCREEN.C				*/
/*======================================================================*/

private int	SCR_HANDLE;		/* VDI handle of screen */

private Int	scr_width,
		scr_height,
		char_width,
		char_height;

#ifndef GEMINI
#   define	scr_xorg	0
#   define	scr_yorg	0
#   define	clip_flag	OFF
#   define	clip_rect	((Int *)genbuf)	/* dummy */
#else
private Int	scr_xorg,
		scr_yorg,
		clip_flag,
		clip_rect[2][2];
#endif

#ifdef FAST_IDLINE
/*
 * in {ins,del}line, don't use a raster op. to clear the lines since I
 * found that mildly annoying to my eyes...
 * {{we cannot use v_clear() here since we do not know the state of
 *   the screen image at this point...}}
 */
private void cl_lines __(( int _(top), int _(num) ));
private void
cl_lines(top, num)
{
	while (--num >= 0) {
		Placur(top++, 0);
		putp(CE);
	}
}

private void scr_op __(( int _(opcode), Int *_(pxy) ));
private void
scr_op(opcode, pxy)
int	opcode;
Int	*pxy;
{
	MFDB	 	scr_mfdb;

	scr_mfdb.fd_addr = 0;	/* so we use the screen */
	vro_cpyfm(SCR_HANDLE, opcode, pxy, &scr_mfdb, &scr_mfdb);
}

private void STcpy_lines __(( int _(src), int _(dest), int _(num) ));
private void
STcpy_lines(src, dest, num)
{
	Int		pxy[8];
	register Int	*p = pxy;
	register int	cheight = char_height;

	/* convert lines -> pixels */
	src  *= cheight;
	dest *= cheight;
	num  *= cheight;
	num--;

	/* source rectangle (x,y)upper left, (x,y)lower right, resp. */
	*p++ = scr_xorg + 0;
	*p++ = scr_yorg + src;
	*p++ = scr_xorg + scr_width;
	*p++ = scr_yorg + src + num;
	/* dest rectangle (x,y)upper left, (x,y)lower right, resp. */
	*p++ = scr_xorg + 0;
	*p++ = scr_yorg + dest;
	*p++ = scr_xorg + scr_width;
	*p++ = scr_yorg + dest + num;
	scr_op(S_ONLY, pxy);
}

private void STins_line __(( int _(top), int _(bottom), int _(num) ));
private void
STins_line(top, bottom, num)
{
	bottom++;
	STcpy_lines(top, (top + num), bottom - (top + num));
	cl_lines(top, num);
}

private void STdel_line __(( int _(top), int _(bottom), int _(num) ));
private void
STdel_line(top, bottom, num)
{
	bottom++;
	STcpy_lines((top + num), top, bottom - (top + num));
	cl_lines(bottom - num, num);
}

#   ifdef GEMINI

#	define Nputpad(cap, M_cap, num, nlines)	{	\
		register int i = num;			\
		do putp(cap); while (--i);		\
	}

/* the following is ripped off from GEN{i,d}_lines. We need it in case we're
   running in the GEMINI console window when it is extending off the bottom
   of the screen.  In that particular case we cannot use our fast bitblt
   scroll since we're not sure that the information to copy actually exists. */

private void SLOWins_line __(( int _(top), int _(bottom), int _(num) ));
private void
SLOWins_line(top, bottom, num)
{
	Placur(bottom + 1 - num, 0);
	Nputpad(DL, M_DL, num, ILI - bottom);
	Placur(top, 0);
	Nputpad(AL, M_AL, num, ILI - top);
}

private void SLOWdel_line __(( int _(top), int _(bottom), int _(num) ));
private void
SLOWdel_line(top, bottom, num)
{
	Placur(top, 0);
	Nputpad(DL, M_DL, num, ILI - top);
	Placur(bottom + 1 - num, 0);
	Nputpad(AL, M_AL, num, ILI - bottom);
}
#   endif /* GEMINI */

void
IDline_setup(tname)
const char *tname;
{
#   ifdef GEMINI
	/* We can't use fast scrolling if window extends off the bottom of
	   the screen. */

	if (scr_yorg + scr_height > clip_rect[1][1]) {
		TTins_line = SLOWins_line;
		TTdel_line = SLOWdel_line;
		ScrollLimit = 50;
		return;
	}
#   endif
	TTins_line = STins_line;
	TTdel_line = STdel_line;

	ScrollLimit = 100;
}

/* show a visual bell */

#ifdef FAST_FLASH
void
flash()
{
	Int		pxy[8];
	register Int	*p = pxy;

	/* set src and dest. rectangle to entire screen */
	*p++ = scr_xorg + 0;
	*p++ = scr_yorg + 0;
	*p++ = scr_xorg + scr_width;
	*p++ = scr_yorg + scr_height;
	*p++ = scr_xorg + 0;
	*p++ = scr_yorg + 0;
	*p++ = scr_xorg + scr_width;
	*p++ = scr_yorg + scr_height;
	cursoff();
	scr_op(D_INVERT, pxy);
	DoSit(1);
	scr_op(D_INVERT, pxy);
}
#endif /* FAST_FLASH */

#endif /* FAST_IDLINE */

#ifdef COLOR
/*
 * note: vt52 emulator uses palette indices, NOT the VDI definitions
 * as I originally thought (so much for ATARIs documentation).
 * The default (low rez) palette colors are as follows:
 *  0 white	 4 blue		 8 dark grey	12 light magenta
 *  1 red	 5 green	 9 light cyan	13 light yellow
 *  2 green	 6 dark blue	10 dark cyan	14 dark magenta
 *  3 brown	 7 light gray	11 dark yellow	15 black
 * (medium rez: 4 palettes, where palette 3 == black)
 */
#   define ST_BLACK	15
#   define ST_RED	 1
#   define ST_GREEN	 2
#   define ST_YELLOW	13	/* or 11 */
#   define ST_BLUE	 4	/* or  6 */
#   define ST_MAGENTA	14	/* or 12 */
#   define ST_CYAN	10	/* or  9 */
#   define ST_WHITE	 0

private int	color_mask;

private const char colormap[NCOLORS] = {
	ST_BLACK,	ST_RED,		ST_GREEN,	ST_YELLOW,
	ST_BLUE,	ST_MAGENTA,	ST_CYAN,	ST_WHITE
};
private char	prev_color[2] = { -1, -1 };

void
set_color(color)
{
	register int	fg_color,
			bg_color;

	if (color == CurrColor)
		return;

	if (False(UseColor))
		return;

	if (CurrColor < 0)		/* fool optimization */
		prev_color[FG] = prev_color[BG] = -1;

	CurrColor = color;

	if (color_mask <= 1)	/* i.e. monochrome */
		return;

	fg_color = FG_COLOR(color);
	bg_color = BG_COLOR(color);

	if (color_mask < NCOLORS) {
		/* medium resolution: only 4 colors */
		static const char remap[] = {
			BLACK, RED, GREEN, WHITE, GREEN, RED, GREEN, WHITE
		};
		fg_color = remap[fg_color];
		bg_color = remap[bg_color];
	}
	fg_color = colormap[fg_color];
	bg_color = colormap[bg_color];

	if (fg_color == bg_color)	/* make sure it's different */
		bg_color ^= color_mask;	/* chooses its complementary color */

	if (prev_color[FG] != (char) fg_color) {
		prev_color[FG] = fg_color;
		printf("\33b%c", fg_color);
	}
	if (prev_color[BG] != (char) bg_color) {
		prev_color[BG] = bg_color;
		printf("\33c%c", bg_color);
	}
}
#endif /* COLOR */

private void scr_inval __(( void ));
private void
scr_inval()
{
	register int	i = ILI;

	do {
		i_set(i, 0);
		v_inval();
	} while (--i >= 0);
}

/*======================================================================*/
/*				TERM.C					*/
/*======================================================================*/
/*
 * Hardwired terminal capabilities, so we don't need termcap database
 */

#ifdef GEMINI
#   define XXXX(a,b,c,d)	((long)((a)<<8|(b))<<16|(c)<<8|(d))

private int inGEMINI __(( void ));
private int
inGEMINI()
{
	long	sav_ssp = Super(0L);
	long	*_shell_p = *(long **) 0x4f6;
	int	result = (_shell_p &&
			  _shell_p[-3] == XXXX('X','B','R','A') &&
			  _shell_p[-2] == XXXX('G','M','N','I'));
	Super(sav_ssp);
	return result;
}
#   undef XXXX
#endif	/* GEMINI */

void
getTERM()
{
	Int		work[57];
	int		dum;
	register char	*t = getenv("TERM");

	if (t == NULL)
		t = "atari";
	TermName = t;
	/*
	 * init SCR_HANDLE here because we "know" that getTERM is called (once)
	 * in main before anything else that will need it ...
	 */
	appl_init();
	SCR_HANDLE = graf_handle(&dum, &dum, &dum, &dum);

	vq_extnd(SCR_HANDLE, 0, work);
	scr_width = work[0];		/* well actually {width,height} - 1 */
	scr_height = work[1];
#ifdef COLOR
	color_mask = (work[13] - 1) & 0xF;	/* nr. of displayable colors */
						/* (assumed a power of 2) */
	if (color_mask > 1)
		UseColor++;
#endif
#ifdef GEMINI
	clip_rect[1][0] = scr_width;
	clip_rect[1][1] = scr_height;

	if (inGEMINI()) {	/* we're running under GEMINI? */
		Int	top_window;
#   if __TURBOC__
#	define _0
#   else
		Int	dummy;
#	define _0	, &dummy
#   endif
		/*
		 * check if we're running in the console window.  If we
		 * are, the console window will be TOPped, and we should use
		 * the dimensions of its work area as our "screen" size.
		 */
		if (!((t = getenv("JOVE_TTP")) && sindex("W:N", t)) &&
		    wind_get(0, WF_TOP, &top_window _0 _0 _0) && top_window &&
		    wind_get(top_window, WF_WORKXYWH,
			     &scr_xorg, &scr_yorg, &scr_width, &scr_height)) {

			clip_rect[0][0] = scr_xorg;
			clip_rect[0][1] = scr_yorg;
			scr_width -= 1;
			scr_height -= 1;

			/* enable clipping if window is partially off-screen */

			if (scr_xorg + scr_width > clip_rect[1][0] ||
			    scr_yorg + scr_height > clip_rect[1][1])
				clip_flag = YES;
		}
	}
#endif /* GEMINI */

	linea0();
	FONT_INIT();
#ifdef MOUSE
	mouse_initially_hidden = M_HID_CT;
#endif
	MetaKey = ESC;
	CanScroll = YES;
	IDline_setup(TermName);
}

/*======================================================================*/
/*				FKEYS.C					*/
/*======================================================================*/

/* These are already defined in <aes.h> / <gemlib.h> ...
#define K_RSHIFT	0x01
#define K_LSHIFT	0x02
#define K_CTRL		0x04
#define K_ALT		0x08
*/
#define K_SHIFT		(K_LSHIFT|K_RSHIFT)
#define K_CAPS		0x10

#define CON_META	0x08

#define SCAN_1		0x02	/* scan code of [1!] key */
#define SCAN_ALT_1	0x78	/* scan code of ALT-[1!] key */

#define PAD		0100	/* "keypad" flag in Fkey table */

/*
 * character codes >= 0200 are used to define function keys.
 */
enum FKEYS {
	ku=0200,kd,	kr,	kl,	k0,	k1,	k2,	k3,
	k4,	k5,	k6,	k7,	k8,	k9,	K1,	K2,
	K3,	K4,	K5,	kq,	kF,	kH,	kI,	kN,
	kP,	kR,	kh,	F1,	F2,	F3,	F4,	F5,
	F6,	F7,	F8,	F9,	F0,	kY,	kt,	kT,
	ka,	kA,	kz,	kZ,	k_,	ML,	MR
};

/*
 * The next table is indexed with the keyboard scan code to determine if
 * it was a function key. The keyboard (scancode) layout is as follows:
 *
 *	3B /3C /3D /3E /3F /40 /41 /42 /43 /44 /
 *    [	54 /55 /56 /57 /58 /59 /5A /5B /5C /5D / ] shifted
 *
 *	 01  02 03 04 05 06 07 08 09 0A 0B 0C 0D 29  0E  62    61  63 64 65 66
 *	 0F   10 11 12 13 14 15 16 17 18 19 1A 1B    53  52 48 47  67 68 69 4A
 *	[1D ]  1E 1F 20 21 22 23 24 25 26 27 28   1C 2B  4B 50 4D  6A 6B 6C 4E
 *	[2A](60)2C 2D 2E 2F 30 31 32 33 34 35 [ 36]                6D 6E 6F
 *	    [ 38]  ----------39----------   [ 3A]                    70  71 72
 *
 *	(Note that US keyboards don't have key 60).
 *
 * These are assigned the following FKEY codes:
 *
 *	k1 /k2 /k3 /k4 /k5 /k6 /k7 /k8 /k9 /k; /
 *    [	F1 /F2 /F3 /F4 /F5 /F6 /F7 /F8 /F9 /F0 / ] shifted
 *
 *	 Esc  1  2  3  4  5  6  7  8  9  0  -  =  ` Bsp  kq    kY  k1 k2 k3 k4
 *	 Tab   q  w  e  r  t  y  u  i  o  p  [  ]   Del  kI ku kh  K1 kP K3 kF
 *	 Ctrl   a  s  d  f  g  h  j  k  l  ;  '  Ret  #  kl kd kr  kt K2 kT kR
 *	 Shift \ z  x  c  v  b  n  m  ,  .  /  Shift               K4 kN K5
 *	     Alt   --------Space---------   Caps                     k0  ka kA
 *
 * Some special codes:
 *
 * Key combination	scancode (hex)	action
 * ---------------	--------	------
 * [Ctrl] + ClrHome	77		= kH
 *	  + LeftArrow	73		= kz
 *	  + Rightarrow	74		= kZ
 * [Alt]  + 1-9,-,=	78-83		adjust scancode in getchar()
 *
 * pseudo-codes ML and MR are used to hook up the mouse-handler for left
 * and right buttons. Scan 0x74 and 0x75 are sometimes generated by the mouse
 * buttons (for reasons unknown to me).
 * A zero entry means use the "standard" character code (see getchar()).
 * Keys on the numeric keypad have the PAD flag ORed in.
 * These are regarded as function keys only if "use-function-keys" is on.
 */

private const unsigned char
FKeyTable[] = {
/*00*/	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
/*10*/	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
/*20*/	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
/*30*/	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	k1,	k2,	k3,	k4,	k5,
/*40*/	k6,	k7,	k8,	k9,	k_,	0,	0,	kh,
	ku,	0,	kF|PAD,	kl,	0,	kr,	kR|PAD,	0,
/*50*/	kd,	0,	kI,	0,	F1,	F2,	F3,	F4,
	F5,	F6,	F7,	F8,	F9,	F0,	0,	0,
/*60*/	0,	kY,	kq,	k1|PAD,	k2|PAD,	k3|PAD,	k4|PAD,	K1|PAD,
	kP|PAD,	K3|PAD,	kt|PAD,	K2|PAD,	kT|PAD,	K4|PAD,	kN|PAD,	K5|PAD,
/*70*/	k0|PAD,	ka|PAD,	kA|PAD,	kz,	kZ,	0,	0,	kH
	/* scan codes >= 0x78 are adjusted */
};

/*
 * This is the function key name table.
 * All (macro) references to function keys are by these names,
 * to make it independent of the actual code of the function key
 * (which vary from machine to machine)
 * {{warning: the byte order in the XX macro is processor-dependent}}
 */

#define XX(a,b)	(a<<8|b)

short	FKeyMap[NFKEYS] = {
	XX('k','u'),	XX('k','d'),	XX('k','r'),	XX('k','l'),
	XX('k','0'), 	XX('k','1'),	XX('k','2'),	XX('k','3'),
	XX('k','4'),	XX('k','5'),	XX('k','6'),	XX('k','7'),
	XX('k','8'),	XX('k','9'),	XX('K','1'),	XX('K','2'),
	XX('K','3'),	XX('K','4'),	XX('K','5'),	XX('k','q'),
	XX('k','F'),	XX('k','H'),	XX('k','I'),	XX('k','N'),
	XX('k','P'),	XX('k','R'),	XX('k','h'),	XX('F','1'),
	XX('F','2'),	XX('F','3'),	XX('F','4'),	XX('F','5'),
	XX('F','6'),	XX('F','7'),	XX('F','8'),	XX('F','9'),
	XX('F','0'),	XX('k','Y'),	XX('k','t'),	XX('k','T'),
	XX('k','a'),	XX('k','A'),	XX('k','z'),	XX('k','Z'),
	XX('k',';'),	XX('M','L'),	XX('M','R')
};

/*
 * "Foreign" i.e. non-US/UK keyboards use ALT-key combinations
 * to get some punctuation characters. This macro tests for these
 * characters. {{Note that we only need this if the Alt-flag is still
 * left up after manipulation by the foreign keyboard handler.
 * Which is something I should check as soon as I get access to
 * a foreign ST}}
 */

#define ISFOREIGN(c)	(Foreign && index(Foreign, c))
/* (use index() since the number of exceptions is usually rather small.) */

private const char *Foreign ZERO;

/*
 * `Foreign' is initialized from the following table, which is indexed
 * by the language code from the OS header.  Its entries are the ASCII
 * codes of all characters that are generated with some ALT key combination.
 * {{I could only get the documentation for a limited number of countries,
 *  but extension of this table is simple -- once you know the codes.}}
 */

#define UNKNOWN	NULL	/* placeholder for unknown entries */

private const char * const LanguageSpecials[] = {
	NULL,			/*  0 USA */
	"[]{}\\@",		/*  1 Germany */
	"[]{}\\@~",		/*  2 France */
	NULL,			/*  3 Great Britain */
	"[]{}#@\x81\x9A",	/*  4 Spain */
	UNKNOWN,		/*  5 Italy */
	"[]{}~`",	/*?*/	/*  6 Sweden */
	"[]{}\\@~", 	/*???*/	/*  7 Swiss-french */
	"[]{}\\@", 	/*???*/	/*  8 Swiss-german */
	UNKNOWN,		/*  9 Turkey */
	UNKNOWN,		/* 10 Finland */
	"[]{}~`",	/*??*/	/* 11 Norway */
	"[]{}~`",	/*??*/	/* 12 Denmark */
				/* 13 Saudi Arabia */
				/* ... */
};

#define _sysbase	(*(SYSHDR **) 0x4f2)

#define LANG_INIT() \
{								\
	long	sav_ssp = Super(0L);				\
	int	lang = _sysbase->os_palmode >> 1;		\
			/* shift off PAL/NSTC mode bit */	\
	Super(sav_ssp);						\
	if (lang < sizeof(LanguageSpecials) / sizeof(char *))	\
		Foreign = LanguageSpecials[lang];		\
}

/* mouse stuff */

#ifdef MOUSE
/*
 * INIT: make sure mouse is invisible and has arrow shape;
 * EXIT: turn mouse back on for dumb shells like Menu+
 */
#   define INIT_MOUSE()			(mouse_on++, graf_mouse(ARROW, NULL))
#   define EXIT_MOUSE()			{if(!mouse_initially_hidden) mouseon();}
#   define SHOW_MOUSE_CURSOR()		v_show_c(SCR_HANDLE, 0)
#   define HIDE_MOUSE_CURSOR()		v_hide_c(SCR_HANDLE)
#   define ALT_KEY_DEPRESSED()		(Kbshift(-1) & K_ALT)
#   define LEFT_BUTTON	1
#   ifndef GEMINI
#	define GET_MOUSE_STATE(b,x,y)	vq_mouse(SCR_HANDLE, b, x, y)
#   else
#	define GET_MOUSE_STATE(b,x,y) {		\
		vq_mouse(SCR_HANDLE, b, x, y);	\
		if ((*(x) -= scr_xorg) < 0)	\
			*(x) = 0;		\
		else if (*(x) > scr_width)	\
			*(x) = scr_width;	\
		if ((*(y) -= scr_yorg) < 0)	\
			*(y) = 0;		\
		else if (*(y) > scr_height)	\
			*(y) = scr_height;	\
	}
#   endif
#   define MOUSE_BITES_CURSOR

#   include "mouse.ci"

#endif /* MOUSE */

/*======================================================================*/
/*				TTY.C					*/
/*======================================================================*/

void
ttsize()
{
	Int	rows, cols;

	vq_chcells(SCR_HANDLE, &rows, &cols);

	char_width = (scr_width + 1) / cols;
	char_height = (scr_height + 1) / rows;

	vs_clip(SCR_HANDLE, clip_flag, (Int *) clip_rect);

	CO = cols;
	LI = rows;
	ILI = LI - 1;
}

typedef struct {
	char	*normal,
		*shift,
		*caps;
} KEYTBL;

typedef struct {
	char	sg_flags;
	KEYTBL	sg_keytbl;
} SGTTY;

private SGTTY	sg[2];

#define conterm	(*(char *) 0x484)

void
do_sgtty()
{
	register long	sav_sp = Super(0L);

	sg[OFF].sg_flags = conterm;
	Super(sav_sp);

	sg[OFF].sg_keytbl = *(KEYTBL *) Keytbl((char *)-1, (char *)-1, (char *)-1);

	sg[ON] = sg[OFF];
	sg[ON].sg_flags |= CON_META;
}

extern int	done_ttinit;

void
ttinit()
{
	LANG_INIT();
	do_sgtty();
	done_ttinit = YES;
}

/* If n is OFF reset to original modes */

void
ttyset(on)
register int	on;
{
	register long	sav_sp;

	if (!done_ttinit)	/* Try to reset before we've set! */
		return;

	if (on)  {
		stdout->f_flags |= F_BINARY;
	} else {
		stdout->f_flags &= ~F_BINARY;
	}
	sav_sp = Super(0L);
	conterm = sg[on].sg_flags;
	Super(sav_sp);
}

void
ttexit()
{
	signal(SIGINT, SIG_SYS);	/* to de-install signal handler */
	FONT_EXIT();
	appl_exit();
}


#ifndef CLK_TCK
#   ifdef CLOCKS_PER_SEC /* The final decision of the ANSI committee... */
#	define CLK_TCK	CLOCKS_PER_SEC
#   else
/*
 * provide clock support, if not yet in <time.h>.
 */
#	define CLK_TCK	200

typedef	unsigned long	clock_t;

#	define _hz_200	(*(clock_t *) 0x4ba)	/* system clock */

clock_t clock __(( void ));
clock_t
clock()
{
	register long		sav_sp;
	register clock_t	ticks;

	sav_sp = Super(0L);
	ticks = _hz_200;
	Super(sav_sp);

	return ticks;
}
#   endif
#endif

/*
 * Emulate alarm()
 */
private sig_tp	(*alarm_proc)__((int _(sig))) = SIG_DFL;
private clock_t	next_alarm;

ALARM_T
alarm(seconds)
register unsigned	seconds;
{
	register clock_t	now = clock(),
				next = next_alarm;

	next_alarm = (seconds) ? now + seconds * CLK_TCK : 0;

	return (unsigned)(now >= next ? 0 : (next - now + CLK_TCK - 1) / CLK_TCK);
}

int	CapsLock;

/*
 * check for alarms in a very regularly called routine.
 * Also monitor CapsLock state from here (for indicator in mode line).
 */
private int kbhit __(( void ));
private int
kbhit()
{
	int	prev = CapsLock;

	if ((CapsLock = Kbshift(-1) & K_CAPS) != prev) {
		updmodline();
		redisplay();
	}
	if (next_alarm && (clock() >= next_alarm)) {
		next_alarm = 0;
		if (alarm_proc != SIG_DFL && alarm_proc != SIG_IGN)
			(*alarm_proc)(SIGALRM);
	}
	return Bconstat(CONSOLE);	/* was: Cconis() */
}

private void	(*org_pterm)__((void)) = (void (*)()) -1;
private sig_tp	(*sigint_proc)__((int _(sig))) = SIG_SYS;

#define PTERM_EXC	0x102

private void do_sigint __(( void ));
private void
do_sigint()
{
	int	dummy;

	Super(&dummy);				/* back to user mode */
	(*signal(SIGINT, SIG_SYS))(SIGINT);	/* to re-install org_pterm */
}

/*
 * Emulate signal()
 * currently this only handles SIGALRM and SIGINT requests; others are ignored.
 * SIGINT in fact handles all traps that use the default trap handler,
 * by replacing GEMDOS termination vector,
 * so SIGINT signal MUST be reset to SIG_SYS before exiting.
 * {{The alternative (for Turbo C) would be to use the stdlib's signal() and
 *  use raise() to emulate SIGALRM.  However, this is not available for
 *  TC versions before 2.0, and even v2.03 is not satisfactory as it hardwires
 *  the interrupt character to ^D.}}
 */
sig_tp (*signal(sig, proc))()
int	sig;
sig_tp	(*proc)__((int _(sig)));
{
	register sig_tp	(*prev)();

	switch (sig) {
	case SIGALRM:
		prev = alarm_proc;
		alarm_proc = proc;
		break;

	case SIGINT:
		prev = sigint_proc;
		sigint_proc = proc;
		proc = Setexc(PTERM_EXC, (proc == SIG_SYS || proc == SIG_IGN ||
					  proc == SIG_DFL) ?
							org_pterm : do_sigint);
		if (org_pterm == (void (*)()) -1)
			org_pterm = (void (*)()) proc;
		break;

	default:
		prev = SIG_DFL;
		break;
	}
	return prev;
}

#if __TURBOC__
void
signal_mode(inhibit) { /* dummy; This is used internally in TC library */ }
#endif

private int	peekc = -1;

int
getchar()
{
	register long	rawc;
	register int	c,
			scancode,
			mode;

	/* slowpoke timeout is now handled here. */
	extern void	(*timeout_proc)__(( void ));
	clock_t		time_out = clock() + ALARM_1_SEC * CLK_TCK;

	if ((c = peekc) >= 0) {
		peekc = -1;
		return c;
	}

	while (!kbhit()) {		/* wait for character */

		if (timeout_proc && clock() >= time_out)
			(*timeout_proc)();
#ifdef MOUSE
		switch (mouse()) {		/* or mouse click */
		case 0:
			continue;

		case LEFT_BUTTON:
			return ML;

		default:		/* right (or middle, if any) button */
			return MR;
		}
#endif
	}
#ifdef MOUSE
	mouseoff();
#endif

	rawc = Bconin(CONSOLE);		/* was: Cnecin() */
	scancode = rawc >> 16;
	mode = scancode >> 8;
	scancode &= 0xff;

	if (mode & K_ALT) {				/* ALT-key */
		/*
		 * for some obscure reasons (well MS-DOS compatibility...)
		 * the scancode of the upper row (digits, -, =) is way off
		 * in combination with Alternate.  So adjust.
		 */
		if (scancode >= SCAN_ALT_1)
			scancode += SCAN_1 - SCAN_ALT_1;
		c = (short) rawc;
		/*
		 * "Foreign" i.e. non-US/UK keyboards use ALT-key combinations
		 * to get some punctuation characters. So don't disturb them.
		 */
		if (ISFOREIGN(c)) {
			if (c >= 0200)
				return peekc = c, QuoteChar;
			return c;
		}
		/*
		 * due to a peculiarity in TOS (MessDOS compatibility
		 * strikes times and again!) the character code is
		 * unreliable (i.e. sometimes 0) in combination with
		 * the ALTERNATE key.  So get it ourselves. (also when
		 * MetaKey is enabled, to avoid unexpected results when
		 * an extended keyboard handler eg. KBEXT is installed.)
		 */
		if (c == '\0' || MetaKey) {
			c = ((mode & K_CAPS) ? sg[ON].sg_keytbl.caps :
			     (mode &(K_SHIFT|K_CTRL)) ? sg[ON].sg_keytbl.shift :
			     sg[ON].sg_keytbl.normal)[scancode];
			if (mode & K_CTRL)
				c &= 037;
		}

		if (!MetaKey)
			return peekc = (c | 0200), QuoteChar;

		rawc = c;	/* remember... */

		if ((c = FKeyTable[scancode]) == 0 ||
		    ((c & PAD) && ((c &= ~PAD), False(UseKeyPad))))
			if ((c = (short) rawc) >= 0200)
				return peekc = c, QuoteChar;

		return peekc = c, MetaKey;
	}
	if ((c = FKeyTable[scancode]) == 0 ||
	    ((c & PAD) && ((c &= ~PAD), False(UseKeyPad))))
		if ((c = (short) rawc) >= 0200)
			return peekc = c, QuoteChar;
	return c;
}

/* Returns non-zero if a character waiting */

int
charp()
{
	if (inIOread)
		return NO;
	if (InJoverc || peekc >= 0)
		return YES;

	return kbhit();
}

/*
 * This does the actual pause, without redisplay.
 */
int
DoSit(delay)
register int	delay;
{
	register clock_t	goal = clock() + delay * (CLK_TCK / 10);

	while (clock() < goal) {
		if (InputPending = charp())
			return YES;
	}
	return NO;
}

/*======================================================================*/
/*				PROC.C					*/
/*======================================================================*/

int
DEFVARG(UnixToBuf, (const char *bufname, int disp, int wsize,
		    int clobber, const char *infile, ...),
		   (bufname, disp, wsize, clobber, infile, va_alist)
	const char	*bufname;
	register int	disp;
	int		clobber;
	const char	*infile;)
{
	/* emulate a pipe by redirecting output to a temporary file,
	   and then reading that file when the command is finished.
	   The "shell" and "shell-flags" variables are not used explicitly.
	   ("shell" is put into the environment variable SHELL, so that system()
	   is able to find it.)
	 */
	char		tmpfile[3][FILESIZE];
	register char	*pipe;
	register int	status;
	static int	org_fd[3] ZERO;
	int		new_fd[3];
	register int	i;
	va_list		ap;
	register char	**argv;
#ifdef TRH
	/* this stuff is to determine whether the shell can be invoked via
	   _shell_p.  It requires my [TRH] system() routine.  This way we can
	   catch output from internal commands, and still handle output
	   redirection correctly if no shell is installed. */
	extern int	_sys_kind;
	extern void	_sys_init __(( void ));

	_sys_init();

#   define SHELL_P	(_sys_kind > 1)
#else
#   define SHELL_P	FALSE
#endif
	exp = 1;

	if (clobber)
		isprocbuf(bufname);

	if (disp) {
		message("Starting up...");
#ifdef PROC_TYPEOUT
	    if (disp > 0) /* the next comma-separated statement */
#endif
		pop_wind(bufname, clobber, clobber ? B_PROCESS : B_FILE),
		set_wsize(wsize);
		redisplay();
	}

	va_begin(ap, infile);
	argv = (char **) ap;	/* m68k has the ``right'' stack direction. */

	/* build a command line for use in system() */
	if (argv[0] == Shell && argv[1] == ShFlags)
		argv += 2;

#define cp pipe
	/* Expand environment variables, just in case the shell doesn't... */
	env_expand(cp = strcpy(genbuf, *argv++));
	cp += strlen(cp);
	while (*argv)
		*cp++ = ' ', cp = appcpy(cp, *argv++);
#undef cp
	va_end(ap);

	/* save original file descriptors */
	if (!org_fd[0]) {
		{ i = 2; } do {
			org_fd[i] = dup(i);
		} while (--i >= 0);
	}

	/* create a temporary file as our ``pipe'' */
	if ((i = creat(pipe = mktmpe(tmpfile[1], "jpXXXXXX"),
		       S_IRUSR|S_IWUSR)) < 0)
		complain(IOerr("pipe", genbuf));

	if (infile == NULL)
		/* connect stdin to ``/dev/null'' */
		close(creat(infile = mktmpe(tmpfile[0], "jpXXXXXX"),
			    S_IRUSR|S_IWUSR));

	/* connect stderr, stdout and stdin to our ``pipe'' */

	dup2(i, 2);
	new_fd[2] = i;		/* Hmm... */

 	if (!SHELL_P) {
		dup2(i, 1);
		new_fd[1] = i;

		if ((i = open(infile, 0)) >= 0) {
			dup2(i, 0);
			new_fd[0] = i;		/* Hmm... */
		}
	}
	else {
		/* This is a workaround for an I/O redirection bug in
		   gulam invoked through _shell_p (gulam apparently uses
		   its original stdio channels, NOT the momentary ones):
		   insert I/O redirection directives in the command line
		   in order to catch output from internal shell commands
		   and multiple `;'-separated commands.  This means that
		   stdout and stderr output are NOT intermixed, but I can
		   live with that.  The alternative, not being able to
		   redirect builtin commands, is much less attractive.
		   Also the input file is redirected this way (only on the
		   first command!) since Gulam eats up the first 3 or 4
		   characters from a directly redirected stdin and places
		   it in its own command buffer. (?!?!) */

		register char	*dp = genbuf,
				*sp = strcpy(iobuff, dp);
		register char	c, quote = '\0';
		int		inf = NO, outf = NO;

		do {
			if (((c = *sp++) == '\0' || c == ';') && !quote) {
				if (!inf++)
					dp = appcpy(appcpy(dp, " <"), infile);
				if (outf) {
					outf = NO;
					continue;
				}
				if (pipe != tmpfile[2])
					pipe = mktmpe(tmpfile[2], "jpXXXXXX");
				dp = appcpy(appcpy(dp, " >>"), pipe);
			}
			else if (quote) {
				if (c == quote)
					quote = '\0';
			}
			else if (c == '"' || c == '\'') {
				quote = c;
			}
			else if (c == '>') {	/* already redirecting */
				outf++;
			}
			else if (c == '<') {
				inf++;
			}
		} while (*dp++ = c);
	}

	putenv(sprint("SHELL=%s", Shell));	/* for system */
	putenv("NOHOLD=1");			/* for TRH startup */

	if (disp)
		message("Grinding along..."),
		redisplay();

	ttyset(OFF);

	if ((status = system(genbuf)) == -1)
		status = -ENOEXEC;	/* presumably exec failure */

	ResetTerm();			/* just to be sure... */
	scr_inval();			/* ...in case screen is clobbered */

	if (disp)
		f_mess("Chugging along...");

	/* restore original affiliation of stdin, stdout, stderr */
	{ i = 2; } do {
		close(i);		/* necessary? YES! */
		/*
		 * for some reason dup2()ed file handles need to be closed
		 * *twice* before they're really gone. If you don't, TOS
		 * keeps eating up filehandles until you run out...
		 * (I thought TOS 1.4 was better :-/)
		 */
		close(new_fd[i]);
		close(new_fd[i]);
		dup2(org_fd[i], i);
		if (SHELL_P)			/* skip stdout */
			break;
	} while (--i >= 0);
	if (infile == tmpfile[0])
		unlink(infile);

	do {
		if (bufname != DevNull && (i = open(pipe, 0)) >= 0)
			read_pipe(bufname, i, disp);
		unlink(pipe);
	} while ((pipe -= sizeof tmpfile[0]) != tmpfile[0]);

	return status;
}

/* push a shell (well, not really, actually...) */
void
Push()
{
	register char	*cmd;
	static char	unset_message[] = "\
[There are modified buffers]\r\n\
[type C-G to return to JOVE]\r\n";
	register char	*mesg_p = unset_message;

	if (!ModBufs(0))
		mesg_p += (sizeof unset_message - 1) / 2;

	putenv(sprint("SHELL=%s", Shell));	/* for system */
	putenv("NOHOLD=1");

	alarm(0);
	curstoLL();
	putp(CE);
	putstr(mesg_p);
	CATCH
		cmd = NullStr;
		for (;;) {
			f_mess(NullStr);
			UpdModLine = NO;
			putp(VS);	/* may be turned off by some command */
			cmd = ask(cmd, "[JOVE] $ ");
			printf("\r\n");
			flusho();
			if (*cmd) {
				minib_add(cmd, YES);	/* history */
				system(cmd);
			}
		}
	ENDCATCH
	ResetTerm();				/* just to be sure... */
	ClAndRedraw();
	alarm(UpdFreq);
	chdir(pwd());				/* might be changed... */
#ifdef RESHAPING
	/*
	 * Some shells (e.g. gulam) are able to switch between 8x16 and
	 * 8x8 fonts in high rez. (thus effectively switching between 25
	 * and 50 lines); so this is just to be sure.
	 */
	win_reshape();
#endif
}

/*======================================================================*/
/*				UTIL.C					*/
/*======================================================================*/
/* (aka kludge dept.) */

char *
getwd(result)
char	*result;
{
	register char	*p = result;
	register int	drive = Dgetdrv();

	*p++ = drive + 'a';
	*p++ = ':';
	Dgetpath(p, 0);
	FIX_FILENAME(p);

	return result;
}

/* change directory -- this is more hairy than you might think
   since Dsetpath() does not do the entire job for you */

int
chdir(path)
register const char	*path;
{
	register int	cur_drive = Dgetdrv();
	char		pathbuf[FILESIZE];

	if (path[0] == '\0')
		return 0;

	if (path[1] == ':') {
		if (Dsetdrv(tolower(path[0]) - 'a') < 0)
			return -1;
		path += 2;
	}
	if (path[0] != SLASH) {
		path = strcpy(&pathbuf[1], path);
		*--(char *)path = SLASH;
	}
	if (Dsetpath(path) < 0) {
		Dsetdrv(cur_drive);
		return -1;
	}
	return 0;
}

#ifdef stat
/* special version of stat tries to generate an unique inode number. */
int
stat(filename, st)
const char		*filename;
register struct stat	*st;
{
#undef stat
	if (stat(filename, st) < 0)
		return -1;
	if (sizeof st->st_ino == sizeof(long)) {
		st->st_ino = st->st_ctime ^ st->st_size;
	}
	else {
		union { long l; short i[2]; } ino;

		ino.l = st->st_ctime ^ st->st_size;
		st->st_ino = ino.i[0] ^ ino.i[1];
	}
	return 0;
}
#endif /* stat */

/* library abort() and _assert() use stdio */

void
abort()
{
	_exit(3);
}

void
_assert(mess, file, line)
char	*mess, file;
{
	curstoLL();
	printf("Assertion Failed: %s (file %s, line %d)%s\r\n",
	       mess, file, line, CE);
	finish(QUIT);
}

/*
 * this is to optimize screen output, and to cope with Ctrl-C.
 * It is a replacement of the write() library routine.
 * Bconout is slightly (~20%) faster than (F)write, and it cannot be
 * interrupted with Ctrl-C.
 */
ssize_t
write(fd, buf, nbytes)
int		fd;
const void	*buf;
size_t		nbytes;
{
	long	st;

	if (fd == 1) {
		register int		n = nbytes;
		register const char	*p = buf;

		while (--n >= 0)
			Bconout(CONSOLE, *p++);

		/* Kludge to force MiNT to flush Bconout buffer. */
		Bcostat(CONSOLE);

		return nbytes;
	}
#ifdef write
#   ifdef __GNUC__
	return _write(fd, buf, nbytes);
#   else
#	undef write
	return write(fd, buf, nbytes);
#   endif
#else
	if ((st = Fwrite(fd, nbytes, buf)) < 0) {
#   if __TURBOC__
		extern int _XltErr __(( int _(tos_errno) ));

		errno = _XltErr((int) st);
#   else
		errno = -st;	/* should be translated, but I am lazy... */
#   endif
		return -1;
	}
	return st;
#endif /* !write */
}

#ifdef read
/* Jove expects Posix compliant read(), i.e, type of 3rd arg is `size_t'.
   Unfortunately gnulib's read is non-compliant if compiled with -mshort.
   Hence this hack. */
ssize_t
read(fd, buf, nbytes)
int	fd;
void	*buf;
size_t	nbytes;
{
#   ifdef __GNUC__
	return _read(fd, buf, nbytes);
#   else
#	undef read
	return read(fd, buf, nbytes);
#   endif
}
#endif /* read */

#ifdef unlink
/* Somehow gnulib's unlink() crashes on me. */
int
unlink(filename)
const char *filename;
{
	int st;

	if (filename == NULL || *filename == '\0') {
		errno = EEXIST;
		return -1;
	}
	if ((st = Fdelete(filename)) < 0) {
#   if __TURBOC__
		extern int _XltErr __(( int _(tos_errno) ));

		errno = _XltErr((int) st);
#   else
		errno = -st;	/* should be translated, but I am lazy... */
#   endif
		st = -1;
	}
	return st;
}
#endif

int
getpid()
{
	extern time_t	time0;

	return (*(short *)&time0 ^ *((short *)&time0 + 1)) & 0x7fff;
}

#ifdef RESHAPING
/* set Line-A parameters to change system font */

private int
set_font(font)
register __FONT	*font;
{
#   if __TURBOC__	/* a little optimization */
	register VDIESC	*vdi = Vdiesc;
#	define Vdiesc	vdi
#   endif
	if (font->font_id != 1 ||		/* no system font */
	    font->form_height == V_CEL_HT)	/* no change */
		return NO;

	V_CEL_HT = font->form_height;
	V_CEL_WR = font->form_height * VWRAP;
	V_CEL_MY = V_Y_MAX / font->form_height - 1;
	V_CEL_MX = V_X_MAX / font->max_cell_width - 1;
	V_FNT_WR = font->form_width;
	V_FNT_ST = font->first_ade;
	V_FNT_ND = font->last_ade;
	V_OFF_AD = font->off_table;
	V_FNT_AD = font->dat_table;

#	undef Vdiesc
	return YES;
}

DEF_CMD( "screen-dense", ScrDense, NO ); _IF(def ATARIST)_IF(def RESHAPING)
DEF_CMD( "screen-normal", ScrDense, NEGATE ) _IF(def ATARIST)_IF(def RESHAPING)
{
#   if __TURBOC__	/* a little optimization */
	register VDIESC	*vdi = Vdiesc;
#	define Vdiesc	vdi
#   endif
	if (VPLANES > 1)
		complain("[\"%f\" only in Monochrome mode]");
	if (scr_xorg || scr_yorg)
		complain("[\"%f\" not in window]");

	if (set_font((exp == 0) ? org_font : FONT_SELECT(exp > 0)))
		win_reshape();
#	undef Vdiesc
}
#endif /* RESHAPING */

#ifdef FILESELECTOR
public char *
FileSelector(prompt, def, buf)
const char	*prompt,
		*def;
char		*buf;
{
	int	valid = NO;
	char	base_name[FILESIZE];
	char	*base_p = "*.*";
#   ifdef CHDIR
#	ifndef TINY
	extern int ask_dir_only;

	if (ask_dir_only >= 0)		/* only select directories */
		base_p = ".";
#	endif
#   endif
	make_filename(buf, pwd(), base_p);
	strupr(buf);
	base_name[0] = '\0';

    /*-	FSelMode = READ;		/* not used */

	f_mess((def && def[0]) ? "%s(default %s)" : "%s", prompt, def);
	mouseon();
	cursoff();

	if (FSEL_EXINPUT_AVAILABLE) {
		char	header[MESG_SIZE];

		/* Build a nice header; assume prompt ends with blank */
		sprintf(header, "\360\360 %s\360\360 ",
			(prompt[0] == ':') ? prompt + 2 : prompt);
		fsel_exinput(buf, base_name, &valid, strupr(header));
	}
	else {
		fsel_input(buf, base_name, &valid);
	}
	ttsize();			/* to reset clip region */
	mouseoff();
	scr_inval();			/* File selector messes screen */

	if (!valid)
		complain("[Aborted]");

	base_p = strcpy(basename(buf), base_name);	/* append filename */

	FIX_FILENAME(buf);

	if (base_p[0] == '\0') {
		if (def == NULL || numcomp(buf, pwd()) == (base_p - buf - 1))
			buf = (char *) def;
#   ifdef CHDIR
#	ifndef TINY
		/* if we are not asking for a directory, and user changed
		   the directory path, append default filename. */
		else if (!(ask_dir_only >= 0))
			strcpy(base_p, basename(def));
#	endif
#   endif
	}
	/* display the result */
	s_mess("%s%s", prompt, buf);

	return buf;
}
#endif /* FILESELECTOR */

#endif /* ATARIST */

/*======================================================================
 * $Log: atari_st.c,v $
 * Revision 14.32.0.9  1994/01/06  00:13:15  tom
 * (LanguageSpecials): add Scandinavian, Swiss support.
 *
 * Revision 14.32.0.6  1993/10/28  00:54:48  tom
 * ported to gcc (2.3.3) + gnulib;
 * UnixToBuf: expand environment variables in command;
 * getwd, FileSelector: replace strlwr() with FIX_FILENAME().
 *
 * Revision 14.32  1993/05/02  22:48:29  tom
 * (write): add Bconout flush kludge for MiNT.
 *
 * Revision 14.31  1993/02/15  02:01:45  tom
 * remove (void) casts; fix done_ttinit.
 *
 * Revision 14.30  1993/02/05  00:07:26  tom
 * cleanup whitespace; some random optimizations.
 *
 * Revision 14.27  1992/09/21  13:15:58  tom
 * replace CTL('Q') with `QuoteChar'; use ssize_t with write().
 *
 * Revision 14.26  1992/08/26  23:57:02  tom
 * add RCS directives.
 *
 */
