home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume33 / problem1 / part03 < prev    next >
Encoding:
Text File  |  1992-11-12  |  47.0 KB  |  1,588 lines

  1. Newsgroups: comp.sources.misc
  2. From: lijewski@rosserv.gsfc.nasa.gov (Mike Lijewski)
  3. Subject:  v33i074:  problem1.1 - A Problem Database Manager, Part03/07
  4. Message-ID: <1992Nov12.195445.28990@sparky.imd.sterling.com>
  5. X-Md4-Signature: 5c56396c13ea3956c9a2168a613bd6e9
  6. Date: Thu, 12 Nov 1992 19:54:45 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: lijewski@rosserv.gsfc.nasa.gov (Mike Lijewski)
  10. Posting-number: Volume 33, Issue 74
  11. Archive-name: problem1.1/part03
  12. Environment: UNIX, C++, GDBM, Termcap
  13. Supersedes: problem: Volume 33, Issue 2-9
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then unpack
  17. # it by saving it into a file and typing "sh file".  To overwrite existing
  18. # files, type "sh file -c".  You can also feed this as standard input via
  19. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  20. # will see the following message at the end:
  21. #        "End of archive 3 (of 7)."
  22. # Contents:  display.C regexp.C
  23. # Wrapped by lijewski@xtesoc2 on Wed Nov 11 16:20:08 1992
  24. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  25. if test -f 'display.C' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'display.C'\"
  27. else
  28. echo shar: Extracting \"'display.C'\" \(17628 characters\)
  29. sed "s/^X//" >'display.C' <<'END_OF_FILE'
  30. X/*
  31. X** Routines controlling the physical display
  32. X**
  33. X** display.C display.C 1.23   Delta\'d: 10:57:09 10/28/92   Mike Lijewski, CNSF
  34. X**
  35. X** Copyright \(c\) 1991, 1992 Cornell University
  36. X** All rights reserved.
  37. X**
  38. X** Redistribution and use in source and binary forms are permitted
  39. X** provided that: \(1\) source distributions retain this entire copyright
  40. X** notice and comment, and \(2\) distributions including binaries display
  41. X** the following acknowledgement:  ``This product includes software
  42. X** developed by Cornell University\'\' in the documentation or other
  43. X** materials provided with the distribution and in all advertising
  44. X** materials mentioning features or use of this software. Neither the
  45. X** name of the University nor the names of its contributors may be used
  46. X** to endorse or promote products derived from this software without
  47. X** specific prior written permission.
  48. X**
  49. X** THIS SOFTWARE IS PROVIDED ``AS IS\'\' AND WITHOUT ANY EXPRESS OR
  50. X** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  51. X** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  52. X*/
  53. X
  54. X#ifndef _IBMR2
  55. X#include <libc.h>
  56. X#endif /*_IBMR2*/
  57. X
  58. X#include <osfcn.h>
  59. X#include <signal.h>
  60. X#include <stdio.h>
  61. X#include <stdlib.h>
  62. X#include <string.h>
  63. X#include <sys/ioctl.h>
  64. X
  65. X#ifdef M_UNIX
  66. X#include <sys/unistd.h>
  67. X#include <sys/stream.h>
  68. X#include <sys/ptem.h>
  69. X#endif /*M_UNIX*/
  70. X
  71. X#ifdef TERMIOS
  72. X#include <termios.h>
  73. X#include <unistd.h>
  74. X#elif  TERMIO
  75. X#include <termio.h>
  76. X#if defined(ISC) || defined(ESIX)
  77. X#include <sys/stream.h>
  78. X#include <sys/ptem.h>
  79. X#endif /*ISC||ESIX*/
  80. X#else
  81. X#include <sgtty.h>
  82. X#endif
  83. X
  84. X#include "utilities.h"
  85. X#include "display.h"
  86. X
  87. X#ifndef EXIT_FAILURE
  88. X#define EXIT_FAILURE 1
  89. X#endif
  90. X
  91. X/*
  92. X** The definition of ospeed -- needed by the termcap routines.
  93. X*/
  94. X
  95. Xshort ospeed;
  96. X
  97. X//
  98. X// termcap capabilities we use
  99. X//
  100. Xchar *AL;               // insert blank line before cursor
  101. Xchar *ALN;              // insert N blank lines at cursor
  102. Xint   AM;               // automatic margins?
  103. Xchar *BC;               // backspace, if not BS
  104. Xint   BS;               // ASCII backspace works
  105. Xchar *CD;               // clear to end of display
  106. Xchar *CE;               // clear to end of line
  107. Xchar *CL;               // clear screen
  108. Xint   CO;               // number of columns
  109. Xchar *CM;               // cursor motion
  110. Xchar *CR;               // cursor beginning of line
  111. Xchar *CS;               // set scroll region
  112. Xint   DA;               // backing store off top?
  113. Xint   DB;               // backing store off bottom?
  114. Xchar *DC;               // delete character at cursor
  115. Xchar *DL;               // delete line cursor is on
  116. Xchar *DLN;              // delete N lines from cursor
  117. Xchar *DM;               // string to enter delete mode
  118. Xchar *DO;               // cursor down
  119. Xchar *ED;               // string to end delete mode
  120. Xint   HC;               // hardcopy terminal?
  121. Xchar *IS;               // initialize terminal
  122. Xchar *HO;               // cursor home
  123. Xchar *KD;               // down arrow key
  124. Xchar *KE;               // de-initialize keypad
  125. Xchar *KS;               // initialize keypad -- for arrow keys
  126. Xchar *KU;               // up arrrow key
  127. Xchar *LE;               // cursor back one column
  128. Xint   LI;               // number of rows
  129. Xchar *LL;               // cursor to lower left
  130. Xint   OS;               // terminal overstrikes?
  131. X#ifndef APOLLO
  132. Xchar  PC;               // pad character
  133. X#endif
  134. Xchar *PCstr;            // pad string
  135. Xchar *SE;               // end standout mode
  136. Xchar *SF;               // scroll screen up one line
  137. Xchar *SO;               // enter standout mode
  138. Xchar *SR;               // scroll screen down one line
  139. Xchar *TE;               // end cursor addressing mode
  140. Xchar *TI;               // enter cursor addressing mode
  141. Xchar *UP;               // cursor up
  142. Xchar *VE;               // end visual mode
  143. Xchar *VS;               // enter visual mode
  144. Xchar *XN;               // strange wrap behaviour
  145. X
  146. X/*
  147. X** termcap - reads termcap file setting all the terminal capabilities
  148. X**           which we will use.
  149. X*/
  150. X
  151. Xvoid termcap(const char *term_type)
  152. X{
  153. X    static char capability_buffer[512];
  154. X    char* bp = capability_buffer;
  155. X    char termcap_buffer[2048];
  156. X    
  157. X    switch (tgetent(termcap_buffer, term_type))
  158. X    {
  159. X      case -1:
  160. X        (void)fputs("couldn't open termcap database\n", stderr);
  161. X        exit(1);
  162. X      case 0:
  163. X        (void)fprintf(stderr, "invalid terminal type: `%s'\n", term_type);
  164. X        exit(1);
  165. X      default: break;
  166. X    }
  167. X    
  168. X    AL = tgetstr("al", &bp);
  169. X    ALN = tgetstr("AL", &bp);
  170. X    AM = tgetflag("am");
  171. X    BC = tgetstr("bc", &bp);
  172. X    BS = tgetflag("bs");
  173. X    CD = tgetstr("cd", &bp);
  174. X    CE = tgetstr("ce", &bp);
  175. X    CL = tgetstr("cl", &bp);
  176. X    CM = tgetstr("cm", &bp);
  177. X    CR = tgetstr("cr", &bp);
  178. X    CS = tgetstr("cs", &bp);
  179. X    DA = tgetflag("da");
  180. X    DB = tgetflag("db");
  181. X    DC = tgetstr("dc", &bp);
  182. X    DL = tgetstr("dl", &bp);
  183. X    DLN = tgetstr("DL", &bp);
  184. X    DM = tgetstr("dm", &bp);
  185. X    DO = tgetstr("do", &bp);
  186. X    ED = tgetstr("ed", &bp);
  187. X    HC = tgetflag("hc");
  188. X    HO = tgetstr("ho", &bp);
  189. X    IS = tgetstr("is", &bp);
  190. X    KD = tgetstr("kd", &bp);
  191. X    KE = tgetstr("ke", &bp);
  192. X    KS = tgetstr("ks", &bp);
  193. X    KU = tgetstr("ku", &bp);
  194. X    LE = tgetstr("le", &bp);
  195. X    LL = tgetstr("ll", &bp);
  196. X    OS = tgetflag("os");
  197. X    PCstr = tgetstr("pc", &bp);
  198. X    SE = tgetstr("se", &bp);
  199. X    SF = tgetstr("sf", &bp);
  200. X    SO = tgetstr("so", &bp);
  201. X    SR = tgetstr("sr", &bp);
  202. X    TE = tgetstr("te", &bp);
  203. X    TI = tgetstr("ti", &bp);
  204. X    UP = tgetstr("up", &bp);
  205. X    VE = tgetstr("ve", &bp);
  206. X    VS = tgetstr("vs", &bp);
  207. X    XN = tgetstr("xn", &bp);
  208. X    
  209. X    PC = PCstr ? PCstr[0] :  0;
  210. X    
  211. X    if (!BC && !LE && !BS)
  212. X    {
  213. X        (void)fputs("terminal can't backspace - unusable\n", stderr);
  214. X        exit(1);
  215. X    }
  216. X    if (!BC) BC = LE ? LE : "\b";
  217. X    if (!CR) CR = "\r";
  218. X    if (!DO) DO = SF ? SF : "\n";
  219. X    
  220. X    const char *tmp = getenv("LINES");
  221. X    if (tmp) LI = atoi(tmp);
  222. X    tmp = getenv("COLUMNS");
  223. X    if (tmp) CO = atoi(tmp);
  224. X    
  225. X#ifdef TIOCGWINSZ
  226. X    struct winsize win;
  227. X    if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&win) == 0)
  228. X    {
  229. X        if (LI == 0 && win.ws_row > 0) LI = win.ws_row;
  230. X        if (CO == 0 && win.ws_col > 0) CO = win.ws_col;
  231. X    }
  232. X#endif
  233. X    
  234. X    if (CO == 0) CO = tgetnum("co");
  235. X    if (LI == 0) LI = tgetnum("li");
  236. X    
  237. X    if (LI == -1 || CO == -1 || HC || !CM || !CE)
  238. X    {
  239. X        (void)fputs("terminal too dumb to be useful\n", stderr);
  240. X        exit(1);
  241. X    }
  242. X    if (LI < 5)
  243. X    {
  244. X        (void)fputs("too few rows to be useful\n", stderr);
  245. X        exit(1);
  246. X    }
  247. X}
  248. X
  249. X/*
  250. X** setraw - puts terminal into raw mode.  Cbreak mode actually, but
  251. X**          why be pedantic.  Flow control is disabled as well as BREAK keys.
  252. X**          Echoing is turned off as well as signal generation.  Hence
  253. X**          keyboard generated signals must be simulated.  Also sets
  254. X**          ospeed.
  255. X*/
  256. X
  257. X#ifdef TERMIOS
  258. Xstatic struct termios tty_mode;    /* save tty mode here */
  259. X#elif  TERMIO
  260. Xstatic struct termio tty_mode;    /* save tty mode here */
  261. X#else
  262. Xstatic struct sgttyb  oarg;      /* save tty stuff here */
  263. Xstatic struct tchars  otarg;
  264. Xstatic struct ltchars oltarg;
  265. X#endif
  266. X
  267. Xvoid setraw()
  268. X{
  269. X#ifdef TERMIOS
  270. X    struct termios temp_mode;
  271. X
  272. X    if (tcgetattr(STDIN_FILENO, &temp_mode) < 0)
  273. X    {
  274. X        perror("tcgetattr");
  275. X        exit(EXIT_FAILURE);
  276. X    }
  277. X
  278. X    tty_mode = temp_mode;  /* save for latter restoration */
  279. X
  280. X    temp_mode.c_iflag &= ~(IGNBRK|ICRNL|INLCR);
  281. X    temp_mode.c_lflag &= ~(ICANON|ECHO|IEXTEN);
  282. X    temp_mode.c_oflag &= ~OPOST;
  283. X    temp_mode.c_cc[VQUIT] = 28; // C-\ is QUIT
  284. X    temp_mode.c_cc[VMIN]  = 1;    // min #chars to satisfy read
  285. X    temp_mode.c_cc[VTIME] = 0;    // read returns immediately
  286. X
  287. X    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &temp_mode) < 0)
  288. X    {
  289. X        perror("tcsetattr");
  290. X        exit(EXIT_FAILURE);
  291. X    }
  292. X
  293. X    ospeed = cfgetospeed(&temp_mode);
  294. X#elif TERMIO
  295. X    struct termio temp_mode;
  296. X    
  297. X    if (ioctl(fileno(stdin), TCGETA, (char *)&temp_mode) < 0)
  298. X    {
  299. X        perror("ioctl - TCGETA");
  300. X        exit(EXIT_FAILURE);
  301. X    }
  302. X
  303. X    tty_mode = temp_mode;  /* save for latter restoration */
  304. X
  305. X    temp_mode.c_iflag &= ~(IGNBRK|ICRNL|INLCR);
  306. X    temp_mode.c_lflag &= ~(ICANON|ECHO);
  307. X    temp_mode.c_oflag &= ~OPOST;
  308. X    temp_mode.c_cc[VQUIT] = 28; // C-\ is QUIT
  309. X    temp_mode.c_cc[VMIN]  = 1;    // min #chars to satisfy read
  310. X    temp_mode.c_cc[VTIME] = 0;    // read returns immediately
  311. X
  312. X    if (ioctl(fileno(stdin), TCSETA, (char *)&temp_mode) < 0)
  313. X    {
  314. X        perror("ioctl - TCSETA");
  315. X        exit(EXIT_FAILURE);
  316. X    }
  317. X
  318. X    ospeed = temp_mode.c_cflag & CBAUD;
  319. X#else
  320. X    struct sgttyb arg;
  321. X    struct tchars targ;
  322. X    struct ltchars ltarg;
  323. X
  324. X    if (ioctl(fileno(stdin), TIOCGETP, (char *)&arg) < 0)
  325. X    {
  326. X        perror("ioctl - TIOCGETP");
  327. X        exit(EXIT_FAILURE);
  328. X    }
  329. X    if (ioctl(fileno(stdin), TIOCGETC, (char *)&targ) < 0)
  330. X    {
  331. X        perror("ioctl - TIOCGETC");
  332. X        exit(EXIT_FAILURE);
  333. X    }
  334. X    if (ioctl(fileno(stdin), TIOCGLTC, (char *)<arg) < 0)
  335. X    {
  336. X        perror("ioctl - TIOCGLTC");
  337. X        exit(EXIT_FAILURE);
  338. X    }
  339. X
  340. X    oarg   = arg;
  341. X    otarg  = targ;
  342. X    oltarg = ltarg;
  343. X
  344. X    arg.sg_flags=((arg.sg_flags&~(ECHO|CRMOD))|CBREAK) ;
  345. X    targ.t_eofc    = -1;  // turn off end-of-file character
  346. X    targ.t_brkc    = -1;  // turn off break delimiter
  347. X    ltarg.t_dsuspc = -1;  // turn off delayed suspend character
  348. X    ltarg.t_rprntc = -1;  // turn off reprint line character
  349. X    ltarg.t_flushc = -1;  // turn off flush character
  350. X    ltarg.t_werasc = -1;  // turn off erase work character
  351. X    ltarg.t_lnextc = -1;  // turn off literal next char
  352. X
  353. X    if (ioctl(fileno(stdin), TIOCSETN, (char *)&arg) < 0)
  354. X    {
  355. X        perror("ioctl - TIOCSETN");
  356. X        exit(EXIT_FAILURE);
  357. X    }
  358. X    if (ioctl(fileno(stdin), TIOCSETC, (char *)&targ) < 0)
  359. X    {
  360. X        perror("ioctl - TIOCSETC");
  361. X        exit(EXIT_FAILURE);
  362. X    }
  363. X    if (ioctl(fileno(stdin), TIOCSLTC, (char *)<arg) < 0)
  364. X    {
  365. X        perror("ioctl - TIOCSLTC");
  366. X        exit(EXIT_FAILURE);
  367. X    }
  368. X
  369. X    ospeed = arg.sg_ospeed;
  370. X#endif
  371. X}
  372. X
  373. X/*
  374. X** unsetraw - Restore the terminal mode to whatever it was on the most
  375. X**            recent call to the setraw function above.
  376. X**            Exits with rc==1 on failure.
  377. X*/
  378. X
  379. Xvoid unsetraw()
  380. X{
  381. X#ifdef TERMIOS
  382. X    if (tcsetattr(0, TCSAFLUSH, &tty_mode) < 0)
  383. X    {
  384. X        perror("tcsetattr");
  385. X        exit(EXIT_FAILURE);
  386. X    }
  387. X#elif TERMIO
  388. X    if (ioctl(fileno(stdin), TCSETA, (char *)&tty_mode) < 0)
  389. X    {
  390. X        perror("ioctl - TCSETA");
  391. X        exit(EXIT_FAILURE);
  392. X    }
  393. X#else
  394. X    if (ioctl(fileno(stdin), TIOCSETN, (char *)&oarg) < 0)
  395. X    {
  396. X        perror("ioctl - TIOSETN");
  397. X        exit(EXIT_FAILURE);
  398. X    }
  399. X    if (ioctl(fileno(stdin), TIOCSETC, (char *)&otarg) < 0)
  400. X    {
  401. X        perror("ioctl - TIOSETC");
  402. X        exit(EXIT_FAILURE);
  403. X    }
  404. X    if (ioctl(fileno(stdin), TIOCSLTC, (char *)&oltarg) < 0) {
  405. X        perror("ioctl - TIOSLTC");
  406. X        exit(EXIT_FAILURE);
  407. X    }
  408. X#endif
  409. X}
  410. X
  411. X/*
  412. X** outputch - a function to output a single character.
  413. X**            Termcap routines NEED a function.
  414. X*/
  415. X
  416. Xint outputch(int ch) { return putchar(ch); }
  417. X
  418. X/*
  419. X** init_screen_and_kbdr - initialize screen and keyboard
  420. X*/
  421. X
  422. Xvoid init_screen_and_kbdr()
  423. X{
  424. X    setraw();
  425. X    enter_cursor_addressing_mode();
  426. X    enter_visual_mode();
  427. X    enable_keypad();
  428. X    synch_display();
  429. X}
  430. X
  431. X/*
  432. X** initialize - get ready to do some real work.
  433. X*/
  434. X
  435. Xvoid initialize()
  436. X{
  437. X    setvbuf(stdout, 0, _IOFBF, 0);  // fully buffer stdout
  438. X    setvbuf(stdin , 0, _IONBF, 0);  // no buffering on stdin
  439. X
  440. X    const char *term = getenv("TERM");
  441. X    if (term == 0 || *term == 0)
  442. X    {
  443. X        (void)fputs("please set your TERM variable appropriately\n", stderr);
  444. X        exit(1);
  445. X    }
  446. X
  447. X    termcap(term);
  448. X    init_screen_and_kbdr();
  449. X}
  450. X
  451. X/*
  452. X** deinit_screen_and_kbdr
  453. X*/
  454. X
  455. Xvoid deinit_screen_and_kbdr()
  456. X{
  457. X    move_to_message_line();
  458. X    clear_to_end_of_line();
  459. X    disable_keypad();
  460. X    end_visual_mode();
  461. X    end_cursor_addressing_mode();
  462. X    synch_display();
  463. X    unsetraw();
  464. X}
  465. X
  466. X//
  467. X// Have we just been continued after being suspended?
  468. X// Should be a sig_atomic_t.
  469. X//
  470. Xint resumingAfterSuspension;
  471. X
  472. X/*
  473. X** scroll_listing_up_N - scrolls the listing window up n lines.
  474. X**                       The cursor is left in column 0 of the first
  475. X**                       line to scroll into the window.
  476. X**                       Must have CS capability.
  477. X*/
  478. X
  479. Xvoid scroll_listing_up_N(int n)
  480. X{
  481. X    output_string_capability(tgoto(CS, rows()-3, 0));
  482. X    move_cursor(rows()-3, 0);
  483. X    for (int i = 0; i < n; i++) cursor_down();
  484. X    output_string_capability(tgoto(CS, rows()-1, 0));
  485. X    move_cursor(rows()-2-n, 0);
  486. X}
  487. X
  488. X/*
  489. X** scroll_listing_down_N - half_down - scrolls the listing window
  490. X**                         \(line 0 - rows-3\) down \(rows-2\)/2 lines.
  491. X**                         The cursor is left in HOME position.
  492. X**                         Must have CS capability.
  493. X*/
  494. X
  495. Xvoid scroll_listing_down_N(int n)
  496. X{
  497. X    output_string_capability(tgoto(CS, rows()-3, 0));
  498. X    move_cursor(0, 0);
  499. X    for (int i = 0; i < n; i++) output_string_capability(SR, rows()-2);
  500. X    output_string_capability(tgoto(CS, rows()-1, 0));
  501. X    cursor_home();
  502. X}
  503. X
  504. X/*
  505. X** scroll_listing_up_one - scrolls the listing window \(line 0 - rows-3\)
  506. X**                         up one row. The cursor is left in column
  507. X**                         0 of rows-3 row.  Assumes CS capability.
  508. X*/
  509. X
  510. Xvoid scroll_listing_up_one()
  511. X{
  512. X    output_string_capability(tgoto(CS, rows()-3, 0));
  513. X    move_cursor(rows()-3, 0);
  514. X    cursor_down();
  515. X    output_string_capability(tgoto(CS, rows()-1, 0));
  516. X    move_cursor(rows()-3, 0);
  517. X}
  518. X
  519. X/*
  520. X** scroll_listing_down_one - scrolls the listing window \(line 0 - rows-3\)
  521. X**                           down one row. The cursor is left at HOME.
  522. X**                           Assumes CS capability.
  523. X*/
  524. X
  525. Xvoid scroll_listing_down_one()
  526. X{
  527. X    output_string_capability(tgoto(CS, rows()-3, 0));
  528. X    cursor_home();
  529. X    output_string_capability(SR, rows()-2);
  530. X    output_string_capability(tgoto(CS, rows()-1, 0));
  531. X    cursor_home();
  532. X}
  533. X
  534. X/*
  535. X** termstop - caught a SIGTSTP.  We are relying on the signal to
  536. X**            interrupt read.
  537. X*/
  538. X
  539. X#ifdef SIGTSTP
  540. Xvoid termstop(int)
  541. X{
  542. X    (void)signal(SIGTSTP,  SIG_IGN);
  543. X#ifdef SIGWINCH
  544. X    (void)signal(SIGWINCH, SIG_IGN);
  545. X#endif
  546. X    deinit_screen_and_kbdr();
  547. X    (void)kill(getpid(), SIGSTOP);
  548. X    init_screen_and_kbdr();
  549. X    (void)signal(SIGTSTP,  termstop);
  550. X#ifdef SIGWINCH
  551. X    (void)signal(SIGWINCH, winch);
  552. X#endif
  553. X
  554. X    //
  555. X    // window size may have changed
  556. X    //
  557. X#ifdef TIOCGWINSZ
  558. X    int oCO = columns(), oLI = rows();
  559. X    struct winsize w;
  560. X    if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&w) == 0 && w.ws_row > 0)
  561. X        LI = w.ws_row;
  562. X    if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&w) == 0 && w.ws_col > 0)
  563. X        CO = w.ws_col;
  564. X    if (oCO != columns() || oLI != rows()) windowSizeChanged = 1;
  565. X#endif
  566. X    resumingAfterSuspension = 1;
  567. X}
  568. X#endif /*SIGTSTP*/
  569. X
  570. X/*
  571. X** clear_display_area - clears all except the last two lines.  Leaves
  572. X**                      the cursor at home.
  573. X*/
  574. X
  575. Xvoid clear_display_area()
  576. X{
  577. X    cursor_home();
  578. X    for (int i = 0; i < rows() - 2; i++)
  579. X    {
  580. X        clear_to_end_of_line();
  581. X        cursor_down();
  582. X    }
  583. X    cursor_home();
  584. X}
  585. X
  586. X/*
  587. X** scroll_screen_up_one - must have DL or SF
  588. X*/
  589. X
  590. Xvoid scroll_screen_up_one()
  591. X{
  592. X    if (DL)
  593. X    {
  594. X        cursor_home();
  595. X        output_string_capability(DL, rows());
  596. X    }
  597. X    else
  598. X    {
  599. X        move_cursor(rows()-1, 0);
  600. X        output_string_capability(SF, rows());
  601. X    }
  602. X    if (DB) clear_message_line();
  603. X}
  604. X
  605. X/*
  606. X** scroll_screen_down_one - must have AL or SR
  607. X*/
  608. X
  609. Xvoid scroll_screen_down_one()
  610. X{
  611. X    cursor_home();
  612. X
  613. X    if (AL)
  614. X        output_string_capability(AL, rows());
  615. X    else
  616. X        output_string_capability(SR, rows());
  617. X    if (DA) clear_to_end_of_line();
  618. X}
  619. X
  620. X/*
  621. X** scroll_screen_up_N - must have DLN, DL or SF.
  622. X**         
  623. X*/
  624. X
  625. Xvoid scroll_screen_up_N(int n)
  626. X{
  627. X    if (DLN)
  628. X    {
  629. X        cursor_home();
  630. X        output_string_capability(tgoto(DLN, 0, n), rows());
  631. X    }
  632. X    else if (DL)
  633. X    {
  634. X        cursor_home();
  635. X        for (int i = 0; i < n; i++)
  636. X            output_string_capability(DL, rows());
  637. X    }
  638. X    else
  639. X    {
  640. X        move_cursor(rows()-1, 0);
  641. X        for (int i = 0; i < n; i++) output_string_capability(SF, rows());
  642. X    }
  643. X    if (DB) clear_to_end_of_screen(rows()-n);
  644. X}
  645. X
  646. X/*
  647. X** scroll_screen_down_N - must have ALN, AL or SR.
  648. X*/
  649. X
  650. Xvoid scroll_screen_down_N(int n)
  651. X{
  652. X    cursor_home();
  653. X    int i;
  654. X    if (ALN)
  655. X        output_string_capability(tgoto(ALN, 0, n), rows());
  656. X    else if (AL)
  657. X        for (i = 0; i < n; i++) output_string_capability(AL, rows());
  658. X    else
  659. X        for (i = 0; i < n; i++) output_string_capability(SR, rows());
  660. X    if (DA)
  661. X    {
  662. X        for (i = 0; i < n; i++)
  663. X        {
  664. X            clear_to_end_of_line();
  665. X            cursor_down();
  666. X        }
  667. X        cursor_home();
  668. X    }
  669. X}
  670. X
  671. X/*
  672. X** clear_to_end_of_screen - clears screen from line y to the bottom
  673. X*/
  674. X
  675. Xvoid clear_to_end_of_screen(int y)
  676. X{
  677. X    move_cursor(y, 0);
  678. X    if (CD)
  679. X        output_string_capability(DL, rows()-y);
  680. X    else
  681. X        for (int i = 0; i < rows()-y; i++)
  682. X        {
  683. X            clear_to_end_of_line();
  684. X            putchar('\n');
  685. X        }
  686. X}
  687. X
  688. X/*
  689. X** delete_listing_line - deletes line at line y, scrolling the lines below
  690. X**                       y up.  We only call this routine when we KNOW that
  691. X**                       there is at least one line in need of being scrolled
  692. X**                       up. Must have CS capability.
  693. X*/
  694. X
  695. Xvoid delete_listing_line(int y)
  696. X{
  697. X    move_cursor(y, 0);
  698. X    clear_to_end_of_line();
  699. X    output_string_capability(tgoto(CS, rows()-3, y));
  700. X    move_cursor(rows()-3, 0);
  701. X    cursor_down();
  702. X    output_string_capability(tgoto(CS, rows()-1, 0));
  703. X}
  704. X
  705. X
  706. END_OF_FILE
  707. if test 17628 -ne `wc -c <'display.C'`; then
  708.     echo shar: \"'display.C'\" unpacked with wrong size!
  709. fi
  710. # end of 'display.C'
  711. fi
  712. if test -f 'regexp.C' -a "${1}" != "-c" ; then 
  713.   echo shar: Will not clobber existing file \"'regexp.C'\"
  714. else
  715. echo shar: Extracting \"'regexp.C'\" \(26725 characters\)
  716. sed "s/^X//" >'regexp.C' <<'END_OF_FILE'
  717. X/*
  718. X** regexp - a C++-ized version of Henry Spencers regexp package.
  719. X**
  720. X** regexp.C regexp.C 1.7   Delta\'d: 15:39:42 9/22/92   Mike Lijewski, CNSF
  721. X**
  722. X**
  723. X** @\(#\)regexp.c    1.3 of 18 April 87
  724. X**
  725. X**    Copyright \(c\) 1986 by University of Toronto.
  726. X**    Written by Henry Spencer.  Not derived from licensed software.
  727. X**
  728. X**    Permission is granted to anyone to use this software for any
  729. X**    purpose on any computer system, and to redistribute it freely,
  730. X**    subject to the following restrictions:
  731. X**
  732. X**    1. The author is not responsible for the consequences of use of
  733. X**        this software, no matter how awful, even if they arise
  734. X**        from defects in it.
  735. X**
  736. X**    2. The origin of this software must not be misrepresented, either
  737. X**        by explicit claim or by omission.
  738. X**
  739. X**    3. Altered versions must be plainly marked as such, and must not
  740. X**        be misrepresented as being the original software.
  741. X**
  742. X** Beware that some of this code is subtly aware of the way operator
  743. X** precedence is structured in regular expressions.  Serious changes in
  744. X** regular-expression syntax might require a total rethink.
  745. X*/
  746. X
  747. X#include <stdio.h>
  748. X#include <stdlib.h>
  749. X#include <string.h>
  750. X
  751. X#include "regexp.h"
  752. X
  753. X/*
  754. X** The "internal use only" fields in regexp.h are present to pass info from
  755. X** compile to execute that permits the execute phase to run lots faster on
  756. X** simple cases.  They are:
  757. X**
  758. X** regstart    char that must begin a match; \'\0\' if none obvious
  759. X** reganch    is the match anchored \(at beginning-of-line only\)?
  760. X** regmust    string \(pointer into program\) that match must include, or 0
  761. X** regmlen    length of regmust string
  762. X**
  763. X** Regstart and reganch permit very fast decisions on suitable starting points
  764. X** for a match, cutting down the work a lot.  Regmust permits fast rejection
  765. X** of lines that cannot possibly match.  The regmust tests are costly enough
  766. X** that regcomp\(\) supplies a regmust only if the r.e. contains something
  767. X** potentially expensive \(at present, the only such thing detected is * or +
  768. X** at the start of the r.e., which can involve a lot of backup\).  Regmlen is
  769. X** supplied because the test in regexec\(\) needs it and regcomp\(\) is computing
  770. X** it anyway.
  771. X*/
  772. X
  773. X/*
  774. X** Structure for regexp "program".  This is essentially a linear encoding
  775. X** of a nondeterministic finite-state machine \(aka syntax charts or
  776. X** "railroad normal form" in parsing technology\).  Each node is an opcode
  777. X** plus a "next" pointer, possibly plus an operand.  "Next" pointers of
  778. X** all nodes except BRANCH implement concatenation; a "next" pointer with
  779. X** a BRANCH on both ends of it is connecting two alternatives.  \(Here we
  780. X** have one of the subtle syntax dependencies:  an individual BRANCH \(as
  781. X** opposed to a collection of them\) is never concatenated with anything
  782. X** because of operator precedence.\)  The operand of some types of node is
  783. X** a literal string; for others, it is a node leading into a sub-FSM.  In
  784. X** particular, the operand of a BRANCH node is the first node of the branch.
  785. X** \(NB this is *not* a tree structure:  the tail of the branch connects
  786. X** to the thing following the set of BRANCHes.\)  The opcodes are:
  787. X*/
  788. X
  789. X/* definition    number    opnd?    meaning */
  790. Xconst int END     = 0;    /* no    End of program. */
  791. Xconst int BOL     = 1;    /* no    Match "" at beginning of line. */
  792. Xconst int EOL     = 2;    /* no    Match "" at end of line. */
  793. Xconst int ANY     = 3;    /* no    Match any one character. */
  794. Xconst int ANYOF   = 4;    /* str    Match any character in this string. */
  795. Xconst int ANYBUT  = 5;    /* str    Match any character not in this string. */
  796. Xconst int BRANCH  = 6;    /* node    Match this alternative, or the next... */
  797. Xconst int BACK    = 7;    /* no    Match "", "next" ptr points backward. */
  798. Xconst int EXACTLY = 8;    /* str    Match this string. */
  799. Xconst int NOTHING = 9;    /* no    Match empty string. */
  800. Xconst int STAR    = 10;    /* node    Match this \(simple\) thing 0 or more times. */
  801. Xconst int PLUS    = 11;    /* node    Match this \(simple\) thing 1 or more times. */
  802. Xconst int OPEN    = 20;    /* no    Mark this point in input as start of #n. */
  803. X            /*    OPEN+1 is number 1, etc. */
  804. Xconst int CLOSE   = 30;    /* no    Analogous to OPEN. */
  805. X
  806. X/*
  807. X** Opcode notes:
  808. X**
  809. X** BRANCH    The set of branches constituting a single choice are hooked
  810. X**        together with their "next" pointers, since precedence prevents
  811. X**        anything being concatenated to any individual branch.  The
  812. X**        "next" pointer of the last BRANCH in a choice points to the
  813. X**        thing following the whole choice.  This is also where the
  814. X**        final "next" pointer of each individual branch points; each
  815. X**        branch starts with the operand node of a BRANCH node.
  816. X**
  817. X** BACK        Normal "next" pointers all implicitly point forward; BACK
  818. X**        exists to make loop structures possible.
  819. X**
  820. X** STAR,PLUS    \'?\', and complex \'*\' and \'+\', are implemented as circular
  821. X**        BRANCH structures using BACK.  Simple cases \(one character
  822. X**        per match\) are implemented with STAR and PLUS for speed
  823. X**        and to minimize recursive plunges.
  824. X**
  825. X** OPEN,CLOSE    ...are numbered at compile time.
  826. X*/
  827. X
  828. X/*
  829. X** A node is one char of opcode followed by two chars of "next" pointer.
  830. X** "Next" pointers are stored as two 8-bit pieces, high order first.  The
  831. X** value is a positive offset from the opcode of the node containing it.
  832. X** An operand, if any, simply follows the node.  \(Note that much of the
  833. X** code generation knows about this implicit relationship.\)
  834. X**
  835. X** Using two bytes for the "next" pointer is vast overkill for most things,
  836. X** but allows patterns to get big without disasters.
  837. X*/
  838. X
  839. X#define    NEXT(p)    (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
  840. X
  841. Xconst int MAGIC = 0234;
  842. Xconst char *const META = "^$.[()|?+*\\";
  843. X
  844. Xinline char OP(const char *const &p) { return *p;                            }
  845. Xinline char *OPERAND(char *p)     { return p + 3;                            }
  846. Xinline int UCHARAT(const char *p) { return (int)*(unsigned char *)p;         }
  847. Xinline int FAIL(const char *m)      { REerror = m; return 0;                   }
  848. Xinline int ISMULT(char c)         { return c == '*' || c == '+' || c == '?'; }
  849. X
  850. X// Flags to be passed up and down.
  851. Xconst int HASWIDTH = 01;    /* Known never to match null string. */
  852. Xconst int SIMPLE   = 02;    /* Simple enough to be STAR/PLUS operand. */
  853. Xconst int SPSTART  = 04;    /* Starts with * or +. */
  854. Xconst int WORST    = 0;     /* Worst case. */
  855. X
  856. X// our definition of REerror
  857. Xconst char *REerror;
  858. X
  859. X// Global work variables for regcomp\(\).
  860. Xstatic const char *regparse;    /* Input-scan pointer. */
  861. Xstatic int   regnpar;        /* \(\) count. */
  862. Xstatic char  regdummy;
  863. Xstatic char *regcode;        /* Code-emit pointer; ®dummy = don\'t. */
  864. Xstatic long  regsize;        /* Code size. */
  865. X
  866. X/*
  867. X** regnext - dig the "next" pointer out of a node
  868. X*/
  869. X
  870. Xstatic char *regnext(char *p)
  871. X{
  872. X    if (p == ®dummy) return 0;
  873. X    int offset = NEXT(p);
  874. X    if (offset == 0) return 0;
  875. X    return (OP(p) == BACK) ? p-offset : p+offset;
  876. X}
  877. X
  878. X/*
  879. X** regtail - set the next-pointer at the end of a node chain
  880. X*/
  881. X
  882. Xstatic void regtail(char *p, char *val)
  883. X{
  884. X    if (p == ®dummy) return;
  885. X    
  886. X    /* Find last node. */
  887. X    char *scan = p;
  888. X    char *temp;
  889. X    for (;;) {
  890. X        temp = regnext(scan);
  891. X        if (temp == 0) break;
  892. X        scan = temp;
  893. X    }
  894. X
  895. X    int offset = (OP(scan) == BACK) ? scan - val : val - scan;
  896. X    *(scan+1) = (offset>>8)&0377;
  897. X    *(scan+2) = offset&0377;
  898. X}
  899. X
  900. X/*
  901. X** regoptail - regtail on operand of first argument; nop if operandless
  902. X*/
  903. X
  904. Xstatic void regoptail(char *p, char *val)
  905. X{
  906. X    /* "Operandless" and "op != BRANCH" are synonymous in practice. */
  907. X    if (p == 0 || p == ®dummy || OP(p) != BRANCH) return;
  908. X    regtail(OPERAND(p), val);
  909. X}
  910. X
  911. X/*
  912. X** regnode - emit a node
  913. X*/
  914. X
  915. Xstatic char *regnode(char op)
  916. X{
  917. X    char *ret = regcode;
  918. X    if (ret == ®dummy) { regsize += 3; return ret; }
  919. X    char *ptr = ret;
  920. X    *ptr++ = op;
  921. X    *ptr++ = '\0';  /* Null "next" pointer. */
  922. X    *ptr++ = '\0';
  923. X    regcode = ptr;
  924. X    return ret;
  925. X}
  926. X
  927. X/*
  928. X** reginsert - insert an operator in front of already-emitted operand
  929. X**
  930. X** Means relocating the operand.
  931. X*/
  932. X
  933. Xstatic void reginsert(char op, char *opnd)
  934. X{
  935. X    if (regcode == ®dummy) { regsize += 3; return; }
  936. X    
  937. X    char *src = regcode;
  938. X    regcode += 3;
  939. X    char *dst = regcode;
  940. X    while (src > opnd) *--dst = *--src;
  941. X    
  942. X    char *place = opnd; // Op node, where operand used to be.
  943. X    *place++ = op;
  944. X    *place++ = '\0';
  945. X    *place++ = '\0';
  946. X}
  947. X
  948. X/*
  949. X** regc - emit \(if appropriate\) a byte of code
  950. X*/
  951. X
  952. Xstatic inline void regc(char b)
  953. X{
  954. X    if (regcode != ®dummy)
  955. X        *regcode++ = b;
  956. X    else
  957. X        regsize++;
  958. X}
  959. X
  960. X// forward reference
  961. Xstatic char *reg(int paren, int *flagp);
  962. X
  963. X/*
  964. X** regatom - the lowest level
  965. X**
  966. X** Optimization:  gobbles an entire sequence of ordinary characters so that
  967. X** it can turn them into a single node, which is smaller to store and
  968. X** faster to run.  Backslashed characters are exceptions, each becoming a
  969. X** separate node; the code is simpler that way and it\'s not worth fixing.
  970. X*/
  971. X
  972. Xstatic char *regatom(int *flagp)
  973. X{
  974. X    char *ret;
  975. X    int flags;
  976. X    
  977. X    *flagp = WORST;        /* Tentatively. */
  978. X    
  979. X    switch (*regparse++) {
  980. X      case '^': ret = regnode(BOL); break;
  981. X      case '$': ret = regnode(EOL); break;
  982. X      case '.': ret = regnode(ANY); *flagp |= HASWIDTH|SIMPLE; break;
  983. X      case '[': {
  984. X          if (*regparse == '^') {    /* Complement of range. */
  985. X              ret = regnode(ANYBUT);
  986. X              regparse++;
  987. X          } else
  988. X              ret = regnode(ANYOF);
  989. X          if (*regparse == ']' || *regparse == '-') regc(*regparse++);
  990. X          while (*regparse != '\0' && *regparse != ']') {
  991. X              if (*regparse == '-') {
  992. X                  regparse++;
  993. X                  if (*regparse == ']' || *regparse == '\0')
  994. X                      regc('-');
  995. X                  else {
  996. X                      int theclass = UCHARAT(regparse-2)+1;
  997. X                      int classend = UCHARAT(regparse);
  998. X                      if (theclass > classend+1) FAIL("invalid [] range");
  999. X                      for (; theclass <= classend; theclass++) regc(theclass);
  1000. X                      regparse++;
  1001. X                  }
  1002. X              } else
  1003. X                  regc(*regparse++);
  1004. X          }
  1005. X          regc('\0');
  1006. X          if (*regparse != ']') FAIL("unmatched []");
  1007. X          regparse++;
  1008. X          *flagp |= HASWIDTH|SIMPLE;
  1009. X      }
  1010. X        break;
  1011. X      case '(':
  1012. X        ret = reg(1, &flags);
  1013. X        if (ret == 0) return 0;
  1014. X        *flagp |= flags&(HASWIDTH|SPSTART);
  1015. X        break;
  1016. X      case '\0':
  1017. X      case '|':
  1018. X      case ')':
  1019. X        FAIL("internal urp"); break; /* Supposed to be caught earlier. */
  1020. X      case '?':
  1021. X      case '+':
  1022. X      case '*':
  1023. X        FAIL("?+* follows nothing"); break;
  1024. X      case '\\':
  1025. X        if (*regparse == '\0') FAIL("trailing \\");
  1026. X        ret = regnode(EXACTLY);
  1027. X        regc(*regparse++);
  1028. X        regc('\0');
  1029. X        *flagp |= HASWIDTH|SIMPLE;
  1030. X        break;
  1031. X      default: {
  1032. X          regparse--;
  1033. X          int len = (int) strcspn(regparse, META);
  1034. X          if (len <= 0) FAIL("internal disaster");
  1035. X          char ender = *(regparse+len);
  1036. X          if (len > 1 && ISMULT(ender)) len--; // Back off clear of ?+* operand
  1037. X          *flagp |= HASWIDTH;
  1038. X          if (len == 1) *flagp |= SIMPLE;
  1039. X          ret = regnode(EXACTLY);
  1040. X          while (len > 0) { regc(*regparse++); len--; }
  1041. X          regc('\0');
  1042. X      }
  1043. X        break;
  1044. X    }
  1045. X    return ret;
  1046. X}
  1047. X
  1048. X/*
  1049. X** regpiece - something followed by possible \[*+?\]
  1050. X**
  1051. X** Note that the branching code sequences used for ? and the general cases
  1052. X** of * and + are somewhat optimized:  they use the same NOTHING node as
  1053. X** both the endmarker for their branch list and the body of the last branch.
  1054. X** It might seem that this node could be dispensed with entirely, but the
  1055. X** endmarker role is not redundant.
  1056. X*/
  1057. X
  1058. Xstatic char *regpiece(int *flagp)
  1059. X{
  1060. X    int flags;
  1061. X    char *ret = regatom(&flags);
  1062. X    if (ret == 0) return 0;
  1063. X    
  1064. X    char op = *regparse;
  1065. X    if (!ISMULT(op)) { *flagp = flags; return ret; }
  1066. X    
  1067. X    if (!(flags&HASWIDTH) && op != '?') FAIL("*+ operand could be empty");
  1068. X    *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
  1069. X
  1070. X    char *next;
  1071. X    if (op == '*' && (flags&SIMPLE))
  1072. X        reginsert(STAR, ret);
  1073. X    else if (op == '*') {
  1074. X        /* Emit x* as \(x&|\), where & means "self". */
  1075. X        reginsert(BRANCH, ret);            /* Either x */
  1076. X        regoptail(ret, regnode(BACK));        /* and loop */
  1077. X        regoptail(ret, ret);            /* back */
  1078. X        regtail(ret, regnode(BRANCH));        /* or */
  1079. X        regtail(ret, regnode(NOTHING));        /* null. */
  1080. X    } else if (op == '+' && (flags&SIMPLE))
  1081. X        reginsert(PLUS, ret);
  1082. X    else if (op == '+') {
  1083. X        /* Emit x+ as x\(&|\), where & means "self". */
  1084. X        next = regnode(BRANCH);            /* Either */
  1085. X        regtail(ret, next);
  1086. X        regtail(regnode(BACK), ret);        /* loop back */
  1087. X        regtail(next, regnode(BRANCH));        /* or */
  1088. X        regtail(ret, regnode(NOTHING));        /* null. */
  1089. X    } else if (op == '?') {
  1090. X        /* Emit x? as \(x|\) */
  1091. X        reginsert(BRANCH, ret);            /* Either x */
  1092. X        regtail(ret, regnode(BRANCH));        /* or */
  1093. X        next = regnode(NOTHING);        /* null. */
  1094. X        regtail(ret, next);
  1095. X        regoptail(ret, next);
  1096. X    }
  1097. X    regparse++;
  1098. X    if (ISMULT(*regparse)) FAIL("nested *?+");
  1099. X    return ret;
  1100. X}
  1101. X
  1102. X/*
  1103. X** regbranch - one alternative of an | operator
  1104. X**
  1105. X** Implements the concatenation operator.
  1106. X*/
  1107. X
  1108. Xstatic char *regbranch(int *flagp)
  1109. X{
  1110. X    *flagp = WORST; /* Tentatively. */
  1111. X    char *latest;
  1112. X    int flags;
  1113. X
  1114. X    char *ret = regnode(BRANCH);
  1115. X    char *chain = 0;
  1116. X    while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
  1117. X        latest = regpiece(&flags);
  1118. X        if (latest == 0) return 0;
  1119. X        *flagp |= flags&HASWIDTH;
  1120. X        if (chain == 0)    /* First piece. */
  1121. X            *flagp |= flags&SPSTART;
  1122. X        else
  1123. X            regtail(chain, latest);
  1124. X        chain = latest;
  1125. X    }
  1126. X    if (chain == 0) (void) regnode(NOTHING); /* Loop ran zero times. */
  1127. X    return ret;
  1128. X}
  1129. X
  1130. X/*
  1131. X** reg - regular expression, i.e. main body or parenthesized thing
  1132. X**
  1133. X** Caller must absorb opening parenthesis.
  1134. X**
  1135. X** Combining parenthesis handling with the base level of regular expression
  1136. X** is a trifle forced, but the need to tie the tails of the branches to what
  1137. X** follows makes it hard to avoid.
  1138. X*/
  1139. X
  1140. Xstatic char *reg(int paren, int *flagp)
  1141. X{
  1142. X    *flagp = HASWIDTH;    /* Tentatively. */
  1143. X    char *ret;
  1144. X    int parno;
  1145. X    
  1146. X    /* Make an OPEN node, if parenthesized. */
  1147. X    if (paren) {
  1148. X        if (regnpar >= NSUBEXP) FAIL("too many ()");
  1149. X        parno = regnpar;
  1150. X        regnpar++;
  1151. X        ret = regnode(OPEN+parno);
  1152. X    } else
  1153. X        ret = 0;
  1154. X    
  1155. X    /* Pick up the branches, linking them together. */
  1156. X    int flags;
  1157. X    char *br = regbranch(&flags);
  1158. X    if (br == 0) return(0);
  1159. X    if (ret != 0)
  1160. X        regtail(ret, br);    /* OPEN -> first. */
  1161. X    else
  1162. X        ret = br;
  1163. X    if (!(flags&HASWIDTH)) *flagp &= ~HASWIDTH;
  1164. X    *flagp |= flags&SPSTART;
  1165. X    while (*regparse == '|') {
  1166. X        regparse++;
  1167. X        br = regbranch(&flags);
  1168. X        if (br == 0) return 0;
  1169. X        regtail(ret, br);    /* BRANCH -> BRANCH. */
  1170. X        if (!(flags&HASWIDTH)) *flagp &= ~HASWIDTH;
  1171. X        *flagp |= flags&SPSTART;
  1172. X    }
  1173. X    
  1174. X    /* Make a closing node, and hook it on the end. */
  1175. X    char *ender = regnode((paren) ? CLOSE+parno : END);    
  1176. X    regtail(ret, ender);
  1177. X    
  1178. X    /* Hook the tails of the branches to the closing node. */
  1179. X    for (br = ret; br != 0; br = regnext(br)) regoptail(br, ender);
  1180. X    
  1181. X    /* Check for proper termination. */
  1182. X    if (paren && *regparse++ != ')') {
  1183. X        FAIL("unmatched ()");
  1184. X    } else if (!paren && *regparse != '\0') {
  1185. X        if (*regparse == ')') {
  1186. X            FAIL("unmatched ()");
  1187. X        } else
  1188. X            FAIL("junk on end");    /* "Can\'t happen". */
  1189. X        /* NOTREACHED */
  1190. X    }
  1191. X    return ret;
  1192. X}
  1193. X
  1194. X/*
  1195. X** regcomp - compile a regular expression into internal code
  1196. X**
  1197. X** We can\'t allocate space until we know how big the compiled form will be,
  1198. X** but we can\'t compile it \(and thus know how big it is\) until we\'ve got a
  1199. X** place to put the code.  So we cheat:  we compile it twice, once with code
  1200. X** generation turned off and size counting turned on, and once "for real".
  1201. X** This also means that we don\'t allocate space until we are sure that the
  1202. X** thing really will compile successfully, and we never have to move the
  1203. X** code and thus invalidate pointers into it.  \(Note that it has to be in
  1204. X** one piece because free\(\) must be able to free it all.\)
  1205. X**
  1206. X** Beware that the optimization-preparation code in here knows about some
  1207. X** of the structure of the compiled regexp.
  1208. X*/
  1209. X
  1210. Xregexp *regcomp(const char *exp)
  1211. X{
  1212. X    if (exp == 0) FAIL("0 argument");
  1213. X    
  1214. X    /* First pass: determine size, legality. */
  1215. X    regparse = exp;
  1216. X    regnpar = 1;
  1217. X    regsize = 0L;
  1218. X    regcode = ®dummy;
  1219. X    regc(MAGIC);
  1220. X
  1221. X    int flags;
  1222. X    if (reg(0, &flags) == 0) return 0;
  1223. X    
  1224. X    /* Small enough for pointer-storage convention? */
  1225. X    if (regsize >= 32767L) FAIL("regexp too big"); // Probably could be 65535L
  1226. X    
  1227. X    /* Allocate space. */
  1228. X    regexp *r = (regexp *) new char[sizeof(regexp) + (unsigned)regsize];
  1229. X    if (r == 0) FAIL("out of space");
  1230. X    
  1231. X    /* Second pass: emit code. */
  1232. X    regparse = exp;
  1233. X    regnpar  = 1;
  1234. X    regcode  = r->program;
  1235. X    regc(MAGIC);
  1236. X    if (reg(0, &flags) == 0) return 0;
  1237. X    
  1238. X    /* Dig out information for optimizations. */
  1239. X    r->regstart = '\0';    /* Worst-case defaults. */
  1240. X    r->reganch = 0;
  1241. X    r->regmust = 0;
  1242. X    r->regmlen = 0;
  1243. X    char *scan = r->program+1;         // First BRANCH.
  1244. X    if (OP(regnext(scan)) == END) {  // Only one top-level choice.
  1245. X        scan = OPERAND(scan);
  1246. X        /* Starting-point info. */
  1247. X        if (OP(scan) == EXACTLY)
  1248. X            r->regstart = *OPERAND(scan);
  1249. X        else if (OP(scan) == BOL)
  1250. X            r->reganch++;
  1251. X        /*
  1252. X         * If there\'s something expensive in the r.e., find the
  1253. X         * longest literal string that must appear and make it the
  1254. X         * regmust.  Resolve ties in favor of later strings, since
  1255. X         * the regstart check works with the beginning of the r.e.
  1256. X         * and avoiding duplication strengthens checking.  Not a
  1257. X         * strong reason, but sufficient in the absence of others.
  1258. X         */
  1259. X        char *longest;
  1260. X        int len;
  1261. X        if (flags&SPSTART) {
  1262. X            longest = 0;
  1263. X            len = 0;
  1264. X            for (; scan != 0; scan = regnext(scan))
  1265. X                if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
  1266. X                    longest = OPERAND(scan);
  1267. X                    len = (int) strlen(OPERAND(scan));
  1268. X                }
  1269. X            r->regmust = longest;
  1270. X            r->regmlen = len;
  1271. X        }
  1272. X    }
  1273. X    return r;
  1274. X}
  1275. X
  1276. X/*
  1277. X** regexec and friends
  1278. X*/
  1279. X
  1280. X/*
  1281. X** Global work variables for regexec\(\).
  1282. X*/
  1283. Xstatic char *reginput;        /* String-input pointer. */
  1284. Xstatic char *regbol;        /* Beginning of input, for ^ check. */
  1285. Xstatic char **regstartp;    /* Pointer to startp array. */
  1286. Xstatic char **regendp;        /* Ditto for endp. */
  1287. X
  1288. X#ifdef DEBUG
  1289. Xint regnarrate = 0;
  1290. Xvoid regdump();
  1291. Xstatic char *regprop();
  1292. X#endif
  1293. X
  1294. X/*
  1295. X** regrepeat - repeatedly match something simple, report how many
  1296. X*/
  1297. X
  1298. Xstatic int regrepeat(char *p)
  1299. X{
  1300. X    int count = 0;
  1301. X    char *scan = reginput;
  1302. X    char *opnd = OPERAND(p);
  1303. X    switch (OP(p)) {
  1304. X      case ANY:
  1305. X        count = (int) strlen(scan);
  1306. X        scan += count;
  1307. X        break;
  1308. X      case EXACTLY:
  1309. X        while (*opnd == *scan) { count++; scan++; }
  1310. X        break;
  1311. X      case ANYOF:
  1312. X        while (*scan != '\0' && strchr(opnd, *scan) != 0) {
  1313. X            count++;
  1314. X            scan++;
  1315. X        }
  1316. X        break;
  1317. X      case ANYBUT:
  1318. X        while (*scan != '\0' && strchr(opnd, *scan) == 0) {
  1319. X            count++;
  1320. X            scan++;
  1321. X        }
  1322. X        break;
  1323. X      default:        /* Oh dear.  Called inappropriately. */
  1324. X        REerror = "internal foulup";
  1325. X        count = 0;    /* Best compromise. */
  1326. X        break;
  1327. X    }
  1328. X    reginput = scan;
  1329. X    return count;
  1330. X}
  1331. X
  1332. X/*
  1333. X** regmatch - main matching routine
  1334. X**
  1335. X** Conceptually the strategy is simple:  check to see whether the current
  1336. X** node matches, call self recursively to see whether the rest matches,
  1337. X** and then act accordingly.  In practice we make some effort to avoid
  1338. X** recursion, in particular by going through "ordinary" nodes \(that don\'t
  1339. X** need to know whether the rest of the match failed\) by a loop instead of
  1340. X** by recursion.
  1341. X**
  1342. X** 0 failure, 1 success
  1343. X*/
  1344. X
  1345. Xstatic int regmatch(char *prog)
  1346. X{
  1347. X    char *scan = prog;    /* Current node. */
  1348. X    char *next;        /* Next node. */
  1349. X    
  1350. X#ifdef DEBUG
  1351. X    if (scan != 0 && regnarrate) fprintf(stderr, "%s(\n", regprop(scan));
  1352. X#endif
  1353. X    while (scan != 0) {
  1354. X#ifdef DEBUG
  1355. X        if (regnarrate) fprintf(stderr, "%s...\n", regprop(scan));
  1356. X#endif
  1357. X        next = regnext(scan);
  1358. X        
  1359. X        switch (OP(scan)) {
  1360. X          case BOL: if (reginput != regbol) return 0; break;
  1361. X          case EOL: if (*reginput != '\0') return 0; break;
  1362. X          case ANY: if (*reginput == '\0') return(0); reginput++; break;
  1363. X          case EXACTLY: {
  1364. X              char *opnd = OPERAND(scan);
  1365. X              /* Inline the first character, for speed. */
  1366. X              if (*opnd != *reginput) return 0;
  1367. X              int len = (int) strlen(opnd);
  1368. X              if (len > 1 && strncmp(opnd, reginput, len) != 0) return 0;
  1369. X              reginput += len;
  1370. X          }
  1371. X            break;
  1372. X          case ANYOF:
  1373. X            if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == 0)
  1374. X                return 0;
  1375. X            reginput++;
  1376. X            break;
  1377. X          case ANYBUT:
  1378. X            if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != 0)
  1379. X                return 0;
  1380. X            reginput++;
  1381. X            break;
  1382. X          case NOTHING: break;
  1383. X          case BACK: break;
  1384. X          case OPEN+1:
  1385. X          case OPEN+2:
  1386. X          case OPEN+3:
  1387. X          case OPEN+4:
  1388. X          case OPEN+5:
  1389. X          case OPEN+6:
  1390. X          case OPEN+7:
  1391. X          case OPEN+8:
  1392. X          case OPEN+9: {
  1393. X              int no     = OP(scan) - OPEN;
  1394. X              char *save = reginput;
  1395. X              if (regmatch(next)) {
  1396. X                  /*
  1397. X                  ** Don\'t set startp if some later invocation of the same
  1398. X                  ** parentheses already has.
  1399. X                  */
  1400. X                  if (regstartp[no] == 0) regstartp[no] = save; return 1;
  1401. X              } else
  1402. X                  return 0;
  1403. X          }
  1404. X          case CLOSE+1:
  1405. X          case CLOSE+2:
  1406. X          case CLOSE+3:
  1407. X          case CLOSE+4:
  1408. X          case CLOSE+5:
  1409. X          case CLOSE+6:
  1410. X          case CLOSE+7:
  1411. X          case CLOSE+8:
  1412. X          case CLOSE+9: {
  1413. X              int no     = OP(scan) - CLOSE;
  1414. X              char *save = reginput;
  1415. X              if (regmatch(next)) {
  1416. X                  /*
  1417. X                  ** Don\'t set endp if some later invocation of the same
  1418. X                  ** parentheses already has.
  1419. X                  */
  1420. X                  if (regendp[no] == 0) regendp[no] = save;
  1421. X                  return 1;
  1422. X              } else
  1423. X                  return 0;
  1424. X          }
  1425. X          case BRANCH: {
  1426. X              char *save;
  1427. X              if (OP(next) != BRANCH)        /* No choice. */
  1428. X                  next = OPERAND(scan);    /* Avoid recursion. */
  1429. X              else {
  1430. X                  do {
  1431. X                      save = reginput;
  1432. X                      if (regmatch(OPERAND(scan))) return(1);
  1433. X                      reginput = save;
  1434. X                      scan = regnext(scan);
  1435. X                  } while (scan != 0 && OP(scan) == BRANCH);
  1436. X                  return 0;
  1437. X              }
  1438. X          }
  1439. X            break;
  1440. X          case STAR:
  1441. X          case PLUS: {
  1442. X              /*
  1443. X              ** Lookahead to avoid useless match attempts
  1444. X              ** when we know what character comes next.
  1445. X              */
  1446. X              char nextch = '\0';
  1447. X              if (OP(next) == EXACTLY) nextch = *OPERAND(next);
  1448. X              int min = (OP(scan) == STAR) ? 0 : 1;
  1449. X              char *save = reginput;
  1450. X              int no = regrepeat(OPERAND(scan));
  1451. X              while (no >= min) {
  1452. X                  /* If it could work, try it. */
  1453. X                  if (nextch == '\0' || *reginput == nextch)
  1454. X                      if (regmatch(next)) return(1);
  1455. X                  /* Couldn\'t or didn\'t -- back up. */
  1456. X                  no--;
  1457. X                  reginput = save + no;
  1458. X              }
  1459. X              return 0;
  1460. X          }
  1461. X          case END: return 1;    /* Success! */
  1462. X          default: REerror = "memory corruption"; return 0;
  1463. X        }
  1464. X        scan = next;
  1465. X    }
  1466. X    
  1467. X    /*
  1468. X     * We get here only if there\'s trouble -- normally "case END" is
  1469. X     * the terminating point.
  1470. X     */
  1471. X    REerror = "corrupted pointers";
  1472. X    return 0;
  1473. X}
  1474. X
  1475. X/*
  1476. X** regtry - try match at specific point
  1477. X**
  1478. X** 0 failure, 1 success
  1479. X*/
  1480. X
  1481. Xstatic int regtry(regexp *prog, char *string)
  1482. X{
  1483. X    reginput  = string;
  1484. X    regstartp = prog->startp;
  1485. X    regendp   = prog->endp;
  1486. X    
  1487. X    char **sp = prog->startp;
  1488. X    char **ep = prog->endp;
  1489. X    for (int i = NSUBEXP; i > 0; i--) { *sp++ = 0; *ep++ = 0; }
  1490. X    if (regmatch(prog->program + 1)) {
  1491. X        prog->startp[0] = string;
  1492. X        prog->endp[0] = reginput;
  1493. X        return 1;
  1494. X    } else
  1495. X        return 0;
  1496. X}
  1497. X
  1498. X/*
  1499. X**  - regexec - match a regexp against a string
  1500. X**/
  1501. X
  1502. Xint regexec(regexp *prog, char *string)
  1503. X{
  1504. X    char *s;
  1505. X    
  1506. X    /* Be paranoid... */
  1507. X    if (prog == 0 || string == 0) { REerror = "0 parameter"; return 0; }
  1508. X    
  1509. X    /* Check validity of program. */
  1510. X    if (UCHARAT(prog->program) != MAGIC) {
  1511. X        REerror = "corrupted program";
  1512. X        return 0;
  1513. X    }
  1514. X    
  1515. X    /* If there is a "must appear" string, look for it. */
  1516. X    if (prog->regmust != 0) {
  1517. X        s = string;
  1518. X        while ((s = strchr(s, prog->regmust[0])) != 0) {
  1519. X            if (strncmp(s, prog->regmust, prog->regmlen) == 0) break; // Found
  1520. X            s++;
  1521. X        }
  1522. X        if (s == 0) return(0); /* Not present. */
  1523. X    }
  1524. X    
  1525. X    /* Mark beginning of line for ^ . */
  1526. X    regbol = string;
  1527. X    
  1528. X    /* Simplest case:  anchored match need be tried only once. */
  1529. X    if (prog->reganch) return(regtry(prog, string));
  1530. X    
  1531. X    /* Messy cases:  unanchored match. */
  1532. X    s = string;
  1533. X    if (prog->regstart != '\0')
  1534. X        /* We know what char it must start with. */
  1535. X        while ((s = strchr(s, prog->regstart)) != 0) {
  1536. X            if (regtry(prog, s)) return 1;
  1537. X            s++;
  1538. X        }
  1539. X    else
  1540. X        /* We don\'t -- general case. */
  1541. X        do {
  1542. X            if (regtry(prog, s)) return(1);
  1543. X        } while (*s++ != '\0');
  1544. X    
  1545. X    /* Failure. */
  1546. X    return 0;
  1547. X}
  1548. X
  1549. X#ifdef TEST
  1550. Xint main()
  1551. X{
  1552. X     char *str = "do";
  1553. X     char *test1 = "do it";
  1554. X     char *test2 = "dog it";
  1555. X     char *test3 = "don't do it";
  1556. X    regexp *rxp = regcomp(str);
  1557. X    if (!rxp) printf("regcomp() failed on %s\n", str);
  1558. X    if (regexec(rxp, test1)) printf("matched %s\n", test1);
  1559. X    if (regexec(rxp, test2)) printf("matched %s\n", test2);
  1560. X    if (regexec(rxp, test3)) printf("matched %s\n", test3);
  1561. X}
  1562. X#endif
  1563. END_OF_FILE
  1564. if test 26725 -ne `wc -c <'regexp.C'`; then
  1565.     echo shar: \"'regexp.C'\" unpacked with wrong size!
  1566. fi
  1567. # end of 'regexp.C'
  1568. fi
  1569. echo shar: End of archive 3 \(of 7\).
  1570. cp /dev/null ark3isdone
  1571. MISSING=""
  1572. for I in 1 2 3 4 5 6 7 ; do
  1573.     if test ! -f ark${I}isdone ; then
  1574.     MISSING="${MISSING} ${I}"
  1575.     fi
  1576. done
  1577. if test "${MISSING}" = "" ; then
  1578.     echo You have unpacked all 7 archives.
  1579.     rm -f ark[1-9]isdone
  1580. else
  1581.     echo You still need to unpack the following archives:
  1582.     echo "        " ${MISSING}
  1583. fi
  1584. ##  End of shell archive.
  1585. exit 0
  1586.  
  1587. exit 0 # Just in case...
  1588.