home *** CD-ROM | disk | FTP | other *** search
/ Der Mediaplex Sampler - Die 6 von Plex / 6_v_plex.zip / 6_v_plex / DISK6 / OS_05 / DTE.ZIP / HWSYSV.C < prev    next >
C/C++ Source or Header  |  1994-01-24  |  44KB  |  1,516 lines

  1. /*
  2.  * Written by Douglas Thomson (1989/1990)
  3.  * Modified for pure System V release 2 by Jay Maynard (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:    hwsysv.c
  13.  * Authors: Douglas Thomson and Jay Maynard
  14.  * System:  This particular version is for generic System V release 2,
  15.  *           although it should suit most versions of UNIX system V.
  16.  * Date:    28 November 1990
  17.  * Notes:   This module has been kept as small as possible, to facilitate
  18.  *           porting between different systems.
  19.  */
  20. #include <curses.h>     /* used to access terminfo database */
  21. #include <term.h>       /* used to set terminal modes */
  22. #include <fcntl.h>      /* used to set up stdin with no delay reads */
  23. #include <signal.h>     /* used to trap various signals (future?) */
  24. #include <varargs.h>    /* used to pass a variable no. of arguments */
  25. #include "common.h"     /* dte types */
  26. #include "hwdep.h"      /* prototypes for functions here */
  27. #include "utils.h"      /* prototypes for display/input etc */
  28. #include "version.h"    /* current version number */
  29.  
  30. /*
  31.  * A bug in some versions of elm causes editing sessions not to be killed
  32.  *  when elm is killed (for example when a modem user unplugs the 'phone
  33.  *  while editing mail!).
  34.  * The editor process is inherited by the init process, so all I do here
  35.  *  is have the editor commit suicide if it detects that its parent has
  36.  *  died.
  37.  */
  38. #define ELM_BUG
  39.  
  40. /*
  41.  * prototypes for all functions in this file
  42.  */
  43. void error ARGS((int kind, ...));
  44. static void myputchar ARGS((char c));
  45. static void hw_attr ARGS((char attr));
  46. static void att_check ARGS((void));
  47. void att_stuff ARGS((void));
  48. void hw_xygoto ARGS((void));
  49. int hw_clreol ARGS((void));
  50. int hw_linedel ARGS((int line));
  51. int hw_lineins ARGS((int line));
  52. int hw_backspace ARGS((void));
  53. int hw_c_insert ARGS((void));
  54. int hw_c_delete ARGS((void));
  55. void hw_c_output ARGS((int c));
  56. void hw_terminate ARGS((void));
  57. static void process_input ARGS((void));
  58. void hw_initialize ARGS((void));
  59. int hw_c_avail ARGS((void));
  60. int hw_c_input ARGS((void));
  61. void main ARGS((int argc, char *argv[]));
  62. void hw_move ARGS((text_ptr dest, text_ptr source, long number));
  63. int hw_rename ARGS((char *old, char *new));
  64. int hw_scroll_up ARGS((int top, int bottom));
  65. int hw_scroll_down ARGS((int top, int bottom));
  66. int min ARGS((int a, int b));
  67. int hw_fattrib ARGS((char *name));
  68. int hw_set_fattrib ARGS((char *name, int attrib));
  69. int hw_unlink ARGS((char *name));
  70. int hw_printable ARGS((int c));
  71. int hw_load ARGS((char *name, text_ptr start, text_ptr limit, text_ptr *end));
  72. static int write_file ARGS((char *name, char *mode, text_ptr start,
  73.         text_ptr end));
  74. int hw_save ARGS((char *name, text_ptr start, text_ptr end));
  75. int hw_append ARGS((char *name, text_ptr start, text_ptr end));
  76. int hw_print ARGS((text_ptr start, text_ptr end));
  77. void hw_copy_path ARGS((char *old, char *name, char *new));
  78.  
  79. #define REVERSE 1       /* reverse video (or standout) attribute */
  80. #define HIGH 2          /* high intensity (or underline) attribute */
  81. #define NORMAL 3        /* normal video attribute */
  82.  
  83. #define UNKNOWN 7       /* flag not yet set to TRUE or FALSE */
  84.  
  85. char *malloc(/*!void*/);     /* memory allocator - this keeps lint happy */
  86.  
  87. /*
  88.  * the following variable determines the size of the memory buffer used. It
  89.  *  is set to something reasonable in main.
  90.  */
  91. static int g_space = 0;
  92.  
  93. /*
  94.  * Name:    error
  95.  * Purpose: To report an error, and usually make the user type <ESC> before
  96.  *           continuing.
  97.  * Date:    October 10, 1989
  98.  * Passed:  kind:   an indication of how serious the error was:
  99.  *                      TEMP:    merely a message, do not wait for <ESC>
  100.  *                      DIAG:    merely a message, but make sure user sees it
  101.  *                      WARNING: error, but editor can continue after <ESC>
  102.  *                      FATAL:   abort the editor!
  103.  *          format: printf format string for any arguments that follow
  104.  *          ...:    arguments to be printed
  105.  * Notes:   This function should be system independent; that is the whole
  106.  *           point of the "varargs" philosophy. However, two of the systems
  107.  *           I have used implemented "varargs" incompatibly, and some older
  108.  *           systems may not support the "varargs" macros at all...
  109.  */
  110. void error(kind, va_alist)
  111. int kind;
  112. va_dcl
  113. {
  114.     char *format;           /* printf format string for error message */
  115.     va_list argptr;         /* used to access various arguments */
  116.     char buff[MAX_COLS];    /* somewhere to store error before printing */
  117.     int c;                  /* character entered by user to continue */
  118.  
  119.     /*
  120.      * obtain the first two arguments
  121.      */
  122.     va_start(argptr);
  123.     format = va_arg(argptr, char *);
  124.  
  125.     /*
  126.      * tell the user what kind of an error it is
  127.      */
  128.     switch (kind) {
  129.     case FATAL:
  130.         strcpy(buff, "Fatal error: ");
  131.         break;
  132.     case WARNING:
  133.         strcpy(buff, "Warning: ");
  134.         break;
  135.     case DIAG:
  136.     case TEMP:
  137.         strcpy(buff, "");
  138.         break;
  139.     }
  140.  
  141.     /*
  142.      * prepare the error message itself
  143.      */
  144.     vsprintf(buff + strlen(buff), format, argptr);
  145.     va_end(argptr);
  146.  
  147.     /*
  148.      * tell the user how to continue editing if necessary
  149.      */
  150.     if (kind == WARNING || kind == DIAG) {
  151.         strcat(buff, ": type <ESC>");
  152.     }
  153.  
  154.     /*
  155.      * output the error message
  156.      */
  157.     set_prompt(buff, 1);
  158.  
  159.     if (kind == FATAL) {
  160.         /*
  161.          * no point in making the user type <ESC>, since the program is
  162.          *  about to abort anyway...
  163.          */
  164.         terminate();
  165.         exit(1);
  166.     }
  167.     else if (kind != TEMP) {
  168.         /*
  169.          * If necessary, force the user to acknowledge the error by
  170.          *  typing <ESC> (or ^U).
  171.          * This prevents any extra commands the user has entered from
  172.          *  causing problems after an error may have made them inappropriate.
  173.          */
  174.         while ((c=c_input()) != 27 && c != CONTROL('U')) {
  175.             set_prompt(buff, 1);
  176.         }
  177.     }
  178. }
  179.  
  180. /*
  181.  * Name:    myputchar
  182.  * Purpose: To output one character to the display terminal.
  183.  * Date:    October 10, 1989
  184.  * Passed:  c: character to be displayed
  185.  * Notes:   This function makes the write system call directly, to try to
  186.  *           minimize the amount of buffering that takes place. (Since this
  187.  *           editor tries to respond quickly to new commands, we do not
  188.  *           want a whole screenful of output stored in a buffer waiting
  189.  *           to be sent to the terminal!)
  190.  *          For very high speed terminals, it may be more appropriate to
  191.  *           encourage buffering... writing just one character has the
  192.  *           disadvantage that under heavy load screen update sometimes
  193.  *           "freezes" temporarily while other processes run!
  194.  */
  195. static void myputchar(c)
  196. char c;
  197. {
  198.     putc(c, stdout);
  199. /*    write(1, &c, 1); */
  200. }
  201.  
  202. /*
  203.  * Name:    hw_attr
  204.  * Purpose: To select a new attribute on the terminal.
  205.  * Date:    October 10, 1989
  206.  * Passed:  attr: the desired attribute
  207.  */
  208. static void hw_attr(attr)
  209. char attr;
  210. {
  211.     static int old_att = -1;         /* existing attribute */
  212.  
  213.     /*
  214.      * If there has been no change, then ignore the call (actually this
  215.      *  should never happen, since hw_attr is only called when the
  216.      *  attribute HAS changed...
  217.      */
  218.     if (old_att == attr) {
  219.         return;
  220.     }
  221.  
  222.     /*
  223.      * If we want the normal attribute, then there may be an easy way of
  224.      *  getting it without undoing other attributes.
  225.      */
  226.     if (attr == g_display.normal) {
  227.         if (exit_attribute_mode) {
  228.             tputs(exit_attribute_mode, 1, myputchar);
  229.             old_att = attr;
  230.             return;
  231.         }
  232.     }
  233.  
  234.     /*
  235.      * end the current attribute
  236.      */
  237.     if (old_att != g_display.normal) {
  238.         if (old_att == g_display.flash) {
  239.             tputs(exit_underline_mode, 1, myputchar);
  240.         }
  241.         else if (old_att == g_display.block) {
  242.             tputs(exit_standout_mode, 1, myputchar);
  243.         }
  244.     }
  245.  
  246.     /*
  247.      * set the new attribute
  248.      */
  249.     if (attr == REVERSE) {
  250.         tputs(enter_standout_mode, 1, myputchar);
  251.     }
  252.     else if (attr == HIGH) {
  253.         tputs(enter_underline_mode, 1, myputchar);
  254.     }
  255.  
  256.     /*
  257.      * record new attribute for next time
  258.      */
  259.     old_att = attr;
  260. }
  261.  
  262. /*
  263.  * Name:    att_check
  264.  * Purpose: To check that the attribute required for the next character is
  265.  *           the one currently in effect, and set it if different.
  266.  * Date:    October 10, 1989
  267.  * Passed:  [g_display.attr]:  the current attribute
  268.  *          [g_status.wanted]: the required attribute
  269.  * Returns: [g_display.attr]:  the newly set attribute
  270.  */
  271. static void att_check()
  272. {
  273.     if (g_display.attr != g_status.wanted) {
  274.         hw_attr(g_status.wanted);
  275.         g_display.attr = g_status.wanted;
  276.     }
  277. }
  278.  
  279. /*
  280.  * Name:    att_stuff
  281.  * Purpose: To make sure that the attribute is set to normal before commands
  282.  *           such as clear to end of line are executed.
  283.  * Date:    October 10, 1989
  284.  * Passed:  [g_display.attr]:   the current attribute
  285.  *          [g_display.normal]: the normal attribute
  286.  * Returns: [g_display.attr]:   set to normal
  287.  * Notes:   This function is necessary because some terminals clear to
  288.  *           spaces using the current attribute, while others clear to
  289.  *           normal spaces. Unfortunately terminfo does not seem to record
  290.  *           this distinction.
  291.  */
  292. void att_stuff()
  293. {
  294.     if (g_display.attr != g_display.normal) {
  295.         hw_attr(g_display.normal);
  296.         g_display.attr = g_display.normal;
  297.     }
  298. }
  299.  
  300. /*
  301.  * Name:    hw_xygoto
  302.  * Purpose: To move the cursor to a new position on the screen.
  303.  * Date:    October 10, 1989
  304.  * Passed:  [g_display.line]: the required line
  305.  *          [g_display.col]:  the required column
  306.  */
  307. void hw_xygoto()
  308. {
  309.     if (!move_standout_mode) {
  310.         /*
  311.          * some terminals can only move the cursor when in normal video
  312.          *  mode
  313.          */
  314.         att_stuff();
  315.     }
  316.     tputs(tparm(cursor_address, g_display.line, g_display.col), 1, myputchar);
  317. }
  318.  
  319. /*
  320.  * The following locally global variables are used to keep track of the
  321.  *  character in the bottom right corner of the screen.
  322.  * It is not safe to write this character, since most terminals will
  323.  *  scroll the whole screen up a line after writing it.
  324.  * However, if the screen is subsequently scrolled up for any reason, then
  325.  *  this character must appear on the second bottom line!
  326.  * This causes numerous complications in the code which follows...
  327.  */
  328. static char g_mem_c = 0;   /* character supposed to be at bottom right */
  329. static char g_mem_attr;    /* attribute for g_mem_c */
  330.  
  331. /*
  332.  * Name:    hw_clreol
  333.  * Purpose: To clear from the cursor to the end of the cursor line.
  334.  * Date:    October 10, 1989
  335.  * Returns: TRUE if the hardware could clear to end of line, FALSE otherwise
  336.  */
  337. int hw_clreol()
  338. {
  339.     static int avail = UNKNOWN;  /* can terminal do it? */
  340.  
  341.     /*
  342.      * find out if function is available, and give up if not
  343.      */
  344.     if (avail == UNKNOWN) {
  345.         avail = strlen(clr_eol) > 0;
  346.     }
  347.     if (!avail) {
  348.         return FALSE;
  349.     }
  350.  
  351.     /*
  352.      * clear to end of line, using normal attribute
  353.      */
  354.     att_stuff();
  355.     tputs(clr_eol, 1, myputchar);
  356.  
  357.     /*
  358.      * If we just cleared the bottom line, then the bottom right character
  359.      *  was cleared too.
  360.      */
  361.     if (g_display.line == g_display.nlines-1) {
  362.         g_mem_c = 0;
  363.     }
  364.  
  365.     return TRUE;
  366. }
  367.  
  368. /*
  369.  * Name:    hw_linedel
  370.  * Purpose: To delete the cursor line, scrolling lines below up.
  371.  * Date:    October 10, 1989
  372.  * Passed:  line:  line on screen to be deleted
  373.  * Returns: TRUE if the hardware could delete the line, FALSE otherwise
  374.  */
  375. int hw_linedel(line)
  376. int line;
  377. {
  378.     static int avail = UNKNOWN; /* can terminal do it? */
  379.  
  380.     /*
  381.      * check availability of function
  382.      */
  383.     if (avail == UNKNOWN) {
  384.         avail = strlen(delete_line) > 0;
  385.     }
  386.     if (!avail) {
  387.         return FALSE;
  388.     }
  389.  
  390.     /*
  391.      * delete the line
  392.      */
  393.     att_stuff();
  394.     xygoto(0, line);
  395.     tputs(delete_line, 1, myputchar);
  396.  
  397.     /*
  398.      * If this caused the bottom line to move up (which will usually be
  399.      *  the case), then add the bottom right character (if any) onto the
  400.      *  second bottom line.
  401.      */
  402.     if (g_mem_c) {
  403.         if (line < g_display.nlines-1) {
  404.             xygoto(g_display.ncols-1, g_display.nlines-2);
  405.             if (g_display.attr != g_mem_attr) {
  406.                 hw_attr(g_mem_attr);
  407.                 g_display.attr = g_mem_attr;
  408.             }
  409.             myputchar(g_mem_c);
  410.  
  411.             /*
  412.              * some terminals wrap, some don't. Terminfo contains this
  413.              *  distinction, but it makes almost no difference...
  414.              */
  415.             g_display.col = g_display.line = -1;
  416.         }
  417.         g_mem_c = 0;
  418.     }
  419.     return TRUE;
  420. }
  421.  
  422. /*
  423.  * Name:    hw_lineins
  424.  * Purpose: To insert a blank line above the cursor line, scrolling the
  425.  *           cursor line and lines below down.
  426.  * Date:    October 10, 1989
  427.  * Passed:  line:  line on screen to be inserted
  428.  * Returns: TRUE if the hardware could insert the line, FALSE otherwise
  429.  */
  430. int hw_lineins(line)
  431. int line;
  432. {
  433.     static int avail = UNKNOWN;  /* can hardware do it? */
  434.  
  435.     /*
  436.      * check that function is available
  437.      */
  438.     if (avail == UNKNOWN) {
  439.         avail = strlen(insert_line) > 0;
  440.     }
  441.     if (!avail) {
  442.         return FALSE;
  443.     }
  444.  
  445.     /*
  446.      * insert the line
  447.      */
  448.     att_stuff();
  449.     xygoto(0, line);
  450.     tputs(insert_line, 1, myputchar);
  451.  
  452.     /*
  453.      * regardless of where the line was inserted, the bottom line
  454.      *  (including the bottom right character) scrolled off the screen
  455.      */
  456.     g_mem_c = 0;
  457.  
  458.     return TRUE;
  459. }
  460.  
  461. /*
  462.  * Name:    hw_backspace
  463.  * Purpose: To move the cursor left one position.
  464.  * Date:    October 10, 1989
  465.  * Returns: TRUE if the hardware could backspace, FALSE otherwise
  466.  * Notes:   This function is used where deletion requires a backspace,
  467.  *           space, backspace. If the terminal can backspace, this may
  468.  *           be much faster than using cursor addressing.
  469.  */
  470. int hw_backspace()
  471. {
  472.     static int avail = UNKNOWN;
  473.  
  474.     if (avail == UNKNOWN) {
  475.         avail = strlen(cursor_left) > 0;
  476.     }
  477.     if (avail) {
  478.         tputs(cursor_left, 1, myputchar);
  479.     }
  480.     return avail;
  481. }
  482.  
  483. /*
  484.  * Name:    hw_c_insert
  485.  * Purpose: To insert a blank character under the cursor.
  486.  * Date:    October 10, 1989
  487.  * Returns: TRUE if the hardware could insert the space, FALSE otherwise
  488.  * Notes:   This function is used where the user has just typed a character
  489.  *           in the middle of a line in insert mode. If it is available, it
  490.  *           saves having to redraw the entire remainder of the line.
  491.  *          No assumptions are made about the contents or attribute of the
  492.  *           inserted character.
  493.  */
  494. int hw_c_insert()
  495. {
  496.     static int avail = UNKNOWN;
  497.  
  498.     if (avail == UNKNOWN) {
  499.         avail = strlen(insert_character) > 0;
  500.     }
  501.     if (avail) {
  502.         tputs(insert_character, 1, myputchar);
  503.     }
  504.     return avail;
  505. }
  506.  
  507. /*
  508.  * Name:    hw_c_delete
  509.  * Purpose: To delete the character under the cursor.
  510.  * Date:    October 10, 1989
  511.  * Returns: TRUE if the hardware could delete the character, FALSE otherwise
  512.  * Notes:   This function is used where the user has deleted a character
  513.  *           in the middle of a line. If it is available, it saves having to
  514.  *           redraw the entire remainder of the line.
  515.  *          The rightmost character on the line after the delete is assumed
  516.  *           to be a space character with normal attribute.
  517.  */
  518. int hw_c_delete()
  519. {
  520.     static int avail = UNKNOWN;
  521.  
  522.     if (avail == UNKNOWN) {
  523.         avail = strlen(delete_character) > 0;
  524.     }
  525.     if (avail) {
  526.         att_stuff();
  527.         tputs(delete_character, 1, myputchar);
  528.         /*
  529.          * bottom right corner character could need to be put on the bottom
  530.          *  line, one character in from the right
  531.          */
  532.         if (g_mem_c && g_display.line == g_display.nlines-1) {
  533.             if (g_display.col < g_display.ncols-2) {
  534.                 xygoto(g_display.ncols-2, g_display.nlines-1);
  535.                 if (g_display.attr != g_mem_attr) {
  536.                     hw_attr(g_mem_attr);
  537.                     g_display.attr = g_mem_attr;
  538.                 }
  539.                 myputchar(g_mem_c);
  540.  
  541.                 /*
  542.                  * some terminals wrap, some don't. Terminfo contains this
  543.                  *  distinction, but it makes almost no difference...
  544.                  */
  545.                 g_display.col = g_display.line = -1;
  546.             }
  547.             g_mem_c = 0;
  548.         }
  549.     }
  550.     return avail;
  551. }
  552.  
  553. /*
  554.  * Name:    hw_c_output
  555.  * Purpose: To output a character, using the current attribute, at the
  556.  *           current screen position.
  557.  * Date:    October 10, 1989
  558.  * Notes:   If the current screen position is the bottom right corner, then
  559.  *           we do not write the character, but merely store it away for
  560.  *           later. (See explanation above.)
  561.  */
  562. void hw_c_output(c)
  563. int c;
  564. {
  565.     if (g_display.line == g_display.nlines-1 && g_display.col ==
  566.             g_display.ncols-1) {
  567.         g_mem_c = c;
  568.         g_mem_attr = g_status.wanted;
  569.         return;
  570.     }
  571.     att_check();
  572.     myputchar(c);
  573. }
  574.  
  575. /*
  576.  * This struct and flag variable save the original terminal modes
  577.  * for restoration on exit.
  578.  */
  579. static struct termio term_orig;
  580. static int kbdflgs;
  581. /*
  582.  * Name:    hw_terminate
  583.  * Purpose: To restore the terminal to a safe state prior to leaving the
  584.  *           editor.
  585.  * Date:    October 10, 1989
  586.  */
  587. void hw_terminate()
  588. {
  589.     /*
  590.      * ensure no windows are left (it is annoying to exit the editor and
  591.      *  then find that only the bottom 4 lines of the screen can be used
  592.      *  for other purposes!
  593.      */
  594.     window_scroll_up(0, g_display.nlines-1);
  595.  
  596.     /*
  597.      * leave the editor text on the screen, but move the cursor to the
  598.      *  bottom line.
  599.      */
  600.     xygoto(0, g_display.nlines-2);
  601.     att_stuff();
  602.     resetterm();
  603.  
  604.     /*
  605.      * Reset the terminal mode to what it was on entry
  606.      */
  607.     if (ioctl(0, TCSETA, &term_orig) == -1) {
  608.         error(FATAL, "could not reset termio");
  609.     }
  610.     fcntl(0, F_SETFL, kbdflgs);
  611.     /*
  612.      * leave the editor version number showing, to make it easier to
  613.      *  keep track of which version is being used where.
  614.      * Once "dte" becomes more stable, this may not be useful.
  615.      */
  616.     printf("\ndte version %s for UNIX (System V)\n", VERSION);
  617. }
  618.  
  619. #ifndef HPUX
  620. /*
  621.  * Buffer declaration for work area for copy function. System V doesn't
  622.  * have memmove, so we'll have to fake it.
  623.  */
  624. static char *g_buffer;
  625. #endif
  626. /*
  627.  * Name:    hw_initialize
  628.  * Purpose: To initialize the display ready for editor use.
  629.  * Date:    October 10, 1989
  630.  * Notes:   Typed characters (including ^S and ^Q and ^\) must all be
  631.  *           returned without echoing!
  632.  */
  633. void hw_initialize()
  634. {
  635.     struct termio term;    /* to avoid ^S ^Q processing */
  636.     int result;            /* result of setting up terminal commands */
  637.     int flags;             /* for setting no delay on read */
  638.  
  639.     /*
  640.      * allocate space for the screen image
  641.      */
  642.     if ((g_screen = (screen_lines *)malloc(MAX_LINES * sizeof(screen_lines)))
  643.             == NULL) {
  644.         printf("no memory for screen image\n");
  645.         exit(1);
  646.     }
  647.  
  648.     /*
  649.      * set path for the help file
  650.      */
  651.     strcpy(g_status.help_file, HELPFILE);
  652.  
  653.     /*
  654.      * locate all the terminfo strings
  655.      */
  656.     setupterm(NULL, 1, &result);
  657.     if (result != 1) {
  658.         error(FATAL, "no TERM set");
  659.     }
  660.  
  661.     /*
  662.      * check that at least cursor addressing is available
  663.      */
  664.     if (strlen(cursor_address) == 0) {
  665.         error(FATAL, "terminal MUST have cursor addressing");
  666.     }
  667.  
  668.     /*
  669.      * do not attempt to use attributes on idiotic terminals!!
  670.      */
  671.     if (magic_cookie_glitch > 0 || ceol_standout_glitch) {
  672.         g_display.block = NORMAL;
  673.         g_display.flash = NORMAL;
  674.         g_display.normal = NORMAL;
  675.         g_display.attr = NORMAL;
  676.     }
  677.     else {
  678.         g_display.block = REVERSE;
  679.         g_display.flash = HIGH;
  680.         g_display.normal = NORMAL;
  681.         g_display.attr = NORMAL;
  682.         hw_attr(NORMAL);
  683.     }
  684.  
  685.     /*
  686.      * work out the actual size of the screen
  687.      */
  688.     if (columns < MAX_COLS) {
  689.         g_display.ncols = columns;
  690.     }
  691.     else {
  692.         g_display.ncols = MAX_COLS;
  693.     }
  694.     if (lines < MAX_LINES) {
  695.         g_display.nlines = lines;
  696.     }
  697.     else {
  698.         g_display.nlines = MAX_LINES;
  699.     }
  700.  
  701.     /*
  702.      * work out the length of the cursor addressing command, so we can
  703.      *  choose the quickest way of getting anywhere
  704.      */
  705.     g_display.ca_len = strlen(tparm(cursor_address, g_display.nlines,
  706.             g_display.ncols));
  707.  
  708.     /*
  709.      * get rid of XON/XOFF handling, echo, and other input processing
  710.      */
  711.     if (ioctl(0, TCGETA, &term) == -1) {
  712.         error(FATAL, "could not get termio");
  713.     }
  714.     (void)ioctl(0, TCGETA, &term_orig);
  715.     term.c_iflag = 0;
  716.     term.c_oflag = 0;
  717.     term.c_lflag = 0;
  718.     term.c_cc[VMIN] = 1;
  719.     term.c_cc[VTIME] = 0;
  720.     if (ioctl(0, TCSETA, &term) == -1) {
  721.         error(FATAL, "could not set termio");
  722.     }
  723.     kbdflgs = fcntl( 0, F_GETFL, 0 );
  724.  
  725.     /*
  726.      * set up no delay when checking if anything is in the buffer
  727.      */
  728.     flags = fcntl(0, F_GETFL);
  729.     flags |= O_NDELAY;
  730.     fcntl(0, F_SETFL, flags);
  731.  
  732.     /*
  733.      * allocate space for the main text buffer and the copying buffer
  734.      */
  735.     if ((g_status.start_mem = (char *)malloc(g_space)) == NULL) {
  736.         error(FATAL, "out of memory for text");
  737.     }
  738.     g_status.max_mem = g_status.start_mem + g_space;
  739. #ifndef HPUX
  740.     if ((g_buffer = (char *)malloc(g_space)) == NULL) {
  741.         error(FATAL, "out of memory for buffer");
  742.     }
  743. #endif
  744. }
  745.  
  746. /*
  747.  * if we succeeded in reading a character from the buffer, then we need
  748.  *  to store it somewhere until it is needed.
  749.  */
  750. static int avail_ch = 0;
  751.  
  752. /*
  753.  * Name:    hw_c_avail
  754.  * Purpose: To test whether or not a character has been typed by the user.
  755.  * Date:    October 10, 1989
  756.  * Returns: TRUE if user typed something, FALSE otherwise
  757.  */
  758. int hw_c_avail()
  759. {
  760.     char c;     /* character read from buffer */
  761.     int result; /* was there a character in the buffer */
  762.  
  763.     /*
  764.      * something already there, so no need to check buffer
  765.      */
  766.     if (avail_ch) {
  767.         return TRUE;
  768.     }
  769.  
  770.     fflush(stdout);
  771.  
  772.     /*
  773.      * try reading from the buffer
  774.      */
  775.     result = read(0, &c, 1);
  776.  
  777.     if (result == 1) {
  778.         /*
  779.          * got something!
  780.          */
  781.         avail_ch = c;
  782.         return TRUE;
  783.     }
  784.     return FALSE;
  785. }
  786.  
  787. /*
  788.  * Name:    hw_c_input
  789.  * Purpose: To input a character from the user (indirectly).
  790.  * Date:    October 10, 1989
  791.  * Returns: the character the user typed
  792.  */
  793. int hw_c_input()
  794. {
  795.     char c;     /* character typed */
  796.     int flags;  /* for setting delay mode */
  797.  
  798. #ifdef ELM_BUG
  799.     if (getppid() == 1) {
  800.         terminate();
  801.         exit(1);
  802.     }
  803. #endif
  804.     if (hw_c_avail()) {
  805.         /*
  806.          * There was a character already there, so return it and record
  807.          *  that it has been used.
  808.          */
  809.         c = avail_ch;
  810.         avail_ch = 0;
  811.         return c;
  812.     }
  813.  
  814.     /*
  815.      * this time, we want to wait until the user types something, not
  816.      *  keep going immediately
  817.      */
  818.     flags = fcntl(0, F_GETFL);
  819.     flags &= ~O_NDELAY;
  820.     fcntl(0, F_SETFL, flags);
  821.  
  822.     /*
  823.      * wait for something to be there
  824.      */
  825.     read(0, &c, 1);
  826.  
  827.     /*
  828.      * set stdin back to no delay, ready for hw_c_avail.
  829.      */
  830.     flags = fcntl(0, F_GETFL);
  831.     flags |= O_NDELAY;
  832.     fcntl(0, F_SETFL, flags);
  833.  
  834.     return c;
  835. }
  836.  
  837. /*
  838.  * Name:    main
  839.  * Purpose: To do any system dependent command line argument processing,
  840.  *           and then call the main editor function.
  841.  * Date:    October 10, 1989
  842.  * Passed:  argc:   number of command line arguments
  843.  *          argv:   text of command line arguments
  844.  */
  845. void main(argc, argv)
  846. int argc;
  847. char *argv[];
  848. {
  849.     /*
  850.      * see if user specified buffer size
  851.      */
  852.     if (argc > 1 && mystrcmp("-s", argv[1]) == 0) {
  853.         g_space = atoi(argv[1]+2);
  854.         ++argv;
  855.         --argc;
  856.     }
  857.  
  858.     /*
  859.      * ensure space is reasonable
  860.      */
  861.     if (g_space < 1000) {
  862.         g_space = 100000;   /* enough for program source code files */
  863.     }
  864.  
  865.     editor(argc, argv);
  866. }
  867.  
  868. /*
  869.  * Name:    hw_move
  870.  * Purpose: To move data from one place to another as efficiently as
  871.  *           possible.
  872.  * Date:    October 10, 1989
  873.  * Passed:  dest:   where to copy to
  874.  *          source: where to copy from
  875.  *          number: number of bytes to copy
  876.  * Notes:   moves may be (usually will be) overlapped
  877.  */
  878. void hw_move(dest, source, number)
  879. text_ptr dest;
  880. text_ptr source;
  881. long number;
  882. {
  883.     if (number < 0) {
  884.         /*
  885.          * this should never happen...
  886.          */
  887.         error(WARNING, "negative move - contact Douglas Thomson!");
  888.     }
  889.     else if (source == dest) {
  890.         /*
  891.          * nothing to be done
  892.          */
  893.         ;
  894.     }
  895.     else {
  896. #ifdef HPUX
  897.         /*
  898.          * overlapping move available, so take advantage of it
  899.          */
  900.         memmove(dest, source, number);
  901. #else
  902.         /*
  903.          * no overlapping move available, so copy to buffer and back
  904.          */
  905.         memcpy(g_buffer, source, number);
  906.         memcpy(dest, g_buffer, number);
  907. #endif
  908.     }
  909. }
  910.  
  911. /*
  912.  * Name:    hw_rename
  913.  * Purpose: To rename a disk file to a new name.
  914.  * Date:    October 10, 1989
  915.  * Passed:  old: current file name
  916.  *          new: new desired file name
  917.  * Returns: OK if rename succeeded, ERROR if any problem
  918.  */
  919. int hw_rename(old, new)
  920. char *old;
  921. char *new;
  922. {
  923.     if (link(old, new) == 0) {
  924.         if (unlink(old) == 0) {
  925.             return OK;
  926.         }
  927.     }
  928.     return ERROR;
  929. }
  930.  
  931. /*
  932.  * Name:    hw_scroll_up
  933.  * Purpose: To scroll the lines in a given region up one line.
  934.  * Date:    October 10, 1989
  935.  * Passed:  top:    the top line in the window
  936.  *          bottom: the bottom line in the window
  937.  * Returns: TRUE if terminal could scroll, FALSE otherwise
  938.  * Notes:   If this function does not exist, then insert and delete line
  939.  *           can achieve the same effect. However, insert and delete line
  940.  *           make lower windows jump, so using terminal scrolling is
  941.  *           preferable.
  942.  */
  943. int hw_scroll_up(top, bottom)
  944. int top;
  945. int bottom;
  946. {
  947.     static int avail = UNKNOWN;  /* can terminal do it? */
  948.  
  949.     /*
  950.      * check if function is available
  951.      */
  952.     if (avail == UNKNOWN) {
  953.         avail = strlen(change_scroll_region) > 0 &&
  954.                       strlen(scroll_forward) > 0;
  955.     }
  956.     if (!avail) {
  957.         return FALSE;
  958.     }
  959.  
  960.     /*
  961.      * select window to be affected
  962.      */
  963.     att_stuff();
  964.     tputs(tparm(change_scroll_region, top, bottom), 1, myputchar);
  965.     g_display.col = -1;
  966.     g_display.line = -1;
  967.  
  968.     /*
  969.      * scroll the window up
  970.      */
  971.     xygoto(0, bottom);
  972.     tputs(scroll_forward, 1, myputchar);
  973.  
  974.     /*
  975.      * don't leave a peculiar region scrolling - it confuses all sorts of
  976.      *  things!
  977.      */
  978.     tputs(tparm(change_scroll_region, 0, g_display.nlines-1), 1, myputchar);
  979.     g_display.col = -1;
  980.     g_display.line = -1;
  981.  
  982.     /*
  983.      * if the bottom line was scrolled up, then restore the old bottom
  984.      *  right character to the second bottom line
  985.      */
  986.     if (g_mem_c) {
  987.         if (bottom == g_display.nlines-1) {
  988.             xygoto(g_display.ncols-1, g_display.nlines-2);
  989.             hw_attr(g_mem_attr);
  990.             g_display.attr = g_mem_attr;
  991.             myputchar(g_mem_c);
  992.             g_display.col = -1;
  993.             g_display.line = -1;
  994.         }
  995.         g_mem_c = 0;
  996.     }
  997.  
  998.     return TRUE;
  999. }
  1000.  
  1001. /*
  1002.  * Name:    hw_scroll_down
  1003.  * Purpose: To scroll the lines in a given region down one line.
  1004.  * Date:    October 10, 1989
  1005.  * Passed:  top:    the top line in the window
  1006.  *          bottom: the bottom line in the window
  1007.  * Returns: TRUE if terminal could scroll, FALSE otherwise
  1008.  * Notes:   If this function does not exist, then insert and delete line
  1009.  *           can achieve the same effect. However, insert and delete line
  1010.  *           make lower windows jump, so using terminal scrolling is
  1011.  *           preferable.
  1012.  */
  1013. int hw_scroll_down(top, bottom)
  1014. int top;
  1015. int bottom;
  1016. {
  1017.     static int avail = UNKNOWN;  /* can terminal do it? */
  1018.  
  1019.     /*
  1020.      * check if function is available
  1021.      */
  1022.     if (avail == UNKNOWN) {
  1023.         avail = strlen(change_scroll_region) > 0 &&
  1024.                       strlen(scroll_reverse) > 0;
  1025.     }
  1026.     if (!avail) {
  1027.         return FALSE;
  1028.     }
  1029.  
  1030.     /*
  1031.      * select region to be affected
  1032.      */
  1033.     att_stuff();
  1034.     tputs(tparm(change_scroll_region, top, bottom), 1, myputchar);
  1035.     g_display.col = -1;
  1036.     g_display.line = -1;
  1037.  
  1038.     /*
  1039.      * scroll down
  1040.      */
  1041.     xygoto(0, top);
  1042.     tputs(scroll_reverse, 1, myputchar);
  1043.  
  1044.     /*
  1045.      * don't leave a peculiar region scrolling - it confuses all sorts of
  1046.      *  things!
  1047.      */
  1048.     tputs(tparm(change_scroll_region, 0, g_display.nlines-1), 1, myputchar);
  1049.     g_display.col = -1;
  1050.     g_display.line = -1;
  1051.  
  1052.     /*
  1053.      * if the region included the bottom line, then the bottom right
  1054.      *  character moved off the screen altogether
  1055.      */
  1056.     if (bottom == g_display.nlines-1) {
  1057.         g_mem_c = 0;
  1058.     }
  1059.  
  1060.     return TRUE;
  1061. }
  1062.  
  1063. /*
  1064.  * Name:    min
  1065.  * Purpose: To return the smaller of two integers.
  1066.  * Date:    October 10, 1989
  1067.  * Passed:  a, b: integers to compare
  1068.  * Returns: a or b, whichever is smaller
  1069.  * Notes:   This function is here because some (most?) C compilers either
  1070.  *           define a macro or have this as a library function.
  1071.  */
  1072. int min(a, b)
  1073. int a;
  1074. int b;
  1075. {
  1076.     return a < b ? a : b;
  1077. }
  1078.  
  1079. #include <sys/types.h>
  1080. #include <sys/stat.h>
  1081. /*
  1082.  * Name:    hw_fattrib
  1083.  * Purpose: To determine the current file attributes.
  1084.  * Date:    October 17, 1989
  1085.  * Passed:  name: name of file to be checked
  1086.  * Returns: current read/write/execute etc attributes of the file, or
  1087.  *          ERROR if file did not exist etc.
  1088.  */
  1089. int hw_fattrib(name)
  1090. char *name;
  1091. {
  1092.     struct stat info;  /* i-node info, including access mode bits */
  1093.  
  1094.     if (stat(name, &info) != 0) {
  1095.         return ERROR;
  1096.     }
  1097.     return info.st_mode;
  1098. }
  1099.  
  1100. /*
  1101.  * Name:    hw_set_fattrib
  1102.  * Purpose: To set the current file attributes.
  1103.  * Date:    October 17, 1989
  1104.  * Passed:  name:   name of file to be changed
  1105.  *          attrib: the required attributes
  1106.  * Returns: new read/write/execute etc attributes of the file, or
  1107.  *          ERROR if file did not exist etc.
  1108.  * Notes:   If "attrib" is ERROR, then do not change attributes.
  1109.  */
  1110. int hw_set_fattrib(name, attrib)
  1111. char *name;
  1112. int attrib;
  1113. {
  1114.     if (attrib == ERROR) {
  1115.         return ERROR;
  1116.     }
  1117.     return chmod(name, attrib);
  1118. }
  1119.  
  1120. /*
  1121.  * Name:    hw_unlink
  1122.  * Purpose: To delete a file, regardless of access modes.
  1123.  * Date:    October 17, 1989
  1124.  * Passed:  name:   name of file to be removed
  1125.  * Returns: OK if file could be removed
  1126.  *          ERROR otherwise
  1127.  */
  1128. int hw_unlink(name)
  1129. char *name;
  1130. {
  1131.     if ((hw_fattrib(name) & 0200) == 0) { /* file cannot be written */
  1132.         set_prompt("File is write protected! Overwrite anyway? (y/n): ", 1);
  1133.         if (display(get_yn, 1) != A_YES) {
  1134.             return ERROR;
  1135.         }
  1136.         if (chmod(name, 0600) == ERROR) {
  1137.             return ERROR;
  1138.         }
  1139.     }
  1140.     return unlink(name);
  1141. }
  1142.  
  1143. /*
  1144.  * Name:    hw_printable
  1145.  * Purpose: To determine whether or not a character is printable on the
  1146.  *           current hardware.
  1147.  * Date:    October 18, 1989
  1148.  * Passed:  c: the character to be tested
  1149.  * Returns: TRUE if c is a visible character, FALSE otherwise
  1150.  * Notes:   This is hardware dependent so that machines like the IBM PC can
  1151.  *           edit files containing graphics characters.
  1152.  */
  1153. int hw_printable(c)
  1154. int c;
  1155. {
  1156.    return (c >= 32 && c < 127);
  1157. }
  1158.  
  1159. /*
  1160.  * Name:    hw_load
  1161.  * Purpose: To load a file into the text buffer.
  1162.  * Date:    November 11, 1989
  1163.  * Passed:  name:  name of disk file
  1164.  *          start: first character in text buffer
  1165.  *          limit: last available character in text buffer
  1166.  *          end:   last character (+1) from the file
  1167.  * Returns: OK, or ERROR if anything went wrong
  1168.  * Notes:   All error messages are displayed here, so the caller should
  1169.  *           neither tell the user what is happening, nor print an error
  1170.  *           message if anything goes wrong.
  1171.  *          The file is read in 1K chunks. It might be more efficient
  1172.  *           to read the entire file with a single system call, provided
  1173.  *           there is no limit on the number of bytes that can be read
  1174.  *           in a single call.
  1175.  *          This function is in the hardware dependent module because
  1176.  *           some computers require non-standard open parameters...
  1177.  */
  1178. int hw_load(name, start, limit, end)
  1179. char *name;
  1180. text_ptr start;
  1181. text_ptr limit;
  1182. text_ptr *end;
  1183. {
  1184.     int fd;         /* file being read */
  1185.     int length;     /* number of bytes actually read */
  1186.  
  1187.     /*
  1188.      * try reading the file
  1189.      */
  1190.     if ((fd = open(name, O_RDONLY)) == ERROR) {
  1191.         error(WARNING, "File '%s' not found", name);
  1192.         return ERROR;
  1193.     }
  1194.  
  1195.     /*
  1196.      * tell the user what is happening
  1197.      */
  1198.     error(TEMP, "Reading file '%s'...", name);
  1199.  
  1200.     /*
  1201.      * read the entire file, without going past end of buffer.
  1202.      * Note that this means a file that is within 1K of the limit
  1203.      *  will not be accepted.
  1204.      */
  1205.     limit -= 1024;
  1206.     for (;;) {
  1207.         if (start >= limit) {
  1208.             error(WARNING, "file '%s' too big", name);
  1209.             close(fd);
  1210.             return ERROR;
  1211.         }
  1212.         if ((length = read(fd, (char *)start, 1024)) == ERROR) {
  1213.             error(WARNING, "could not read file '%s'", name);
  1214.             close(fd);
  1215.             return ERROR;
  1216.         }
  1217.         start += length;
  1218.         if (length == 0) {
  1219.             /*
  1220.              * we reached the end of file
  1221.              */
  1222.             break;
  1223.         }
  1224.     }
  1225.  
  1226.     /*
  1227.      * close the file and report the final character in the buffer
  1228.      */
  1229.     close(fd);
  1230.     *end = start;
  1231.  
  1232.     return OK;
  1233. }
  1234.  
  1235. /*
  1236.  * Name:    write_file
  1237.  * Purpose: To write text to a file, eliminating trailing space on the
  1238.  *           way.
  1239.  * Date:    November 11, 1989
  1240.  * Passed:  name:  name of disk file or device
  1241.  *          mode:  fopen flags to be used when opening file
  1242.  *          start: first character in text buffer
  1243.  *          end:   last character (+1) in text buffer
  1244.  * Returns: OK, or ERROR if anything went wrong
  1245.  * Notes:   Trailing space at the very end of the text is NOT removed,
  1246.  *           so that a block write of a block of spaces will work.
  1247.  *          No error messages are displayed here, so the caller must
  1248.  *           both tell the user what is happening, and print an error
  1249.  *           message if anything goes wrong.
  1250.  *          This function is in the hardware dependent module because
  1251.  *           some computers require non-standard open parameters...
  1252.  */
  1253. static int write_file(name, mode, start, end)
  1254. char *name;
  1255. char *mode;
  1256. text_ptr start;
  1257. text_ptr end;
  1258. {
  1259.     FILE *fp;       /* file to be written */
  1260.     int spaces;     /* no. of space characters pending */
  1261.     char c;         /* current character in file */
  1262.  
  1263.     /*
  1264.      * create a new file, or truncate an old one
  1265.      */
  1266.     if ((fp = fopen(name, mode)) == NULL) {
  1267.         return ERROR;
  1268.     }
  1269.  
  1270.     /*
  1271.      * save the file, eliminating trailing space
  1272.      */
  1273.     spaces = 0;
  1274.     for (;;) {
  1275.         if (start == end) {
  1276.             break;
  1277.         }
  1278.         if ((c = *start++) == ' ') {
  1279.             spaces++;   /* count them, maybe output later */
  1280.             continue;
  1281.         }
  1282.  
  1283.         if (c == '\n') {
  1284.             spaces = 0; /* eliminate the trailing space */
  1285.         }
  1286.         else if (spaces) {
  1287.             /*
  1288.              * the spaces were NOT trailing, so output them now
  1289.              */
  1290.             do {
  1291.                 if (putc(' ', fp) == ERROR) {
  1292.                     fclose(fp);
  1293.                     return ERROR;
  1294.                 }
  1295.             } while (--spaces);
  1296.         }
  1297.  
  1298.         if (putc(c, fp) == ERROR) {
  1299.             fclose(fp);
  1300.             return ERROR;
  1301.         }
  1302.     }
  1303.  
  1304.     /*
  1305.      * output any trailing space at end of file - this may be important
  1306.      *  for block writes.
  1307.      */
  1308.     if (spaces) {
  1309.         do {
  1310.             if (putc(' ', fp) == ERROR) {
  1311.                 fclose(fp);
  1312.                 return ERROR;
  1313.             }
  1314.         } while (--spaces);
  1315.     }
  1316.  
  1317.     return fclose(fp);
  1318. }
  1319.  
  1320. /*
  1321.  * Name:    hw_save
  1322.  * Purpose: To save text to a file, eliminating trailing space on the
  1323.  *           way.
  1324.  * Date:    November 11, 1989
  1325.  * Passed:  name:  name of disk file
  1326.  *          start: first character in text buffer
  1327.  *          end:   last character (+1) in text buffer
  1328.  * Returns: OK, or ERROR if anything went wrong
  1329.  * Notes:   Trailing space at the very end of the file is NOT removed,
  1330.  *           so that a block write of a block of spaces will work.
  1331.  *          No error messages are displayed here, so the caller must
  1332.  *           both tell the user what is happening, and print an error
  1333.  *           message if anything goes wrong.
  1334.  *          This function is in the hardware dependent module because
  1335.  *           some computers require non-standard open parameters...
  1336.  */
  1337. int hw_save(name, start, end)
  1338. char *name;
  1339. text_ptr start;
  1340. text_ptr end;
  1341. {
  1342.     return write_file(name, "w", start, end);
  1343. }
  1344.  
  1345. /*
  1346.  * Name:    hw_append
  1347.  * Purpose: To append text to a file.
  1348.  * Date:    November 11, 1989
  1349.  * Passed:  name:  name of disk file
  1350.  *          start: first character in text buffer
  1351.  *          end:   last character (+1) in text buffer
  1352.  * Returns: OK, or ERROR if anything went wrong
  1353.  * Notes:   No error messages are displayed here, so the caller must
  1354.  *           both tell the user what is happening, and print an error
  1355.  *           message if anything goes wrong.
  1356.  *          This function is in the hardware dependent module because
  1357.  *           some computers require non-standard open parameters...
  1358.  */
  1359. int hw_append(name, start, end)
  1360. char *name;
  1361. text_ptr start;
  1362. text_ptr end;
  1363. {
  1364.     return write_file(name, "a", start, end);
  1365. }
  1366.  
  1367. /*
  1368.  * Name:    hw_print
  1369.  * Purpose: To print text to a printer.
  1370.  * Date:    November 11, 1989
  1371.  * Passed:  start: first character in text buffer
  1372.  *          end:   last character (+1) in text buffer
  1373.  * Returns: OK, or ERROR if anything went wrong
  1374.  * Notes:   This function is in the hardware dependent module because
  1375.  *           some computers require non-standard open parameters...
  1376.  *          The system "lp" command is used to get banners etc.
  1377.  */
  1378. int hw_print(start, end)
  1379. text_ptr start;
  1380. text_ptr end;
  1381. {
  1382.     char command[80];       /* printer selection */
  1383.     char temp[40];          /* temporary file name */
  1384.  
  1385.     /*
  1386.      * make temporary file name
  1387.      */
  1388.     strcpy(temp, "/tmp/dteXXXXXX");
  1389.     mktemp(temp);
  1390.  
  1391.     /*
  1392.      * work out where to print
  1393.      */
  1394.     for (;;) {
  1395.         strcpy(command, "2");
  1396.         if (get_name("Printer 1N208 (1), 2S125 (2), Computer Center (3): ",
  1397.                 1, command) != OK) {
  1398.             return ERROR;
  1399.         }
  1400.         switch (atoi(command)) {
  1401.         case 1:
  1402.             sprintf(command, "lp -dlp208 -c -s -t%s %s",
  1403.                     g_status.current_window->file_info->file_name, temp);
  1404.             break;
  1405.         case 2:
  1406.             sprintf(command, "lp -dlp125 -c -s -t%s %s",
  1407.                     g_status.current_window->file_info->file_name, temp);
  1408.             break;
  1409.         case 3:
  1410.             sprintf(command, "lp -dlpc -c -s -t%s %s",
  1411.                     g_status.current_window->file_info->file_name, temp);
  1412.             break;
  1413.         default:
  1414.             continue;
  1415.         }
  1416.         break;
  1417.     }
  1418.  
  1419.     /*
  1420.      * print file
  1421.      */
  1422.     write_file(temp, "w", start, end);
  1423.     system(command);
  1424.     unlink(temp);
  1425. }
  1426.  
  1427. /*
  1428.  * Name:    hw_copy_path
  1429.  * Purpose: To create a new file path using most of an old path but
  1430.  *           changing just the file name.
  1431.  * Date:    November 8, 1989
  1432.  * Passed:  old:   the file path to extract path info from
  1433.  *          name:  the file name to add to the extracted path info
  1434.  * Returns: new:   the new path
  1435.  * Notes:   The file is located in the same place as the original, so
  1436.  *           that related editor files stay in the same directory.
  1437.  *          This function is hardware dependent because different characters
  1438.  *           delimit directories on different systems.
  1439.  */
  1440. void hw_copy_path(old, name, new)
  1441. char *old;
  1442. char *name;
  1443. char *new;
  1444. {
  1445.     char *cp;           /* cutoff point in old path */
  1446.     char *id;           /* current user id */
  1447.  
  1448.     strcpy(new, old);
  1449.     if ((cp = strrchr(new, '/')) != NULL) {
  1450.         ++cp;
  1451.     }
  1452.     else {
  1453.         cp = new;
  1454.     }
  1455.  
  1456.     /*
  1457.      * On UNIX systems it is common for multiple users to access the same
  1458.      *  directory (for example /tmp). It is therefore desirable to make
  1459.      *  the save file name unique for each user...
  1460.      */
  1461.     if (strcmp(name, RECOVERY) == 0) {
  1462.         strcpy(cp, (id = cuserid(NULL)) ? id : "");
  1463.         cp += strlen(cp);
  1464.     }
  1465.  
  1466.     strcpy(cp, name);
  1467. }
  1468.  
  1469. /*
  1470.  * Name:    hw_os_shell
  1471.  * Purpose: To shell out of the editor into the operating system, in such a
  1472.  *           way that editing may be resumed later.
  1473.  * Date:    November 28, 1990
  1474.  * Returns: TRUE if screen may have been clobbered, FALSE if screen OK.
  1475.  */
  1476. int hw_os_shell()
  1477. {
  1478.     struct termio term;    /* to avoid ^S ^Q processing */
  1479.     int flags;             /* for setting no delay on read */
  1480.     static char ci[100] = "/bin/csh";
  1481.     char *getenv();
  1482.     if (*getenv("SHELL")) {
  1483.         strcpy(ci, getenv("SHELL"));
  1484.     }
  1485.     hw_terminate();
  1486.     if (fork() == 0) {
  1487.         execl(ci, ci, NULL);
  1488.     }
  1489.     else {
  1490.         wait(NULL);     /* wait for child to finish */
  1491.     }
  1492.     /*
  1493.      * get rid of XON/XOFF handling, echo, and other input processing
  1494.      */
  1495.     if (ioctl(0, TCGETA, &term) == -1) {
  1496.         error(FATAL, "could not get termio");
  1497.     }
  1498.     (void)ioctl(0, TCGETA, &term_orig);
  1499.     term.c_iflag = 0;
  1500.     term.c_oflag = 0;
  1501.     term.c_lflag = 0;
  1502.     term.c_cc[VMIN] = 1;
  1503.     term.c_cc[VTIME] = 0;
  1504.     if (ioctl(0, TCSETA, &term) == -1) {
  1505.         error(FATAL, "could not set termio");
  1506.     }
  1507.     kbdflgs = fcntl( 0, F_GETFL, 0 );
  1508.     /*
  1509.      * set up no delay when checking if anything is in the buffer
  1510.      */
  1511.     flags = fcntl(0, F_GETFL);
  1512.     flags |= O_NDELAY;
  1513.     fcntl(0, F_SETFL, flags);
  1514.     return TRUE;
  1515. }
  1516.