home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3579 < prev    next >
Text File  |  1991-07-02  |  20KB  |  680 lines

  1. Newsgroups: alt.sources
  2. From: goer@ellis.uchicago.edu (Richard L. Goerwitz)
  3. Subject: kjv browser, part 3 of 11
  4. Message-ID: <1991Jul3.065005.27989@midway.uchicago.edu>
  5. Date: Wed, 3 Jul 1991 06:50:05 GMT
  6.  
  7. ---- Cut Here and feed the following to sh ----
  8. #!/bin/sh
  9. # this is bibleref.03 (part 3 of a multipart archive)
  10. # do not concatenate these parts, unpack them in order with /bin/sh
  11. # file srchutil.icn continued
  12. #
  13. if test ! -r _shar_seq_.tmp; then
  14.     echo 'Please unpack part 1 first!'
  15.     exit 1
  16. fi
  17. (read Scheck
  18.  if test "$Scheck" != 3; then
  19.     echo Please unpack part "$Scheck" next!
  20.     exit 1
  21.  else
  22.     exit 0
  23.  fi
  24. ) < _shar_seq_.tmp || exit 1
  25. if test ! -f _shar_wnt_.tmp; then
  26.     echo 'x - still skipping srchutil.icn'
  27. else
  28. echo 'x - continuing file srchutil.icn'
  29. sed 's/^X//' << 'SHAR_EOF' >> 'srchutil.icn' &&
  30. X#
  31. X#    Name:     srchutil.icn
  32. X#
  33. X#    Title:     search utilities for bibleref
  34. X#
  35. X#    Author:     Richard L. Goerwitz
  36. X#
  37. X#    Version: 1.5
  38. X#
  39. X############################################################################
  40. X#
  41. X#  Contains:
  42. X#
  43. X#      compose_search(), which compiles a little automaton which, when
  44. X#          run via do_search(), returns a list of hits (i.e. retrieve-
  45. X#          format bitmaps),
  46. X#
  47. X#      do_search(), on which see above,
  48. X#
  49. X#      various other utilities (e.g. compose_spaced_search())
  50. X#
  51. X############################################################################
  52. X#
  53. X#  Links:  ./complete.icn
  54. X#
  55. X#  See also:  bibleref.icn
  56. X#
  57. X############################################################################
  58. X
  59. X# for debugging
  60. X# link ximage
  61. X
  62. Xprocedure search_database()
  63. X
  64. X    #
  65. X    # Search database for word or patterns matching whole words.
  66. X    #
  67. X    local search_machine, result, string_memb, tmp
  68. X
  69. X    search_machine := compose_search() | {
  70. X    err_message("No search performed.  Aborting.")
  71. X    fail
  72. X    }
  73. X
  74. X    # for debugging purposes
  75. X    # write(&errout, ximage(search_machine))
  76. X
  77. X    if *search_machine > 4
  78. X    then message("Executing complex search...")
  79. X    else message("Executing search...")
  80. X    *(result := do_search(search_machine)[1]) > 0 | {
  81. X    err_message("No hits.")
  82. X    fail
  83. X    }
  84. X
  85. X    #
  86. X    # Nasty kludge to see what search strings were incorporated into the
  87. X    # search_machine.    
  88. X    #
  89. X    string_memb := "???"
  90. X    tmp := search_machine[2]
  91. X    repeat {
  92. X    if type(tmp) == "string" then
  93. X        string_memb := tmp & break
  94. X    else if type(tmp) == "list" then
  95. X        tmp := tmp[2]
  96. X    else break
  97. X    }
  98. X    if *search_machine > 4 then string_memb ||:= "..."
  99. X
  100. X    if type(result) == "set" then result := sort(result)
  101. X    put(lists, lst(result, 0, &null, string_memb))
  102. X    return lists[-1]
  103. X
  104. Xend
  105. X    
  106. X
  107. X
  108. Xprocedure compose_search()
  109. X
  110. X    #
  111. X    # Put together a little search machine out of patterns specified by
  112. X    # the user.  Don't execute, though.  Just return a list containing
  113. X    # the user's directions to the calling procedure, and let it handle
  114. X    # execution (via do_search()).
  115. X    #
  116. X
  117. X    local pattern, status, sense_of_search, rsp, result, u, r
  118. X    static blanks
  119. X    initial blanks := ' \t,'
  120. X
  121. X    if pos(0)
  122. X    then rsp := trim(snarf_input("Enter word (q to abort):  "), blanks)
  123. X    else return compose_spaced_search(blanks)
  124. X
  125. X    rsp == (""|"q") & fail
  126. X    if rsp ? (="!", pattern := tab(many(blanks)), tab(0)) then
  127. X    sense_of_search := "inverted"
  128. X    else pattern := rsp
  129. X
  130. X    result := [retrieve, pattern, kjv_filename, sense_of_search]
  131. X    repeat {
  132. X    status := map(snarf_input("f to finish, or a/o/n (q aborts):  "))
  133. X    if upto(blanks, rsp) then
  134. X        # And together all words in the input string.
  135. X        return rsp ? compose_spaced_search(blanks)
  136. X    if status == "f" then
  137. X        return result
  138. X    else if status == ("a"|"n"|"o") then {
  139. X        if status ~== "o" then {
  140. X        u := GetUnit() | next
  141. X        r := GetRange() | next
  142. X        }
  143. X        return case status of {
  144. X        "a"  : [r_and, result, compose_search(), kjv_filename, u, r]
  145. X        "o"  : [r_and, result, compose_search(), kjv_filename, u, r]
  146. X        "n"  : [r_and, result, compose_search(), kjv_filename, u, r]
  147. X        } | fail
  148. X    }
  149. X    else if status == (""|"q") then fail
  150. X    else err_message("F = finish, a = and, o = or, n = and-not.")
  151. X    }
  152. X
  153. Xend
  154. X
  155. X
  156. X
  157. Xprocedure GetUnit()
  158. X
  159. X    local resp
  160. X
  161. X    repeat {
  162. X    resp := map(snarf_input("Enter unit (b/c/v):  "))
  163. X    case resp of {
  164. X        "b"    : return 1
  165. X        "c"    : return 2
  166. X        "v"    : return 3
  167. X        "q"|"" : fail
  168. X        default : {
  169. X        err_message("Enter b (book), c (chapter), or v (verse).")
  170. X        next
  171. X        }
  172. X    }
  173. X    }
  174. X
  175. Xend
  176. X
  177. X
  178. X
  179. Xprocedure GetRange()
  180. X
  181. X    local resp
  182. X
  183. X    repeat {
  184. X    resp := map(snarf_input("Enter range:  "))
  185. X    if resp := integer(resp) then
  186. X        return resp
  187. X    else {
  188. X        resp == ("q"|"") & fail
  189. X        err_message("Enter b (book), c (chapter), or v (verse).")
  190. X        next
  191. X    }
  192. X    }
  193. X
  194. Xend
  195. X
  196. X
  197. X
  198. Xprocedure do_search(l)
  199. X
  200. X    #
  201. X    # Executes the little machine put together by compose_search().
  202. X    #
  203. X
  204. X    if *l = 0 
  205. X    then return l
  206. X
  207. X    case type(l[1]) of {
  208. X    "list"       : return do_search(l[1]) ||| do_search(l[2:0])
  209. X    "procedure"  : return [l[1]!do_search(l[2:0])] | [[]]
  210. X    default      : return [l[1]] ||| do_search(l[2:0])
  211. X    }
  212. X
  213. Xend
  214. X
  215. X
  216. X
  217. Xprocedure compose_spaced_search(blanks)
  218. X
  219. X    #
  220. X    # Try to turn searches with spaces in them (e.g. "sackcloth and ashes")
  221. X    # into separate searches for each constituent word anded together.
  222. X    # This routine is set up, though, to handle single words or patterns
  223. X    # as well (e.g. "sackcloth").
  224. X    #
  225. X
  226. X    local token, sense_of_search, search_list, dumb_move
  227. X    static wordchars
  228. X    initial wordchars := ~blanks
  229. X
  230. X    #
  231. X    # Whoops, no string.  This shouldn't happen, but just in case I screw
  232. X    # up somewhere in the code, and forget to strip out superfluous blanks
  233. X    # typed in by the user...
  234. X    #
  235. X    tab(upto(wordchars)) | {
  236. X    err_message("No search string.  Aborting.")
  237. X    fail
  238. X    }
  239. X    if ="!" then {
  240. X    sense_of_search := 1
  241. X    tab(upto(wordchars))
  242. X    }
  243. X    token := tab(many(wordchars)) | {
  244. X    err_message("Unexpected end of input.  Aborting")
  245. X    fail
  246. X    }
  247. X
  248. X    #
  249. X    # Make sure tokens aren't just wildcard patterns!  Also, warn the
  250. X    # user about searches containing really common words.
  251. X    #
  252. X    upto(&letters, token) | {
  253. X    err_message("Token "||token||" has no letters in it!")
  254. X    fail
  255. X    }
  256. X    token == (dumb_move := "and"|"the"|"a") & {
  257. X    err_message("Try not to use common words like \""||dumb_move||".\"")
  258. X    }
  259. X
  260. X    #
  261. X    # If we've reached the end of the search string, return what we
  262. X    # have...
  263. X    #
  264. X    search_list := [retrieve, token, kjv_filename, sense_of_search]
  265. X    pos(0) & (return search_list)
  266. X
  267. X    #
  268. X    # ...otherwise and this search string together with the next one,
  269. X    #
  270. X    return [r_and, search_list,
  271. X        compose_spaced_search(blanks),
  272. X        kjv_filename, 3, 0]
  273. X
  274. Xend
  275. SHAR_EOF
  276. echo 'File srchutil.icn is complete' &&
  277. true || echo 'restore of srchutil.icn failed'
  278. rm -f _shar_wnt_.tmp
  279. fi
  280. # ============= complete.icn ==============
  281. if test -f 'complete.icn' -a X"$1" != X"-c"; then
  282.     echo 'x - skipping complete.icn (File already exists)'
  283.     rm -f _shar_wnt_.tmp
  284. else
  285. > _shar_wnt_.tmp
  286. echo 'x - extracting complete.icn (Text)'
  287. sed 's/^X//' << 'SHAR_EOF' > 'complete.icn' &&
  288. X############################################################################
  289. X#
  290. X#    Name:     complete.icn
  291. X#
  292. X#    Title:     complete partial input string
  293. X#
  294. X#    Author:     Richard L. Goerwitz
  295. X#
  296. X#    Version: 1.7
  297. X#
  298. X############################################################################
  299. X#
  300. X#  This file contains a single procedure, complete(s,st), which
  301. X#  completes a string (s) relative to a set or list of strings (st).
  302. X#  Put differently, complete() lets you supply a partial string, s,
  303. X#  and get back those strings in st that s is either equal to or a
  304. X#  substring of.
  305. X#
  306. X#  Lots of command interfaces allow completion of partial input.
  307. X#  Complete() simply represents my personal sentiments about how this
  308. X#  might best be done in Icon.  If you strip away the profuse comments
  309. X#  below, you end up with only about thirty lines of actual source
  310. X#  code.
  311. X#
  312. X#  I have arranged things so that only that portion of an automaton
  313. X#  which is needed to complete a given string is actually created and
  314. X#  stored.  Storing automata for later use naturally makes complete()
  315. X#  eat up more memory.  The performance gains can make it worth the
  316. X#  trouble, though.  If, for some reason, there comes a time when it
  317. X#  is advisable to reclaim the space occupied by complete's static
  318. X#  structures, you can just call it without arguments.  This
  319. X#  "resets" complete() and forces an immediate garbage collection.
  320. X#  
  321. X# Example code:
  322. X#
  323. X#      commands := ["run","stop","quit","save","load","continue"]
  324. X#      while line := read(&input) do {
  325. X#          cmds := list()
  326. X#          every put(cmds, complete(line, commands))
  327. X#          case *cmds of {
  328. X#              0 : input_error(line)
  329. X#              1 : do_command(cmds[1])
  330. X#              default : display_possible_completions(cmds)
  331. X#          }
  332. X#          etc...
  333. X#
  334. X#  More Iconish methods might include displaying successive
  335. X#  alternatives each time the user presses the tab key (this would,
  336. X#  however, require using the nonportable getch() routine).  Another
  337. X#  method might be to use the first string suspended by complete().
  338. X#
  339. X#  NOTE: This entire shebang could be replaced with a slightly slower
  340. X#  and much smaller program suggested to me by Jerry Nowlin and Bob
  341. X#  Alexander.
  342. X#
  343. X#      procedure terscompl(s, st)
  344. X#          suspend match(s, p := !st) & p
  345. X#      end
  346. X#
  347. X#  This program will work fine for lists with just a few members, and
  348. X#  also for cases where s is fairly large.  It will also use much less
  349. X#  memory.
  350. X#
  351. X############################################################################
  352. X#
  353. X#  Links: none
  354. X#
  355. X############################################################################
  356. X
  357. X
  358. X
  359. Xprocedure complete(s,st)
  360. X
  361. X    local dfstn, c, l, old_chr, chr, newtbl, str, strset
  362. X    static t
  363. X    initial t := table()
  364. X
  365. X    # No-arg invocation wipes out static structures & causes an
  366. X    # immediate garbage collection.
  367. X    if /s & /st then {
  368. X    t := table()
  369. X    collect()        # do it NOW
  370. X    fail
  371. X    }
  372. X    type(st) == ("list"|"set") |
  373. X    stop("error (complete):  list or set expected for arg2")
  374. X
  375. X    # Seriously, all that's being done here is that possible states
  376. X    # are being represented by sets containing possible completions of
  377. X    # s relative to st.  Each time a character is snarfed from s, we
  378. X    # check to see what strings in st might represent possible
  379. X    # completions, and store these in yet another set.  At some
  380. X    # point, we either run into a character in s that makes comple-
  381. X    # tion impossible (fail), or we run out of characters in s (in
  382. X    # which case we succeed, & suspend each of the possible
  383. X    # completions).
  384. X
  385. X    # Store any sets we have to create in a static structure for later
  386. X    # re-use.
  387. X    /t[st] := table()
  388. X
  389. X    # We'll call the table entry for the current set dfstn.  (It really
  390. X    # does enable us to do things deterministically.)
  391. X    dfstn := t[st]
  392. X
  393. X    # Snarf one character at a time from s.
  394. X    every c := !s do {
  395. X
  396. X    # The state we're in is represented by the set of all possible
  397. X    # completions before c was read.  If we haven't yet seen char
  398. X    # c in this state, run through the current-possible-completion
  399. X    # set, popping off the first character of each possible
  400. X    # completion, and then construct a table which uses these
  401. X    # initial chars as keys, and makes the completions that are
  402. X    # possible for each of these characters into the values for
  403. X    # those keys.
  404. X    if /dfstn[st] then {
  405. X
  406. X        # To get strings that start with the same char together,
  407. X        # sort the current string set (st).
  408. X        l := sort(st)
  409. X        newtbl := table()
  410. X        old_chr := ""
  411. X        # Now pop off each member of the sorted string set.  Use
  412. X        # first characters as keys, and then divvy up the full strings
  413. X        # into sets of strings having the same initial letter.
  414. X        every str := !l do {
  415. X        str ? { chr := move(1) | next; str := tab(0) }
  416. X        if old_chr ~==:= chr then {
  417. X            strset := set([str])
  418. X            insert(newtbl, chr, strset)
  419. X        }
  420. X        else insert(strset, str)
  421. X        }
  422. X        insert(dfstn, st, newtbl)
  423. X    }
  424. X
  425. X    # What we've done essentially is to create a table in which
  426. X    # the keys represent labeled arcs out of the current state,
  427. X    # and the values represent possible completion sets for those
  428. X    # paths.  What we need to do now is store that table in dfstn
  429. X    # as the value of the current state-set (i.e. the current
  430. X    # range of possible completions).  Once stored, we can then
  431. X    # see if there is any arc from the current state (dfstn[st])
  432. X    # with the label c (dfstn[st][c]).  If so, its value becomes
  433. X    # the new current state (st), and we cycle around again for
  434. X    # yet another c.
  435. X    st := \dfstn[st][c] | fail
  436. X    if *st = 1 & match(s,!st)
  437. X    then break
  438. X    }
  439. X
  440. X    # Eventually we run out of characters in c.  The current state
  441. X    # (i.e. the set of possible completions) can simply be suspended
  442. X    # one element at a time, with s prefixed to each element.  If, for
  443. X    # instance, st had contained ["hello","help","hear"] at the outset
  444. X    # and s was equal to "hel", we would now be suspending "hel" ||
  445. X    # !set(["lo","p"]).
  446. X    suspend s || !st
  447. X
  448. Xend
  449. SHAR_EOF
  450. true || echo 'restore of complete.icn failed'
  451. rm -f _shar_wnt_.tmp
  452. fi
  453. # ============= ipause.icn ==============
  454. if test -f 'ipause.icn' -a X"$1" != X"-c"; then
  455.     echo 'x - skipping ipause.icn (File already exists)'
  456.     rm -f _shar_wnt_.tmp
  457. else
  458. > _shar_wnt_.tmp
  459. echo 'x - extracting ipause.icn (Text)'
  460. sed 's/^X//' << 'SHAR_EOF' > 'ipause.icn' &&
  461. X############################################################################
  462. X#
  463. X#    Name:     ipause.icn
  464. X#
  465. X#    Title:     pause within an Icon program
  466. X#
  467. X#    Author:     Richard L. Goerwitz
  468. X#
  469. X#    Version: 1.2
  470. X#
  471. X############################################################################
  472. X#
  473. X#  Ipause(i) - pause i milliseconds (accuracy depends on the resolution
  474. X#  of the system clock).  Would be nice if Icon had a nap() function, so
  475. X#  that we didn't just have to loop.  Of course, for operating systems
  476. X#  that don't support all this multitasking nonsense, ipause() will do
  477. X#  just fine.
  478. X#
  479. X############################################################################
  480. X
  481. X
  482. Xprocedure ipause(i)
  483. X
  484. X    local T
  485. X    T := &time
  486. X    until &time >= (T + i)
  487. X    return
  488. X
  489. Xend
  490. SHAR_EOF
  491. true || echo 'restore of ipause.icn failed'
  492. rm -f _shar_wnt_.tmp
  493. fi
  494. # ============= rewrap.icn ==============
  495. if test -f 'rewrap.icn' -a X"$1" != X"-c"; then
  496.     echo 'x - skipping rewrap.icn (File already exists)'
  497.     rm -f _shar_wnt_.tmp
  498. else
  499. > _shar_wnt_.tmp
  500. echo 'x - extracting rewrap.icn (Text)'
  501. sed 's/^X//' << 'SHAR_EOF' > 'rewrap.icn' &&
  502. X############################################################################
  503. X#
  504. X#    Name:     rewrap.icn
  505. X#
  506. X#    Title:     advanced line rewrap utility
  507. X#
  508. X#    Author:     Richard L. Goerwitz
  509. X#
  510. X#    Version: 1.3
  511. X#
  512. X############################################################################
  513. X#
  514. X#  The procedure rewrap(s,i), included in this file, reformats text
  515. X#  fed to it into strings < i in length.  Rewrap utilizes a static
  516. X#  buffer, so it can be called repeatedly with different s arguments,
  517. X#  and still produce homogenous output.  This buffer is flushed by
  518. X#  calling rewrap with a null first argument.  The default for
  519. X#  argument 2 (i) is 70.
  520. X#
  521. X#  Here's a simple example of how rewrap could be used.  The following
  522. X#  program reads the standard input, producing fully rewrapped output.
  523. X#
  524. X#  procedure main()
  525. X#      every write(rewrap(!&input))
  526. X#      write(rewrap())
  527. X#  end
  528. X#
  529. X#  Naturally, in practice you would want to do things like check for in-
  530. X#  dentation or blank lines in order to wrap only on a paragraph-by para-
  531. X#  graph basis, as in
  532. X#
  533. X#  procedure main()
  534. X#      while line := read(&input) do {
  535. X#          if line == "" then {
  536. X#              "" ~== write(rewrap())
  537. X#              write(line)
  538. X#          } else {
  539. X#              if match("\t", line) then {
  540. X#                  write(rewrap())
  541. X#                  write(rewrap(line))
  542. X#              } else {
  543. X#                  write(rewrap(line))
  544. X#              }
  545. X#          }
  546. X#      }
  547. X#  end
  548. X#
  549. X#  Fill-prefixes can be implemented simply by prepending them to the
  550. X#  output of rewrap:
  551. X#
  552. X#      i := 70; fill_prefix := " > "
  553. X#      while line := read(input_file) do {
  554. X#          line ?:= (f_bit := tab(many('> ')) | "", tab(0))
  555. X#          write(fill_prefix || f_bit || rewrap(line, i - *fill_prefix))
  556. X#          etc.
  557. X#
  558. X#  Obviously, these examples are fairly simplistic.  Putting them to
  559. X#  actual use would certainly require a few environment-specific
  560. X#  modifications and/or extensions.  Still, I hope they offer some
  561. X#  indication of the kinds of applications rewrap might be used in.
  562. X# 
  563. X#  Note:  If you want leading and trailing tabs removed, map them to
  564. X#  spaces first.  Rewrap only fools with spaces, leaving tabs intact.
  565. X#  This can be changed easily enough, by running its input through the
  566. X#  Icon detab() function.
  567. X#
  568. X############################################################################
  569. X#
  570. X#  See also:  wrap.icn
  571. X#
  572. X############################################################################
  573. X
  574. X
  575. Xprocedure rewrap(s,i)
  576. X
  577. X    local extra_bit, line
  578. X    static old_line
  579. X    initial old_line := ""
  580. X
  581. X    # Default column to wrap on is 70.
  582. X    /i := 70
  583. X    # Flush buffer on null first argument.
  584. X    if /s then {
  585. X    extra_bit := old_line
  586. X    old_line := ""
  587. X    return "" ~== extra_bit
  588. X    }
  589. X
  590. X    # Prepend to s anything that is in the buffer (leftovers from the last s).
  591. X    s ?:= { tab(many(' ')); old_line || trim(tab(0)) }
  592. X
  593. X    # If the line isn't long enough, just add everything to old_line.
  594. X    if *s < i then old_line := s || " " & fail
  595. X
  596. X    s ? {
  597. X
  598. X    # While it is possible to find places to break s, do so.
  599. X    while any(' -',line := EndToFront(i),-1) do {
  600. X        # Clean up and suspend the last piece of s tabbed over.
  601. X        line ?:= (tab(many(' ')), trim(tab(0)))
  602. X            if *&subject - &pos + *line > i
  603. X        then suspend line
  604. X        else {
  605. X        old_line := ""
  606. X        return line || tab(0)
  607. X        }
  608. X    }
  609. X
  610. X    # Keep the extra section of s in a buffer.
  611. X    old_line := tab(0)
  612. X
  613. X    # If the reason the remaining section of s was unrewrapable was
  614. X    # that it was too long, and couldn't be broken up, then just return
  615. X    # the thing as-is.
  616. X    if *old_line > i then {
  617. X        old_line ? {
  618. X        if extra_bit := tab(upto(' -')+1) || (tab(many(' ')) | "")
  619. X        then old_line := tab(0)
  620. X        else extra_bit := old_line & old_line := ""
  621. X        return trim(extra_bit)
  622. X        }
  623. X    }
  624. X    # Otherwise, clean up the buffer for prepending to the next s.
  625. X    else {
  626. X        # If old_line is blank, then don't mess with it.  Otherwise,
  627. X        # add whatever is needed in order to link it with the next s.
  628. X        if old_line ~== "" then {
  629. X        # If old_line ends in a dash, then there's no need to add a
  630. X        # space to it.
  631. X        if old_line[-1] ~== "-"
  632. X        then old_line ||:= " "
  633. X        }
  634. X    }
  635. X    }
  636. X    
  637. Xend
  638. X
  639. X
  640. X
  641. Xprocedure EndToFront(i)
  642. X    # Goes with rewrap(s,i)
  643. X    *&subject+1 - &pos >= i | fail
  644. X    suspend &subject[.&pos:&pos <- &pos+i to &pos by -1]
  645. Xend
  646. SHAR_EOF
  647. true || echo 'restore of rewrap.icn failed'
  648. rm -f _shar_wnt_.tmp
  649. fi
  650. # ============= binsrch.icn ==============
  651. if test -f 'binsrch.icn' -a X"$1" != X"-c"; then
  652.     echo 'x - skipping binsrch.icn (File already exists)'
  653.     rm -f _shar_wnt_.tmp
  654. else
  655. > _shar_wnt_.tmp
  656. echo 'x - extracting binsrch.icn (Text)'
  657. sed 's/^X//' << 'SHAR_EOF' > 'binsrch.icn' &&
  658. X############################################################################
  659. X#
  660. X#    Name:     binsrch.icn
  661. X#
  662. X#    Title:     general-purpose binary index search
  663. X#
  664. X#    Author:     Richard L. Goerwitz
  665. X#
  666. X#    Version: 1.4
  667. X#
  668. X############################################################################
  669. SHAR_EOF
  670. true || echo 'restore of binsrch.icn failed'
  671. fi
  672. echo 'End of  part 3'
  673. echo 'File binsrch.icn is continued in part 4'
  674. echo 4 > _shar_seq_.tmp
  675. exit 0
  676. -- 
  677.  
  678.    -Richard L. Goerwitz              goer%sophist@uchicago.bitnet
  679.    goer@sophist.uchicago.edu         rutgers!oddjob!gide!sophist!goer
  680.