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