home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume35 / ipick / part02 < prev    next >
Encoding:
Text File  |  1993-03-03  |  60.7 KB  |  2,737 lines

  1. Newsgroups: comp.sources.misc
  2. From: markd@werple.apana.org.au (Mark Delany)
  3. Subject: v35i118:  ipick - an interactive filter to pick lines, Part02/05
  4. Message-ID: <1993Mar4.192417.9520@sparky.imd.sterling.com>
  5. X-Md4-Signature: b83f9ad9af2fc911492bbcc7d65d30f2
  6. Date: Thu, 4 Mar 1993 19:24:17 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: markd@werple.apana.org.au (Mark Delany)
  10. Posting-number: Volume 35, Issue 118
  11. Archive-name: ipick/part02
  12. Environment: UNIX, Curses
  13.  
  14. #! /bin/sh
  15. # This is a shell archive.  Remove anything before this line, then feed it
  16. # into a shell via "sh file" or similar.  To overwrite existing files,
  17. # type "sh file -c".
  18. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  19. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  20. # Contents:  ipick/ipick.h ipick/misc.c ipick/command.c ipick/data.c
  21. #   ipick/help.c ipick/port.c ipick/config/gould
  22. # Wrapped by markd@bushwire on Sun Feb 28 10:06:38 1993
  23. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  24. echo If this archive is complete, you will see the following message:
  25. echo '          "shar: End of archive 2 (of 5)."'
  26. if test -f 'ipick/ipick.h' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'ipick/ipick.h'\"
  28. else
  29.   echo shar: Extracting \"'ipick/ipick.h'\" \(10318 characters\)
  30.   sed "s/^X//" >'ipick/ipick.h' <<'END_OF_FILE'
  31. X/* $Id: ipick.h,v 1.1 1993/02/27 21:48:01 markd Exp $
  32. X *
  33. X * macros, #defines and prototypes
  34. X *
  35. X *    Copyright (c) 1993, Mark Delany
  36. X *
  37. X *    You may distribute under the terms of either the GNU General Public
  38. X *    License or the Artistic License, as specified in the README file.
  39. X *
  40. X * $Log: ipick.h,v $
  41. X * Revision 1.1  1993/02/27  21:48:01  markd
  42. X * Initial revision
  43. X *
  44. X */
  45. X
  46. X#define    NAME        "ipick"
  47. X#define    VERSION        "1.1"
  48. X#define    RELEASE        ""    /* " Alpha", " Beta" or "" */
  49. X#define    AUTHOR        "Mark Delany <markd@werple.apana.org.au>"
  50. X
  51. X#include "config.h"
  52. X#include "patchlevel.h"
  53. X
  54. X
  55. X#ifndef    TRUE
  56. X#define    TRUE        1
  57. X#endif
  58. X
  59. X#ifndef    FALSE
  60. X#define    FALSE        0
  61. X#endif
  62. X
  63. X
  64. X/*
  65. X * To handle sigwinch we need:
  66. X *
  67. X *    o    to be allowed to
  68. X *    o    the system to support it
  69. X *    o    for curses to have newterm (the fake one in port.c won't do)
  70. X */
  71. X
  72. X
  73. X#ifndef    NO_SIGWINCH
  74. X#ifndef    NO_NEWTERM
  75. X#ifdef    SIGWINCH
  76. X#define    HANDLE_SIGWINCH
  77. X#endif
  78. X#endif
  79. X#endif
  80. X
  81. X
  82. X#define    RC_NAME        "~/.ipickrc"
  83. X
  84. X
  85. X/*
  86. X * Xterm support uses the following sequences to enable and disable
  87. X * normal mouse tracking.
  88. X */
  89. X
  90. X#define    XTERM_ENABLE_TRACKING    "\033[?1000h"
  91. X#define    XTERM_DISABLE_TRACKING    "\033[?1000l"
  92. X
  93. X
  94. X/*
  95. X * The online help is separated by lumps and pages. A lump will never
  96. X * be split and a page boundary always will be.
  97. X */
  98. X
  99. X#define    LUMP_END    ".LE"
  100. X#define    PAGE_END    ".PE"
  101. X
  102. X
  103. X/* The maximum number of characters allowed in the command buffer. */
  104. X
  105. X#define    MAX_COMMAND    100
  106. X
  107. X
  108. X/*
  109. X * MOD_VALUE defines the modulo by which line numbers are displayed on
  110. X * the screen. It's order of magnitude is significant due to the
  111. X * screen positions consumed. If you change this, look at screen.c for 
  112. X * work needed there.
  113. X */
  114. X
  115. X#define    MOD_VALUE    1000
  116. X
  117. X
  118. X/*
  119. X * The screen layout consists of three regions: the title, data and
  120. X * command region.
  121. X *
  122. X * The title reqion is only present if the user supplies a title
  123. X * with either -t or -T. The data reqion contains the lines to pick
  124. X * from. The command (or interface) region is where commands are
  125. X * entered and error messages printed.
  126. X *
  127. X * Accordingly, the only fixed sized area of the screen is the
  128. X * command region defined below. (BTW, some of you may recognize
  129. X * this layout - a prize to the first correct entry).
  130. X */
  131. X
  132. X
  133. X#define    COMMAND_LINES        3    /* Lines needed by command region */
  134. X
  135. X#define    COMMAND_INPUT        (LINES-2)    /* 2nd last line of screen */
  136. X#define    COMMAND_MSG        (LINES-1)    /* Last line of screen */
  137. X
  138. X
  139. X/*
  140. X * TAB_SIZE defines the tabstop distance. It is used around the traps
  141. X * so that we can find all references if tabstops subsequently become
  142. X * configurable (I doubt it!)
  143. X */
  144. X
  145. X#define    TAB_SIZE        8
  146. X
  147. X
  148. X
  149. X#ifdef    MAIN
  150. X#define    PUBLIC(def, val)    def = val
  151. X#else
  152. X#define    PUBLIC(def, val)    extern    def
  153. X#endif
  154. X
  155. X
  156. X
  157. X#ifdef    NO_PROTOTYPES
  158. X#define PROTO(def) ()
  159. X#else
  160. X#define PROTO(def) def
  161. X#endif
  162. X
  163. X
  164. X
  165. X#define    MY_CTRL(c)        ((c) & 037)
  166. X#define    MY_META(c)        ((c) | 0200)
  167. X
  168. X
  169. X#define    DEL_CHAR        0177
  170. X#define    TAB_CHAR        MY_CTRL('I')
  171. X#define    LF_CHAR            '\n'
  172. X
  173. X#define    MY_MIN(a,b)        ((a) < (b) ? (a) : (b))
  174. X
  175. X
  176. X/* Not completely portable, but even EBCDIC has contiguous numbers... */
  177. X
  178. X#define    MY_ISOCTAL(cc)    ( ((cc) >= '0') && ((cc) <= '7') )
  179. X
  180. X
  181. X#define    ERRMSG(msg)        disp_message(TRUE, msg)
  182. X#define    STATMSG(msg)        disp_message(FALSE, msg)
  183. X
  184. X
  185. X/* Each line read in is stashed in a linked-list of LN structures */
  186. X
  187. Xtypedef    struct    ln_s    LN;
  188. X
  189. Xstruct    ln_s {
  190. X    LN        *next;
  191. X    LN        *prev;
  192. X    char    *data;            /* Raw data read in */
  193. X    char    *display_data;        /* Massaged for display purposes */
  194. X    int        len;            /* Total length of data */
  195. X    int        display_len;        /* Ditto for display_data */
  196. X    int        line_number;
  197. X    char    picked;            /* Already picked */
  198. X};
  199. X
  200. X
  201. X/*
  202. X * Function list passed back by getkey. If this is changed, then the
  203. X * FUNC_MAP structure in keyboard will also need changing.
  204. X */
  205. X
  206. Xtypedef    enum {
  207. X
  208. XFIRST_COMMAND=0,
  209. XSELECT_RANGE, SELECT_NUMBER, SELECT_ALL,
  210. XCLEAR_RANGE, CLEAR_ALL,
  211. XTOGGLE_CURRENT, TOGGLE_RANGE, TOGGLE_UNREAD,
  212. X
  213. XTOP_OF_SCREEN, BOTTOM_OF_SCREEN,
  214. X
  215. XPREVIOUS_LINE, NEXT_LINE,
  216. X
  217. XQUIT, ABORT, HELP, REFRESH,
  218. X
  219. XSCROLL_LEFT_CHAR, SCROLL_RIGHT_CHAR,
  220. X
  221. XBEGINNING_OF_LINE, END_OF_LINE,
  222. X
  223. XSCROLL_TAB, SCROLL_BACKTAB,
  224. X
  225. XSCROLL_LEFT_SCREEN, SCROLL_RIGHT_SCREEN,
  226. X
  227. XSCROLL_UP_HALF, SCROLL_DOWN_HALF,
  228. X
  229. XSCROLL_UP_FULL, SCROLL_DOWN_FULL,
  230. X
  231. XBEGINNING_OF_FILE, END_OF_FILE,
  232. X
  233. XSEARCH_FORWARD,
  234. XSEARCH_BACKWARD,
  235. XRE_SEARCH_FORWARD,
  236. XRE_SEARCH_BACKWARD,
  237. X
  238. XNEXT_SELECTED,
  239. XPREVIOUS_SELECTED,
  240. X
  241. XGOTO_LINE,
  242. X
  243. XSHELL,
  244. X
  245. XPIPE,
  246. X
  247. XREDO_COMMAND,
  248. X
  249. XXTERM_MOUSE,
  250. X
  251. XINVALID_COMMAND,
  252. X
  253. XNO_COMMAND,
  254. X
  255. XMULTI_COMMAND,
  256. X
  257. XLAST_COMMAND } FUNC_CODE;
  258. X
  259. X
  260. X/*
  261. X * Whenever a binding is made, the associated help text is added into
  262. X * the FUNC_MAP so that the online help can provide exact details about
  263. X * which bindings are available.
  264. X */
  265. X
  266. Xtypedef    struct    bound_help_s BOUND_HELP;
  267. X
  268. Xstruct    bound_help_s {
  269. X    BOUND_HELP    *next;
  270. X    char    *text;
  271. X};
  272. X
  273. X
  274. X/*
  275. X * All of the functions have a FUNC_MAP entry in data.c These entries are
  276. X * used by a lot of the ipick code. 
  277. X */
  278. X
  279. Xtypedef struct    {
  280. X    char    *func_name;
  281. X    int        message_index;        /* Index into msg_keyboard for */
  282. X                        /* commands with data. -1 means none */
  283. X    char    retain_first;        /* If keystroke is data too! */
  284. X    char    prompt_char;
  285. X    BOUND_HELP    *bound_help;
  286. X} FUNC_MAP;
  287. X
  288. X
  289. X
  290. X/*
  291. X * Help text is ensconsed in a structure to assist in identifying when
  292. X * Function codes need to be mapped to the bound help text.
  293. X */
  294. X
  295. Xtypedef    struct help_text_s {
  296. X    char    *text;
  297. X    FUNC_CODE    fc;
  298. X} HELP_TEXT;
  299. X
  300. X
  301. X/*
  302. X * Prototypes for all external routines. All are unique in the first 8
  303. X * characters for our geriatric *nix friends (maybe it should be 6?)
  304. X */
  305. X
  306. X
  307. Xextern    int        command();
  308. X
  309. Xextern    void        scrn_beep();
  310. Xextern    void        scrn_clear_modes();
  311. Xextern    int        scrn_getch();
  312. Xextern    void        scrn_init();
  313. Xextern    void        scrn_refresh();
  314. Xextern    void        scrn_repaint();
  315. Xextern    void        scrn_resume();
  316. Xextern    void        scrn_set_modes();
  317. Xextern    void        scrn_suspend();
  318. Xextern    void        scrn_term();
  319. Xextern    void        scrn_update();
  320. X
  321. Xextern    void        disp_cursor();
  322. Xextern    void        disp_data PROTO((int));
  323. Xextern    void        disp_message PROTO((int, char *));
  324. Xextern    void        disp_status();
  325. Xextern    void        disp_title();
  326. Xextern    void        clear_message();
  327. X
  328. Xextern    void        hlp_display();
  329. Xextern    void        hlp_add PROTO((FUNC_CODE, char *));
  330. X
  331. Xextern    void        read_lines PROTO((int));
  332. Xextern    int        write_picked PROTO((int));
  333. Xextern    void        drain_stdin();
  334. X
  335. Xextern    void        sigs_on();
  336. Xextern    void        sigs_off();
  337. X
  338. Xextern    int        my_atoi PROTO((char *));
  339. Xextern    void        shell_cmd PROTO((char *));
  340. Xextern    void        pipe_cmd PROTO((char *, char *, int));
  341. Xextern    char        *xmalloc PROTO((int, char *));
  342. Xextern    char        *xrealloc PROTO((char *, int, char *));
  343. Xextern    char        *xstrdup PROTO((char *, char *));
  344. Xextern    void        xfree PROTO((char *, char *));
  345. Xextern    void        fatal PROTO((char *, char *));
  346. Xextern    void        kill_me PROTO((int));
  347. Xextern    char        *decode PROTO((char *, char *));
  348. Xextern    void        kb_addcode PROTO((unsigned char *, FUNC_CODE, char *));
  349. Xextern    int        kb_addtcap PROTO((char *, FUNC_CODE, char *));
  350. Xextern    int        kb_addtinfo PROTO((char *, FUNC_CODE, char *));
  351. Xextern    FUNC_CODE    kb_findfunc PROTO((char *));
  352. Xextern    FUNC_CODE    kb_getkey PROTO((unsigned char *, int, char *, int));
  353. Xextern    void        kb_init();
  354. Xextern    void        kb_done();
  355. X
  356. Xextern    int        rc_init();
  357. X
  358. X
  359. X/* Define prototypes for routines that may be supplied in port.c */
  360. X
  361. X
  362. X#ifdef    NO_BEEP
  363. Xextern    void        beep();
  364. X#endif
  365. X
  366. X#ifdef    NO_NEWTERM
  367. Xextern    int        newterm PROTO((char *, FILE *, FILE *));
  368. X#endif
  369. X
  370. X#ifdef    NO_STANDOUT
  371. Xextern    void        standout();
  372. Xextern    void        standend();
  373. X#endif
  374. X
  375. X#ifdef    NO_STRDUP
  376. Xextern    char        *strdup PROTO((char *));
  377. X#endif
  378. X
  379. Xextern    char        *my_strrstr PROTO((char *, int, char *));
  380. X
  381. X#ifdef    NO_STRPBRK
  382. Xextern    char        *strpbrk PROTO((char *, char *));
  383. X#endif
  384. X
  385. X#ifdef    NO_STRSTR
  386. Xextern    char        *strstr PROTO((char *, char *));
  387. X#endif
  388. X
  389. X
  390. X/* Define prototypes if the #include doesn't exist */
  391. X
  392. X#ifdef    NO_STDLIB_H
  393. X
  394. Xextern    char    *getenv PROTO((char *));
  395. X
  396. X#ifdef    NO_MALLOC_H
  397. Xextern    char    *malloc PROTO((int));
  398. Xextern    char    *realloc PROTO((char *, int));
  399. X#endif
  400. X#endif
  401. X
  402. X#ifdef    NO_MEMORY_H
  403. Xextern    char    *memcpy PROTO((char *, char *, int));
  404. X#endif
  405. X
  406. X#ifdef    NO_STRING_H
  407. Xextern    int    strlen PROTO((char *));
  408. Xextern    int    strcmp PROTO((char *, char *));
  409. Xextern    int    strncmp PROTO((char *, char *, int));
  410. Xextern    char    *strcpy PROTO((char *, char *));
  411. X#endif
  412. X
  413. X
  414. X/* COMMON VARIABLES */
  415. X
  416. X
  417. X/* Results of command line arguments */
  418. X
  419. XPUBLIC(    int    auto_exit    ,    FALSE);
  420. XPUBLIC(    int    bell_flag    ,    FALSE);
  421. XPUBLIC(    char    *ctitle_string    ,    NULL);
  422. XPUBLIC(    int    ctitle_lcount    ,    0);    /* Command title lines */
  423. XPUBLIC(    int    stitle_lcount    ,    0);    /* stdin title lines */
  424. XPUBLIC(    int    minimum        ,    0);
  425. XPUBLIC(    int    maximum        ,    0);
  426. XPUBLIC(    int    drain_requested    ,    FALSE);
  427. XPUBLIC(    int    invert_mode        ,    FALSE);    /* Invert selection */
  428. XPUBLIC(    int    restricted_mode    ,    FALSE);
  429. XPUBLIC(    int    raw_mode    ,    TRUE);
  430. XPUBLIC(    char    *xterm_substr    ,    "xterm");
  431. XPUBLIC(    int    xterm_active    ,    FALSE);
  432. X
  433. X/* All screen I/O goes via termfile */
  434. X
  435. XPUBLIC(    FILE    *termfile    ,    NULL);
  436. X
  437. X/* All data I/O uses infile and outfile */
  438. X
  439. XPUBLIC(    FILE    *infile        ,    NULL);
  440. XPUBLIC(    FILE    *outfile    ,    NULL);
  441. X
  442. X
  443. XPUBLIC(    int    end_of_data    ,    FALSE);
  444. XPUBLIC(    int    sigwinch_raised    ,    FALSE);
  445. XPUBLIC(    int    select_in_range    ,    FALSE);
  446. X
  447. X
  448. XPUBLIC(    LN    *first_title    ,    NULL);    /* Start of title lines */
  449. X
  450. X
  451. X/* State of the screen image */
  452. X
  453. XPUBLIC(    int    msg_on_screen    ,    FALSE);
  454. XPUBLIC(    int    cmd_on_screen    ,    FALSE);
  455. X
  456. XPUBLIC(    LN    *top_of_screen     ,     NULL);    /* First line of on screen */
  457. XPUBLIC(    LN    *bottom_of_screen,     NULL);    /* Last ... */
  458. XPUBLIC(    LN    *current_line    ,    NULL);
  459. XPUBLIC(    int    column_offset    ,    0);
  460. XPUBLIC(    int    cursor_offset    ,    0);
  461. X
  462. XPUBLIC(    LN    *first_line    ,    NULL);    /* Start of all data lines */
  463. XPUBLIC(    LN    *last_line    ,    NULL);    /* End of file (in memory) */
  464. X
  465. X
  466. XPUBLIC(    int    wtitle_lines    ,    0);    /* Lines in title_win */
  467. XPUBLIC(    int    wdata_lines    ,    0);    /* Lines in data_win */
  468. XPUBLIC(    int    wdata_cols    ,    0);    /* Data cols in data_win */
  469. X
  470. X/* Default edit characters if curses doesn't know */
  471. X
  472. XPUBLIC(    char    erase_char    ,    DEL_CHAR);
  473. XPUBLIC(    char    kill_char    ,    MY_CTRL('U'));
  474. X
  475. X
  476. XPUBLIC(    int    total_picked    ,    0);
  477. XPUBLIC(    int    total_lines    ,    0);
  478. XPUBLIC(    int    unread_picked    ,    FALSE);
  479. X
  480. X
  481. X/* Define the text message externals */
  482. X
  483. X#ifndef    IN_LANGUAGE
  484. Xextern    char        *msg_command[];
  485. Xextern    char        *msg_curses[];
  486. Xextern    char        *msg_fileio[];
  487. Xextern    char        *msg_help[];
  488. Xextern    HELP_TEXT    help_text[];
  489. Xextern    char        *msg_keyboard[];
  490. Xextern    char        *msg_main[];
  491. Xextern    char        *msg_misc[];
  492. Xextern    char        *msg_port[];
  493. Xextern    char        *msg_rc[];
  494. X#endif
  495. X
  496. X/* Define the Function list. */
  497. X
  498. X#ifndef    IN_DATA
  499. Xextern    FUNC_MAP    func_list[];
  500. X#endif
  501. END_OF_FILE
  502.   if test 10318 -ne `wc -c <'ipick/ipick.h'`; then
  503.     echo shar: \"'ipick/ipick.h'\" unpacked with wrong size!
  504.   fi
  505.   # end of 'ipick/ipick.h'
  506. fi
  507. if test -f 'ipick/misc.c' -a "${1}" != "-c" ; then 
  508.   echo shar: Will not clobber existing file \"'ipick/misc.c'\"
  509. else
  510.   echo shar: Extracting \"'ipick/misc.c'\" \(5162 characters\)
  511.   sed "s/^X//" >'ipick/misc.c' <<'END_OF_FILE'
  512. X/* $Id: misc.c,v 1.1 1993/02/27 21:48:01 markd Exp $
  513. X *
  514. X * Miscellaneous routines for ipick
  515. X *
  516. X *    Copyright (c) 1993, Mark Delany
  517. X *
  518. X *    You may distribute under the terms of either the GNU General Public
  519. X *    License or the Artistic License, as specified in the README file.
  520. X *
  521. X * $Log: misc.c,v $
  522. X * Revision 1.1  1993/02/27  21:48:01  markd
  523. X * Initial revision
  524. X *
  525. X */
  526. X
  527. X#include "ipick.h"
  528. X
  529. X/*
  530. X * The standard atoi is just too slack when it comes to accepting
  531. X * numbers. Eg, it accepts 10abc as a number, which isn't much chop for
  532. X * command line options like -M23datafile
  533. X *
  534. X * My routine only accepts positive integers and returns -1 if data is
  535. X * not terminated by whitespace.
  536. X */
  537. X
  538. Xextern    int
  539. Xmy_atoi(datap)
  540. X
  541. X    char    *datap;
  542. X
  543. X{
  544. X    int        val;
  545. X    int        last_val;
  546. X
  547. X    for (val=0; isdigit(*datap); datap++) {
  548. X    last_val = val;
  549. X    val *= 10;
  550. X    val += *datap - '0';
  551. X    if (val < last_val) return -1;    /* Too large */
  552. X    }
  553. X
  554. X    if (*datap && !isspace(*datap)) return -1;
  555. X
  556. X    return val;
  557. X}
  558. X
  559. X
  560. X/*
  561. X * shell. Shell out the command. It's here rather than in command
  562. X * to localize the signal code to one module.
  563. X */
  564. X
  565. Xextern    void
  566. Xshell_cmd(cmd)
  567. X
  568. X    char    *cmd;
  569. X
  570. X{
  571. X
  572. X    int        child;
  573. X
  574. X/*
  575. X * Fork to revert signals, but leave the parent process protected.
  576. X */
  577. X
  578. X    child = fork();
  579. X
  580. X    switch (child) {
  581. X
  582. X      case -1:
  583. X    ERRMSG(msg_main[15]);    /* Could not fork */
  584. X    break;
  585. X
  586. X
  587. X      case 0:        /* Child */
  588. X    sigs_off();
  589. X    (void) system(cmd);        /* El slacko aren't I ? */
  590. X    _exit(0);
  591. X
  592. X
  593. X      default:        /* Parent */
  594. X    while (wait((int *) NULL) != child)  ;   /* Empty loop */
  595. X    break;
  596. X
  597. X    }
  598. X
  599. X    return;
  600. X}
  601. X
  602. X
  603. X/*
  604. X * Pipe the data out to the nominated command. Like shell_cmd, it's
  605. X * here rather than in command to to localize the signal code to one
  606. X * module.
  607. X */
  608. X
  609. Xextern    void
  610. Xpipe_cmd(cmd, data, data_len)
  611. X
  612. X    char    *cmd;
  613. X    char    *data;
  614. X    int        data_len;
  615. X
  616. X{
  617. X    FILE    *pf;
  618. X
  619. X    pf = popen(cmd, "w");
  620. X    if (!pf) {
  621. X    ERRMSG(msg_misc[0]);    /* Could not open pipe */
  622. X    return;
  623. X    }
  624. X
  625. X    fwrite(data, 1, data_len, pf);
  626. X
  627. X    pclose(pf);
  628. X}
  629. X
  630. X
  631. X
  632. X
  633. X/* Common malloc to handle no memory */
  634. X
  635. Xchar    *
  636. Xxmalloc(bytes, caller)
  637. X
  638. X    int        bytes;
  639. X    char    *caller;
  640. X
  641. X{
  642. X    char    *cp;
  643. X
  644. X    cp = (char *) malloc(bytes);
  645. X/*    fprintf(stderr, "M:%x for %d. C=%s\n", cp, bytes, caller); */
  646. X    if (!cp) fatal(msg_misc[1], caller);
  647. X
  648. X    return cp;
  649. X}
  650. X
  651. X
  652. X
  653. X/* Common remalloc to handle no memory */
  654. X
  655. Xchar    *
  656. Xxrealloc(orig, bytes, caller)
  657. X
  658. X    char    *orig;
  659. X    int        bytes;
  660. X    char    *caller;
  661. X
  662. X{
  663. X    char    *cp;
  664. X
  665. X    cp = (char *) realloc(orig, bytes);
  666. X/*    fprintf(stderr, "R:%x for %d was %x. C=%s\n", cp, bytes, orig, caller); */
  667. X    if (!cp) fatal(msg_misc[2], caller);
  668. X
  669. X    return cp;
  670. X}
  671. X
  672. X
  673. Xchar    *
  674. Xxstrdup(string, caller)
  675. X
  676. X    char    *string;
  677. X    char    *caller;
  678. X
  679. X{
  680. X    char    *cp;
  681. X
  682. X    cp = strdup(string);
  683. X/*    fprintf(stderr, "D: %x. C=%s\n", cp, caller); */
  684. X    if (!cp) fatal(msg_misc[3], caller);
  685. X    return cp;
  686. X}
  687. X
  688. X
  689. X/* Good ol' orthogonality. Some free's return a value, some don't - sigh */
  690. X
  691. Xextern    void
  692. Xxfree(orig, caller)
  693. X
  694. X    char    *orig;
  695. X    char    *caller;
  696. X
  697. X{
  698. X/*    fprintf(stderr, "F: %x C=%s\n", orig, caller); */
  699. X    (void) free(orig);
  700. X
  701. X    return;
  702. X}
  703. X
  704. X
  705. X
  706. X/*
  707. X * Decode converts a pretty standard set of substitutions into their
  708. X * values.
  709. X */
  710. X
  711. Xextern    char    *
  712. Xdecode(in, out_arg)
  713. X
  714. X    char    *in;
  715. X    char    *out_arg;
  716. X
  717. X{
  718. X
  719. X    char    cc;
  720. X    int        i;
  721. X    unsigned    char    octval;
  722. X    unsigned    char    *out;
  723. X
  724. X    out = (unsigned char *) out_arg;
  725. X
  726. X    while (cc = *in++) {
  727. X
  728. X/* Handle the simple cases first */
  729. X
  730. X    if (cc == '^') {        /* CONTROL */
  731. X        cc = *in++;
  732. X        if (!cc) return msg_misc[4];    /* Incomplete sequence */
  733. X        *out++ = MY_CTRL(cc);
  734. X        continue;
  735. X    }
  736. X
  737. X    if (cc != '\\') {        /* Normal character ? */
  738. X        *out++ = cc;
  739. X        continue;
  740. X    }
  741. X
  742. X/* Sub dispatch on \ */
  743. X
  744. X    cc = *in++;
  745. X    if (!cc) return msg_misc[5];    /* Incomplete // sequence */
  746. X
  747. X    switch (cc) {
  748. X
  749. X      case 'b': *out++ = '\b'; continue;
  750. X      case 'e': *out++ = '\033'; continue;
  751. X      case 'f': *out++ = '\f'; continue;
  752. X      case 'n': *out++ = '\n'; continue;
  753. X      case 'r': *out++ = '\r'; continue;
  754. X      case 's': *out++ = ' '; continue;
  755. X      case 't': *out++ = '\t'; continue;
  756. X
  757. X      default: break;
  758. X    }
  759. X
  760. X/*
  761. X * Only valid thing left is \NNN as an octal value. If it's not numeric, 
  762. X * treat as itself.
  763. X */
  764. X
  765. X    if (!MY_ISOCTAL(cc)) {
  766. X        *out++ = cc;
  767. X        continue;
  768. X    }
  769. X
  770. X/*
  771. X * It's a digit, decode until either 3 digits or \0. It has to be
  772. X * exactly 3 digits and no less as there is no way to differentiate
  773. X * between eg: \031 that could mean either ETX or STX SOH.
  774. X */
  775. X
  776. X    octval = cc - '0';
  777. X
  778. X    for (i=0; i < 2; i++) {
  779. X        octval <<= 3;
  780. X        cc = *in++;
  781. X        if (!MY_ISOCTAL(cc)) return msg_misc[6];    /* Incomplete octal */
  782. X        octval += cc - '0';
  783. X    }
  784. X    *out++ = octval;
  785. X    }
  786. X
  787. X    *out = '\0';
  788. X
  789. X    return NULL;
  790. X}
  791. X
  792. X
  793. X
  794. X/* fatal prints the error message as well as an optional perror, then exits */
  795. X
  796. Xextern    void
  797. Xfatal(message, perror_text)
  798. X
  799. X    char    *message;
  800. X    char    *perror_text;
  801. X
  802. X{
  803. X    fprintf(stderr, "\n\r%s:%s: %s.\n\r", NAME, msg_main[25], message);
  804. X
  805. X    if (perror_text) {
  806. X    fprintf(stderr, "errno=%d: ", errno);
  807. X    perror(perror_text);
  808. X    fputs("\n\r", stderr);
  809. X    }
  810. X
  811. X    scrn_suspend();    /* Ha! */
  812. X    exit(2);
  813. X}
  814. X
  815. X
  816. Xvoid
  817. Xkill_me(sig)
  818. X
  819. X    int        sig;
  820. X
  821. X{
  822. X    scrn_term();
  823. X    exit(2);
  824. X}
  825. END_OF_FILE
  826.   if test 5162 -ne `wc -c <'ipick/misc.c'`; then
  827.     echo shar: \"'ipick/misc.c'\" unpacked with wrong size!
  828.   fi
  829.   # end of 'ipick/misc.c'
  830. fi
  831. if test -f 'ipick/command.c' -a "${1}" != "-c" ; then 
  832.   echo shar: Will not clobber existing file \"'ipick/command.c'\"
  833. else
  834.   echo shar: Extracting \"'ipick/command.c'\" \(29050 characters\)
  835.   sed "s/^X//" >'ipick/command.c' <<'END_OF_FILE'
  836. X/* $Id: command.c,v 1.1 1993/02/27 22:04:54 markd Exp $
  837. X *
  838. X * Process each command from the keyboard
  839. X *
  840. X *    Copyright (c) 1993, Mark Delany
  841. X *
  842. X *    You may distribute under the terms of either the GNU General Public
  843. X *    License or the Artistic License, as specified in the README file.
  844. X *
  845. X * $Log: command.c,v $
  846. X * Revision 1.1  1993/02/27  22:04:54  markd
  847. X * Initial revision
  848. X *
  849. X */
  850. X
  851. X#include "ipick.h"
  852. X
  853. X#define    ALL_STRING    msg_command[0]
  854. X#define    VISIBLE_STRING    msg_command[1]
  855. X
  856. X/* Toggle the state of a single line - fix the counter too */
  857. X
  858. X#define    TOGGLE_LN(lnp)    \
  859. X    {    \
  860. X        lnp->picked = !lnp->picked;        \
  861. X        total_picked += lnp->picked ? 1 : -1;    \
  862. X    }
  863. X
  864. X/* 
  865. X * SAVE_STATE, STATE_UNCHANGED and SCREEN_DATA_UNCHANGED are used to
  866. X * determine whether a movement command has actually changed anything.
  867. X * If it hasn't then the command will generate a message stating the
  868. X * resultant position. In this way, the user *always* gets some
  869. X * feedback for every command.
  870. X *
  871. X * SCREEN_DATA_UNCHANGED is used to determine whether to avoid
  872. X * a full redisplay of the data by only displaying the selection
  873. X * and cursor part of the screen.
  874. X */
  875. X
  876. X#define    SAVE_STATE()    \
  877. X    {        \
  878. X        s_top_of_screen = top_of_screen;    \
  879. X        s_current_line = current_line;        \
  880. X        s_column_offset = column_offset;    \
  881. X        s_cursor_offset = cursor_offset;    \
  882. X    }
  883. X
  884. X
  885. X#define    SCREEN_DATA_UNCHANGED()        \
  886. X    (    (s_top_of_screen == top_of_screen)        \
  887. X     &&    (s_column_offset == column_offset)        \
  888. X     )
  889. X
  890. X#define    STATE_UNCHANGED()        \
  891. X    (    SCREEN_DATA_UNCHANGED()                \
  892. X     &&    (s_current_line == current_line)        \
  893. X     &&    (s_cursor_offset == cursor_offset)        \
  894. X     )
  895. X
  896. X
  897. X/* Return TRUE if the nominated line is on the screen */
  898. X
  899. X#define    ON_SCREEN(lnp)                        \
  900. X                                    \
  901. X    (    (lnp->line_number >= top_of_screen->line_number)    \
  902. X     &&    (lnp->line_number <= bottom_of_screen->line_number))
  903. X
  904. X
  905. X/*
  906. X * FIX_BOTTOM re-adjusts bottom_of_screen so that it's correct
  907. X * relative to the top.
  908. X */
  909. X
  910. X#define    FIX_BOTTOM()            \
  911. X        bottom_of_screen = move_relative(top_of_screen, wdata_lines-1)
  912. X
  913. X
  914. X/*
  915. X * MAKE_MIDPOINT positions the screen relative to the current_line so
  916. X * that the current pointer is in the midpoint of the screen.
  917. X */
  918. X
  919. X#define    MAKE_MIDPOINT(lnp)        \
  920. X    {                \
  921. X        top_of_screen = move_relative(lnp, -wdata_lines/2);    \
  922. X        FIX_BOTTOM();                    \
  923. X    }
  924. X
  925. X
  926. X/* CURSOR_OFFSCREEN determines if the cursor is current on the screen. */
  927. X
  928. X#define    CURSOR_OFFSCREEN()        \
  929. X    ((cursor_offset < column_offset) ||    \
  930. X     (cursor_offset > (column_offset+wdata_cols)))
  931. X
  932. X
  933. X/* Remember previous command for possible redo */
  934. X
  935. Xstatic    char        last_data[MAX_COMMAND];
  936. Xstatic    FUNC_CODE    last_func = NO_COMMAND;
  937. X
  938. X/* Remember previous search string for possible redo */
  939. X
  940. Xstatic    char    last_search[MAX_COMMAND] = "";
  941. X
  942. X/* Save of screen image state - see SAVE_STATE STATE_UNCHANGED macros */
  943. X
  944. Xstatic    LN    *s_top_of_screen;
  945. Xstatic    LN    *s_current_line;
  946. Xstatic    int    s_column_offset;
  947. Xstatic    int    s_cursor_offset;
  948. X
  949. X
  950. X
  951. X/* Internal routines */
  952. X
  953. Xstatic    int    check_in_range();
  954. X
  955. Xstatic    void    goto_line();
  956. Xstatic    void    scroll_down();
  957. Xstatic    void    scroll_up();
  958. Xstatic    void    scroll_left();
  959. X
  960. Xstatic    LN    *move_relative();
  961. X
  962. Xstatic    void    process_range();
  963. Xstatic    int    parse_number_range();
  964. Xstatic    int    find_and_handle();
  965. X
  966. Xstatic    void    toggle_range();
  967. Xstatic    void    clear_range();
  968. Xstatic    void    set_range();
  969. X
  970. Xstatic    void    search();
  971. Xstatic    LN    *find_pattern();
  972. Xstatic    int    find_inline();
  973. X
  974. Xstatic    void    find_selected();
  975. X
  976. Xstatic    void    xterm_event();
  977. X
  978. X
  979. X/*
  980. X * The command routine reads in a single command and, well, processes
  981. X * it. If a prog termination is in order, it returns TRUE, otherwise
  982. X * FALSE.
  983. X */
  984. X
  985. Xextern    int
  986. Xcommand()
  987. X
  988. X{
  989. X    unsigned    char    keystrokes[MAX_COMMAND];
  990. X    char    data[MAX_COMMAND];
  991. X    FUNC_CODE    func;
  992. X    int        line_number;
  993. X    int        adjust;
  994. X
  995. X/* Make sure the title and data windows are up-to-date */
  996. X
  997. X    if (SCREEN_DATA_UNCHANGED()) {
  998. X    disp_data(FALSE);
  999. X    }
  1000. X    else {
  1001. X    disp_title();
  1002. X    disp_data(TRUE);
  1003. X    }
  1004. X
  1005. X    select_in_range = check_in_range(FALSE);    /* disp_status needs this */
  1006. X    disp_status();
  1007. X
  1008. X    scrn_update();
  1009. X
  1010. X/* Check to see whether auto exit can now occur */
  1011. X
  1012. X    if (auto_exit && select_in_range) return TRUE;
  1013. X
  1014. X/* Get the command string. The loop is solely for signals. */
  1015. X
  1016. X    *keystrokes = '\0';
  1017. X    *data = '\0';
  1018. X
  1019. X    for (;;) {
  1020. X    disp_cursor();        /* Put cursor in correct spot */
  1021. X    func = kb_getkey(keystrokes, sizeof(keystrokes), data, sizeof(data));
  1022. X
  1023. X    if (sigwinch_raised) {
  1024. X        sigwinch_raised = FALSE;
  1025. X        scrn_term();
  1026. X        scrn_init();
  1027. X        scrn_repaint();
  1028. X        continue;
  1029. X    }
  1030. X
  1031. X    break;        /* Normal case! */
  1032. X    }
  1033. X
  1034. X
  1035. X/* Have a function from the keyboard */
  1036. X
  1037. X    SAVE_STATE();
  1038. X
  1039. X/* Redo must re-establish the last valid command prior to the dispatch */
  1040. X
  1041. X    if (func == REDO_COMMAND) {
  1042. X    if (last_func == NO_COMMAND) {
  1043. X        ERRMSG(msg_command[3]);    /* No previous command */
  1044. X        return FALSE;
  1045. X    }
  1046. X    strcpy(data, last_data);
  1047. X    func = last_func;
  1048. X    }
  1049. X
  1050. X
  1051. X    switch (func) {
  1052. X
  1053. X      case SELECT_RANGE:
  1054. X      case SELECT_NUMBER:
  1055. X      case TOGGLE_RANGE:
  1056. X      case CLEAR_RANGE:    process_range(data, func);
  1057. X            break;
  1058. X
  1059. X      case SELECT_ALL:    set_range(first_line, last_line);
  1060. X            unread_picked = TRUE;
  1061. X            STATMSG(msg_command[4]);    /* All lines sel.. */
  1062. X            break;
  1063. X
  1064. X      case CLEAR_ALL:    clear_range(first_line, last_line);
  1065. X            unread_picked = FALSE;
  1066. X            STATMSG(msg_command[5]);    /* All lines cleared */
  1067. X            break;
  1068. X
  1069. X      case TOGGLE_UNREAD:
  1070. X            unread_picked = !unread_picked;
  1071. X            break;
  1072. X
  1073. X
  1074. X      case TOGGLE_CURRENT:    /* Select current and move down one */
  1075. X            TOGGLE_LN(current_line);
  1076. X            if (current_line->next) {
  1077. X                current_line = current_line->next;
  1078. X                if (!ON_SCREEN(current_line)) scroll_down(1);
  1079. X            }
  1080. X            break;
  1081. X
  1082. X
  1083. X      case TOP_OF_SCREEN:
  1084. X            current_line = top_of_screen;
  1085. X            if (STATE_UNCHANGED()) {
  1086. X                STATMSG(msg_command[6]);     /* Top of screen */
  1087. X            }
  1088. X            break; 
  1089. X
  1090. X      case BOTTOM_OF_SCREEN:
  1091. X            current_line = bottom_of_screen;
  1092. X            if (STATE_UNCHANGED()) {
  1093. X                STATMSG(msg_command[7]);     /* Bottom of screen */
  1094. X            }
  1095. X            break; 
  1096. X
  1097. X
  1098. X      case PREVIOUS_LINE:   
  1099. X            if (current_line->prev) {
  1100. X                  current_line = current_line->prev;
  1101. X                if (!ON_SCREEN(current_line)) scroll_up(1);
  1102. X            }
  1103. X            if (STATE_UNCHANGED()) {
  1104. X                STATMSG(msg_command[8]);    /* Top of file */
  1105. X            }
  1106. X            break;
  1107. X
  1108. X      case NEXT_LINE:    if (current_line->next) {
  1109. X                current_line = current_line->next;
  1110. X                if (!ON_SCREEN(current_line)) scroll_down(1);
  1111. X            }
  1112. X            if (STATE_UNCHANGED()) {
  1113. X                STATMSG(msg_command[9]);    /* End of file */
  1114. X            }
  1115. X            break;
  1116. X
  1117. X
  1118. X      case QUIT:     if (check_in_range(TRUE)) return TRUE;
  1119. X            break;
  1120. X
  1121. X      case ABORT:    if (!restricted_mode) kill_me(SIGTERM);
  1122. X            ERRMSG(msg_command[16]);
  1123. X            break;
  1124. X
  1125. X      case HELP:    hlp_display(); scrn_repaint(); break;
  1126. X
  1127. X      case REFRESH:    scrn_refresh(); break;
  1128. X
  1129. X
  1130. X      case BEGINNING_OF_LINE: 
  1131. X            cursor_offset = column_offset = 0;
  1132. X            if (STATE_UNCHANGED()) {
  1133. X                STATMSG(msg_command[10]);    /* Beginning of line */
  1134. X            }
  1135. X            break;
  1136. X
  1137. X      case END_OF_LINE:    cursor_offset = current_line->display_len;
  1138. X            column_offset = current_line->display_len - wdata_cols;
  1139. X            if (column_offset < 0) column_offset = 0;
  1140. X            if (STATE_UNCHANGED()) {
  1141. X                STATMSG(msg_command[11]);    /* End of line */
  1142. X            }
  1143. X            break;
  1144. X
  1145. X
  1146. X      case SCROLL_LEFT_CHAR:
  1147. X            scroll_left(1);
  1148. X            break;
  1149. X
  1150. X      case SCROLL_BACKTAB:
  1151. X            adjust = cursor_offset % TAB_SIZE;
  1152. X            if (!adjust) adjust = TAB_SIZE;
  1153. X            scroll_left(adjust);
  1154. X            break;
  1155. X
  1156. X      case SCROLL_LEFT_SCREEN:
  1157. X            scroll_left(wdata_cols / 2);
  1158. X            break;
  1159. X
  1160. X      case SCROLL_RIGHT_CHAR:
  1161. X            cursor_offset++;
  1162. X            if (CURSOR_OFFSCREEN()) column_offset++;
  1163. X            break;
  1164. X
  1165. X      case SCROLL_TAB:    adjust = TAB_SIZE - cursor_offset % TAB_SIZE;
  1166. X            cursor_offset += adjust;
  1167. X            if (CURSOR_OFFSCREEN()) column_offset += adjust;
  1168. X            break;
  1169. X
  1170. X      case SCROLL_RIGHT_SCREEN:
  1171. X            adjust = wdata_cols / 2;
  1172. X            cursor_offset += adjust;
  1173. X            if (CURSOR_OFFSCREEN()) column_offset += adjust;
  1174. X            break;
  1175. X
  1176. X      case SCROLL_UP_HALF:
  1177. X      case SCROLL_UP_FULL:
  1178. X            scroll_up(func == SCROLL_UP_HALF
  1179. X                    ? wdata_lines/2
  1180. X                    : wdata_lines-2);
  1181. X            if (!ON_SCREEN(current_line)) {
  1182. X                current_line = bottom_of_screen;
  1183. X            }
  1184. X            if (STATE_UNCHANGED()) current_line = top_of_screen;
  1185. X            if (STATE_UNCHANGED()) {
  1186. X                STATMSG(msg_command[8]);    /* Top of file */
  1187. X            }
  1188. X            break;
  1189. X
  1190. X      case SCROLL_DOWN_HALF:
  1191. X      case SCROLL_DOWN_FULL:
  1192. X            scroll_down(func == SCROLL_DOWN_HALF
  1193. X                    ? wdata_lines/2
  1194. X                    : wdata_lines-2);
  1195. X            if (!ON_SCREEN(current_line)) {
  1196. X                current_line = top_of_screen;
  1197. X            }
  1198. X            if (STATE_UNCHANGED()) STATMSG(msg_command[9]);
  1199. X            break;
  1200. X
  1201. X
  1202. X      case BEGINNING_OF_FILE:
  1203. X            current_line = first_line;
  1204. X            if (!ON_SCREEN(current_line)) {
  1205. X                top_of_screen = current_line;
  1206. X                FIX_BOTTOM();
  1207. X            }
  1208. X            if (STATE_UNCHANGED()) STATMSG(msg_command[8]);
  1209. X            break;
  1210. X
  1211. X
  1212. X      case END_OF_FILE:    while (!end_of_data) read_lines(wdata_lines);
  1213. X            current_line = last_line;
  1214. X            if (!ON_SCREEN(current_line)) {
  1215. X                top_of_screen = move_relative(current_line,
  1216. X                              -(wdata_lines-2));
  1217. X                FIX_BOTTOM();
  1218. X            }
  1219. X            if (STATE_UNCHANGED()) STATMSG(msg_command[9]);
  1220. X            break;
  1221. X
  1222. X/*
  1223. X * Both forward and reverse search use the last search string if
  1224. X * none has been supplied this time.
  1225. X */
  1226. X
  1227. X      case SEARCH_FORWARD:
  1228. X            search(TRUE, data);
  1229. X            break;
  1230. X
  1231. X      case RE_SEARCH_FORWARD:
  1232. X            search(TRUE, last_search);
  1233. X            break;
  1234. X
  1235. X      case SEARCH_BACKWARD:
  1236. X            search(FALSE, data);
  1237. X            break;
  1238. X
  1239. X      case RE_SEARCH_BACKWARD:
  1240. X            search(FALSE, last_search);
  1241. X            break;
  1242. X
  1243. X      case NEXT_SELECTED:
  1244. X            find_selected(TRUE, current_line->next);
  1245. X            break;
  1246. X
  1247. X      case PREVIOUS_SELECTED:
  1248. X            find_selected(FALSE, current_line->prev);
  1249. X            break;
  1250. X
  1251. X
  1252. X      case GOTO_LINE:    if (!*data) break;
  1253. X
  1254. X            line_number = my_atoi(data);
  1255. X            if (line_number <= 0) {
  1256. X                ERRMSG(msg_command[12]);    /* Invalid line no. */
  1257. X            }
  1258. X            else {
  1259. X                goto_line(line_number);
  1260. X            }
  1261. X            break;
  1262. X
  1263. X
  1264. X      case SHELL:
  1265. X      case PIPE:    if (!*data) break;
  1266. X
  1267. X            if (restricted_mode) {
  1268. X                ERRMSG(func == SHELL
  1269. X                  ? msg_command[13]    /* Shell illegal */
  1270. X                  : msg_command[14]);    /* Pipe illegal */
  1271. X                break;
  1272. X            }
  1273. X
  1274. X            scrn_suspend();    /* Temporarily suspend curses */
  1275. X
  1276. X            if (func == SHELL) {
  1277. X                shell_cmd(data);
  1278. X            }
  1279. X            else {
  1280. X                pipe_cmd(data, current_line->data,
  1281. X                     current_line->len);
  1282. X            }
  1283. X
  1284. X             fprintf(termfile, msg_command[15], NAME);
  1285. X            fflush(termfile);
  1286. X
  1287. X            scrn_resume();        /* Back to curses modes */
  1288. X            (void) scrn_getch();    /* Get response to Depress */
  1289. X            scrn_refresh();
  1290. X
  1291. X            STATMSG(msg_command[17]);    /* Command completed */
  1292. X            break;
  1293. X
  1294. X
  1295. X      case XTERM_MOUSE:    xterm_event();
  1296. X            break;
  1297. X
  1298. X      case NO_COMMAND:    break;
  1299. X
  1300. X      case INVALID_COMMAND:
  1301. X      default:        /* Some compiler's can't cope without a default */
  1302. X            ERRMSG(msg_command[18]);    /* Invalid, try ? */
  1303. X            break;
  1304. X
  1305. X    }
  1306. X
  1307. X/* End of dispatch - save current command for possible redo */
  1308. X
  1309. X    if (func != INVALID_COMMAND) {
  1310. X    strcpy(last_data, data);
  1311. X    last_func = func;
  1312. X    }
  1313. X
  1314. X    return FALSE;
  1315. X}
  1316. X
  1317. X
  1318. X
  1319. X/*
  1320. X * Check that the lines selected thus far are in range (if nominated).
  1321. X * One interesting anomoly is when a minimum is set, but there aren't
  1322. X * that many lines in total.
  1323. X *
  1324. X * The only sensible choice is to accept when all lines have been
  1325. X * selected as this is the closest that they can get to the minimum.
  1326. X * If this isn't done, then the process upstream becomes more
  1327. X * complicated as they have to check for reaching the minimum and
  1328. X * construct ipick's command line accordingly.
  1329. X */
  1330. X
  1331. Xstatic    int
  1332. Xcheck_in_range(complain)
  1333. X
  1334. X    int        complain;
  1335. X
  1336. X{
  1337. X    int        test_minimum;
  1338. X
  1339. X    if (!minimum && !maximum) return TRUE;
  1340. X
  1341. X    if (minimum) {
  1342. X    test_minimum = minimum;
  1343. X    if (end_of_data && (total_lines < minimum)) test_minimum = total_lines;
  1344. X
  1345. X    if (total_picked < test_minimum) {
  1346. X            if (complain) ERRMSG(msg_command[19]);    /* Min not reached */
  1347. X        return FALSE;
  1348. X    }
  1349. X    }
  1350. X
  1351. X    if (maximum && (total_picked > maximum)) {
  1352. X    if (complain) ERRMSG(msg_command[20]);        /* Max exceeded */
  1353. X    return FALSE;
  1354. X    }
  1355. X
  1356. X/*
  1357. X * The result of selecting as yet unread lines means that we have no
  1358. X * idea whether the maximum will be exceeded or not.
  1359. X */
  1360. X
  1361. X    if (unread_picked && maximum) {
  1362. X    if (complain) ERRMSG(msg_command[21]);    /* Unread may exceed max */
  1363. X    return FALSE;
  1364. X    }
  1365. X
  1366. X    return TRUE;
  1367. X}
  1368. X
  1369. X
  1370. X/* Move the screen display to the nominated line */
  1371. X
  1372. Xstatic    void
  1373. Xgoto_line(line)
  1374. X
  1375. X    int        line;
  1376. X
  1377. X{
  1378. X
  1379. X/* Optimization: Use the closest starting point possible */
  1380. X
  1381. X    current_line = first_line;
  1382. X    if (line >= top_of_screen->line_number) current_line = top_of_screen;
  1383. X    if (line >= bottom_of_screen->line_number) current_line = bottom_of_screen;
  1384. X    if (line >= last_line->line_number) current_line = last_line;
  1385. X
  1386. X/* Make sure that there is a next */
  1387. X
  1388. X    if (!end_of_data) read_lines(2);
  1389. X
  1390. X/* Skip forward to find correct line number */
  1391. X
  1392. X    while (current_line->next && (line > current_line->line_number)) {
  1393. X
  1394. X    current_line = current_line->next;
  1395. X    if (!current_line->next && !end_of_data) read_lines(2);
  1396. X    }
  1397. X
  1398. X    if (!ON_SCREEN(current_line)) MAKE_MIDPOINT(current_line);
  1399. X/* Did we get to the correct line? */
  1400. X
  1401. X    if (line != current_line->line_number) STATMSG(msg_command[9]);
  1402. X}
  1403. X
  1404. X
  1405. X/*
  1406. X * Move the screen display down the nominated number of lines. Don't worry
  1407. X * about current_line.
  1408. X */
  1409. X
  1410. Xstatic    void
  1411. Xscroll_down(lines)
  1412. X
  1413. X    int        lines;
  1414. X
  1415. X{
  1416. X    if (!end_of_data) read_lines(2);
  1417. X    while (top_of_screen->next && lines-- > 0) {
  1418. X    top_of_screen = top_of_screen->next;
  1419. X    }
  1420. X
  1421. X    FIX_BOTTOM();
  1422. X}
  1423. X
  1424. X
  1425. X/*
  1426. X * Move the screen display up the nominated number of lines. Don't worry
  1427. X * about current_line.
  1428. X */
  1429. X
  1430. Xstatic    void
  1431. Xscroll_up(lines)
  1432. X
  1433. X    int        lines;
  1434. X
  1435. X{
  1436. X    while (top_of_screen->prev && (lines-- > 0)) {
  1437. X    top_of_screen = top_of_screen->prev;
  1438. X    }
  1439. X
  1440. X    FIX_BOTTOM();
  1441. X}
  1442. X
  1443. X
  1444. X/*
  1445. X * Move the cursor left the nominated number of cols. If the cursor
  1446. X * has moved off of the screen, move it back too.
  1447. X */
  1448. X
  1449. Xstatic    void
  1450. Xscroll_left(cols)
  1451. X
  1452. X    int        cols;
  1453. X
  1454. X{
  1455. X    cursor_offset -= cols;
  1456. X    if (cursor_offset < 0) cursor_offset = 0;
  1457. X    if (CURSOR_OFFSCREEN()) column_offset -= cols;
  1458. X    if (column_offset < 0) column_offset = 0;
  1459. X
  1460. X    if (STATE_UNCHANGED()) STATMSG(msg_command[10]);    /* Beginning of line */
  1461. X}
  1462. X
  1463. X
  1464. X/*
  1465. X * move_relative moves to the line that is the nominated distance from
  1466. X * the current line. Distance can be negative.
  1467. X *
  1468. X * If move_relative hits the top or bottom, then it returns that one.
  1469. X *
  1470. X * The caller should make provision for read_lines() if necessary.
  1471. X */
  1472. X
  1473. X
  1474. Xstatic    LN    *
  1475. Xmove_relative(lnp, distance)
  1476. X
  1477. X    LN        *lnp;
  1478. X    int        distance;
  1479. X
  1480. X{
  1481. X
  1482. X/* Do the backwards direction - if any */
  1483. X
  1484. X    while ((distance < 0) && (lnp->prev)) {
  1485. X    lnp = lnp->prev;
  1486. X    distance++;
  1487. X    }
  1488. X
  1489. X/* and the forwards direction - if any */
  1490. X
  1491. X    while ((distance > 0) && (lnp->next)) {
  1492. X    lnp = lnp->next;
  1493. X    distance--;
  1494. X    }
  1495. X
  1496. X    return lnp;
  1497. X}
  1498. X
  1499. X
  1500. X/*
  1501. X * The routines: process_range, parse_number_range and find_and_handle
  1502. X * are responsible for parsing a list of line selection numbers and
  1503. X * passing to the handler routine. These routines (especially the
  1504. X * parsing) are not pretty.
  1505. X *
  1506. X * The number list can be any one of:
  1507. X *
  1508. X *    1.    a simple number            eg:    14
  1509. X *            or
  1510. X *        a range of numbers        eg:    4-7  21-9
  1511. X *
  1512. X *    2.    "visible" or part thereof    eg:    vis
  1513. X *
  1514. X *    3.    "all" or part thereof        eg:    a
  1515. X */
  1516. X
  1517. X
  1518. Xstatic    void
  1519. Xprocess_range(data, func)
  1520. X
  1521. X    char    *data;
  1522. X    FUNC_CODE    func;
  1523. X
  1524. X{
  1525. X
  1526. X    int        new_unread_picked;
  1527. X    char    *cp;
  1528. X    void    (*handler)();
  1529. X
  1530. X/*
  1531. X * Determine which routine to call based on the function. Importantly,
  1532. X * establish what happens to the unread_picked status when the "all"
  1533. X * range is selected.
  1534. X */
  1535. X
  1536. X    switch (func) {
  1537. X
  1538. X      case SELECT_NUMBER:
  1539. X            handler = set_range;
  1540. X            new_unread_picked = unread_picked;
  1541. X            break;
  1542. X
  1543. X      case SELECT_RANGE:handler = set_range;
  1544. X            new_unread_picked = TRUE;
  1545. X            break;
  1546. X
  1547. X      case CLEAR_RANGE:    handler = clear_range;
  1548. X            new_unread_picked = FALSE;
  1549. X            break;
  1550. X
  1551. X      default:        /* Some broken compilers need this */
  1552. X      case TOGGLE_RANGE:handler = toggle_range;
  1553. X            new_unread_picked = !unread_picked;
  1554. X            break;
  1555. X    }
  1556. X
  1557. X/* Partially process the data here by trimming both ends */
  1558. X
  1559. X    while (*data && isspace(*data)) data++;        /* Front trimmed */
  1560. X
  1561. X    for (cp = data + strlen(data); cp > data;) {    /* Rear trimmed */
  1562. X    cp--;
  1563. X    if (!isspace(*cp)) break;
  1564. X    *cp = '\0';
  1565. X    }
  1566. X
  1567. X/* If nothing has been entered, assume the current line */
  1568. X
  1569. X    if (!*data) {
  1570. X    (handler)(current_line, current_line);
  1571. X    return;
  1572. X    }
  1573. X
  1574. X/* Convert to lower case so that strncasecmp isn't needed */
  1575. X
  1576. X    for (cp=data; *cp; cp++) {
  1577. X    if (isupper(*cp)) *cp = tolower(*cp);
  1578. X    }
  1579. X
  1580. X/*
  1581. X * Since the data can only be ONE OF: numbers, visible or all, 
  1582. X * get the problem of "visible" and "all" out of the way early.
  1583. X */
  1584. X
  1585. X    if (strncmp(data, VISIBLE_STRING, (strlen(data))) == 0) {
  1586. X    (handler)(top_of_screen, bottom_of_screen);
  1587. X    }
  1588. X    else {
  1589. X    if (strncmp(data, ALL_STRING, (strlen(data))) == 0) {
  1590. X        (handler)(first_line, last_line);
  1591. X        unread_picked = new_unread_picked;
  1592. X    }
  1593. X
  1594. X/* Not a special string, call parse to check, if all ok, re-call to do! */
  1595. X
  1596. X    else {
  1597. X        if (parse_number_range(data, TRUE, handler)) {
  1598. X        (void) parse_number_range(data, FALSE, handler);
  1599. X        }
  1600. X    }
  1601. X    }
  1602. X}
  1603. X
  1604. X
  1605. X/*
  1606. X * Ahhh, a quick and nasty parse - now things starts to get really
  1607. X * ugly. In addition to the parsing itself, the ranges are tricky as
  1608. X * they may cross a MOD_VALUE boundary, thus determining a reverse
  1609. X * range requires some care.
  1610. X *
  1611. X * If check_only is TRUE do not action the numbers, only check them.
  1612. X *
  1613. X * Return TRUE if data is OK.
  1614. X */
  1615. X
  1616. X
  1617. Xstatic    int
  1618. Xparse_number_range(data, check_only, handler)
  1619. X
  1620. X    char    *data;
  1621. X    int        check_only;
  1622. X    void    (*handler)();
  1623. X
  1624. X{
  1625. X    int        in_range = FALSE;    /* State to track where in the */
  1626. X    int        have_num1 = FALSE;    /* parse we are */
  1627. X    int        num1, num2;
  1628. X    int        cc;
  1629. X
  1630. X    num1 = num2 = 0;            /* The good ship lint */
  1631. X
  1632. X    while (cc = *data++) {
  1633. X
  1634. X    if (isspace(cc)) continue;    /* Ignore all whitespace */
  1635. X
  1636. X    if (isdigit(cc)) {    /* Found a digit, decode into a number */
  1637. X
  1638. X        num2 = cc - '0';
  1639. X        while (isdigit(*data)) {    /* Non-numeric stops a number */
  1640. X        num2 *= 10;
  1641. X        num2 += *data++ - '0';
  1642. X
  1643. X        if (num2 >= MOD_VALUE) {    /* Is number reasonable */
  1644. X            ERRMSG(msg_command[22]);    /* Too large */
  1645. X            return FALSE;
  1646. X        }
  1647. X        }
  1648. X
  1649. X/*
  1650. X * Convert to absolute line number by making it mod relative to the
  1651. X * first line on the screen. Ie, if they enter 45 and the line number
  1652. X * of the top_of_screen is 10XX, then add 1000 to 45.
  1653. X */
  1654. X
  1655. X        num2 += MOD_VALUE * (top_of_screen->line_number / MOD_VALUE);
  1656. X
  1657. X/*
  1658. X * If it's still less than top_of_screen, make it mod relative to the 
  1659. X * MOD_VALUE above top_of_screen. Consider when 45 is entered and the
  1660. X * line number of top_of_screen is 150. For this to be valid, it will
  1661. X * have to get through the reverse range processing.
  1662. X */
  1663. X
  1664. X        if (num2 < top_of_screen->line_number) num2 += MOD_VALUE;
  1665. X
  1666. X
  1667. X/* Now have a number, what state: First number, second number, end of range? */
  1668. X
  1669. X        if (in_range) {        /* In a range so it's now completed */
  1670. X
  1671. X/* Be nice and accept reversed ranges by swapping to ensure ascending. */
  1672. X
  1673. X        if (num2 < num1) {
  1674. X            num2 ^= num1; num1 ^= num2; num2 ^= num1;
  1675. X        }
  1676. X
  1677. X/* Process the range */
  1678. X
  1679. X        while (num1 <= num2) {
  1680. X            if (!find_and_handle(num1++, check_only, handler)) {
  1681. X            return FALSE;
  1682. X            }
  1683. X        }
  1684. X        in_range = have_num1 = FALSE;    /* Done with the range */
  1685. X        continue;
  1686. X        }
  1687. X
  1688. X/*
  1689. X * Not in range, if there's a previous number, it must be a series of
  1690. X * numbers, so process the first and make the second the first
  1691. X */
  1692. X
  1693. X        if (have_num1) {
  1694. X        if (!find_and_handle(num1, check_only, handler)) return FALSE;
  1695. X        num1 = num2;
  1696. X        continue;
  1697. X        }
  1698. X
  1699. X/*
  1700. X * First number we've seen, hold onto it until the subsequent token
  1701. X * tells us what to do.
  1702. X */
  1703. X
  1704. X        num1 = num2;
  1705. X        have_num1 = TRUE;
  1706. X        continue;
  1707. X    }
  1708. X
  1709. X/*
  1710. X * The input character is not a digit, it had better be a range
  1711. X * character.
  1712. X */
  1713. X
  1714. X    if (cc != '-') {
  1715. X        ERRMSG(msg_command[23]);    /* Illegal character in .. */
  1716. X        return FALSE;
  1717. X    }
  1718. X
  1719. X/*
  1720. X * If already in range (eg nn--), then "be generous in what we
  1721. X * accept", ie quietly accept it as a duplicate range character.
  1722. X */
  1723. X
  1724. X    if (in_range) continue;
  1725. X
  1726. X/* If there is a preceeding number, must be in range. (eg nn-) */
  1727. X
  1728. X    if (have_num1) {
  1729. X        in_range = TRUE;
  1730. X        continue;
  1731. X    }
  1732. X
  1733. X/* No preceeding number (eg -nn) in range assume top_of_screen. */
  1734. X
  1735. X    have_num1 = in_range = TRUE;
  1736. X    num1 = top_of_screen->line_number;
  1737. X    }
  1738. X
  1739. X/* End of data, what's left? */
  1740. X
  1741. X/* If range incomplete (eg nn-) assume bottom_of_screen */
  1742. X
  1743. X    if (in_range) {    /* Eg nn- */
  1744. X    num2 = bottom_of_screen->line_number;
  1745. X    while (num1 <= num2) {
  1746. X        if (!find_and_handle(num1++, check_only, handler)) return FALSE;
  1747. X    }
  1748. X    in_range = have_num1 = FALSE;    /* Done with the range */
  1749. X    }
  1750. X
  1751. X/* A trailing number needs to be processed */
  1752. X
  1753. X    if (have_num1) {
  1754. X    return (find_and_handle(num1, check_only, handler));
  1755. X    }
  1756. X
  1757. X    return TRUE;
  1758. X}
  1759. X
  1760. X
  1761. X
  1762. X/*
  1763. X * Find_and_handle is a helper routine for parse_number_range. Find the
  1764. X * nominated item on the screen and call the handler to change it,
  1765. X * otherwise barf and return FALSE. If good, return TRUE.
  1766. X */
  1767. X
  1768. Xstatic    int
  1769. Xfind_and_handle(item, check_only, handler)
  1770. X
  1771. X    int        item;
  1772. X    int        check_only;
  1773. X    void    (*handler)();
  1774. X
  1775. X{
  1776. X    LN        *lnp;
  1777. X
  1778. X    for (lnp=top_of_screen; lnp != bottom_of_screen->next; lnp = lnp->next) {
  1779. X
  1780. X    if (lnp->line_number == item) {    /* Found it */
  1781. X        if (!check_only) {
  1782. X        (*handler)(lnp, lnp);
  1783. X        current_line = lnp;
  1784. X        }
  1785. X        return TRUE;
  1786. X    }
  1787. X    }
  1788. X
  1789. X    ERRMSG(msg_command[24]);    /* Item number not on screen */
  1790. X
  1791. X    return FALSE;
  1792. X}
  1793. X
  1794. X
  1795. X/*
  1796. X * xterm support is a bit of a hack (IMHO). Basically, the keyboard
  1797. X * routine has seen the introducer of a mouse event (ESC [ M) and
  1798. X * returned that as a completed keybinding function to us. 
  1799. X *
  1800. X * This routine slurps in the remaining part of the sequence which
  1801. X * is hopefully there!
  1802. X *
  1803. X * The first mouse-down event defines the start line for the process
  1804. X * while the first mouse-up event defines the end line.
  1805. X *
  1806. X * Extraneous events are ignored.
  1807. X *
  1808. X * The complete sequence is ESC [ M b x y.
  1809. X *
  1810. X * Where:
  1811. X *    b    button (bottom 2 bits 0=Button1, 1=Button2, 2=Button3,
  1812. X *        3=Release.
  1813. X *
  1814. X *    x, y    Co-ords of mouse event in character positions (1 relative)
  1815. X *
  1816. X *    Note that b, x and y are encoded with a space in the usual way.
  1817. X */
  1818. X
  1819. Xstatic    void
  1820. Xxterm_event()
  1821. X
  1822. X{
  1823. X    static    LN    *mouse_down_lnp = NULL;
  1824. X    static    int    mouse_number;
  1825. X
  1826. X    int        button;
  1827. X    int        y;
  1828. X    LN        *mouse_up_lnp;
  1829. X    LN        *lnp;
  1830. X
  1831. X/* Slurp in the rest of the key sequence */
  1832. X
  1833. X    button = (scrn_getch() - ' ') & 0x3;    /* Bottom two bits only */
  1834. X    (void) scrn_getch();
  1835. X    y = scrn_getch() - ' ';
  1836. X
  1837. X#ifdef    TESTING
  1838. X    {
  1839. X    char    msg[100];
  1840. X    sprintf(msg, "Mouse: Y:%d event: %d (DN=%x)", /* English is ok here */
  1841. X        y, button, mouse_down_lnp);
  1842. X    STATMSG(msg);
  1843. X    sleep(1);
  1844. X    }
  1845. X#endif
  1846. X
  1847. X/* Filter out extraneous button events */
  1848. X
  1849. X    if (mouse_down_lnp && (button < 3)) {    /* Already have "down" */
  1850. X    return;
  1851. X    }
  1852. X
  1853. X    if (!mouse_down_lnp && (button == 3)) {    /* "Up" but no "down" */
  1854. X    return;
  1855. X    }
  1856. X
  1857. X
  1858. X/* Calculate y as the distance from the top_of_screen */
  1859. X
  1860. X    y -= wtitle_lines;
  1861. X
  1862. X/*
  1863. X * If the y co-ord is out of range (title or command perhaps ?), then
  1864. X * silently ignore. This is consistent with Xish handling of mouse
  1865. X * events.
  1866. X */
  1867. X
  1868. X    if ((y < 1) || (y > wdata_lines)) return;
  1869. X
  1870. X    y--;            /* y was 1 relative */
  1871. X        
  1872. X    lnp = move_relative(top_of_screen, y);    /* Find the line */
  1873. X
  1874. X    if (button < 3) {        /* Mouse down */
  1875. X
  1876. X    mouse_down_lnp = lnp;    /* Save it for up event */
  1877. X    mouse_number = button;
  1878. X
  1879. X    if (ON_SCREEN(mouse_down_lnp)) current_line = mouse_down_lnp;
  1880. X    return;
  1881. X    }
  1882. X
  1883. X    mouse_up_lnp = lnp;
  1884. X
  1885. X/*
  1886. X * Have an "up" event now and mouse_down_lnp has the previous "down"
  1887. X * event. Make sure that the range: mouse_down_lnp and mouse_up_lnp;
  1888. X * are in ascending order.
  1889. X */
  1890. X
  1891. X    if (mouse_down_lnp->line_number > mouse_up_lnp->line_number) {
  1892. X    lnp = mouse_up_lnp;
  1893. X    mouse_up_lnp = mouse_down_lnp;
  1894. X    mouse_down_lnp = lnp;
  1895. X    }
  1896. X
  1897. X/* Process depending on button */
  1898. X
  1899. X    switch (mouse_number) {
  1900. X
  1901. X      case 0:    /* Button 1 sets range */
  1902. X        set_range(mouse_down_lnp, mouse_up_lnp);
  1903. X        break;
  1904. X
  1905. X      case 1:    /* Button 2 toggles range */
  1906. X        toggle_range(mouse_down_lnp, mouse_up_lnp);
  1907. X        break;
  1908. X
  1909. X      case 2:    /* and 3 clears */
  1910. X        clear_range(mouse_down_lnp, mouse_up_lnp);
  1911. X        break;
  1912. X    }
  1913. X
  1914. X/* The release *should* be on screen, move the cursor to there */
  1915. X
  1916. X    if (ON_SCREEN(mouse_up_lnp)) current_line = mouse_up_lnp;
  1917. X
  1918. X    mouse_down_lnp = NULL;    /* Event processed */
  1919. X}
  1920. X
  1921. X
  1922. X/* Toggle range of lines inclusive. Cope with a possibly NULL 'to' value */
  1923. X
  1924. Xstatic    void
  1925. Xtoggle_range(from, to)
  1926. X
  1927. X    LN        *from;
  1928. X    LN        *to;
  1929. X
  1930. X{
  1931. X    LN        *lnp;
  1932. X
  1933. X    if (to) to = to->next;    /* To is inclusive */
  1934. X
  1935. X    for (lnp=from; lnp && (lnp != to); lnp=lnp->next) TOGGLE_LN(lnp);
  1936. X}
  1937. X
  1938. X
  1939. X/* Clear range of lines inclusive. Cope with a possibly NULL 'to' value */
  1940. X
  1941. Xstatic    void
  1942. Xclear_range(from, to)
  1943. X
  1944. X    LN        *from;
  1945. X    LN        *to;
  1946. X
  1947. X{
  1948. X    LN        *lnp;
  1949. X
  1950. X    if (to) to = to->next;    /* To is inclusive */
  1951. X
  1952. X    for (lnp=from; lnp && (lnp != to); lnp=lnp->next) {
  1953. X    if (lnp->picked) {
  1954. X        lnp->picked = FALSE;
  1955. X        total_picked--;
  1956. X    }
  1957. X    }
  1958. X}
  1959. X
  1960. X
  1961. X/* Set range of lines inclusive. Cope with a possibly NULL 'to' value */
  1962. X
  1963. Xstatic    void
  1964. Xset_range(from, to)
  1965. X
  1966. X    LN        *from;
  1967. X    LN        *to;
  1968. X
  1969. X{
  1970. X    LN        *lnp;
  1971. X
  1972. X    if (to) to = to->next;    /* To is inclusive */
  1973. X
  1974. X    for (lnp=from; lnp && (lnp != to); lnp=lnp->next) {
  1975. X    if (!lnp->picked) {
  1976. X        lnp->picked = TRUE;
  1977. X        total_picked++;
  1978. X    }
  1979. X    }
  1980. X}
  1981. X
  1982. X
  1983. X/*
  1984. X * Search is the command routine that handles all the search requests. 
  1985. X * It saves the previous pattern, slurps data lines as necessary and
  1986. X * fixes the screen afterwards.
  1987. X *
  1988. X * Note that forward_search has to ensure a screen full of data
  1989. X * *after* a successful find. This is necessary as it is one of the
  1990. X * few commands that can skip forward at a greater rate than lines are
  1991. X * being created.
  1992. X */
  1993. X
  1994. Xstatic    void
  1995. Xsearch(forward, pattern)
  1996. X
  1997. X    int        forward;
  1998. X    char    *pattern;
  1999. X
  2000. X{
  2001. X
  2002. X    LN        *search_lnp;
  2003. X
  2004. X    if (!pattern || !*pattern) {    /* If nothing provided, try last */
  2005. X    pattern = last_search;
  2006. X    }
  2007. X
  2008. X    if (!pattern || !*pattern) {    /* If still nothing, barf */
  2009. X    ERRMSG(msg_command[25]);    /* No previous pattern to search */
  2010. X    return;
  2011. X    }
  2012. X
  2013. X/*
  2014. X * Save search pattern if it's changed. Note that the strcmp check is
  2015. X * additionally making sure that the strcpy doesn't overcopy the same
  2016. X * piece of memory in the case of re-search - sorry.
  2017. X */
  2018. X
  2019. X    if (strcmp(pattern, last_search) != 0) strcpy(last_search, pattern);
  2020. X
  2021. X
  2022. X/* Start searching on the current line */
  2023. X
  2024. X    if (find_inline(current_line, forward, pattern)) {
  2025. X    search_lnp = current_line;
  2026. X    }
  2027. X    else {
  2028. X
  2029. X/* If not on the current line, go further afield */
  2030. X
  2031. X    if (!end_of_data) read_lines(2);
  2032. X    search_lnp = find_pattern(forward
  2033. X                  ? current_line->next
  2034. X                  : current_line->prev, forward, pattern);
  2035. X    }
  2036. X
  2037. X/* How did it go? */
  2038. X
  2039. X    if (search_lnp) {                /* Found */
  2040. X    current_line = search_lnp;        /* Point to found line */
  2041. X
  2042. X/* Cursor is located correctly, fix up column offset */
  2043. X
  2044. X    if     (    (cursor_offset < column_offset)
  2045. X         ||    ((cursor_offset + (int) strlen(pattern))
  2046. X             > (column_offset+wdata_cols))) {
  2047. X        column_offset = cursor_offset - (wdata_cols / 2);
  2048. X        if (column_offset < 0) column_offset = 0;
  2049. X    }
  2050. X
  2051. X    if (!ON_SCREEN(current_line)) MAKE_MIDPOINT(current_line);
  2052. X    }
  2053. X    else {
  2054. X    ERRMSG(msg_command[26]);
  2055. X    }
  2056. X}
  2057. X
  2058. X/*
  2059. X * find_inline handles the special case of searching on the current
  2060. X * line as the search has to go from the current cursor position to
  2061. X * the end/beginning of the line.
  2062. X */
  2063. X
  2064. Xstatic    int
  2065. Xfind_inline(lnp, forward, pattern)
  2066. X
  2067. X    LN        *lnp;
  2068. X    int        forward;
  2069. X    char    *pattern;
  2070. X
  2071. X{
  2072. X    char    *matchcp;
  2073. X
  2074. X    matchcp = NULL;
  2075. X
  2076. X    if (forward) {
  2077. X    if (cursor_offset < lnp->display_len) {
  2078. X        matchcp = strstr(lnp->display_data+cursor_offset+1, pattern);
  2079. X    }
  2080. X    }
  2081. X    else {
  2082. X    if (cursor_offset > 1) {
  2083. X        matchcp = my_strrstr(lnp->display_data, cursor_offset-1, pattern);
  2084. X    }
  2085. X    }
  2086. X
  2087. X/* If found, adjust the cursor */
  2088. X
  2089. X    if (matchcp) cursor_offset = matchcp - lnp->display_data + 1;
  2090. X
  2091. X    return (matchcp != NULL);
  2092. X}
  2093. X
  2094. X
  2095. X/*
  2096. X * Find routine is crude. No r.e's, no case insensitivity, nuffin'
  2097. X * It also sets cursor_offset and column_offset if a pattern is
  2098. X * found.
  2099. X */
  2100. X
  2101. X
  2102. Xstatic    LN    *
  2103. Xfind_pattern(start, forward, pattern)
  2104. X
  2105. X    LN        *start;
  2106. X    int        forward;
  2107. X    char    *pattern;
  2108. X
  2109. X{
  2110. X    LN        *lnp;
  2111. X    char    *matchcp;
  2112. X
  2113. X    for (lnp=start, matchcp=NULL;
  2114. X     lnp;
  2115. X     lnp = (forward ? lnp->next : lnp->prev)) {
  2116. X
  2117. X    if (forward) {
  2118. X        if (!lnp->next && !end_of_data) read_lines(2);
  2119. X        matchcp = strstr(lnp->display_data, pattern);
  2120. X    }
  2121. X    else {
  2122. X        matchcp = my_strrstr(lnp->display_data, lnp->display_len, pattern);
  2123. X    }
  2124. X    if (matchcp) break;
  2125. X    }
  2126. X
  2127. X    if (!matchcp) return NULL;
  2128. X
  2129. X/* Place the cursor on the first character of the matching pattern */
  2130. X
  2131. X    cursor_offset = (matchcp - lnp->display_data) + 1;
  2132. X
  2133. X    return lnp;
  2134. X}
  2135. X
  2136. X
  2137. X/*
  2138. X * Find_selected searches for the next selected item. If found, 
  2139. X * place it on the screen.
  2140. X */
  2141. X
  2142. Xstatic    void
  2143. Xfind_selected(forward, lnp)
  2144. X
  2145. X    int        forward;
  2146. X    LN        *lnp;
  2147. X
  2148. X{
  2149. X
  2150. X    while (lnp) {
  2151. X    if (lnp->picked) break;
  2152. X    lnp = forward ? lnp->next : lnp->prev;
  2153. X    }
  2154. X
  2155. X    if (!lnp) {        /* Was it found? */
  2156. X    ERRMSG(forward ? msg_command[27] : msg_command[28]); /* No more/prev */
  2157. X    }
  2158. X    else {
  2159. X    current_line = lnp;
  2160. X    if (!ON_SCREEN(current_line)) MAKE_MIDPOINT(current_line);
  2161. X    }
  2162. X}    
  2163. END_OF_FILE
  2164.   if test 29050 -ne `wc -c <'ipick/command.c'`; then
  2165.     echo shar: \"'ipick/command.c'\" unpacked with wrong size!
  2166.   fi
  2167.   # end of 'ipick/command.c'
  2168. fi
  2169. if test -f 'ipick/data.c' -a "${1}" != "-c" ; then 
  2170.   echo shar: Will not clobber existing file \"'ipick/data.c'\"
  2171. else
  2172.   echo shar: Extracting \"'ipick/data.c'\" \(3314 characters\)
  2173.   sed "s/^X//" >'ipick/data.c' <<'END_OF_FILE'
  2174. X/* $Id: data.c,v 1.1 1993/02/27 21:48:01 markd Exp $
  2175. X *
  2176. X * Static data structures for ipick, eg: command table lookup
  2177. X *
  2178. X *    Copyright (c) 1993, Mark Delany
  2179. X *
  2180. X *    You may distribute under the terms of either the GNU General Public
  2181. X *    License or the Artistic License, as specified in the README file.
  2182. X *
  2183. X * $Log: data.c,v $
  2184. X * Revision 1.1  1993/02/27  21:48:01  markd
  2185. X * Initial revision
  2186. X *
  2187. X */
  2188. X
  2189. X#define    IN_DATA
  2190. X
  2191. X#include "ipick.h"
  2192. X
  2193. X
  2194. X/* 
  2195. X * FUNC_MAP defines the actions to take for a given function.
  2196. X * Data_message serves two purposes. First it points to the message
  2197. X * that goes on the bottom of the screen to tell the user which
  2198. X * function they're in the middle of. Second, it tells getkey that the
  2199. X * command has data.
  2200. X *
  2201. X * Retain_first is a bit of a hack to cope with those commands which
  2202. X * also form part of the data. There is only one, when numbers are
  2203. X * used to select line numbers.
  2204. X *
  2205. X * **IMPORTANT**IMPORTANT**IMPORTANT.
  2206. X * **IMPORTANT**IMPORTANT**IMPORTANT.
  2207. X *
  2208. X * The initialization order of FUNC_MAP MUST match the order in which
  2209. X * the function codes have been enumerated in pick.h, otherwise
  2210. X * lookups of these codes will fail.  In other words, fiddlers beware.
  2211. X */
  2212. X
  2213. X    FUNC_MAP    func_list[LAST_COMMAND+1] = {
  2214. X
  2215. X/* func_name        message_index    Retain_first    Prompt */
  2216. X
  2217. X"first command",    -1,        FALSE,        '\0',    NULL,
  2218. X"select-range",        0,        FALSE,        '\0',    NULL,
  2219. X"select-number",    1,        TRUE,        '\0',    NULL,
  2220. X"select-all",        -1,        FALSE,        '\0',    NULL,
  2221. X"clear-range",        2,        FALSE,        '\0',    NULL,
  2222. X"clear-all",        -1,        FALSE,        '\0',    NULL,
  2223. X
  2224. X"toggle-current",    -1,        FALSE,        '\0',    NULL,
  2225. X"toggle-range",        3,        FALSE,        '\0',    NULL,
  2226. X"toggle-unread",    -1,        FALSE,        '\0',    NULL,
  2227. X
  2228. X"top-of-screen",    -1,        FALSE,        '\0',    NULL,
  2229. X"bottom-of-screen",    -1,        FALSE,        '\0',    NULL,
  2230. X
  2231. X"previous-line",    -1,        FALSE,        '\0',    NULL,
  2232. X"next-line",        -1,        FALSE,        '\0',    NULL,
  2233. X
  2234. X"quit",            -1,        FALSE,        '\0',    NULL,
  2235. X"abort",        -1,        FALSE,        '\0',    NULL,
  2236. X"help",            -1,        FALSE,        '\0',    NULL,
  2237. X"refresh",        -1,        FALSE,        '\0',    NULL,
  2238. X
  2239. X"scroll-left-char",    -1,        FALSE,        '\0',    NULL,
  2240. X"scroll-right-char",    -1,        FALSE,        '\0',    NULL,
  2241. X
  2242. X"beginning-of-line",    -1,        FALSE,        '\0',    NULL,
  2243. X"end-of-line",        -1,        FALSE,        '\0',    NULL,
  2244. X
  2245. X"scroll-tab",        -1,        FALSE,        '\0',    NULL,
  2246. X"scroll-backtab",    -1,        FALSE,        '\0',    NULL,
  2247. X
  2248. X"scroll-left-screen",    -1,        FALSE,        '\0',    NULL,
  2249. X"scroll-right-screen",    -1,        FALSE,        '\0',    NULL,
  2250. X
  2251. X"scroll-up-half",    -1,        FALSE,        '\0',    NULL,
  2252. X"scroll-down-half",    -1,        FALSE,        '\0',    NULL,
  2253. X
  2254. X"scroll-up-full",    -1,        FALSE,        '\0',    NULL,
  2255. X"scroll-down-full",    -1,        FALSE,        '\0',    NULL,
  2256. X
  2257. X"beginning-of-file",    -1,        FALSE,        '\0',    NULL,
  2258. X"end-of-file",        -1,        FALSE,        '\0',    NULL,
  2259. X
  2260. X"search-forward",    4,        FALSE,        '/',    NULL,
  2261. X"search-backward",    5,        FALSE,        '\\',    NULL,
  2262. X
  2263. X"re-search-forward",    -1,        FALSE,        '\0',    NULL,
  2264. X"re-search-backward",    -1,        FALSE,        '\0',    NULL,
  2265. X
  2266. X"next-selected",    -1,        FALSE,        '\0',    NULL,
  2267. X"previous-selected",    -1,        FALSE,        '\0',    NULL,
  2268. X
  2269. X"goto-line",        6,        FALSE,        '\0',    NULL,
  2270. X
  2271. X"shell",        7,        FALSE,        '!',    NULL,
  2272. X"pipe",            8,        FALSE,        '|',    NULL,
  2273. X
  2274. X"redo-command",        -1,        FALSE,        '\0',    NULL,
  2275. X
  2276. X"xterm-mouse",        -1,        FALSE,        '\0',    NULL,
  2277. X
  2278. X"invalid-command",    -1,        FALSE,        '\0',    NULL,
  2279. X
  2280. X/*
  2281. X * The follow commands are especially named so that they cannot possibly
  2282. X * match a token comming from a .rc file
  2283. X */
  2284. X
  2285. X"no command",        -1,        FALSE,        '\0',    NULL,
  2286. X"Lead in multi",    -1,        FALSE,        '\0',    NULL,
  2287. X
  2288. XNULL
  2289. X};
  2290. END_OF_FILE
  2291.   if test 3314 -ne `wc -c <'ipick/data.c'`; then
  2292.     echo shar: \"'ipick/data.c'\" unpacked with wrong size!
  2293.   fi
  2294.   # end of 'ipick/data.c'
  2295. fi
  2296. if test -f 'ipick/help.c' -a "${1}" != "-c" ; then 
  2297.   echo shar: Will not clobber existing file \"'ipick/help.c'\"
  2298. else
  2299.   echo shar: Extracting \"'ipick/help.c'\" \(3437 characters\)
  2300.   sed "s/^X//" >'ipick/help.c' <<'END_OF_FILE'
  2301. X/* $Id: help.c,v 1.1 1993/02/27 21:48:01 markd Exp $
  2302. X *
  2303. X * Give online help
  2304. X *
  2305. X *    Copyright (c) 1993, Mark Delany
  2306. X *
  2307. X *    You may distribute under the terms of either the GNU General Public
  2308. X *    License or the Artistic License, as specified in the README file.
  2309. X *
  2310. X * $Log: help.c,v $
  2311. X * Revision 1.1  1993/02/27  21:48:01  markd
  2312. X * Initial revision
  2313. X *
  2314. X */
  2315. X
  2316. X#include "ipick.h"
  2317. X
  2318. X
  2319. X/*
  2320. X * Give help does not work properly if the width of the screen is less
  2321. X * then 80 columns or the number of lines is less than the largest
  2322. X * lump - tough bikkies.
  2323. X */
  2324. X
  2325. Xextern    void
  2326. Xhlp_display()
  2327. X
  2328. X{
  2329. X    int        cc;
  2330. X    int        y, lump_size;
  2331. X    int        end_y;
  2332. X    HELP_TEXT    *start_htp, *end_htp;
  2333. X    BOUND_HELP    *bhp;
  2334. X
  2335. X    end_y = LINES - 2;
  2336. X    start_htp = help_text;
  2337. X
  2338. X/* Generate each screen of help while there is still more data */
  2339. X
  2340. X    while (start_htp->text) {
  2341. X
  2342. X/* Reset window to initial state */
  2343. X
  2344. X    move(0, 0);
  2345. X    clrtobot();
  2346. X    y = 0;
  2347. X
  2348. X/* Add in lumps while they fit */
  2349. X
  2350. X    while (start_htp->text && (y <= end_y)) {
  2351. X
  2352. X/* Find size of next lump */
  2353. X
  2354. X        for (end_htp = start_htp, lump_size = 0;
  2355. X         end_htp->text;
  2356. X         end_htp++, lump_size++) {
  2357. X        if (    (strcmp(end_htp->text, LUMP_END) == 0)
  2358. X            ||    (strcmp(end_htp->text, PAGE_END) == 0)) {
  2359. X            lump_size++;
  2360. X            break;
  2361. X        }
  2362. X        }
  2363. X
  2364. X/* If lump doesn't fit and not first lump on screen, iterate */
  2365. X
  2366. X        if (((y + lump_size) > end_y) && (y > 1)) break;
  2367. X
  2368. X/* It does fit, write it out until LUMP_END, PAGE_END or window full */
  2369. X
  2370. X        while (start_htp->text && (y <= end_y) && (lump_size > 0)) {
  2371. X        if (strcmp(start_htp->text, LUMP_END) == 0) {
  2372. X            start_htp++;
  2373. X            y++;
  2374. X            break;
  2375. X        }
  2376. X
  2377. X        if (strcmp(start_htp->text, PAGE_END) == 0) {
  2378. X            start_htp++;
  2379. X            y += LINES;        /* Force end of page */
  2380. X            break;
  2381. X        }
  2382. X
  2383. X        mvaddstr(y, 0, start_htp->text);
  2384. X
  2385. X/* Add in the bindings for this help */
  2386. X
  2387. X        if (start_htp->fc != NO_COMMAND) {
  2388. X            bhp=func_list[(int) start_htp->fc].bound_help;
  2389. X            if (!bhp) {
  2390. X            addstr(msg_help[2]);
  2391. X            }
  2392. X            else {
  2393. X            for (; bhp; bhp=bhp->next) {
  2394. X                addstr(bhp->text);
  2395. X                if (bhp->next) addstr(", ");
  2396. X            }
  2397. X            }
  2398. X        }
  2399. X
  2400. X/* Next line */
  2401. X
  2402. X        lump_size--;
  2403. X        y++;
  2404. X        start_htp++;
  2405. X        }
  2406. X    }
  2407. X
  2408. X/* Add "continue" message if more to go */
  2409. X
  2410. X    move(LINES - 1, 0);
  2411. X    printw("%s %s%s:          ", NAME, VERSION, RELEASE);
  2412. X
  2413. X    if (start_htp->text) {
  2414. X        addstr(msg_help[0]);    /* 'Q' to quit... */
  2415. X    }
  2416. X    else {
  2417. X        printw(msg_help[1], NAME);    /* Return to continue... */
  2418. X    }
  2419. X
  2420. X    refresh();
  2421. X
  2422. X    cc = scrn_getch();
  2423. X
  2424. X/* Continue if Ok to do so */
  2425. X
  2426. X    if (    (cc == 'q') || (cc == 'Q')
  2427. X        ||    (cc == MY_CTRL('D')) || !start_htp->text) {
  2428. X        break;
  2429. X    }
  2430. X    }
  2431. X}
  2432. X
  2433. X
  2434. X/*
  2435. X * Add a help string to function code mapping for possible later help
  2436. X * display requests.
  2437. X */
  2438. X
  2439. Xextern    void
  2440. Xhlp_add(func_code, code_help_text)
  2441. X
  2442. X    FUNC_CODE    func_code;
  2443. X    char    *code_help_text;
  2444. X
  2445. X{
  2446. X    BOUND_HELP    *bhp;
  2447. X    BOUND_HELP    *search_bhp;
  2448. X
  2449. X    if (!code_help_text || !*code_help_text) return;
  2450. X
  2451. X/* Malloc the structure to stash the data */
  2452. X
  2453. X    bhp = (BOUND_HELP *) xmalloc(sizeof(BOUND_HELP), "help:BOUND");
  2454. X    bhp->text = code_help_text;
  2455. X    bhp->next = NULL;
  2456. X
  2457. X    if (!func_list[(int) func_code].bound_help) {    /* Very first entry */
  2458. X    func_list[(int) func_code].bound_help = bhp;
  2459. X    return;
  2460. X    }
  2461. X
  2462. X/* Always insert at the end of the list of the same func_code */
  2463. X
  2464. X    for (search_bhp=func_list[(int) func_code].bound_help;
  2465. X     search_bhp->next;
  2466. X     search_bhp=search_bhp->next)    /* Empty loop */ ;
  2467. X
  2468. X    search_bhp->next = bhp;
  2469. X
  2470. X    return;
  2471. X}
  2472. END_OF_FILE
  2473.   if test 3437 -ne `wc -c <'ipick/help.c'`; then
  2474.     echo shar: \"'ipick/help.c'\" unpacked with wrong size!
  2475.   fi
  2476.   # end of 'ipick/help.c'
  2477. fi
  2478. if test -f 'ipick/port.c' -a "${1}" != "-c" ; then 
  2479.   echo shar: Will not clobber existing file \"'ipick/port.c'\"
  2480. else
  2481.   echo shar: Extracting \"'ipick/port.c'\" \(3643 characters\)
  2482.   sed "s/^X//" >'ipick/port.c' <<'END_OF_FILE'
  2483. X/* $Id: port.c,v 1.1 1993/02/27 21:48:01 markd Exp $
  2484. X *
  2485. X * Replacement routines when missing from the local system 
  2486. X *
  2487. X *    Copyright (c) 1993, Mark Delany
  2488. X *
  2489. X *    You may distribute under the terms of either the GNU General Public
  2490. X *    License or the Artistic License, as specified in the README file.
  2491. X *
  2492. X * $Log: port.c,v $
  2493. X * Revision 1.1  1993/02/27  21:48:01  markd
  2494. X * Initial revision
  2495. X *
  2496. X */
  2497. X
  2498. X#include "ipick.h"
  2499. X
  2500. X/*
  2501. X * Stuff not support by some systems. All of this is a crock, so you
  2502. X * will almost certainly want to improve any of the functions your
  2503. X * system needs for ipick. Feel marginally free to hack this module
  2504. X * around and send it back to the author.
  2505. X */
  2506. X
  2507. X
  2508. X#ifdef    NO_BEEP
  2509. X
  2510. Xextern    void
  2511. Xbeep()
  2512. X
  2513. X{
  2514. X    write(2, "\7", 1);
  2515. X}
  2516. X
  2517. X#endif
  2518. X
  2519. X
  2520. X#ifdef    NO_NEWTERM
  2521. X
  2522. X/*
  2523. X * If you've lied about NO_NEWTERM, you may still get the real one as
  2524. X * it's typically a macro in curses.h If the call does end up here, the
  2525. X * big problem is that most initscr()'s don't like multiple
  2526. X * calls (it is documented that way too), even with interspersed
  2527. X * endwin's.
  2528. X *
  2529. X * To protect against this problem, SIGWINCH handling is disabled
  2530. X * if NO_NEWTERM is set. This is because SIGWINCH is the only
  2531. X * reason that curses is restarted.
  2532. X */
  2533. X
  2534. Xextern    int
  2535. Xnewterm(TERM, tinfile, toutfile)
  2536. X
  2537. X    char    *TERM;
  2538. X    FILE    *tinfile;
  2539. X    FILE    *toutfile;
  2540. X{
  2541. X    close(0);
  2542. X    dup(fileno(tinfile));
  2543. X    close(1); 
  2544. X    dup(fileno(toutfile));
  2545. X    initscr();
  2546. X
  2547. X/* newterm is meant to return a (SCREEN *) but I know the caller.. */
  2548. X
  2549. X    return TRUE;
  2550. X}
  2551. X
  2552. X#endif
  2553. X
  2554. X
  2555. X#ifdef    NO_STANDOUT
  2556. X
  2557. X/* Really? There are curses that don't have even these old functions? */
  2558. X
  2559. Xextern    void
  2560. Xstandout()
  2561. X
  2562. X{
  2563. X}
  2564. X
  2565. Xextern    void
  2566. Xstandend()
  2567. X
  2568. X{
  2569. X}
  2570. X
  2571. X#endif
  2572. X
  2573. X
  2574. X#ifdef    NO_STRDUP
  2575. X
  2576. Xextern    char    *
  2577. Xstrdup(str)
  2578. X
  2579. X    char    *str;
  2580. X
  2581. X{
  2582. X    char    *cp;
  2583. X
  2584. X    cp = (char *) malloc(strlen(str) + 1);
  2585. X    if (cp) strcpy(cp, str);
  2586. X
  2587. X    return cp;
  2588. X}
  2589. X
  2590. X#endif
  2591. X
  2592. X
  2593. X#ifdef    NO_STRPBRK
  2594. X
  2595. X/*
  2596. X * Yet another crude libc replacement routine. Determine if any of the
  2597. X * characters in pattern exist in data. Return the pointer into data
  2598. X * of the first one found. Order of search is irrelevant to ipick but
  2599. X * I *believe* that the way it searchs is correct.
  2600. X */
  2601. X
  2602. Xextern    char    *
  2603. Xstrpbrk(data, pattern)
  2604. X
  2605. X    char    *data;
  2606. X    char    *pattern;
  2607. X
  2608. X{
  2609. X    int        pattern_len = strlen(pattern);
  2610. X    int        data_len = strlen(data);
  2611. X    int        di, pi;
  2612. X
  2613. X    for (di=0; di<data_len; di++) {
  2614. X    for (pi=0; pi<pattern_len; pi++) {
  2615. X        if (data[di] == pattern[pi]) return data+di;
  2616. X    }
  2617. X    }
  2618. X
  2619. X    return NULL;
  2620. X}
  2621. X
  2622. X#endif
  2623. X
  2624. X
  2625. X/*
  2626. X * Maybe this shouldn't be in port as no Clib I know of has this function.
  2627. X * Oops. I spoke too soon. HP/UX has it.
  2628. X *
  2629. X * Like strstr() except that the search starts at the end of the string
  2630. X * and works it way back to the beginning.
  2631. X */
  2632. X
  2633. Xextern    char    *
  2634. Xmy_strrstr(str, len, pattern)
  2635. X
  2636. X    char    *str;
  2637. X    int        len;
  2638. X    char    *pattern;
  2639. X
  2640. X{
  2641. X
  2642. X    int        patternl = strlen(pattern);
  2643. X    register    char    *cp;
  2644. X
  2645. X    if (len < patternl) return NULL;
  2646. X
  2647. X    for (cp = str + len - patternl; cp >= str; cp--) {
  2648. X    if ((*cp == *pattern) && (strncmp(cp, pattern, patternl) == 0)) {
  2649. X        return cp;
  2650. X    }
  2651. X    }
  2652. X
  2653. X    return NULL;
  2654. X}
  2655. X
  2656. X
  2657. X
  2658. X#ifdef    NO_STRSTR
  2659. X
  2660. X/*
  2661. X * Find pattern in data. Crude approximation to the real thing that
  2662. X * does the job sufficiently for ipick.
  2663. X */
  2664. X
  2665. Xextern    char    *
  2666. Xstrstr(data, pattern)
  2667. X
  2668. X    char    *data;
  2669. X    char    *pattern;
  2670. X
  2671. X{
  2672. X    int        dlen = strlen(data);
  2673. X    int        plen = strlen(pattern);
  2674. X    register    char    *dp;
  2675. X
  2676. X    dp = data;
  2677. X    dlen -= (plen - 1);        /* Can stop search at this point */
  2678. X
  2679. X    for (dp = data; dlen >= 0; dlen--, dp++) {
  2680. X
  2681. X    if (*dp == *pattern) {        /* Quick check */
  2682. X        if (strncmp(dp, pattern, plen) == 0) return dp;
  2683. X    }
  2684. X
  2685. X    }
  2686. X
  2687. X    return NULL;
  2688. X}
  2689. X
  2690. X#endif
  2691. END_OF_FILE
  2692.   if test 3643 -ne `wc -c <'ipick/port.c'`; then
  2693.     echo shar: \"'ipick/port.c'\" unpacked with wrong size!
  2694.   fi
  2695.   # end of 'ipick/port.c'
  2696. fi
  2697. if test -f 'ipick/config/gould' -a "${1}" != "-c" ; then 
  2698.   echo shar: Will not clobber existing file \"'ipick/config/gould'\"
  2699. else
  2700.   echo shar: Extracting \"'ipick/config/gould'\" \(222 characters\)
  2701.   sed "s/^X//" >'ipick/config/gould' <<'END_OF_FILE'
  2702. X#    For:    Gould SEL PowerNode (6040) UTX/32 2.0 - AT&T universe
  2703. X#    From:    Kevin Stock <kstock@encore.com>
  2704. X
  2705. XP_CFLAGS    =
  2706. XP_LIBS        = -lcurses
  2707. XP_NO_FLAGS    = -DNO_STDLIB_H -DNO_PROTOTYPES -DNO_STRSTR -DNO_STRDUP
  2708. X
  2709. X# Maybe? -DNO_STRPBRK
  2710. END_OF_FILE
  2711.   if test 222 -ne `wc -c <'ipick/config/gould'`; then
  2712.     echo shar: \"'ipick/config/gould'\" unpacked with wrong size!
  2713.   fi
  2714.   # end of 'ipick/config/gould'
  2715. fi
  2716. echo shar: End of archive 2 \(of 5\).
  2717. cp /dev/null ark2isdone
  2718. MISSING=""
  2719. for I in 1 2 3 4 5 ; do
  2720.     if test ! -f ark${I}isdone ; then
  2721.     MISSING="${MISSING} ${I}"
  2722.     fi
  2723. done
  2724. if test "${MISSING}" = "" ; then
  2725.     echo You have unpacked all 5 archives.
  2726.     echo "Check Makefile then run 'make'"
  2727.     rm -f ark[1-9]isdone
  2728. else
  2729.     echo You still must unpack the following archives:
  2730.     echo "        " ${MISSING}
  2731. fi
  2732. exit 0
  2733.  
  2734. exit 0 # Just in case...
  2735.