home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 399_01 / minedaux.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-03  |  40.6 KB  |  1,785 lines

  1. /*  ==================================================================    *
  2.  *                Editor mined                *
  3.  *                auxiliary part                *
  4.  *  ==================================================================    */
  5.  
  6. #include "mined.h"
  7.  
  8. int panic_level = 0;        /* To adjust error handling to situation */
  9. FLAG pagewrapped = FALSE;    /* Did output on the bottom line wrap and scroll? */
  10.  
  11. /*  ==================================================================    *
  12.  *            Auxiliary routines                *
  13.  *  ==================================================================    */
  14.  
  15. /*
  16.  * Delete file.
  17.  */
  18. void
  19. delete_file (file)
  20.   char * file;
  21. {
  22. #ifdef unix
  23.   unlink (file);
  24. #endif
  25. #ifdef msdos
  26.   unlink (file);
  27. #endif
  28. #ifdef vms
  29.   delete (file);
  30. #endif
  31. }
  32.  
  33. /*
  34.  * Delete yank file if there is one.
  35.  */
  36. void
  37. delete_yank_file ()
  38. {
  39.   if (yank_status == VALID) delete_file (yank_file);
  40. }
  41.  
  42. /*
  43.  * Panic () is called with a mined error msg and an optional system error msg.
  44.  * It is called when something unrecoverable has happened.
  45.  * It writes the message to the terminal, resets the tty and exits.
  46.  * Ask the user if he wants to save his file.
  47.  */
  48. #define panic_msg(msg)    if (isscreenmode == TRUE) {status_msg (msg); sleep (2);} else (void) printf ("%s\n", msg);
  49. void
  50. panicking (message, err, signum)
  51.   register char * message;
  52.   register char * err;
  53.   register int signum;
  54. {
  55.   int panic_written;
  56.   void QUED ();
  57.  
  58.   panic_level ++;
  59.  
  60.   if (panic_level < 2) {
  61.     if (loading == FALSE && modified == TRUE) {
  62.         panic_written = panicwrite ();
  63.         if (panic_written == ERRORS) {
  64.             build_string (text_buffer, "Error writing panic file %s", panic_file);
  65.             sleep (2);
  66.         }
  67.         else
  68.             build_string (text_buffer, "Panic file %s written", panic_file);
  69.         ring_bell ();
  70.         panic_msg (text_buffer);
  71.     }
  72.     if (signum != 0)
  73.         build_string (text_buffer, message, signum);
  74.     else if (err == NIL_PTR)
  75.         build_string (text_buffer, "%s", message);
  76.     else
  77.         build_string (text_buffer, "%s (Error: %s)", message, err);
  78.     panic_msg (text_buffer);
  79.  
  80.     /* "normal" panic handling: */
  81.     if (loading == FALSE) {
  82.         QUED ();    /* Try to save the file and quit */
  83.         /* QUED returned: something wrong */
  84.         sleep (2);
  85.         panic_msg ("Aborted writing file in panic mode - trying to continue");
  86.         panic_level --;
  87.         return;
  88.     }
  89.   }
  90.  
  91.   if (panic_level < 3) {
  92.     if (isscreenmode == TRUE) {
  93.         set_cursor (0, YMAX);
  94.         putchar ('\n');
  95.         raw_mode (OFF);
  96.     }
  97.     delete_yank_file ();
  98.   }
  99.   exit (1) /* abort () sends IOT which would again be caught */;
  100. }
  101.  
  102. void
  103. panic (message, err)
  104.   register char * message;
  105.   register char * err;
  106. {
  107.   panicking (message, err, 0);
  108. }
  109.  
  110. void
  111. panicio (message, err)
  112.   register char * message;
  113.   register char * err;
  114. {
  115. /* Should panic_level already be increased here ? */
  116.   panic (message, err);
  117. }
  118.  
  119. void
  120. catch_interrupt (signum)
  121.   int signum;
  122. {
  123.   panicking ("External signal %d caught - terminating", NIL_PTR, signum);
  124.   catch_signals (catch_interrupt);
  125. }
  126.  
  127. /*-------------------------------------------------------------------------*/
  128.  
  129. /*
  130.  * Memory allocation
  131.  */
  132. #ifdef msdos
  133. #include <alloc.h>
  134. #define allocate farmalloc
  135. #define freemem farfree
  136. #else
  137. extern void * malloc ();
  138. extern void free ();
  139. #define allocate malloc
  140. #define freemem free
  141. #endif
  142.  
  143. char *
  144. alloc (bytes)
  145.   int bytes;
  146. {
  147.   return allocate ((unsigned) bytes);
  148. /*
  149.   char * p;
  150.  
  151.   if ((p = allocate ((unsigned) bytes)) == NIL_PTR)
  152.     panic ("Out of memory", NIL_PTR);
  153.   return p;
  154. */
  155. }
  156.  
  157. void
  158. free_space (p)
  159.   char * p;
  160. {
  161.   freemem (p);
  162. }
  163.  
  164. /*
  165.  * free header list
  166.  */
  167.  
  168. #define pointersize    sizeof (void *)
  169. #define blocksizeof(typ) ((sizeof (typ) + pointersize - 1) / pointersize * pointersize)
  170.  
  171. LINE * free_header_list = NIL_LINE;
  172.  
  173. void
  174. alloc_headerblock (n)
  175.   int n;
  176. {
  177.   LINE * new_header;
  178.   LINE * new_list;
  179.   int i = 0;
  180.  
  181.   new_list = (LINE *) alloc (n * blocksizeof (LINE));
  182.   if (new_list == NIL_LINE) free_header_list = NIL_LINE;
  183.   else while (i < n) {
  184.     new_header = (LINE *) ((long) new_list + i * blocksizeof (LINE));
  185.     new_header->next = free_header_list;
  186.     free_header_list = new_header;
  187.     i ++;
  188.   }
  189. }
  190.  
  191. LINE *
  192. alloc_header ()
  193. {
  194. /*  return (LINE *) alloc (sizeof (LINE)); */
  195.   LINE * new_header;
  196.  
  197.   if (free_header_list == NIL_LINE) {
  198.     alloc_headerblock (64);
  199.     if (free_header_list == NIL_LINE) alloc_headerblock (16);
  200.     if (free_header_list == NIL_LINE) alloc_headerblock (4);
  201.     if (free_header_list == NIL_LINE) alloc_headerblock (1);
  202.     if (free_header_list == NIL_LINE) return NIL_LINE;
  203.   }
  204.   new_header = free_header_list;
  205.   free_header_list = free_header_list->next;
  206.   return new_header;
  207. }
  208.  
  209. void
  210. free_header (hp)
  211.   LINE * hp;
  212. {
  213. /*  freemem (hp); */
  214.   hp->next = free_header_list;
  215.   free_header_list = hp;
  216. }
  217.  
  218. /*
  219.  * Basename () finds the absolute name of the file out of a given path_name.
  220.  */
  221. #ifdef UNUSED
  222. char *
  223. basename (path)
  224.   char * path;
  225. {
  226.   register char * ptr = path;
  227.   register char * last = NIL_PTR;
  228.  
  229.   while (* ptr != '\0') {
  230.     if (* ptr == '/')
  231.         last = ptr;
  232.     ptr ++;
  233.   }
  234.   if (last == NIL_PTR)
  235.     return path;
  236.   if (* (last + 1) == '\0') {    /* E.g. /usr/tmp/pipo/ */
  237.     * last = '\0';
  238.     return basename (path);    /* Try again */
  239.   }
  240.   return last + 1;
  241. }
  242. #endif
  243.  
  244. /*
  245.  * Unnull () changes a NULL string pointer into an empty string pointer 
  246.  * to allow easy feading of string results into build_string / sprintf
  247.  */
  248. char *
  249. unnull (s)
  250.   char * s;
  251. {
  252.   if (s == NIL_PTR) return "";
  253.   else return s;
  254. }
  255.  
  256. /*
  257.  * Output an (unsigned) long in a 10 digit field without leading zeros.
  258.  * It returns a pointer to the first digit in the buffer.
  259.  */
  260. char *
  261. num_out (number)
  262.   long number;
  263. {
  264.   static char num_buf [11];        /* Buffer to build number */
  265.   register long digit;            /* Next digit of number */
  266.   register long pow = 1000000000L;    /* Highest ten power of long */
  267.   FLAG digit_seen = FALSE;
  268.   int i;
  269.  
  270.   for (i = 0; i < 10; i ++) {
  271.     digit = number / pow;        /* Get next digit */
  272.     if (digit == 0L && digit_seen == FALSE && i != 9)
  273.         num_buf [i] = ' ';
  274.     else {
  275.         num_buf [i] = '0' + (char) digit;
  276.         number -= digit * pow;    /* Erase digit */
  277.         digit_seen = TRUE;
  278.     }
  279.     pow /= 10L;            /* Get next digit */
  280.   }
  281.   num_buf [11] = '\0';
  282.   for (i = 0; num_buf [i] == ' '; i ++)    /* Skip leading spaces */
  283.     ;
  284.   return & num_buf [i];
  285. }
  286.  
  287. /*
  288.  * Build_string () prints the arguments as described in fmt, into the buffer.
  289.  * %s indicates a string argument, %d indicates an integer argument.
  290.  */
  291. #ifndef build_string /* otherwise build_string is sprintf */
  292. /* VARARGS */
  293. void
  294. build_string (buf, fmt, args)
  295.   register char * buf, * fmt;
  296.   int args;
  297. {
  298.   int * argptr = & args;
  299.   char * scanp;
  300.   FLAG islong;
  301.  
  302.   while (* fmt) {
  303.      if (* fmt == '%') {
  304.     fmt ++;
  305.     if (* fmt == 'l') {islong = TRUE; fmt ++;}
  306.              else islong = FALSE;
  307.     switch (* fmt ++) {
  308.     case 's' :
  309.         scanp = (char *) * argptr;
  310.         break;
  311.     case 'd' :
  312.         if (islong == TRUE) {
  313.             scanp = num_out ((long) * ((long *) argptr));
  314.             if (sizeof (long) > sizeof (int)) argptr ++;
  315.         break;
  316.         }
  317.         else {
  318.             scanp = num_out ((long) * argptr);
  319.             break;
  320.         }
  321.     case 'D' :
  322.         scanp = num_out ((long) * ((long *) argptr));
  323.         if (sizeof (long) > sizeof (int)) argptr ++;
  324.         break;
  325.     default :
  326.         scanp = "";
  327.     }
  328.     while (* buf ++ = * scanp ++)
  329.         ;
  330.     buf --;
  331.     argptr ++;
  332.      }
  333.      else
  334.     * buf ++ = * fmt ++;
  335.   }
  336.   * buf = '\0';
  337. }
  338. #endif /* ndef build_string */
  339.  
  340. /*
  341.  * make_number () converts a string into a natural number
  342.  * returns the character after the last digit
  343.  */
  344. int
  345. make_number (num, str)
  346.   int * num;
  347.   char * str;
  348. {
  349.   register char * chpoi = str;
  350.  
  351.   * num = 0;
  352.   while (* chpoi >= '0' && * chpoi <= '9' && quit == FALSE) {
  353.     * num *= 10;
  354.     * num += * chpoi - '0';
  355.     chpoi ++;
  356.   }
  357.   return * chpoi;
  358. }
  359.  
  360. /*
  361.  * Length_of () returns the number of characters in the string `string'
  362.  * excluding the '\0'.
  363.  */
  364. int
  365. length_of (string)
  366.   register char * string;
  367. {
  368.   register int count = 0;
  369.  
  370.   if (string != NIL_PTR) {
  371.     while (* string ++ != '\0')
  372.         count ++;
  373.   }
  374.   return count;
  375. }
  376.  
  377. /*
  378.  * text_length_of () returns the number of characters in the string `string'
  379.  * up to and excluding the first '\n'.
  380.  */
  381. int
  382. text_length_of (string)
  383.   register char * string;
  384. {
  385.   register int count = 0;
  386.  
  387.   if (string != NIL_PTR) {
  388.     while (* string != '\0' && * string != '\n') {
  389.         string ++;
  390.         count ++;
  391.     }
  392.   }
  393.   return count;
  394. }
  395.  
  396. /*
  397.  * Copy_string () copies the string `from' into the string `to'. `To' must be
  398.  * long enough to hold `from'.
  399.  */
  400. void
  401. copy_string (to, from)
  402.   register char * to;
  403.   register char * from;
  404. {
  405.   while ((* to ++ = * from ++) != '\0')
  406.     ;
  407. }
  408.  
  409. /*-------------------------------------------------------------------------*/
  410.  
  411. /*
  412.  * serrorof delivers the error message of the given errno value.
  413.  * serror delivers the error message of the current errno value.
  414.  * geterrno just returns the current errno value.
  415.  */
  416. #ifdef vms
  417. /* #define includeerrno */
  418. #  ifdef includeerrno
  419. #  include <errno.h>
  420. #  else
  421.     extern volatile int noshare errno;
  422.     extern volatile int noshare vaxc$errno; /* VMS error code when errno = EVMSERR */
  423. #  define EVMSERR 65535
  424.     extern volatile int noshare sys_nerr;
  425.     extern volatile char noshare * sys_errlist [];
  426. #  endif
  427. #else
  428.   extern int errno;
  429.   extern int sys_nerr;
  430.   extern char * sys_errlist [];
  431. #endif
  432.  
  433. char *
  434. serrorof (errnum)
  435.   int errnum;
  436. {
  437.   if ((errnum < 0) || (errnum >= sys_nerr))
  438.     { static char s [20];
  439. #ifdef vms
  440.       if (errnum == EVMSERR)
  441.          build_string (s, "VMS error %d", vaxc$errno);
  442.       else
  443. #endif
  444.          build_string (s, "Unknown error %d", errnum);
  445.       return s;
  446.     }
  447.   else    return sys_errlist [errnum];
  448. }
  449.  
  450. char *
  451. serror ()
  452. { return serrorof (errno); }
  453.  
  454. int
  455. geterrno ()
  456. { return errno; }
  457.  
  458. /*  ==================================================================    *
  459.  *                Output                    *
  460.  *  ==================================================================    */
  461.  
  462. #ifdef msdos
  463. #define iscontrol(c)    (((c) == '\177') || ((uchar) c < (uchar) ' '))
  464. #else
  465. #define iscontrol(c)    (((c) == '\177') || ((uchar) ((c) & '\177') < (uchar) ' '))
  466. #endif
  467. #define controlchar(c)    (((c) == '\177') ? '?' : (c) + '@')
  468.  
  469. /*
  470.  * Bad_write () is called when a write failed. Notify the user.
  471.  */
  472. void
  473. bad_write (fd)
  474.   int fd;
  475. {
  476.   if (fd == output_fd) {    /* Cannot write to terminal? */
  477.     raw_mode (OFF);
  478.     panicio ("Write error on terminal", NIL_PTR);
  479.   }
  480.  
  481.   clear_buffer (); /* out_count = 0; */
  482.   ring_bell ();
  483.   error ("Write aborted (File incomplete): ", serror ());
  484. }
  485.  
  486. /*
  487.  * Flush the I/O buffer on filedescriptor fd.
  488. flush () is (void) flush_buffer (output_fd)
  489.  */
  490. int
  491. flush_buffer (fd)
  492.   int fd;
  493. {
  494.   if (out_count <= 0)        /* There is nothing to flush */
  495.     return FINE;
  496. #ifdef conio
  497.   if (fd == output_fd) {
  498.     cputs (screen);
  499.   }
  500. #else
  501. #ifdef BorlandC
  502.   if (fd == output_fd) {
  503.     screen [out_count] = '\0';
  504. /* don't ask me why that crazy compiler doesn't work with write () below */
  505.     printf ("%s", screen);
  506.   }
  507. #endif
  508. #endif
  509.   else
  510.   if (write (fd, screen, out_count) != out_count) {
  511.     bad_write (fd);
  512.     return ERRORS;
  513.   }
  514.   clear_buffer (); /* Empty buffer: out_count = 0; */
  515.   return FINE;
  516. }
  517.  
  518. /*
  519.  * Write_char does a buffered output.
  520. putchar (c) is (void) writechar (output_fd, (c))
  521.  */
  522. int
  523. writechar (fd, c)
  524.   int fd;
  525.   char c;
  526. {
  527.   if (c == '\n') if (fd == output_fd) {
  528.     if (writechar (fd, '\015') == ERRORS) return ERRORS;
  529.   }
  530.   screen [out_count ++] = c;
  531.   if (out_count == screen_BUFL)    /* Flush on screen_BUFL chars */
  532.     return flush_buffer (fd);
  533. #ifdef DEBUG
  534.   if (fd == output_fd) flush ();
  535. #endif
  536.   return FINE;
  537. }
  538.  
  539. /*
  540.  * Writestring writes the given string on the given filedescriptor.
  541.  * (buffered via writechar via screen !)
  542. putstring (str) is (void) writestring (output_fd, (str))
  543.  */
  544. int
  545. writestring (fd, text)
  546.   register int fd;
  547.   register char * text;
  548. {
  549.   while (* text)
  550.      if (writechar (fd, * text ++) == ERRORS)
  551.         return ERRORS;
  552.   return FINE;
  553. }
  554.  
  555. /*
  556.  * Print string on terminal, printing controls with ^.
  557.  */
  558. int lpos = 0;
  559.  
  560. void
  561. print_char (c)
  562.   register uchar c;
  563. {
  564.   lpos ++;
  565.   if (iscontrol (c)) {
  566.     putchar ('^');
  567.     lpos ++;
  568.     putchar (controlchar (c));
  569.   }
  570.   else putchar (c);
  571. }
  572.  
  573. int
  574. printlim_char (c, limit)
  575.   register uchar c;
  576.   register int limit;
  577. {
  578.   if (lpos == limit) {putchar (SHIFT_MARK); return ERRORS;}
  579.   lpos ++;
  580.   if (iscontrol (c)) {
  581.     putchar ('^');
  582.     if (lpos == limit) {putchar (SHIFT_MARK); return ERRORS;}
  583.     lpos ++;
  584.     putchar (controlchar (c));
  585.   }
  586.   else putchar (c);
  587.   return FINE;
  588. }
  589.  
  590. void
  591. printlim_string (text, limit)
  592.   register char * text;
  593.   register int limit;
  594. {
  595.   lpos = 0;
  596.   while (* text != '\0')
  597.     if (printlim_char (* text ++, limit) == ERRORS)
  598.         return;
  599. }
  600.  
  601. void
  602. print_string (text)
  603.   register char * text;
  604. {
  605.   lpos = 0;
  606.   while (* text != '\0')
  607.     print_char (* text ++);
  608. }
  609.  
  610. void
  611. put_blanks (endpos)
  612.   int endpos;
  613. {
  614.   int startpos = 0;
  615.   while (startpos ++ <= endpos) putchar (' ');
  616. }
  617.  
  618. void
  619. clear_wholeline ()
  620. {
  621.   if (can_clear_eol == TRUE) clear_eol ();
  622.   else put_blanks (XMAX);
  623. }
  624.  
  625. void
  626. clear_lastline ()
  627. {
  628.   if (can_clear_eol == TRUE) clear_eol ();
  629.   else put_blanks (XMAX - 1);
  630. }
  631.  
  632. /*  ==================================================================    *
  633.  *            Buffer oriented output                *
  634.  *  ==================================================================    */
  635.  
  636. /*
  637.  * Put_line prints the given line on the standard output.
  638.  * If offset is not zero, printing will start at that x-coordinate.
  639.  * If the FLAG clear_line is TRUE, then the screen line will be cleared
  640.  * when the end of the line has been reached.
  641. line_print (line) is put_line (line, 0, TRUE, FALSE)
  642.  * put_line is directly called only by S () and delete_text ()
  643.  */
  644. void
  645. put_line (line, offset, clear_line, positioning)
  646.   LINE * line;        /* Line to print */
  647.   int offset;        /* Offset to start if positioning == FALSE */
  648.             /* position if positioning == TRUE */
  649.   FLAG clear_line;    /* Clear to eoln if TRUE */
  650.   FLAG positioning;    /* positioning inside line if TRUE (for prop. fonts) */
  651. {
  652.   register char * textp = line->text;
  653.   register int count = get_shift (line->shift_count) * - SHIFT_SIZE;
  654.   int count_ini = count;
  655.   int tab_count;            /* Used in tab expansion */
  656.   int offset_start;
  657.   int offset_stop;
  658.  
  659.   if (positioning == TRUE) {
  660.     offset_start = 0;
  661.     offset_stop = offset;
  662.   } else {
  663.     offset_start = offset;
  664.     offset_stop = XBREAK;
  665.   }
  666.  
  667. /* Skip all chars as indicated by the offset_start and the shift_count field */
  668.   while (count < offset_start) {
  669.     if (is_tab (* textp ++))
  670.         count = tab (count);
  671.     else
  672.         count ++;
  673.   }
  674.  
  675.   if (count == 0 && count_ini < 0 && SHIFT_BEG != '\0') {
  676.     putchar (SHIFT_BEG);
  677.     count ++;
  678.     if (! is_tab (* textp)) textp ++;
  679.   }
  680.  
  681.   while (* textp != '\n' && count < offset_stop) {
  682.     if (is_tab (* textp)) {        /* Expand tabs to spaces */
  683.         tab_count = tab (count);
  684.         while (count < offset_stop && count < tab_count) {
  685.             count ++;
  686.             putchar (TABchar);
  687.         }
  688.         textp ++;
  689.     }
  690.     else {
  691.         if (iscontrol (* textp)) {
  692.             reverse_on ();
  693.             putchar (controlchar (* textp));
  694.             reverse_off ();
  695.             textp ++;
  696.         }
  697.         else
  698.             putchar (* textp ++);
  699.         count ++;
  700.     }
  701.   }
  702.  
  703.   if (positioning == TRUE) {
  704.     /* self-made cursor for terminals (such as xterm)
  705.        which have display problems with proportional screen fonts
  706.        and their cursor */
  707.     reverse_on ();
  708.     if (* textp != '\n') putchar (* textp);
  709.     else if (RET_MARK != '\0') putchar (RET_MARK);
  710.     else putchar (' ');
  711.     reverse_off ();
  712.     set_cursor (0, YMAX);
  713.   }
  714.   else /* (positioning == FALSE) */ {
  715.     /* If line is longer than XBREAK chars, print the shift_mark */
  716.     if (count == XBREAK && * textp != '\n') {
  717.         putchar (SHIFT_MARK);
  718.         count ++;
  719.     }
  720.  
  721.     /* Mark end of line if so desired */
  722.     if (* textp == '\n' && RET_MARK != '\0') {
  723.         putchar (RET_MARK);
  724.         count ++;
  725.         if (RET_BLANK) {
  726.             while (count < XBREAK) {
  727.                 putchar (RET_BLANK);
  728.                 count ++;
  729.             }
  730.             if (RET_BLANK2 && count <= XBREAK) {
  731.                 putchar (RET_BLANK2);
  732.                 count ++;
  733.             }
  734.         }
  735.     }
  736.  
  737.     /* Clear the rest of the line if clear_line is TRUE */
  738.     if (clear_line == TRUE) {
  739.         if (can_clear_eol == TRUE) {
  740.             if (count <= XBREAK) clear_eol ();
  741.         }
  742.         else {
  743.             while (count ++ <= XBREAK)    /* clear up to XMAX */
  744.                 putchar (' ');
  745.         }
  746.     }
  747.   }
  748. }
  749.  
  750. /*
  751.  * set_cursor_xy sets the cursor by either directly calling set_cursor
  752.  * or, in the case of proportional font support, reprinting the line
  753.  * up to the x position
  754.  */
  755. void
  756. set_cursor_xy ()
  757. {
  758.   if (proportional == TRUE) {
  759.     set_cursor (0, y);
  760.     if (x != 0) put_line (cur_line, x, FALSE, TRUE);
  761.     /* cur_line may still be undefined if x == 0 */
  762.   }
  763.     else set_cursor (x, y);
  764. }
  765.  
  766. /*
  767.  * Proceed returns the count'th line after `line'. When count is negative
  768.  * it returns the count'th line before `line'. When the next (previous)
  769.  * line is the tail (header) indicating EOF (tof) it stops.
  770.  */
  771. LINE *
  772. proceed (line, count)
  773.   register LINE * line;
  774.   register int count;
  775. {
  776.   if (count < 0)
  777.     while (count ++ < 0 && line != header)
  778.         line = line->prev;
  779.   else
  780.     while (count -- > 0 && line != tail)
  781.         line = line->next;
  782.   return line;
  783. }
  784.  
  785. /*
  786.  * Reset assigns bot_line, top_line and cur_line according to `head_line'
  787.  * which must be the first line of the screen, and a y-coordinate,
  788.  * which will be the current y-coordinate (if it isn't larger than last_y)
  789.  */
  790. void
  791. reset (head_line, screen_y)
  792.   LINE * head_line;
  793.   int screen_y;
  794. {
  795.   register LINE * line;
  796.  
  797.   top_line = line = head_line;
  798.  
  799. /* Search for bot_line (might be last line in file) */
  800.   for (last_y = 0; last_y < total_lines - 1 && last_y < SCREENMAX
  801.                     && line->next != tail; last_y ++)
  802.     line = line->next;
  803.  
  804.   bot_line = line;
  805.   y = (screen_y > last_y) ? last_y : screen_y;
  806.  
  807. /* Set cur_line according to the new y value */
  808.   cur_line = proceed (top_line, y);
  809. }
  810.  
  811. /*
  812.  * Display line at screen line y_pos if it lies between y_min and y_max.
  813.  * If it is no text line (end of file), clear screen line.
  814.  */
  815. void
  816. display_line_at (y_pos, line, y_min, y_max, first)
  817.   int y_pos, y_min, y_max;
  818.   register LINE * line;
  819.   FLAG first;
  820. {
  821.   line = proceed (line, y_pos - y_min);
  822.   if (y_pos >= y_min && y_pos <= y_max) {
  823.     set_cursor (0, y_pos);
  824.     if (line == tail) clear_wholeline ();
  825.     else {
  826.         if (first == FALSE) {
  827.             if (display_delay >= 0) flush ();
  828.             if (display_delay > 0)
  829. #ifdef msdos
  830.                 delay (display_delay);
  831. #else
  832.                 (void) usleep (1000 * display_delay);
  833. #endif
  834.         }
  835.         line_print (line);
  836.     }
  837.   }
  838. }
  839.  
  840. /*
  841.  * Display () shows count + 1 lines on the terminal starting at the given 
  842.  * coordinates. At end of file, the rest of the screen is blanked.
  843.  * When count is negative, a backwards print from `line' will be done.
  844.  */
  845. void
  846. display (y_coord, line, count, new_pos)
  847.   int y_coord, new_pos;
  848.   register LINE * line;
  849.   register int count;
  850. {
  851.   int y_max = y_coord + count;
  852.   int y_off;
  853.  
  854. /* Find new startline if count is negative */
  855.   if (count < 0) {
  856.     line = proceed (line, count);
  857.     count = - count;
  858.   }
  859.  
  860.   display_line_at (new_pos, line, y_coord, y_max, TRUE);
  861.   y_off = 0;
  862.   while (y_off < count) {
  863.     y_off ++;
  864.     display_line_at (new_pos - y_off, line, y_coord, y_max, FALSE);
  865.     display_line_at (new_pos + y_off, line, y_coord, y_max, FALSE);
  866.   }
  867. #ifdef UNUSED
  868. /* old code, building the display from top to bottom (how boring): */
  869. /* with this code, XBREAK must be set to XMAX - 1 */
  870. /* Print the lines */
  871.   set_cursor (0, y_coord);
  872.   while (line != tail && count -- >= 0) {
  873.     line_print (line);
  874.     line = line->next;
  875.   }
  876.  
  877. /* Print the blank lines (if any) */
  878.   if (loading == FALSE) {
  879.     while (count -- >= 0) {
  880.         clear_eol ();
  881.         putchar ('\n');
  882.     }
  883.   }
  884. #endif
  885. }
  886.  
  887. /*  ==================================================================    *
  888.  *            Mined Terminal Dialog                *
  889.  *  ==================================================================    */
  890.  
  891. /*
  892.  * promptyn reads in a 'y' or 'n' character.
  893.  */
  894. uchar
  895. promptyn ()
  896. {
  897.   register uchar c;
  898.   while ((c = readchar ()) != 'y' && c != 'n' && c != '\033' && quit == FALSE) {
  899.     ring_bell ();
  900.     flush ();
  901.   }
  902.   if (c == '\033') quit = TRUE;
  903.   return c;
  904. }
  905.  
  906. /*
  907.  * In case of a QUIT signal, swallow the dummy char generated by catchquit ()
  908.  * called by re_search () and change ()
  909.  */
  910. void
  911. swallow_dummy_quit_char ()
  912. {
  913. #ifdef UNUSED
  914.   (void) readchar (); /* Swallow away a quit character delivered by QUIT */
  915. /* Not needed because this character is ignored by being the CANCEL command */
  916. #endif
  917. }
  918.  
  919. /*
  920.  * Readchar () reads one character from the terminal.
  921.  * There are problems due to interruption of the read operation by signals
  922.  * (QUIT, WINCH). The waitingforinput flag is only a partial solution.
  923.  * Unix doesn't provide sufficient facilities to handle these situations
  924.  * neatly and properly. Moreover, different Unix versions yield different
  925.  * surprising effects. However, the use of select () could still be
  926.  * an improvement.
  927.  */
  928. int
  929. readchar ()
  930. {
  931.   register uchar c;
  932.  
  933. #ifdef msdos
  934.   FLAG waiting;
  935.  
  936.   if (winchg == TRUE && waitingforinput == FALSE) RDwin ();
  937.     /* In the Unix version, this is now done in __readchar () */
  938.   waiting = waitingforinput;
  939.     /* must be saved since in the MSDOS version, readchar can 
  940.        be called recursively */
  941. #endif
  942.   waitingforinput = TRUE;
  943.  
  944.   c = _readchar ();
  945.  
  946. #ifdef msdos
  947.   waitingforinput = waiting;
  948. #else
  949.   waitingforinput = FALSE;
  950. #endif
  951.  
  952.   /* the modification   if (quit == TRUE) c = QUITCHAR;   (now in __readchar)
  953.      must not be placed after resetting the flag  waitingforinput = FALSE;  .
  954.      Otherwise a QUIT signal coming in just between these two would
  955.      discard the last valid character just taken up. */
  956.  
  957.   return c;
  958. }
  959.  
  960. /*-------------------------------------------------------------------------*/
  961.  
  962. #ifndef msdos
  963.  
  964. extern struct {
  965.     char * fk;
  966.     void (* fp) ();
  967. } keycode [];
  968.  
  969. #define MAXCODELEN 7 /* max. length of function key sequence to be detected,
  970.             depending on the keycode table */
  971.  
  972. /*
  973.  * queue collects the keys of an Escape sequence typed in until the 
  974.  * sequence can be detected or rejected.
  975.  * If the queue is not empty, queue [0] contains the character next 
  976.  * to be delivered by _readchar () (it's not a ring buffer).
  977.  * The queue contents is always terminated by a '\0', so queue can also 
  978.  * be taken as a character string.
  979.  */
  980. static uchar queue [MAXCODELEN + 1], * endp = queue;
  981.  
  982. int
  983. q_empty ()
  984. {
  985.   return (endp == queue ? 1 : 0);
  986. }
  987.  
  988. int
  989. q_notfull ()
  990. {
  991.   return (endp - queue == MAXCODELEN ? 0 : 1);
  992. }
  993.  
  994. void
  995. q_clear ()
  996. {
  997.   endp = queue;
  998. }
  999.  
  1000. int
  1001. q_len ()
  1002. {
  1003.   return endp - queue;
  1004. }
  1005.  
  1006. void
  1007. q_put (c)
  1008.   uchar c;
  1009. /* queue must not be full prior to this call! */
  1010. {
  1011.   * endp = c;
  1012.   * ++ endp = '\0';
  1013. }
  1014.  
  1015. uchar
  1016. q_get ()
  1017. {
  1018.   uchar c;
  1019.   register uchar * pd, * ps;
  1020.  
  1021.   c = * queue; pd = queue; ps = pd + 1;
  1022.   while (ps <= endp) * pd ++ = * ps ++;
  1023.   if (endp > queue) endp --;
  1024.   return c;
  1025. }
  1026.  
  1027. /*
  1028.  * Look up key sequence in keycode table.
  1029.  * findkey (str) >=  0: str == keycode [findkey (str)].fk
  1030.  *         == -1: str is prefix of some entry in keycode
  1031.  *         == -2: str is not contained in keycode
  1032.  */
  1033. int
  1034. findkey (str)
  1035.   char * str;
  1036. {
  1037.   static int lastmatch = 0;    /* last index with string matching prefix */
  1038.   register int i;
  1039.  
  1040.   if (keycode [0].fk == NIL_PTR) return -2;
  1041.   i = lastmatch;
  1042.   do {
  1043.     if (strncmp (str, keycode [i].fk, strlen (str)) == 0) {
  1044.         lastmatch = i;
  1045.         return (strlen (str) == strlen (keycode [i].fk) ? i : -1);
  1046.     }
  1047.     ++ i;
  1048.     if (keycode [i].fk == NIL_PTR) i = 0;
  1049.   } while (i != lastmatch);
  1050.   return -2;
  1051. }
  1052.  
  1053. /*
  1054.  * Is a character available within a specified number of milliseconds ?
  1055.  */
  1056. int
  1057. char_ready_within (msec)
  1058.   int msec;
  1059. {
  1060.   return (q_len () > 0) || inputreadyafter (input_fd, msec);
  1061. }
  1062.  
  1063. #else /* def msdos: */
  1064.  
  1065. int
  1066. char_ready_within (msec)
  1067.   int msec;
  1068. {
  1069.   return inputreadyafter (input_fd, msec);
  1070. }
  1071.  
  1072. #endif /* def msdos */
  1073.  
  1074. void (* keyproc) () = I;
  1075. uchar (* accentproc) ();
  1076.  
  1077. /*
  1078.  * Read a character from terminal, considering function keys and 
  1079.  * composing special character of an 8 bit character set.
  1080.  * _readchar () takes the following actions:
  1081.  *  -    function key sequences according to the table 'keycode' are 
  1082.  *    transformed into a special controlling character which is 
  1083.  *    assigned the function FUNKEY. Also the intended editor function, 
  1084.  *    as taken from the table 'keycode', is saved in a variable for 
  1085.  *    use by FUNKEY.
  1086.  *  -    the prefix keys for diacritic and special characters are 
  1087.  *    combined with the following key to make up the character.
  1088.  */
  1089. int
  1090. _readchar ()
  1091. {
  1092.   register uchar ch;
  1093. #ifndef msdos
  1094.   int res;
  1095. #endif
  1096.  
  1097. #ifndef msdos
  1098.   if (q_len () > 0) return q_get ();
  1099. #endif
  1100.  
  1101.   ch = __readchar ();
  1102.  
  1103.   if (ch == '\000') {
  1104.     ch = __readchar ();
  1105.     keyproc = pc_key_map [ch];
  1106. #ifdef DEBUG
  1107.     if ((voidfunc) keyproc == (voidfunc) I) return ch;
  1108.     /* (voidfunc) is an identity cast here. It seems to be required 
  1109.        for the sake of the apparently totally rotten microvax compiler */
  1110. #endif
  1111.     accentproc = (charfunc) keyproc;
  1112.     if ((accentproc == grave) || (accentproc == circumflex)
  1113.          || (accentproc == acute) || (accentproc == diaeresis)
  1114.          || (accentproc == tilde) || (accentproc == angstrom))
  1115.         {ch = (* accentproc) (readchar ());
  1116.          keyproc = I;
  1117.          return ch;
  1118.         }
  1119.     else return FUNcmd /* index of FUNKEY */;
  1120.   }
  1121. #ifndef msdos
  1122.   else if (ch == '\033') {
  1123.     /* q_clear (); */
  1124.     q_put (ch);
  1125.     while  ((res = findkey (queue)) == -1 /* prefix of table entry */
  1126.         && q_notfull ()
  1127.         && inputreadyafter (input_fd, 300)
  1128.            )
  1129.         q_put (__readchar ());
  1130.     if (quit == TRUE) return '\0';
  1131.     else if (res < 0) /* key pattern not detected in keycode table */
  1132.          /* {if (q_len () > 1) return 0;
  1133.          else return ch;} */
  1134.          return q_get () /* just deliver the typed characters */;
  1135.     else {
  1136.          q_clear ();
  1137.          keyproc = keycode [res].fp;
  1138.          accentproc = (charfunc) keyproc;
  1139.          if ((accentproc == grave) || (accentproc == circumflex)
  1140.          || (accentproc == acute) || (accentproc == diaeresis)
  1141.          || (accentproc == tilde) || (accentproc == angstrom))
  1142.         {ch = (* accentproc) (readchar ());
  1143.          keyproc = I;
  1144.          return ch;
  1145.         }
  1146.          else return FUNcmd /* index of FUNKEY */;
  1147.     }
  1148.   }
  1149. #endif
  1150.   else
  1151.     return ch;
  1152. }
  1153.  
  1154. /*  ==================================================================    *
  1155.  *            Status Line Dialog                *
  1156.  *  ==================================================================    */
  1157.  
  1158. /*
  1159.  * Display a line telling how many chars and lines the file contains. Also tell
  1160.  * whether the file is readonly and/or modified.
  1161. fstatus (mess, cnt) is    file_status ((mess), (cnt), file_name, \
  1162.                      total_lines, TRUE, writable, modified, viewonly)
  1163.  */
  1164. /* directly called only from WB: file_status 
  1165.    (msg_done, chars_saved, file_name, lines_saved, FALSE, TRUE, FALSE, FALSE); */
  1166. void
  1167. file_status (message, count, file, lines, textstat, writefl, changed, viewing)
  1168.   char * message;
  1169.   register long count;        /* Contains number of characters in file */
  1170.   char * file;
  1171.   int lines;
  1172.   FLAG textstat, writefl, changed, viewing;
  1173. {
  1174.   register LINE * line;
  1175.   register int line_num = 0;
  1176.   int line_number = 1;
  1177.   static char msg [maxLINE_LEN + 40];    /* Buffer to hold line */
  1178.   char yank_msg [maxLINE_LEN];    /* Buffer for msg of yank_file */
  1179.  
  1180.   if (count < 0)        /* Not valid. Count chars in file */
  1181.     for (line = header->next; line != tail; line = line->next) {
  1182.         count += length_of (line->text);
  1183.         line_num ++;
  1184.         if (line == cur_line) line_number = line_num;
  1185.     }
  1186.  
  1187.   if (yank_status == VALID && textstat == TRUE)    /* Append buffer info */
  1188.     /* build_string (yank_msg, " Buffer: %ld char%s.", chars_saved,
  1189.                     (chars_saved == 1L) ? "" : "s");
  1190.     */
  1191.     build_string (yank_msg, "");
  1192.     /* Empty paste buffer is only an initial condition and thus this 
  1193.        would be no significant information; since buffer contents 
  1194.        may be appended, the exact count is not available anyway. */
  1195.   else
  1196.     yank_msg [0] = '\0';
  1197.  
  1198.   build_string (msg,
  1199.         (textstat == TRUE) 
  1200.             ? "%s %s%s%s%s, %d line%s, %ld char%s. Line %d.%s"
  1201.             : "%s %s%s%s%s, %d line%s, %ld char%s.",
  1202.         message,
  1203.         (rpipe == TRUE && * message != '[') ?
  1204.             "standard input" : file,
  1205.             /* previously only basename (file) was printed */
  1206.         (viewing == TRUE) ? " (View only)" : "",
  1207.         (changed == TRUE) ? " (modified)" : "",
  1208.         (writefl == FALSE) ? " (Readonly)" : "",
  1209.         lines, (lines == 1) ? "" : "s",
  1210.         count, (count == 1L) ? "" : "s",
  1211.         line_number,
  1212.         yank_msg);
  1213.  
  1214.   status_msg (msg);        /* Print the information */
  1215. }
  1216.  
  1217. /*
  1218.  * Input () reads a string from the terminal.
  1219.  * Return values:
  1220.  *    when QUIT character typed => ERRORS
  1221.  *    when empty input and clearfl == TRUE: NO_INPUT
  1222.  *    else: FINE
  1223.  */
  1224. int
  1225. input (inbuf, clearfl)
  1226.   uchar * inbuf;
  1227.   FLAG clearfl;
  1228. {
  1229.   register uchar * ptr;
  1230.   register uchar c;
  1231.  
  1232.   ptr = inbuf;
  1233.   * ptr = '\0';
  1234.   while (quit == FALSE) {
  1235.      flush ();
  1236.      if (lpos >= XBREAK) pagewrapped = TRUE;
  1237.      switch (c = readchar ()) {
  1238.     case '\b' :        /* Erase previous char */
  1239.     case '\177' /* DEL */ :
  1240.         if (ptr > inbuf) {
  1241.         ptr --;
  1242.         reverse_off ();
  1243.         if (Chinese == TRUE && inmultichar (inbuf, ptr)) {
  1244.             ptr --;
  1245.             putstring (" \b\b\b  \b\b");
  1246.             lpos = lpos - 2;
  1247.         } else if (iscontrol (* ptr)) {
  1248.             putstring (" \b\b\b  \b\b");
  1249.             lpos = lpos - 2;
  1250.         } else {
  1251.             putstring (" \b\b \b");
  1252.             lpos = lpos - 1;
  1253.         }
  1254.         reverse_on ();
  1255.         putstring (" \b");
  1256.         * ptr = '\0';
  1257.         } else
  1258.         ring_bell ();
  1259.         break;
  1260.     case QUITCHAR :
  1261.     case '\033' :
  1262.         quit = TRUE;
  1263.         break;
  1264.     case '\n' :        /* End of input */
  1265.     case '\015' :
  1266.         /* If inbuf is empty clear status_line */
  1267.         return (ptr == inbuf && clearfl == TRUE) ?
  1268.             NO_INPUT : FINE;
  1269.     default :
  1270.         if (c == control_prefix /* ^V/^P */) {
  1271.             c = readchar ();
  1272.             if (c == ring || c == ',')
  1273.                 {c = angstrom (readchar ());}
  1274.             else switch (c) {
  1275.             case '"':    {c = diaeresis (readchar ()); break;}
  1276.             case '\'':    {c = acute (readchar ()); break;}
  1277.             case '`':    {c = grave (readchar ()); break;}
  1278.             case '^':    {c = circumflex (readchar ()); break;}
  1279.             case '~':    {c = tilde (readchar ()); break;}
  1280.             default:
  1281.                 if (c == '?') c = '\177';
  1282.                 if (c != '\177') c = c & '\237';
  1283.             }
  1284.         }
  1285.         if (Chinese == TRUE && multichar (c)) {
  1286.            if ((ptr - inbuf) + 1 < maxLINE_LEN) {
  1287.             * ptr ++ = c;
  1288.             print_char (c);
  1289.             c = readchar ();
  1290.             * ptr ++ = c;
  1291.             print_char (c);
  1292.             * ptr = '\0';
  1293.             putstring (" \b");
  1294.            }
  1295.            else
  1296.             ring_bell ();
  1297.         } else if ((ptr - inbuf) < maxLINE_LEN) {
  1298.             if ((c > '\0')) {
  1299.                 * ptr ++ = c;
  1300.                 * ptr = '\0';
  1301.                 print_char (c);
  1302.                 putstring (" \b");
  1303.             }
  1304.             else
  1305.                 ring_bell ();
  1306.         }
  1307.         else
  1308.             ring_bell ();
  1309.      }
  1310.   }
  1311.   quit = FALSE;
  1312.   return ERRORS;
  1313. }
  1314.  
  1315. /*
  1316.  * Show concatenation of s1 and s2 on the status line (bottom of screen)
  1317.  * If revfl is TRUE, turn on reverse video on both strings. Set stat_visible
  1318.  * only if bottom_line is visible.
  1319.  * The return value is FINE except for get_string, where it is taken
  1320.  * from the call to input ().
  1321. status_line (str1, str2)    is (void) bottom_line (ON, (str1), (str2), NIL_PTR, FALSE)
  1322. status_msg (str)        is status_line (str, NIL_PTR)
  1323. status_beg (str)        is (void) bottom_line (ON, (str), NIL_PTR, NIL_PTR, TRUE)
  1324. error (str1, str2)        is (void) bottom_line (ON, (str1), (str2), NIL_PTR, FALSE)
  1325. clear_status ()            is (void) bottom_line (OFF, NIL_PTR, NIL_PTR, NIL_PTR, FALSE)
  1326. get_string (str1, str2, fl) is bottom_line (ON, (str1), NIL_PTR, (str2), fl)
  1327.  */
  1328. FLAG lastrevfl;
  1329. char * lastinbuf;
  1330. FLAG input_active = FALSE;
  1331. char status_buf [maxLINE_LEN];
  1332.  
  1333. void
  1334. rd_bottom_line ()
  1335. {
  1336.   set_cursor (0, YMAX);
  1337.   reverse_on ();
  1338.   if (lastinbuf == NIL_PTR)
  1339.     printlim_string (status_buf, XBREAK);
  1340.   else {
  1341.     print_string (status_buf);
  1342.     print_string (lastinbuf);
  1343.   }
  1344.   if (! input_active) {
  1345.     reverse_off ();
  1346.     set_cursor_xy ();    /* Set cursor back to old position */
  1347.   }
  1348.   flush ();    /* Perform the actual screen output */
  1349. }
  1350.  
  1351. int
  1352. bottom_line (revfl, s1, s2, inbuf, statfl)
  1353.   FLAG revfl;
  1354.   char * s1, * s2;
  1355.   char * inbuf;
  1356.   FLAG statfl;
  1357. {
  1358.   int ret = FINE;
  1359.  
  1360.   if (inbuf != NIL_PTR) * inbuf = '\0';
  1361.   lastrevfl = revfl;
  1362.   lastinbuf = inbuf;
  1363.  
  1364.   if (pagewrapped == TRUE) {
  1365.     status_buf [0] = '\0';
  1366.     RD ();
  1367.     pagewrapped = FALSE;
  1368.   }
  1369.  
  1370.   build_string (status_buf, " %s%s ", unnull (s1), unnull (s2));
  1371.         /* (s1 == NIL_PTR) ? "" : s1, (s2 == NIL_PTR) ? "" : s2); */
  1372.  
  1373.   if (revfl == ON && stat_visible == TRUE) {
  1374.     set_cursor (0, YMAX);
  1375.     clear_lastline ();
  1376.   }
  1377.   set_cursor (0, YMAX);
  1378.   if (revfl == ON) {        /* Print rev. start sequence */
  1379.     reverse_on ();
  1380.     stat_visible = TRUE;
  1381.   }
  1382.   else {            /* Used as clear_status () */
  1383.     reverse_off ();
  1384.     stat_visible = FALSE;
  1385.   }
  1386.  
  1387.   if (inbuf == NIL_PTR)
  1388.     printlim_string (status_buf, XBREAK);
  1389.   else {
  1390.     print_string (status_buf);
  1391.     input_active = TRUE;
  1392.     ret = input (inbuf, statfl);
  1393.     input_active = FALSE;
  1394.   }
  1395.  
  1396.   /* Print normal video */
  1397.   reverse_off ();
  1398.   if (can_clear_eol == TRUE) clear_eol ();
  1399.   else {
  1400.     put_blanks (XMAX - 1 - lpos);
  1401.     set_cursor (lpos, YMAX);
  1402.   }
  1403.  
  1404.   if (inbuf != NIL_PTR) {
  1405.     set_cursor (0, YMAX);
  1406.   }
  1407.   else if (statfl == TRUE)
  1408.     reverse_on ();
  1409.   else
  1410.     set_cursor_xy ();    /* Set cursor back to old position */
  1411.  
  1412.   flush ();    /* Perform the actual screen output */
  1413.   if (ret != FINE) clear_status ();
  1414.   return ret;
  1415. }
  1416.  
  1417. /*
  1418.  * Get_number () reads a number from the terminal.
  1419.  * The last character typed in is returned.
  1420.  * ERRORS is returned on a bad number or on interrupted input.
  1421.  * The resulting number is put into the integer the arguments points to.
  1422.  */
  1423. int
  1424. get_number (message, firstdigit, result)
  1425.   char * message;
  1426.   char firstdigit;
  1427.   int * result;
  1428. {
  1429.   register int index;
  1430.   register int count;
  1431.  
  1432.   status_beg (message);
  1433.  
  1434.   if (firstdigit > '\0')
  1435.     index = firstdigit;
  1436.   else
  1437.     index = readchar ();
  1438.  
  1439.   if (index == QUITCHAR) quit = TRUE;
  1440.   if (index == '\033') quit = TRUE;
  1441.   if (quit == FALSE && (index < '0' || index > '9')) {
  1442.     error ("Bad number", NIL_PTR);
  1443.     return ERRORS;
  1444.   }
  1445.  
  1446. /* Convert input to a decimal number */
  1447.   count = 0;
  1448.   while (index >= '0' && index <= '9' && quit == FALSE) {
  1449.     print_char (index); flush ();
  1450.     if (lpos >= XBREAK) pagewrapped = TRUE;
  1451.     count *= 10;
  1452.     count += index - '0';
  1453.     index = readchar ();
  1454.     if (index == QUITCHAR) quit = TRUE;
  1455.     if (index == '\033') quit = TRUE;
  1456.   }
  1457.  
  1458.   clear_status ();
  1459.   if (quit == TRUE) {
  1460.     clear_status ();
  1461.     return ERRORS;
  1462.   }
  1463.   * result = count;
  1464.   return index;
  1465. }
  1466.  
  1467. /*
  1468.  * get_digits () reads in a number. In contrast to get_number, it does no
  1469.  * echoing, no messaging, and it does not require any digits at all.
  1470.  * The last character typed in is returned.
  1471.  * The resulting number is put into the integer the arguments points to.
  1472.  */
  1473. int
  1474. get_digits (result)
  1475.   int * result;
  1476. {
  1477.   register int index;
  1478.   register int count;
  1479.  
  1480.   index = readchar ();
  1481.   if (index == QUITCHAR) quit = TRUE;
  1482.   * result = -1;
  1483. /* Convert input to a decimal number */
  1484.   count = 0;
  1485.   while (index >= '0' && index <= '9' && quit == FALSE) {
  1486.     count *= 10;
  1487.     count += index - '0';
  1488.     * result = count;
  1489.     index = readchar ();
  1490.     if (index == QUITCHAR) quit = TRUE;
  1491.   }
  1492.  
  1493.   if (quit == TRUE) {
  1494.     return QUITCHAR;
  1495.   }
  1496.   return index;
  1497. }
  1498.  
  1499. /*
  1500.  * Get_file () reads a filename from the terminal.
  1501.  */
  1502. int
  1503. get_file (message, file)
  1504.   char * message, * file;
  1505. {
  1506.   int ret = get_string (message, file, TRUE);
  1507. #ifndef msdos
  1508.   char * filei;
  1509.   char file1 [maxLINE_LEN];
  1510.  
  1511.   if (file [0] == '~' && file [1] == '/') {
  1512.     filei = file; filei ++;
  1513.     build_string (file1, "%s%s", unnull (getenv ("HOME")), filei);
  1514.     build_string (file, file1);
  1515.   }
  1516. #endif
  1517.   return ret;
  1518. }
  1519.  
  1520. /*  ==================================================================    *
  1521.  *            text modification routines            *
  1522.  *  ==================================================================    */
  1523.  
  1524. extern void viewonlyerr ();
  1525.  
  1526. /*
  1527.  * make_line installs the buffer into a LINE structure.
  1528.  * It returns a pointer to the allocated structure.
  1529.  */
  1530. LINE *
  1531. make_line (buffer, length)
  1532.   char * buffer;
  1533.   int length;
  1534. {
  1535.   register LINE * new_line = alloc_header ();
  1536.  
  1537.   if (new_line == NIL_LINE) {
  1538.     ring_bell ();
  1539.     error ("Cannot allocate more memory for new line header", NIL_PTR);
  1540.     return NIL_LINE;
  1541.   } else {
  1542.     new_line->text = alloc (length + 1);
  1543.     if (new_line->text == NIL_PTR) {
  1544.     ring_bell ();
  1545.     error ("Cannot allocate more memory for new line", NIL_PTR);
  1546.     return NIL_LINE;
  1547.     } else {
  1548.     new_line->shift_count = 0;
  1549.     copy_string (new_line->text, buffer);
  1550.     return new_line;
  1551.     }
  1552.   }
  1553. }
  1554.  
  1555. /*
  1556.  * Line_insert () inserts a new line with text pointed to by `string'.
  1557.  * It returns the address of the new line.
  1558.  */
  1559. LINE *
  1560. line_insert (line, string, len)
  1561.   register LINE * line;
  1562.   char * string;
  1563.   int len;
  1564. {
  1565.   register LINE * new_line;
  1566.  
  1567. /* Allocate space for LINE structure and text */
  1568.   new_line = make_line (string, len);
  1569.  
  1570.   if (new_line != NIL_LINE) {
  1571. /* Install the line into the double linked list */
  1572.     new_line->prev = line;
  1573.     new_line->next = line->next;
  1574.     line->next = new_line;
  1575.     new_line->next->prev = new_line;
  1576. /* Increment total_lines */
  1577.     total_lines ++;
  1578.   }
  1579.   return new_line;
  1580. }
  1581.  
  1582. /*
  1583.  * Insert () insert the string `string' at the given line and location.
  1584.  */
  1585. int
  1586. insert (line, location, string)
  1587.   register LINE * line;
  1588.   char * location, * string;
  1589. {
  1590.   register char * bufp = text_buffer;    /* Buffer for building line */
  1591.   register char * textp = line->text;
  1592.   char * newtext;
  1593.  
  1594.   if (viewonly == TRUE)
  1595.     {viewonlyerr (); return ERRORS;}
  1596.  
  1597.   if (length_of (textp) + text_length_of (string) >= MAX_CHARS) {
  1598.     error ("Line too long", NIL_PTR);
  1599.     return ERRORS;
  1600.   }
  1601.  
  1602. /* Copy part of line until `location' has been reached */
  1603.   while (textp != location)
  1604.     * bufp ++ = * textp ++;
  1605.  
  1606. /* Insert string at this location */
  1607.   while (* string != '\0')
  1608.     * bufp ++ = * string ++;
  1609.   * bufp = '\0';
  1610.  
  1611. /* First, allocate memory for next line contents to make sure the */
  1612. /* operation succeeds or fails as a whole */
  1613.   newtext = alloc (length_of (text_buffer) + length_of (location) + 1);
  1614.   if (newtext == NIL_PTR) {
  1615.     ring_bell ();
  1616.     error ("Cannot allocate memory for insertion", NIL_PTR);
  1617.     return ERRORS;
  1618.   }
  1619.   else { /* Install the new text in this line */
  1620.     if (* (string - 1) == '\n') {        /* Insert a new line */
  1621.         if (line_insert (line, location, length_of (location)) == NIL_LINE)
  1622.             return ERRORS;
  1623.         modified = TRUE;
  1624.     }
  1625.     else        /* Append last part of line to text_buffer */
  1626.         copy_string (bufp, location);
  1627.  
  1628.     free_space (line->text);
  1629.     modified = TRUE;
  1630.     line->text = newtext;
  1631.     copy_string (line->text, text_buffer);
  1632.     return FINE;
  1633.   }
  1634. }
  1635.  
  1636. /*
  1637.  * Line_delete () deletes the argument line out of the line list. The pointer
  1638.  * to the next line is returned.
  1639.  */
  1640. LINE *
  1641. line_delete (line)
  1642.   register LINE * line;
  1643. {
  1644.   register LINE * next_line = line->next;
  1645.  
  1646. /* Delete the line */
  1647.   line->prev->next = line->next;
  1648.   line->next->prev = line->prev;
  1649.  
  1650. /* Free allocated space */
  1651.   free_space (line->text);
  1652.   free_header (line);
  1653.  
  1654. /* Decrement total_lines */
  1655.   total_lines --;
  1656.  
  1657.   return next_line;
  1658. }
  1659.  
  1660. /*
  1661.  * Delete_text () deletes all the characters (including newlines) between the 
  1662.  * startposition and endposition and fixes the screen accordingly. It 
  1663.  * displays the number of lines deleted.
  1664.  */
  1665. FLAG
  1666. delete_text (start_line, start_textp, end_line, end_textp)
  1667.   register LINE * start_line;
  1668.   LINE * end_line;
  1669.   char * start_textp, * end_textp;
  1670. {
  1671.   register char * textp = start_line->text;
  1672.   register char * bufp = text_buffer;    /* Storage for new line->text */
  1673.   LINE * line;
  1674.   LINE * after_end = end_line->next;
  1675.   int line_cnt = 0;            /* Nr of lines deleted */
  1676.   int count = 0;
  1677.   int shift = 0;            /* Used in shift calculation */
  1678.   int nx = x;
  1679.   FLAG ret = FINE;
  1680.   char * newtext;
  1681.  
  1682.   if (viewonly == TRUE)
  1683.     {viewonlyerr (); return ret;}
  1684.  
  1685.   modified = TRUE;            /* File has been modified */
  1686.  
  1687. /* Set up new line. Copy first part of start line until start_position. */
  1688.   while (textp < start_textp) {
  1689.     * bufp ++ = * textp ++;
  1690.     count ++;
  1691.   }
  1692.  
  1693. /* Check if line doesn't exceed MAX_CHARS */
  1694.   if (count + length_of (end_textp) >= MAX_CHARS) {
  1695.     error ("Line too long", NIL_PTR);
  1696.     return ret;
  1697.   }
  1698.  
  1699. /* Copy last part of end_line if end_line is not tail */
  1700.   copy_string (bufp, (end_textp != NIL_PTR) ? end_textp : "\n");
  1701.  
  1702. /* Delete all lines between start and end_position (including end_line) */
  1703.   line = start_line->next;
  1704.   while (line != after_end && line != tail) {
  1705.     /* Here, the original mined compared with end_line->next which has 
  1706.        already been discarded when the comparison should become true.
  1707.        This severe error remained undetected until I ported to MSDOS */
  1708.     line = line_delete (line);
  1709.     line_cnt ++;
  1710.   }
  1711.  
  1712. /* Check if last line of file should be deleted */
  1713.   if (end_textp == NIL_PTR && length_of (start_line->text) == 1 && total_lines > 1) {
  1714.     start_line = start_line->prev;
  1715.     (void) line_delete (start_line->next);
  1716.     line_cnt ++;
  1717.   }
  1718.   else {    /* Install new text */
  1719.     newtext = alloc (length_of (text_buffer) + 1);
  1720.     if (newtext == NIL_PTR) {
  1721.         ring_bell ();
  1722.         error ("No more memory after deletion", NIL_PTR);
  1723.         ret = ERRORS;
  1724.     } else {
  1725.         free_space (start_line->text);
  1726.         start_line->text = newtext;
  1727.         copy_string (start_line->text, text_buffer);
  1728.     }
  1729.   }
  1730.  
  1731. /* Fix screen. First check if line is shifted. Perhaps we should shift it back */
  1732. #ifdef UNUSED
  1733. /* !!! This resulted in a positioning error when a line containing TABs */
  1734. /* !!! was shifted back. So better leave it.                */
  1735.   if (get_shift (start_line->shift_count)) {
  1736.     shift = (XBREAK - count_chars (start_line)) / SHIFT_SIZE;
  1737.     if (shift > 0) {        /* Shift line `shift' back */
  1738.         if (shift >= get_shift (start_line->shift_count))
  1739.             start_line->shift_count = 0;
  1740.         else
  1741.             start_line->shift_count -= shift;
  1742.         nx += shift * SHIFT_SIZE; /* Reset x value */
  1743.     }
  1744.   }
  1745. #endif
  1746.  
  1747.   if (line_cnt == 0) {        /* Check if only one line changed */
  1748.     if (shift > 0) {    /* Reprint whole line */
  1749.         set_cursor (0, y);
  1750.         line_print (start_line);
  1751.     }
  1752.     else {            /* Just display last part of line */
  1753.         set_cursor_xy ();
  1754.         put_line (start_line, x, TRUE, FALSE);
  1755.     }
  1756.     move_to (nx, y);       /* Reset cur_text */
  1757.     return ret;
  1758.   }
  1759.  
  1760.   shift = last_y;       /* Save value */
  1761.   reset (top_line, y);
  1762.   if ((line_cnt <= SCREENMAX - y) && can_delete_line == TRUE) {
  1763.     clear_status ();
  1764.     display (y, start_line, 0, y);
  1765.     line = proceed (start_line, SCREENMAX - y - line_cnt + 1);
  1766.     while (line_cnt -- > 0) {
  1767.         delete_line (y + 1);
  1768.         if (line != tail) {
  1769.             set_cursor (0, SCREENMAX);
  1770.             line_print (line);
  1771.             line = line->next;
  1772.         }
  1773.     }
  1774.   }
  1775.   else
  1776.     display (y, start_line, shift - y, y);
  1777. /*  move_to ((line_cnt == 1) ? nx : 0, y); */
  1778.   move_to (nx, y);
  1779.   return ret;
  1780. }
  1781.  
  1782. /*  ==================================================================    *
  1783.  *                End                    *
  1784.  *  ==================================================================    */
  1785.