home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume23 / quranref / part04 < prev    next >
Encoding:
Text File  |  1991-10-19  |  32.2 KB  |  1,059 lines

  1. Newsgroups: comp.sources.misc
  2. From: goer@midway.uchicago.edu (Richard L. Goerwitz)
  3. Subject:  v23i070:  quranref - Holy Qur'an word and passage based retrievals, Part04/08
  4. Message-ID: <1991Oct19.022300.12923@sparky.imd.sterling.com>
  5. X-Md4-Signature: f2be581ed2d06ec701e8e7c057e32ebc
  6. Date: Sat, 19 Oct 1991 02:23:00 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: goer@midway.uchicago.edu (Richard L. Goerwitz)
  10. Posting-number: Volume 23, Issue 70
  11. Archive-name: quranref/part04
  12. Environment: Icon
  13.  
  14. ---- Cut Here and feed the following to sh ----
  15. #!/bin/sh
  16. # this is quranref.04 (part 4 of a multipart archive)
  17. # do not concatenate these parts, unpack them in order with /bin/sh
  18. # file huffcode.icn continued
  19. #
  20. if test ! -r _shar_seq_.tmp; then
  21.     echo 'Please unpack part 1 first!'
  22.     exit 1
  23. fi
  24. (read Scheck
  25.  if test "$Scheck" != 4; then
  26.     echo Please unpack part "$Scheck" next!
  27.     exit 1
  28.  else
  29.     exit 0
  30.  fi
  31. ) < _shar_seq_.tmp || exit 1
  32. if test ! -f _shar_wnt_.tmp; then
  33.     echo 'x - still skipping huffcode.icn'
  34. else
  35. echo 'x - continuing file huffcode.icn'
  36. sed 's/^X//' << 'SHAR_EOF' >> 'huffcode.icn' &&
  37. X    # 1.  That one element remaining (h[1]) is your Huffman tree.
  38. X    #
  39. X    # Based loosely on Sedgewick (2nd ed., 1988), p. 328-9.
  40. X    #
  41. X    local frst, scnd, count
  42. X
  43. X    until *h = 1 do {
  44. X
  45. X    h[1] :=: h[*h]        # Reverse first and last elements.
  46. X    frst := pull(h)        # Pop last elem off & save it.
  47. X    resettle_heap(h, 1)    # Resettle the heap.
  48. X    scnd := !h        # Save (but don't clobber) top element.
  49. X
  50. X    count := frst.n + scnd.n
  51. X    frst := { if *frst = 2 then frst.c else _N(frst.l, frst.r) }
  52. X    scnd := { if *scnd = 2 then scnd.c else _N(scnd.l, scnd.r) }
  53. X
  54. X    h[1] := node(frst, scnd, count) # Create new node().
  55. X    resettle_heap(h, 1)    # Resettle once again.
  56. X    }
  57. X
  58. X    # H is no longer a stack.  It's single element - the root of a
  59. X    # Huffman tree made up of node()s and leaf()s.  Put the l and r
  60. X    # fields of that element into an _N record, and return the new
  61. X    # record.
  62. X    return _N(h[1].l, h[1].r)
  63. X
  64. Xend
  65. X
  66. X
  67. Xprocedure hash_huffcodes(tr)
  68. X
  69. X    #
  70. X    # Hash Huffman codes.  Tr (arg 1) is a Huffman tree created by
  71. X    # heap_2_huffman_tree(heap).  Output is a table, with the keys
  72. X    # representing characters, and the values being records of type
  73. X    # hcode(i,len), where i is the Huffcode (an integer) and len is
  74. X    # the number of bits it occupies.
  75. X    #
  76. X    local code, huffman_table
  77. X
  78. X    huffman_table := table()
  79. X    every code := build_codes(tr) do
  80. X    insert(huffman_table, code.c, code)
  81. X    return huffman_table
  82. X
  83. Xend
  84. X    
  85. X
  86. Xprocedure build_codes(tr, i, len)
  87. X
  88. X    #
  89. X    # Decompose Huffman tree tr into hcode() records which contain
  90. X    # 3 fields:  c (the character encoded), i (its integer code),
  91. X    # and len (the number of bytes the integer code occupies).  Sus-
  92. X    # pend one such record for each character encoded in tree tr.
  93. X    #
  94. X
  95. X    if type(tr) == "string" then
  96. X    return hcode(tr, i, len)
  97. X    else {
  98. X    (/len := 1) | (len +:= 1)
  99. X    (/i   := 0) | (i   *:= 2)
  100. X    suspend build_codes(tr.l, i, len)
  101. X    i   +:= 1
  102. X    suspend build_codes(tr.r, i, len)
  103. X    }
  104. X
  105. Xend
  106. X
  107. X
  108. Xprocedure block_encode(s, huffman_table)
  109. X
  110. X    #
  111. X    # Write to file f string s encoded using huffman_table (a table having
  112. X    # chars as keys and huffman codes as values).
  113. X    #
  114. X    # Create huffman_table as follows (char_tbl is a table, with chars as
  115. X    # keys and frequencies as values):
  116. X    #
  117. X    # heap  := heap_init(char_tbl)
  118. X    # hufftree := heap_2_huffman_tree(heap)
  119. X    # huffman_table  := hash_huffcodes(hufftree)
  120. X    #
  121. X    # Store the tree, hufftree.  Pass the huffman table to block_encode as
  122. X    # its second argument.
  123. X
  124. X    local s2, size, hcode_4_chr, chr
  125. X
  126. X    *s > 2r1111111111111111 &
  127. X    stop("write_string:  too many characters in s")
  128. X
  129. X    s2 := ""            # initialize size string
  130. X    outbits()            # just in case
  131. X    every s2 ||:= outbits(*s, 16) # block size = 2 bytes
  132. X
  133. X    s ? {
  134. X    while chr := move(1) do {
  135. X        hcode_4_chr := \huffman_table[chr] |
  136. X        stop("write_string:  unexpected char, ",image(chr))
  137. X        every s2 ||:= outbits(hcode_4_chr.i, hcode_4_chr.len)
  138. X    }
  139. X    s2 ||:= outbits()
  140. X    }
  141. X
  142. X    return s2
  143. X
  144. Xend
  145. X
  146. X
  147. Xprocedure block_decode(f, huff_tree)
  148. X
  149. X    # Undo what block_encode does.
  150. X    
  151. X    local how_many, s2, E, chr, bit
  152. X
  153. X    s2 := ""
  154. X
  155. X    # The first two bytes record how many characters the original
  156. X    # text had in it.  If the read fails, it means that the file
  157. X    # system filled up while making the index, and the bitmaps now
  158. X    # can't be located in f.
  159. X    how_many := ishift(ord(reads(f)), 8) + ord(reads(f)) |
  160. X    stop("block_decode:  failure reading ",image(f))
  161. X    # If the original text was blank (zero characters), then return
  162. X    # an empty string.
  163. X    if how_many = 0 then { return "" }
  164. X
  165. X    reads(f, how_many) ? {
  166. X
  167. X    # Otherwise, set E = to the top node of the Huffman tree, and
  168. X    # begin decoding.
  169. X    E := huff_tree
  170. X    while chr := move(1) do {
  171. X        every bit := iand(1, ishift(ord(chr), -7 to 0)) do {
  172. X        E := { if bit = 0 then E.l else E.r }
  173. X        if s2 ||:= string(E) then {
  174. X            if *s2 = how_many
  175. X            then return s2
  176. X            else E := huff_tree
  177. X        }
  178. X        }
  179. X    }
  180. X    }
  181. X
  182. X    # If we get to here, something is quite amiss!
  183. X    stop("read_string:  bad character count")
  184. X
  185. Xend
  186. SHAR_EOF
  187. echo 'File huffcode.icn is complete' &&
  188. true || echo 'restore of huffcode.icn failed'
  189. rm -f _shar_wnt_.tmp
  190. fi
  191. # ============= binsrch.icn ==============
  192. if test -f 'binsrch.icn' -a X"$1" != X"-c"; then
  193.     echo 'x - skipping binsrch.icn (File already exists)'
  194.     rm -f _shar_wnt_.tmp
  195. else
  196. > _shar_wnt_.tmp
  197. echo 'x - extracting binsrch.icn (Text)'
  198. sed 's/^X//' << 'SHAR_EOF' > 'binsrch.icn' &&
  199. X############################################################################
  200. X#
  201. X#    Name:     binsrch.icn
  202. X#
  203. X#    Title:     general-purpose binary index search
  204. X#
  205. X#    Author:     Richard L. Goerwitz
  206. X#
  207. X#    Version: 1.4
  208. X#
  209. X############################################################################
  210. X#
  211. X#  This file contains a single procedure, binary_index_search(str,
  212. X#  filename), which goes through a file called filename looking for a
  213. X#  line beginning with str.  Note well that binary_index_search()
  214. X#  assumes lines in filename will contain more than str.  Str must
  215. X#  occupy the first part of the line, separated from the remainder by
  216. X#  a tab.
  217. X#
  218. X############################################################################
  219. X#
  220. X#  Links: none
  221. X#
  222. X#  See also: retrieve.icn, makeind.icn
  223. X#
  224. X############################################################################
  225. X
  226. X
  227. Xprocedure binary_index_search(entry, index_filename)
  228. X
  229. X    local in_index, bottom, top, loc, incr, firstpart, offset
  230. X
  231. X    in_index := open(index_filename) |
  232. X    abort("binary_index_search","can't open "||index_filename,18)
  233. X
  234. X    bottom := 1
  235. X    seek(in_index, 0)
  236. X    top := where(in_index)
  237. X
  238. X    # If bottom gets bigger than top, there's no such entry.
  239. X    until bottom > top do {
  240. X
  241. X    loc := (top+bottom) / 2
  242. X    seek(in_index, loc)
  243. X
  244. X    # Move past next newline.  If at bottom, break.
  245. X    incr := 1
  246. X    until reads(in_index) == "\n" do
  247. X        incr +:= 1
  248. X    if loc+incr = bottom then {
  249. X        top := loc-1
  250. X        next
  251. X    }
  252. X
  253. X    # Check to see if the current line starts with entry (arg 1).
  254. X    read(in_index) ? {
  255. X
  256. X        # .IND file line format is entry\tbitmap-file-offset
  257. X        if entry == (firstpart := tab(find("\t"))) then {
  258. X        # return offset
  259. X        return (move(1), tab(0))
  260. X        }
  261. X        # Ah, this is what all binary searches do.
  262. X        else {
  263. X        if entry << firstpart
  264. X        then top := loc-1
  265. X        else bottom := loc + incr + *&subject
  266. X        }
  267. X    }
  268. X    }
  269. X
  270. Xend
  271. SHAR_EOF
  272. true || echo 'restore of binsrch.icn failed'
  273. rm -f _shar_wnt_.tmp
  274. fi
  275. # ============= bmp2text.icn ==============
  276. if test -f 'bmp2text.icn' -a X"$1" != X"-c"; then
  277.     echo 'x - skipping bmp2text.icn (File already exists)'
  278.     rm -f _shar_wnt_.tmp
  279. else
  280. > _shar_wnt_.tmp
  281. echo 'x - extracting bmp2text.icn (Text)'
  282. sed 's/^X//' << 'SHAR_EOF' > 'bmp2text.icn' &&
  283. X############################################################################
  284. X#
  285. X#    Name:     bmp2text.icn
  286. X#
  287. X#    Title:     convert a bitmap to a text-chunk
  288. X#
  289. X#    Author:     Richard L. Goerwitz
  290. X#
  291. X#    Version: 2.6
  292. X#
  293. X############################################################################
  294. X#
  295. X#  This file contains bitmap_2_text(bitmap, filename).  Recall that
  296. X#  bitmaps are just a series of fixed-length bitfields used to mark
  297. X#  divisions within a text.  The procedure retrieve() locates words in
  298. X#  an index file, and returns a list of these bitmaps, which point to
  299. X#  divisions within the original text file - divisions within which a
  300. X#  given indexed word found by retrieve() occurs.  The procedure
  301. X#  bitmap_2_filename() simply takes a given bitmap and finds the text
  302. X#  with which it is associated in the main text file.
  303. X#
  304. X#  Note that bitmap_2_text() does not seek directly to the correct
  305. X#  location within "filename" (arg 2).  It first breaks down the
  306. X#  bitmap into a less precise form via an offset table (read in via
  307. X#  the .OFS file), looks up the precise location of the bitmap in the
  308. X#  .UNT file, and then finally seeks up to that location in the main
  309. X#  text file, decodes the text it finds at that location, and then
  310. X#  returns the decoded section as a string.  The reason
  311. X#  bitmap_2_text() does this is that makeind (the indexing routine
  312. X#  which creates data files for retrieve() and bitmap_2_text()) does
  313. X#  not store the offset within the main text for every bitmap.  It
  314. X#  just saves the locations of major blocks in the .OFS file, then
  315. X#  keeps a full list on disk in the .UNT file.  This is basically just
  316. X#  a space-saving device.  It would eat up too much core memory to
  317. X#  keep a list of every offset for every chunk of text marked out by a
  318. X#  bitmap in filename.
  319. X#
  320. X#  Note also that, although retrieve() returns a list of bitmaps, bit-
  321. X#  map_2_text(bitmap, filename) expects a single bitmap as its first
  322. X#  argument.  It is better that text be retrieved as needed, one chunk
  323. X#  at a time, and not stuffed en masse into core memory as soon as it
  324. X#  is retrieve()'d.
  325. X#
  326. X############################################################################
  327. X#
  328. X#  Links: ./indexutl.icn, ./initfile.icn
  329. X#
  330. X#  See also: retrieve.icn, makeind.icn
  331. X#
  332. X############################################################################
  333. X
  334. X# Declared in indexutl.icn.
  335. X# record is(FS, s_len, len, no, is_case_sensitive, hufftree)
  336. X# global IS
  337. X
  338. X# Declared in initfile.icn.
  339. X# global filestats
  340. X# record Fs(ind_filename, bmp_filename, lim_filename, unt_filename,
  341. X#           IS, ofs_table)
  342. X
  343. Xprocedure bitmap_2_text(bitmap, filename)
  344. X
  345. X    local cut_down_bitmap, upto_field, offset, line, base_value_mask,
  346. X    base_value, location
  347. X    static t, old_main_filename, in_main_file, in_unt_file
  348. X    # global filestats, IS
  349. X    initial {
  350. X    t := table()
  351. X    old_main_filename := ""
  352. X    }
  353. X
  354. X    # Check for sloppy programming.
  355. X    /filename & abort("bitmap_2_text","you called me without a filename",29)
  356. X
  357. X    if old_main_filename ~==:= filename then {
  358. X    #
  359. X        # If necessary, initialize stats for the current file.
  360. X        #
  361. X    if /filestats | /filestats[filename]
  362. X    then initfile(filename)           # see initfile.icn
  363. X        # open full text file for reading
  364. X    every close(\in_main_file | \in_unt_file)
  365. X    in_main_file := open(filename) |
  366. X        abort("bitmap_2_text", "can't open "||filename, 26)
  367. X    in_unt_file := open(filestats[filename].unt_filename) |
  368. X        abort("bitmap_2_text", "can't open .UNT file for "||filename, 27)
  369. X    }
  370. X
  371. X    # Reset IS to current file.
  372. X    IS := filestats[filename].IS
  373. X
  374. X    # Determine offset to seek to by using the bitmap->offset table
  375. X    # for the current file (arg 2).  The name of the bitmap_offset
  376. X    # table is stored in filestats[filename].ofs_table.
  377. X    #
  378. X    upto_field := 1 < (filestats[filename].IS.no * 2) / 3 | 1
  379. X    cut_down_bitmap := ishift(bitmap, -(IS.no - upto_field) * IS.len)
  380. X    offset := \filestats[filename].ofs_table[cut_down_bitmap] | fail
  381. X
  382. X    # Seek to offset, and begin looking for the string equiv. of
  383. X    # bitmap (arg 1).
  384. X    #
  385. X    seek(in_unt_file, offset) |
  386. X    abort("bitmap_2_text","can't seek to offset "||offset, 27)
  387. X    
  388. X    #
  389. X    # Find the major text division for bitmap using the offset table
  390. X    # (in filestats[filename].ofs_table), look up its precise loca-
  391. X    # tion in the .UNT file, then seek to that location in the main
  392. X    # text file and decode whatever text is encoded at that location.
  393. X    #
  394. X
  395. X    #
  396. X    # First figure out how to tell if we've gone too far.  Basically,
  397. X    # mask out the lower bits, and record the value of the upper bits.
  398. X    # Some fooling around is necessary because bitmaps may use large
  399. X    # ints, making it impossible to use icom() in a naive manner.
  400. X    # If the upper bits of the bitmaps being read change, then we've
  401. X    # gone too far.
  402. X    #
  403. X    base_value_mask := icom(2^((IS.no - upto_field) * IS.len)- 1)
  404. X    base_value := iand(bitmap, base_value_mask)
  405. X
  406. X    while line := read(in_unt_file) do {
  407. X    line ? {
  408. X        location := digits_2_bitmap(tab(find("\t"))) # in indexutl.icn
  409. X        if bitmap = location then {
  410. X        move(1)        # move past tab character
  411. X        # block_decode() is in huffcode.icn; decodes the encoded
  412. X        # verse and returns the result (should be an ASCII string)
  413. X        seek(in_main_file, offset := integer(tab(0))) |
  414. X            abort("bitmap_2_text","unable to seek to "||offset,28)
  415. X        return block_decode(in_main_file, IS.hufftree)
  416. X        }
  417. X        else {
  418. X        if base_value ~= iand(location, base_value_mask)
  419. X        then fail
  420. X        }
  421. X    }
  422. X    }
  423. X
  424. X    # we should have returned by now
  425. X    fail
  426. X
  427. Xend
  428. SHAR_EOF
  429. true || echo 'restore of bmp2text.icn failed'
  430. rm -f _shar_wnt_.tmp
  431. fi
  432. # ============= initfile.icn ==============
  433. if test -f 'initfile.icn' -a X"$1" != X"-c"; then
  434.     echo 'x - skipping initfile.icn (File already exists)'
  435.     rm -f _shar_wnt_.tmp
  436. else
  437. > _shar_wnt_.tmp
  438. echo 'x - extracting initfile.icn (Text)'
  439. sed 's/^X//' << 'SHAR_EOF' > 'initfile.icn' &&
  440. X############################################################################
  441. X#
  442. X#    Name:     initfile.icn
  443. X#
  444. X#    Title:     initialize entry for file in filestats table
  445. X#
  446. X#    Author:     Richard L. Goerwitz
  447. X#
  448. X#    Version: 2.2
  449. X#
  450. X############################################################################
  451. X#
  452. X#  This file contains initfile(filename), which creates a set of stats
  453. X#  for the indexed database contained in filename.  Uses several global
  454. X#  structures, primarily for speed.  Beware.
  455. X#
  456. X############################################################################
  457. X#
  458. X#  See also: retrieve.icn, bmp2text.icn, retrops.icn
  459. X#
  460. X############################################################################
  461. X
  462. X# Used to store stats for each filename.
  463. Xrecord Fs(ind_filename, bmp_filename, lim_filename, unt_filename,
  464. X      IS, ofs_table)
  465. X
  466. X# IS is declared in indexutl.icn.
  467. X# global IS
  468. X
  469. Xglobal filestats
  470. X
  471. Xprocedure initfile(filename)
  472. X
  473. X    # Messy procedure which creates and stores the names of several
  474. X    # files that will be repeatedly used with "filename."  Reads in
  475. X    # the stats for filename from that file's .IS file.  Also reads in
  476. X    # the bitmap->offset (.OFS file) table, and puts it into
  477. X    # filestats[filename].ofs_table for later (re-)use.  The bitmap->
  478. X    # offset table contains pointers into the .UNT file for filename,
  479. X    # which lists all the main text divisions, with pointers into the
  480. X    # main text file (i.e. filename) for each division.  The scheme
  481. X    # is: .OFS file (locates larger divisions) -> .UNT file (contains
  482. X    # the offsets for smaller divisions in filename) -> filename (the
  483. X    # actual compressed text).
  484. X
  485. X    local IS_filename, in_IS, upto_field, stored_bitmap_length,
  486. X    ofs_filename, intext, cut_down_bitmap, block_size, offset
  487. X    # global filestats
  488. X    initial {
  489. X    filestats := table()
  490. X    # OS-specific parameters are initialized here.
  491. X    initialize_os_params()    # in indexutl.icn
  492. X    }
  493. X
  494. X    # Check for sloppy programming.  Did we do this one already??
  495. X    if not (/filestats[filename] := Fs(,,,,,table())) then fail
  496. X
  497. X    filestats[filename].ind_filename :=
  498. X    dir_name(filename)||create_fname(filename, "IND")
  499. X    filestats[filename].bmp_filename :=
  500. X    dir_name(filename)||create_fname(filename, "BMP")
  501. X    filestats[filename].lim_filename :=
  502. X    dir_name(filename)||create_fname(filename, "LIM")
  503. X    filestats[filename].unt_filename :=
  504. X    dir_name(filename)||create_fname(filename, "UNT")
  505. X
  506. X    # Decode stored IS record for filename.
  507. X    IS_filename := dir_name(filename)||create_fname(filename, "IS")
  508. X    in_IS := open(IS_filename) | abort("bitmap_2_text",
  509. X    "Can't open "||IS_filename||".  Did you forget to index?", 24)
  510. X    filestats[filename].IS := decode(!in_IS)
  511. X    close(in_IS)
  512. X    
  513. X    # Having decoded IS, we can now determine the length of the cut-
  514. X    # down bitmaps stored in the .OFS file for filename.
  515. X    upto_field := 1 < (filestats[filename].IS.no * 2) / 3 | 1
  516. X    stored_bitmap_length :=
  517. X    ((filestats[filename].IS.len * upto_field) <= seq(0,8))
  518. X
  519. X    # open .OFS file
  520. X    ofs_filename := dir_name(filename)||create_fname(filename, "OFS")
  521. X    intext := open(ofs_filename) |
  522. X    abort("bitmap_2_text", "can't open "||ofs_filename, 23)
  523. X    
  524. X    # read in blocks from .OFS file, breaking them into their
  525. X    # constituent parts
  526. X    while block_size := read_int(intext, 8) * 8 do {
  527. X    cut_down_bitmap := read_int(intext, stored_bitmap_length)
  528. X    offset := read_int(intext, block_size - stored_bitmap_length)
  529. X    insert(filestats[filename].ofs_table, cut_down_bitmap, offset)
  530. X    }
  531. X    close(intext)
  532. X
  533. X    return *filestats[filename].ofs_table
  534. X
  535. Xend
  536. SHAR_EOF
  537. true || echo 'restore of initfile.icn failed'
  538. rm -f _shar_wnt_.tmp
  539. fi
  540. # ============= retrieve.icn ==============
  541. if test -f 'retrieve.icn' -a X"$1" != X"-c"; then
  542.     echo 'x - skipping retrieve.icn (File already exists)'
  543.     rm -f _shar_wnt_.tmp
  544. else
  545. > _shar_wnt_.tmp
  546. echo 'x - extracting retrieve.icn (Text)'
  547. sed 's/^X//' << 'SHAR_EOF' > 'retrieve.icn' &&
  548. X############################################################################
  549. X#
  550. X#    Name:     retrieve.icn
  551. X#
  552. X#    Title:     retrieve locations of words in database file
  553. X#
  554. X#    Author:     Richard L. Goerwitz
  555. X#
  556. X#    Version: 1.25
  557. X#
  558. X############################################################################
  559. X#
  560. X#  Retrieve(pattern, filename) retrieves all locations containing
  561. X#  words matching pattern (arg1) in filename (arg2), placing them in a
  562. X#  list.  "Locations" are integer-coded pointers to places in filename
  563. X#  where corresponding text is located.  To actually retrieve that
  564. X#  block of text, you must call bitmap_2_text(location, filename).
  565. X#  Retrieve() only gathers up a list of locations in filename
  566. X#  containing words which match pattern.
  567. X#
  568. X#  The reason retrieve() doesn't do the logical thing - namely, to
  569. X#  "retrieve" text itself - is that doing so might use a *lot* of
  570. X#  memory.  It is far more economical to retrieve text only when a
  571. X#  given chunk is requested via bitmap_2_text().
  572. X#
  573. X#  Note:  Patterns must match words in their entirety.  For instance,
  574. X#  retrieve("dog",filename) would only retrieve exact matches for the
  575. X#  word "dog" in filename.  To catch, say, "doggie" as well, it would
  576. X#  be necessary to call retrieve with a regular expression that
  577. X#  matched both dog and doggie (e.g. retrieve("dog.*",filename)).
  578. X#
  579. X############################################################################
  580. X#
  581. X#  Links: codeobj.icn, ./indexutl.icn, ./binsrch.icn, ./initfile.icn
  582. X#         ./findre.icn shquote.icn
  583. X#
  584. X#  See also: makeind.icn, bmp2text.icn
  585. X#
  586. X############################################################################
  587. X
  588. Xlink codeobj, shquote
  589. X
  590. X# The following globals contain stats for current file (here, arg2).
  591. X# global filestats    # declared in initfile.icn
  592. X# global IS           # declared in indexutl.icn
  593. X
  594. Xprocedure retrieve(pattern, filename, inverse)
  595. X
  596. X    local bitmap_set, bmp_file, in_egrep, intext, cmd,
  597. X    offset, pattern2, line
  598. X    static is_UNIX, egrep_filename
  599. X    initial {
  600. X    if is_UNIX := find("UNIX",&features) then
  601. X        # If egrep is available, use it.  It's fast.
  602. X        egrep_filename := "egrep"
  603. X        # egrep_filename := "/usr/local/bin/gnuegrep"
  604. X    }
  605. X
  606. X    # Check for sloppy programming.
  607. X    /filename & abort("retrieve","you called me without a filename",22)
  608. X
  609. X    # Initialize important variables.
  610. X    #
  611. X    if /filestats | /filestats[filename]
  612. X    then initfile(filename)           # see initfile.icn
  613. X    bitmap_set := set()              # list will contain locations of hits
  614. X    IS := filestats[filename].IS      # re-initialize IS for current file
  615. X    if /IS.is_case_sensitive then
  616. X    pattern := map(pattern)
  617. X
  618. X    # Open bitmap file.
  619. X    #
  620. X    bmp_file := open(filestats[filename].bmp_filename) |
  621. X    abort("retrieve","can't open "||filestats[filename].bmp_filename, 29)
  622. X
  623. X    # Search index.
  624. X    #
  625. X    if are_metas(pattern) then {
  626. X    # NB: are_metas() can be found in indexutl.icn
  627. X
  628. X    # If there are metacharacters in pattern, do a regexp pattern match.
  629. X    # The .IND file goes:  line ::= key \t other-stuff.
  630. X    pattern := "^(" || pattern || ")\t"
  631. X
  632. X    # If UNIX, then use egrep to search index.
  633. X    #
  634. X    if \is_UNIX then {
  635. X
  636. X        # Set up command line to be passed to /bin/sh.  First make
  637. X        # sure we don't have any apostrophe's hanging around to
  638. X        # screw up the command line to be passed to /bin/sh, then
  639. X        # put together a command line to be passed to egrep.
  640. X        pattern2 := shquote(pattern)                # from the IPL
  641. X
  642. X        cmd := egrep_filename || " " || pattern2 ||
  643. X        " " || filestats[filename].ind_filename ||
  644. X        " 2>&1"
  645. X        # open pipe
  646. X        in_egrep := open(cmd, "rp") |
  647. X        abort("retrieve","can't open pipe from\n\t"||cmd, 20)
  648. X        # grep .IND index file
  649. X        every line := !in_egrep do {
  650. X        # Kludge, but it's the only way to tell if there's an error.
  651. X        find("error"|"grep", line) & {
  652. X            # Define some routine here that issues a warning; there
  653. X            # is no need to actually abort!
  654. X            (\err_message)("Regular expression syntax error.") |
  655. X            stop("retrieve:  regexp syntax error")
  656. X            break
  657. X        }
  658. X        line ? (tab(find("\t")+1), offset := integer(tab(0)))
  659. X        bitmap_set ++:=
  660. X            retrieve_bitmaps(offset, bmp_file)
  661. X        }
  662. X        close(in_egrep)
  663. X
  664. X    # ...otherwise (i.e. if not UNIX) use findre() instead of egrep
  665. X    #
  666. X    } else {
  667. X
  668. X        # Probably MS-DOS or something else.  SLOW, SLOW!
  669. X        intext := open(filestats[filename].ind_filename) |
  670. X        abort("retrieve","can't open index file", 21)
  671. X        # grep .IND file
  672. X        every line := !intext do {
  673. X        line ? {
  674. X            if findre(pattern) then {
  675. X            offset := integer(tab(0))
  676. X            bitmap_set ++:=
  677. X                retrieve_bitmaps(offset, bmp_file)
  678. X            }
  679. X        }
  680. X        }
  681. X        every close(bmp_file | intext)
  682. X    }
  683. X
  684. X    # If *not* are_metas(pattern), then do a binary search of the index.
  685. X    # No need to worry about is_UNIX, egrep, findre(), etc.
  686. X    #
  687. X    } else {
  688. X    if offset :=
  689. X        binary_index_search(pattern, filestats[filename].ind_filename)
  690. X    then bitmap_set ++:=
  691. X        retrieve_bitmaps(offset, bmp_file)
  692. X    }
  693. X
  694. X    # If inverse (arg 3) is nonnull, then invert the sense of the search.
  695. X    # Do this by knocking out those parts of the full bitmap set that are
  696. X    # in the bitmap_set, and then assigning the result to bitmap_set.
  697. X    #
  698. X    if \inverse then
  699. X    bitmap_set := (all_bitmaps(bmp_file) -- bitmap_set)
  700. X
  701. X    # We're done.  See if there were any hits.  If so, sort & return a
  702. X    # list (lists are easier for the display routines to handle).
  703. X    #
  704. X    close(bmp_file)
  705. X    #
  706. X    if *bitmap_set > 0
  707. X    then return sort(bitmap_set)
  708. X    else fail
  709. X
  710. Xend
  711. X
  712. X
  713. X
  714. Xprocedure retrieve_bitmaps(offset, f, return_a_list)
  715. X
  716. X    local bitmap_list, bitmap_length, i, tmp, how_many_bitmaps,
  717. X    bits_needed, inverse_signal
  718. X
  719. X    bits_needed := 24
  720. X    inverse_signal := 8388608
  721. X    
  722. X    seek(f, offset)
  723. X    bitmap_length := ((IS.len * IS.no) <= seq(0,8))
  724. X    tmp := read_int(f, bits_needed)
  725. X    how_many_bitmaps := iand(inverse_signal-1, tmp)
  726. X
  727. X    # Slower way.
  728. X    # bitmap_list := list(how_many_bitmaps)
  729. X    # every i := 1 to how_many_bitmaps do
  730. X    #     bitmap_list[i] := read_int(f, bitmap_length)
  731. X
  732. X    # Slow way.
  733. X    bitmap_list := list()
  734. X    every i := 1 to how_many_bitmaps do
  735. X    put(bitmap_list, read_int(f, bitmap_length))
  736. X
  737. X    # If the inverse signal bit is turned on, then the BMP file stores
  738. X    # non-occurrences for a given key (rather than occurrences).  Saves
  739. X    # space for a/the/and, etc., but necessitates collecting all bitmaps
  740. X    # for the current file into a set a set difference.  The procedure
  741. X    # all_bitmaps does the collecting.
  742. X    if iand(inverse_signal, tmp) ~= 0 then {
  743. X    bitmap_list := (all_bitmaps(f) -- set(bitmap_list))
  744. X    }
  745. X
  746. X    if \return_a_list
  747. X    then return sort(bitmap_list)
  748. X    else {
  749. X    if type(bitmap_list) == "list"
  750. X    then return set(bitmap_list)
  751. X    else return bitmap_list
  752. X    }
  753. X
  754. Xend
  755. X
  756. X
  757. X
  758. Xprocedure all_bitmaps(f, return_a_list)
  759. X
  760. X    # At offset 1 in the BMP file is the list of all bitmaps in the
  761. X    # full file.  Returns the set of these, unless a list is desired,
  762. X    # in which case one must call all_bitmaps() with a nonnull second
  763. X    # argument.
  764. X    return retrieve_bitmaps(1, f, return_a_list)
  765. X
  766. Xend
  767. SHAR_EOF
  768. true || echo 'restore of retrieve.icn failed'
  769. rm -f _shar_wnt_.tmp
  770. fi
  771. # ============= indexutl.icn ==============
  772. if test -f 'indexutl.icn' -a X"$1" != X"-c"; then
  773.     echo 'x - skipping indexutl.icn (File already exists)'
  774.     rm -f _shar_wnt_.tmp
  775. else
  776. > _shar_wnt_.tmp
  777. echo 'x - extracting indexutl.icn (Text)'
  778. sed 's/^X//' << 'SHAR_EOF' > 'indexutl.icn' &&
  779. X############################################################################
  780. X#
  781. X#    Name:     indexutl.icn
  782. X#
  783. X#    Title:     indexing utilities
  784. X#
  785. X#    Author:     Richard L. Goerwitz
  786. X#
  787. X#    Version: 2.1
  788. X#
  789. X############################################################################
  790. X#
  791. X#  This file contains base_name(), dir_name(), get_index_fname(),
  792. X#  stripchars(), abort(), and gettokens().
  793. X#
  794. X#  base_name(s), dir_name(s)    - like the Unix system commands
  795. X#  create_fname(fname,ext)    - get a new filename based on fname + ext
  796. X#  stripchars(s,c)        - strip chars c from string s
  797. X#  abort(proc,msg,ecode)    - abort procedure proc with exit code ecode
  798. X#  write_int(f, int, size)    - breaks int into 8-bit chunks & writes to f
  799. X#  read_int(f, int, size)    - like write_int, only constructs int from f
  800. X#  are_metas(pattern)        - succeeds if pattern has egrep-style metas
  801. X#  digits_2_bitmap(s)        - converts string 01:13:94 to an int-bitmap
  802. X#
  803. X############################################################################
  804. X#
  805. X#  Links: ./findre.icn, radcon.icn, bincvt.icn
  806. X#
  807. X#  See also: retrieve.icn, retrops.icn, bmp2text.icn, makeind.icn
  808. X#
  809. X############################################################################
  810. X
  811. X#
  812. X# All from the IPL.
  813. X#
  814. Xlink radcon, bincvt
  815. X
  816. X#
  817. X# FS = field separator, s_len = string length of fields, len =
  818. X# byte length of fields, no = number of fields, is_case_sensitive =
  819. X# whether to map index entries to lowercase, r_field = rollover
  820. X# field for limits file.
  821. X#
  822. Xrecord is(FS, s_len, len, no, is_case_sensitive, r_field, hufftree)
  823. Xglobal _slash, _baselen, IS
  824. X
  825. X
  826. Xprocedure base_name(s)
  827. X
  828. X    # If s == "/usr/local/man/man1/icon.1", base_name will return
  829. X    # "icon.1".  Somewhat like the Unix basename system command.
  830. X
  831. X    # global _slash        # _slash = \ for MS-DOS, / for Unix
  832. X    s ? {
  833. X    while tab(find(_slash)+1)
  834. X    return tab(0)
  835. X    }
  836. X
  837. Xend
  838. X
  839. X
  840. X
  841. Xprocedure dir_name(s)
  842. X
  843. X    # If s == "/usr/local/man/man1/icon.1", dir_name will return
  844. X    # "/usr/local/man/man1".  Somewhat like the Unix dirname system
  845. X    # command.
  846. X
  847. X    local s2
  848. X    # global _slash        # _slash = \ for MS-DOS, / for Unix
  849. X
  850. X    s2 := ""
  851. X    s ? {
  852. X    while s2 ||:= tab(find(_slash)+1)
  853. X    return s2
  854. X    }
  855. X
  856. Xend
  857. X
  858. X
  859. X
  860. Xprocedure create_fname(FNAME, EXT)
  861. X
  862. X    #
  863. X    # Discard path component.  Cut basename down to a small enough
  864. X    # size that the OS will be able to handle addition of the ex-
  865. X    # tension, EXT.
  866. X    #
  867. X
  868. X    # global _slash, _baselen
  869. X
  870. X    *EXT > 3 &
  871. X    abort("get_index_fname","extension too long",7)
  872. X
  873. X    return right(
  874. X    stripchars(base_name(FNAME,_slash),'.'), _baselen, "x") ||
  875. X        "." || EXT
  876. X
  877. Xend
  878. X
  879. X
  880. X
  881. Xprocedure stripchars(s,c)
  882. X
  883. X    # Strip chars (c) from string (s).  Return stripped s.
  884. X
  885. X    local s2
  886. X
  887. X    s2 := ""
  888. X    s ? {
  889. X    while s2 ||:= tab(upto(c))
  890. X    do tab(many(c))
  891. X    s2 ||:= tab(0)
  892. X    }
  893. X    return s2
  894. X
  895. Xend
  896. X
  897. X
  898. X
  899. Xprocedure abort(proc_name, message, error_code)
  900. X
  901. X    if not (/proc_name := "") then
  902. X    proc_name := trim(proc_name, ': ') || ":  "
  903. X    /error_code := 1
  904. X    
  905. X    write(&errout, proc_name, \message) # fail if there's no error msg,
  906. X    exit(error_code)         # then abort
  907. X
  908. Xend
  909. X
  910. X
  911. X
  912. Xprocedure write_int(f, i, size)
  913. X
  914. X    # Write out an integer byte-by-byte.
  915. X    #
  916. X    # Important little routine.  I know it looks inelegant and slow.
  917. X    # Feel free to modify it for speed, and send me the results.
  918. X    # Don't knock out the old code, though.  You understood it when
  919. X    # you read it, right?  That's the idea :-).
  920. X
  921. X    local marker, how_many
  922. X
  923. X    marker := ""
  924. X    how_many := 0
  925. X    /size := (*exbase10(i,2) <= seq(0,8))
  926. X
  927. X    # output bytes most significant first; then least significant
  928. X    until (size -:= 8) <= -8 do {
  929. X    how_many +:= 1
  930. X    marker ||:= (f, char(iand(ishift(i, -size), 2r11111111)))
  931. X    }
  932. X
  933. X    writes(f, marker)
  934. X    return how_many        # number of characters written
  935. X
  936. Xend
  937. X
  938. X
  939. X
  940. Xprocedure read_int(f, size)
  941. X
  942. X    local i, _shift
  943. X
  944. X    # collect bytes, putting the first one read into the high
  945. X    # end of an integer, and on down to the last read (into the
  946. X    # low end)
  947. X    i := _shift := 0
  948. X    while (_shift +:= 8) <= size do
  949. X    i +:= ishift(ord(reads(f)), size - _shift) | fail
  950. X    return i
  951. X
  952. Xend
  953. X
  954. X
  955. X
  956. Xprocedure initialize_os_params()
  957. X
  958. X    local os
  959. X    # global _slash, _baselen
  960. X
  961. X    if find("MS-DOS", os := &features) then {
  962. X    _slash := "\\";    _baselen := 8
  963. X    }
  964. X    else if find("UNIX", os := &features) then {
  965. X    _slash := "/"; _baselen := 10
  966. X    }
  967. X    else abort("initialize_os_params","os parameters undefined", 6)
  968. X
  969. X    return os
  970. X
  971. Xend
  972. X
  973. X
  974. Xprocedure are_metas(str)
  975. X
  976. X    local chr, tmp
  977. X
  978. X    str ? {
  979. X
  980. X    # String-initial metacharacters are meaningless.
  981. X    tab(many('*+?|'))
  982. X
  983. X    # Look for metacharacters and backslashes.
  984. X    while tab(upto('\\*+()|?.$^[')) do {
  985. X
  986. X        # If a backslash comes first, then the next character can't
  987. X        # be a meta.  Move past it, and try again.
  988. X        if ="\\" then move(1) |
  989. X        abort("are_metas","malformed \-escape sequence",19)
  990. X        # Otherwise, we have a metacharacter.  Return its position
  991. X        # in str.  Dereference just so as not to have a global var.
  992. X        # on the loose.
  993. X        else return .&pos
  994. X    }
  995. X
  996. X    }
  997. X
  998. X    # If we've gotten this far without returning, then the string is
  999. X    # clean of metacharacters, and (in boolean terms) the procedure
  1000. X    # are_metas() returns false.
  1001. X    fail
  1002. X
  1003. Xend
  1004. X
  1005. X
  1006. X#
  1007. X# digits_2_bitmap
  1008. X#
  1009. X# Converts a string representation of a set of bit-fields into an
  1010. X# integer.  I.e. 1:1:3 becomes binary 010111 (decimal 23).  This
  1011. X# integer is like a map, and is called, in text-processing circles,
  1012. X# a bitmap (not to be confused with bit-mapped display techniques).
  1013. X# 
  1014. Xprocedure digits_2_bitmap(s)
  1015. X
  1016. X    # s        = location string (e.g. 10:02:03:75)
  1017. X    # IS.s_len = the string length of fields in s (3 in the above example)
  1018. X    # IS.len   = the number of bits needed to hold an integer
  1019. X    #             representation of a single field
  1020. X    # IS.no    = number of fields in s (4 in the above example)
  1021. X    #
  1022. X    # Fixed field lengths make things much simpler, but a whole
  1023. X    # helluva lot less economical.  Be sure that (IS.len * IS.no) does
  1024. X    # not exceed the register width for your CPU if either a) your
  1025. X    # implementation has no limits on the size of integers, or b) you
  1026. X    # are really concerned about performance.  Otherwise, never mind.
  1027. X
  1028. X    local bitmap, field, no
  1029. X
  1030. X    no       := IS.no
  1031. X    bitmap   := 0
  1032. X
  1033. X    s ? {
  1034. X    if upto(~&digits) then {
  1035. X        # The bitmap is delineated by field-markers (e.g. 11;23).
  1036. X        tab(upto(&digits))
  1037. X        while field := tab(many(&digits)) do {
  1038. X        no -:= 1
  1039. X        tab(upto(&digits))
  1040. X        bitmap +:= ishift(field, no * IS.len)
  1041. X        }
  1042. X    } else {
  1043. X        # Yuck!  An un-delineated bitmap (e.g. 23423).
  1044. X        while field := integer(move(IS.s_len)) do {
  1045. SHAR_EOF
  1046. true || echo 'restore of indexutl.icn failed'
  1047. fi
  1048. echo 'End of  part 4'
  1049. echo 'File indexutl.icn is continued in part 5'
  1050. echo 5 > _shar_seq_.tmp
  1051. exit 0
  1052.  
  1053. exit 0 # Just in case...
  1054. -- 
  1055. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1056. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1057. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1058. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1059.