home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume33 / problem / part03 < prev    next >
Encoding:
Text File  |  1992-10-18  |  46.7 KB  |  1,576 lines

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