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

  1. Newsgroups: comp.sources.misc
  2. From: lijewski@rosserv.gsfc.nasa.gov (Mike Lijewski)
  3. Subject:  v33i006:  problem - A Problem Database Manager, Part04/07
  4. Message-ID: <1992Oct19.165856.4207@sparky.imd.sterling.com>
  5. X-Md4-Signature: 463719d06e07eaf6f9695031a25ab8fd
  6. Date: Mon, 19 Oct 1992 16:58:56 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 6
  11. Archive-name: problem/part04
  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 4 (of 7)."
  21. # Contents:  classes.C lister.C
  22. # Wrapped by lijewski@xtesoc2 on Mon Oct 19 11:05:10 1992
  23. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  24. if test -f 'classes.C' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'classes.C'\"
  26. else
  27. echo shar: Extracting \"'classes.C'\" \(7402 characters\)
  28. sed "s/^X//" >'classes.C' <<'END_OF_FILE'
  29. X/*
  30. X** classes.C - contains definitions of the member functions which
  31. X**             aren\'t defined in the relevant class declarations.
  32. X**
  33. X** classes.C classes.C 1.9   Delta\'d: 12:28:24 9/23/92   Mike Lijewski, CNSF
  34. X**
  35. X** Copyright \(c\) 1991, 1992 Cornell University
  36. X** All rights reserved.
  37. X**
  38. X** Redistribution and use in source and binary forms are permitted
  39. X** provided that: \(1\) source distributions retain this entire copyright
  40. X** notice and comment, and \(2\) distributions including binaries display
  41. X** the following acknowledgement:  ``This product includes software
  42. X** developed by Cornell University\'\' in the documentation or other
  43. X** materials provided with the distribution and in all advertising
  44. X** materials mentioning features or use of this software. Neither the
  45. X** name of the University nor the names of its contributors may be used
  46. X** to endorse or promote products derived from this software without
  47. X** specific prior written permission.
  48. X**
  49. X** THIS SOFTWARE IS PROVIDED ``AS IS\'\' AND WITHOUT ANY EXPRESS OR
  50. X** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  51. X** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  52. X*/
  53. X
  54. X#include <new.h>
  55. X#include <stdlib.h>
  56. X#include <string.h>
  57. X
  58. X#include "classes.h"
  59. X#include "utilities.h"
  60. X
  61. Xtypedef void (*PEHF)();
  62. X
  63. XStringRep::StringRep()
  64. X{
  65. X    rep = ::new char[1];
  66. X    len = 0;
  67. X    *rep = '\0';
  68. X    count = 1;
  69. X}
  70. X
  71. XStringRep::StringRep(const char *s)
  72. X{
  73. X    len = ::strlen(s);
  74. X    rep = ::new char[len + 1];
  75. X    ::strcpy(rep, s);
  76. X    count = 1;
  77. X}
  78. X
  79. XString StringRep::operator+(const String& s) const
  80. X{
  81. X    size_t slen  = s.length() + length();
  82. X    char *buf    = ::new char[slen + 1];
  83. X    ::strcpy(buf, rep);
  84. X    ::strcat(buf, s.p->rep);
  85. X    return String(&buf, slen);
  86. X}
  87. X
  88. X/*
  89. X** The definition of the head of the freelist that StringRep::operator new\(\)
  90. X** uses to dole out StringReps efficiently.
  91. X*/
  92. X
  93. XStringRep *StringRep::freeList;
  94. X
  95. Xvoid* StringRep::operator new(size_t size)
  96. X{
  97. X    if (size != sizeof(StringRep)) return ::new char[size];
  98. X    StringRep *s = freeList;
  99. X    if (s)
  100. X        freeList = s->next;
  101. X    else
  102. X    {
  103. X        StringRep *block = (StringRep*)::new char[chunksize*sizeof(StringRep)];
  104. X        if (block == 0)
  105. X        {
  106. X            PEHF newHandler = set_new_handler(0);
  107. X            set_new_handler(newHandler);
  108. X            if (newHandler)
  109. X                newHandler();
  110. X            else
  111. X                return 0;
  112. X        }
  113. X        for (int i = 0; i < chunksize - 1; i++)
  114. X            block[i].next = (StringRep *)&block[i + 1];
  115. X        block[chunksize - 1].next = 0;
  116. X        s = block;
  117. X        freeList = &block[1];
  118. X    }
  119. X    return s;
  120. X}
  121. X
  122. Xvoid StringRep::operator delete(void *object)
  123. X{
  124. X    StringRep *s = (StringRep *)object;
  125. X    s->next = freeList;
  126. X    freeList = s;
  127. X}
  128. X
  129. XString::~String() { if (--p->count <= 0) delete p; }
  130. X
  131. XString& String::operator=(const String& rhs)
  132. X{
  133. X    rhs.p->count++;
  134. X    if (--p->count <= 0) delete p;
  135. X    p = rhs.p;
  136. X    return *this;
  137. X}
  138. X
  139. Xvoid String::operator+=(const String& rhs)
  140. X{
  141. X    size_t slen = p->length() + rhs.length();
  142. X    char *buf   = ::new char[slen + 1];
  143. X    (void)strcpy(buf, p->rep);
  144. X    (void)strcat(buf, rhs.p->rep);
  145. X    if (p->count == 1)
  146. X    {
  147. X        DELETE p->rep;
  148. X        p->rep = buf;
  149. X        p->len = slen;
  150. X    }
  151. X    else
  152. X        operator=(String(&buf, slen));
  153. X}
  154. X
  155. Xvoid String::operator+=(const char *rhs)
  156. X{
  157. X    size_t slen = p->length() + ::strlen(rhs);
  158. X    char *buf = ::new char[slen + 1];
  159. X    ::strcpy(buf, p->rep);
  160. X    ::strcat(buf, rhs);
  161. X    if (p->count == 1)
  162. X    {
  163. X        DELETE p->rep;
  164. X        p->rep = buf;
  165. X        p->len = slen;
  166. X    }
  167. X    else
  168. X        operator=(String(&buf, slen));
  169. X}
  170. X
  171. Xvoid String::range_error(int index)
  172. X{
  173. X    ::error("range error: %d out of bounds", index);
  174. X    exit(1);
  175. X}
  176. X
  177. XSBHelper String::operator[](int index)
  178. X{
  179. X    if (index < 0 || index >= length()) range_error(index);
  180. X    return SBHelper(*this, index);
  181. X}
  182. X
  183. XSBHelper::SBHelper(String& s, int i) : str(s), index(i) { };
  184. X
  185. Xchar SBHelper::operator=(char c)
  186. X{
  187. X    if (str.p->count == 1)
  188. X        //
  189. X        // Only one reference to our String.  Just assign the character to
  190. X        // the appropriate place.  Note that String::operator\[\] does the
  191. X        // range checking.
  192. X        //
  193. X        str.p->rep[index] = c;
  194. X    else
  195. X    {
  196. X        // We have to uniquify our str.
  197. X        str = String(str.p->rep);
  198. X        str.p->rep[index] = c;
  199. X    }
  200. X    return c;
  201. X}
  202. X
  203. XDLink::DLink(char **line) : _line(line) { _next = _prev = 0; }
  204. X
  205. X//
  206. X// Update the line in DLink with a new version.  The new
  207. X// line should have been been allocated via new\(\).
  208. X// 
  209. Xvoid DLink::update(char **new_line) { _line = String(new_line); }
  210. X
  211. XDList::DList()
  212. X{
  213. X    _head   = _tail = 0;
  214. X    _next   = _prev = 0;
  215. X    _firstLine = _lastLine = _currLine = 0;
  216. X    _nelems    = _saved_x  = _saved_y  = 0;
  217. X}
  218. X
  219. X//
  220. X// Adds the DLink to the listing maintained by DList.
  221. X//
  222. X
  223. Xvoid DList::add(DLink *link)
  224. X{
  225. X    if (nelems())
  226. X    {
  227. X        _tail->_next = link;
  228. X        _tail->_next->_prev = tail();
  229. X        _tail = link;
  230. X        _nelems++;
  231. X    }
  232. X    else
  233. X    {
  234. X        _head = _tail = link;
  235. X        _nelems = 1;
  236. X    }
  237. X}
  238. X
  239. X//
  240. X// Delete the current listing line in the window
  241. X// and update our view.  The width of our view
  242. X// always decreases by one.  If the calling procedure
  243. X// adds more lines to the screen, they\'ll have to reset
  244. X// lastLine\(\) and/or firstLine\(\), but currLine doesn\'t need to change.
  245. X//
  246. X
  247. Xvoid DList::deleteLine()
  248. X{
  249. X    DLink *line = currLine();
  250. X
  251. X    if (atBegOfList())
  252. X    {
  253. X        //
  254. X        // that is, firstLine\(\) == head\(\)
  255. X        //
  256. X        _head = _firstLine = _currLine = head()->next();
  257. X        _head->_prev = 0;
  258. X    }
  259. X    else if (atWindowTop())
  260. X    {
  261. X        //
  262. X        // but firstLine\(\) != head\(\)
  263. X        //
  264. X        _firstLine = _currLine = line->next();
  265. X        line->_next->_prev = line->prev();
  266. X        line->_prev->_next = line->next();
  267. X    }
  268. X    else if (atEndOfList())
  269. X    {
  270. X        //
  271. X        // lastLine\(\) == tail\(\)
  272. X        //
  273. X        _tail = _lastLine = _currLine = line->prev();
  274. X        _tail->_next = 0;
  275. X    }
  276. X    else
  277. X    {
  278. X        _currLine = line->next();
  279. X        line->_next->_prev = line->prev();
  280. X        line->_prev->_next = line->next();
  281. X    }
  282. X
  283. X    _nelems--;
  284. X    delete line;
  285. X}
  286. X
  287. XDList::~DList()
  288. X{
  289. X    if (nelems())
  290. X    {
  291. X        DLink *tmp = tail(), *prev = tail()->prev();
  292. X        while(tmp)
  293. X        {
  294. X            delete tmp;
  295. X            if ((tmp = prev) != 0) prev = tmp->prev();
  296. X        }
  297. X        delete tmp;
  298. X    }
  299. X}
  300. X
  301. X//
  302. X// The definition of the head of the freelist that DLink::operator new\(\)
  303. X// uses to dole out dirLines efficiently.
  304. X//
  305. XDLink *DLink::freeList;
  306. X
  307. Xvoid *DLink::operator new(size_t)
  308. X{
  309. X    DLink *line = freeList;
  310. X    if (line)
  311. X        freeList = line->next();
  312. X    else
  313. X    {
  314. X        DLink *block = (DLink *) ::new char[chunksize * sizeof(DLink)];
  315. X        if (block == 0)
  316. X        {
  317. X            PEHF newHandler = set_new_handler(0);
  318. X            set_new_handler(newHandler);
  319. X            if (newHandler)
  320. X                newHandler();
  321. X            else
  322. X                return 0;
  323. X        }
  324. X        for (int i = 0; i < chunksize - 1; i++)
  325. X            block[i]._next = (DLink *)&block[i + 1];
  326. X        block[chunksize - 1]._next = 0;
  327. X        line = block;
  328. X        freeList = &block[1];
  329. X    }
  330. X    return line;
  331. X}
  332. X
  333. Xvoid DLink::operator delete(void *object)
  334. X{
  335. X    DLink *line = (DLink *)object;
  336. X    line->_next = freeList;
  337. X    freeList = line;
  338. X}
  339. X
  340. X
  341. END_OF_FILE
  342. if test 7402 -ne `wc -c <'classes.C'`; then
  343.     echo shar: \"'classes.C'\" unpacked with wrong size!
  344. fi
  345. # end of 'classes.C'
  346. fi
  347. if test -f 'lister.C' -a "${1}" != "-c" ; then 
  348.   echo shar: Will not clobber existing file \"'lister.C'\"
  349. else
  350. echo shar: Extracting \"'lister.C'\" \(38236 characters\)
  351. sed "s/^X//" >'lister.C' <<'END_OF_FILE'
  352. X/*
  353. X** lister.C - a very simple line lister which manages it\'s own screen
  354. X**            and provides functions which can be executed on the line
  355. X**            the cursor is on -- the "current" line.
  356. X**
  357. X** lister.C 1.25   Delta\'d: 17:43:17 10/8/92   Mike Lijewski, CNSF
  358. X**
  359. X** Copyright \(c\) 1991, 1992 Cornell University
  360. X** All rights reserved.
  361. X**
  362. X** Redistribution and use in source and binary forms are permitted
  363. X** provided that: \(1\) source distributions retain this entire copyright
  364. X** notice and comment, and \(2\) distributions including binaries display
  365. X** the following acknowledgement:  ``This product includes software
  366. X** developed by Cornell University\'\' in the documentation or other
  367. X** materials provided with the distribution and in all advertising
  368. X** materials mentioning features or use of this software. Neither the
  369. X** name of the University nor the names of its contributors may be used
  370. X** to endorse or promote products derived from this software without
  371. X** specific prior written permission.
  372. X**
  373. X** THIS SOFTWARE IS PROVIDED ``AS IS\'\' AND WITHOUT ANY EXPRESS OR
  374. X** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  375. X** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  376. X*/
  377. X
  378. X
  379. X#include <ctype.h>
  380. X
  381. X#ifndef _IBMR2
  382. X#include <libc.h>
  383. X#endif
  384. X
  385. X#include <osfcn.h>
  386. X#include <signal.h>
  387. X#include <stdio.h>
  388. X#include <stdlib.h>
  389. X#include <string.h>
  390. X#include <sys/types.h>
  391. X#ifdef ESIX
  392. Xtypedef int pid_t;
  393. X#endif /*ESIX*/
  394. X#include <sys/wait.h>
  395. X#include <unistd.h>
  396. X
  397. X#include "classes.h"
  398. X#include "display.h"
  399. X#include "help.h"
  400. X#include "keys.h"
  401. X#include "lister.h"
  402. X#include "problem.h"
  403. X#include "utilities.h"
  404. X#include "version.h"
  405. X
  406. X// our screen
  407. Xstatic DList *screen;
  408. X
  409. X/*
  410. X** initialize_lister - initialize the lister.  This is so we can
  411. X**                     call redisplay\(\) with no arguments.
  412. X*/
  413. X
  414. Xvoid initialize_lister(DList *dl) { screen = dl; }
  415. X
  416. X/*
  417. X** redisplay - this routine redisplays the DList which is our screen.
  418. X**             It assumes that the physical screen has become corrupted,
  419. X**             clearing each line before writing to it.
  420. X*/
  421. X
  422. Xstatic void redisplay()
  423. X{
  424. X    DLink *ln = screen->firstLine();
  425. X    cursor_home();
  426. X    for (int i = 0; i < rows() - 2 && ln; i++, ln = ln->next())
  427. X    {
  428. X        clear_to_end_of_line();
  429. X        display_string(ln->line(), ln->length());
  430. X    }
  431. X    move_cursor(i, 0);
  432. X    for (; i < rows() - 2; i++) { clear_to_end_of_line(); cursor_down(); }
  433. X    move_to_modeline();
  434. X    clear_to_end_of_line();
  435. X    update_modeline();
  436. X    clear_message_line();
  437. X    if (screen->currLine()->length() > columns())
  438. X        leftshift_current_line(screen);
  439. X    else
  440. X        move_cursor(screen->savedYPos(), screen->savedXPos());
  441. X    synch_display();
  442. X}
  443. X
  444. X/*
  445. X** scroll_up_one_line - Scroll the listing up one line.
  446. X**                      We only call this routine when we KNOW that
  447. X**                      there is at least one line below the window
  448. X**                      which can be scrolled into it and the cursor
  449. X**                      is on the last line of the screen.
  450. X*/
  451. X
  452. Xstatic void scroll_up_one_line(DList *dl)
  453. X{
  454. X    dl->setFirst(dl->firstLine()->next());
  455. X    dl->setLast(dl->lastLine()->next());
  456. X    dl->setCurrLine(dl->lastLine());
  457. X
  458. X    if (CS)
  459. X    {
  460. X        scroll_listing_up_one();
  461. X        display_string(dl->currLine()->line(), dl->currLine()->length());
  462. X    }
  463. X    else if (DL || SF)
  464. X    {
  465. X        clear_modeline();
  466. X        scroll_screen_up_one();
  467. X        update_modeline();
  468. X        move_cursor(rows()-3, 0);
  469. X        display_string(dl->currLine()->line(), dl->currLine()->length());
  470. X    }
  471. X    else
  472. X        redisplay();
  473. X
  474. X    dl->saveYXPos(rows()-3, goal_column(dl));
  475. X    move_cursor(rows()-3, dl->savedXPos());
  476. X}
  477. X
  478. X/*
  479. X** scroll_down_one_line - Scroll the listing down one line.
  480. X**                        We only call this routine when we KNOW
  481. X**                        that the head\(\) of the listing is not visible
  482. X**                        and the cursor is on the first line in the window.
  483. X*/
  484. X
  485. Xstatic void scroll_down_one_line(DList *dl)
  486. X{
  487. X    if (lines_displayed(dl) == rows() - 2)
  488. X        //
  489. X        // Must update lastLine.  We previously had a screenfull of lines.
  490. X        //
  491. X        dl->setLast(dl->lastLine()->prev());
  492. X
  493. X    dl->setFirst(dl->firstLine()->prev());
  494. X    dl->setCurrLine(dl->firstLine());
  495. X
  496. X    if (CS)
  497. X    {
  498. X        scroll_listing_down_one();
  499. X        display_string(dl->currLine()->line(), dl->currLine()->length());
  500. X    }
  501. X    else if (AL || SR)
  502. X    {
  503. X        clear_modeline();
  504. X        scroll_screen_down_one();
  505. X        update_modeline();
  506. X        cursor_home();
  507. X        display_string(dl->currLine()->line(), dl->currLine()->length());
  508. X    }
  509. X    else
  510. X        redisplay();
  511. X
  512. X    dl->saveYXPos(0, goal_column(dl));
  513. X    move_cursor(0, dl->savedXPos());
  514. X}
  515. X
  516. X/*
  517. X** scroll_up_full_window - scroll listing up one full window,
  518. X**                         leaving one line of overlap.  This routine
  519. X**                         is only called when we know that the tail\(\)
  520. X**                         of the listing is not currently displayed.
  521. X*/
  522. X
  523. Xstatic void scroll_up_full_window(DList *dl)
  524. X{
  525. X    DLink *ln = dl->lastLine();
  526. X    dl->setFirst(ln);
  527. X    dl->setCurrLine(ln);
  528. X
  529. X    cursor_home();
  530. X    for (int i = 0; i < rows() - 2 && ln; i++, ln = ln->next())
  531. X    {
  532. X        clear_to_end_of_line();
  533. X        display_string(ln->line(), ln->length());
  534. X    }
  535. X    move_cursor(i, 0);
  536. X    for (; i < rows() - 2; i++) { clear_to_end_of_line(); cursor_down(); }
  537. X    
  538. X    ln ? dl->setLast(ln->prev()) : dl->setLast(dl->tail());
  539. X    dl->saveYXPos(0, goal_column(dl));
  540. X    if (dl->currLine()->length() > columns())
  541. X        leftshift_current_line(dl);
  542. X    else
  543. X        move_cursor(0, dl->savedXPos());
  544. X    
  545. X    synch_display();
  546. X}
  547. X
  548. X/*
  549. X** scroll_down_full_window - try to scroll listing down one full window,
  550. X**                           with one line of overlap.  This routine is
  551. X**                           only called when we KNOW that there is at
  552. X**                           least one line "above" the current listing.
  553. X**                           Only change the current line if it flows off
  554. X**                           the "bottom" of the screen.  This routine is
  555. X**                           only called when we know that the head\(\) of the
  556. X**                           listing isn\'t currently displayed.
  557. X*/
  558. X
  559. Xstatic void scroll_down_full_window(DList *dl)
  560. X{
  561. X    DLink *ln = dl->firstLine();
  562. X    for (int y = 0; y < rows() - 3 && ln != dl->head(); y++, ln = ln->prev());
  563. X    //
  564. X    // y == # of lines preceding firstLine\(\) to add to screen
  565. X    //
  566. X    dl->setFirst(ln);
  567. X    cursor_home();
  568. X    for (int j = 0; j < rows()-2 && ln; j++, ln = ln->next())
  569. X    {
  570. X        clear_to_end_of_line();
  571. X        display_string(ln->line(), ln->length());
  572. X    }
  573. X    move_cursor(j, 0);
  574. X    for (; j < rows() - 2; j++) { clear_to_end_of_line(); cursor_down(); }
  575. X    
  576. X    if (ln) dl->setLast(ln->prev());
  577. X    
  578. X    if (dl->savedYPos()+y >= rows()-2)
  579. X    {
  580. X        dl->setCurrLine(dl->lastLine());
  581. X        dl->saveYXPos(rows()-3, goal_column(dl));
  582. X    }
  583. X    else
  584. X        dl->saveYXPos(dl->savedYPos()+y, dl->savedXPos());
  585. X
  586. X    if (dl->currLine()->length() > columns())
  587. X        leftshift_current_line(dl);
  588. X    else
  589. X        move_cursor(dl->savedYPos(), dl->savedXPos());
  590. X    
  591. X    synch_display();
  592. X}
  593. X
  594. X/*
  595. X** scroll_up_half_window - scroll listing up half a window.  This routine
  596. X**                         is only called when the tail\(\) of the listing
  597. X**                         isn\'t being displayed.  We try to leave the
  598. X**                         cursor on the file it was on previously,
  599. X**                         otherwise it is left on the first file in
  600. X**                         the screen.
  601. X*/
  602. X
  603. Xstatic void scroll_up_half_window(DList *dl, int y)
  604. X{
  605. X    if (dl->currLine()->length() > columns()) rightshift_current_line(dl);
  606. X
  607. X    DLink *ln = dl->firstLine();
  608. X    for (int i = 0; i < (rows() - 2)/2; i++, ln = ln->next()) ;
  609. X    dl->setFirst(ln);
  610. X    
  611. X    if (CS || DL || SF || DLN)
  612. X    {
  613. X        if (CS)
  614. X            scroll_listing_up_N((rows()-2)/2);
  615. X        else
  616. X        {
  617. X            clear_modeline();
  618. X            scroll_screen_up_N((rows()-2)/2);
  619. X            update_modeline();
  620. X        }
  621. X        move_cursor(rows() - 2 -((rows()-2)/2), 0);
  622. X        ln = dl->lastLine()->next();
  623. X        for (i = 0; i < (rows() - 2)/2 && ln; i++, ln = ln->next())
  624. X            display_string(ln->line(), ln->length());
  625. X        ln ? dl->setLast(ln->prev()) : dl->setLast(dl->tail());
  626. X    }
  627. X    else
  628. X    {
  629. X        
  630. X        cursor_home();
  631. X        for (i = 0; i < rows() - 2 && ln->next(); i++, ln = ln->next())
  632. X        {
  633. X            clear_to_end_of_line();
  634. X            display_string(ln->line(), ln->length());
  635. X        }
  636. X        
  637. X        if (i != rows()-2)
  638. X        {
  639. X            //
  640. X            // We hit last line before outputing all that we could.
  641. X            // Must output lastLine\(\) == tail\(\).
  642. X            //
  643. X            display_string(ln->line(), ln->length());
  644. X            dl->setLast(ln);
  645. X            i++;  // so we know how many lines have been written
  646. X        }
  647. X        else
  648. X            dl->setLast(ln->prev());
  649. X
  650. X        // now clear any remaining rows on the screen
  651. X        move_cursor(i, 0);
  652. X        for (; i < rows() - 2; i++) { clear_to_end_of_line(); cursor_down(); }
  653. X    }
  654. X
  655. X    int pos = y - (rows()-2)/2;
  656. X    if (pos < 0) { pos = 0; dl->setCurrLine(dl->firstLine()); }
  657. X    
  658. X    dl->saveYXPos(pos, goal_column(dl));
  659. X    if (dl->currLine()->length() > columns())
  660. X        leftshift_current_line(dl);
  661. X    else
  662. X        move_cursor(pos, dl->savedXPos());
  663. X    
  664. X    synch_display();
  665. X}
  666. X
  667. X/*
  668. X** scroll_down_half_window - try to scroll listing down half a window.
  669. X**                           If `freshen\' is true, which is the default,
  670. X**                           the screen is refreshed.  It is important
  671. X**                           to note that we may not be able to scroll
  672. X**                           down a complete half window, since we
  673. X**                           always anchor the head of the listing to
  674. X**                           the first line in the screen.  This routine
  675. X**                           is only called when the head\(\) of the
  676. X**                           listing isn\'t being displayed.
  677. X*/
  678. X
  679. Xstatic void scroll_down_half_window(DList *dl, int y, int freshen = 1)
  680. X{
  681. X    if (dl->firstLine() != dl->head())
  682. X    {
  683. X        //
  684. X        // We can scroll down.  Try to leave the cursor on the file
  685. X        // it started out on.  Otherwise, leave it on the
  686. X        // \(rows\(\)-2\)/2 line, which was the previous firstLine\(\).
  687. X        //
  688. X        DLink *ln = dl->firstLine();
  689. X        for (int i = 0; i < (rows()-2)/2 && ln->prev(); i++, ln = ln->prev()) ;
  690. X        dl->setFirst(ln);
  691. X
  692. X        if (dl->currLine()->length() > columns()) rightshift_current_line(dl);
  693. X
  694. X        if (CS || AL || ALN || SR)
  695. X        {
  696. X            if (CS)
  697. X                scroll_listing_down_N(i);
  698. X            else
  699. X            {
  700. X                clear_modeline();
  701. X                scroll_screen_down_N(i);
  702. X                update_modeline();
  703. X                clear_message_line();
  704. X            }
  705. X            cursor_home();
  706. X            for (int j = 0; j < i; j++, ln = ln->next())
  707. X                display_string(ln->line(), ln->length());
  708. X            ln = dl->firstLine();
  709. X            for (int i = 0; i < rows()-2 && ln->next(); i++, ln = ln->next()) ;
  710. X            dl->setLast(ln);
  711. X        }
  712. X        else
  713. X        {
  714. X            cursor_home();
  715. X            for (int i = 0; i < rows()-2 && ln->next(); i++, ln = ln->next())
  716. X            {
  717. X                clear_to_end_of_line();
  718. X                display_string(ln->line(), ln->length());
  719. X            }
  720. X            
  721. X            if (i != rows() - 2)
  722. X            {
  723. X                //
  724. X                // We hit last line before outputing all that we could.
  725. X                // Must output lastLine\(\) == tail\(\).
  726. X                //
  727. X                display_string(ln->line(), ln->length());
  728. X                dl->setLast(ln);
  729. X                i++;  // so we know how many lines have been written
  730. X            }
  731. X            else
  732. X                dl->setLast(ln->prev());
  733. X
  734. X            // now clear any remaining rows on the screen
  735. X            int tmp = i;
  736. X            for (; i < rows()-2; i++) { clear_to_end_of_line();cursor_down(); }
  737. X            i = tmp;  // because we use `i\' below
  738. X        }
  739. X
  740. X        int pos = i + y;
  741. X        if (pos > rows() - 3)
  742. X        {
  743. X            pos = rows() - 3;
  744. X            dl->setCurrLine(dl->lastLine());
  745. X        }
  746. X
  747. X        dl->saveYXPos(pos, goal_column(dl));
  748. X        if (dl->currLine()->length() > columns())
  749. X            leftshift_current_line(dl);
  750. X        else
  751. X            move_cursor(pos, dl->savedXPos());
  752. X
  753. X        if (freshen) synch_display();
  754. X    }
  755. X}
  756. X
  757. X/*
  758. X** goto_first - position cursor on first line in listing.  This routine
  759. X**              is not called if atBegOfList\(\) is true.
  760. X*/
  761. X
  762. Xstatic void goto_first(DList *dl)
  763. X{
  764. X    if (dl->head() != dl->firstLine())
  765. X        initial_listing(dl);
  766. X    else
  767. X    {
  768. X        if (dl->currLine()->length() > columns()) rightshift_current_line(dl);
  769. X        dl->setCurrLine(dl->head());
  770. X    }
  771. X
  772. X    dl->saveYXPos(0, goal_column(dl));
  773. X    if (dl->currLine()->length() > columns())
  774. X        leftshift_current_line(dl);
  775. X    else
  776. X        move_cursor(0, dl->savedXPos());
  777. X
  778. X    synch_display();
  779. X}
  780. X
  781. X/*
  782. X** goto_last - position cursor on last file in listing.  This routine is
  783. X**             not called if atEndOfList\(\) is true.
  784. X*/
  785. X
  786. Xstatic void goto_last(DList *dl)
  787. X{
  788. X    if (dl->currLine()->length() > columns()) rightshift_current_line(dl);
  789. X
  790. X    dl->setCurrLine(dl->tail());
  791. X
  792. X    if (dl->tail() == dl->lastLine())
  793. X    {
  794. X        //
  795. X        // Only need to reposition the cursor.
  796. X        //
  797. X        dl->saveYXPos(lines_displayed(dl) - 1, goal_column(dl));
  798. X        if (dl->currLine()->length() > columns())
  799. X            leftshift_current_line(dl);
  800. X        else
  801. X            move_cursor(dl->savedYPos(), dl->savedXPos());
  802. X    }
  803. X    else
  804. X    {
  805. X        //
  806. X        // Redisplay end of listing & update our view.
  807. X        //
  808. X        DLink *ln = dl->tail();
  809. X        dl->setLast(ln);
  810. X        for (int i = 0; i < rows() - 2; i++, ln = ln->prev())
  811. X        {
  812. X            move_cursor(rows() - 3 - i, 0);
  813. X            clear_to_end_of_line();
  814. X            display_string(ln->line(), ln->length());
  815. X        }
  816. X        dl->setFirst(ln->next());
  817. X        dl->saveYXPos(rows() - 3,goal_column(dl));
  818. X        if (dl->currLine()->length() > columns())
  819. X            leftshift_current_line(dl);
  820. X        else
  821. X            move_cursor(rows() -3 , dl->savedXPos());
  822. X    }
  823. X    synch_display();
  824. X}
  825. X
  826. X
  827. X/*
  828. X** type_any_key_to_continue - ask user to type any key to continue.
  829. X**                            Done in standout mode.
  830. X*/
  831. X
  832. Xstatic inline void type_any_key_to_continue()
  833. X{
  834. X    eat_a_character("Press Any Key to Continue", redisplay, 1);
  835. X}
  836. X
  837. X/*
  838. X** examine_current_problem - attempt to examine the current problem.
  839. X*/
  840. X
  841. Xstatic void examine_current_problem(const DList *dl)
  842. X{
  843. X    char *number = get_prob_number(dl);
  844. X    if (examine_problem(number))
  845. X        redisplay();
  846. X    else
  847. X    {
  848. X        move_cursor(dl->savedYPos(), dl->savedXPos());
  849. X        synch_display();
  850. X    }
  851. X}
  852. X
  853. X/*
  854. X** update_problem_listing - updates the current line in the listing.
  855. X**                          `number\' is the number of the problem
  856. X**                          in the current line.
  857. X*/
  858. X
  859. Xstatic void update_problem_listing(DList *dl, const char *number)
  860. X{
  861. X    datum key;
  862. X    key.dptr  = (char *) number;
  863. X    key.dsize = (int)strlen(key.dptr) + 1;
  864. X    open_database(GDBM_READER);
  865. X    datum data = gdbm_fetch(GdbmFile, key);
  866. X    gdbm_close(GdbmFile);
  867. X    if (data.dptr)
  868. X    {
  869. X        char *newline = summary_info(data);
  870. X        dl->currLine()->update(&newline);
  871. X        free(data.dptr);
  872. X    }
  873. X}
  874. X
  875. X/*
  876. X** append_current_problem - attempt to append to the current problem.
  877. X**                          If successful, updates the listing line.
  878. X*/
  879. X
  880. Xstatic void append_current_problem(DList *dl)
  881. X{
  882. X    char *number = get_prob_number(dl);
  883. X    if (append_to_problem(number))
  884. X    {
  885. X        //
  886. X        // Replace old listing line with new.
  887. X        //
  888. X        update_problem_listing(dl, number);
  889. X        redisplay();
  890. X    }
  891. X    else
  892. X    {
  893. X        move_cursor(dl->savedYPos(), dl->savedXPos());
  894. X        synch_display();
  895. X    }
  896. X}
  897. X
  898. X/*
  899. X** close_current_problem - attempt to close the current problem
  900. X*/
  901. X
  902. Xstatic void close_current_problem(DList *dl)
  903. X{
  904. X    char *number = get_prob_number(dl);
  905. X    if (close_problem(number))
  906. X    {
  907. X        //
  908. X        // Replace old listing line with new.
  909. X        //
  910. X        update_problem_listing(dl, number);
  911. X        redisplay();
  912. X    }
  913. X    else
  914. X    {
  915. X        move_cursor(dl->savedYPos(), dl->savedXPos());
  916. X        synch_display();
  917. X    }
  918. X}
  919. X
  920. X/*
  921. X** reopen_current_problem - attempt to reopen the current problem
  922. X*/
  923. X
  924. Xstatic void reopen_current_problem(DList *dl)
  925. X{
  926. X    char *number = get_prob_number(dl);
  927. X    if (reopen_problem(number))
  928. X    {
  929. X        //
  930. X        // Replace old listing line with new.
  931. X        //
  932. X        update_problem_listing(dl, number);
  933. X        redisplay();
  934. X    }
  935. X    else
  936. X    {
  937. X        move_cursor(dl->savedYPos(), dl->savedXPos());
  938. X        synch_display();
  939. X    }
  940. X}
  941. X
  942. X/*
  943. X** modify_current_keywords - attempt to modify the keywords of the
  944. X**                           current problem
  945. X*/
  946. X
  947. Xstatic void modify_current_keywords(DList *dl)
  948. X{
  949. X    char *number = get_prob_number(dl);
  950. X    if (modify_keywords(number))
  951. X    {
  952. X        //
  953. X        // Replace old listing line with new.
  954. X        //
  955. X        update_problem_listing(dl, number);
  956. X        redisplay();
  957. X    }
  958. X    else
  959. X    {
  960. X        move_cursor(dl->savedYPos(), dl->savedXPos());
  961. X        synch_display();
  962. X    }
  963. X}
  964. X
  965. X/*
  966. X** remove_listing_line - delete the current line in the DList
  967. X**                       and update both the screen and data
  968. X**                       structures appropriately.  `y\' is the position
  969. X**                       in the window of the current line.  Returns 0
  970. X**                       if we\'ve removed the last line in the listing,
  971. X**                       otherwise returns 1.
  972. X*/
  973. X
  974. Xstatic int remove_listing_line(DList *dl, int y)
  975. X{
  976. X    if (dl->lastLine() != dl->tail())
  977. X    {
  978. X        //
  979. X        // Last line in listing isn\'t in window - scroll up one line.
  980. X        //
  981. X        dl->setLast(dl->lastLine()->next());
  982. X        dl->deleteLine();
  983. X
  984. X        if (CS || DL)
  985. X        {
  986. X            if (CS)
  987. X                delete_listing_line(y);
  988. X            else
  989. X            {
  990. X                clear_modeline();
  991. X                delete_screen_line(y);
  992. X                update_modeline();
  993. X            }
  994. X            move_cursor(rows()-3, 0);
  995. X            display_string(dl->lastLine()->line(), dl->lastLine()->length());
  996. X        }
  997. X        else
  998. X        {
  999. X            clear_to_end_of_screen(y);
  1000. X            move_cursor(y, 0);
  1001. X            DLink *ln = dl->currLine();
  1002. X            for (int i = y; i < rows()-2; i++, ln = ln->next())
  1003. X                display_string(ln->line(), ln->length());
  1004. X            update_modeline();
  1005. X        }
  1006. X        dl->saveYXPos(y, goal_column(dl));
  1007. X    } else
  1008. X    {
  1009. X        //
  1010. X        // Last line of listing is visible in window.
  1011. X        //
  1012. X        if (dl->atWindowTop() && dl->atWindowBot())
  1013. X        {
  1014. X            //
  1015. X            // The last line in the window is also the first line.
  1016. X            //
  1017. X            if (dl->nelems() == 1)
  1018. X            {
  1019. X                cursor_home();
  1020. X                clear_to_end_of_line();
  1021. X                return 0;
  1022. X            }
  1023. X            scroll_down_half_window(dl, y, 0);
  1024. X            dl->deleteLine();
  1025. X            DLink *ln = dl->firstLine();
  1026. X            for (int pos = 0; ln != dl->tail(); pos++, ln = ln->next()) ;
  1027. X            dl->saveYXPos(pos, goal_column(dl));
  1028. X            move_cursor(pos+1, 0);
  1029. X            clear_to_end_of_line();
  1030. X            move_cursor(pos, dl->savedXPos());
  1031. X        }
  1032. X        else if (dl->atWindowBot())
  1033. X        {
  1034. X            //
  1035. X            // We want to delete the last line in the window.
  1036. X            //
  1037. X            dl->deleteLine();
  1038. X            move_cursor(y, 0);
  1039. X            clear_to_end_of_line();
  1040. X            dl->saveYXPos(y-1, goal_column(dl));
  1041. X            move_cursor(y-1, dl->savedXPos());
  1042. X        }
  1043. X        else
  1044. X        {
  1045. X            //
  1046. X            // We are in the middle of the listing.
  1047. X            //
  1048. X            dl->deleteLine();
  1049. X            if (CS || DL)
  1050. X            {
  1051. X                if (CS)
  1052. X                    delete_listing_line(y);
  1053. X                else
  1054. X                {
  1055. X                    clear_modeline();
  1056. X                    delete_screen_line(y);
  1057. X                    update_modeline();
  1058. X                }
  1059. X            } else
  1060. X            {
  1061. X                clear_to_end_of_screen(y);
  1062. X                move_cursor(y, 0);
  1063. X                for (DLink *ln = dl->currLine(); ln; ln = ln->next())
  1064. X                    display_string(ln->line(), ln->length());
  1065. X                update_modeline();
  1066. X            }
  1067. X            dl->saveYXPos(y, goal_column(dl));
  1068. X         }
  1069. X    }
  1070. X    if (dl->currLine()->length() > columns())
  1071. X        leftshift_current_line(dl);
  1072. X    else
  1073. X        move_cursor(dl->savedYPos(), dl->savedXPos());
  1074. X    return 1;
  1075. X}
  1076. X
  1077. X/*
  1078. X** delete_current_problem - attempt to delete the current problem.  Returns
  1079. X**                          0 if there are no more problems to view.
  1080. X*/
  1081. X
  1082. Xstatic int delete_current_problem(DList *dl)
  1083. X{
  1084. X    char *number = get_prob_number(dl);
  1085. X
  1086. X    if (delete_problem(number))
  1087. X        if (remove_listing_line(dl, dl->savedYPos()) == 0)
  1088. X            return 0;
  1089. X
  1090. X    move_cursor(dl->savedYPos(), dl->savedXPos());
  1091. X    synch_display();
  1092. X    return 1;
  1093. X}
  1094. X
  1095. X/*
  1096. X** save_problem_listing - saves problem listing to a file of the users choice.
  1097. X**                        If the first character of the filename is `~\', the
  1098. X**                        tilde is replaced by the users home directory.
  1099. X*/
  1100. X
  1101. Xstatic void save_problem_listing(const DList *dl)
  1102. X{
  1103. X    message_window_dirty = 1; // force message window dirty
  1104. X    int status;
  1105. X    char *file = prompt("Filename --> ", redisplay);
  1106. X    const char *fullname;
  1107. X    fullname = *file == '~' ? expand_tilde(file) : file;
  1108. X    pid_t pid = fork();
  1109. X    switch(pid)
  1110. X    {
  1111. X      case -1: // error
  1112. X        message("Sorry, can't write, fork() failed");
  1113. X        break;
  1114. X      case 0:  // in the child
  1115. X        if (setuid(getuid()) < 0)
  1116. X            exit(1);
  1117. X        else
  1118. X        {
  1119. X            FILE *fp = fopen(fullname, "w");
  1120. X            if (!fp)
  1121. X                exit(1);
  1122. X            else
  1123. X            {
  1124. X                // try to write the file
  1125. X                for (DLink *ln = dl->head(); ln; ln = ln->next())
  1126. X                    (void)fprintf(fp, "%s\n", ln->line());
  1127. X                if (ferror(fp)) exit(1);
  1128. X            }
  1129. X            (void)fclose(fp);
  1130. X        }
  1131. X        exit(0);
  1132. X      default: // in the parent
  1133. X        waitpid(pid, &status, 0);
  1134. X        if (!status)
  1135. X            message("listing saved to file `%'", fullname);
  1136. X        else
  1137. X            message("problem saving listing, sorry");
  1138. X        DELETE file;
  1139. X        break;
  1140. X    }
  1141. X    move_cursor(dl->savedYPos(), dl->savedXPos());
  1142. X    synch_display();        
  1143. X}
  1144. X
  1145. X/*
  1146. X** help - give some help.  Deal with SIGWINCH and SIGTSTP.
  1147. X*/
  1148. X
  1149. Xstatic void help()
  1150. X{
  1151. X    String old_modeline(current_modeline);
  1152. X    update_modeline("----- HELP");
  1153. X    
  1154. X    int position = 0;
  1155. X    char key;
  1156. X    do {
  1157. X        cursor_home();
  1158. X        for (int i = 0; i < rows() - 2 && i + position < HELP_FILE_DIM; i++)
  1159. X        {
  1160. X            clear_to_end_of_line();
  1161. X            display_string(help_file[position + i]);
  1162. X        }
  1163. X        move_cursor(i, 0);
  1164. X        for (; i < rows() - 2; i++) { clear_to_end_of_line(); cursor_down(); }
  1165. X        clear_message_line();
  1166. X
  1167. X        if (position + rows() -2 >= HELP_FILE_DIM)
  1168. X            // the tail of the help message
  1169. X            (void)fputs(HELP_MSG[2], stdout);
  1170. X        else if (position == 0)
  1171. X            // the head of the help message
  1172. X            (void)fputs(HELP_MSG[0], stdout);
  1173. X        else
  1174. X            //  somewhere in between
  1175. X            (void)fputs(HELP_MSG[1], stdout);
  1176. X        synch_display();
  1177. X
  1178. X        if (resumingAfterSuspension ||
  1179. X#ifdef SIGWINCH
  1180. X            windowSizeChanged       ||
  1181. X#endif
  1182. X            read(0, &key, 1) < 0 // assume fails only when errno == EINTR
  1183. X            )
  1184. X        {
  1185. X#ifdef SIGWINCH
  1186. X            if (windowSizeChanged) { windowSizeChanged = 0; adjust_window(); }
  1187. X#endif
  1188. X            resumingAfterSuspension = 0;
  1189. X            redisplay();
  1190. X        }
  1191. X        else if (key == KEY_SPC)
  1192. X        {
  1193. X            if (position >= HELP_FILE_DIM - 1) break;
  1194. X            position += rows() - 2;
  1195. X        }
  1196. X        else if (key == *BC)
  1197. X        {
  1198. X            if (position == 0) break;
  1199. X            position -= rows() - 2;
  1200. X        }
  1201. X        else 
  1202. X            break;  // return to the listing
  1203. X    }
  1204. X    while (position < HELP_FILE_DIM - 1);
  1205. X
  1206. X    update_modeline(old_modeline);
  1207. X    redisplay();
  1208. X}
  1209. X
  1210. X/*
  1211. X** shell_command - execute a shell command.
  1212. X**                 If *cmd == \'\0\', start up a shell.
  1213. X**                 If *cmd == \'!\', reexecute most recent shell command.
  1214. X*/
  1215. X
  1216. Xstatic void shell_command(DList *dl)
  1217. X{
  1218. X    static String saved_cmd;
  1219. X    static String saved_shell;
  1220. X    const char *separators   = " \t";     // separators for tokenize\(\)
  1221. X    const char *cmd  = prompt("!", redisplay);
  1222. X
  1223. X    if (cmd == 0)
  1224. X        return;  // aborted
  1225. X    else if (*cmd == 0)
  1226. X    {
  1227. X        //
  1228. X        // Start up a shell.
  1229. X        //
  1230. X        if (saved_shell == "") saved_shell = getenv("SHELL");
  1231. X        if (saved_shell == "") saved_shell = "sh";
  1232. X        saved_cmd = saved_shell;
  1233. X
  1234. X        const char *slash = strrchr(saved_shell, '/');
  1235. X        const char *args[2];
  1236. X        args[0] = slash ? slash + 1 : (const char *)saved_shell;
  1237. X        args[1] = 0;
  1238. X
  1239. X        message("Starting interactive shell ...");
  1240. X        cursor_wrap();
  1241. X        synch_display();
  1242. X        execute(saved_shell, args);
  1243. X    }
  1244. X    else if (*cmd == '!')
  1245. X    {
  1246. X        //
  1247. X        //  Re-execute previously saved command.
  1248. X        //
  1249. X        if (saved_cmd != "")
  1250. X        {
  1251. X            message(saved_cmd);
  1252. X            cursor_wrap();
  1253. X            synch_display();
  1254. X            const char **args = tokenize(saved_cmd, separators);
  1255. X            execute(args[0], args);
  1256. X            type_any_key_to_continue();
  1257. X        }
  1258. X        else
  1259. X        {
  1260. X            ding();
  1261. X            message("No previous shell command");
  1262. X            move_cursor(dl->savedYPos(), dl->savedXPos());
  1263. X            synch_display();
  1264. X            return;
  1265. X        }
  1266. X    } else
  1267. X    {
  1268. X        //
  1269. X        // Execute command.
  1270. X        //
  1271. X        saved_cmd = cmd;
  1272. X        message(saved_cmd);
  1273. X        cursor_wrap();
  1274. X        synch_display();
  1275. X        const char **args = tokenize(saved_cmd, separators);
  1276. X        execute(args[0], args);
  1277. X        type_any_key_to_continue();
  1278. X    }
  1279. X    redisplay();
  1280. X}
  1281. X
  1282. X/*
  1283. X** read_from_keybd - read a key from the keyboard, taking care
  1284. X**                   of SIGTSTPs and SIGWINCHs.  If the read\(\) fails,
  1285. X**                   we assume it\'s because we caught a SIGTSTP
  1286. X**                   or SIGWINCH.  In that case we redraw the screen
  1287. X**                   with redisplay\(\).
  1288. X*/
  1289. X
  1290. Xstatic char read_from_keybd()
  1291. X{
  1292. X    char key;
  1293. X    while (1)
  1294. X    {
  1295. X        if (resumingAfterSuspension ||
  1296. X#ifdef SIGWINCH
  1297. X            windowSizeChanged       ||
  1298. X#endif
  1299. X            read(0, &key, 1) < 0)  // assume only fails when errno==EINTR
  1300. X        {
  1301. X#ifdef SIGWINCH
  1302. X            if (windowSizeChanged)
  1303. X            {
  1304. X                windowSizeChanged = 0;
  1305. X                adjust_window();
  1306. X            }
  1307. X#endif
  1308. X            resumingAfterSuspension = 0;
  1309. X            redisplay();
  1310. X            continue;
  1311. X        }
  1312. X        return key;
  1313. X    }
  1314. X}
  1315. X
  1316. X/*
  1317. X** get_key - reads a key and then clears the message window,
  1318. X**           if it needs to be cleared. Used only by read_commands in the
  1319. X**           main switch statement so that message\(\) doesn\'t need to sleep\(\)
  1320. X**           and clear\(\) on the messages that get written.  This way, the
  1321. X**           message window is cleared after each keypress within the main
  1322. X**           loop, when necessary.  We also check for and deal with window
  1323. X**           size changes and the UP and DOWN arrow keys here.
  1324. X*/
  1325. X
  1326. Xstruct arrow_key {
  1327. X    int len;
  1328. X    int *seq;
  1329. X    arrow_key(const char *);
  1330. X};
  1331. X
  1332. Xarrow_key::arrow_key(const char *str)
  1333. X{
  1334. X    if (str == 0)
  1335. X    {
  1336. X        //
  1337. X        // The capability isn\'t defined.
  1338. X        //
  1339. X        len = 0;
  1340. X        seq = 0;
  1341. X        return;
  1342. X    }
  1343. X
  1344. X    seq = new int[12]; // should be ample
  1345. X
  1346. X    int i = 0;
  1347. X    do
  1348. X    {
  1349. X        switch (*str)
  1350. X        {
  1351. X          case '\\':
  1352. X          {
  1353. X              int c = *++str;
  1354. X              switch (c)
  1355. X              {
  1356. X                case 'E':  seq[i++] = 0x1b; break;
  1357. X                case 'b':  seq[i++] = '\b'; break;
  1358. X                case 'f':  seq[i++] = '\f'; break;
  1359. X                case 'n':  seq[i++] = '\n'; break;
  1360. X                case 'r':  seq[i++] = '\r'; break;
  1361. X                case 't':  seq[i++] = '\t'; break;
  1362. X                case 'v':  seq[i++] = '\v'; break;
  1363. X                case '\\': seq[i++] = '\\'; break;
  1364. X                case '\'': seq[i++] = '\''; break;
  1365. X                case '\"': seq[i++] = '\"'; break;
  1366. X                case '^':  seq[i++] = '^';  break;
  1367. X                default:
  1368. X                    error("invalid escape in /etc/termcap for arrow key: \\%c", c);
  1369. X                    break;
  1370. X              }
  1371. X              break;
  1372. X          }
  1373. X          case '^':
  1374. X          {
  1375. X              int c = *++str;
  1376. X              if (isalpha(c))
  1377. X              {
  1378. X                  seq[i] = (c > 'a' ? c - 'a' : c - 'A') + 1;
  1379. X                  i++;  // g++ 2.2.2 chokes if I write seq\[i++\] = ...
  1380. X              }
  1381. X              else
  1382. X                  switch(c)
  1383. X                  {
  1384. X                    case '[':  seq[i++] = 0x1b; break;
  1385. X                    case '\\': seq[i++] = 0x1c; break;
  1386. X                    case ']':  seq[i++] = 0x1d; break;
  1387. X                    case '^':  seq[i++] = 0x1e; break;
  1388. X                    case '_':  seq[i++] = 0x1f; break;
  1389. X                    default:
  1390. X                        error("invalid control sequence for arrow key: ^%c", c);
  1391. X                        break;
  1392. X                  }
  1393. X              }
  1394. X              break;
  1395. X          default: seq[i++] = *str; break;
  1396. X        }
  1397. X    } while (*++str);
  1398. X    len = i;
  1399. X}
  1400. X
  1401. Xstatic char get_key(DList *dl)
  1402. X{
  1403. X    int index = 0;
  1404. X    char key;
  1405. X    static arrow_key up(KU), down(KD);
  1406. X    static char *keys;
  1407. X    static int remaining = 0;
  1408. X
  1409. X    if (!keys) keys = new char[max(up.len, down.len)];
  1410. X
  1411. X    if (remaining)
  1412. X    {
  1413. X        //
  1414. X        // We have some characters left over from a partial match
  1415. X        // of an arrow key; use them up.
  1416. X        //
  1417. X        key = keys[0];
  1418. X        remaining--;
  1419. X        for (int i = 0; i < remaining; i++) keys[i] = keys[i+1];
  1420. X        return key;
  1421. X    }
  1422. X    else
  1423. X        key = read_from_keybd();
  1424. X
  1425. X    if (message_window_dirty)
  1426. X    {
  1427. X        clear_message_line();
  1428. X        move_cursor(dl->savedYPos(), dl->savedXPos());
  1429. X        synch_display();
  1430. X        message_window_dirty = 0;
  1431. X    }
  1432. X
  1433. X    //
  1434. X    // Now deal with potential arrow keys.
  1435. X    //
  1436. X    if (KU || KD)
  1437. X    {
  1438. X        for (index = 0; (index < up.len && up.seq[index] == key) ||
  1439. X             (index < down.len && down.seq[index] == key); index++)
  1440. X        {
  1441. X            if ((up.len - 1) == index && up.seq[index] == key)
  1442. X                return KEY_ARROW_UP;
  1443. X            if ((down.len - 1) == index && down.seq[index] == key)
  1444. X                return KEY_ARROW_DOWN;
  1445. X            if (index == (max(up.len, down.len) - 1)) break;
  1446. X            keys[index] = key;
  1447. X            key = read_from_keybd();
  1448. X        }
  1449. X        if (index == 0)
  1450. X            return key; // no initial match -- the most usual case
  1451. X        else
  1452. X        {
  1453. X            //
  1454. X            // We had a partial match, but not a complete one.
  1455. X            // We must return the characters which we\'ve read in
  1456. X            // the proper order so that the main command loop can
  1457. X            // check for matches.  The problem here is the potential
  1458. X            // ambiguity between what the terminal claims to be arrow
  1459. X            // keys and what has been hardcoded as commands.
  1460. X            //
  1461. X            keys[index] = key;
  1462. X            key = keys[0];  // what we\'ll return to the command loop
  1463. X            for (int i = 0; i < index; i++) keys[i] = keys[i+1];
  1464. X            remaining = index;
  1465. X            return key;
  1466. X        }
  1467. X    }
  1468. X    else
  1469. X        return key;
  1470. X}
  1471. X
  1472. X/*
  1473. X** read_commands - the command loop
  1474. X*/
  1475. X
  1476. Xvoid lister_cmd_loop(DList *dl)
  1477. X{
  1478. X    int key;
  1479. X    for (;;)
  1480. X    {
  1481. X        switch (key = get_key(dl))
  1482. X        {
  1483. X          case KEY_j:
  1484. X          case KEY_n:
  1485. X          case KEY_CTL_N:
  1486. X          case KEY_SPC:
  1487. X          case KEY_CR:
  1488. X          case KEY_ARROW_DOWN:
  1489. X            if (dl->atEndOfList())
  1490. X            {
  1491. X                ding();
  1492. X                break;
  1493. X            }
  1494. X            if (dl->currLine()->length() > columns()) 
  1495. X                rightshift_current_line(dl);
  1496. X            if (dl->savedYPos() < rows() - 3)
  1497. X            {
  1498. X                // There are still more lines below us in the window
  1499. X                // so we just move the cursor down one line.
  1500. X                dl->setCurrLine(dl->currLine()->next());
  1501. X                int x = goal_column(dl);
  1502. X                if (x == dl->savedXPos())
  1503. X                    cursor_down();
  1504. X                else
  1505. X                    move_cursor(dl->savedYPos() + 1, x);
  1506. X                dl->saveYXPos(dl->savedYPos() + 1, x);
  1507. X            }
  1508. X            else
  1509. X                //
  1510. X                // We are on the last line on the screen and there
  1511. X                // are more lines to display.  Scroll up one line
  1512. X                // and leave the cursor on the next logical line.
  1513. X                //
  1514. X                scroll_up_one_line(dl);
  1515. X            if (dl->currLine()->length() > columns())
  1516. X                leftshift_current_line(dl);
  1517. X            synch_display();
  1518. X            break;
  1519. X
  1520. X          case KEY_k:
  1521. X          case KEY_p:
  1522. X          case KEY_CTL_P:
  1523. X          case KEY_CTL_Y:
  1524. X          case KEY_ARROW_UP:
  1525. X            if (dl->atBegOfList())
  1526. X            {
  1527. X                ding();
  1528. X                break;
  1529. X            }
  1530. X            if (dl->currLine()->length() > columns())
  1531. X                rightshift_current_line(dl);
  1532. X            if (dl->savedYPos() != 0)
  1533. X            {
  1534. X                // We aren\'t at the top of the window so can move up.
  1535. X                dl->setCurrLine(dl->currLine()->prev());
  1536. X                int x = goal_column(dl);
  1537. X                if (x == dl->savedXPos() && UP)
  1538. X                    cursor_up();
  1539. X                else
  1540. X                    move_cursor(dl->savedYPos() - 1, x);
  1541. X                dl->saveYXPos(dl->savedYPos() - 1, x);
  1542. X            }
  1543. X           else
  1544. X               //
  1545. X               // We are on the first line of the window and there are
  1546. X               // lines preceding us in the directory listing.
  1547. X               //
  1548. X               scroll_down_one_line(dl);
  1549. X            if (dl->currLine()->length() > columns())
  1550. X                leftshift_current_line(dl);
  1551. X            synch_display();
  1552. X            break;
  1553. X
  1554. X          case KEY_CTL_F:
  1555. X          case KEY_CTL_V:
  1556. X            if (dl->lastLine() == dl->tail())
  1557. X            {
  1558. X                ding();
  1559. X                break;
  1560. X            }
  1561. X            scroll_up_full_window(dl);
  1562. X            break;
  1563. X
  1564. X          case KEY_b:
  1565. X          case KEY_CTL_B:
  1566. X            if (dl->firstLine() == dl->head())
  1567. X            {
  1568. X                ding();
  1569. X                break;
  1570. X            }
  1571. X            scroll_down_full_window(dl);
  1572. X            break;
  1573. X
  1574. X          case KEY_CTL_D:
  1575. X            if (dl->lastLine() == dl->tail())
  1576. X            {
  1577. X                ding();
  1578. X                break;
  1579. X            }
  1580. X            scroll_up_half_window(dl, dl->savedYPos());
  1581. X            break;
  1582. X
  1583. X          case KEY_CTL_U:
  1584. X            if (dl->firstLine() == dl->head())
  1585. X            {
  1586. X                ding();
  1587. X                break;
  1588. X            }
  1589. X            scroll_down_half_window(dl, dl->savedYPos());
  1590. X            break;
  1591. X
  1592. X          case KEY_TOP:
  1593. X            if (dl->atBegOfList())
  1594. X            {
  1595. X                ding();
  1596. X                break;
  1597. X            }
  1598. X            goto_first(dl);
  1599. X            break;
  1600. X
  1601. X          case KEY_BOT:
  1602. X            if (dl->atEndOfList())
  1603. X            {
  1604. X                ding();
  1605. X                break;
  1606. X            }
  1607. X            goto_last(dl);
  1608. X            break;
  1609. X
  1610. X          case KEY_e:
  1611. X          case KEY_m:
  1612. X          case KEY_v:
  1613. X            examine_current_problem(dl); break;
  1614. X
  1615. X          case KEY_a:
  1616. X            append_current_problem(dl); break;
  1617. X
  1618. X          case KEY_c:
  1619. X            close_current_problem(dl); break;
  1620. X
  1621. X          case KEY_d:
  1622. X            if (delete_current_problem(dl) == 0)
  1623. X                //
  1624. X                // No more problems to view.
  1625. X                //
  1626. X                return;
  1627. X            break;
  1628. X
  1629. X          case KEY_r:
  1630. X            reorganize_database(0);
  1631. X            move_cursor(dl->savedYPos(), dl->savedXPos());
  1632. X            synch_display();
  1633. X            break;
  1634. X
  1635. X          case KEY_M:
  1636. X            modify_current_keywords(dl); break;
  1637. X
  1638. X          case KEY_R:
  1639. X            reopen_current_problem(dl); break;
  1640. X
  1641. X          case KEY_S:
  1642. X            save_problem_listing(dl); break;
  1643. X
  1644. X          case KEY_QM: case KEY_H:
  1645. X            help(); break;
  1646. X
  1647. X          case KEY_BANG:
  1648. X            shell_command(dl); break;
  1649. X
  1650. X          case KEY_V:
  1651. X            message(Version);
  1652. X            move_cursor(dl->savedYPos(), dl->savedXPos());
  1653. X            synch_display();
  1654. X            break;
  1655. X
  1656. X          case KEY_CTL_L:
  1657. X            redisplay(); break;
  1658. X
  1659. X          case KEY_q:
  1660. X            return;
  1661. X
  1662. X          case KEY_ESC:
  1663. X            //
  1664. X            // Some Emacs ESC key bindings.
  1665. X            //
  1666. X            switch(get_key(dl))
  1667. X            {
  1668. X              case KEY_v:
  1669. X                if (dl->firstLine() == dl->head())
  1670. X                {
  1671. X                    ding();
  1672. X                    break;
  1673. X                }
  1674. X                scroll_down_full_window(dl);
  1675. X                break;
  1676. X
  1677. X              case KEY_TOP:
  1678. X                if (dl->atBegOfList())
  1679. X                {
  1680. X                    ding();
  1681. X                    break;
  1682. X                }
  1683. X                goto_first(dl);
  1684. X                break;
  1685. X
  1686. X              case KEY_BOT:
  1687. X                if (dl->atEndOfList())
  1688. X                {
  1689. X                    ding();
  1690. X                    break;
  1691. X                }
  1692. X                goto_last(dl);
  1693. X                break;
  1694. X
  1695. X              default:
  1696. X                ding();
  1697. X                break;
  1698. X            }
  1699. X            break;
  1700. X
  1701. X          default: ding(); break;
  1702. X        }
  1703. X    }
  1704. X}
  1705. END_OF_FILE
  1706. if test 38236 -ne `wc -c <'lister.C'`; then
  1707.     echo shar: \"'lister.C'\" unpacked with wrong size!
  1708. fi
  1709. # end of 'lister.C'
  1710. fi
  1711. echo shar: End of archive 4 \(of 7\).
  1712. cp /dev/null ark4isdone
  1713. MISSING=""
  1714. for I in 1 2 3 4 5 6 7 ; do
  1715.     if test ! -f ark${I}isdone ; then
  1716.     MISSING="${MISSING} ${I}"
  1717.     fi
  1718. done
  1719. if test "${MISSING}" = "" ; then
  1720.     echo You have unpacked all 7 archives.
  1721.     rm -f ark[1-9]isdone
  1722. else
  1723.     echo You still need to unpack the following archives:
  1724.     echo "        " ${MISSING}
  1725. fi
  1726. ##  End of shell archive.
  1727. exit 0
  1728.  
  1729. exit 0 # Just in case...
  1730.