/******************* 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 * Date: June 5, 1992, version 2.0 * * 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 /* for renaming files */ #ifdef __TURBOC__ #include /* for searching the current path */ #endif #include /* for direct BIOS keyboard input */ #if defined( __TURBOC__ ) #include /* for memory allocation */ #elif defined( __MSC__ ) #include /* for memory allocation */ #endif #include /* for file attribute code */ #include /* open flags */ #if defined( __MSC__ ) #include #include #include /* S_IWRITE etc */ #endif #include /* S_IWRITE etc */ #if defined( __MSC__ ) void (interrupt far *old_control_c)( void ); /* variable for old CNTL-C */ void (interrupt far *old_int1b)( void ); /* variable for old int 1b */ #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, Frank. */ 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 ); old_int1b = _dos_getvect( 0x1b ); _dos_setvect( 0x1b, ctrl_break ); 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 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: /* * fatal error */ strcpy( buff, main1 ); break; case WARNING: /* * warning */ strcpy( buff, main2 ); break; } /* * prepare the error message itself */ strcat( buff, message ); /* * tell the user how to continue editing if necessary */ if (kind == WARNING) /* * press a key */ strcat( buff, main3 ); /* * 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 , 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: Do nothing when control-C is pressed * Date: June 5, 1991 * Notes: Interrupt 23, the Control-C handler, is a MS DOS system function. * Since we want to use Control-C as a regular function key, * let's do absolutely nothing when Control-C is pressed. */ #if defined( __MSC__ ) void interrupt far harmless( void ) #else static int harmless(void) #endif { } /* * Name: ctrl_break * Purpose: Set our control-break flag when control-break is pressed. * Date: June 5, 1992 * Notes: Control-break is a little different from Control-C. When * Control-C is pressed, MS DOS processes it as soon as * possible, which may be quite a while. On the other hand, * when Control-break is pressed on IBM and compatibles, * interrupt 0x1b is generated immediately. Since an interrupt * is generated immediately, we can gain control of run-away * functions, like recursive macros, by checking our Control-break * flag. */ void interrupt far ctrl_break( void ) { g_status.control_break = TRUE; } /* * 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( 0x1b, old_int1b ); _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) /* * out of memory */ error( FATAL, g_display.nlines, main4 ); #else if ((g_status.start_mem = farmalloc(space)) == NULL) error( FATAL, g_display.nlines, main4 ); #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: 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... * * negative move contact me */ error( WARNING, g_display.nlines, main5 ); 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 ); /* * file is write protected. overwrite anyway (y/n)? */ set_prompt( main6, 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) { /* * file not found or error loading file */ combine_strings( buff, main7a, name, main7b ); 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 ) { /* * file too big */ combine_strings( buff, main8a, name, main8b ); 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) { /* * file too big */ combine_strings( buff, main10a, name, main10b ); 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. */ int 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( ); return( OK ); } /* * 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... * * if Ctrl-Break is pressed, it returns 0xffff as the key pressed. * let's set it to CONTROL_BREAK == 269 and do nothing. */ int getkey( void ) { unsigned key, num_lock, control, shift; register scan; register unsigned lo; /* * this code is used during testing to check the amount of memory * in the near heap. * * char buff[MAX_COLS]; * ultoa( _memavl( ), buff, 10 ); * s_output( "h= ", g_display.mode_line, 23, g_display.mode_color ); * s_output( buff, g_display.mode_line, 25, g_display.mode_color ); */ /* * _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; /* * now test for Control-Break. let's set this to do nothing in the * editor. manually map Control-Break to 269 - DO NOT assign * any function to 269. */ } else if (key == 0xffff) lo = CONTROL_BREAK; /* * 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) { /* * plain Grey Enter */ if (lo == 13 && !shift) lo = 285; /* * shift Grey Enter */ else if (lo == 13) lo = 298; /* * control Grey Enter */ else if (lo == 10) lo = 299; } /* * 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 * Passed: window: pointer to current window * 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. */ int record_on_off( WINDOW *window ) { register 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 ); /* * press key that will play back recording */ set_prompt( main11, 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)) { /* * cannot assign a recording to this key */ error( WARNING, line, main12 ); mode.record = FALSE; } else if (g_status.stroke_count == 0) { /* * no more room in recording buffer */ error( WARNING, line, main13 ); mode.record = FALSE; } else { /* * everything is everything so far, just check for a prev macro */ prev = OK; if (func == PlayBack) { /* * overwrite recording (y/n)? */ set_prompt( main14, 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; /* * recording */ s_output( main15, 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_smarttab_mode( ); 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; } return( OK ); } /* * Name: record_keys * Purpose: save keystrokes in keystroke buffer * Date: April 1, 1992 * Passed: line: line to display prompts * 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) /* * no more room in recording buffer */ error( WARNING, line, main13 ); 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) /* * no more room in recording buffer */ error( WARNING, line, main13 ); else { /* * next == prev if we are recording the initial macro node. */ macro.strokes[prev].next = next; macro.strokes[next].next = -1; macro.strokes[next].key = key; g_status.stroke_count--; show_avail_strokes( ); } } } } } /* * 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( main18, 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 * Passed: window: pointer to current window */ int 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 */ register int rc; int prompt_line; int fattr; name[0] = '\0'; prompt_line = window->bottom_line; save_screen_line( 0, prompt_line, line_buff ); /* * name for macro file */ if ((rc = get_name( main19, 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) { /* * overwrite existing file */ set_prompt( main20, 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 ); return( OK ); } /* * Name: load_strokes * Purpose: load strokes from a file * Date: April 1, 1992 * Passed: window: pointer to current window * 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. */ int load_strokes( WINDOW *window ) { register FILE *fp; /* file to be read */ char dname[MAX_COLS]; /* directory search pattern */ char stem[MAX_COLS]; /* directory stem */ register int rc; dname[0] = '\0'; /* * search path for macro file */ if (get_name( main21, 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 /* * invalid path or file name */ error( WARNING, window->bottom_line, main22 ); } return( OK ); } /* * 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. */ int clear_macros( WINDOW *arg_filler ) { register int i; g_status.stroke_count = STROKE_LIMIT; for (i=0; i