home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume33 / problem1 / part04 < prev    next >
Encoding:
Text File  |  1992-11-12  |  49.5 KB  |  1,831 lines

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