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