home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #16 / NN_1992_16.iso / spool / comp / editors / 1832 < prev    next >
Encoding:
Internet Message Format  |  1992-07-28  |  57.2 KB

  1. Path: sparky!uunet!usc!elroy.jpl.nasa.gov!swrinde!mips!darwin.sura.net!Sirius.dfn.de!math.fu-berlin.de!wolff
  2. From: wolff@inf.fu-berlin.de (Thomas Wolff)
  3. Newsgroups: comp.editors
  4. Subject: Editor mined (3/4)
  5. Message-ID: <52I5VTD@math.fu-berlin.de>
  6. Date: 28 Jul 92 15:03:50 GMT
  7. Sender: news@math.fu-berlin.de (Math Department)
  8. Organization: Free University of Berlin, Germany
  9. Lines: 2194
  10.  
  11. #! /bin/sh
  12. : This is a sharchive -- extract the files by running through sh
  13.  
  14. echo ---------------------- extracting mined2.c -----------------
  15. sed 's/^,//' << \EOSED > mined2.c
  16. ,/*  ==================================================================    *
  17. , *                Editor mined                *
  18. , *                Part 2                    *
  19. , *  ==================================================================    */
  20. ,
  21. ,#include "mined.h"
  22. ,
  23. ,/*  ==================================================================    *
  24. , *            Definitions specific for mined2.c        *
  25. , *  ==================================================================    */
  26. ,
  27. ,#ifdef vms
  28. ,#define copycommand "copy %s %s"
  29. ,#endif
  30. ,#ifdef msdos
  31. ,#define copycommand "copy %s %s > nul:"
  32. ,#endif
  33. ,
  34. ,/*  ==================================================================    *
  35. , *                Move Commands                *
  36. , *  ==================================================================    */
  37. ,
  38. ,/*
  39. , * Move one line up.
  40. , */
  41. ,MUP ()
  42. ,{
  43. ,  if (hop_flag > 0) return HIGH ();
  44. ,  if (y == 0) {        /* Top line of screen. Scroll one line */
  45. ,    (void) reverse_scroll ();
  46. ,    set_cursor (0, YMAX);        /* Erase very bottom line */
  47. ,    clear_eol ();
  48. ,    move_y (y);
  49. ,  }
  50. ,  else            /* Move to previous line */
  51. ,    move_y (y - 1);
  52. ,}
  53. ,
  54. ,/*
  55. , * Move one line down.
  56. , */
  57. ,MDN ()
  58. ,{
  59. ,  if (hop_flag > 0) return LOW ();
  60. ,  if (y == last_y) {    /* Last line of screen. Scroll one line */
  61. ,    if (bot_line->next == tail && bot_line->text [0] != '\n') {
  62. ,    /*    dummy_line ();    don't create new empty line ! */
  63. ,    /*    MDN (); */
  64. ,        return;
  65. ,    }
  66. ,    else {
  67. ,        (void) forward_scroll ();
  68. ,        move_y (y);
  69. ,    }
  70. ,  }
  71. ,  else            /* Move to next line */
  72. ,    move_y (y + 1);
  73. ,}
  74. ,
  75. ,/*
  76. , * Move left one position.
  77. , */
  78. ,MLF ()
  79. ,{
  80. ,  if (hop_flag > 0) return BL ();
  81. ,  if (x == 0 && get_shift (cur_line->shift_count) == 0) {/* Begin of line */
  82. ,    if (cur_line->prev != header) {
  83. ,        MUP ();                    /* Move one line up */
  84. ,        move_to (LINE_END, y);
  85. ,    }
  86. ,  }
  87. ,  else
  88. ,    move_to (x - 1, y);
  89. ,}
  90. ,
  91. ,/*
  92. , * Move right one position.
  93. , */
  94. ,MRT ()
  95. ,{
  96. ,  if (hop_flag > 0) return EL ();
  97. ,  if (* cur_text == '\n') {
  98. ,    if (cur_line->next != tail) {        /* Last char of file */
  99. ,        MDN ();                /* Move one line down */
  100. ,        move_to (LINE_START, y);
  101. ,    }
  102. ,  }
  103. ,  else
  104. ,    move_to (x + 1, y);
  105. ,}
  106. ,
  107. ,/*
  108. , * Move to top of screen
  109. , */
  110. ,HIGH ()
  111. ,{
  112. ,  move_y (0);
  113. ,}
  114. ,
  115. ,/*
  116. , * Move to bottom of screen
  117. , */
  118. ,LOW ()
  119. ,{
  120. ,  move_y (last_y);
  121. ,}
  122. ,
  123. ,/*
  124. , * Move to begin of line.
  125. , */
  126. ,BL ()
  127. ,{
  128. ,  move_to (LINE_START, y);
  129. ,}
  130. ,
  131. ,/*
  132. , * Move to end of line.
  133. , */
  134. ,EL ()
  135. ,{
  136. ,  move_to (LINE_END, y);
  137. ,}
  138. ,
  139. ,/*
  140. , * GOTO () prompts for a linenumber and moves to that line.
  141. , */
  142. ,GOTO ()
  143. ,{
  144. ,  u_char c;
  145. ,  char end;
  146. ,  extern int (* key_map []) ();
  147. ,  int number;
  148. ,
  149. ,  if (! char_ready_within (500)) status_msg ("HOP ... command (fortified) or line number...");
  150. ,  if (quit == TRUE) return;
  151. ,  c = readchar ();
  152. ,  if (quit == TRUE) return;
  153. ,  if (('0' <= c) && (c <= '9')) {
  154. ,    end = get_number ("Please continue line number...", c, & number);
  155. ,    if (end == '%')
  156. ,        goproz (number);
  157. ,    else if (end != ERRORS)
  158. ,        goline (number);
  159. ,    return;
  160. ,  }
  161. ,  else {
  162. ,    clear_status ();
  163. ,    hop_flag = 1;
  164. ,    (* key_map [c]) (c);
  165. ,    return;
  166. ,  }
  167. ,}
  168. ,
  169. ,goline (number)
  170. ,  int number;
  171. ,{
  172. ,  LINE * line;
  173. ,  if (number <= 0 || (line = proceed (header->next, number - 1)) == tail)
  174. ,    error ("Illegal line number: ", num_out ((long) number));
  175. ,  else    {
  176. ,      clear_status ();
  177. ,      move_y (find_y (line));
  178. ,    }
  179. ,}
  180. ,
  181. ,goproz (number)
  182. ,  int number;
  183. ,{
  184. ,  goline (number * nlines / 100);
  185. ,}
  186. ,
  187. ,/*
  188. , * Scroll forward one page or to eof, whatever comes first. (Bot_line becomes
  189. , * top_line of display.) Try to leave the cursor on the same line. If this is
  190. , * not possible, leave cursor on the line halfway the page.
  191. , */
  192. ,PD ()
  193. ,{
  194. ,  register int i;
  195. ,
  196. ,  if (hop_flag > 0) {hop_flag = 0; return EF ();}
  197. ,  for (i = 0; i < SCREENMAX; i ++)
  198. ,    if (forward_scroll () == ERRORS)
  199. ,        break;            /* EOF reached */
  200. ,  if (y - i < 0)            /* Line no longer on screen */
  201. ,    move_y (SCREENMAX >> 1);
  202. ,  else
  203. ,    move_y (y - i);
  204. ,}
  205. ,
  206. ,/*
  207. , * Scroll backwards one page or to top of file, whatever comes first.
  208. , * (Top_line becomes bot_line of display).
  209. , * The very bottom line (YMAX) is always blank.
  210. , * Try to leave the cursor on the same line.
  211. , * If this is not possible, leave cursor on the line halfway the page.
  212. , */
  213. ,PU ()
  214. ,{
  215. ,  register int i;
  216. ,
  217. ,  if (hop_flag > 0) {hop_flag = 0; return HOME ();}
  218. ,  for (i = 0; i < SCREENMAX; i ++)
  219. ,    if (reverse_scroll () == ERRORS)
  220. ,        break;            /* Top of file reached */
  221. ,  set_cursor (0, YMAX);            /* Erase very bottom line */
  222. ,  clear_eol ();
  223. ,  if (y + i > SCREENMAX)        /* line no longer on screen */
  224. ,    move_y (SCREENMAX >> 1);
  225. ,  else
  226. ,    move_y (y + i);
  227. ,}
  228. ,
  229. ,/*
  230. , * Go to top of file, scrolling if possible, else redrawing screen.
  231. , */
  232. ,HOME ()
  233. ,{
  234. ,  if (proceed (top_line, - SCREENMAX) == header)
  235. ,    PU ();            /* It fits. Let PU do it */
  236. ,  else {
  237. ,    reset (header->next, 0);/* Reset top_line, etc. */
  238. ,    RD ();            /* Display full page */
  239. ,  }
  240. ,  move_to (LINE_START, 0);
  241. ,}
  242. ,
  243. ,/*
  244. , * Go to last line of file, scrolling if possible, else redrawing screen
  245. , */
  246. ,EF ()
  247. ,{
  248. ,  if (tail->prev->text [0] != '\n')
  249. ,    /* dummy_line () */;
  250. ,  if (proceed (bot_line, SCREENMAX) == tail)
  251. ,    PD ();            /* It fits. Let PD do it */
  252. ,  else {
  253. ,    reset (proceed (tail->prev, - SCREENMAX), SCREENMAX);
  254. ,    RD ();            /* Display full page */
  255. ,  }
  256. ,  move_to (LINE_END /* not START (EF!) */, last_y);
  257. ,}
  258. ,
  259. ,/*
  260. , * Scroll one line up. Leave the cursor on the same line (if possible).
  261. , */
  262. ,SU ()
  263. ,{
  264. ,  register int i;
  265. ,
  266. ,  if (hop_flag > 0) {
  267. ,    hop_flag = 0;
  268. ,    for (i = 0; i < SCREENMAX >> 1; i ++) SU ();
  269. ,    return;
  270. ,  }
  271. ,
  272. ,  if (top_line->prev == header)    /* Top of file. Can't scroll */
  273. ,    return;
  274. ,  (void) reverse_scroll ();
  275. ,  set_cursor (0, YMAX);        /* Erase very bottom line */
  276. ,  clear_eol ();
  277. ,  move_y ((y == SCREENMAX) ? SCREENMAX : y + 1);
  278. ,}
  279. ,
  280. ,/*
  281. , * Scroll one line down. Leave the cursor on the same line (if possible).
  282. , */
  283. ,SD ()
  284. ,{
  285. ,  register int i;
  286. ,
  287. ,  if (hop_flag > 0) {
  288. ,    hop_flag = 0;
  289. ,    for (i = 0; i < SCREENMAX >> 1; i ++) SD ();
  290. ,    return;
  291. ,  }
  292. ,
  293. ,  if (forward_scroll () != ERRORS)
  294. ,    move_y ((y == 0) ? 0 : y - 1);
  295. ,  else
  296. ,    set_cursor (x, y);
  297. ,}
  298. ,
  299. ,/*
  300. , * Perform a forward scroll. It returns ERRORS if we're at the last line of
  301. , * the file.
  302. , */
  303. ,forward_scroll ()
  304. ,{
  305. ,  if (bot_line->next == tail)        /* Last line of file. No dice */
  306. ,    return ERRORS;
  307. ,  top_line = top_line->next;
  308. ,  bot_line = bot_line->next;
  309. ,  cur_line = cur_line->next;
  310. ,  scroll_forward ();
  311. ,  set_cursor (0, SCREENMAX);
  312. ,  line_print (bot_line);
  313. ,
  314. ,  return FINE;
  315. ,}
  316. ,
  317. ,/*
  318. , * Perform a backwards scroll. It returns ERRORS if we're at the first line
  319. , * of the file.
  320. , */
  321. ,reverse_scroll ()
  322. ,{
  323. ,  if (top_line->prev == header)
  324. ,    return ERRORS;        /* Top of file. Can't scroll */
  325. ,
  326. ,  if (last_y != SCREENMAX)    /* Reset last_y if necessary */
  327. ,    last_y ++;
  328. ,  else
  329. ,    bot_line = bot_line->prev;    /* Else adjust bot_line */
  330. ,  top_line = top_line->prev;
  331. ,  cur_line = cur_line->prev;
  332. ,
  333. ,/* Perform the scroll */
  334. ,  set_cursor (0, 0);
  335. ,  scroll_reverse ();
  336. ,  set_cursor (0, 0);
  337. ,  line_print (top_line);
  338. ,
  339. ,  return FINE;
  340. ,}
  341. ,
  342. ,/*----------------------*
  343. , *    Word moves    *
  344. , *----------------------*/
  345. ,/*
  346. , * Word definitions
  347. , */
  348. ,#define white_space(c)    ((c) == ' ' || (c) == '\t')
  349. ,#define alpha(c)    ((c) != ' ' && (c) != '\t' && (c) != '\n')
  350. ,/*
  351. , * A word was previously defined as a number of non-blank characters
  352. , * separated by tabs, spaces or linefeeds.
  353. , * By consulting idfchar (), sequences of real letters only or digits
  354. , * or underlines are recognized as words.
  355. , */
  356. ,int
  357. ,idfchar (ch)
  358. ,  u_char ch;
  359. ,{
  360. ,  return ('0' <= ch && ch <= '9') ||
  361. ,     ('A' <= ch && ch <= 'Z') ||
  362. ,     ('a' <= ch && ch <= 'z') ||
  363. ,#ifdef msdos
  364. ,     ((u_char) '\200' <= ch) ||    /* might be more specific */
  365. ,#else
  366. ,     ((u_char) '\300' <= ch && ch != (u_char) 'W' && ch != (u_char) 'w') ||
  367. ,     (ch == (u_char) '5') ||
  368. ,#endif
  369. ,     (ch == '_');
  370. ,}
  371. ,
  372. ,/*
  373. , * MPW () moves to the start of the previous word. A word is defined as a
  374. , * number of non-blank characters separated by tabs spaces or linefeeds.
  375. , */
  376. ,MPW ()
  377. ,{
  378. ,  if (hop_flag > 0) return BSEN ();
  379. ,  move_previous_word (NO_DELETE);
  380. ,}
  381. ,
  382. ,move_previous_word (remove)
  383. ,  FLAG remove;
  384. ,{
  385. ,  register char * begin_line;
  386. ,  register char * textp;
  387. ,  char start_char = * cur_text;
  388. ,  char * start_pos = cur_text;
  389. ,  FLAG idfsearch;
  390. ,
  391. ,  if ((remove == DELETE) && (viewonly == TRUE))
  392. ,    return viewonlyerr ();
  393. ,
  394. ,/* First check if we're at the beginning of line. */
  395. ,  if (cur_text == cur_line->text) {
  396. ,    if (cur_line->prev == header)
  397. ,        return;
  398. ,    start_char = '\0';
  399. ,  }
  400. ,
  401. ,  MLF ();
  402. ,
  403. ,  begin_line = cur_line->text;
  404. ,  textp = cur_text;
  405. ,
  406. ,/* Check if we're in the middle of a word. */
  407. ,  if (!alpha (* textp) || !alpha (start_char)) {
  408. ,    while (textp != begin_line && (white_space (* textp) || * textp == '\n'))
  409. ,        textp --;
  410. ,  }
  411. ,
  412. ,/* Now we're at the end of previous word. Skip non-blanks until a blank comes */
  413. ,  if (idfchar (* textp)) {
  414. ,    idfsearch = TRUE;
  415. ,    while (textp != begin_line && idfchar (* textp))
  416. ,        textp --;
  417. ,  }
  418. ,  else {
  419. ,    idfsearch = FALSE;
  420. ,    while (textp != begin_line && alpha (* textp) && !idfchar (* textp))
  421. ,        textp --;
  422. ,  }
  423. ,
  424. ,/* Go to the next char if we're not at the beginning of the line */
  425. ,/* At the beginning of the line, check whether to stay or to go to the word */
  426. ,  if (textp != begin_line && * textp != '\n')
  427. ,    textp ++;
  428. ,  else if (textp == begin_line &&
  429. ,       (idfsearch == TRUE) ? !idfchar (* textp) /* : white_space (* t.) */
  430. ,            : (!alpha (* textp) || idfchar (* textp))) {
  431. ,    textp ++;
  432. ,    if (white_space (* textp) || textp == start_pos)
  433. ,        /* no word there or not moved, so go back */
  434. ,        textp --;
  435. ,  }
  436. ,
  437. ,/* Find the x-coordinate of this address, and move to it */
  438. ,  move_address (textp, y);
  439. ,  if (remove == DELETE)
  440. ,    delete_text (cur_line, textp, cur_line, start_pos);
  441. ,}
  442. ,
  443. ,/*
  444. , * MNW () moves to the start of the next word. A word is defined as a number of
  445. , * non-blank characters separated by tabs spaces or linefeeds. Always keep in
  446. , * mind that the pointer shouldn't pass the '\n'.
  447. , */
  448. ,MNW ()
  449. ,{
  450. ,  if (hop_flag > 0) return ESEN ();
  451. ,  move_next_word (NO_DELETE);
  452. ,}
  453. ,
  454. ,move_next_word (remove)
  455. ,  FLAG remove;
  456. ,{
  457. ,  register char * textp = cur_text;
  458. ,
  459. ,  if ((remove == DELETE) && (viewonly == TRUE))
  460. ,    return viewonlyerr ();
  461. ,
  462. ,/* Move to the end of the current word. */
  463. ,  if (idfchar (* textp))
  464. ,    while (* textp != '\n' && idfchar (* textp))
  465. ,    textp ++;
  466. ,  else
  467. ,    while (alpha (* textp) && !idfchar (* textp))
  468. ,    textp ++;
  469. ,
  470. ,/* Skip all white spaces */
  471. ,  while (* textp != '\n' && white_space (* textp))
  472. ,    textp ++;
  473. ,/* If we're deleting, delete the text in between */
  474. ,  if (remove == DELETE) {
  475. ,    delete_text (cur_line, cur_text, cur_line, textp);
  476. ,    return;
  477. ,  }
  478. ,
  479. ,/* If we're at end of line, move to the beginning of (first word on) the next line */
  480. ,  if (* textp == '\n' && cur_line->next != tail) {
  481. ,    MDN ();
  482. ,    move_to (LINE_START, y);
  483. ,    textp = cur_text;
  484. ,/*    while (* textp != '\n' && white_space (* textp))
  485. ,/*        textp ++;    /**/
  486. ,  }
  487. ,  move_address (textp, y);
  488. ,}
  489. ,
  490. ,/*
  491. , * BSEN () and ESEN () look for the beginning or end of the current sentence.
  492. , */
  493. ,BSEN ()
  494. ,{
  495. ,  search_for ("[;.]", REVERSE);
  496. ,}
  497. ,
  498. ,ESEN ()
  499. ,{
  500. ,  search_for ("[;.]", FORWARD);
  501. ,}
  502. ,
  503. ,/*
  504. , * find_y () checks if the matched line is on the current page. If it is, it
  505. , * returns the new y coordinate, else it displays the correct page with the
  506. , * matched line in the middle and returns the new y value;
  507. , */
  508. ,find_y (match_line)
  509. ,  LINE * match_line;
  510. ,{
  511. ,  return find_y_RD (match_line, TRUE);
  512. ,}
  513. ,
  514. ,find_y_w_o_RD (match_line)
  515. ,  LINE * match_line;
  516. ,{
  517. ,  return find_y_RD (match_line, FALSE);
  518. ,}
  519. ,
  520. ,find_y_RD (match_line, redrawflag)
  521. ,  LINE * match_line;
  522. ,  FLAG redrawflag;
  523. ,{
  524. ,  register LINE * line;
  525. ,  register int count = 0;
  526. ,
  527. ,/* Check if match_line is on the same page as currently displayed. */
  528. ,  for (line = top_line; line != match_line && line != bot_line->next;
  529. ,                              line = line->next)
  530. ,    count ++;
  531. ,  if (line != bot_line->next)
  532. ,    return count;
  533. ,
  534. ,/* Display new page, with match_line in center. */
  535. ,  if ((line = proceed (match_line, - (SCREENMAX >> 1))) == header) {
  536. ,  /* Can't display in the middle. Make first line of file top_line */
  537. ,    count = 0;
  538. ,    for (line = header->next; line != match_line; line = line->next)
  539. ,        count ++;
  540. ,    line = header->next;
  541. ,  }
  542. ,  else    /* New page is displayed. Set cursor to middle of page */
  543. ,    count = SCREENMAX >> 1;
  544. ,
  545. ,/* Reset pointers and redraw the screen */
  546. ,  reset (line, 0);
  547. ,  if (redrawflag == TRUE) RD ();
  548. ,
  549. ,  return count;
  550. ,}
  551. ,
  552. ,/*
  553. , * Dummy_line () adds an empty line at the end of the file. This is sometimes
  554. , * useful in combination with the EF and MDN command in combination with the
  555. , * Yank command set.
  556. , * !!! I see no use for this and I don't consider such autonomous 
  557. , * !!! modifications of the text (without user request) acceptable. TW.
  558. , */
  559. ,#ifdef UNUSED
  560. ,dummy_line ()
  561. ,{
  562. ,    (void) line_insert (tail->prev, "\n", 1);
  563. ,    tail->prev->shift_count = DUMMY;
  564. ,    if (last_y != SCREENMAX) {
  565. ,        last_y ++;
  566. ,        bot_line = bot_line->next;
  567. ,    }
  568. ,}
  569. ,#endif UNUSED
  570. ,
  571. ,/*  ==================================================================    *
  572. , *                Modify Commands                *
  573. , *  ==================================================================    */
  574. ,
  575. ,/*
  576. , * viewonlyerr () outputs an error message with a beep
  577. , */
  578. ,viewonlyerr ()
  579. ,{
  580. ,  ring_bell ();
  581. ,  error ("View only mode", NIL_PTR);
  582. ,  return ERRORS;
  583. ,}
  584. ,
  585. ,/*
  586. , * DCC deletes the character under the cursor. If this character is a '\n' the
  587. , * current line is joined with the next one.
  588. , * If this character is the only character of the line, the current line will
  589. , * be deleted.
  590. , */
  591. ,DCC ()
  592. ,{
  593. ,  if (* cur_text == '\n')
  594. ,    if (cur_line->next == tail)
  595. ,       return;
  596. ,    else
  597. ,       delete_text (cur_line, cur_text, cur_line->next, cur_line->next->text);
  598. ,  else
  599. ,    delete_text (cur_line, cur_text, cur_line, cur_text + 1);
  600. ,}
  601. ,
  602. ,/*
  603. , * DPC deletes the character on the left side of the cursor.  If the cursor
  604. , * is at the beginning of the line, the last character if the previous line
  605. , * is deleted.
  606. , */
  607. ,DPC ()
  608. ,{
  609. ,  hop_flag = 0;
  610. ,
  611. ,  if (x == 0 && cur_line->prev == header)
  612. ,    return;            /* Top of file */
  613. ,
  614. ,  if (viewonly == TRUE)
  615. ,    return viewonlyerr ();
  616. ,
  617. ,  MLF ();            /* Move one left */
  618. ,  DCC ();            /* Delete character under cursor */
  619. ,}
  620. ,
  621. ,/*
  622. , * DLN deletes all characters until the end of the line. If the current
  623. , * character is a '\n', then delete that char.
  624. , */
  625. ,DLN ()
  626. ,{
  627. ,  if (* cur_text == '\n')
  628. ,    DCC ();
  629. ,  else
  630. ,    delete_text (cur_line, cur_text, cur_line, cur_text + length_of (cur_text) - 1);
  631. ,}
  632. ,
  633. ,/*
  634. , * DNW () deletes the next word (as defined in MNW ())
  635. , */
  636. ,DNW ()
  637. ,{
  638. ,  if (* cur_text == '\n')
  639. ,    DCC ();
  640. ,  else
  641. ,    move_next_word (DELETE);
  642. ,}
  643. ,
  644. ,/*
  645. , * DPW () deletes the previous word (as defined in MPW ())
  646. , */
  647. ,DPW ()
  648. ,{
  649. ,  if (cur_text == cur_line->text)
  650. ,    DPC ();
  651. ,  else
  652. ,    move_previous_word (DELETE);
  653. ,}
  654. ,
  655. ,/*
  656. , * Insert character `character' at current location.
  657. , */
  658. ,SNL ()
  659. ,{
  660. ,  S ('\n');
  661. ,}
  662. ,S (character)
  663. ,  register char character;
  664. ,{
  665. ,  static char buffer [2];
  666. ,
  667. ,  buffer [0] = character;
  668. ,/* Insert the character */
  669. ,  if (insert (cur_line, cur_text, buffer) == ERRORS)
  670. ,    return;
  671. ,
  672. ,/* Fix screen */
  673. ,  if (character == '\n') {
  674. ,    set_cursor (0, y);
  675. ,    if (y == SCREENMAX) {        /* Can't use display () */
  676. ,        line_print (cur_line);
  677. ,        (void) forward_scroll ();
  678. ,    }
  679. ,    else {
  680. ,        reset (top_line, y);    /* Reset pointers */
  681. ,        if (can_add_line == TRUE) {
  682. ,            add_line (y + 1);
  683. ,            clear_status ();
  684. ,            display (0, y, cur_line, 1);
  685. ,        }
  686. ,        else    display (0, y, cur_line, last_y - y);
  687. ,    }
  688. ,    move_to (0, (y == SCREENMAX) ? y : y + 1);
  689. ,  }
  690. ,  else if (x + 1 == XBREAK) /* If line must be shifted, just call move_to */
  691. ,    move_to (x + 1, y);
  692. ,  else {            /* else display rest of line */
  693. ,    put_line (cur_line, x, FALSE);
  694. ,    move_to (x + 1, y);
  695. ,  }
  696. ,}
  697. ,
  698. ,/*
  699. , * CTRl inserts a control-char at the current location. A message that this
  700. , * function is called is displayed at the status line.
  701. , */
  702. ,CTRl ()
  703. ,{
  704. ,  register u_char ctrl;
  705. ,
  706. ,  status_msg ("Enter control character...");
  707. ,  ctrl = readchar ();
  708. ,  clear_status ();
  709. ,  if ((ctrl == '\177') || (ctrl == '?')) return S ('\177');
  710. ,  ctrl = ctrl & '\237';
  711. ,  if (ctrl == '\0') error ("Can't handle NULL char - not inserted", NIL_PTR);
  712. ,  else S (ctrl);
  713. ,}
  714. ,
  715. ,/*
  716. , * LIB insert a line at the current position and moves back to the end of
  717. , * the previous line.
  718. , */
  719. ,LIB ()
  720. ,{
  721. ,  hop_flag = 0;
  722. ,
  723. ,  if (viewonly == TRUE)
  724. ,    return viewonlyerr ();
  725. ,
  726. ,  S ('\n');            /* Insert the line */
  727. ,  MUP ();            /* Move one line up */
  728. ,  move_to (LINE_END, y);    /* Move to end of this line */
  729. ,}
  730. ,
  731. ,/*
  732. , * Install_line installs the buffer into a LINE structure it returns
  733. , * a pointer to the allocated structure.
  734. , */
  735. ,LINE *
  736. ,install_line (buffer, length)
  737. ,  char * buffer;
  738. ,  int length;
  739. ,{
  740. ,  register LINE * new_line = (LINE *) alloc (sizeof (LINE));
  741. ,
  742. ,  new_line->text = alloc (length + 1);
  743. ,  new_line->shift_count = 0;
  744. ,  copy_string (new_line->text, buffer);
  745. ,
  746. ,  return new_line;
  747. ,}
  748. ,
  749. ,/*
  750. , * Line_insert () inserts a new line with text pointed to by `string'.
  751. , * It returns the address of the new line.
  752. , */
  753. ,LINE *
  754. ,line_insert (line, string, len)
  755. ,  register LINE * line;
  756. ,  char * string;
  757. ,  int len;
  758. ,{
  759. ,  register LINE * new_line;
  760. ,
  761. ,/* Allocate space for LINE structure and text */
  762. ,  new_line = install_line (string, len);
  763. ,
  764. ,/* Install the line into the double linked list */
  765. ,  new_line->prev = line;
  766. ,  new_line->next = line->next;
  767. ,  line->next = new_line;
  768. ,  new_line->next->prev = new_line;
  769. ,
  770. ,/* Increment nlines */
  771. ,  nlines ++;
  772. ,
  773. ,  return new_line;
  774. ,}
  775. ,
  776. ,/*
  777. , * Insert () insert the string `string' at the given line and location.
  778. , */
  779. ,insert (line, location, string)
  780. ,  register LINE * line;
  781. ,  char * location, * string;
  782. ,{
  783. ,  register char * bufp = text_buffer;    /* Buffer for building line */
  784. ,  register char * textp = line->text;
  785. ,
  786. ,  if (viewonly == TRUE)
  787. ,    return viewonlyerr ();
  788. ,
  789. ,  if (length_of (textp) + length_of (string) >= MAX_CHARS) {
  790. ,    error ("Line too long", NIL_PTR);
  791. ,    return ERRORS;
  792. ,  }
  793. ,
  794. ,  modified = TRUE;            /* File has been modified */
  795. ,
  796. ,/* Copy part of line until `location' has been reached */
  797. ,  while (textp != location)
  798. ,    * bufp ++ = * textp ++;
  799. ,
  800. ,/* Insert string at this location */
  801. ,  while (* string != '\0')
  802. ,    * bufp ++ = * string ++;
  803. ,  * bufp = '\0';
  804. ,
  805. ,  if (* (string - 1) == '\n')        /* Insert a new line */
  806. ,    (void) line_insert (line, location, length_of (location));
  807. ,  else                    /* Append last part of line */
  808. ,    copy_string (bufp, location);
  809. ,
  810. ,/* Install the new text in this line */
  811. ,  free_space (line->text);
  812. ,  line->text = alloc (length_of (text_buffer) + 1);
  813. ,  copy_string (line->text, text_buffer);
  814. ,
  815. ,  return FINE;
  816. ,}
  817. ,
  818. ,/*
  819. , * Line_delete () deletes the argument line out of the line list. The pointer
  820. , * to the next line is returned.
  821. , */
  822. ,LINE *
  823. ,line_delete (line)
  824. ,  register LINE * line;
  825. ,{
  826. ,  register LINE * next_line = line->next;
  827. ,
  828. ,/* Delete the line */
  829. ,  line->prev->next = line->next;
  830. ,  line->next->prev = line->prev;
  831. ,
  832. ,/* Free allocated space */
  833. ,  free_space (line->text);
  834. ,  free_space (line);
  835. ,
  836. ,/* Decrement nlines */
  837. ,  nlines --;
  838. ,
  839. ,  return next_line;
  840. ,}
  841. ,
  842. ,/*
  843. , * Delete () deletes all the characters (including newlines) between the
  844. , * startposition and endposition and fixes the screen accordingly. It
  845. , * returns the number of lines deleted.
  846. , */
  847. ,delete_text (start_line, start_textp, end_line, end_textp)
  848. ,  register LINE * start_line;
  849. ,  LINE * end_line;
  850. ,  char * start_textp, * end_textp;
  851. ,{
  852. ,  register char * textp = start_line->text;
  853. ,  register char * bufp = text_buffer;    /* Storage for new line->text */
  854. ,  LINE * line;
  855. ,  LINE * after_end = end_line->next;
  856. ,  int line_cnt = 0;            /* Nr of lines deleted */
  857. ,  int count = 0;
  858. ,  int shift = 0;            /* Used in shift calculation */
  859. ,  int nx = x;
  860. ,
  861. ,  if (viewonly == TRUE)
  862. ,    return viewonlyerr ();
  863. ,
  864. ,  modified = TRUE;            /* File has been modified */
  865. ,
  866. ,/* Set up new line. Copy first part of start line until start_position. */
  867. ,  while (textp < start_textp) {
  868. ,    * bufp ++ = * textp ++;
  869. ,    count ++;
  870. ,  }
  871. ,
  872. ,/* Check if line doesn't exceed MAX_CHARS */
  873. ,  if (count + length_of (end_textp) >= MAX_CHARS) {
  874. ,    error ("Line too long", NIL_PTR);
  875. ,    return;
  876. ,  }
  877. ,
  878. ,/* Copy last part of end_line if end_line is not tail */
  879. ,  copy_string (bufp, (end_textp != NIL_PTR) ? end_textp : "\n");
  880. ,
  881. ,/* Delete all lines between start and end_position (including end_line) */
  882. ,  line = start_line->next;
  883. ,  while (line != after_end && line != tail) {
  884. ,    /* Here, the original mined compared with end_line->next which has 
  885. ,       already been discarded when the comparison should become true.
  886. ,       This severe error remained undetected until I ported to MSDOS */
  887. ,    line = line_delete (line);
  888. ,    line_cnt ++;
  889. ,  }
  890. ,
  891. ,/* Check if last line of file should be deleted */
  892. ,  if (end_textp == NIL_PTR && length_of (start_line->text) == 1 && nlines > 1) {
  893. ,    start_line = start_line->prev;
  894. ,    (void) line_delete (start_line->next);
  895. ,    line_cnt ++;
  896. ,  }
  897. ,  else {    /* Install new text */
  898. ,    free_space (start_line->text);
  899. ,    start_line->text = alloc (length_of (text_buffer) + 1);
  900. ,    copy_string (start_line->text, text_buffer);
  901. ,  }
  902. ,
  903. ,/* Fix screen. First check if line is shifted. Perhaps we should shift it back */
  904. ,/* !!! This resulted in a positioning error when a line containing TABs
  905. ,/* !!! was shifted back. So better leave it.
  906. ,/*  if (get_shift (start_line->shift_count)) {
  907. ,/*    shift = (XBREAK - count_chars (start_line)) / SHIFT_SIZE;
  908. ,/*    if (shift > 0) {        /* Shift line `shift' back */
  909. ,/*        if (shift >= get_shift (start_line->shift_count))
  910. ,/*            start_line->shift_count = 0;
  911. ,/*        else
  912. ,/*            start_line->shift_count -= shift;
  913. ,/*        nx += shift * SHIFT_SIZE; /* Reset x value */
  914. ,/*    }
  915. ,/*  } */
  916. ,
  917. ,  if (line_cnt == 0) {        /* Check if only one line changed */
  918. ,    if (shift > 0) {    /* Reprint whole line */
  919. ,        set_cursor (0, y);
  920. ,        line_print (start_line);
  921. ,    }
  922. ,    else {            /* Just display last part of line */
  923. ,        set_cursor (x, y);
  924. ,        put_line (start_line, x, TRUE);
  925. ,    }
  926. ,    move_to (nx, y);       /* Reset cur_text */
  927. ,    return;
  928. ,  }
  929. ,
  930. ,  shift = last_y;       /* Save value */
  931. ,  reset (top_line, y);
  932. ,  if ((line_cnt <= SCREENMAX - y) && (can_delete_line == TRUE)) {
  933. ,    clear_status ();
  934. ,    display (0, y, start_line, 0);
  935. ,    line = proceed (start_line, SCREENMAX - y - line_cnt + 1);
  936. ,    while (line_cnt -- > 0) {
  937. ,        delete_line (y + 1);
  938. ,        if (line != tail) {
  939. ,            set_cursor (0, SCREENMAX);
  940. ,            line_print (line);
  941. ,            line = line->next;
  942. ,        }
  943. ,    }
  944. ,  }
  945. ,  else
  946. ,    display (0, y, start_line, shift - y);
  947. ,/*  move_to ((line_cnt == 1) ? nx : 0, y); */
  948. ,  move_to (nx, y);
  949. ,}
  950. ,
  951. ,/*  ==================================================================    *
  952. , *                Yank Commands                *
  953. , *  ==================================================================    */
  954. ,
  955. ,LINE * mark_line = NIL_LINE;        /* For marking position. */
  956. ,char * mark_text = NIL_PTR;
  957. ,int lines_saved;            /* Nr of lines in buffer */
  958. ,
  959. ,/*
  960. , * PT () inserts the buffer at the current location.
  961. , */
  962. ,PT ()
  963. ,{
  964. ,  register int fd;        /* File descriptor for buffer */
  965. ,
  966. ,  if (viewonly == TRUE)
  967. ,    return viewonlyerr ();
  968. ,
  969. ,  if (hop_flag > 0) {
  970. ,    if ((fd = open (yankie_file, O_RDONLY, 0)) < 0) {
  971. ,        error ("No inter window buffer present.", NIL_PTR);
  972. ,        return;
  973. ,    }
  974. ,  }
  975. ,  else if ((fd = scratch_file (READ, FALSE)) == ERRORS) {
  976. ,    error ("Buffer is empty.", NIL_PTR);
  977. ,    return;
  978. ,  }
  979. ,  /* Insert the buffer */
  980. ,/*    file_insert (fd, FALSE); /* could yield a positioning error */
  981. ,    file_insert (fd, TRUE);
  982. ,}
  983. ,
  984. ,/*
  985. , * INF () prompt for a filename and inserts the file at the current location
  986. , * in the file.
  987. , */
  988. ,INF ()
  989. ,{
  990. ,  register int fd;        /* File descriptor of file */
  991. ,  char name [maxLINE_LEN];    /* Buffer for file name */
  992. ,
  993. ,  if (viewonly == TRUE)
  994. ,    return viewonlyerr ();
  995. ,
  996. ,/* Get the file name */
  997. ,  if (get_file ("Get and insert file:", name) != FINE)
  998. ,    return;
  999. ,  clear_status ();
  1000. ,
  1001. ,  if ((fd = open (name, O_RDONLY, 0)) < 0)
  1002. ,    error ("Cannot open file: " /*, name */, serror ());
  1003. ,  else {    /* Insert the file */
  1004. ,    file_insert (fd, TRUE);    /* leave cursor at begin of insertion */
  1005. ,  }
  1006. ,}
  1007. ,
  1008. ,/*
  1009. , * File_insert () inserts the contents of an opened file (as given by
  1010. , * filedescriptor fd) at the current location.
  1011. , * After the insertion, if old_pos is TRUE, the cursor remains at the
  1012. , * start of the inserted text, if old_pos is FALSE, it is placed to
  1013. , * its end. If old_pos is FALSE, this works erroneously if the last line
  1014. , * inserted contains a TAB character!!
  1015. , */
  1016. ,file_insert (fd, old_pos)
  1017. ,  int fd;
  1018. ,  FLAG old_pos;
  1019. ,{
  1020. ,  char line_buffer [MAX_CHARS];        /* Buffer for next line */
  1021. ,  register LINE * line = cur_line;
  1022. ,  register int line_count = nlines;    /* Nr of lines inserted */
  1023. ,  LINE * page = cur_line;
  1024. ,  int ret = ERRORS;
  1025. ,
  1026. ,  get_line_err1 = NIL_PTR;
  1027. ,  get_line_err2 = NIL_PTR;
  1028. ,
  1029. ,/* Get the first piece of text (might be ended with a '\n') from fd */
  1030. ,  if (get_line (fd, line_buffer) == ERRORS)
  1031. ,    return;                /* Empty file */
  1032. ,
  1033. ,/* Insert this text at the current location */
  1034. ,  if (insert (line, cur_text, line_buffer) == ERRORS)
  1035. ,    return;
  1036. ,
  1037. ,/* Repeat getting lines (and inserting lines) until EOF is reached */
  1038. ,  while ((ret = get_line (fd, line_buffer)) != ERRORS && ret != NO_LINE)
  1039. ,    line = line_insert (line, line_buffer, ret);
  1040. ,
  1041. ,  if (ret == NO_LINE) {    /* Last line read not ended by a '\n' */
  1042. ,    line = line->next;
  1043. ,    (void) insert (line, line->text, line_buffer);
  1044. ,  }
  1045. ,
  1046. ,  (void) close (fd);
  1047. ,
  1048. ,/* If illegal lines were input, report */
  1049. ,  if ((get_line_err1 != NIL_PTR) || (get_line_err2 != NIL_PTR)) {
  1050. ,    ring_bell ();
  1051. ,    error (get_line_err1, get_line_err2);
  1052. ,    sleep (1);
  1053. ,  }
  1054. ,
  1055. ,/* Calculate nr of lines added */
  1056. ,  line_count = nlines - line_count;
  1057. ,
  1058. ,/* Fix the screen */
  1059. ,  if (line_count == 0) {        /* Only one line changed */
  1060. ,    set_cursor (0, y);
  1061. ,    line_print (line);
  1062. ,    move_to ((old_pos == TRUE) ? x : x + length_of (line_buffer), y);
  1063. ,  }
  1064. ,  else {                /* Several lines changed */
  1065. ,    reset (top_line, y);    /* Reset pointers */
  1066. ,    while (page != line && page != bot_line->next)
  1067. ,        page = page->next;
  1068. ,    if (page != bot_line->next || old_pos == TRUE)
  1069. ,        display (0, y, cur_line, SCREENMAX - y);
  1070. ,    if (old_pos == TRUE)
  1071. ,        move_to (x, y);
  1072. ,    else if (ret == NO_LINE)
  1073. ,        move_to (length_of (line_buffer), find_y (line));
  1074. ,    else
  1075. ,        move_to (0, find_y (line->next));
  1076. ,  }
  1077. ,
  1078. ,/* If nr of added line >= REPORT, print the count */
  1079. ,  if (line_count >= REPORT)
  1080. ,    status_line (num_out ((long) line_count), " lines added.");
  1081. ,}
  1082. ,
  1083. ,/*
  1084. , * WB () writes the buffer (yank_file) into another file, which
  1085. , * is prompted for.
  1086. , */
  1087. ,WB ()
  1088. ,{
  1089. ,  register int new_fd;        /* Filedescriptor to copy file */
  1090. ,  int yank_fd;            /* Filedescriptor to buffer */
  1091. ,  register int cnt;        /* Count check for read/write */
  1092. ,  int ret;            /* Error check for write */
  1093. ,  char file_name [maxLINE_LEN];    /* Output file name */
  1094. ,  char * msg_doing; char * msg_done;
  1095. ,
  1096. ,/* Checkout the buffer */
  1097. ,  if ((yank_fd = scratch_file (READ, FALSE)) == ERRORS) {
  1098. ,    error ("Buffer is empty.", NIL_PTR);
  1099. ,    return;
  1100. ,  }
  1101. ,
  1102. ,/* Get file name */
  1103. ,  if (get_file ((hop_flag > 0) ? "Append buffer to file:"
  1104. ,                   : "Write buffer to file:", file_name) != FINE)
  1105. ,    return;
  1106. ,
  1107. ,/* Create the new file or open previous file for appending */
  1108. ,  if (hop_flag > 0) {
  1109. ,    if ((new_fd = open (file_name, O_WRONLY | O_CREAT | O_APPEND, fprot)) < 0) {
  1110. ,    error ("Cannot append to file: ", serror ());
  1111. ,    return;
  1112. ,    }
  1113. ,    msg_doing = "Appending "; msg_done = "Appended";
  1114. ,  }
  1115. ,  else {
  1116. ,    if (checkoverwrite (file_name) != TRUE)
  1117. ,    return ERRORS;
  1118. ,    else if ((new_fd = creat (file_name, fprot)) < 0) {
  1119. ,    error ("Cannot create file: ", serror ());
  1120. ,    return;
  1121. ,    }
  1122. ,    msg_doing = "Writing "; msg_done = "Wrote";
  1123. ,  }
  1124. ,
  1125. ,  status_line (msg_doing, file_name);
  1126. ,
  1127. ,/* Copy buffer into file */
  1128. ,  while ((cnt = read (yank_fd, text_buffer, sizeof (text_buffer))) > 0)
  1129. ,    if (write (new_fd, text_buffer, cnt) != cnt) {
  1130. ,        bad_write (new_fd);
  1131. ,        ret = ERRORS;
  1132. ,        break;
  1133. ,    }
  1134. ,
  1135. ,/* Clean up open files and status_line */
  1136. ,  (void) close (new_fd);
  1137. ,  (void) close (yank_fd);
  1138. ,
  1139. ,  if (ret != ERRORS)            /* Bad write */
  1140. ,    file_status (msg_done, chars_saved, file_name, lines_saved, TRUE, FALSE, FALSE);
  1141. ,}
  1142. ,
  1143. ,/*
  1144. , * MARK sets mark_line / mark_text to the current line / current text pointer.
  1145. , */
  1146. ,MARK ()
  1147. ,{
  1148. ,  if (hop_flag > 0) return GOMA ();
  1149. ,  mark_line = cur_line;
  1150. ,  mark_text = cur_text;
  1151. ,  status_msg ("Mark set");
  1152. ,}
  1153. ,
  1154. ,/*
  1155. , * GOMA moves to the marked position
  1156. , */
  1157. ,GOMA ()
  1158. ,{
  1159. ,  FLAG checkmark ();
  1160. ,
  1161. ,  if (checkmark () == NOT_VALID)
  1162. ,    return error ("Mark not set.", NIL_PTR);
  1163. ,  else
  1164. ,    move_address (mark_text, find_y (mark_line));
  1165. ,}
  1166. ,
  1167. ,/*
  1168. , * YA () puts the text between the marked position and the current
  1169. , * in the buffer.
  1170. , */
  1171. ,YA ()
  1172. ,{
  1173. ,  set_up (NO_DELETE, (hop_flag > 0) ? TRUE : FALSE);
  1174. ,}
  1175. ,
  1176. ,/*
  1177. , * DT () is essentially the same as YA (), but in DT () the text is deleted.
  1178. , */
  1179. ,DT ()
  1180. ,{
  1181. ,  if (viewonly == TRUE)
  1182. ,    return viewonlyerr ();
  1183. ,
  1184. ,  set_up (DELETE, (hop_flag > 0) ? TRUE : FALSE);
  1185. ,}
  1186. ,
  1187. ,/*
  1188. , * Set_up is an interface to the actual yank. It calls checkmark () to check
  1189. , * if the marked position is still valid. If it is, yank is called with the
  1190. , * arguments in the right order.
  1191. , */
  1192. ,set_up (remove, append)
  1193. ,  FLAG remove;    /* == DELETE if text should be deleted */
  1194. ,  FLAG append;    /* == TRUE if text should only be appended to yank buffer */
  1195. ,{
  1196. ,  FLAG checkmark ();
  1197. ,
  1198. ,  switch (checkmark ()) {
  1199. ,    case NOT_VALID :
  1200. ,        error ("Mark not set.", NIL_PTR);
  1201. ,        return;
  1202. ,    case SMALLER :
  1203. ,        yank (mark_line, mark_text, cur_line, cur_text, remove, append);
  1204. ,        yankie ();
  1205. ,        break;
  1206. ,    case BIGGER :
  1207. ,        yank (cur_line, cur_text, mark_line, mark_text, remove, append);
  1208. ,        yankie ();
  1209. ,        break;
  1210. ,    case SAME :        /* Ignore stupid behaviour */
  1211. ,    /*    yank_status = EMPTY;    */
  1212. ,        chars_saved = 0L;
  1213. ,        status_msg ("Nothing to save");
  1214. ,        break;
  1215. ,  }
  1216. ,}
  1217. ,
  1218. ,/*
  1219. , * Yankie () provides a reference to the last saved buffer to be read
  1220. , * by other mined invocations.
  1221. , */
  1222. ,yankie ()
  1223. ,{
  1224. ,  delete_file (yankie_file);
  1225. ,#ifdef unix
  1226. ,  link (yank_file, yankie_file);
  1227. ,#else
  1228. ,  build_string (text_buffer, copycommand, yank_file, yankie_file);
  1229. ,  system (text_buffer);
  1230. ,#endif
  1231. ,}
  1232. ,
  1233. ,/*
  1234. , * Check_mark () checks if mark_line and mark_text are still valid pointers.
  1235. , * If they are it returns
  1236. , * SMALLER if the marked position is before the current,
  1237. , * BIGGER if it isn't or SAME if somebody didn't get the point.
  1238. , * NOT_VALID is returned when mark_line and/or mark_text are no longer valid.
  1239. , * Legal () checks if mark_text is valid on the mark_line.
  1240. , */
  1241. ,FLAG
  1242. ,checkmark ()
  1243. ,{
  1244. ,  register LINE * line;
  1245. ,  FLAG cur_seen = FALSE;
  1246. ,
  1247. ,/* Special case: check is mark_line and cur_line are the same. */
  1248. ,  if (mark_line == cur_line) {
  1249. ,    if (mark_text == cur_text)    /* Even same place */
  1250. ,        return SAME;
  1251. ,    if (legal () == ERRORS)        /* mark_text out of range */
  1252. ,        return NOT_VALID;
  1253. ,    return (mark_text < cur_text) ? SMALLER : BIGGER;
  1254. ,  }
  1255. ,
  1256. ,/* Start looking for mark_line in the line structure */
  1257. ,  for (line = header->next; line != tail; line = line->next) {
  1258. ,    if (line == cur_line)
  1259. ,        cur_seen = TRUE;
  1260. ,    else if (line == mark_line)
  1261. ,        break;
  1262. ,  }
  1263. ,
  1264. ,/* If we found mark_line (line != tail) check for legality of mark_text */
  1265. ,  if (line == tail || legal () == ERRORS)
  1266. ,    return NOT_VALID;
  1267. ,
  1268. ,/* cur_seen is TRUE if cur_line is before mark_line */
  1269. ,  return (cur_seen == TRUE) ? BIGGER : SMALLER;
  1270. ,}
  1271. ,
  1272. ,/*
  1273. , * Legal () checks if mark_text is still a valid pointer.
  1274. , */
  1275. ,legal ()
  1276. ,{
  1277. ,  register char * textp = mark_line->text;
  1278. ,
  1279. ,/* Locate mark_text on mark_line */
  1280. ,  while (textp != mark_text && * textp ++ != '\0')
  1281. ,    ;
  1282. ,  return (* textp == '\0') ? ERRORS : FINE;
  1283. ,}
  1284. ,
  1285. ,/*
  1286. , * Yank puts all the text between start_position and end_position into
  1287. , * the buffer.
  1288. , * The caller must check that the arguments to yank () are valid (e.g. in
  1289. , * the right order).
  1290. , */
  1291. ,yank (start_line, start_textp, end_line, end_textp, remove, append)
  1292. ,  LINE * start_line, * end_line;
  1293. ,  char * start_textp, * end_textp;
  1294. ,  FLAG remove;    /* == DELETE if text should be deleted */
  1295. ,  FLAG append;    /* == TRUE if text should only be appended to yank buffer */
  1296. ,{
  1297. ,  register LINE * line = start_line;
  1298. ,  register char * textp = start_textp;
  1299. ,  int fd;
  1300. ,
  1301. ,/* Create file to hold buffer */
  1302. ,  if ((fd = scratch_file (WRITE, append)) == ERRORS)
  1303. ,    return;
  1304. ,
  1305. ,  chars_saved = 0L;
  1306. ,  lines_saved = 0;
  1307. ,  if (append == TRUE)
  1308. ,    status_msg ("Appending text ...");
  1309. ,  else    status_msg ("Saving text ...");
  1310. ,
  1311. ,/* Keep writing chars until the end_location is reached. */
  1312. ,  while (textp != end_textp) {
  1313. ,    if (writechar (fd, * textp) == ERRORS) {
  1314. ,        (void) close (fd);
  1315. ,        return;
  1316. ,    }
  1317. ,    if (* textp ++ == '\n') {    /* Move to the next line */
  1318. ,        line = line->next;
  1319. ,        textp = line->text;
  1320. ,        lines_saved ++;
  1321. ,    }
  1322. ,    chars_saved ++;
  1323. ,  }
  1324. ,
  1325. ,/* Flush the I/O buffer and close file */
  1326. ,  if (flush_buffer (fd) == ERRORS) {
  1327. ,    (void) close (fd);
  1328. ,    return;
  1329. ,  }
  1330. ,  (void) close (fd);
  1331. ,  yank_status = VALID;
  1332. ,
  1333. ,  /*
  1334. ,   * Check if the text should be deleted as well. In case it should,
  1335. ,   * the following hack is used to save a lot of code.
  1336. ,   * First move back to the start_position (this might be the current
  1337. ,   * location) and then delete the text.
  1338. ,   * This might look a bit confusing to the user the first time.
  1339. ,   * Delete () will fix the screen.
  1340. ,   */
  1341. ,  if (remove == DELETE) {
  1342. ,    move_to (find_x (start_line, start_textp), find_y (start_line));
  1343. ,    delete_text (start_line, start_textp, end_line, end_textp);
  1344. ,    mark_line = cur_line;
  1345. ,    mark_text = cur_text;
  1346. ,  }
  1347. ,
  1348. ,  if (append == TRUE)
  1349. ,    status_line (num_out (chars_saved), " characters appended to buffer.");
  1350. ,  else    status_line (num_out (chars_saved), " characters saved in buffer.");
  1351. ,}
  1352. ,
  1353. ,/*
  1354. , * Scratch_file () tries to create a unique file in a temporary directory.
  1355. , * It tries several different filenames until one can be created
  1356. , * or MAXTRIALS attempts have been made.
  1357. , * After MAXTRIALS times, an error message is given and ERRORS is returned.
  1358. , */
  1359. ,
  1360. ,#define MAXTRIALS 99
  1361. ,
  1362. ,scratch_file (mode, append)
  1363. ,  FLAG mode;    /* Can be READ or WRITE permission */
  1364. ,  FLAG append;    /* == TRUE if text should only be appended to yank buffer */
  1365. ,{
  1366. ,  static int trials = 0;    /* Keep track of trials */
  1367. ,  register char * y_ptr, * n_ptr;
  1368. ,  int fd = 0;            /* Filedescriptor to buffer */
  1369. ,
  1370. ,/* If yank_status == NOT_VALID, scratch_file is called for the first time */
  1371. ,  if (yank_status == NOT_VALID && mode == WRITE) { /* Create new file */
  1372. ,    /* Generate file name. */
  1373. ,#ifdef msdos
  1374. ,    build_string (yank_file, "%s.%d", yankie_file, trials);
  1375. ,#else
  1376. ,    build_string (yank_file, "%s.%d.%d", yankie_file, getpid (), trials);
  1377. ,#endif
  1378. ,    /* Check file existence */
  1379. ,    if (access (yank_file, 0 /* F_OK */) == 0
  1380. ,        || (fd = creat (yank_file, fprot)) < 0) {
  1381. ,        if (++ trials >= MAXTRIALS) {
  1382. ,            build_string (text_buffer, "Unable to create scratchfile %s: ", yank_file);
  1383. ,            if (fd == 0)
  1384. ,            error (text_buffer, "File exists");
  1385. ,            else
  1386. ,            error (text_buffer, serror ());
  1387. ,            return ERRORS;
  1388. ,        }
  1389. ,        else
  1390. ,            return scratch_file (mode, append);    /* try again */
  1391. ,    }
  1392. ,  }
  1393. ,  else if (yank_status == NOT_VALID && mode == READ) {
  1394. ,    return ERRORS;
  1395. ,  }
  1396. ,  else /* yank_status == VALID */
  1397. ,    if (  (mode == READ && (fd = open (yank_file, O_RDONLY, 0)) < 0)
  1398. ,       || (mode == WRITE &&
  1399. ,        (fd = open (yank_file, O_WRONLY | O_CREAT |
  1400. ,            ((append == TRUE) ? O_APPEND : O_TRUNC), fprot)) < 0)) {
  1401. ,    yank_status = NOT_VALID;
  1402. ,    return ERRORS;
  1403. ,  }
  1404. ,
  1405. ,  clear_buffer ();
  1406. ,  return fd;
  1407. ,}
  1408. ,
  1409. ,/*  ==================================================================    *
  1410. , *                Search Commands                *
  1411. , *  ==================================================================    */
  1412. ,
  1413. ,/*
  1414. , * A regular expression consists of a sequence of:
  1415. , *    1. A normal character matching that character.
  1416. , *    2. A . matching any character.
  1417. , *    3. A ^ matching the begin of a line.
  1418. , *    4. A $ (as last character of the pattern) mathing the end of a line.
  1419. , *    5. A \<character> matching <character>.
  1420. , *    6. A number of characters enclosed in [] pairs matching any of these
  1421. , *       characters. A list of characters can be indicated by a '-'. So
  1422. , *       [a-z] matches any letter of the alphabet. If the first character
  1423. , *       after the '[' is a '^' then the set is negated (matching none of
  1424. , *       the characters).
  1425. , *       A ']', '^' or '-' can be escaped by putting a '\' in front of it.
  1426. , *    7. If one of the expressions as described in 1-6 is followed by a
  1427. , *       '*' than that expressions matches a sequence of 0 or more of
  1428. , *       that expression.
  1429. , */
  1430. ,
  1431. ,char typed_expression [maxLINE_LEN];    /* Holds previous search expression */
  1432. ,
  1433. ,/*
  1434. , * SFW searches forward for an expression.
  1435. , */
  1436. ,SFW ()
  1437. ,{
  1438. ,  search ("Search forward:", FORWARD);
  1439. ,}
  1440. ,
  1441. ,/*
  1442. , * SRV searches backwards for an expression.
  1443. , */
  1444. ,SRV ()
  1445. ,{
  1446. ,  search ("Search reverse:", REVERSE);
  1447. ,}
  1448. ,
  1449. ,/*
  1450. , * RS searches using the last search direction and expression.
  1451. , */
  1452. ,RS ()
  1453. ,{
  1454. ,  re_search ();
  1455. ,}
  1456. ,
  1457. ,/*
  1458. , * Get_expression () prompts for an expression. If just a return is typed, the
  1459. , * old expression is used. If the expression changed, compile () is called and
  1460. , * the returning REGEX structure is returned. It returns NIL_REG upon error.
  1461. , * The save flag indicates whether the expression should be appended at the
  1462. , * message pointer.
  1463. , */
  1464. ,REGEX *
  1465. ,get_expression (message)
  1466. ,  char * message;
  1467. ,{
  1468. ,  static REGEX program;            /* Program of expression */
  1469. ,  char exp_buf [maxLINE_LEN];        /* Buffer for new expr. */
  1470. ,
  1471. ,  if (get_string (message, exp_buf, FALSE) == ERRORS)
  1472. ,    return NIL_REG;
  1473. ,
  1474. ,  if (exp_buf [0] == '\0' && typed_expression [0] == '\0') {
  1475. ,    error ("No previous search expression.", NIL_PTR);
  1476. ,    return NIL_REG;
  1477. ,  }
  1478. ,
  1479. ,  if (exp_buf [0] != '\0') {            /* A new expr. is typed */
  1480. ,    copy_string (typed_expression, exp_buf);    /* Save expr. */
  1481. ,    compile (exp_buf, & program);        /* Compile new expression */
  1482. ,  }
  1483. ,
  1484. ,  if (program.status == REG_ERROR) {    /* Error during compiling */
  1485. ,    error (program.result.err_mess, NIL_PTR);
  1486. ,    return NIL_REG;
  1487. ,  }
  1488. ,  return & program;
  1489. ,}
  1490. ,
  1491. ,REGEX *
  1492. ,make_expression (expr)
  1493. ,  char * expr;
  1494. ,{
  1495. ,  static REGEX program;            /* Program of expression */
  1496. ,
  1497. ,  compile (expr, & program);        /* Compile new expression */
  1498. ,
  1499. ,  if (program.status == REG_ERROR) {    /* Error during compiling */
  1500. ,    error (program.result.err_mess, NIL_PTR);
  1501. ,    return NIL_REG;
  1502. ,  }
  1503. ,  return & program;
  1504. ,}
  1505. ,
  1506. ,/*
  1507. , * GR () a replaces all matches from the current position until the end
  1508. , * of the file.
  1509. , */
  1510. ,GR ()
  1511. ,{
  1512. ,  change ("Global replace:", VALID, FALSE);
  1513. ,}
  1514. ,
  1515. ,/*
  1516. , * Replace is substitute with confirmation dialogue.
  1517. , */
  1518. ,REPL ()
  1519. ,{
  1520. ,  change ("Global replace (with confirm):", VALID, TRUE);
  1521. ,}
  1522. ,
  1523. ,/*
  1524. , * LR () replaces all matches on the current line.
  1525. , */
  1526. ,LR ()
  1527. ,{
  1528. ,  change ("Line replace:", NOT_VALID, FALSE);
  1529. ,}
  1530. ,
  1531. ,/*
  1532. , * Change () prompts for an expression and a substitution pattern and changes
  1533. , * all matches of the expression into the substitution.
  1534. , * change () starts looking for expressions at the current line and
  1535. , * continues until the end of the file if the FLAG file is VALID.
  1536. , * For a call graph of search procedures see search ().
  1537. , */
  1538. ,change (message, global, confirm)
  1539. ,  char * message;        /* Message to prompt for expression */
  1540. ,  FLAG global, confirm;
  1541. ,{
  1542. ,  char mess_buf [maxLINE_LEN];        /* Buffer to hold message */
  1543. ,  char replacement [maxLINE_LEN];    /* Buffer to hold subst. pattern */
  1544. ,  REGEX * program;            /* Program resulting from compilation */
  1545. ,  register LINE * line = cur_line;
  1546. ,  register char * textp;
  1547. ,  char * substitute ();
  1548. ,  long lines = 0L;        /* Nr of lines on which subs occurred */
  1549. ,  long subs = 0L;        /* Nr of subs made */
  1550. ,  int ly = y;            /* Index to check if line is on screen */
  1551. ,  char c;
  1552. ,
  1553. ,  if (viewonly == TRUE)
  1554. ,    return viewonlyerr ();
  1555. ,
  1556. ,/* Save message and get expression */
  1557. ,  copy_string (mess_buf, message);
  1558. ,  if ((program = get_expression (mess_buf)) == NIL_REG)
  1559. ,    return;
  1560. ,
  1561. ,/* Get substitution pattern */
  1562. ,  build_string (mess_buf, "%s %s by:", mess_buf, typed_expression);
  1563. ,  if (get_string (mess_buf, replacement, FALSE) == ERRORS)
  1564. ,    return;
  1565. ,
  1566. ,  set_cursor (0, YMAX);
  1567. ,  flush ();
  1568. ,/* Substitute until end of file */
  1569. ,  do {
  1570. ,    if (line_check (program, line->text, FORWARD)) {
  1571. ,        lines ++;
  1572. ,        /* Repeat sub. on this line as long as we find a match*/
  1573. ,        do {
  1574. ,        if (confirm == TRUE) {
  1575. ,            ly = find_y (line);
  1576. ,            textp = program->start_ptr;
  1577. ,            move_address (program->start_ptr, ly);
  1578. ,            status_msg ("Replace ? (y/n)");
  1579. ,            c = promptyn ();
  1580. ,            clear_status ();
  1581. ,            if (c == 'y') {
  1582. ,                subs ++;    /* Increment subs */
  1583. ,                if ((textp = substitute (line, program, replacement, confirm))
  1584. ,                            == NIL_PTR)
  1585. ,                    return;    /* Line too long */
  1586. ,                set_cursor (0, ly);
  1587. ,                line_print (line);
  1588. ,            }
  1589. ,            else
  1590. ,                textp ++;
  1591. ,        }
  1592. ,        else {
  1593. ,            subs ++;    /* Increment subs */
  1594. ,            if ((textp = substitute (line, program, replacement, confirm))
  1595. ,                            == NIL_PTR)
  1596. ,            return;    /* Line too long */
  1597. ,        }
  1598. ,        } while (  (program->status & BEGIN_LINE) != BEGIN_LINE
  1599. ,            && (program->status & END_LINE) != END_LINE
  1600. ,            && line_check (program, textp, FORWARD)
  1601. ,            && quit == FALSE);
  1602. ,        /* Check to see if we can print the result */
  1603. ,        if (confirm == FALSE && ly <= SCREENMAX) {
  1604. ,        set_cursor (0, ly);
  1605. ,        line_print (line);
  1606. ,        }
  1607. ,    }
  1608. ,    if (ly <= SCREENMAX)
  1609. ,        ly ++;
  1610. ,    line = line->next;
  1611. ,  } while (line != tail && global == VALID && quit == FALSE);
  1612. ,
  1613. ,  copy_string (mess_buf, (quit == TRUE) ? "(Aborted) " : "");
  1614. ,/* Fix the status line */
  1615. ,  if (subs == 0L && quit == FALSE)
  1616. ,    error ("Pattern not found.", NIL_PTR);
  1617. ,  else if (lines >= REPORT || quit == TRUE) {
  1618. ,    build_string (mess_buf, "%s %ld substitutions on %ld lines.",
  1619. ,                mess_buf, subs, lines);
  1620. ,    status_msg (mess_buf);
  1621. ,  }
  1622. ,  else if (global == NOT_VALID && subs >= REPORT)
  1623. ,    status_line (num_out (subs), " substitutions.");
  1624. ,  else
  1625. ,    clear_status ();
  1626. ,  move_to (x, y);
  1627. ,/*  if (quit == TRUE) swallow_dummy_quit_char (); */
  1628. ,  quit = FALSE;
  1629. ,}
  1630. ,
  1631. ,/*
  1632. , * Substitute () replaces the match on this line by the substitute pattern
  1633. , * as indicated by the program. Every '&' in the replacement is replaced by
  1634. , * the original match. A \ in the replacement escapes the next character.
  1635. , */
  1636. ,char *
  1637. ,substitute (line, program, replacement, confirm)
  1638. ,  LINE * line;
  1639. ,  REGEX * program;
  1640. ,  char * replacement;    /* Contains replacement pattern */
  1641. ,  FLAG confirm;
  1642. ,{
  1643. ,  register char * textp = text_buffer;
  1644. ,  register char * subp = replacement;
  1645. ,  char * linep = line->text;
  1646. ,  char * amp;
  1647. ,
  1648. ,  modified = TRUE;
  1649. ,
  1650. ,/* Copy part of line until the beginning of the match */
  1651. ,  while (linep != program->start_ptr)
  1652. ,    * textp ++ = * linep ++;
  1653. ,
  1654. ,/*
  1655. , * Replace the match by the substitution pattern. Each occurrence of '&' is
  1656. , * replaced by the original match. A \ escapes the next character.
  1657. , */
  1658. ,  while (* subp != '\0' && textp < & text_buffer [MAX_CHARS]) {
  1659. ,    if (* subp == '&') {        /* Replace the original match */
  1660. ,        amp = program->start_ptr;
  1661. ,        while (amp < program->end_ptr && textp < & text_buffer [MAX_CHARS])
  1662. ,            * textp ++ = * amp ++;
  1663. ,        subp ++;
  1664. ,    }
  1665. ,    else {
  1666. ,        if (* subp == '\\' && * (subp + 1) != '\0')
  1667. ,            subp ++;
  1668. ,        * textp ++ = * subp ++;
  1669. ,    }
  1670. ,  }
  1671. ,
  1672. ,/* Check for line length not exceeding MAX_CHARS */
  1673. ,  if (length_of (text_buffer) + length_of (program->end_ptr) >= MAX_CHARS) {
  1674. ,    error ("Substitution failed: resulted line too long", NIL_PTR);
  1675. ,    return NIL_PTR;
  1676. ,  }
  1677. ,
  1678. ,/* Append last part of line to the new build line */
  1679. ,  copy_string (textp, program->end_ptr);
  1680. ,
  1681. ,/* Free old line and install new one */
  1682. ,  free_space (line->text);
  1683. ,  line->text = alloc (length_of (text_buffer) + 1);
  1684. ,  copy_string (line->text, text_buffer);
  1685. ,
  1686. ,  return (line->text + (textp - text_buffer));
  1687. ,}
  1688. ,
  1689. ,/*
  1690. , * Search () calls get_expression to fetch the expression. If this went well,
  1691. , * the function match () is called which returns the line with the next match.
  1692. , * If this line is the NIL_LINE, it means that a match could not be found.
  1693. , * Find_x () and find_y () display the right page on the screen, and return
  1694. , * the right coordinates for x and y.
  1695. , * These coordinates are passed to move_to ().
  1696. , * Re_search () searches using the last search program and direction.
  1697. ,   Call graph of search procedures:
  1698. ,RS ------------\
  1699. ,SFW -\        ; re_search ; match -----------\
  1700. ,SRV --; search +                \
  1701. ,           \                 \
  1702. ,            ; get_expression ; compile      ; line_check ; check_string
  1703. ,GR  \           /                 /
  1704. ,REPL ; change +---------------------------------/
  1705. ,LR  /           \
  1706. ,        \ substitute
  1707. ,
  1708. , */
  1709. ,REGEX * lastprogram = NIL_REG;
  1710. ,FLAG lastmethod = NOT_VALID;    /* FORWARD, REVERSE */
  1711. ,
  1712. ,search (message, method)
  1713. ,  char * message;
  1714. ,  FLAG method;
  1715. ,{
  1716. ,  register REGEX * program;
  1717. ,
  1718. ,  lastmethod = method;
  1719. ,
  1720. ,/* Get the expression */
  1721. ,  if ((program = get_expression (message)) == NIL_REG)
  1722. ,    return;
  1723. ,  lastprogram = program;
  1724. ,  re_search ();
  1725. ,}
  1726. ,
  1727. ,search_for (expr, method)
  1728. ,  char * expr;
  1729. ,  FLAG method;
  1730. ,{
  1731. ,  register REGEX * program;
  1732. ,
  1733. ,/* make the expression */
  1734. ,  if ((program = make_expression (expr)) == NIL_REG)
  1735. ,    return;
  1736. ,  do_search (program, method);
  1737. ,}
  1738. ,
  1739. ,re_search ()
  1740. ,{
  1741. ,  do_search (lastprogram, lastmethod);
  1742. ,}
  1743. ,
  1744. ,do_search (program, method)
  1745. ,  register REGEX * program;
  1746. ,  FLAG method;
  1747. ,{
  1748. ,  register LINE * match_line;
  1749. ,
  1750. ,  if (method == NOT_VALID) return error ("No previous search.", NIL_PTR);
  1751. ,  if (program == NIL_REG) return error ("No previous search expression.", NIL_PTR);
  1752. ,
  1753. ,  set_cursor (0, YMAX);
  1754. ,  clear_status ();
  1755. ,  flush ();
  1756. ,/* Find the match */
  1757. ,  if ((match_line = match (program, cur_text, method)) == NIL_LINE) {
  1758. ,    if (quit == TRUE) {
  1759. ,        status_msg ("Aborted");
  1760. ,/*        swallow_dummy_quit_char (); */
  1761. ,        quit = FALSE;
  1762. ,    }
  1763. ,    else
  1764. ,        status_msg ("Pattern not found");
  1765. ,    return;
  1766. ,  }
  1767. ,
  1768. ,/*  clear_status (); */
  1769. ,  move_address (program->start_ptr, find_y (match_line));
  1770. ,}
  1771. ,
  1772. ,/* Now the search and replace utilities */
  1773. ,/* ------------------------------------ */
  1774. ,/* Opcodes for characters */
  1775. ,#define    NORMAL        0x0200
  1776. ,#define DOT        0x0400
  1777. ,#define EOLN        0x0800
  1778. ,#define STAR        0x1000
  1779. ,#define BRACKET        0x2000
  1780. ,#define NEGATE        0x0100
  1781. ,#define DONE        0x4000
  1782. ,
  1783. ,/* Mask for opcodes and characters */
  1784. ,#define LOW_BYTE    0x00FF
  1785. ,#define HIGH_BYTE    0xFF00
  1786. ,
  1787. ,/* Previous is the contents of the previous address (ptr) points to */
  1788. ,#define previous(ptr)        (* ((ptr) - 1))
  1789. ,
  1790. ,/* Buffer to store outcome of compilation */
  1791. ,int exp_buffer [BLOCK_SIZE];
  1792. ,
  1793. ,/* Errors often used */
  1794. ,char * too_long = "Regular expression too long";
  1795. ,
  1796. ,/*
  1797. , * Reg_error () is called by compile () if something went wrong. It sets the
  1798. , * status of the structure to error, and assigns the error field of the union.
  1799. , */
  1800. ,#define reg_error(str)    program->status = REG_ERROR, \
  1801. ,                    program->result.err_mess = (str)
  1802. ,/*
  1803. , * Finished () is called when everything went right during compilation. It
  1804. , * allocates space for the expression, and copies the expression buffer into
  1805. , * this field.
  1806. , */
  1807. ,finished (program, last_exp)
  1808. ,  register REGEX * program;
  1809. ,  int * last_exp;
  1810. ,{
  1811. ,  register int length = (last_exp - exp_buffer) * sizeof (int);
  1812. ,/* Allocate space */
  1813. ,  program->result.expression = (int *) alloc (length);
  1814. ,/* Copy expression. (expression consists of ints!) */
  1815. ,  bcopy (exp_buffer, program->result.expression, length);
  1816. ,}
  1817. ,
  1818. ,/*
  1819. , * Bcopy copies `bytes' bytes from the `from' address into the `to' address.
  1820. , */
  1821. ,#ifdef vms
  1822. ,#define defbcopy
  1823. ,#endif
  1824. ,#ifdef msdos
  1825. ,#define defbcopy
  1826. ,#endif
  1827. ,#ifdef defbcopy    /* otherwise also in standard library */
  1828. ,bcopy (from, to, bytes)
  1829. ,  register char * from, * to;
  1830. ,  register int bytes;
  1831. ,{
  1832. ,  while (bytes --)
  1833. ,    * to ++ = * from ++;
  1834. ,}
  1835. ,#endif
  1836. ,
  1837. ,/*
  1838. , * Compile compiles the pattern into a more comprehensible form and returns a
  1839. , * REGEX structure. If something went wrong, the status field of the structure
  1840. , * is set to REG_ERROR and an error message is set into the err_mess field of
  1841. , * the union. If all went well the expression is saved and the expression
  1842. , * pointer is set to the saved (and compiled) expression.
  1843. , */
  1844. ,compile (pattern, program)
  1845. ,  register u_char * pattern;    /* Pointer to pattern */
  1846. ,  REGEX * program;
  1847. ,{
  1848. ,  register int * expression = exp_buffer;
  1849. ,  int * prev_char;        /* Pointer to previous compiled atom */
  1850. ,  int * acct_field;        /* Pointer to last BRACKET start */
  1851. ,  FLAG negate;            /* Negate flag for BRACKET */
  1852. ,  u_char low_char;        /* Index for chars in BRACKET */
  1853. ,  u_char c;
  1854. ,
  1855. ,/* Check for begin of line */
  1856. ,  if (* pattern == '^') {
  1857. ,    program->status = BEGIN_LINE;
  1858. ,    pattern ++;
  1859. ,  }
  1860. ,  else {
  1861. ,    program->status = 0;
  1862. ,/* If the first character is a '*' we have to assign it here. */
  1863. ,    if (* pattern == '*') {
  1864. ,        * expression ++ = '*' + NORMAL;
  1865. ,        pattern ++;
  1866. ,    }
  1867. ,  }
  1868. ,
  1869. ,  for (; ;) {    c = * pattern ++;
  1870. ,    switch (c) {
  1871. ,    case '.' :
  1872. ,        * expression ++ = DOT;
  1873. ,        break;
  1874. ,    case '$' :
  1875. ,        /*
  1876. ,         * Only means EOLN if it is the last char of the pattern
  1877. ,         */
  1878. ,        if (* pattern == '\0') {
  1879. ,            * expression ++ = EOLN | DONE;
  1880. ,            program->status |= END_LINE;
  1881. ,            finished (program, expression);
  1882. ,            return;
  1883. ,        }
  1884. ,        else
  1885. ,            * expression ++ = NORMAL + '$';
  1886. ,        break;
  1887. ,    case '\0' :
  1888. ,        * expression ++ = DONE;
  1889. ,        finished (program, expression);
  1890. ,        return;
  1891. ,    case '\\' :
  1892. ,        /* If last char, it must! mean a normal '\' */
  1893. ,        if (* pattern == '\0')
  1894. ,            * expression ++ = NORMAL + '\\';
  1895. ,        else
  1896. ,            * expression ++ = NORMAL + * pattern ++;
  1897. ,        break;
  1898. ,    case '*' :
  1899. ,        /*
  1900. ,         * If the previous expression was a [] find out the
  1901. ,         * begin of the list, and adjust the opcode.
  1902. ,         */
  1903. ,        prev_char = expression - 1;
  1904. ,        if (* prev_char & BRACKET)
  1905. ,            * (expression - (* acct_field & LOW_BYTE)) |= STAR;
  1906. ,        else
  1907. ,            * prev_char |= STAR;
  1908. ,        break;
  1909. ,    case '[' :
  1910. ,        /*
  1911. ,         * First field in expression gives information about
  1912. ,         * the list.
  1913. ,         * The opcode consists of BRACKET and if necessary
  1914. ,         * NEGATE to indicate that the list should be negated
  1915. ,         * and/or STAR to indicate a number of sequence of this
  1916. ,         * list.
  1917. ,         * The lower byte contains the length of the list.
  1918. ,         */
  1919. ,        acct_field = expression ++;
  1920. ,        if (* pattern == '^') {    /* List must be negated */
  1921. ,            pattern ++;
  1922. ,            negate = TRUE;
  1923. ,        }
  1924. ,        else
  1925. ,            negate = FALSE;
  1926. ,        while (* pattern != ']') {
  1927. ,            if (* pattern == '\0') {
  1928. ,                reg_error ("Missing ]");
  1929. ,                return;
  1930. ,            }
  1931. ,            if (* pattern == '\\')
  1932. ,                pattern ++;
  1933. ,            * expression ++ = * pattern ++;
  1934. ,            if (* pattern == '-') {
  1935. ,                        /* Make list of chars */
  1936. ,                low_char = previous (pattern);
  1937. ,                pattern ++;    /* Skip '-' */
  1938. ,                if (low_char ++ > * pattern) {
  1939. ,                    reg_error ("Bad range in [a-z]");
  1940. ,                    return;
  1941. ,                }
  1942. ,                /* Build list */
  1943. ,                while (low_char <= * pattern
  1944. ,                    && low_char != '\0')
  1945. ,                 /* avoid wrap-around beyond '\377' (loop!) */
  1946. ,                    * expression ++ = low_char ++;
  1947. ,                pattern ++;
  1948. ,            }
  1949. ,            if (expression >= & exp_buffer [BLOCK_SIZE]) {
  1950. ,                reg_error (too_long);
  1951. ,                return;
  1952. ,            }
  1953. ,        }
  1954. ,        pattern ++;            /* Skip ']' */
  1955. ,        /* Assign length of list in acct field */
  1956. ,        if ((* acct_field = (expression - acct_field)) == 1) {
  1957. ,            reg_error ("Empty []");
  1958. ,            return;
  1959. ,        }
  1960. ,        /* Assign negate and bracket field */
  1961. ,        * acct_field |= BRACKET;
  1962. ,        if (negate == TRUE)
  1963. ,            * acct_field |= NEGATE;
  1964. ,        /*
  1965. ,         * Add BRACKET to opcode of last char in field because
  1966. ,         * a '*' may be following the list.
  1967. ,         */
  1968. ,        previous (expression) |= BRACKET;
  1969. ,        break;
  1970. ,    default :
  1971. ,        * expression ++ = c + NORMAL;
  1972. ,    }
  1973. ,    if (expression == & exp_buffer [BLOCK_SIZE]) {
  1974. ,        reg_error (too_long);
  1975. ,        return;
  1976. ,    }
  1977. ,  }
  1978. ,  /* NOTREACHED */
  1979. ,}
  1980. ,
  1981. ,/*
  1982. , * Match gets as argument the program, pointer to place in current line to
  1983. , * start from and the method to search for (either FORWARD or REVERSE).
  1984. , * Match () will look through the whole file until a match is found.
  1985. , * NIL_LINE is returned if no match could be found.
  1986. , */
  1987. ,LINE *
  1988. ,match (program, string, method)
  1989. ,  REGEX * program;
  1990. ,  u_char * string;
  1991. ,  register FLAG method;
  1992. ,{
  1993. ,  register LINE * line = cur_line;
  1994. ,  u_char old_char;                /* For saving chars */
  1995. ,
  1996. ,/* Corrupted program */
  1997. ,  if (program->status == REG_ERROR)
  1998. ,    return NIL_LINE;
  1999. ,
  2000. ,/* Check part of text first */
  2001. ,  if (! (program->status & BEGIN_LINE)) {
  2002. ,    if (method == FORWARD) {
  2003. ,        if (line_check (program, string + 1, method) == MATCH)
  2004. ,            return cur_line;    /* Match found */
  2005. ,    }
  2006. ,    else if (! (program->status & END_LINE)) {
  2007. ,        old_char = * string;    /* Save char and */
  2008. ,        * string = '\n';    /* Assign '\n' for line_check */
  2009. ,        if (line_check (program, line->text, method) == MATCH) {
  2010. ,            * string = old_char; /* Restore char */
  2011. ,            return cur_line;    /* Found match */
  2012. ,        }
  2013. ,        * string = old_char;    /* No match, but restore char */
  2014. ,    }
  2015. ,  }
  2016. ,
  2017. ,/* No match in last (or first) part of line. Check out rest of file */
  2018. ,  do {
  2019. ,    line = (method == FORWARD) ? line->next : line->prev;
  2020. ,    if (line->text == NIL_PTR) {    /* Header/tail */
  2021. ,        status_msg ("Search wrapped around end of file");
  2022. ,        continue;
  2023. ,        }
  2024. ,    if (line_check (program, line->text, method) == MATCH)
  2025. ,        return line;
  2026. ,  } while (line != cur_line && quit == FALSE);
  2027. ,
  2028. ,/* No match found. */
  2029. ,  return NIL_LINE;
  2030. ,}
  2031. ,
  2032. ,/*
  2033. , * Line_check () checks the line (or rather string) for a match. Method
  2034. , * indicates FORWARD or REVERSE search. It scans through the whole string
  2035. , * until a match is found, or the end of the string is reached.
  2036. , */
  2037. ,line_check (program, string, method)
  2038. ,  register REGEX * program;
  2039. ,  char * string;
  2040. ,  FLAG method;
  2041. ,{
  2042. ,  register char * textp = string;
  2043. ,
  2044. ,/* Assign start_ptr field. We might find a match right away! */
  2045. ,  program->start_ptr = textp;
  2046. ,
  2047. ,/* If the match must be anchored, just check the string. */
  2048. ,  if (program->status & BEGIN_LINE)
  2049. ,    return check_string (program, string, NIL_INT);
  2050. ,
  2051. ,  if (method == REVERSE) {
  2052. ,    /* First move to the end of the string */
  2053. ,    for (textp = string; * textp != '\n'; textp ++)
  2054. ,        ;
  2055. ,    /* Start checking string until the begin of the string is met */
  2056. ,    while (textp >= string) {
  2057. ,        program->start_ptr = textp;
  2058. ,        if (check_string (program, textp --, NIL_INT))
  2059. ,            return MATCH;
  2060. ,    }
  2061. ,  }
  2062. ,  else {
  2063. ,    /* Move through the string until the end of is found */
  2064. ,    while (quit == FALSE && * textp != '\0') {
  2065. ,        program->start_ptr = textp;
  2066. ,        if (check_string (program, textp, NIL_INT))
  2067. ,            return MATCH;
  2068. ,        if (* textp == '\n')
  2069. ,            break;
  2070. ,        textp ++;
  2071. ,    }
  2072. ,  }
  2073. ,
  2074. ,  return NO_MATCH;
  2075. ,}
  2076. ,
  2077. ,/*
  2078. , * Check_string () checks if a match can be found in the given string.
  2079. , * Whenever a STAR is found during matching, then the begin position of
  2080. , * the string is marked and the maximum number of matches is performed.
  2081. , * Then the function star () is called which starts to finish the match
  2082. , * from this position of the string (and expression).
  2083. , * Check () returns MATCH for a match, NO_MATCH if the string
  2084. , * couldn't be matched or REG_ERROR for an illegal opcode in expression.
  2085. , */
  2086. ,check_string (program, string, expression)
  2087. ,  REGEX * program;
  2088. ,  register u_char * string;
  2089. ,  int * expression;
  2090. ,{
  2091. ,  register int opcode;        /* Holds opcode of next expr. atom */
  2092. ,  u_char c;            /* Char that must be matched */
  2093. ,  u_char * mark;        /* For marking position */
  2094. ,  int star_fl;            /* A star has been born */
  2095. ,
  2096. ,  if (expression == NIL_INT)
  2097. ,    expression = program->result.expression;
  2098. ,
  2099. ,/* Loop until end of string or end of expression */
  2100. ,  while (quit == FALSE && ! (* expression & DONE) &&
  2101. ,                       * string != '\0' && * string != '\n') {
  2102. ,    c = * expression & LOW_BYTE;      /* Extract match char */
  2103. ,    opcode = * expression & HIGH_BYTE; /* Extract opcode */
  2104. ,    if ((star_fl = (opcode & STAR)) != 0) {  /* Check star occurrence */
  2105. ,        opcode &= ~STAR;      /* Strip opcode */
  2106. ,        mark = string;          /* Mark current position */
  2107. ,    }
  2108. ,    expression ++;        /* Increment expr. */
  2109. ,    switch (opcode) {
  2110. ,    case NORMAL :
  2111. ,        if (star_fl)
  2112. ,            while (* string ++ == c)    /* Skip all matches */
  2113. ,                ;
  2114. ,        else if (* string ++ != c)
  2115. ,            return NO_MATCH;
  2116. ,        break;
  2117. ,    case DOT :
  2118. ,        string ++;
  2119. ,        if (star_fl)            /* Skip to eoln */
  2120. ,            while (* string != '\0' && * string ++ != '\n')
  2121. ,                ;
  2122. ,        break;
  2123. ,    case NEGATE | BRACKET:
  2124. ,    case BRACKET :
  2125. ,         if (star_fl)
  2126. ,        while (in_list (expression, * string ++, c, opcode) == MATCH)
  2127. ,            ;
  2128. ,         else
  2129. ,        if (in_list (expression, * string ++, c, opcode) == NO_MATCH)
  2130. ,            return NO_MATCH;
  2131. ,        expression += c - 1;    /* Add length of list */
  2132. ,        break;
  2133. ,    default :
  2134. ,        panic ("Corrupted search program", NIL_PTR);
  2135. ,    }
  2136. ,    if (star_fl)
  2137. ,        return star (program, mark, string, expression);
  2138. ,  }
  2139. ,  if (* expression & DONE) {
  2140. ,    program->end_ptr = (char *) string;    /* Match ends here */
  2141. ,    /*
  2142. ,     * We might have found a match. The last thing to do is check
  2143. ,     * whether a '$' was given at the end of the expression, or
  2144. ,     * the match was found on a null string. (E.g. [a-z]* always
  2145. ,     * matches) unless a ^ or $ was included in the pattern.
  2146. ,     */
  2147. ,    if ((* expression & EOLN) && * string != '\n' && * string != '\0')
  2148. ,        return NO_MATCH;
  2149. ,    if (string == (u_char *) program->start_ptr
  2150. ,                    && ! (program->status & BEGIN_LINE)
  2151. ,                    && ! (* expression & EOLN))
  2152. ,        return NO_MATCH;
  2153. ,    return MATCH;
  2154. ,  }
  2155. ,  return NO_MATCH;
  2156. ,}
  2157. ,
  2158. ,/*
  2159. , * Star () calls check_string () to find out the longest match possible.
  2160. , * It searches backwards until the (in check_string ()) marked position
  2161. , * is reached, or a match is found.
  2162. , */
  2163. ,star (program, end_position, string, expression)
  2164. ,  REGEX * program;
  2165. ,  register char * end_position;
  2166. ,  register char * string;
  2167. ,  int * expression;
  2168. ,{
  2169. ,  do {
  2170. ,    string --;
  2171. ,    if (check_string (program, string, expression))
  2172. ,        return MATCH;
  2173. ,  } while (string != end_position);
  2174. ,
  2175. ,  return NO_MATCH;
  2176. ,}
  2177. ,
  2178. ,/*
  2179. , * In_list () checks if the given character is in the list of []. If it is
  2180. , * it returns MATCH. if it isn't it returns NO_MATCH. These returns values
  2181. , * are reversed when the NEGATE field in the opcode is present.
  2182. , */
  2183. ,in_list (list, c, list_length, opcode)
  2184. ,  register int * list;
  2185. ,  u_char c;
  2186. ,  register int list_length;
  2187. ,  int opcode;
  2188. ,{
  2189. ,  if (c == '\0' || c == '\n')    /* End of string, never matches */
  2190. ,    return NO_MATCH;
  2191. ,  while (list_length -- > 1) {    /* > 1, don't check acct_field */
  2192. ,    if ((* list & LOW_BYTE) == c)
  2193. ,        return (opcode & NEGATE) ? NO_MATCH : MATCH;
  2194. ,    list ++;
  2195. ,  }
  2196. ,  return (opcode & NEGATE) ? MATCH : NO_MATCH;
  2197. ,}
  2198. ,
  2199. ,/*  ==================================================================    *
  2200. , *                End                    *
  2201. , *  ==================================================================    */
  2202. EOSED
  2203.  
  2204. exit 0
  2205.