home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / editors / tde150.arj / MAIN.C < prev    next >
C/C++ Source or Header  |  1992-04-01  |  55KB  |  1,719 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - hardware dependent module
  10.  * Purpose: This file contains all the code that needs to be different on
  11.  *           different hardware.
  12.  * File:    hwibm.c
  13.  * Author:  Douglas Thomson
  14.  * System:  This particular version is for the IBM PC and close compatibles.
  15.  *           It write directly to video RAM, so it is faster than other
  16.  *           techniques, but will cause "snow" on most CGA cards. See the
  17.  *           file "hwibmcga.c" for a version that avoids snow.
  18.  *          The compiler is Turbo C 2.0, using one of the large data memory
  19.  *           models.
  20.  * Date:    October 10, 1989
  21.  * Notes:   This module has been kept as small as possible, to facilitate
  22.  *           porting between different systems.
  23.  */
  24. /*********************  end of original comments   ********************/
  25.  
  26.  
  27. /*
  28.  * These routines were rewritten for Microsoft C.  They are pretty much system
  29.  * dependent and pretty much Microsoft C dependent.  I also renamed this file
  30.  * "main.c" - easier to find the main function.
  31.  *
  32.  * New editor name:  tde, the Thomson-Davis Editor.
  33.  * Author:           Frank Davis
  34.  * Date:             June 5, 1991, version 1.0
  35.  * Date:             July 29, 1991, version 1.1
  36.  * Date:             October 5, 1991, version 1.2
  37.  * Date:             January 20, 1992, version 1.3
  38.  * Date:             February 17, 1992, version 1.4
  39.  * Date:             April 1, 1992, version 1.5
  40.  *
  41.  * This modification of Douglas Thomson's code is released into the
  42.  * public domain, Frank Davis.  You may distribute it freely.
  43.  */
  44.  
  45.  
  46. char *greatest_composer_ever = "W. A. Mozart, 1756-1791";
  47.  
  48.  
  49. #include "tdestr.h"             /* tde types */
  50. #include "common.h"
  51. #include "define.h"
  52. #include "help.h"
  53. #include "tdefunc.h"
  54. #include <dos.h>                /* for renaming files */
  55. #ifdef __TURBOC__
  56.    #include <dir.h>             /* for searching the current path */
  57. #endif
  58. #include <bios.h>               /* for direct BIOS keyboard input */
  59. #if defined( __TURBOC__ )
  60.    #include <alloc.h>           /* for memory allocation */
  61. #elif defined( __MSC__ )
  62.    #include <malloc.h>          /* for memory allocation */
  63. #endif
  64. #include <io.h>                 /* for file attribute code */
  65. #include <fcntl.h>              /* open flags */
  66. #if defined( __MSC__ )
  67.    #include <bios.h>
  68.    #include <errno.h>
  69.    #include <sys\types.h>       /* S_IWRITE etc */
  70. #endif
  71. #include <sys\stat.h>           /* S_IWRITE etc */
  72.  
  73. #if defined( __MSC__ )
  74. void (interrupt far *old_control_c)( void );  /* variable for old CNTL-C */
  75. #endif
  76.  
  77. int full_screen_buffer[2000];  /* 25 lines * 80 columns = 2000 characters */
  78.                                /* (make it an int for the attribute)      */
  79.  
  80.  
  81. /*
  82.  * Default color settings.  Incidentally, I'm color blind (mild red-green) and
  83.  * the default colors look fine to me.
  84.  */
  85. static int colors[2][12] = {
  86.    { HERC_REVERSE, HERC_NORMAL, HERC_REVERSE, HERC_REVERSE, HERC_HIGH,
  87.      HERC_NORMAL, HERC_NORMAL, HERC_HIGH, HERC_HIGH, HERC_HIGH, HERC_REVERSE,
  88.      HERC_REVERSE },
  89.    { COLOR_HEAD, COLOR_TEXT, COLOR_MODE, COLOR_BLOCK, COLOR_MESSAGE,
  90.      COLOR_HELP, COLOR_DIAG, COLOR_EOF, COLOR_CURL, COLOR_RULER, COLOR_POINTER,
  91.      COLOR_TEXT }
  92. };
  93.  
  94.  
  95. /*
  96.  * original control-break checking flag
  97.  */
  98. static int s_cbrk;
  99.  
  100.  
  101. /*
  102.  * Name:    main
  103.  * Purpose: To do any system dependent command line argument processing,
  104.  *           and then call the main editor function.
  105.  * Date:    October 10, 1989
  106.  * Passed:  argc:   number of command line arguments
  107.  *          argv:   text of command line arguments
  108.  */
  109. void main( int argc, char *argv[] )
  110. {
  111. #if defined( __MSC__ )
  112.    union REGS inregs, outregs;
  113. #endif
  114.  
  115.    g_status.found_first = FALSE;
  116.    g_status.arg         = 1;
  117.    g_status.argc        = argc;
  118.    g_status.argv        = argv;
  119.  
  120.    /*
  121.     * trap control-break to make it harmless, and turn checking off
  122.     */
  123. #if defined( __MSC__ )
  124.    inregs.h.ah = 0x33;
  125.    inregs.h.al = 0;
  126.    intdos( &inregs, &outregs );
  127.    s_cbrk = outregs.h.dl;
  128.    old_control_c = _dos_getvect( 0x23 );
  129.    _dos_setvect( 0x23, harmless );
  130.    inregs.h.ah = 0x33;
  131.    inregs.h.al = 1;
  132.    inregs.h.dl = 0;
  133.    intdos( &inregs, &outregs );
  134. #else
  135.    s_cbrk = getcbrk( );
  136.    ctrlbrk( harmless );
  137.    setcbrk( 0 );
  138. #endif
  139.  
  140.    initialize( );
  141.    editor( );
  142.    terminate( );
  143. }
  144.  
  145.  
  146. /*
  147.  * Name:    error
  148.  * Purpose: To report an error, and usually make the user type <ESC> before
  149.  *           continuing.
  150.  * Date:    June 5, 1991
  151.  * Passed:  kind:   an indication of how serious the error was:
  152.  *                      WARNING: continue after pressing a key
  153.  *                      FATAL:   abort the editor
  154.  *          line:    line to display message
  155.  *          message: string to be printed
  156.  * Notes:   Show user the message and ask for a key if needed.
  157.  */
  158. void error( int kind, int line, char *message )
  159. {
  160. char buff[MAX_COLS+2];          /* somewhere to store error before printing */
  161. register int c;                 /* character entered by user to continue */
  162. char line_buff[(MAX_COLS+2)*2]; /* buffer for char and attribute  */
  163.  
  164.    /*
  165.     * tell the user what kind of an error it is
  166.     */
  167.    switch (kind) {
  168.       case FATAL:
  169.          strcpy( buff, "Fatal error: " );
  170.          break;
  171.      case WARNING:
  172.          strcpy( buff, "Warning: " );
  173.          break;
  174.    }
  175.  
  176.    /*
  177.     * prepare the error message itself
  178.     */
  179.    strcat( buff, message );
  180.  
  181.    /*
  182.     * tell the user how to continue editing if necessary
  183.     */
  184.    if (kind == WARNING)
  185.       strcat( buff, ": press a key" );
  186.  
  187.    /*
  188.     * output the error message
  189.     */
  190.    save_screen_line( 0, line, line_buff );
  191.    set_prompt( buff, line );
  192.  
  193.    if (kind == FATAL) {
  194.       /*
  195.        * no point in making the user type <ESC>, since the program is
  196.        *  about to abort anyway...
  197.        */
  198.       terminate( );
  199.       exit( 1 );
  200.    }
  201.  
  202.    c = getkey( );
  203.    restore_screen_line( 0, line, line_buff );
  204.    if (g_status.wrapped) {
  205.       g_status.wrapped = FALSE;
  206.       show_search_message( CLR_SEARCH, g_display.mode_color );
  207.    }
  208. }
  209.  
  210.  
  211. /*
  212.  * Name:    harmless
  213.  * Purpose: To process control-break by ignoring it, so that the editor is
  214.  *           not aborted.
  215.  * Date:    June 5, 1991
  216.  */
  217. #if defined( __MSC__ )
  218. int interrupt far harmless( void )
  219. #else
  220. static int harmless(void)
  221. #endif
  222. {
  223.    return( 1 );   /* ignore */
  224. }
  225.  
  226.  
  227. /*
  228.  * Name:    terminate
  229.  * Purpose: To free all dynamic structures and unload anything we loaded
  230.  * Date:    June 5, 1991
  231.  */
  232. void terminate( void )
  233. {
  234. union REGS inregs, outregs;
  235. register WINDOW     *wp;        /* register for scanning windows */
  236. WINDOW              *w;         /* free window */
  237. register file_infos *fp;        /* register for scanning files */
  238. file_infos          *f;         /* free files */
  239.  
  240.    /*
  241.     * restore control-break checking
  242.     */
  243. #if defined( __MSC__ )
  244.    _dos_setvect( 0x23, old_control_c );
  245.    inregs.h.ah = 0x33;
  246.    inregs.h.al = 1;
  247.    inregs.h.dl = (char)s_cbrk;
  248.    intdos( &inregs, &outregs );
  249. #else
  250.    setcbrk( s_cbrk );
  251. #endif
  252.  
  253.    /*
  254.     * free the text buffer
  255.     */
  256.    hfree( (void huge *)g_status.start_mem );
  257.  
  258.    /*
  259.     * free the file structures if not already free.
  260.     */
  261.    fp = g_status.file_list;
  262.    while (fp != NULL) {
  263.       f  = fp;
  264.       fp = fp->next;
  265.       free( f );
  266.    }
  267.  
  268.    /*
  269.     * free the window structures if not already free.
  270.     */
  271.    wp = g_status.window_list;
  272.    while (wp != NULL) {
  273.       w  = wp;
  274.       wp = wp->next;
  275.       free( w );
  276.    }
  277.  
  278.    /*
  279.     * reset the cursor size and unload the 83/84 key keyboard utility
  280.     */
  281.    set_cursor_size( mode.cursor_size == SMALL_INS ? g_display.insert_cursor :
  282.                                                     g_display.overw_cursor );
  283.    if (mode.enh_kbd == FALSE)
  284.       simulate_enh_kbd( 0 );
  285. }
  286.  
  287.  
  288. /*
  289.  * Name:    hw_initialize
  290.  * Purpose: To initialize the display ready for editor use.
  291.  * Date:    June 5, 1991
  292.  */
  293. void hw_initialize( void )
  294. {
  295. struct vcfg cfg;       /* defined in .h */
  296. unsigned paragraphs;
  297. long space;            /* amount of memory to use */
  298. register int *clr;
  299.  
  300.    /*
  301.     * set up screen size
  302.     */
  303.    g_display.ncols     = MAX_COLS;
  304.    g_display.nlines    = MAX_LINES - 1;
  305.    g_display.mode_line = MAX_LINES;
  306.    g_display.line_length = MAX_LINE_LENGTH;
  307.  
  308.    /*
  309.     * work out what kind of display is in use, and set attributes and
  310.     *  display address accordingly. Note that this will only work with
  311.     *  close IBM compatibles.
  312.     */
  313.  
  314.    video_config( &cfg );
  315.    g_display.display_address = (char far *)cfg.videomem;
  316.  
  317.    /*
  318.     * Use an integer pointer to go thru the color array for setting up the
  319.     * various color fields.
  320.     */
  321.    clr = cfg.color == FALSE ? &colors[0][0] : &colors[1][0];
  322.  
  323.    g_display.head_color    = *clr++;
  324.    g_display.text_color    = *clr++;
  325.    g_display.mode_color    = *clr++;
  326.    g_display.block_color   = *clr++;
  327.    g_display.message_color = *clr++;
  328.    g_display.help_color    = *clr++;
  329.    g_display.diag_color    = *clr++;
  330.    g_display.eof_color     = *clr++;
  331.    g_display.curl_color    = *clr++;
  332.    g_display.ruler_color   = *clr++;
  333.    g_display.ruler_pointer = *clr++;
  334.    g_display.hilited_file  = *clr;
  335.  
  336.    /*
  337.     * grab all the available memory for the text buffer
  338.     */
  339. #if defined( __MSC__ )
  340.    _dos_allocmem( 0xffff, ¶graphs );
  341.    /*
  342.     * A paragraph is 16 bytes.  Convert paragraphs to bytes by shifting left
  343.     * 4 bits.
  344.     */
  345.    space = (long)paragraphs << 4;
  346.  
  347.    /*
  348.     * if using Microsoft C, allocate all available memory.  If debugging in
  349.     * in QC 2.5, uncomment the next lines so the debugger will have some room.
  350.     */
  351. /*   if (space > 12000l)
  352.       space = 12000l;  */
  353.    if (space <= 0)
  354.       return;
  355. #else
  356.    space = farcoreleft() - 30000L;
  357. #endif
  358.  
  359. #if defined( __MSC__ )
  360.    if ((g_status.start_mem = (text_ptr)halloc( space, sizeof( char ))) == NULL)
  361.       error( FATAL, g_display.nlines, "out of memory???" );
  362. #else
  363.    if ((g_status.start_mem = farmalloc(space)) == NULL)
  364.       error( FATAL, g_display.nlines, "out of memory???" );
  365. #endif
  366.    g_status.max_mem = addltop( space, g_status.start_mem );
  367. }
  368.  
  369.  
  370. /*
  371.  *   Video BIOS Data Areas
  372.  *   The BIOS routines maintain several dynamic variables in an area of
  373.  *   memory called the Video Display Data Area.  The following contains a
  374.  *   summary of these variables' addresses, their symbolic names, and
  375.  *   their contents.  All addresses are relative to the 0x0000h segment.
  376.  *   From the IBM Technical Reference and other sources.
  377.  *
  378.  *   Address  Name           Type   Description
  379.  *   0x0449   CRT_MODE       Byte   Current BIOS video number
  380.  *   0x044a   CRT_COLS       Word   Number of displayed character columns
  381.  *   0x044c   CRT_LEN        Word   Size of video buffer in bytes
  382.  *   0x044e   CRT_START      Word   Offset of start of video buffer
  383.  *   0x0450   CURSOR_POSN    Word   Array of eight words containing the cursor
  384.  *                                    position for each of eight possible
  385.  *                                    video pages.  The high-order byte of
  386.  *                                    each word contains the character row,
  387.  *                                    the low-order byte the character column
  388.  *   0x0460   CURSOR_MODE    Word   Starting and ending lines for alphanumeric
  389.  *                                    cursor.  The high-order byte contains
  390.  *                                    the starting (top) line; the low-order
  391.  *                                    byte contains the ending (bottom) line
  392.  *   0x0462   ACTIVE_PAGE    Byte   Currently displayed video page number
  393.  *   0x0463   ADDR_6845      Word   I/O port address of CRT Controller's
  394.  *                                    Address register (3B4h for mono;
  395.  *                                    3D4h for color)
  396.  *   0x0465   CRT_MODE_SET   Byte   Current value for Mode Control register
  397.  *                                    (3B8h on MDA, 3D8h on CGA).  On the
  398.  *                                    EGA and VGA, the value emulates those
  399.  *                                    used on the MDA and CGA.
  400.  *   0x0466   CRT_PALETTE    Byte   Current value for the CGA Color Select
  401.  *                                    register (3D9h).  On the EGA and VGA,
  402.  *                                    the value emulates those used on the
  403.  *                                    MDA and CGA.
  404.  *   0x0467   io_rom_init    Word   Pointer to optional i/o rom init routine
  405.  *   0x0469   io_rom_seg     Word   Pointer to io rom segment
  406.  *   0x046b   intr_flag      Byte   Flag to indicate an interrupt happened
  407.  *   0x046c   timer_low      Word   Low word of timer count
  408.  *   0x046e   timer_high     Word   High word of timer count
  409.  *   0x0470   timer_ofl      Byte   Timer has rolled over since last count
  410.  *   0x0471   bios_break     Byte   Bit 7 = 1 if Break Key has been hit
  411.  *   0x0472   reset_flag     Word   Word = 1234h if keyboard reset underway
  412.  *   0x0484   ROWS           Byte   Number of displayed character rows - 1
  413.  *   0x0485   POINTS         Word   Height of character matrix
  414.  *   0x0487   INFO           Byte   EGA and VGA display data
  415.  *   0x0488   INFO_3         Byte   Configuration switches for EGA and VGA
  416.  *   0x0489   flags          Byte   Miscellaneous flags
  417.  *   0x0496   kb_flag_3      Byte   Additional keyboard flag
  418.  *   0x048A   DCC            Byte   Display Combination Code
  419.  *   0x04A8   SAVE_PTR       Dword  Pointer to BIOS save area
  420.  *
  421. */
  422. void video_config( struct vcfg *cfg )
  423. {
  424. #pragma pack( 1 )    /* Use pragma to force packing on byte boundaries. */
  425.  
  426. struct LOWMEMVID
  427. {
  428.    char     vidmode;           /* 0x449 */
  429.    unsigned scrwid;            /* 0x44A */
  430.    unsigned scrlen;            /* 0x44C */
  431.    unsigned scroff;            /* 0x44E */
  432.    struct   LOCATE
  433.    {
  434.       unsigned char col;
  435.       unsigned char row;
  436.    } csrpos[8];                /* 0x450 */
  437.    struct   CURSIZE
  438.    {
  439.       unsigned char end;
  440.       unsigned char start;
  441.    } csrsize;                  /* 0x460 */
  442.    char      page;             /* 0x462 */
  443.    unsigned  addr_6845;        /* 0x463 */
  444.    char      crt_mode_set;     /* 0x465 */
  445.    char      crt_palette[30];  /* 0x466 */
  446.    char      rows;             /* 0x484 */
  447.    unsigned  points;           /* 0x485 */
  448.    char      ega_info;         /* 0x487 */
  449.    char      info_3;           /* 0x488 */
  450.    char      skip[13];         /* 0x489 */
  451.    char      kb_flag_3;        /* 0x496 */
  452. } vid;
  453. struct LOWMEMVID _far *pvid = &vid;
  454. #pragma pack( )    /* revert to previously defined pack pragma. */
  455.  
  456. union REGS in, out;
  457. unsigned char temp, active_display;
  458.  
  459.    /*
  460.     * Move system information into our video structure.
  461.     */
  462.    _fmemmove( pvid, (void far *)0x00000449l, sizeof( vid ) );
  463.  
  464.    cfg->rescan = FALSE;
  465.    in.x.ax =  0x1a00;
  466.    int86( VIDEO_INT, &in, &out );
  467.    temp = out.h.al;
  468.    active_display = out.h.bl;
  469.    if (temp == 0x1a && (active_display == 7 || active_display == 8))
  470.       g_display.adapter = VGA;
  471.    else {
  472.       in.h.ah =  0x12;
  473.       in.h.bl =  0x10;
  474.       int86( VIDEO_INT, &in, &out );
  475.       if (out.h.bl != 0x10) {         /* EGA */
  476.          if (vid.ega_info & 0x08)
  477.             g_display.adapter = vid.addr_6845 == 0x3d4 ? CGA : MDA;
  478.          else
  479.             g_display.adapter = EGA;
  480.       } else if (vid.addr_6845 == 0x3d4)
  481.          g_display.adapter = CGA;
  482.       else
  483.          g_display.adapter = MDA;
  484.    }
  485.  
  486.    if (g_display.adapter == CGA || g_display.adapter == EGA) {
  487.       if (g_display.adapter == CGA)
  488.          cfg->rescan = TRUE;
  489.       g_display.insert_cursor = mode.cursor_size == SMALL_INS ? 0x0607 : 0x0407;
  490.       g_display.overw_cursor = mode.cursor_size == SMALL_INS ? 0x0407 : 0x0607;
  491.    } else {
  492.       g_display.insert_cursor = mode.cursor_size == SMALL_INS ? 0x0b0c : 0x070b;
  493.       g_display.overw_cursor = mode.cursor_size == SMALL_INS ? 0x070b : 0x0b0c;
  494.    }
  495.  
  496.    cfg->mode = vid.vidmode;
  497.    if (vid.addr_6845 == 0x3D4) {
  498.       cfg->color = TRUE;
  499.       cfg->videomem = (void far *)0xb8000000l;
  500.    } else {
  501.       cfg->color = FALSE;
  502.       cfg->videomem = (void far *)0xb0000000l;
  503.    }
  504.  
  505.    /*
  506.     * Get keyboard type.  Since there is no interrupt that determines
  507.     * keyboard type, use this method.  Look at bit 4 in keyboard flag3.
  508.     * This method is not always foolproof on clones.
  509.     */
  510.    if ((vid.kb_flag_3 & 0x10) != 0)
  511.       mode.enh_kbd = TRUE;
  512.    else
  513.       mode.enh_kbd = FALSE;
  514.    if (mode.enh_kbd == FALSE)
  515.       simulate_enh_kbd( 1 );
  516.    install_ceh( &ceh );
  517.    ceh.flag = OK;
  518. }
  519.  
  520.  
  521. /*
  522.  * Name:    set_cursor_size
  523.  * Purpose: To set cursor size according to insert mode.
  524.  * Date:    June 5, 1991
  525.  * Passed:  csize:  desired cursor size
  526.  * Notes:   use the global display structures to set the cursor size
  527.  */
  528. void set_cursor_size( int csize )
  529. {
  530.    _asm {
  531.         mov     ah, 1                   ; function 1 - set cursor size
  532.         mov     cx, WORD PTR csize      ; get cursor size ch:cl == top:bot
  533.         int     VIDEO_INT               ; video interrupt = 10h
  534.    }
  535. }
  536.  
  537.  
  538. /*
  539.  * Name:    hw_move
  540.  * Purpose: To move data from one place to another as efficiently as
  541.  *           possible.
  542.  * Date:    June 5, 1991
  543.  * Passed:  dest:   where to copy to
  544.  *          source: where to copy from
  545.  *          number: number of bytes to copy
  546.  * Notes:   moves may be (usually will be) overlapped.  Although we can
  547.  *          move up to 64k-1 bytes at once, we can safely  move only
  548.  *          0xfff0 bytes at one time.  Let's try only 0xf000.
  549.  */
  550. void hw_move( text_ptr dest, text_ptr source, long number )
  551. {
  552. unsigned long s, d;
  553.  
  554.    s = ptoul( source );
  555.    d = ptoul( dest );
  556.    if (number < 0)
  557.       /*
  558.        * this should never happen...
  559.        */
  560.       error( WARNING, g_display.nlines, "negative move - contact Frank Davis" );
  561.    else if (s == d)
  562.       /*
  563.        * nothing to be done
  564.        */
  565.       ;
  566.    else if (s > d) {
  567.       while (number > 0xF000L) {
  568.          dest = nptos( dest );
  569.          source = nptos( source );
  570.          _fmemmove( dest, source, 0xF000 );
  571.          dest = addltop( 0xF000L, dest );
  572.          source = addltop( 0xF000L, source );
  573.          number -= 0xF000L;
  574.       }
  575.       dest = nptos( dest );
  576.       source = nptos( source );
  577.       _fmemmove( dest, source, (unsigned)number );
  578.    } else {
  579.       source = addltop( number, source );
  580.       dest = addltop( number, dest );
  581.       while (number > 0xF000L) {
  582.          source = addltop( -0xF000L, source );
  583.          source = nptos( source );
  584.          dest = addltop( -0xF000L, dest );
  585.          dest = nptos( dest );
  586.          _fmemmove( dest, source, 0xF000 );
  587.          number -= 0xF000L;
  588.       }
  589.       source = addltop( -number, source );
  590.       dest = addltop( -number, dest );
  591.       source = nptos( source );
  592.       dest = nptos( dest );
  593.       _fmemmove( dest, source, (unsigned)number );
  594.    }
  595. }
  596.  
  597.  
  598. /*
  599.  * Name:    hw_fattrib
  600.  * Purpose: To determine the current file attributes.
  601.  * Date:    December 26, 1991
  602.  * Passed:  name: name of file to be checked
  603.  * Returns: use the function in the tdeasm file to get the DOS file
  604.  *          attributes.  get_fattr() returns 0 or OK if no error.
  605.  */
  606. int  hw_fattrib( char *name )
  607. {
  608. register int rc;
  609. int fattr;
  610.  
  611.    rc = get_fattr( name, &fattr );
  612.    return( rc == OK ? rc : ERROR );
  613. }
  614.  
  615.  
  616. /*
  617.  * Name:    change_mode
  618.  * Purpose: To prompt for file access mode.
  619.  * Date:    January 11, 1992
  620.  * Passed:  name:  name of file
  621.  *          line:  line to display message
  622.  * Returns: OK if file could be changed
  623.  *          ERROR otherwise
  624.  * Notes:   function is used to change file attributes for save_as function.
  625.  */
  626. int  change_mode( char *name, int line )
  627. {
  628. int result;
  629. int fattr;
  630. register int rc;
  631. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  632.  
  633.    rc = OK;
  634.    result = get_fattr( name, &fattr );
  635.    if (result != OK)
  636.       rc = ERROR;
  637.    else if (result == OK && fattr & READ_ONLY) {
  638.       /*
  639.        * file is read only
  640.        */
  641.       save_screen_line( 0, line, line_buff );
  642.       set_prompt( "File is write protected! Overwrite anyway? (y/n): ", line );
  643.       if (get_yn( ) != A_YES)
  644.          rc = ERROR;
  645.       if (rc == OK && set_fattr( name, ARCHIVE ) != OK)
  646.          rc = ERROR;
  647.       restore_screen_line( 0, line, line_buff );
  648.    }
  649.    return( rc );
  650. }
  651.  
  652.  
  653. /*
  654.  * Name:    write_file
  655.  * Purpose: To write text to a file
  656.  *           way.
  657.  * Date:    June 5, 1991
  658.  * Passed:  name:  name of disk file or device
  659.  *          mode:  fopen flags to be used in open
  660.  *          start: first character in text buffer
  661.  *          end:   last character (+1) in text buffer
  662.  *          block: write a file or a marked block
  663.  * Returns: OK, or ERROR if anything went wrong
  664.  */
  665. int  write_file( char *name, char *mode, text_ptr start, text_ptr end,
  666.                  int  block )
  667. {
  668. FILE *fp;       /* file to be written */
  669. char *p;
  670. register int rc;
  671. register int len;
  672. int bc, ec, last_c;
  673. file_infos *file;
  674. long lend;
  675. long number;
  676.  
  677.    rc = OK;
  678.    if ((fp = fopen( name, mode )) == NULL || ceh.flag == ERROR)
  679.       rc = ERROR;
  680.    else {
  681.       start = cpf( start );
  682.       if (block == LINE || block == BOX || block == STREAM) {
  683.          if (g_status.marked_file == NULL)
  684.             rc = ERROR;
  685.          else if (block == BOX || block == STREAM) {
  686.             file = g_status.marked_file;
  687.             bc = file->block_bc;
  688.             ec = file->block_ec;
  689.             last_c = ec + 1 - bc;
  690.          }
  691.          if (block == STREAM) {
  692.             len = linelen( start );
  693.             if (ptoul( start ) == ptoul( end )) {
  694.                end    = len > ec ? start + ec + 1 : start + len + 1;
  695.                start += bc < len ? bc : len;
  696.             } else {
  697.                len = linelen( end );
  698.                end += len > ec ? ec + 1 : len;
  699.             }
  700.          }
  701.       }
  702.       p = g_status.line_buff;
  703.       if (rc == OK) {
  704.          if (block == BOX) {
  705.             lend = ptoul( end );
  706.             for (;ptoul( start ) <= (unsigned long)lend && rc == OK;) {
  707.                g_status.copied = FALSE;
  708.                load_box_buff( p, start, bc, ec );
  709.                *(p+last_c) = '\n';
  710.                *(p+last_c+1) = CONTROL_Z;
  711.                len = find_CONTROL_Z( p );
  712.                if (fwrite( p, sizeof( char ), len, fp ) < (unsigned)len ||
  713.                    ceh.flag == ERROR)
  714.                   rc = ERROR;
  715.                if (rc == OK) {
  716.                   start = find_next( start );
  717.                   if (start == NULL)
  718.                      start = end + 1;
  719.                }
  720.             }
  721.             g_status.copied = FALSE;
  722.          } else {
  723.             number = ptoul( end ) - ptoul( start );
  724.             len = 0x0800;
  725.             start = nptos( start );
  726.             while (number > 0x0800L && rc != ERROR) {
  727.                _fmemcpy( full_screen_buffer, start, len );
  728.                if (fwrite(full_screen_buffer,sizeof(char),len,fp)<(unsigned)len
  729.                    || ceh.flag == ERROR)
  730.                   rc = ERROR;
  731.                if (rc != ERROR) {
  732.                   number -= 0x0800L;
  733.                   start += 0x0800;
  734.                   start = nptos( start );
  735.                }
  736.             }
  737.             /*
  738.              * now less than 2k is left, so finish off the write
  739.              */
  740.             if (rc != ERROR) {
  741.                len = (int)number;
  742.                _fmemcpy( full_screen_buffer, start, len );
  743.                if (fwrite(full_screen_buffer,sizeof(char),len,fp) < (unsigned)len
  744.                     || ceh.flag == ERROR)
  745.                   rc = ERROR;
  746.             }
  747.          }
  748.          if (ceh.flag != ERROR) {
  749.             if (fclose( fp ) != 0)
  750.                rc = ERROR;
  751.          }
  752.       }
  753.    }
  754.    return( rc );
  755. }
  756.  
  757.  
  758. /*
  759.  * Name:    hw_save
  760.  * Purpose: To save text to a file, eliminating trailing space on the
  761.  *           way.
  762.  * Date:    November 11, 1989
  763.  * Passed:  name:  name of disk file
  764.  *          start: first character in text buffer
  765.  *          end:   last character (+1) in text buffer
  766.  *          block: type of block defined
  767.  * Returns: OK, or ERROR if anything went wrong
  768.  */
  769. int hw_save( char *name, text_ptr start, text_ptr end, int block )
  770. {
  771. char *lf = "wb";
  772. char *crlf = "w";
  773. register char *write_mode;
  774.  
  775.    write_mode = mode.crlf == LF ? lf : crlf;
  776.    return( write_file( name, write_mode, start, end, block ) );
  777. }
  778.  
  779.  
  780. /*
  781.  * Name:    hw_append
  782.  * Purpose: To append text to a file.
  783.  * Date:    November 11, 1989
  784.  * Passed:  name:  name of disk file
  785.  *          start: first character in text buffer
  786.  *          end:   last character (+1) in text buffer
  787.  *          block: type of defined block
  788.  * Returns: OK, or ERROR if anything went wrong
  789.  */
  790. int hw_append( char *name, text_ptr start, text_ptr end, int block )
  791. {
  792. char *lf = "ab";
  793. char *crlf = "a";
  794. register char *append_mode;
  795.  
  796.    append_mode = mode.crlf == LF ? lf : crlf;
  797.    return( write_file( name, append_mode, start, end, block ) );
  798. }
  799.  
  800.  
  801. /*
  802.  * Name:    hw_load
  803.  * Purpose: To load a file into the text buffer.
  804.  * Date:    November 11, 1989
  805.  * Passed:  name:  name of disk file
  806.  *          start: first character in text buffer
  807.  *          limit: last available character in text buffer
  808.  *          end:   last character of file in text buffer
  809.  *          line:  line to display messages
  810.  * Returns: OK, or ERROR if anything went wrong
  811.  */
  812. int hw_load( char *name, text_ptr start, text_ptr limit, text_ptr *end,
  813.              int line )
  814. {
  815. int fd;                 /* file being read */
  816. register int length;    /* number of bytes actually read */
  817. register int rc;
  818. unsigned long ustart, ulimit;
  819. char buff[MAX_COLS+2];
  820. char line_buff[(MAX_COLS+2)*2]; /* buffer for char and attribute  */
  821.  
  822.    /*
  823.     * try reading the file
  824.     */
  825.    rc = OK;
  826.    if ((fd = open( name, O_RDONLY )) == ERROR || ceh.flag == ERROR) {
  827.       combine_strings( buff, "File '", name,"'not found or error loading file");
  828.       save_screen_line( 0, line, line_buff );
  829.       set_prompt( buff, line );
  830.       getkey( );
  831.       restore_screen_line( 0, line, line_buff );
  832.       rc = ERROR;
  833.    } else {
  834.       /*
  835.        * read the entire file, without going past end of buffer.
  836.        * Note that this means a file that is within 1K of the limit
  837.        *  will not be accepted.  length set to a number > 0 for first loop
  838.        */
  839.       limit = addltop( -1024, limit );
  840.       ulimit = ptoul( limit );
  841.       start = cpf( start );
  842.       for (length=1; rc == OK && length > 0;) {
  843.          ustart = ptoul( start );
  844.          if (ustart >= ulimit ) {
  845.             combine_strings( buff, "file '", name,
  846.                     "'too big.  FILE WILL BE TRUNCATED IF EDITED AND SAVED!" );
  847.             error( WARNING, line, buff );
  848.             rc = WARNING;
  849.          } else {
  850.  
  851.             /*
  852.              * length = number of bytes read.  since we only try to
  853.              * read 2048 bytes at one time, we should have no problem
  854.              * using the results from unsigned long arithmetic.
  855.              */
  856.             if ((length = read( fd, full_screen_buffer, 2048 )) == ERROR ||
  857.                ceh.flag == ERROR) {
  858.                combine_strings( buff, "error reading file '", name, "'" );
  859.                error( WARNING, line, buff );
  860.                rc = ERROR;
  861.             } else {
  862.                if (ustart + (unsigned long)length >= ulimit) {
  863.                   combine_strings( buff, "file '", name, "'too big" );
  864.                   error( WARNING, line, buff );
  865.                   rc = WARNING;
  866.                   length = (int)(ulimit - ustart);
  867.                }
  868.                _fmemcpy( start, full_screen_buffer, length );
  869.                start = addltop( length, start );
  870.             }
  871.             start = cpf( start );
  872.          }
  873.       }
  874.       if (rc != ERROR) {
  875.          if (*(start-1) != '\n')
  876.             *start++ = '\n';
  877.       }
  878.       /*
  879.        * close the file and report the final character in the buffer
  880.        */
  881.       close( fd );
  882.       *end = start;
  883.    }
  884.    return( rc );
  885. }
  886.  
  887.  
  888. /*
  889.  * Name:    get_help
  890.  * Purpose: save the screen and display key definitions
  891.  * Date:    June 5, 1991
  892.  * Notes:   This routine is dependent on the length of the strings in the
  893.  *          help screen.  To make it easy to load in a new help screen,
  894.  *          the strings are assumed to be 80 characters long followed by
  895.  *          the '\0' character.  It is assumed each that string contains
  896.  *          exactly 81 characters.
  897.  */
  898. void get_help( WINDOW *arg_filler )
  899. {
  900. register char *help;
  901. register int line;
  902.  
  903.    xygoto( -1, -1 );
  904.    save_screen( );
  905.    help = help_screen[0];
  906.    for (line=0; help != NULL; ) {
  907.       s_output( help, line, 0, g_display.help_color );
  908.       help = help_screen[++line];
  909.    }
  910.    line = getkey( );
  911.    restore_screen( );
  912. }
  913.  
  914.  
  915. /*
  916.  * Name:    save_screen
  917.  * Purpose: save the contents of the screen to the screen buffer
  918.  * Date:    June 5, 1991
  919.  */
  920. void save_screen( void )
  921. {
  922.    _fmemcpy( full_screen_buffer, g_display.display_address, 4000 );
  923. }
  924.  
  925.  
  926. /*
  927.  * Name:    restore_screen
  928.  * Purpose: restore the contents of the screen from screen buffer
  929.  * Date:    June 5, 1991
  930.  */
  931. void restore_screen( void )
  932. {
  933.    _fmemcpy( g_display.display_address, full_screen_buffer, 4000 );
  934. }
  935.  
  936.  
  937. /*
  938.  * Name:    show_credits
  939.  * Purpose: display authors
  940.  * Date:    June 5, 1991
  941.  */
  942. void show_credits( void )
  943. {
  944. register char *credit;
  945. int line;
  946.  
  947.    xygoto( -1, -1 );
  948.    credit = credit_screen[0];
  949.    for (line=0; credit != NULL; ) {
  950.       s_output( credit, line+2, 11, g_display.text_color );
  951.       credit = credit_screen[++line];
  952.    }
  953. }
  954.  
  955.  
  956. /*
  957.  * The next function was written by Tom Waters, twaters@nswc-wo.navy.mil and
  958.  * modified extensively by Frank Davis.
  959.  *
  960.  * "I use ANSI.SYS to redefine a few of my function keys and this causes a
  961.  * problem when getch() is used in a program.  For example, instead of returning
  962.  * 321 for the F7, I get the redefined string inserted in the text. So, instead
  963.  * of using getch(), I use the following function ..."
  964.  *
  965.  * Tom, thanks for the info and solution.  I'm sure your function will be
  966.  * be appreciated by everyone with ANSI key defs, Frank.
  967.  */
  968.  
  969. /*
  970.  * Name:    getkey
  971.  * Purpose: use bios to get keyboard input (getch has trouble w/ ANSI
  972.  *          redefined keys)
  973.  * Date:    July 2, 1991
  974.  * Modified:July 12, 1991, Frank Davis - accept ALT-xxx keyboard entry
  975.  *          September 10, 1991, Frank Davis - add support for Ctrl+Up, etc...
  976.  * Passed:  None
  977.  * Notes:   Uses the BIOS to read the next keyboard character.  Service
  978.  *          0 is keyboard read.  Service 0x10 is the extended keyboard read.
  979.  *          Test for a bunch of special cases.  Allow the user to enter any
  980.  *          ASCII or Extended ASCII code as a normal text characters,
  981.  *          exceptions are 10 and 26 (LF, EOF).
  982.  *
  983.  *          Control @ is defined as 0.  we need to do a special test
  984.  *           for this key, otherwise it's interpretted as an Alt key.  It's
  985.  *           the only "regular" Control key that returns 0 in AL and the scan
  986.  *           byte in AH.  The "regular" Control keys are those in the range
  987.  *           0-31 and they return the Control character in AL and the scan
  988.  *           code in AH.  All of the Alt + CHARACTER keys return 0 in AL and
  989.  *           the scan code in ah.
  990.  *
  991.  *          This function was written for US keyboards.  It may have to be
  992.  *          modified for other keyboards, eg. Belgium, Canada, Czech,
  993.  *          Slovak, Denmark, France, Germany, etc...
  994.  */
  995. int getkey( void )
  996. {
  997. unsigned key, num_lock, control, shift;
  998. register scan;
  999. register unsigned lo;
  1000.  
  1001.    /*
  1002.     *  _bios_keybrd == int 16.  It returns the scan code in ah, hi part of key,
  1003.     *  and the ascii key code in al, lo part of key.  If the character was
  1004.     *  entered via ALT-xxx, the scan code, hi part of key, is 0.
  1005.     */
  1006.    if (mode.enh_kbd) {
  1007.       key = _bios_keybrd( 0x10 );
  1008.       lo  = _bios_keybrd( 0x12 );
  1009.  
  1010.       /*
  1011.        * couple of special cases:  1) if user enters Alt-224 then the
  1012.        *   hi byte == 0 and lo byte == 0xe0.  we need to let this get
  1013.        *   thru as an Extended ASCII char.  2) although we could make the
  1014.        *   cursor pad keys do different functions than the numeric pad
  1015.        *   cursor keys, let's set the 0xe0 in the lo byte to zero and forget
  1016.        *   about support for those keys.
  1017.        */
  1018.       if ((key & 0x00ff) == 0x00e0 && (key & 0xff00) != 0)
  1019.          key = key & 0xff00;
  1020.    } else {
  1021.       key = _bios_keybrd( 0 );
  1022.       lo  = _bios_keybrd( 2 );
  1023.    }
  1024.    num_lock = lo & 0x20;
  1025.    control  = lo & 0x04;
  1026.    shift    = lo & 0x03;
  1027.    scan = (key & 0xff00) >> 8;
  1028.    lo = key & 0X00FF;
  1029.  
  1030.    if (lo == 0) {
  1031.       /*
  1032.        * special case for Control @, which is defined as 0 or NULL.
  1033.        */
  1034.       if (scan == 3)
  1035.          lo = 430;
  1036.  
  1037.       /*
  1038.        * when first byte is 0, map it above 256, so that we can
  1039.        *   let ALT-xxx keys map to their 'natural' place.  In
  1040.        *   otherwords, use 0-255 as normal text characters and
  1041.        *   those >= 256 are function keys.
  1042.        */
  1043.       else
  1044.          lo = scan | 0x100;
  1045.    }
  1046.  
  1047.    /*
  1048.     * Pressing Control+BackSpace generates the 0x7f character.  Instead of
  1049.     * 0x7f, make lo the ASCII backspace character.  If anyone wants the
  1050.     * 0x7f character, then they can enter it via ALT+xxx.
  1051.     */
  1052.    if (scan == 14 && lo == 0x7f)
  1053.       lo = 8;
  1054.  
  1055.    /*
  1056.     * At the bottom of page 195 in MASM 6.0 ref manual, "..when the keypad
  1057.     *  ENTER and / keys are read through the BIOS interrupt 16h, only E0h
  1058.     *  is seen since the interrupt only gives one-byte scan codes."
  1059.     */
  1060.    else if (scan == 0xe0) {
  1061.       if (lo == 13)
  1062.          scan = 28;
  1063.       else if (lo == 10)
  1064.          scan = 28;
  1065.    }
  1066.  
  1067.    /*
  1068.     *  let's massage all of the control key combinations.
  1069.     */
  1070.    if (lo < 32) {
  1071.  
  1072.       /*
  1073.        * My machine at home is sorta weird.  On every machine that I've
  1074.        * tested at the awffice, the ALT-xxx combination returns 0 for the
  1075.        * scan byte and xxx for the ASCII code.  My machine returns 184 (decimal)
  1076.        * as the scan code?!?!?  I added the next two lines for my machine at
  1077.        * home.  I wonder if someone forgot to zero out ah for Alt keypad entry
  1078.        * when they wrote my bios.
  1079.        */
  1080.       if (scan > 0x80)
  1081.          scan = 0;
  1082.  
  1083.       /*
  1084.        * If user enters ALT+010 make this a return.  LF is a special character
  1085.        * and needs to be handled by the editor.
  1086.        */
  1087.       if (scan == 0 && lo == 10)
  1088.          lo = 425;
  1089.  
  1090.       /*
  1091.        * Separate the ESC key from the ^[ key.  The scan code for the ESC
  1092.        * key is 1.  Map this to a different index into the key function
  1093.        * array just in case someone wants to define ESC or ^[ to different
  1094.        * functions.  BTW, ESC and ^[ return the same ASCII code, 27.
  1095.        *
  1096.        */
  1097.       else if (scan == 1) {
  1098.          if (shift)
  1099.             lo = 259;
  1100.          else if (control)
  1101.             lo = 260;
  1102.          else
  1103.             lo = 258;
  1104.       }
  1105.  
  1106.       /*
  1107.        * Scan code for Enter = 28.  Separate the various Enter keys.
  1108.        */
  1109.       else if (scan == 28) {
  1110.          if (shift)
  1111.             lo = 263;
  1112.          else if (control)
  1113.             lo = 264;
  1114.          else
  1115.             lo = 262;
  1116.       }
  1117.  
  1118.       /*
  1119.        * Scan code for Backspace = 14.  Separate the various BackSpace keys.
  1120.        */
  1121.       else if (scan == 14) {
  1122.          if (shift)
  1123.             lo = 266;
  1124.          else if (control)
  1125.             lo = 267;
  1126.          else
  1127.             lo = 265;
  1128.       }
  1129.  
  1130.       /*
  1131.        * Scan code for Tab = 15.  Separate the tab key.
  1132.        */
  1133.       else if (scan == 15) {
  1134.          lo = 268;
  1135.       }
  1136.  
  1137.       /*
  1138.        * if scan code is not 0, then a Control key was pressed.  Map
  1139.        * those keys to the WordStar commands.
  1140.        */
  1141.       else if (scan > 0)
  1142.          lo += 430;
  1143.  
  1144.       /*
  1145.        * Do not allow control z to get thru.  Code 256 is not assigned to
  1146.        * any function, see default.h for more info.
  1147.        */
  1148.       if (lo == 26)
  1149.          lo = 256;
  1150.  
  1151.    } else if (!num_lock) {
  1152.       switch (scan) {
  1153.          /*
  1154.           * scan code for grey - == 74.  if num_lock is not toggled, assign it
  1155.           * to the scroll line up function.
  1156.           */
  1157.          case 74 :
  1158.             lo = 423;
  1159.             break;
  1160.  
  1161.          /*
  1162.           * scan code for grey + == 78.  if num_lock is not toggled, assign it
  1163.           * to the scroll line down function.  if shift grey + then stationary
  1164.           * scroll down.
  1165.           */
  1166.          case 78 :
  1167.             lo = 424;
  1168.             break;
  1169.       }
  1170.    }
  1171.  
  1172.  
  1173.    /*
  1174.     * let's set up for the Shift+Alt and Control+Alt keys.
  1175.     *  With these key combinations, we can do the International keyboard
  1176.     *  stuff, see the Microsoft MS DOS 5.0 manual pages 623-637.
  1177.     */
  1178.    if (lo > 256 && (shift || control)) {
  1179.  
  1180.       /*
  1181.        * add 55 to Ctrl+Left thru Ctrl+Home when the shift key is pressed.
  1182.        *  this is not part of the International keyboard stuff, just a way
  1183.        *  to assign the horizontal scroll left and right funcs to cursor keys.
  1184.        */
  1185.       if (shift) {
  1186.          if (lo >= 371 && lo <= 374)
  1187.             lo += 55;
  1188.  
  1189.          /*
  1190.           * if shift is down, map alt 1! thru alt =+ to shift alt 1! thru alt =+
  1191.           */
  1192.          else if (lo >= 376 && lo <= 387)
  1193.             lo += 86;
  1194.  
  1195.          /*
  1196.           * internation keyboard stuff
  1197.           */
  1198.          else if (lo >= 272 && lo <= 309)
  1199.             lo += 202;
  1200.       }
  1201.    }
  1202.  
  1203.    /*
  1204.     * the line feed is a special character that must be handled
  1205.     * by the editor.
  1206.     * don't let the eof character, 26, get thru either.
  1207.     */
  1208.    if (lo == 10)
  1209.       lo = 425;
  1210.    else if (lo == 26)
  1211.       lo = 256;
  1212.    return( lo );
  1213. }
  1214.  
  1215.  
  1216. /*
  1217.  * Name:    getfunc
  1218.  * Purpose: get the function assigned to key c
  1219.  * Date:    July 11, 1991
  1220.  * Passed:  c:  key just pressed
  1221.  * Notes:   key codes less than 256 or 0x100 are not assigned a function.
  1222.  *          The codes in the range 0-255 are ASCII and extended ASCII chars.
  1223.  */
  1224. int getfunc( int c )
  1225. {
  1226. register int i = c;
  1227.  
  1228.    if (i <= 256)
  1229.       i = 0;
  1230.    else
  1231.       i = key_func[i-256];
  1232.    return( i );
  1233. }
  1234.  
  1235.  
  1236. /*
  1237.  * Name:    record_on_off
  1238.  * Purpose: save keystrokes in keystroke buffer
  1239.  * Date:    April 1, 1992
  1240.  * Notes:   -1 in .next field indicates the end of a recording
  1241.  *          -1 in .key field indicates the initial, unassigned macro key
  1242.  *          STROKE_LIMIT+1 in .next field indicates an unused space.
  1243.  */
  1244. void record_on_off( WINDOW *window )
  1245. {
  1246. int next;
  1247. int prev;
  1248. int line;
  1249. int key;
  1250. int func;
  1251. char line_buff[(MAX_COLS+2)*2]; /* buffer for char and attribute  */
  1252.  
  1253.    mode.record = !mode.record;
  1254.    if (mode.record == TRUE) {
  1255.       line = window->bottom_line;
  1256.       show_avail_strokes( );
  1257.       save_screen_line( 0, line, line_buff );
  1258.       set_prompt( "Press the key that will play back this recording : ", line );
  1259.  
  1260.       /*
  1261.        * get the candidate macro key and look up the function assigned to it.
  1262.        */
  1263.       key = getkey( );
  1264.       func = getfunc( key );
  1265.  
  1266.       /*
  1267.        * the key must be an unused, recognized function key or a function
  1268.        * key assigned to a previously defined macro.  we also need room
  1269.        * in the macro structure.
  1270.        */
  1271.       if (key <= 256 || (func != 0 && func != PlayBack)) {
  1272.          error( WARNING, line, "Cannot assign a recording to this key" );
  1273.          mode.record = FALSE;
  1274.       } else if (g_status.stroke_count == 0) {
  1275.          error( WARNING, line, "No more room in recording buffer" );
  1276.          mode.record = FALSE;
  1277.       } else {
  1278.  
  1279.          /*
  1280.           * everything is everything so far, just check for a prev macro
  1281.           */
  1282.          prev = OK;
  1283.          if (func == PlayBack) {
  1284.             set_prompt( "Overwrite recording assigned to this key? (y/n) : ",
  1285.                         line );
  1286.             if (get_yn( ) == A_NO) {
  1287.                prev = ERROR;
  1288.                mode.record = FALSE;
  1289.             }
  1290.          }
  1291.          if (prev == OK) {
  1292.             g_status.recording_key = key;
  1293.             next = macro.first_stroke[key-256];
  1294.  
  1295.             /*
  1296.              * if key has already been assigned to a macro, clear macro def.
  1297.              */
  1298.             if (next != STROKE_LIMIT+1) {
  1299.                do {
  1300.                   prev = next;
  1301.                   next = macro.strokes[next].next;
  1302.                   macro.strokes[prev].key  = MAX_KEYS+1;
  1303.                   macro.strokes[prev].next = STROKE_LIMIT+1;
  1304.                   ++g_status.stroke_count;
  1305.                } while (next != -1);
  1306.                show_avail_strokes( );
  1307.             }
  1308.  
  1309.             /*
  1310.              * find the first open space and initialize
  1311.              */
  1312.             for (next=0; macro.strokes[next].next != STROKE_LIMIT+1;)
  1313.                next++;
  1314.             macro.first_stroke[key-256] = next;
  1315.             macro.strokes[next].key  = -1;
  1316.             macro.strokes[next].next = -1;
  1317.             key_func[key-256] = PlayBack;
  1318.             s_output( "Recording", g_display.mode_line, 23,
  1319.                       g_display.mode_color | 0x80 );
  1320.          }
  1321.       }
  1322.       restore_screen_line( 0, line, line_buff );
  1323.    }
  1324.  
  1325.    /*
  1326.     * the flashing "Recording" and the stroke count write over the modes.
  1327.     *  when we get thru defining a macro, redisplay the modes.
  1328.     */
  1329.    if (mode.record == FALSE) {
  1330.       memset( line_buff, ' ', 36 );
  1331.       line_buff[36] = '\0';
  1332.       s_output( line_buff, g_display.mode_line, 23, g_display.mode_color );
  1333.       show_indent_mode( );
  1334.       show_sync_mode( );
  1335.       show_search_case( );
  1336.       show_wordwrap_mode( );
  1337.  
  1338.       /*
  1339.        * let's look at the macro.  if the first .key of the macro is
  1340.        *   still -1, which is the initial unassigned key in a macro, reset
  1341.        *   the macro so other keys may be assigned to this node.
  1342.        */
  1343.       key = g_status.recording_key;
  1344.       if (key != 0) {
  1345.          next = macro.first_stroke[key-256];
  1346.          if (macro.strokes[next].key == -1) {
  1347.             macro.strokes[next].key  = MAX_KEYS+1;
  1348.             macro.strokes[next].next = STROKE_LIMIT+1;
  1349.             macro.first_stroke[key-256] = STROKE_LIMIT+1;
  1350.             if (getfunc( key ) == PlayBack)
  1351.                key_func[key-256] = 0;
  1352.          }
  1353.       }
  1354.       g_status.recording_key = 0;
  1355.    }
  1356. }
  1357.  
  1358.  
  1359. /*
  1360.  * Name:    record_keys
  1361.  * Purpose: save keystrokes in keystroke buffer
  1362.  * Date:    April 1, 1992
  1363.  * Notes:   -1 in .next field indicates the end of a recording
  1364.  *          STROKE_LIMIT+1 in .next field indicates an unused space.
  1365.  */
  1366. void record_keys( int line )
  1367. {
  1368. register int next;
  1369. register int prev;
  1370. int key;
  1371. int func;
  1372.  
  1373.    if (mode.record == TRUE) {
  1374.       if (g_status.stroke_count == 0)
  1375.          error( WARNING, line, "No more room in recording buffer" );
  1376.       else {
  1377.          key = g_status.key_pressed;
  1378.          func = getfunc( key );
  1379.          if (func != RecordMacro && func != SaveMacro && func != LoadMacro &&
  1380.              func != ClearAllMacros) {
  1381.  
  1382.             /*
  1383.              * a -1 in the next field marks the end of the keystroke recording.
  1384.              */
  1385.             next = macro.first_stroke[g_status.recording_key - 256];
  1386.             if (macro.strokes[next].next != STROKE_LIMIT+1) {
  1387.                while (macro.strokes[next].next != -1)
  1388.                   next = macro.strokes[next].next;
  1389.             }
  1390.             prev = next;
  1391.  
  1392.             /*
  1393.              * now find an open space to record the current key.
  1394.              */
  1395.             if (macro.strokes[next].key != -1) {
  1396.                for (; next < STROKE_LIMIT &&
  1397.                             macro.strokes[next].next != STROKE_LIMIT+1;)
  1398.                   next++;
  1399.                if (next == STROKE_LIMIT) {
  1400.                   for (next=0; next < prev &&
  1401.                                macro.strokes[next].next != STROKE_LIMIT+1;)
  1402.                      next++;
  1403.                }
  1404.             }
  1405.             if (next == prev && macro.strokes[prev].key != -1)
  1406.                error( WARNING, line, "No more room for keys" );
  1407.             else {
  1408.  
  1409.                /*
  1410.                 * check for recursion.
  1411.                 * next == prev if we are recording the initial macro node.
  1412.                 */
  1413.                if (g_status.recording_key != key) {
  1414.                   macro.strokes[prev].next = next;
  1415.                   macro.strokes[next].next = -1;
  1416.                   macro.strokes[next].key  = key;
  1417.                   g_status.stroke_count--;
  1418.                   show_avail_strokes( );
  1419.                } else
  1420.                   error( WARNING, line, "Cannot record this key" );
  1421.             }
  1422.          }
  1423.       }
  1424.    }
  1425. }
  1426.  
  1427.  
  1428. /*
  1429.  * Name:    show_avail_strokes
  1430.  * Purpose: show available free key strokes in lite bar at bottom of screen
  1431.  * Date:    April 1, 1992
  1432.  */
  1433. void show_avail_strokes( void )
  1434. {
  1435. char strokes[MAX_COLS];
  1436.  
  1437.    s_output( "  Avail strokes = ", g_display.mode_line, 34, g_display.mode_color );
  1438.    itoa( g_status.stroke_count, strokes, 10 );
  1439.    s_output( "      ", g_display.mode_line, 52, g_display.mode_color );
  1440.    s_output( strokes, g_display.mode_line, 52, g_display.mode_color );
  1441. }
  1442.  
  1443.  
  1444. /*
  1445.  * Name:    save_strokes
  1446.  * Purpose: save strokes to a file
  1447.  * Date:    April 1, 1992
  1448.  */
  1449. void save_strokes( WINDOW *window )
  1450. {
  1451. FILE *fp;                       /* file to be written */
  1452. char name[MAX_COLS+2];          /* file name */
  1453. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1454. int rc;
  1455. int prompt_line;
  1456. int fattr;
  1457.  
  1458.    name[0] = '\0';
  1459.    prompt_line = window->bottom_line;
  1460.    save_screen_line( 0, prompt_line, line_buff );
  1461.    if ((rc = get_name( "Name for macro file name: ", prompt_line, name,
  1462.                  g_display.message_color )) == OK) {
  1463.  
  1464.       /*
  1465.        * make sure it is OK to overwrite any existing file
  1466.        */
  1467.       rc = get_fattr( name, &fattr );
  1468.       if (rc == OK) {
  1469.          set_prompt( "Overwrite existing file? (y/n): ", prompt_line );
  1470.          if (get_yn( ) != A_YES  ||  change_mode( name, prompt_line ) == ERROR)
  1471.             rc = ERROR;
  1472.       }
  1473.       if (rc != ERROR) {
  1474.          if ((fp = fopen( name, "wb" )) != NULL) {
  1475.             fwrite( ¯o, sizeof( MACRO ), 1, fp );
  1476.             fclose( fp );
  1477.          }
  1478.       }
  1479.    }
  1480.    restore_screen_line( 0, prompt_line, line_buff );
  1481. }
  1482.  
  1483.  
  1484. /*
  1485.  * Name:    load_strokes
  1486.  * Purpose: load strokes from a file
  1487.  * Date:    April 1, 1992
  1488.  * Notes:   show the user a file pick list.  I can never remember macro
  1489.  *          file names or the directory in which they hide.  might as well
  1490.  *          give the user a file pick list.
  1491.  */
  1492. void load_strokes( WINDOW *window )
  1493. {
  1494. FILE *fp;               /* file to be read */
  1495. char dname[MAX_COLS];   /* directory search pattern */
  1496. char stem[MAX_COLS];    /* directory stem */
  1497. int rc;
  1498.  
  1499.    dname[0] = '\0';
  1500.    if (get_name( "Search path or file name for macro file : ",
  1501.                  window->bottom_line, dname, g_display.message_color ) == OK) {
  1502.       if (validate_path( dname, stem ) == OK) {
  1503.          rc = list_and_pick( dname, stem, window );
  1504.  
  1505.          /*
  1506.           * if everything is everything, load in the file selected by user.
  1507.           */
  1508.          if (rc == OK) {
  1509.             if ((fp = fopen( dname, "rb" )) != NULL && ceh.flag != ERROR) {
  1510.                fread( ¯o, sizeof( MACRO ), 1, fp );
  1511.                fclose( fp );
  1512.             }
  1513.             if (ceh.flag == OK)
  1514.                connect_macros( );
  1515.          }
  1516.       } else
  1517.          error( WARNING, window->bottom_line, "Invalid path or file name" );
  1518.    }
  1519. }
  1520.  
  1521.  
  1522. /*
  1523.  * Name:    clear_macro
  1524.  * Purpose: reset all macro buffers, pointers, functions.
  1525.  * Date:    April 1, 1992
  1526.  * Notes:   reset the available macro stroke count.  reset all fields in
  1527.  *          macro structure.  clear any keys assigned to macros in the
  1528.  *          function assignment array.
  1529.  */
  1530. void clear_macros( WINDOW *arg_filler )
  1531. {
  1532. register int i;
  1533.  
  1534.    g_status.stroke_count = STROKE_LIMIT;
  1535.    for (i=0; i<STROKE_LIMIT; i++) {
  1536.       macro.strokes[i].next = STROKE_LIMIT+1;
  1537.       macro.strokes[i].key  = MAX_KEYS+1;
  1538.    }
  1539.    for (i=0; i<MAX_KEYS; i++) {
  1540.       macro.first_stroke[i] = STROKE_LIMIT+1;
  1541.       if (key_func[i] == PlayBack)
  1542.          key_func[i] = 0;
  1543.    }
  1544. }
  1545.  
  1546.  
  1547. /*
  1548.  * Name:    connect_macros
  1549.  * Purpose: hook up all (if any) macros to the function key definition table
  1550.  * Date:    April 1, 1992
  1551.  * Notes:   we need to connect all macro definitions to the key definition
  1552.  *          table in the startup routine or when we read in a new macro
  1553.  *          definition file.  the predefined func assignments take
  1554.  *          precedence over macro definitions.
  1555.  */
  1556. void connect_macros( void )
  1557. {
  1558. register int i;
  1559.  
  1560.    /*
  1561.     * reset the key function assignment array.  initially, no keys may be
  1562.     * assigned to a macro.
  1563.     */
  1564.    for (i=0; i<MAX_KEYS; i++)
  1565.       if (key_func[i] == PlayBack)
  1566.          key_func[i] = 0;
  1567.  
  1568.    /*
  1569.     * now, find out how many free keystrokes are in the macro structure.
  1570.     */
  1571.    g_status.stroke_count = 0;
  1572.    for (i=0; i<STROKE_LIMIT; i++)
  1573.       if (macro.strokes[i].next == STROKE_LIMIT+1)
  1574.          ++g_status.stroke_count;
  1575.  
  1576.    /*
  1577.     * go thru the first stroke list to see if any key has been assigned to
  1578.     * a macro and connect the macro def to the key.  predefined function
  1579.     * assignments take precedence over macros.
  1580.     */
  1581.    for (i=0; i<MAX_KEYS; i++) {
  1582.       if (macro.first_stroke[i] != STROKE_LIMIT+1)
  1583.          if (key_func[i] == 0)
  1584.             key_func[i] = PlayBack;
  1585.    }
  1586. }
  1587.  
  1588.  
  1589. /*
  1590.  * Name:    findfirst
  1591.  * Purpose: find the first file matching a pattern using DOS interrupt
  1592.  * Date:    January 6, 1992
  1593.  * Passed:  dta:  disk transfer address
  1594.  *          path: path to search for files
  1595.  * Notes:   return codes for findfirst:
  1596.  *             0  no error
  1597.  *             2  file is invalid or does not exist
  1598.  *             3  path is invalid or does not exist
  1599.  *            18  no matching directory entry was found
  1600.  *            -1  check the critical error flag for critical errors
  1601.  */
  1602. int  findfirst( DTA far *dta, char far *path, int f_attr )
  1603. {
  1604. void far *old_dta;
  1605. void far *new_dta;
  1606. int rc;
  1607.  
  1608.    new_dta = (void far *)dta;
  1609.    _asm {
  1610.  
  1611. ; save the old dta
  1612.         mov     ah, 0x2f                ; DOS get dta
  1613.         int     0x21                    ; DOS interrupt
  1614.         mov     WORD PTR old_dta, bx    ; save OFFSET of old DTA
  1615.         mov     ax, es
  1616.         mov     WORD PTR old_dta+2, ax  ; save SEGMENT of old DTA
  1617.  
  1618. ; set the new dta
  1619.         push    ds                      ; save ds
  1620.         mov     dx, WORD PTR new_dta    ; get OFFSET of new dta
  1621.         mov     ax, WORD PTR new_dta+2  ; get SEGMENT of new dta
  1622.         mov     ds, ax                  ; put it in ds
  1623.         mov     ah, 0x1a                ; DOS set dta
  1624.         int     0x21                    ; DOS interrupt
  1625.         pop     ds                      ; get back ds
  1626.  
  1627. ; find first matching file
  1628.         push    ds                      ; save ds
  1629.         mov     cx, WORD PTR f_attr     ; file attributes to search for
  1630.         mov     dx, WORD PTR path       ; get OFFSET of path
  1631.         mov     ax, WORD PTR path+2     ; get SEGMENT of path
  1632.         mov     ds, ax                  ; put it in ds
  1633.         mov     ah, 0x4e                ; DOS find first file
  1634.         int     0x21                    ; DOS interrupt
  1635.         pop     ds                      ; get back ds
  1636.  
  1637. ; save the return code
  1638.         jc      an_error                ; carry is set if an error occured
  1639.         xor     ax, ax                  ; zero out ax, return OK if no error
  1640. an_error:
  1641.         mov     WORD PTR rc, ax         ; save the return code
  1642.  
  1643. ; get back old dta
  1644.         push    ds                      ; save ds
  1645.         mov     dx, WORD PTR old_dta    ; get OFFSET of old dta
  1646.         mov     ax, WORD PTR old_dta+2  ; get SEGMENT of old dta
  1647.         mov     ds, ax                  ; put it in ds
  1648.         mov     ah, 0x1a                ; DOS set dta
  1649.         int     0x21                    ; DOS interrupt
  1650.         pop     ds                      ; get back ds
  1651.    }
  1652.    if (ceh.flag == ERROR)
  1653.       rc = ERROR;
  1654.    return( rc );
  1655. }
  1656.  
  1657.  
  1658. /*
  1659.  * Name:    findnext
  1660.  * Purpose: find the next file matching a pattern using DOS interrupt
  1661.  * Date:    January 6, 1992
  1662.  * Passed:  dta:  disk transfer address
  1663.  *          path: path to search for files
  1664.  * Notes:   findfirst() MUST be called before calling this function.
  1665.  *          return codes for findnext:
  1666.  *             0  no error
  1667.  *             2  path is invalid or does not exist
  1668.  *            18  no matching directory entry was found
  1669.  *            -1  check the critical error flag for critical errors
  1670.  */
  1671. int  findnext( DTA far *dta )
  1672. {
  1673. void far *old_dta;
  1674. void far *new_dta;
  1675. int rc;
  1676.  
  1677.    new_dta = (void far *)dta;
  1678.    _asm {
  1679.  
  1680. ; save the old dta
  1681.         mov     ah, 0x2f                ; DOS get dta
  1682.         int     0x21                    ; DOS interrupt
  1683.         mov     WORD PTR old_dta, bx    ; save OFFSET of old DTA
  1684.         mov     ax, es
  1685.         mov     WORD PTR old_dta+2, ax  ; save SEGMENT of old DTA
  1686.  
  1687. ; set the new dta
  1688.         push    ds                      ; save ds
  1689.         mov     dx, WORD PTR new_dta    ; get OFFSET of new dta
  1690.         mov     ax, WORD PTR new_dta+2  ; get SEGMENT of new dta
  1691.         mov     ds, ax                  ; put it in ds
  1692.         mov     ah, 0x1a                ; DOS set dta
  1693.         int     0x21                    ; DOS interrupt
  1694.         pop     ds                      ; get back ds
  1695.  
  1696. ; find next matching file
  1697.         mov     ah, 0x4f                ; DOS find first file
  1698.         int     0x21                    ; DOS interrupt
  1699.  
  1700. ; save the return code
  1701.         jc      an_error                ; carry is set if an error occured
  1702.         xor     ax, ax                  ; zero out ax, return OK if no error
  1703. an_error:
  1704.         mov     WORD PTR rc, ax         ; save the return code
  1705.  
  1706. ; get back old dta
  1707.         push    ds                      ; save ds
  1708.         mov     dx, WORD PTR old_dta    ; get OFFSET of old dta
  1709.         mov     ax, WORD PTR old_dta+2  ; get SEGMENT of old dta
  1710.         mov     ds, ax                  ; put it in ds
  1711.         mov     ah, 0x1a                ; DOS set dta
  1712.         int     0x21                    ; DOS interrupt
  1713.         pop     ds                      ; get back ds
  1714.    }
  1715.    if (ceh.flag == ERROR)
  1716.       rc = ERROR;
  1717.    return( rc );
  1718. }
  1719.