summaryrefslogtreecommitdiff
path: root/ipl/procs/gettext.icn
diff options
context:
space:
mode:
Diffstat (limited to 'ipl/procs/gettext.icn')
-rw-r--r--ipl/procs/gettext.icn265
1 files changed, 265 insertions, 0 deletions
diff --git a/ipl/procs/gettext.icn b/ipl/procs/gettext.icn
new file mode 100644
index 0000000..0ed0d3f
--- /dev/null
+++ b/ipl/procs/gettext.icn
@@ -0,0 +1,265 @@
+############################################################################
+#
+# File: gettext.icn
+#
+# Subject: Procedures for gettext (simple text-base routines)
+#
+# Author: Richard L. Goerwitz
+#
+# Date: May 2, 2001
+#
+############################################################################
+#
+# This file is in the public domain.
+#
+############################################################################
+#
+# History:
+# Version 1.19: December 28, 1993 (plt)
+# Tested with DOS, DOS-386, OS/2, ProIcon, UNIX
+# Modified link and OS statements.
+# Open index file in untranslated mode for
+# MS-DOS and OS/2 -- ignored by UNIX and Amiga
+# Handle multiple, indexed citations.
+# Change delimiter from <TAB> to char(255).
+# Simplified binary search.
+# Version 1.20: August 5, 1995 (plt)
+# Replace link statement with preprocessor include.
+# Retrieve text for multiple keys on the same line.
+# Correct debug printout of indexed and sequential
+# search values.
+#
+############################################################################
+#
+# Version: 1.19 December 28, 1993 - Phillip Lee Thomas
+# Version: 1.20 August 5, 1995 - plt
+#
+############################################################################
+#
+# Gettext() and associated routines allow the user to maintain a file
+# of KEY/value combinations such that a call to gettext(KEY, FNAME)
+# will produce value. Gettext() fails if no such KEY exists.
+# Returns an empty string if the key exists, but has no associated
+# value in the file, FNAME.
+#
+# The file format is simple. Keys belong on separate lines, marked
+# as such by an initial colon+colon (::). Values begin on the line
+# following their respective keys, and extend up to the next
+# colon+colon-initial line or EOF. E.g.
+#
+# ::sample.1
+# or:
+# ::sample.1 ::sample.2
+#
+# Notice how the key above, sample.1, has :: prepended to mark it
+# out as a key. The text you are now reading represents that key's
+# value. To retrieve this text, you would call gettext() with the
+# name of the key passed as its first argument, and the name of the
+# file in which this text is stored as its second argument (as in
+# gettext("sample.1","tmp.idx")).
+# ::next.key
+# etc...
+#
+# For faster access, an indexing utility is included, idxtext. Idxtext
+# creates a separate index for a given text-base file. If an index file
+# exists in the same directory as FNAME, gettext() will make use of it.
+# The index becomes worthwhile (at least on my system) after the text-
+# base file becomes longer than 5 kilobytes.
+#
+# Donts:
+# 1) Don't nest gettext text-base files.
+# 2) In searches, surround phrases with spaces or tabs in
+# key names with quotation marks: "an example"
+# 3) Don't modify indexed files in any way other than to append
+# additional keys/values (unless you want to re-index).
+#
+# This program is intended for situations where keys tend to have
+# very large values, and use of an Icon table structure would be
+# unwieldy.
+#
+# BUGS: Gettext() relies on the Icon runtime system and the OS to
+# make sure the last text/index file it opens gets closed.
+#
+############################################################################
+#
+# Links: adjuncts
+#
+############################################################################
+#
+# Invoke set_OS() before first call to gettext() or
+# sequential_search()
+#
+# Tested with UNIX, OS/2, DOS, DOS-386, ProIcon
+#
+############################################################################
+
+link adjuncts
+
+global _slash, _baselen, _delimiter
+
+procedure gettext(KEY,FNAME) #: search database by indexed term
+
+ local line, value
+ static last_FNAME, intext, inidx, off_set, off_sets
+
+ (/KEY | /FNAME) & stop("error (gettext): null argument")
+
+ if FNAME == \last_FNAME then {
+ seek(intext, 1)
+ seek(\inidx, 1)
+ }
+ else {
+ # We've got a new text-base file. Close the old one.
+ every close(\intext | \inidx)
+ # Try to open named text-base file.
+ intext := open(FNAME) | stop("gettext: file \"",FNAME,"\" not found")
+ # Try to open index file.
+ inidx := open(Pathname(FNAME) || getidxname(FNAME),"ru") | &null
+ }
+ last_FNAME := FNAME
+
+ # Find offsets, if any, for key KEY in index file.
+ # Then seek to the end and do a sequential search
+ # for any key/value entries that have been added
+ # since the last time idxtext was run.
+
+ if off_sets := get_offsets(KEY, inidx) then {
+ off_sets ? {
+ while off_set := (move(1),tab(many(&digits))) do {
+ seek(intext, off_set)
+
+ # Find key. Should be right there, unless the user has appended
+ # key/value pairs to the end without re-indexing, or else has not
+ # bothered to index in the first place. In this case we're
+ # supposed to start a sequential search for KEY upto EOF.
+
+ while line := (read(intext) | fail) do {
+ line ? {
+ if (="::",KEY)
+ then break
+ }
+ }
+
+ # Collect all text upto the next colon+colon line (::)
+ # or EOF.
+ value := ""
+ while line := read(intext) do {
+ find("::",line) & break
+ value ||:= line || "\n"
+ }
+
+ # Note that a key with an empty value returns an empty string.
+ suspend trim(value, '\n') || " (" || off_set || "-i)"
+ }
+ }
+ }
+
+ # Find additional values appended to file since last indexing.
+
+ seek(intext, \firstline - _OS_offset)
+ while value := sequential_search(KEY, intext) do
+ suspend trim(value,'\n') #|| " (" || off_set || "-s)"
+
+end
+
+procedure get_offsets(KEY, inidx) #: binary search of index
+ local incr, bottom, top, loc, firstpart, offset, line
+
+ # Use these to store values likely to be reused.
+ static old_inidx, SOF, EOF
+
+ # If there's no index file, then fail.
+ if /inidx then
+ fail
+
+ # First line contains offset of last indexed byte in the main
+ # text file. We need this later. Save it. Start the binary
+ # search routine at the next byte after this line.
+
+ seek(inidx, 1)
+ if not (inidx === \old_inidx) then {
+
+ # Get first line.
+ firstline := !inidx
+
+ # Set "bottom."
+ SOF := 1
+
+ # How big is this file?
+ seek(inidx, 0)
+ EOF := where(inidx)
+
+ old_inidx := inidx
+ }
+
+ # SOF, EOF constant for a given inidx file.
+ bottom := SOF ; top := EOF
+
+
+ # If bottom gets bigger than top, there's no such key.
+ until bottom >= top do {
+
+ loc := (top+bottom) / 2
+ seek(inidx, loc)
+
+ # Move past next newline. If at EOF, break.
+
+ read(inidx)
+ if (where(inidx) > EOF) | (loc = bottom) | (loc = top) then {
+ break
+ }
+
+ # Check to see if the current line contains KEY.
+ if line := read(inidx) then {
+ line ? {
+
+ # .IDX file line format is KEY<delimiter>offset
+ firstpart := tab(upto(_delimiter))
+
+ if KEY == firstpart then {
+ # return offset and addresses for any added material
+ return tab(1 - _OS_offset)
+ }
+
+ # Ah, this is what all binary searches do.
+ else {
+ if KEY >> firstpart
+ then bottom := loc
+ else top := loc
+ }
+ }
+ }
+ else top := loc # Too far, move back
+ }
+end
+
+# Perform sequential search of intext for all instances of KEY.
+
+procedure sequential_search(KEY, intext) #: brute-force database search
+
+ local line, value, off_set
+
+ # Collect all text upto the next colon+colon line (::)
+ # or EOF.
+
+ off_set := where(intext)
+ while (line := read(intext)) | fail do {
+ line ? {
+ if =("::" || KEY) & (match(" " | "\t") | pos(0))
+ then break
+ else off_set := where(intext)
+ }
+ }
+ value := ""
+ while line := read(intext) do {
+ find("::", line) & break
+ value ||:= line || "\n"
+ }
+
+ # Debug information for sequential searching:
+ value := value[1:-1] || " (" || off_set || "-s)\n"
+
+ # Back up to allow for consecutive instances of KEY.
+ seek(intext, where(intext) - *line - 2)
+ suspend trim(value || "\n")
+end