home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Black Box 4
/
BlackBox.cdr
/
editors
/
tde150.arj
/
MAIN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-04-01
|
55KB
|
1,719 lines
/******************* start of original comments ********************/
/*
* Written by Douglas Thomson (1989/1990)
*
* This source code is released into the public domain.
*/
/*
* Name: dte - Doug's Text Editor program - hardware dependent module
* Purpose: This file contains all the code that needs to be different on
* different hardware.
* File: hwibm.c
* Author: Douglas Thomson
* System: This particular version is for the IBM PC and close compatibles.
* It write directly to video RAM, so it is faster than other
* techniques, but will cause "snow" on most CGA cards. See the
* file "hwibmcga.c" for a version that avoids snow.
* The compiler is Turbo C 2.0, using one of the large data memory
* models.
* Date: October 10, 1989
* Notes: This module has been kept as small as possible, to facilitate
* porting between different systems.
*/
/********************* end of original comments ********************/
/*
* These routines were rewritten for Microsoft C. They are pretty much system
* dependent and pretty much Microsoft C dependent. I also renamed this file
* "main.c" - easier to find the main function.
*
* New editor name: tde, the Thomson-Davis Editor.
* Author: Frank Davis
* Date: June 5, 1991, version 1.0
* Date: July 29, 1991, version 1.1
* Date: October 5, 1991, version 1.2
* Date: January 20, 1992, version 1.3
* Date: February 17, 1992, version 1.4
* Date: April 1, 1992, version 1.5
*
* This modification of Douglas Thomson's code is released into the
* public domain, Frank Davis. You may distribute it freely.
*/
char *greatest_composer_ever = "W. A. Mozart, 1756-1791";
#include "tdestr.h" /* tde types */
#include "common.h"
#include "define.h"
#include "help.h"
#include "tdefunc.h"
#include <dos.h> /* for renaming files */
#ifdef __TURBOC__
#include <dir.h> /* for searching the current path */
#endif
#include <bios.h> /* for direct BIOS keyboard input */
#if defined( __TURBOC__ )
#include <alloc.h> /* for memory allocation */
#elif defined( __MSC__ )
#include <malloc.h> /* for memory allocation */
#endif
#include <io.h> /* for file attribute code */
#include <fcntl.h> /* open flags */
#if defined( __MSC__ )
#include <bios.h>
#include <errno.h>
#include <sys\types.h> /* S_IWRITE etc */
#endif
#include <sys\stat.h> /* S_IWRITE etc */
#if defined( __MSC__ )
void (interrupt far *old_control_c)( void ); /* variable for old CNTL-C */
#endif
int full_screen_buffer[2000]; /* 25 lines * 80 columns = 2000 characters */
/* (make it an int for the attribute) */
/*
* Default color settings. Incidentally, I'm color blind (mild red-green) and
* the default colors look fine to me.
*/
static int colors[2][12] = {
{ HERC_REVERSE, HERC_NORMAL, HERC_REVERSE, HERC_REVERSE, HERC_HIGH,
HERC_NORMAL, HERC_NORMAL, HERC_HIGH, HERC_HIGH, HERC_HIGH, HERC_REVERSE,
HERC_REVERSE },
{ COLOR_HEAD, COLOR_TEXT, COLOR_MODE, COLOR_BLOCK, COLOR_MESSAGE,
COLOR_HELP, COLOR_DIAG, COLOR_EOF, COLOR_CURL, COLOR_RULER, COLOR_POINTER,
COLOR_TEXT }
};
/*
* original control-break checking flag
*/
static int s_cbrk;
/*
* Name: main
* Purpose: To do any system dependent command line argument processing,
* and then call the main editor function.
* Date: October 10, 1989
* Passed: argc: number of command line arguments
* argv: text of command line arguments
*/
void main( int argc, char *argv[] )
{
#if defined( __MSC__ )
union REGS inregs, outregs;
#endif
g_status.found_first = FALSE;
g_status.arg = 1;
g_status.argc = argc;
g_status.argv = argv;
/*
* trap control-break to make it harmless, and turn checking off
*/
#if defined( __MSC__ )
inregs.h.ah = 0x33;
inregs.h.al = 0;
intdos( &inregs, &outregs );
s_cbrk = outregs.h.dl;
old_control_c = _dos_getvect( 0x23 );
_dos_setvect( 0x23, harmless );
inregs.h.ah = 0x33;
inregs.h.al = 1;
inregs.h.dl = 0;
intdos( &inregs, &outregs );
#else
s_cbrk = getcbrk( );
ctrlbrk( harmless );
setcbrk( 0 );
#endif
initialize( );
editor( );
terminate( );
}
/*
* Name: error
* Purpose: To report an error, and usually make the user type <ESC> before
* continuing.
* Date: June 5, 1991
* Passed: kind: an indication of how serious the error was:
* WARNING: continue after pressing a key
* FATAL: abort the editor
* line: line to display message
* message: string to be printed
* Notes: Show user the message and ask for a key if needed.
*/
void error( int kind, int line, char *message )
{
char buff[MAX_COLS+2]; /* somewhere to store error before printing */
register int c; /* character entered by user to continue */
char line_buff[(MAX_COLS+2)*2]; /* buffer for char and attribute */
/*
* tell the user what kind of an error it is
*/
switch (kind) {
case FATAL:
strcpy( buff, "Fatal error: " );
break;
case WARNING:
strcpy( buff, "Warning: " );
break;
}
/*
* prepare the error message itself
*/
strcat( buff, message );
/*
* tell the user how to continue editing if necessary
*/
if (kind == WARNING)
strcat( buff, ": press a key" );
/*
* output the error message
*/
save_screen_line( 0, line, line_buff );
set_prompt( buff, line );
if (kind == FATAL) {
/*
* no point in making the user type <ESC>, since the program is
* about to abort anyway...
*/
terminate( );
exit( 1 );
}
c = getkey( );
restore_screen_line( 0, line, line_buff );
if (g_status.wrapped) {
g_status.wrapped = FALSE;
show_search_message( CLR_SEARCH, g_display.mode_color );
}
}
/*
* Name: harmless
* Purpose: To process control-break by ignoring it, so that the editor is
* not aborted.
* Date: June 5, 1991
*/
#if defined( __MSC__ )
int interrupt far harmless( void )
#else
static int harmless(void)
#endif
{
return( 1 ); /* ignore */
}
/*
* Name: terminate
* Purpose: To free all dynamic structures and unload anything we loaded
* Date: June 5, 1991
*/
void terminate( void )
{
union REGS inregs, outregs;
register WINDOW *wp; /* register for scanning windows */
WINDOW *w; /* free window */
register file_infos *fp; /* register for scanning files */
file_infos *f; /* free files */
/*
* restore control-break checking
*/
#if defined( __MSC__ )
_dos_setvect( 0x23, old_control_c );
inregs.h.ah = 0x33;
inregs.h.al = 1;
inregs.h.dl = (char)s_cbrk;
intdos( &inregs, &outregs );
#else
setcbrk( s_cbrk );
#endif
/*
* free the text buffer
*/
hfree( (void huge *)g_status.start_mem );
/*
* free the file structures if not already free.
*/
fp = g_status.file_list;
while (fp != NULL) {
f = fp;
fp = fp->next;
free( f );
}
/*
* free the window structures if not already free.
*/
wp = g_status.window_list;
while (wp != NULL) {
w = wp;
wp = wp->next;
free( w );
}
/*
* reset the cursor size and unload the 83/84 key keyboard utility
*/
set_cursor_size( mode.cursor_size == SMALL_INS ? g_display.insert_cursor :
g_display.overw_cursor );
if (mode.enh_kbd == FALSE)
simulate_enh_kbd( 0 );
}
/*
* Name: hw_initialize
* Purpose: To initialize the display ready for editor use.
* Date: June 5, 1991
*/
void hw_initialize( void )
{
struct vcfg cfg; /* defined in .h */
unsigned paragraphs;
long space; /* amount of memory to use */
register int *clr;
/*
* set up screen size
*/
g_display.ncols = MAX_COLS;
g_display.nlines = MAX_LINES - 1;
g_display.mode_line = MAX_LINES;
g_display.line_length = MAX_LINE_LENGTH;
/*
* work out what kind of display is in use, and set attributes and
* display address accordingly. Note that this will only work with
* close IBM compatibles.
*/
video_config( &cfg );
g_display.display_address = (char far *)cfg.videomem;
/*
* Use an integer pointer to go thru the color array for setting up the
* various color fields.
*/
clr = cfg.color == FALSE ? &colors[0][0] : &colors[1][0];
g_display.head_color = *clr++;
g_display.text_color = *clr++;
g_display.mode_color = *clr++;
g_display.block_color = *clr++;
g_display.message_color = *clr++;
g_display.help_color = *clr++;
g_display.diag_color = *clr++;
g_display.eof_color = *clr++;
g_display.curl_color = *clr++;
g_display.ruler_color = *clr++;
g_display.ruler_pointer = *clr++;
g_display.hilited_file = *clr;
/*
* grab all the available memory for the text buffer
*/
#if defined( __MSC__ )
_dos_allocmem( 0xffff, ¶graphs );
/*
* A paragraph is 16 bytes. Convert paragraphs to bytes by shifting left
* 4 bits.
*/
space = (long)paragraphs << 4;
/*
* if using Microsoft C, allocate all available memory. If debugging in
* in QC 2.5, uncomment the next lines so the debugger will have some room.
*/
/* if (space > 12000l)
space = 12000l; */
if (space <= 0)
return;
#else
space = farcoreleft() - 30000L;
#endif
#if defined( __MSC__ )
if ((g_status.start_mem = (text_ptr)halloc( space, sizeof( char ))) == NULL)
error( FATAL, g_display.nlines, "out of memory???" );
#else
if ((g_status.start_mem = farmalloc(space)) == NULL)
error( FATAL, g_display.nlines, "out of memory???" );
#endif
g_status.max_mem = addltop( space, g_status.start_mem );
}
/*
* Video BIOS Data Areas
* The BIOS routines maintain several dynamic variables in an area of
* memory called the Video Display Data Area. The following contains a
* summary of these variables' addresses, their symbolic names, and
* their contents. All addresses are relative to the 0x0000h segment.
* From the IBM Technical Reference and other sources.
*
* Address Name Type Description
* 0x0449 CRT_MODE Byte Current BIOS video number
* 0x044a CRT_COLS Word Number of displayed character columns
* 0x044c CRT_LEN Word Size of video buffer in bytes
* 0x044e CRT_START Word Offset of start of video buffer
* 0x0450 CURSOR_POSN Word Array of eight words containing the cursor
* position for each of eight possible
* video pages. The high-order byte of
* each word contains the character row,
* the low-order byte the character column
* 0x0460 CURSOR_MODE Word Starting and ending lines for alphanumeric
* cursor. The high-order byte contains
* the starting (top) line; the low-order
* byte contains the ending (bottom) line
* 0x0462 ACTIVE_PAGE Byte Currently displayed video page number
* 0x0463 ADDR_6845 Word I/O port address of CRT Controller's
* Address register (3B4h for mono;
* 3D4h for color)
* 0x0465 CRT_MODE_SET Byte Current value for Mode Control register
* (3B8h on MDA, 3D8h on CGA). On the
* EGA and VGA, the value emulates those
* used on the MDA and CGA.
* 0x0466 CRT_PALETTE Byte Current value for the CGA Color Select
* register (3D9h). On the EGA and VGA,
* the value emulates those used on the
* MDA and CGA.
* 0x0467 io_rom_init Word Pointer to optional i/o rom init routine
* 0x0469 io_rom_seg Word Pointer to io rom segment
* 0x046b intr_flag Byte Flag to indicate an interrupt happened
* 0x046c timer_low Word Low word of timer count
* 0x046e timer_high Word High word of timer count
* 0x0470 timer_ofl Byte Timer has rolled over since last count
* 0x0471 bios_break Byte Bit 7 = 1 if Break Key has been hit
* 0x0472 reset_flag Word Word = 1234h if keyboard reset underway
* 0x0484 ROWS Byte Number of displayed character rows - 1
* 0x0485 POINTS Word Height of character matrix
* 0x0487 INFO Byte EGA and VGA display data
* 0x0488 INFO_3 Byte Configuration switches for EGA and VGA
* 0x0489 flags Byte Miscellaneous flags
* 0x0496 kb_flag_3 Byte Additional keyboard flag
* 0x048A DCC Byte Display Combination Code
* 0x04A8 SAVE_PTR Dword Pointer to BIOS save area
*
*/
void video_config( struct vcfg *cfg )
{
#pragma pack( 1 ) /* Use pragma to force packing on byte boundaries. */
struct LOWMEMVID
{
char vidmode; /* 0x449 */
unsigned scrwid; /* 0x44A */
unsigned scrlen; /* 0x44C */
unsigned scroff; /* 0x44E */
struct LOCATE
{
unsigned char col;
unsigned char row;
} csrpos[8]; /* 0x450 */
struct CURSIZE
{
unsigned char end;
unsigned char start;
} csrsize; /* 0x460 */
char page; /* 0x462 */
unsigned addr_6845; /* 0x463 */
char crt_mode_set; /* 0x465 */
char crt_palette[30]; /* 0x466 */
char rows; /* 0x484 */
unsigned points; /* 0x485 */
char ega_info; /* 0x487 */
char info_3; /* 0x488 */
char skip[13]; /* 0x489 */
char kb_flag_3; /* 0x496 */
} vid;
struct LOWMEMVID _far *pvid = &vid;
#pragma pack( ) /* revert to previously defined pack pragma. */
union REGS in, out;
unsigned char temp, active_display;
/*
* Move system information into our video structure.
*/
_fmemmove( pvid, (void far *)0x00000449l, sizeof( vid ) );
cfg->rescan = FALSE;
in.x.ax = 0x1a00;
int86( VIDEO_INT, &in, &out );
temp = out.h.al;
active_display = out.h.bl;
if (temp == 0x1a && (active_display == 7 || active_display == 8))
g_display.adapter = VGA;
else {
in.h.ah = 0x12;
in.h.bl = 0x10;
int86( VIDEO_INT, &in, &out );
if (out.h.bl != 0x10) { /* EGA */
if (vid.ega_info & 0x08)
g_display.adapter = vid.addr_6845 == 0x3d4 ? CGA : MDA;
else
g_display.adapter = EGA;
} else if (vid.addr_6845 == 0x3d4)
g_display.adapter = CGA;
else
g_display.adapter = MDA;
}
if (g_display.adapter == CGA || g_display.adapter == EGA) {
if (g_display.adapter == CGA)
cfg->rescan = TRUE;
g_display.insert_cursor = mode.cursor_size == SMALL_INS ? 0x0607 : 0x0407;
g_display.overw_cursor = mode.cursor_size == SMALL_INS ? 0x0407 : 0x0607;
} else {
g_display.insert_cursor = mode.cursor_size == SMALL_INS ? 0x0b0c : 0x070b;
g_display.overw_cursor = mode.cursor_size == SMALL_INS ? 0x070b : 0x0b0c;
}
cfg->mode = vid.vidmode;
if (vid.addr_6845 == 0x3D4) {
cfg->color = TRUE;
cfg->videomem = (void far *)0xb8000000l;
} else {
cfg->color = FALSE;
cfg->videomem = (void far *)0xb0000000l;
}
/*
* Get keyboard type. Since there is no interrupt that determines
* keyboard type, use this method. Look at bit 4 in keyboard flag3.
* This method is not always foolproof on clones.
*/
if ((vid.kb_flag_3 & 0x10) != 0)
mode.enh_kbd = TRUE;
else
mode.enh_kbd = FALSE;
if (mode.enh_kbd == FALSE)
simulate_enh_kbd( 1 );
install_ceh( &ceh );
ceh.flag = OK;
}
/*
* Name: set_cursor_size
* Purpose: To set cursor size according to insert mode.
* Date: June 5, 1991
* Passed: csize: desired cursor size
* Notes: use the global display structures to set the cursor size
*/
void set_cursor_size( int csize )
{
_asm {
mov ah, 1 ; function 1 - set cursor size
mov cx, WORD PTR csize ; get cursor size ch:cl == top:bot
int VIDEO_INT ; video interrupt = 10h
}
}
/*
* Name: hw_move
* Purpose: To move data from one place to another as efficiently as
* possible.
* Date: June 5, 1991
* Passed: dest: where to copy to
* source: where to copy from
* number: number of bytes to copy
* Notes: moves may be (usually will be) overlapped. Although we can
* move up to 64k-1 bytes at once, we can safely move only
* 0xfff0 bytes at one time. Let's try only 0xf000.
*/
void hw_move( text_ptr dest, text_ptr source, long number )
{
unsigned long s, d;
s = ptoul( source );
d = ptoul( dest );
if (number < 0)
/*
* this should never happen...
*/
error( WARNING, g_display.nlines, "negative move - contact Frank Davis" );
else if (s == d)
/*
* nothing to be done
*/
;
else if (s > d) {
while (number > 0xF000L) {
dest = nptos( dest );
source = nptos( source );
_fmemmove( dest, source, 0xF000 );
dest = addltop( 0xF000L, dest );
source = addltop( 0xF000L, source );
number -= 0xF000L;
}
dest = nptos( dest );
source = nptos( source );
_fmemmove( dest, source, (unsigned)number );
} else {
source = addltop( number, source );
dest = addltop( number, dest );
while (number > 0xF000L) {
source = addltop( -0xF000L, source );
source = nptos( source );
dest = addltop( -0xF000L, dest );
dest = nptos( dest );
_fmemmove( dest, source, 0xF000 );
number -= 0xF000L;
}
source = addltop( -number, source );
dest = addltop( -number, dest );
source = nptos( source );
dest = nptos( dest );
_fmemmove( dest, source, (unsigned)number );
}
}
/*
* Name: hw_fattrib
* Purpose: To determine the current file attributes.
* Date: December 26, 1991
* Passed: name: name of file to be checked
* Returns: use the function in the tdeasm file to get the DOS file
* attributes. get_fattr() returns 0 or OK if no error.
*/
int hw_fattrib( char *name )
{
register int rc;
int fattr;
rc = get_fattr( name, &fattr );
return( rc == OK ? rc : ERROR );
}
/*
* Name: change_mode
* Purpose: To prompt for file access mode.
* Date: January 11, 1992
* Passed: name: name of file
* line: line to display message
* Returns: OK if file could be changed
* ERROR otherwise
* Notes: function is used to change file attributes for save_as function.
*/
int change_mode( char *name, int line )
{
int result;
int fattr;
register int rc;
char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */
rc = OK;
result = get_fattr( name, &fattr );
if (result != OK)
rc = ERROR;
else if (result == OK && fattr & READ_ONLY) {
/*
* file is read only
*/
save_screen_line( 0, line, line_buff );
set_prompt( "File is write protected! Overwrite anyway? (y/n): ", line );
if (get_yn( ) != A_YES)
rc = ERROR;
if (rc == OK && set_fattr( name, ARCHIVE ) != OK)
rc = ERROR;
restore_screen_line( 0, line, line_buff );
}
return( rc );
}
/*
* Name: write_file
* Purpose: To write text to a file
* way.
* Date: June 5, 1991
* Passed: name: name of disk file or device
* mode: fopen flags to be used in open
* start: first character in text buffer
* end: last character (+1) in text buffer
* block: write a file or a marked block
* Returns: OK, or ERROR if anything went wrong
*/
int write_file( char *name, char *mode, text_ptr start, text_ptr end,
int block )
{
FILE *fp; /* file to be written */
char *p;
register int rc;
register int len;
int bc, ec, last_c;
file_infos *file;
long lend;
long number;
rc = OK;
if ((fp = fopen( name, mode )) == NULL || ceh.flag == ERROR)
rc = ERROR;
else {
start = cpf( start );
if (block == LINE || block == BOX || block == STREAM) {
if (g_status.marked_file == NULL)
rc = ERROR;
else if (block == BOX || block == STREAM) {
file = g_status.marked_file;
bc = file->block_bc;
ec = file->block_ec;
last_c = ec + 1 - bc;
}
if (block == STREAM) {
len = linelen( start );
if (ptoul( start ) == ptoul( end )) {
end = len > ec ? start + ec + 1 : start + len + 1;
start += bc < len ? bc : len;
} else {
len = linelen( end );
end += len > ec ? ec + 1 : len;
}
}
}
p = g_status.line_buff;
if (rc == OK) {
if (block == BOX) {
lend = ptoul( end );
for (;ptoul( start ) <= (unsigned long)lend && rc == OK;) {
g_status.copied = FALSE;
load_box_buff( p, start, bc, ec );
*(p+last_c) = '\n';
*(p+last_c+1) = CONTROL_Z;
len = find_CONTROL_Z( p );
if (fwrite( p, sizeof( char ), len, fp ) < (unsigned)len ||
ceh.flag == ERROR)
rc = ERROR;
if (rc == OK) {
start = find_next( start );
if (start == NULL)
start = end + 1;
}
}
g_status.copied = FALSE;
} else {
number = ptoul( end ) - ptoul( start );
len = 0x0800;
start = nptos( start );
while (number > 0x0800L && rc != ERROR) {
_fmemcpy( full_screen_buffer, start, len );
if (fwrite(full_screen_buffer,sizeof(char),len,fp)<(unsigned)len
|| ceh.flag == ERROR)
rc = ERROR;
if (rc != ERROR) {
number -= 0x0800L;
start += 0x0800;
start = nptos( start );
}
}
/*
* now less than 2k is left, so finish off the write
*/
if (rc != ERROR) {
len = (int)number;
_fmemcpy( full_screen_buffer, start, len );
if (fwrite(full_screen_buffer,sizeof(char),len,fp) < (unsigned)len
|| ceh.flag == ERROR)
rc = ERROR;
}
}
if (ceh.flag != ERROR) {
if (fclose( fp ) != 0)
rc = ERROR;
}
}
}
return( rc );
}
/*
* Name: hw_save
* Purpose: To save text to a file, eliminating trailing space on the
* way.
* Date: November 11, 1989
* Passed: name: name of disk file
* start: first character in text buffer
* end: last character (+1) in text buffer
* block: type of block defined
* Returns: OK, or ERROR if anything went wrong
*/
int hw_save( char *name, text_ptr start, text_ptr end, int block )
{
char *lf = "wb";
char *crlf = "w";
register char *write_mode;
write_mode = mode.crlf == LF ? lf : crlf;
return( write_file( name, write_mode, start, end, block ) );
}
/*
* Name: hw_append
* Purpose: To append text to a file.
* Date: November 11, 1989
* Passed: name: name of disk file
* start: first character in text buffer
* end: last character (+1) in text buffer
* block: type of defined block
* Returns: OK, or ERROR if anything went wrong
*/
int hw_append( char *name, text_ptr start, text_ptr end, int block )
{
char *lf = "ab";
char *crlf = "a";
register char *append_mode;
append_mode = mode.crlf == LF ? lf : crlf;
return( write_file( name, append_mode, start, end, block ) );
}
/*
* Name: hw_load
* Purpose: To load a file into the text buffer.
* Date: November 11, 1989
* Passed: name: name of disk file
* start: first character in text buffer
* limit: last available character in text buffer
* end: last character of file in text buffer
* line: line to display messages
* Returns: OK, or ERROR if anything went wrong
*/
int hw_load( char *name, text_ptr start, text_ptr limit, text_ptr *end,
int line )
{
int fd; /* file being read */
register int length; /* number of bytes actually read */
register int rc;
unsigned long ustart, ulimit;
char buff[MAX_COLS+2];
char line_buff[(MAX_COLS+2)*2]; /* buffer for char and attribute */
/*
* try reading the file
*/
rc = OK;
if ((fd = open( name, O_RDONLY )) == ERROR || ceh.flag == ERROR) {
combine_strings( buff, "File '", name,"'not found or error loading file");
save_screen_line( 0, line, line_buff );
set_prompt( buff, line );
getkey( );
restore_screen_line( 0, line, line_buff );
rc = ERROR;
} else {
/*
* read the entire file, without going past end of buffer.
* Note that this means a file that is within 1K of the limit
* will not be accepted. length set to a number > 0 for first loop
*/
limit = addltop( -1024, limit );
ulimit = ptoul( limit );
start = cpf( start );
for (length=1; rc == OK && length > 0;) {
ustart = ptoul( start );
if (ustart >= ulimit ) {
combine_strings( buff, "file '", name,
"'too big. FILE WILL BE TRUNCATED IF EDITED AND SAVED!" );
error( WARNING, line, buff );
rc = WARNING;
} else {
/*
* length = number of bytes read. since we only try to
* read 2048 bytes at one time, we should have no problem
* using the results from unsigned long arithmetic.
*/
if ((length = read( fd, full_screen_buffer, 2048 )) == ERROR ||
ceh.flag == ERROR) {
combine_strings( buff, "error reading file '", name, "'" );
error( WARNING, line, buff );
rc = ERROR;
} else {
if (ustart + (unsigned long)length >= ulimit) {
combine_strings( buff, "file '", name, "'too big" );
error( WARNING, line, buff );
rc = WARNING;
length = (int)(ulimit - ustart);
}
_fmemcpy( start, full_screen_buffer, length );
start = addltop( length, start );
}
start = cpf( start );
}
}
if (rc != ERROR) {
if (*(start-1) != '\n')
*start++ = '\n';
}
/*
* close the file and report the final character in the buffer
*/
close( fd );
*end = start;
}
return( rc );
}
/*
* Name: get_help
* Purpose: save the screen and display key definitions
* Date: June 5, 1991
* Notes: This routine is dependent on the length of the strings in the
* help screen. To make it easy to load in a new help screen,
* the strings are assumed to be 80 characters long followed by
* the '\0' character. It is assumed each that string contains
* exactly 81 characters.
*/
void get_help( WINDOW *arg_filler )
{
register char *help;
register int line;
xygoto( -1, -1 );
save_screen( );
help = help_screen[0];
for (line=0; help != NULL; ) {
s_output( help, line, 0, g_display.help_color );
help = help_screen[++line];
}
line = getkey( );
restore_screen( );
}
/*
* Name: save_screen
* Purpose: save the contents of the screen to the screen buffer
* Date: June 5, 1991
*/
void save_screen( void )
{
_fmemcpy( full_screen_buffer, g_display.display_address, 4000 );
}
/*
* Name: restore_screen
* Purpose: restore the contents of the screen from screen buffer
* Date: June 5, 1991
*/
void restore_screen( void )
{
_fmemcpy( g_display.display_address, full_screen_buffer, 4000 );
}
/*
* Name: show_credits
* Purpose: display authors
* Date: June 5, 1991
*/
void show_credits( void )
{
register char *credit;
int line;
xygoto( -1, -1 );
credit = credit_screen[0];
for (line=0; credit != NULL; ) {
s_output( credit, line+2, 11, g_display.text_color );
credit = credit_screen[++line];
}
}
/*
* The next function was written by Tom Waters, twaters@nswc-wo.navy.mil and
* modified extensively by Frank Davis.
*
* "I use ANSI.SYS to redefine a few of my function keys and this causes a
* problem when getch() is used in a program. For example, instead of returning
* 321 for the F7, I get the redefined string inserted in the text. So, instead
* of using getch(), I use the following function ..."
*
* Tom, thanks for the info and solution. I'm sure your function will be
* be appreciated by everyone with ANSI key defs, Frank.
*/
/*
* Name: getkey
* Purpose: use bios to get keyboard input (getch has trouble w/ ANSI
* redefined keys)
* Date: July 2, 1991
* Modified:July 12, 1991, Frank Davis - accept ALT-xxx keyboard entry
* September 10, 1991, Frank Davis - add support for Ctrl+Up, etc...
* Passed: None
* Notes: Uses the BIOS to read the next keyboard character. Service
* 0 is keyboard read. Service 0x10 is the extended keyboard read.
* Test for a bunch of special cases. Allow the user to enter any
* ASCII or Extended ASCII code as a normal text characters,
* exceptions are 10 and 26 (LF, EOF).
*
* Control @ is defined as 0. we need to do a special test
* for this key, otherwise it's interpretted as an Alt key. It's
* the only "regular" Control key that returns 0 in AL and the scan
* byte in AH. The "regular" Control keys are those in the range
* 0-31 and they return the Control character in AL and the scan
* code in AH. All of the Alt + CHARACTER keys return 0 in AL and
* the scan code in ah.
*
* This function was written for US keyboards. It may have to be
* modified for other keyboards, eg. Belgium, Canada, Czech,
* Slovak, Denmark, France, Germany, etc...
*/
int getkey( void )
{
unsigned key, num_lock, control, shift;
register scan;
register unsigned lo;
/*
* _bios_keybrd == int 16. It returns the scan code in ah, hi part of key,
* and the ascii key code in al, lo part of key. If the character was
* entered via ALT-xxx, the scan code, hi part of key, is 0.
*/
if (mode.enh_kbd) {
key = _bios_keybrd( 0x10 );
lo = _bios_keybrd( 0x12 );
/*
* couple of special cases: 1) if user enters Alt-224 then the
* hi byte == 0 and lo byte == 0xe0. we need to let this get
* thru as an Extended ASCII char. 2) although we could make the
* cursor pad keys do different functions than the numeric pad
* cursor keys, let's set the 0xe0 in the lo byte to zero and forget
* about support for those keys.
*/
if ((key & 0x00ff) == 0x00e0 && (key & 0xff00) != 0)
key = key & 0xff00;
} else {
key = _bios_keybrd( 0 );
lo = _bios_keybrd( 2 );
}
num_lock = lo & 0x20;
control = lo & 0x04;
shift = lo & 0x03;
scan = (key & 0xff00) >> 8;
lo = key & 0X00FF;
if (lo == 0) {
/*
* special case for Control @, which is defined as 0 or NULL.
*/
if (scan == 3)
lo = 430;
/*
* when first byte is 0, map it above 256, so that we can
* let ALT-xxx keys map to their 'natural' place. In
* otherwords, use 0-255 as normal text characters and
* those >= 256 are function keys.
*/
else
lo = scan | 0x100;
}
/*
* Pressing Control+BackSpace generates the 0x7f character. Instead of
* 0x7f, make lo the ASCII backspace character. If anyone wants the
* 0x7f character, then they can enter it via ALT+xxx.
*/
if (scan == 14 && lo == 0x7f)
lo = 8;
/*
* At the bottom of page 195 in MASM 6.0 ref manual, "..when the keypad
* ENTER and / keys are read through the BIOS interrupt 16h, only E0h
* is seen since the interrupt only gives one-byte scan codes."
*/
else if (scan == 0xe0) {
if (lo == 13)
scan = 28;
else if (lo == 10)
scan = 28;
}
/*
* let's massage all of the control key combinations.
*/
if (lo < 32) {
/*
* My machine at home is sorta weird. On every machine that I've
* tested at the awffice, the ALT-xxx combination returns 0 for the
* scan byte and xxx for the ASCII code. My machine returns 184 (decimal)
* as the scan code?!?!? I added the next two lines for my machine at
* home. I wonder if someone forgot to zero out ah for Alt keypad entry
* when they wrote my bios.
*/
if (scan > 0x80)
scan = 0;
/*
* If user enters ALT+010 make this a return. LF is a special character
* and needs to be handled by the editor.
*/
if (scan == 0 && lo == 10)
lo = 425;
/*
* Separate the ESC key from the ^[ key. The scan code for the ESC
* key is 1. Map this to a different index into the key function
* array just in case someone wants to define ESC or ^[ to different
* functions. BTW, ESC and ^[ return the same ASCII code, 27.
*
*/
else if (scan == 1) {
if (shift)
lo = 259;
else if (control)
lo = 260;
else
lo = 258;
}
/*
* Scan code for Enter = 28. Separate the various Enter keys.
*/
else if (scan == 28) {
if (shift)
lo = 263;
else if (control)
lo = 264;
else
lo = 262;
}
/*
* Scan code for Backspace = 14. Separate the various BackSpace keys.
*/
else if (scan == 14) {
if (shift)
lo = 266;
else if (control)
lo = 267;
else
lo = 265;
}
/*
* Scan code for Tab = 15. Separate the tab key.
*/
else if (scan == 15) {
lo = 268;
}
/*
* if scan code is not 0, then a Control key was pressed. Map
* those keys to the WordStar commands.
*/
else if (scan > 0)
lo += 430;
/*
* Do not allow control z to get thru. Code 256 is not assigned to
* any function, see default.h for more info.
*/
if (lo == 26)
lo = 256;
} else if (!num_lock) {
switch (scan) {
/*
* scan code for grey - == 74. if num_lock is not toggled, assign it
* to the scroll line up function.
*/
case 74 :
lo = 423;
break;
/*
* scan code for grey + == 78. if num_lock is not toggled, assign it
* to the scroll line down function. if shift grey + then stationary
* scroll down.
*/
case 78 :
lo = 424;
break;
}
}
/*
* let's set up for the Shift+Alt and Control+Alt keys.
* With these key combinations, we can do the International keyboard
* stuff, see the Microsoft MS DOS 5.0 manual pages 623-637.
*/
if (lo > 256 && (shift || control)) {
/*
* add 55 to Ctrl+Left thru Ctrl+Home when the shift key is pressed.
* this is not part of the International keyboard stuff, just a way
* to assign the horizontal scroll left and right funcs to cursor keys.
*/
if (shift) {
if (lo >= 371 && lo <= 374)
lo += 55;
/*
* if shift is down, map alt 1! thru alt =+ to shift alt 1! thru alt =+
*/
else if (lo >= 376 && lo <= 387)
lo += 86;
/*
* internation keyboard stuff
*/
else if (lo >= 272 && lo <= 309)
lo += 202;
}
}
/*
* the line feed is a special character that must be handled
* by the editor.
* don't let the eof character, 26, get thru either.
*/
if (lo == 10)
lo = 425;
else if (lo == 26)
lo = 256;
return( lo );
}
/*
* Name: getfunc
* Purpose: get the function assigned to key c
* Date: July 11, 1991
* Passed: c: key just pressed
* Notes: key codes less than 256 or 0x100 are not assigned a function.
* The codes in the range 0-255 are ASCII and extended ASCII chars.
*/
int getfunc( int c )
{
register int i = c;
if (i <= 256)
i = 0;
else
i = key_func[i-256];
return( i );
}
/*
* Name: record_on_off
* Purpose: save keystrokes in keystroke buffer
* Date: April 1, 1992
* Notes: -1 in .next field indicates the end of a recording
* -1 in .key field indicates the initial, unassigned macro key
* STROKE_LIMIT+1 in .next field indicates an unused space.
*/
void record_on_off( WINDOW *window )
{
int next;
int prev;
int line;
int key;
int func;
char line_buff[(MAX_COLS+2)*2]; /* buffer for char and attribute */
mode.record = !mode.record;
if (mode.record == TRUE) {
line = window->bottom_line;
show_avail_strokes( );
save_screen_line( 0, line, line_buff );
set_prompt( "Press the key that will play back this recording : ", line );
/*
* get the candidate macro key and look up the function assigned to it.
*/
key = getkey( );
func = getfunc( key );
/*
* the key must be an unused, recognized function key or a function
* key assigned to a previously defined macro. we also need room
* in the macro structure.
*/
if (key <= 256 || (func != 0 && func != PlayBack)) {
error( WARNING, line, "Cannot assign a recording to this key" );
mode.record = FALSE;
} else if (g_status.stroke_count == 0) {
error( WARNING, line, "No more room in recording buffer" );
mode.record = FALSE;
} else {
/*
* everything is everything so far, just check for a prev macro
*/
prev = OK;
if (func == PlayBack) {
set_prompt( "Overwrite recording assigned to this key? (y/n) : ",
line );
if (get_yn( ) == A_NO) {
prev = ERROR;
mode.record = FALSE;
}
}
if (prev == OK) {
g_status.recording_key = key;
next = macro.first_stroke[key-256];
/*
* if key has already been assigned to a macro, clear macro def.
*/
if (next != STROKE_LIMIT+1) {
do {
prev = next;
next = macro.strokes[next].next;
macro.strokes[prev].key = MAX_KEYS+1;
macro.strokes[prev].next = STROKE_LIMIT+1;
++g_status.stroke_count;
} while (next != -1);
show_avail_strokes( );
}
/*
* find the first open space and initialize
*/
for (next=0; macro.strokes[next].next != STROKE_LIMIT+1;)
next++;
macro.first_stroke[key-256] = next;
macro.strokes[next].key = -1;
macro.strokes[next].next = -1;
key_func[key-256] = PlayBack;
s_output( "Recording", g_display.mode_line, 23,
g_display.mode_color | 0x80 );
}
}
restore_screen_line( 0, line, line_buff );
}
/*
* the flashing "Recording" and the stroke count write over the modes.
* when we get thru defining a macro, redisplay the modes.
*/
if (mode.record == FALSE) {
memset( line_buff, ' ', 36 );
line_buff[36] = '\0';
s_output( line_buff, g_display.mode_line, 23, g_display.mode_color );
show_indent_mode( );
show_sync_mode( );
show_search_case( );
show_wordwrap_mode( );
/*
* let's look at the macro. if the first .key of the macro is
* still -1, which is the initial unassigned key in a macro, reset
* the macro so other keys may be assigned to this node.
*/
key = g_status.recording_key;
if (key != 0) {
next = macro.first_stroke[key-256];
if (macro.strokes[next].key == -1) {
macro.strokes[next].key = MAX_KEYS+1;
macro.strokes[next].next = STROKE_LIMIT+1;
macro.first_stroke[key-256] = STROKE_LIMIT+1;
if (getfunc( key ) == PlayBack)
key_func[key-256] = 0;
}
}
g_status.recording_key = 0;
}
}
/*
* Name: record_keys
* Purpose: save keystrokes in keystroke buffer
* Date: April 1, 1992
* Notes: -1 in .next field indicates the end of a recording
* STROKE_LIMIT+1 in .next field indicates an unused space.
*/
void record_keys( int line )
{
register int next;
register int prev;
int key;
int func;
if (mode.record == TRUE) {
if (g_status.stroke_count == 0)
error( WARNING, line, "No more room in recording buffer" );
else {
key = g_status.key_pressed;
func = getfunc( key );
if (func != RecordMacro && func != SaveMacro && func != LoadMacro &&
func != ClearAllMacros) {
/*
* a -1 in the next field marks the end of the keystroke recording.
*/
next = macro.first_stroke[g_status.recording_key - 256];
if (macro.strokes[next].next != STROKE_LIMIT+1) {
while (macro.strokes[next].next != -1)
next = macro.strokes[next].next;
}
prev = next;
/*
* now find an open space to record the current key.
*/
if (macro.strokes[next].key != -1) {
for (; next < STROKE_LIMIT &&
macro.strokes[next].next != STROKE_LIMIT+1;)
next++;
if (next == STROKE_LIMIT) {
for (next=0; next < prev &&
macro.strokes[next].next != STROKE_LIMIT+1;)
next++;
}
}
if (next == prev && macro.strokes[prev].key != -1)
error( WARNING, line, "No more room for keys" );
else {
/*
* check for recursion.
* next == prev if we are recording the initial macro node.
*/
if (g_status.recording_key != key) {
macro.strokes[prev].next = next;
macro.strokes[next].next = -1;
macro.strokes[next].key = key;
g_status.stroke_count--;
show_avail_strokes( );
} else
error( WARNING, line, "Cannot record this key" );
}
}
}
}
}
/*
* Name: show_avail_strokes
* Purpose: show available free key strokes in lite bar at bottom of screen
* Date: April 1, 1992
*/
void show_avail_strokes( void )
{
char strokes[MAX_COLS];
s_output( " Avail strokes = ", g_display.mode_line, 34, g_display.mode_color );
itoa( g_status.stroke_count, strokes, 10 );
s_output( " ", g_display.mode_line, 52, g_display.mode_color );
s_output( strokes, g_display.mode_line, 52, g_display.mode_color );
}
/*
* Name: save_strokes
* Purpose: save strokes to a file
* Date: April 1, 1992
*/
void save_strokes( WINDOW *window )
{
FILE *fp; /* file to be written */
char name[MAX_COLS+2]; /* file name */
char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */
int rc;
int prompt_line;
int fattr;
name[0] = '\0';
prompt_line = window->bottom_line;
save_screen_line( 0, prompt_line, line_buff );
if ((rc = get_name( "Name for macro file name: ", prompt_line, name,
g_display.message_color )) == OK) {
/*
* make sure it is OK to overwrite any existing file
*/
rc = get_fattr( name, &fattr );
if (rc == OK) {
set_prompt( "Overwrite existing file? (y/n): ", prompt_line );
if (get_yn( ) != A_YES || change_mode( name, prompt_line ) == ERROR)
rc = ERROR;
}
if (rc != ERROR) {
if ((fp = fopen( name, "wb" )) != NULL) {
fwrite( ¯o, sizeof( MACRO ), 1, fp );
fclose( fp );
}
}
}
restore_screen_line( 0, prompt_line, line_buff );
}
/*
* Name: load_strokes
* Purpose: load strokes from a file
* Date: April 1, 1992
* Notes: show the user a file pick list. I can never remember macro
* file names or the directory in which they hide. might as well
* give the user a file pick list.
*/
void load_strokes( WINDOW *window )
{
FILE *fp; /* file to be read */
char dname[MAX_COLS]; /* directory search pattern */
char stem[MAX_COLS]; /* directory stem */
int rc;
dname[0] = '\0';
if (get_name( "Search path or file name for macro file : ",
window->bottom_line, dname, g_display.message_color ) == OK) {
if (validate_path( dname, stem ) == OK) {
rc = list_and_pick( dname, stem, window );
/*
* if everything is everything, load in the file selected by user.
*/
if (rc == OK) {
if ((fp = fopen( dname, "rb" )) != NULL && ceh.flag != ERROR) {
fread( ¯o, sizeof( MACRO ), 1, fp );
fclose( fp );
}
if (ceh.flag == OK)
connect_macros( );
}
} else
error( WARNING, window->bottom_line, "Invalid path or file name" );
}
}
/*
* Name: clear_macro
* Purpose: reset all macro buffers, pointers, functions.
* Date: April 1, 1992
* Notes: reset the available macro stroke count. reset all fields in
* macro structure. clear any keys assigned to macros in the
* function assignment array.
*/
void clear_macros( WINDOW *arg_filler )
{
register int i;
g_status.stroke_count = STROKE_LIMIT;
for (i=0; i<STROKE_LIMIT; i++) {
macro.strokes[i].next = STROKE_LIMIT+1;
macro.strokes[i].key = MAX_KEYS+1;
}
for (i=0; i<MAX_KEYS; i++) {
macro.first_stroke[i] = STROKE_LIMIT+1;
if (key_func[i] == PlayBack)
key_func[i] = 0;
}
}
/*
* Name: connect_macros
* Purpose: hook up all (if any) macros to the function key definition table
* Date: April 1, 1992
* Notes: we need to connect all macro definitions to the key definition
* table in the startup routine or when we read in a new macro
* definition file. the predefined func assignments take
* precedence over macro definitions.
*/
void connect_macros( void )
{
register int i;
/*
* reset the key function assignment array. initially, no keys may be
* assigned to a macro.
*/
for (i=0; i<MAX_KEYS; i++)
if (key_func[i] == PlayBack)
key_func[i] = 0;
/*
* now, find out how many free keystrokes are in the macro structure.
*/
g_status.stroke_count = 0;
for (i=0; i<STROKE_LIMIT; i++)
if (macro.strokes[i].next == STROKE_LIMIT+1)
++g_status.stroke_count;
/*
* go thru the first stroke list to see if any key has been assigned to
* a macro and connect the macro def to the key. predefined function
* assignments take precedence over macros.
*/
for (i=0; i<MAX_KEYS; i++) {
if (macro.first_stroke[i] != STROKE_LIMIT+1)
if (key_func[i] == 0)
key_func[i] = PlayBack;
}
}
/*
* Name: findfirst
* Purpose: find the first file matching a pattern using DOS interrupt
* Date: January 6, 1992
* Passed: dta: disk transfer address
* path: path to search for files
* Notes: return codes for findfirst:
* 0 no error
* 2 file is invalid or does not exist
* 3 path is invalid or does not exist
* 18 no matching directory entry was found
* -1 check the critical error flag for critical errors
*/
int findfirst( DTA far *dta, char far *path, int f_attr )
{
void far *old_dta;
void far *new_dta;
int rc;
new_dta = (void far *)dta;
_asm {
; save the old dta
mov ah, 0x2f ; DOS get dta
int 0x21 ; DOS interrupt
mov WORD PTR old_dta, bx ; save OFFSET of old DTA
mov ax, es
mov WORD PTR old_dta+2, ax ; save SEGMENT of old DTA
; set the new dta
push ds ; save ds
mov dx, WORD PTR new_dta ; get OFFSET of new dta
mov ax, WORD PTR new_dta+2 ; get SEGMENT of new dta
mov ds, ax ; put it in ds
mov ah, 0x1a ; DOS set dta
int 0x21 ; DOS interrupt
pop ds ; get back ds
; find first matching file
push ds ; save ds
mov cx, WORD PTR f_attr ; file attributes to search for
mov dx, WORD PTR path ; get OFFSET of path
mov ax, WORD PTR path+2 ; get SEGMENT of path
mov ds, ax ; put it in ds
mov ah, 0x4e ; DOS find first file
int 0x21 ; DOS interrupt
pop ds ; get back ds
; save the return code
jc an_error ; carry is set if an error occured
xor ax, ax ; zero out ax, return OK if no error
an_error:
mov WORD PTR rc, ax ; save the return code
; get back old dta
push ds ; save ds
mov dx, WORD PTR old_dta ; get OFFSET of old dta
mov ax, WORD PTR old_dta+2 ; get SEGMENT of old dta
mov ds, ax ; put it in ds
mov ah, 0x1a ; DOS set dta
int 0x21 ; DOS interrupt
pop ds ; get back ds
}
if (ceh.flag == ERROR)
rc = ERROR;
return( rc );
}
/*
* Name: findnext
* Purpose: find the next file matching a pattern using DOS interrupt
* Date: January 6, 1992
* Passed: dta: disk transfer address
* path: path to search for files
* Notes: findfirst() MUST be called before calling this function.
* return codes for findnext:
* 0 no error
* 2 path is invalid or does not exist
* 18 no matching directory entry was found
* -1 check the critical error flag for critical errors
*/
int findnext( DTA far *dta )
{
void far *old_dta;
void far *new_dta;
int rc;
new_dta = (void far *)dta;
_asm {
; save the old dta
mov ah, 0x2f ; DOS get dta
int 0x21 ; DOS interrupt
mov WORD PTR old_dta, bx ; save OFFSET of old DTA
mov ax, es
mov WORD PTR old_dta+2, ax ; save SEGMENT of old DTA
; set the new dta
push ds ; save ds
mov dx, WORD PTR new_dta ; get OFFSET of new dta
mov ax, WORD PTR new_dta+2 ; get SEGMENT of new dta
mov ds, ax ; put it in ds
mov ah, 0x1a ; DOS set dta
int 0x21 ; DOS interrupt
pop ds ; get back ds
; find next matching file
mov ah, 0x4f ; DOS find first file
int 0x21 ; DOS interrupt
; save the return code
jc an_error ; carry is set if an error occured
xor ax, ax ; zero out ax, return OK if no error
an_error:
mov WORD PTR rc, ax ; save the return code
; get back old dta
push ds ; save ds
mov dx, WORD PTR old_dta ; get OFFSET of old dta
mov ax, WORD PTR old_dta+2 ; get SEGMENT of old dta
mov ds, ax ; put it in ds
mov ah, 0x1a ; DOS set dta
int 0x21 ; DOS interrupt
pop ds ; get back ds
}
if (ceh.flag == ERROR)
rc = ERROR;
return( rc );
}