home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #26 / NN_1992_26.iso / spool / comp / sources / misc / 4083 < prev    next >
Encoding:
Text File  |  1992-11-12  |  47.2 KB  |  1,595 lines

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