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

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