home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume26 / input-edit / patch01 next >
Text File  |  1991-11-30  |  25KB  |  826 lines

  1. Newsgroups: comp.sources.misc
  2. From: thewalt@canuck.CE.Berkeley.EDU (Chris Thewalt)
  3. Subject:  v26i092:  input-edit - C input functions for line editing with history, Patch01
  4. Message-ID: <1991Nov30.210948.4573@sparky.imd.sterling.com>
  5. X-Md4-Signature: b8bf2ca4014f08c3651aba5c21908e83
  6. Date: Sat, 30 Nov 1991 21:09:48 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: thewalt@canuck.CE.Berkeley.EDU (Chris Thewalt)
  10. Posting-number: Volume 26, Issue 92
  11. Archive-name: input-edit/patch01
  12. Environment: UNIX, MS-DOS
  13. Patch-To: input-edit: Volume 25, Issue 56
  14.  
  15. The patch to getline.c was larger than a repost of the file, so this
  16. version replaces the original getline.c posted in Volume 25, Issue 56.
  17.  
  18. The changes since the first version are:
  19.  
  20.     1) removed stdio calls to decrease executable size
  21.        (thanks to Scott Schwartz, schwartz@groucho.cs.psu.edu)
  22.     2) added VMS support
  23.        (thanks to Christoph Keller, keller@bernina.ethz.ch)
  24.     3) added some new key bindings and functionality:
  25.          ^T:         transpose current and previous character
  26.      arrow keys: motion for ANSI and MSDOS arrow keys
  27.      ^G:         toggles replace (overwrite) mode. Initially
  28.                  in insert mode.
  29.  
  30. What is input-edit?             
  31.  
  32. environment:  ANSI C, UNIX (sysv or bsd), MSDOS with MSC, VMS
  33.               (Converting to K&R C should be fairly simple)
  34.          
  35. tested on:    DECstation 5000, Ultrix 4.2 with gcc
  36.           Sun Sparc II, SunOS 4.1.1, gcc
  37.           SGI Iris, IRIX System V.3, cc
  38.           PC, DR DOS 5.0, MSC 6.0
  39.  
  40. description: Getline can be used in programs that want to provide
  41.              an emacs style line editing cabability with history.
  42.          Getline allows the user to edit the current line
  43.              and move through the history list of lines previously
  44.          typed and returns the buffer to the caller when RETURN
  45.          is entered.  Long lines are handled by horizontal
  46.          scrolling. Does NOT use termcap, uses only \b to move around. 
  47.  
  48.              The actual editing capabilites are a very small subset
  49.          of emacs commands, but then the package is very small
  50.          and  quite portable.  Get GNU readline if you need more
  51.          powerful editing.
  52.  
  53. #!/bin/sh
  54. # This is a shell archive (produced by shar 3.49)
  55. # To extract the files from this archive, save it to a file, remove
  56. # everything above the "!/bin/sh" line above, and type "sh file_name".
  57. #
  58. # made 11/25/1991 16:03 UTC by thewalt@canuck.CE.Berkeley.EDU
  59. # Source directory /usr/users/thewalt/et
  60. #
  61. # existing files WILL be overwritten
  62. #
  63. # This shar contains:
  64. # length  mode       name
  65. # ------ ---------- ------------------------------------------
  66. #  20798 -rw-r--r-- getline.c
  67. #
  68. # ============= getline.c ==============
  69. echo 'x - extracting getline.c (Text)'
  70. sed 's/^X//' << 'SHAR_EOF' > 'getline.c' &&
  71. #ifndef lint
  72. static char     rcsid[] =
  73. "$Id: getline.c,v 2.1 1991/11/25 15:58:19 thewalt Exp thewalt $";
  74. #endif
  75. X
  76. /* 
  77. X * Fairly portable (ANSI C),  emacs style line editing input package.  
  78. X * This package uses \b to move, and \007 to ring the bell.  
  79. X * It uses a fixed screen width, as initialized in the gl_init() call,
  80. X * and does not draw in the last location to avoid line wraps.
  81. X * The only non-portable part is how to turn off character echoing.
  82. X * This code works for *NIX of BSD or SYSV flavor, as well as MSDOS (MSC6.0).
  83. X * No TERMCAP features are used, so long lines are scrolled on one line 
  84. X * rather than extending over several lines.  The function getline 
  85. X * returns a pointer to a static buffer area which holds the input string, 
  86. X * including the newline. On EOF the first character is set equal to '\0'.  
  87. X * The caller supplies a desired prompt, as shown in the prototype:
  88. X *
  89. X *      char *getline(char *prompt)
  90. X *
  91. X * Getline is different from GNU readline in that:
  92. X *    - getline has much more limited editing capabilities, but it
  93. X *      is also much smaller and doesn't need termcap.
  94. X *    - you don't free the buffer when done, since it is static 
  95. X *      (make a copy yourself if you want one)
  96. X *    - the newline is appended to the buffer
  97. X *    - you don't add lines to history, it is done automatically.
  98. X *
  99. X * The function gl_init(int screen_width) should be called before 
  100. X * calling getline(char *prompt), and gl_cleanup(void) should be 
  101. X * called before before exiting.  The function gl_redraw(void) may also 
  102. X * be called to redraw the screen (as is done when ^L is read).
  103. X * The function gl_replace() is also user callable and toggles getline
  104. X * between insert and replace (overwrite) mode. Getline begins in insert
  105. X * mode.
  106. X *
  107. X * The editing keys are:
  108. X *  ^A, ^E   - move to beginnning or end of line, respectively.
  109. X *  ^F, ^B   - nondestructive move forward or back one location, respectively.
  110. X *  ^D       - delete the character currently under the cursor, or
  111. X *             send EOF if no characters in the buffer.
  112. X *  ^G       - toggle replace (overwrite) mode, initally in insert mode.
  113. X *  ^H, DEL  - delete character left of the cursor.
  114. X *  ^K       - delete from cursor to end of line.
  115. X *  ^L       - redraw the current line.
  116. X *  ^P, ^N   - move through history, previous and next, respectively.
  117. X *  ^T       - transpose chars under and to left of cursor
  118. X *  TAB      - call user defined function if bound, or insert spaces
  119. X *             to get to next TAB stop (just past every 8th column).
  120. X *  NL, CR   - places line on history list if nonblank, calls output
  121. X *             cleanup function if bound, appends newline and returns
  122. X *             to the caller.
  123. X *  arrow keys - appropriate motion
  124. X *
  125. X * In addition, the caller can modify the buffer in certain ways, which
  126. X * may be useful for things like auto-indent modes.  There are three
  127. X * function pointers which can be bound to user functions.
  128. X * Each of these functions must return the index of the first location 
  129. X * at which the buffer was modified, or -1 if the buffer wasn't modified.  
  130. X * Each of the functions receive the current input buffer as the first 
  131. X * argument.  The screen is automatically cleaned up if the buffer is changed.
  132. X * The user is responsible for not writing beyond the end of the static 
  133. X * buffer.  The function pointer prototypes are:
  134. X * 
  135. X *   int (*gl_in_hook)(char *buf)
  136. X *       - called on entry to getline, and each time a new history
  137. X *         string is loaded, from a ^P or ^N. Initally NULL.
  138. X *   int (*gl_out_hook)(char *buf)
  139. X *       - called when a \n or \r is read, before appending \n and
  140. X *         returning to caller. Initally NULL.
  141. X *   int (*gl_tab_hook)(char *buf, int offset, int *loc)
  142. X *       - called whenever a TAB is read.  The second argument is
  143. X *         the current line offset due to the width of the prompt. 
  144. X *         The third argument is a pointer to the variable holding the 
  145. X *         current location in the buffer.  The location may be reset 
  146. X *         by the user to move the cursor when the call returns.
  147. X *         Initially a built in tabbing function is bound.
  148. X *
  149. X * Please send bug reports, fixes and enhancements to Chris Thewalt,
  150. X * thewalt@ce.berkeley.edu
  151. X */
  152. X
  153. static char *copyright = "Copyright (C) 1991, Chris Thewalt";
  154. /*
  155. X * Copyright (C) 1991 by Chris Thewalt
  156. X *
  157. X * Permission to use, copy, modify, and distribute this software 
  158. X * for any purpose and without fee is hereby granted, provided
  159. X * that the above copyright notices appear in all copies and that both the
  160. X * copyright notice and this permission notice appear in supporting
  161. X * documentation.  This software is provided "as is" without express or
  162. X * implied warranty.
  163. X */
  164. X
  165. #include <stdlib.h>
  166. #include <string.h>
  167. #include <ctype.h>
  168. #include <errno.h>
  169. X
  170. extern int      isatty();    
  171. X
  172. #define BUF_SIZE 1024
  173. #define SCROLL   30
  174. X
  175. static int      gl_init_done = 0;    /* -1 is terminal, 1 is batch  */
  176. static int      gl_screen = 80;        /* screen width */
  177. static int      gl_width = 0;        /* net size available for input */
  178. static int      gl_extent = 0;        /* how far to redraw, 0 means all */
  179. static int      gl_overwrite = 0;    /* overwrite mode */
  180. static int      gl_pos, gl_cnt = 0;     /* position and size of input */
  181. static char     gl_buf[BUF_SIZE];       /* input buffer */
  182. static char    *gl_prompt;        /* to save the prompt string */
  183. X
  184. void            gl_init(int);        
  185. void            gl_cleanup(void);    /* to undo gl_init */
  186. void            gl_redraw(void);    /* issue \n and redraw all */
  187. void            gl_replace(void);    /* toggle replace/insert mode */
  188. static void     gl_char_init(void);    /* get ready for no echo input */
  189. static void     gl_char_cleanup(void);    /* undo gl_char_init */
  190. static int      gl_getc(void);          /* read one char from terminal */
  191. static void     gl_putc(int);           /* write one char to terminal */
  192. static void     gl_gets(char *, int);   /* get a line from terminal */
  193. static void     gl_puts(char *);        /* write a line to terminal */
  194. static void     gl_addchar(int);    /* install specified char */
  195. static void     gl_transpose(void);    /* transpose two chars */
  196. static void     gl_newline(void);    /* handle \n or \r */
  197. static void     gl_fixup(int, int);    /* fixup state variables and screen */
  198. static void     gl_del(int);        /* del, either left (-1) or cur (0) */
  199. static void     gl_kill(void);        /* delete to EOL */
  200. static int      gl_tab(char *, int, int *);    /* default TAB handler */
  201. X
  202. static void     hist_add(void);        /* adds nonblank entries to hist */
  203. static void     hist_init(void);    /* initializes hist pointers */
  204. static void     hist_next(void);    /* copies next entry to input buf */
  205. static void     hist_prev(void);    /* copies prev entry to input buf */
  206. static char    *hist_save(char *);    /* makes copy of a string */
  207. X
  208. int         (*gl_in_hook)(char *) = 0;
  209. int         (*gl_out_hook)(char *) = 0;
  210. int         (*gl_tab_hook)(char *, int, int *) = gl_tab;
  211. X
  212. /************************ nonportable part *********************************/
  213. extern int      write();
  214. extern void     _exit();
  215. X
  216. #ifdef MSDOS
  217. #include <bios.h>
  218. #endif
  219. X
  220. #ifdef unix
  221. extern int      read();
  222. #include <sys/ioctl.h>
  223. #ifndef TIOCGETP
  224. #include <termio.h>
  225. struct termio   tty, old_tty;
  226. #else
  227. #include <sgtty.h>
  228. struct sgttyb   tty, old_tty;
  229. #endif /* TIOCGETP */
  230. extern int      ioctl();
  231. #endif    /* unix */
  232. X
  233. #ifdef vms
  234. #include <descrip.h>
  235. #include <ttdef.h>
  236. #include <iodef.h>
  237. #include unixio
  238. X   
  239. static int setbuff[2];               /* buffer to set terminal attributes */
  240. static short chan = -1;              /* channel to terminal */
  241. struct dsc$descriptor_s descrip;     /* VMS descriptor */
  242. #endif
  243. X
  244. static void
  245. gl_char_init()
  246. /* turn off input echo */
  247. {
  248. #ifdef unix
  249. #ifdef TIOCGETP
  250. X    ioctl(0, TIOCGETP, &old_tty);
  251. X    tty = old_tty;
  252. X    tty.sg_flags |= CBREAK;
  253. X    tty.sg_flags &= ~ECHO;
  254. X    ioctl(0, TIOCSETP, &tty);
  255. #else
  256. X    ioctl(0, TCGETA, &old_tty);
  257. X    tty = old_tty;
  258. X    tty.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  259. X    tty.c_cc[VMIN] = 1;
  260. X    tty.c_cc[VTIME] = 0;
  261. X    ioctl(0, TCSETA, &tty);
  262. #endif 
  263. #endif /* unix */
  264. #ifdef vms
  265. X    descrip.dsc$w_length  = strlen("tt:");
  266. X    descrip.dsc$b_dtype   = DSC$K_DTYPE_T;
  267. X    descrip.dsc$b_class   = DSC$K_CLASS_S;
  268. X    descrip.dsc$a_pointer = "tt:";
  269. X    (void)sys$assign(&descrip,&chan,0,0);
  270. X    (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0);
  271. X    setbuff[1] |= TT$M_NOECHO;
  272. X    (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  273. #endif /* vms */
  274. }
  275. X
  276. static void
  277. gl_char_cleanup()
  278. /* undo effects of gl_char_init, as necessary */
  279. {
  280. #ifdef unix
  281. #ifdef TIOCSETP
  282. X    ioctl(0, TIOCSETP, &old_tty);
  283. #else
  284. X    ioctl(0, TCSETA, &old_tty);
  285. #endif
  286. #endif /* unix */
  287. X
  288. #ifdef vms
  289. X    setbuff[1] &= ~TT$M_NOECHO;
  290. X    (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
  291. X    sys$dassgn(chan);
  292. X    chan = -1;
  293. #endif 
  294. }
  295. X
  296. static int
  297. gl_getc()
  298. /* get a character without echoing it to screen */
  299. {
  300. X    int             c;
  301. X    char            ch;
  302. X
  303. #ifdef unix
  304. X    c = (read(0, &ch, 1) > 0)? ch : -1;
  305. #endif
  306. #ifdef MSDOS
  307. X    c = _bios_keybrd(_NKEYBRD_READ);
  308. X    if ((c & 0377) == 224) {
  309. X    switch (c = (c >> 8) & 0377) {
  310. X      case 72: c = 16;   /* up -> ^P */
  311. X        break;
  312. X          case 80: c = 14;   /* down -> ^N */
  313. X        break;
  314. X      case 75: c = 2;    /* left -> ^B */
  315. X        break;
  316. X          case 77: c = 6;    /* right -> ^F */
  317. X        break;
  318. X      default: c = 254;  /* make it garbage */
  319. X    }
  320. X    } else {
  321. X    c = c & 0377;
  322. X    }
  323. #endif
  324. #ifdef vms
  325. X    if(chan < 0) {
  326. X       c='\0';
  327. X    }
  328. X    (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0);
  329. X    c &= 0177;                        /* get a char */
  330. #endif
  331. X    return c;
  332. }
  333. X
  334. static void
  335. gl_putc(int c)
  336. {
  337. X    char   ch = c;
  338. X
  339. X    write(1, &ch, 1);
  340. }
  341. X
  342. /******************** fairly portable part *********************************/
  343. X
  344. static void
  345. gl_gets(char *buf, int size)    /* slow, only used in batch mode */
  346. {
  347. X    char *p = buf;
  348. X    int   c;
  349. X    
  350. X    size -= 1;
  351. X    while (buf + size > p && (c = gl_getc()) > 0) {
  352. X    *p++ = c;
  353. X    if (c == '\n') 
  354. X        break;
  355. X    }
  356. X    *p = 0;
  357. }
  358. X
  359. static void
  360. gl_puts(char *buf)
  361. {
  362. X    while (*buf) 
  363. X    gl_putc(*buf++);
  364. }
  365. X
  366. void
  367. gl_init(int scrn_wdth)
  368. /* set up variables and terminal */
  369. {
  370. X    gl_screen = scrn_wdth;
  371. X    if (gl_init_done == 0) {
  372. X        hist_init();
  373. X        if (isatty(0)) { 
  374. X        gl_char_init();
  375. X            gl_init_done = -1;        /* -1 means terminal */
  376. X        } else { 
  377. X            gl_init_done = 1;        /* 1 means batch */
  378. X    }
  379. X    }
  380. X    gl_pos = gl_cnt = 0;
  381. }
  382. X
  383. void
  384. gl_cleanup()
  385. /* undo effects of gl_init, as necessary */
  386. {
  387. X    if (gl_init_done == -1)
  388. X        gl_char_cleanup();
  389. X    gl_init_done = 0;
  390. }
  391. X
  392. char *
  393. getline(char *prompt)
  394. {
  395. X    int             c, loc, tmp;
  396. X
  397. X    if (!gl_init_done)
  398. X    gl_init(80);
  399. X    gl_buf[0] = 0;        /* used as end of input indicator */
  400. X    if (gl_init_done == 1) {    /* no input editing, and no prompt output */
  401. X    gl_gets(gl_buf, BUF_SIZE);
  402. X    return gl_buf;
  403. X    }
  404. X    gl_fixup(-1, 0);            /* this resets gl_fixup */
  405. X    gl_width = gl_screen - strlen(prompt);
  406. X    if (prompt == 0)    
  407. X    prompt = "";
  408. X    gl_prompt = prompt;
  409. X    gl_pos = gl_cnt = 0;
  410. X    gl_puts(prompt);
  411. X    if (gl_in_hook) {
  412. X    loc = gl_in_hook(gl_buf);
  413. X    if (loc >= 0)
  414. X        gl_fixup(0, BUF_SIZE);
  415. X    }
  416. X    while ((c = gl_getc()) >= 0) {
  417. X    gl_extent = 0;
  418. X    if (isprint(c)) {
  419. X        gl_addchar(c);
  420. X    } else {
  421. X        switch (c) {
  422. X          case '\n': case '\r':             /* newline */
  423. X        gl_newline();
  424. X        return gl_buf;
  425. X        break;
  426. X          case '\001': gl_fixup(-1, 0);        /* ^A */
  427. X        break;
  428. X          case '\002': gl_fixup(-1, gl_pos-1);    /* ^B */
  429. X        break;
  430. X          case '\004':                /* ^D */
  431. X        if (gl_cnt == 0) {
  432. X            gl_buf[0] = 0;
  433. X            gl_cleanup();
  434. X            gl_putc('\n');
  435. X            return gl_buf;
  436. X        } else {
  437. X            gl_del(0);
  438. X        }
  439. X        break;
  440. X          case '\005': gl_fixup(-1, gl_cnt);    /* ^E */
  441. X        break;
  442. X          case '\006': gl_fixup(-1, gl_pos+1);    /* ^F */
  443. X        break;
  444. X          case '\007': gl_replace();                /* ^G */
  445. X        break;
  446. X          case '\010': case '\177': gl_del(-1);    /* ^H and DEL */
  447. X        break;
  448. X          case '\t':                    /* TAB */
  449. X                if (gl_tab_hook) {
  450. X            tmp = gl_pos;
  451. X                loc = gl_tab_hook(gl_buf, strlen(gl_prompt), &tmp);
  452. X                if (loc >= 0 || tmp != gl_pos)
  453. X                    gl_fixup(loc, tmp);
  454. X                }
  455. X        break;
  456. X          case '\013': gl_kill();            /* ^K */
  457. X        break;
  458. X          case '\014': gl_redraw();            /* ^L */
  459. X        break;
  460. X          case '\016': hist_next();            /* ^N */
  461. X        break;
  462. X          case '\020': hist_prev();            /* ^P */
  463. X        break;
  464. X          case '\024': gl_transpose();        /* ^T */
  465. X        break;
  466. X          case '\033':                /* ansi arrow keys */
  467. X        c = gl_getc();
  468. X        if (c == '[') {
  469. X            switch(c = gl_getc()) {
  470. X              case 'A': hist_prev();        /* up */
  471. X                break;
  472. X              case 'B': hist_next();            /* down */
  473. X                break;
  474. X              case 'C': gl_fixup(-1, gl_pos+1); /* right */
  475. X                break;
  476. X              case 'D': gl_fixup(-1, gl_pos-1); /* left */
  477. X                break;
  478. X              default: gl_putc('\007');         /* who knows */
  479. X                break;
  480. X            }
  481. X        } else
  482. X            gl_putc('\007');
  483. X        break;
  484. X          default:
  485. X        gl_putc('\007');
  486. X        break;
  487. X        }
  488. X    }
  489. X    }
  490. X    gl_cleanup();
  491. X    return gl_buf;
  492. }
  493. X
  494. static void
  495. gl_addchar(int c)
  496. /* adds the character c to the input buffer at current location */
  497. {
  498. X    int  i;
  499. X
  500. X    if (gl_cnt >= BUF_SIZE - 1) {
  501. X    gl_puts("getline: input buffer overflow\n");
  502. X    _exit(1);
  503. X    }
  504. X    if (gl_overwrite == 0 || gl_pos == gl_cnt) {
  505. X        for (i=gl_cnt; i >= gl_pos; i--)
  506. X            gl_buf[i+1] = gl_buf[i];
  507. X        gl_buf[gl_pos] = c;
  508. X        gl_fixup(gl_pos, gl_pos+1);
  509. X    } else {
  510. X    gl_buf[gl_pos] = c;
  511. X    gl_extent = 1;
  512. X        gl_fixup(gl_pos, gl_pos+1);
  513. X    }
  514. }
  515. X
  516. static void
  517. gl_transpose(void)
  518. /* switch character under cursor and to left of cursor */
  519. {
  520. X    int    c;
  521. X
  522. X    if (gl_pos > 0 && gl_cnt > gl_pos) {
  523. X    c = gl_buf[gl_pos-1];
  524. X    gl_buf[gl_pos-1] = gl_buf[gl_pos];
  525. X    gl_buf[gl_pos] = c;
  526. X    gl_extent = 2;
  527. X    gl_fixup(gl_pos-1, gl_pos);
  528. X    } else
  529. X    gl_putc('\007');
  530. }
  531. X
  532. void
  533. gl_replace()
  534. X    gl_overwrite = !gl_overwrite;
  535. }
  536. X
  537. static void
  538. gl_newline()
  539. /*
  540. X * Cleans up entire line before returning to caller. A \n is appended.
  541. X * If line longer than screen, we redraw starting at beginning
  542. X */
  543. {
  544. X    int change = gl_cnt;
  545. X    int len = gl_cnt;
  546. X    int loc = gl_width - 5;    /* shifts line back to start position */
  547. X
  548. X    if (gl_cnt >= BUF_SIZE - 1) {
  549. X    gl_puts("getline: input buffer overflow\n");
  550. X    _exit(1);
  551. X    }
  552. X    hist_add();            /* only adds if nonblank */
  553. X    if (gl_out_hook) {
  554. X    change = gl_out_hook(gl_buf);
  555. X        len = strlen(gl_buf);
  556. X    } 
  557. X    if (loc > len)
  558. X    loc = len;
  559. X    gl_fixup(change, loc);    /* must do this before appending \n */
  560. X    gl_buf[len] = '\n';
  561. X    gl_buf[len+1] = '\0';
  562. X    gl_putc('\n');
  563. }
  564. X
  565. static void
  566. gl_del(int loc)
  567. /*
  568. X * Delete a character.  The loc variable can be:
  569. X *    -1 : delete character to left of cursor
  570. X *     0 : delete character under cursor
  571. X */
  572. {
  573. X    int i;
  574. X
  575. X    if (loc == -1 && gl_pos > 0 || loc == 0 && gl_pos < gl_cnt) {
  576. X        for (i=gl_pos+loc; i < gl_cnt; i++)
  577. X        gl_buf[i] = gl_buf[i+1];
  578. X    gl_fixup(gl_pos+loc, gl_pos+loc);
  579. X    } else
  580. X    gl_putc('\007');
  581. }
  582. X
  583. static void
  584. gl_kill()
  585. /* delete from current position to the end of line */
  586. {
  587. X    if (gl_pos < gl_cnt) {
  588. X    gl_buf[gl_pos] = '\0';
  589. X    gl_fixup(gl_pos, gl_pos);
  590. X    } else
  591. X    gl_putc('\007');
  592. }
  593. X
  594. void
  595. gl_redraw()
  596. /* emit a newline, reset and redraw prompt and current input line */
  597. {
  598. X    if (gl_init_done == -1) {
  599. X        gl_putc('\n');
  600. X        gl_puts(gl_prompt);
  601. X        gl_pos = 0;
  602. X        gl_fixup(0, BUF_SIZE);
  603. X    }
  604. }
  605. X
  606. static void
  607. gl_fixup(int change, int cursor)
  608. /*
  609. X * This function is used both for redrawing when input changes or for
  610. X * moving within the input line.  The parameters are:
  611. X *   change : the index of the start of changes in the input buffer,
  612. X *            with -1 indicating no changes.
  613. X *   cursor : the desired location of the cursor after the call.
  614. X *            A value of BUF_SIZE can be used  to indicate the cursor should
  615. X *            move just past the end of the input line.
  616. X */
  617. {
  618. X    static int   gl_shift;    /* index of first on screen character */
  619. X    static int   off_right;    /* true if more text right of screen */
  620. X    static int   off_left;    /* true if more text left of screen */
  621. X    int          left = 0, right = -1;        /* bounds for redraw */
  622. X    int          pad;        /* how much to erase at end of line */
  623. X    int          backup;        /* how far to backup before fixing */
  624. X    int          new_shift;     /* value of shift based on cursor */
  625. X    int          extra;         /* adjusts when shift (scroll) happens */
  626. X    int          i;
  627. X    int          new_right;     /* alternate right bound, using gl_extent */
  628. X
  629. X    if (change == -1 && cursor == 0 && gl_buf[0] == 0) {   /* reset */
  630. X    gl_shift = off_right = off_left = 0;
  631. X    return;
  632. X    }
  633. X    pad = (off_right)? gl_width - 1 : gl_cnt - gl_shift;   /* old length */
  634. X    backup = gl_pos - gl_shift;
  635. X    if (change >= 0) {
  636. X        gl_cnt = strlen(gl_buf);
  637. X        if (change > gl_cnt)
  638. X        change = gl_cnt;
  639. X    }
  640. X    if (cursor > gl_cnt) {
  641. X    if (cursor != BUF_SIZE)        /* BUF_SIZE means end of line */
  642. X        gl_putc('\007');
  643. X    cursor = gl_cnt;
  644. X    }
  645. X    if (cursor < 0) {
  646. X    gl_putc('\007');
  647. X    cursor = 0;
  648. X    }
  649. X    if (off_right || off_left && cursor < gl_shift + gl_width - SCROLL / 2)
  650. X    extra = 2;            /* shift the scrolling boundary */
  651. X    else 
  652. X    extra = 0;
  653. X    new_shift = cursor + extra + SCROLL - gl_width;
  654. X    if (new_shift > 0) {
  655. X    new_shift /= SCROLL;
  656. X    new_shift *= SCROLL;
  657. X    } else
  658. X    new_shift = 0;
  659. X    if (new_shift != gl_shift) {    /* scroll occurs */
  660. X    gl_shift = new_shift;
  661. X    off_left = (gl_shift)? 1 : 0;
  662. X    off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  663. X        left = gl_shift;
  664. X    new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  665. X    } else if (change >= 0) {        /* no scroll, but text changed */
  666. X    if (change < gl_shift + off_left) {
  667. X        left = gl_shift;
  668. X    } else {
  669. X        left = change;
  670. X        backup = gl_pos - change;
  671. X    }
  672. X    off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
  673. X    right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
  674. X    new_right = (gl_extent && (right > left + gl_extent))? 
  675. X                 left + gl_extent : right;
  676. X    }
  677. X    pad -= (off_right)? gl_width - 1 : gl_cnt - gl_shift;
  678. X    pad = (pad < 0)? 0 : pad;
  679. X    if (left <= right) {        /* clean up screen */
  680. X    for (i=0; i < backup; i++)
  681. X        gl_putc('\b');
  682. X    if (left == gl_shift && off_left) {
  683. X        gl_putc('$');
  684. X        left++;
  685. X        }
  686. X    for (i=left; i < new_right; i++)
  687. X        gl_putc(gl_buf[i]);
  688. X    gl_pos = new_right;
  689. X    if (off_right && new_right == right) {
  690. X        gl_putc('$');
  691. X        gl_pos++;
  692. X    } else { 
  693. X        for (i=0; i < pad; i++)    /* erase remains of prev line */
  694. X        gl_putc(' ');
  695. X        gl_pos += pad;
  696. X    }
  697. X    }
  698. X    i = gl_pos - cursor;        /* move to final cursor location */
  699. X    if (i > 0) {
  700. X    while (i--)
  701. X       gl_putc('\b');
  702. X    } else {
  703. X    for (i=gl_pos; i < cursor; i++)
  704. X        gl_putc(gl_buf[i]);
  705. X    }
  706. X    gl_pos = cursor;
  707. }
  708. X
  709. static int
  710. gl_tab(char *buf, int offset, int *loc)
  711. /* default tab handler, acts like tabstops every 8 cols */
  712. {
  713. X    int i, count, len;
  714. X
  715. X    len = strlen(buf);
  716. X    count = 8 - (offset + *loc) % 8;
  717. X    for (i=len; i >= *loc; i--)
  718. X        buf[i+count] = buf[i];
  719. X    for (i=0; i < count; i++)
  720. X        buf[*loc+i] = ' ';
  721. X    i = *loc;
  722. X    *loc = i + count;
  723. X    return i;
  724. }
  725. X
  726. /******************* History stuff **************************************/
  727. X
  728. #ifndef HIST_SIZE
  729. #define HIST_SIZE 100
  730. #endif
  731. X
  732. int             hist_pos, hist_last;
  733. char           *hist_buf[HIST_SIZE];
  734. X
  735. static void
  736. hist_init()
  737. {
  738. X    int i;
  739. X
  740. X    for (i=0; i < HIST_SIZE; i++)
  741. X    hist_buf[i] = (char *)0;
  742. }
  743. X
  744. static void
  745. hist_add()
  746. {
  747. X    char *p = gl_buf;
  748. X
  749. X    while (*p == ' ' || *p == '\t')    /* only save nonblank line */
  750. X    p++;
  751. X    if (*p) {
  752. X        hist_buf[hist_last] = hist_save(gl_buf);
  753. X        hist_last = (hist_last + 1) % HIST_SIZE;
  754. X        if (hist_buf[hist_last]) {    /* erase next location */
  755. X        free(hist_buf[hist_last]);
  756. X        hist_buf[hist_last] = 0;
  757. X        }
  758. X    }
  759. X    hist_pos = hist_last;
  760. }
  761. X
  762. static void
  763. hist_prev()
  764. /* loads previous hist entry into input buffer, sticks on first */
  765. {
  766. X    int   next;
  767. X
  768. X    next = (hist_pos - 1 + HIST_SIZE) % HIST_SIZE;
  769. X    if (next != hist_last) {
  770. X        if (hist_buf[next]) {
  771. X        hist_pos = next;
  772. X        strcpy(gl_buf, hist_buf[hist_pos]);
  773. X    } else
  774. X        gl_putc('\007');
  775. X    } else
  776. X    gl_putc('\007');
  777. X    if (gl_in_hook)
  778. X    gl_in_hook(gl_buf);
  779. X    gl_fixup(0, BUF_SIZE);
  780. }
  781. X
  782. static void
  783. hist_next()
  784. /* loads next hist entry into input buffer, clears on last */
  785. {
  786. X    if (hist_pos != hist_last) {
  787. X    hist_pos = (hist_pos + 1) % HIST_SIZE;
  788. X    if (hist_buf[hist_pos]) {
  789. X        strcpy(gl_buf, hist_buf[hist_pos]);
  790. X    } else {
  791. X        gl_buf[0] = 0;
  792. X    }
  793. X    } else
  794. X    gl_putc('\007');
  795. X    if (gl_in_hook) 
  796. X    gl_in_hook(gl_buf);
  797. X    gl_fixup(0, BUF_SIZE);
  798. }
  799. X
  800. static char *
  801. hist_save(char *p)
  802. /* makes a copy of the string */
  803. {
  804. X    char *s = 0;
  805. X
  806. X    if (p && ((s = malloc(strlen(p)+1)) != 0)) {
  807. X            strcpy(s, p);
  808. X    }
  809. X    return s;
  810. }
  811. SHAR_EOF
  812. chmod 0644 getline.c ||
  813. echo 'restore of getline.c failed'
  814. Wc_c="`wc -c < 'getline.c'`"
  815. test 20798 -eq "$Wc_c" ||
  816.     echo 'getline.c: original size 20798, current size' "$Wc_c"
  817. exit 0
  818.  
  819. exit 0 # Just in case...
  820. -- 
  821. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  822. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  823. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  824. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  825.