home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3001 / gettext.icn < prev    next >
Text File  |  1991-03-06  |  6KB  |  206 lines

  1. ############################################################################
  2. #
  3. #    Name:     gettext.icn
  4. #
  5. #    Title:     gettext (simple text-base routines)
  6. #
  7. #    Author:     Richard L. Goerwitz
  8. #
  9. #    Version: 1.14
  10. #
  11. ############################################################################
  12. #
  13. #  Gettext() and associated routines allow the user to maintain a file
  14. #  of KEY/value combinations such that a call to gettext(KEY, FNAME)
  15. #  will produce value.  Gettext() fails if no such KEY exists.
  16. #  Returns an empty string if the key exists, but has no associated
  17. #  value in the file, FNAME.
  18. #
  19. #  The file format is simple.  Keys belong on separate lines, marked
  20. #  as such by an initial colon+colon (::).  Values begin on the line
  21. #  following their respective keys, and extend up to the next
  22. #  colon+colon-initial line or EOF.  E.g.
  23. #
  24. #    ::sample.1
  25. #    Notice how the key above, sample.1, has :: prepended to mark it
  26. #    out as a key.  The text you are now reading represents that key's
  27. #    value.  To retrieve this text, you would call gettext() with the
  28. #    name of the key passed as its first argument, and the name of the
  29. #    file in which this text is stored as its second argument (as in
  30. #    gettext("sample.1","tmp.idx")).
  31. #    ::next.key
  32. #    etc...
  33. #
  34. #  For faster access, an indexing utility is included, idxtext.  Idxtext
  35. #  creates a separate index for a given text-base file.  If an index file
  36. #  exists in the same directory as FNAME, gettext() will make use of it.
  37. #  The index becomes worthwhile (at least on my system) after the text-
  38. #  base file becomes longer than 5 kilobytes.
  39. #
  40. #  Donts:
  41. #      1) Don't nest gettext text-base files.
  42. #      2) Don't use spaces and/or tabs in key names.
  43. #      3) Don't modify indexed files in any way other than to append
  44. #         additional keys/values (unless you want to re-index).
  45. #
  46. #  This program is intended for situations where keys tend to have
  47. #  very large values, and use of an Icon table structure would be
  48. #  unweildy.
  49. #
  50. #  BUGS:  Gettext() relies on the Icon runtime system and the OS to
  51. #  make sure the last text/index file it opens gets closed.
  52. #
  53. #  Note:  This program is NOT YET TESTED UNDER DOS.  In particular,
  54. #  I have no idea whether the indexing mechanism will work, due to
  55. #  translation that has to be done on MS-DOS text files.
  56. #
  57. ############################################################################
  58. #
  59. #  Links: ./adjuncts.icn
  60. #
  61. #  Requires: UNIX (maybe MS-DOS; untested)
  62. #
  63. ############################################################################
  64.  
  65.  
  66. global _slash, _baselen
  67.  
  68. procedure gettext(KEY,FNAME)
  69.  
  70.     local line, value
  71.     static last_FNAME, intext, inidx
  72.     initial {
  73.     if find("UNIX", &features) then {
  74.         _slash := "/"
  75.         _baselen := 10
  76.     }
  77.     else if find("MS-DOS", &features) then {
  78.         _slash := "\\"
  79.         _baselen := 8
  80.     }
  81.     else stop("gettext:  OS not supported")
  82.     }
  83.  
  84.     (/KEY | /FNAME) & stop("error (gettext):  null argument")
  85.  
  86.     if FNAME ~== \last_FNAME then {
  87.     seek(intext, 1)
  88.     seek(\inidx, 1)
  89.     }
  90.     else {
  91.     # We've got a new text-base file.  Close the old one.
  92.     every close(\intext | \inidx)
  93.         # Try to open named text-base file.
  94.     intext := open(FNAME) | stop("gettext:  ",FNAME," not found")
  95.         # Try to open index file.
  96.     inidx := open(Pathname(FNAME) || getidxname(FNAME))
  97.     }
  98.     last_FNAME := FNAME
  99.  
  100.     # Find offsets for key KEY in index file.  If inidx (the index
  101.     # file) is null (which happens when none was found), get_offsets()
  102.     # defaults to 1.  Otherwise it returns the offset for KEY in the
  103.     # index file, and then returns the last indexed byte of the file.
  104.     # Returning the last indexed byte lets us seek to the end and do a
  105.     # sequential search of any key/value entries that have been added
  106.     # since the last time idxtext was run.
  107.  
  108.     seek(intext, get_offsets(KEY, inidx))
  109.  
  110.     # Find key.  Should be right there, unless the user has appended
  111.     # key/value pairs to the end without re-indexing, or else has not
  112.     # bothered to index in the first place.  In this case we're
  113.     # supposed to start a sequential search for KEY upto EOF.
  114.  
  115.     while line := (read(intext) | fail) do {
  116.     line ? {
  117.         if (="::", =KEY, pos(0))
  118.         then break
  119.     }
  120.     }
  121.  
  122.     # Collect all text upto the next colon+colon-initial line (::)
  123.     # or EOF.
  124.     value := ""
  125.     while line := read(intext) do {
  126.     match("::",line) & break
  127.     value ||:= line || "\n"
  128.     }
  129.  
  130.     # Note that a key with an empty value returns an empty string.
  131.     return trim(value, '\n')
  132.  
  133. end
  134.  
  135.  
  136.  
  137. procedure get_offsets(KEY, inidx)
  138.  
  139.     local bottom, top, loc, firstpart, offset
  140.     # Use these to store values likely to be reused.
  141.     static old_inidx, firstline, SOF, EOF
  142.  
  143.     # If there's no index file, then just return an offset of 1.
  144.     if /inidx then
  145.     return 1
  146.  
  147.     # First line contains offset of last indexed byte in the main
  148.     # text file.  We need this later.  Save it.  Start the binary
  149.     # search routine at the next byte after this line.
  150.     seek(inidx, 1)
  151.     if not (inidx === \old_inidx) then {
  152.  
  153.     # Get first line.
  154.     firstline := !inidx
  155.     # Set "bottom."
  156.     1 = (SOF := where(inidx)-1) &
  157.         stop("get_offsets:  corrupt .IDX file; reindex")
  158.     # How big is this file?
  159.     seek(inidx, 0)
  160.     EOF := where(inidx)
  161.  
  162.     old_inidx := inidx
  163.     }
  164.     # SOF, EOF constant for a given inidx file.
  165.     bottom := SOF; top := EOF
  166.  
  167.     # If bottom gets bigger than top, there's no such key.
  168.     until bottom > top do {
  169.  
  170.     loc := (top+bottom) / 2
  171.     seek(inidx, loc)
  172.  
  173.     # Move past next newline.  If at EOF, break.
  174.     incr := 1
  175.     until reads(inidx) == "\n" do
  176.         incr +:= 1
  177.     if loc+incr = EOF then {
  178.         top := loc-1
  179.         next
  180.     }
  181.  
  182.     # Check to see if the current line contains KEY.
  183.     read(inidx) ? {
  184.  
  185.         # .IDX file line format is KEY\toffset
  186.         firstpart := tab(find("\t"))
  187.         if KEY == firstpart then {
  188.         # return offset
  189.         return (move(1), tab(0))
  190.         }
  191.         # Ah, this is what all binary searches do.
  192.         else {
  193.         if KEY << firstpart
  194.         then top := loc-1
  195.         else bottom := loc + incr + *&subject
  196.         }
  197.     }
  198.     }
  199.  
  200.     # First line of the index file contains offset of last indexed
  201.     # byte + 1.  Might be the only line in the file (if it had no
  202.     # keys when it was indexed).
  203.     return firstline
  204.  
  205. end
  206.