aboutsummaryrefslogtreecommitdiff
path: root/pkg/utilities/nttools/lib
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/utilities/nttools/lib')
-rw-r--r--pkg/utilities/nttools/lib/allcols.x29
-rw-r--r--pkg/utilities/nttools/lib/allrows.x29
-rw-r--r--pkg/utilities/nttools/lib/compare.com7
-rw-r--r--pkg/utilities/nttools/lib/compare.x258
-rw-r--r--pkg/utilities/nttools/lib/ftnexpr.x127
-rw-r--r--pkg/utilities/nttools/lib/gettabcol.x67
-rw-r--r--pkg/utilities/nttools/lib/inquotes.x121
-rw-r--r--pkg/utilities/nttools/lib/invert.x55
-rw-r--r--pkg/utilities/nttools/lib/mjd.x94
-rw-r--r--pkg/utilities/nttools/lib/mkpkg33
-rw-r--r--pkg/utilities/nttools/lib/movenulls.x35
-rw-r--r--pkg/utilities/nttools/lib/msort.x113
-rw-r--r--pkg/utilities/nttools/lib/newcolnam.x97
-rw-r--r--pkg/utilities/nttools/lib/reloperr.h3
-rw-r--r--pkg/utilities/nttools/lib/reorder.x60
-rw-r--r--pkg/utilities/nttools/lib/select.x99
-rw-r--r--pkg/utilities/nttools/lib/tabvar.x118
-rw-r--r--pkg/utilities/nttools/lib/tbfile.x85
-rw-r--r--pkg/utilities/nttools/lib/tbleval.x159
-rw-r--r--pkg/utilities/nttools/lib/tbljoin.x168
-rw-r--r--pkg/utilities/nttools/lib/tblmerge.x162
-rw-r--r--pkg/utilities/nttools/lib/tblsearch.x104
-rw-r--r--pkg/utilities/nttools/lib/tblsort.x39
-rw-r--r--pkg/utilities/nttools/lib/tblsort1.x157
-rw-r--r--pkg/utilities/nttools/lib/tblsortm.x168
-rw-r--r--pkg/utilities/nttools/lib/tblterm.com7
-rw-r--r--pkg/utilities/nttools/lib/tblterm.x256
-rw-r--r--pkg/utilities/nttools/lib/tctexp.x442
-rw-r--r--pkg/utilities/nttools/lib/tldtype.x70
-rw-r--r--pkg/utilities/nttools/lib/tuopen.x197
-rw-r--r--pkg/utilities/nttools/lib/unique.x64
31 files changed, 3423 insertions, 0 deletions
diff --git a/pkg/utilities/nttools/lib/allcols.x b/pkg/utilities/nttools/lib/allcols.x
new file mode 100644
index 00000000..64e49786
--- /dev/null
+++ b/pkg/utilities/nttools/lib/allcols.x
@@ -0,0 +1,29 @@
+include <tbset.h>
+
+# ALLCOLS -- Return a pointer to an array containing the indices of all
+# the columns in a table. The calling procedure must free the array when it
+# is through with it.
+#
+# B.Simon 11-Dec-87 First Code
+
+procedure allcolumns (tp, numcol, colptr)
+
+pointer tp # i: Table descriptor
+int numcol # o: Number of columns in the table
+pointer colptr # o: Pointer to array of indices
+
+int icol
+
+int tbpsta(), tbcnum()
+
+errchk tbpsta, malloc
+
+begin
+
+ numcol = tbpsta (tp, TBL_NCOLS)
+ call malloc (colptr, numcol, TY_INT)
+
+ do icol = 1, numcol
+ Memi[colptr+icol-1] = tbcnum (tp, icol)
+
+end
diff --git a/pkg/utilities/nttools/lib/allrows.x b/pkg/utilities/nttools/lib/allrows.x
new file mode 100644
index 00000000..086e6a4a
--- /dev/null
+++ b/pkg/utilities/nttools/lib/allrows.x
@@ -0,0 +1,29 @@
+include <tbset.h>
+
+# ALLROWS -- Return a pointer to an array containing the indices of all
+# the rows in a table. The calling procedure must free the array when it
+# is through with it.
+#
+# B.Simon 11-Dec-87 First Code
+
+procedure allrows (tp, numrow, rowptr)
+
+pointer tp # i: Table descriptor
+int numrow # o: Number of rows in the table
+pointer rowptr # o: Pointer to array of indices
+
+int irow
+
+int tbpsta()
+
+errchk tbpsta, malloc
+
+begin
+
+ numrow = tbpsta (tp, TBL_NROWS)
+ call malloc (rowptr, numrow, TY_INT)
+
+ do irow = 1, numrow
+ Memi[rowptr+irow-1] = irow
+
+end
diff --git a/pkg/utilities/nttools/lib/compare.com b/pkg/utilities/nttools/lib/compare.com
new file mode 100644
index 00000000..546e7222
--- /dev/null
+++ b/pkg/utilities/nttools/lib/compare.com
@@ -0,0 +1,7 @@
+# Variables needed by comparison routines used by the sort routines
+
+int lendata # length of a data element in units of its type
+pointer dataptr # pointer to the beginning of array holding
+ # data to be sorted
+
+common /compare/ lendata, dataptr
diff --git a/pkg/utilities/nttools/lib/compare.x b/pkg/utilities/nttools/lib/compare.x
new file mode 100644
index 00000000..b05a50c1
--- /dev/null
+++ b/pkg/utilities/nttools/lib/compare.x
@@ -0,0 +1,258 @@
+.help compare
+.nf___________________________________________________________________________
+
+Comparison routines used to sort table columns. There are two sets of routines,
+compasc[bdirt] for sorting in ascending order and compdsc[bdirt] for sorting in
+descending order. The last letter indicates the type of data compared in the
+sort. All routines return an integer that indicates the results of comparison.
+The value of the integer is set according to the following scheme:
+
+ Ascending Descending
+
+ if mem[i] < mem[j], order = -1 if mem[i] > mem[j], order = -1
+ if mem[i] == mem[j], order = 0 if mem[i] == mem[j], order = 0
+ if mem[i] > mem[j], order = 1 if mem[i] < mem[j], order = 1
+
+.endhelp_______________________________________________________________________
+
+# B.Simon 16-Sept-87 First Code
+
+# COMPASCB -- Boolean comparison routine used for sort in ascending order
+
+int procedure compascb (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+begin
+ # false < true
+
+ if (! Memb[dataptr+i-1] && Memb[dataptr+j-1])
+ order = -1
+ else if (Memb[dataptr+i-1] && ! Memb[dataptr+j-1])
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
+
+# COMPASCD -- Double comparison routine used for sort in ascending order
+
+int procedure compascd (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+begin
+
+ if (Memd[dataptr+i-1] < Memd[dataptr+j-1])
+ order = -1
+ else if (Memd[dataptr+i-1] > Memd[dataptr+j-1])
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
+
+# COMPASCI -- Integer comparison routine used for sort in ascending order
+
+int procedure compasci (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+begin
+
+ if (Memi[dataptr+i-1] < Memi[dataptr+j-1])
+ order = -1
+ else if (Memi[dataptr+i-1] > Memi[dataptr+j-1])
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
+
+# COMPASCR -- Real comparison routine used for sort in ascending order
+
+int procedure compascr (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+begin
+
+ if (Memr[dataptr+i-1] < Memr[dataptr+j-1])
+ order = -1
+ else if (Memr[dataptr+i-1] > Memr[dataptr+j-1])
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
+
+# COMPASCT -- Text comparison routine used for sort in ascending order
+
+int procedure compasct (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+bool strlt(), strgt()
+
+begin
+
+ if (strlt (Memc[dataptr+(i-1)*(lendata+1)],
+ Memc[dataptr+(j-1)*(lendata+1)]) )
+ order = -1
+ else if (strgt (Memc[dataptr+(i-1)*(lendata+1)],
+ Memc[dataptr+(j-1)*(lendata+1)]) )
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
+
+# COMPDSCB -- Boolean comparison routine used for sort in descending order
+
+int procedure compdscb (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+begin
+ # true > false
+
+ if (Memb[dataptr+i-1] && ! Memb[dataptr+j-1])
+ order = -1
+ else if (! Memb[dataptr+i-1] && Memb[dataptr+j-1])
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
+
+# COMPDSCD -- Double comparison routine used for sort in descending order
+
+int procedure compdscd (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+begin
+
+ if (Memd[dataptr+i-1] > Memd[dataptr+j-1])
+ order = -1
+ else if (Memd[dataptr+i-1] < Memd[dataptr+j-1])
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
+
+# COMPDSCI -- Integer comparison routine used for sort in descending order
+
+int procedure compdsci (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+begin
+
+ if (Memi[dataptr+i-1] > Memi[dataptr+j-1])
+ order = -1
+ else if (Memi[dataptr+i-1] < Memi[dataptr+j-1])
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
+
+# COMPDSCR -- Real comparison routine used for sort in descending order
+
+int procedure compdscr (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+begin
+
+ if (Memr[dataptr+i-1] > Memr[dataptr+j-1])
+ order = -1
+ else if (Memr[dataptr+i-1] < Memr[dataptr+j-1])
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
+
+# COMPDSCT -- Text comparison routine used for sort in descending order
+
+int procedure compdsct (i, j)
+
+int i # i: Index to first array element in comparison
+int j # i: Index to second element in comparison
+#--
+include "compare.com"
+
+int order
+
+bool strgt(), strlt()
+
+begin
+
+ if (strgt (Memc[dataptr+(i-1)*(lendata+1)],
+ Memc[dataptr+(j-1)*(lendata+1)]) )
+ order = -1
+ else if (strlt (Memc[dataptr+(i-1)*(lendata+1)],
+ Memc[dataptr+(j-1)*(lendata+1)]) )
+ order = 1
+ else
+ order = 0
+
+ return (order)
+end
diff --git a/pkg/utilities/nttools/lib/ftnexpr.x b/pkg/utilities/nttools/lib/ftnexpr.x
new file mode 100644
index 00000000..a8472bdb
--- /dev/null
+++ b/pkg/utilities/nttools/lib/ftnexpr.x
@@ -0,0 +1,127 @@
+include <ctype.h>
+define DOT '.'
+define SQUOTE '\''
+define DQUOTE '"'
+define BSLASH '\\'
+
+#* HISTORY *
+#* B.Simon 04-Jan-93 Original
+#* B.Simon 01-Dec-93 No longer removes backslashes
+
+
+# FTNEXPR -- Convert a Fortran boolean expression to SPP
+
+procedure ftnexpr (oldexpr, newexpr, maxch)
+
+char oldexpr[ARB] # i: Fortran expression
+char newexpr[ARB] # o: SPP expression
+int maxch # i: Maximum length of SPP expression
+#--
+char ch, term
+int ic, jc, kc, iw
+pointer sp, dotbuf
+
+string ftnlist ".eq. .and. .or. .gt. .ge. .lt. .le. .not. .ne."
+string spplist " == && || > >= < <= ! !="
+
+int gstrcpy(), word_match(), word_find()
+
+begin
+ # Allocate dynamic memory for strings
+
+ call smark (sp)
+ call salloc (dotbuf, SZ_LINE, TY_CHAR)
+
+ # Loop over each character in the old expression
+ # Characters between quote marks or dots are treated specially
+ # To indicate this, term is set to the leading character
+
+ ic = 1
+ jc = 1
+ kc = 0
+ term = EOS
+
+ while (oldexpr[ic] != EOS) {
+ ch = oldexpr[ic]
+
+ if (ch != term) {
+ if (term == EOS) {
+ if (ch == DOT) {
+ kc = 1
+ term = ch
+ Memc[dotbuf] = ch
+ } else {
+ if (ch == SQUOTE || ch == DQUOTE)
+ term = ch
+
+ newexpr[jc] = ch
+ jc = jc + 1
+ }
+
+ } else if (term == DOT) {
+ if (IS_ALPHA(ch)) {
+ if (kc < SZ_LINE) {
+ Memc[dotbuf+kc] = ch
+ kc = kc + 1
+ }
+ } else {
+ Memc[dotbuf+kc] = ch
+ Memc[dotbuf+kc+1] = EOS
+ jc = jc + gstrcpy (Memc[dotbuf], newexpr[jc],
+ maxch-jc+1)
+
+ kc = 0
+ term = EOS
+ }
+
+ } else {
+ newexpr[jc] = ch
+ jc = jc + 1
+
+ if (ch == BSLASH) {
+ ic = ic + 1
+ newexpr[jc] = oldexpr[ic]
+ jc = jc + 1
+ }
+ }
+
+ } else {
+ term = EOS
+
+ if (ch != DOT) {
+ newexpr[jc] = ch
+ jc = jc + 1
+
+ } else {
+ Memc[dotbuf+kc] = ch
+ Memc[dotbuf+kc+1] = EOS
+ call strlwr (Memc[dotbuf])
+
+ iw = word_match (Memc[dotbuf], ftnlist)
+ if (iw == 0) {
+ jc = jc + gstrcpy (Memc[dotbuf], newexpr[jc],
+ maxch-jc+1)
+ } else {
+ jc = jc + word_find (iw, spplist, newexpr[jc],
+ maxch-jc+1)
+ }
+
+ kc = 0
+ }
+ }
+
+ ic = ic + 1
+ }
+
+ # If there is anything left in the dot buffer copy it unchanged
+ # to the output string
+
+ newexpr[jc] = EOS
+
+ if (kc > 0) {
+ Memc[dotbuf+kc] = EOS
+ call strcat (Memc[dotbuf], newexpr, maxch)
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/utilities/nttools/lib/gettabcol.x b/pkg/utilities/nttools/lib/gettabcol.x
new file mode 100644
index 00000000..154aff4f
--- /dev/null
+++ b/pkg/utilities/nttools/lib/gettabcol.x
@@ -0,0 +1,67 @@
+include <tbset.h>
+
+# GETTABCOL -- Read in a table column of any data type
+#
+# This procedure produces an array of table column values and an array of
+# null flags given an input table descriptor, column descriptor, and data
+# type. If the data type is set to zero, the column data type is queried
+# and returned to the calling program. The arrays are put in dynamic memory
+# and pointers to these arrays are returned to the calling program, which must
+# free the arrays when it is done with them.
+#
+# B.Simon 15-Dec-87 First Code
+
+procedure gettabcol (tp, cp, dtype, nary, aryptr, nulptr)
+
+pointer tp # i: Table descriptor
+pointer cp # i: Column descriptor
+int dtype # io: Data type of column (strings are -length)
+int nary # o: Length of output arrays
+pointer aryptr # o: Pointer to array of values
+pointer nulptr # o: Pointer to array of null flags
+#--
+int lendata, spptype
+int tbpsta(), tbcigi()
+
+errchk malloc, tbpsta
+
+begin
+ # Allocate storage for null flags
+
+ nary = tbpsta (tp, TBL_NROWS)
+ call malloc (nulptr, nary, TY_BOOL)
+ if (dtype == 0)
+ dtype = tbcigi (cp, TBL_COL_DATATYPE)
+
+ # Break down data type into spp type and length
+
+ if (dtype < 0) {
+ lendata = - dtype
+ spptype = TY_CHAR
+ } else {
+ lendata = 1
+ spptype = dtype
+ }
+
+ # Read in the column of table values
+
+ switch (spptype) {
+ case TY_BOOL:
+ call malloc (aryptr, nary, TY_BOOL)
+ call tbcgtb (tp, cp, Memb[aryptr], Memb[nulptr], 1, nary)
+ case TY_CHAR:
+ call malloc (aryptr, nary*(lendata+1), TY_CHAR)
+ call tbcgtt (tp, cp, Memc[aryptr], Memb[nulptr], lendata,
+ 1, nary)
+ case TY_SHORT,TY_INT,TY_LONG:
+ call malloc (aryptr, nary, TY_INT)
+ call tbcgti (tp, cp, Memi[aryptr], Memb[nulptr], 1, nary)
+ case TY_REAL:
+ call malloc (aryptr, nary, TY_REAL)
+ call tbcgtr (tp, cp, Memr[aryptr], Memb[nulptr], 1, nary)
+ case TY_DOUBLE:
+ call malloc (aryptr, nary, TY_DOUBLE)
+ call tbcgtd (tp, cp, Memd[aryptr], Memb[nulptr], 1, nary)
+ }
+
+end
diff --git a/pkg/utilities/nttools/lib/inquotes.x b/pkg/utilities/nttools/lib/inquotes.x
new file mode 100644
index 00000000..2cc0d8ce
--- /dev/null
+++ b/pkg/utilities/nttools/lib/inquotes.x
@@ -0,0 +1,121 @@
+include <chars.h>
+
+# inquotes -- Put quotes around string
+# This procedure examines the input/output string for blanks, tabs and
+# double quotes. If any of these is found, the string will be enclosed in
+# double quotes (unless it already begins with "), and embedded quotes will
+# be escaped with the '\' character. If the input string is null then it
+# will be replaced with a pair of adjacent double quotes. If maxch is not
+# large enough to include the extra characters, however, the string will not
+# be modified. The input and output strings may be the same.
+#
+# If there are trailing blanks but no embedded blanks, tabs or quotes,
+# then the input will be copied unmodified to the output. (6/17/92)
+#
+# The reason for enclosing a string in quotes is so that it may be read
+# later using ctowrd, and the entire string will be taken as one "word".
+#
+# Phil Hodge, 21-Jul-1987 Subroutine created.
+# Phil Hodge, 11-Aug-1987 Add outstr to calling sequence.
+# Phil Hodge, 17-Jun-1992 Also check for tabs; ignore trailing whitespace.
+# Phil Hodge, 13-Jan-1995 Include show_trailing argument in calling sequence.
+
+procedure inquotes (instr, outstr, maxch, show_trailing)
+
+char instr[ARB] # i: the string to be enclosed in quotes
+char outstr[ARB] # o: copy of instr, possibly enclosed in quotes
+int maxch # i: maximum length of string outstr
+int show_trailing # i: YES means show trailing blanks
+#--
+bool must_fix # true if str contains blanks and/or quotes
+int non_blank_len # length of instr up to last non-blank char
+int inlen # same as non_blank_len
+int outlen # length of outstr on output
+int numquotes # a count of the number of embedded quotes
+int ip, op # counters for input & output locations
+int strlen()
+
+begin
+ # Find the length of the string ...
+ if (show_trailing == YES) {
+ # ... including trailing blanks.
+ non_blank_len = strlen (instr)
+ } else {
+ # ... up to the last non-blank character.
+ non_blank_len = 0 # initial value
+ do ip = 1, maxch {
+ if (instr[ip] == EOS)
+ break
+ if (instr[ip] != BLANK) # else ignore blank
+ non_blank_len = ip
+ }
+ }
+
+ # Replace a null or completely blank string with "".
+ if (instr[1] == EOS || non_blank_len < 1) {
+ if (maxch >= 2)
+ call strcpy ("\"\"", outstr, maxch)
+ else # can't fix it
+ call strcpy (instr, outstr, maxch)
+ return
+ }
+
+ inlen = non_blank_len
+ numquotes = 0 # initial values
+ must_fix = false
+
+ # Run through the input string, but only go as far as the last
+ # non-blank character so we don't include trailing blanks.
+ do ip = 1, non_blank_len {
+ if (instr[ip] == EOS) {
+ break
+ } else if (instr[ip] == BLANK) {
+ must_fix = true
+ } else if (instr[ip] == TAB) {
+ must_fix = true
+ } else if (instr[ip] == DQUOTE) {
+ if (ip == 1) {
+ call strcpy (instr, outstr, maxch)
+ return # begins with ", so don't "fix" it
+ }
+ if (instr[ip-1] != ESCAPE) {
+ must_fix = true
+ numquotes = numquotes + 1
+ }
+ }
+ }
+
+ outlen = inlen + numquotes + 2
+ if (outlen > maxch || !must_fix) {
+ call strcpy (instr, outstr, maxch)
+ return # can't fix it or don't need to
+ }
+
+ # Work from the end toward the beginning in case instr = outstr.
+ outstr[outlen+1] = EOS
+ outstr[outlen] = DQUOTE
+ op = outlen - 1
+
+ if (numquotes > 0) {
+ # There are quotes within the string.
+ do ip = inlen, 1, -1 {
+ outstr[op] = instr[ip]
+ if (instr[ip] == DQUOTE) {
+ if (instr[ip-1] != ESCAPE) {
+ op = op - 1
+ outstr[op] = ESCAPE
+ }
+ }
+ op = op - 1
+ }
+ } else {
+ # No embedded quotes.
+ do ip = inlen, 1, -1 {
+ outstr[op] = instr[ip]
+ op = op - 1
+ }
+ }
+ outstr[1] = DQUOTE
+ if (op != 1)
+ call error (1, "miscount in inquotes")
+end
diff --git a/pkg/utilities/nttools/lib/invert.x b/pkg/utilities/nttools/lib/invert.x
new file mode 100644
index 00000000..f8a36675
--- /dev/null
+++ b/pkg/utilities/nttools/lib/invert.x
@@ -0,0 +1,55 @@
+
+include <tbset.h>
+
+# INVERT -- Create the complement (inverse) of an array of column pointers
+#
+# B.Simon 20-Oct-87 First Code
+
+procedure invert (tp, numptr, colptr)
+
+pointer tp # i: Table descriptor
+int numptr # io: Number of column pointers
+pointer colptr[ARB] # io: Array of column pointers
+
+bool match
+int numcol, icol, iptr, jptr
+pointer newptr, cp
+
+int tbpsta(), tbcnum()
+
+begin
+ # Create a temporary array to hold the pointers
+
+ numcol = tbpsta (tp, TBL_NCOLS)
+ call malloc (newptr, numcol, TY_INT)
+
+ jptr = 0
+ do icol = 1, numcol {
+
+ # Get each pointer in the table and
+ # see if it is in the original array
+
+ cp = tbcnum (tp, icol)
+ match = false
+ do iptr = 1, numptr {
+ if (cp == colptr[iptr]) {
+ match = true
+ break
+ }
+ }
+
+ # If not, add it to the temporary array
+
+ if (! match) {
+ Memi[newptr+jptr] = cp
+ jptr = jptr + 1
+ }
+ }
+
+# Copy the temporary array to the output array
+
+ numptr = jptr
+ call amovi (Memi[newptr], colptr, numptr)
+ call mfree (newptr, TY_INT)
+
+end
diff --git a/pkg/utilities/nttools/lib/mjd.x b/pkg/utilities/nttools/lib/mjd.x
new file mode 100644
index 00000000..601cf546
--- /dev/null
+++ b/pkg/utilities/nttools/lib/mjd.x
@@ -0,0 +1,94 @@
+include <ctype.h>
+include "reloperr.h"
+
+define TFIELDS 7
+define REQFIELD 3
+
+# MJD -- Compute the modified julian date of a time expressed as a string
+#
+# Dates are of the form YYYYMMDD:HHMMSSCC (fields after the colon are optional).
+# If an optional field is not present, its value is considered to be zero.
+# Dates must be between 1 Jan 1858 and 31 Dec 2099
+#
+# B.Simon 7-Oct-87 First Code
+# Phil Hodge 20-Feb-91 Move the data statements.
+
+double procedure mjd (date)
+
+char date[ARB] # i: String in the form YYYYMMDD:HHMMSSCC
+#--
+int jd, datelen, it, ic
+int time[TFIELDS], tpos[2,TFIELDS], tlim[2,TFIELDS]
+pointer sp, errtxt
+double df
+
+int strlen()
+
+string badfmt "Date has incorrect format (%s)"
+
+data tpos / 1, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17 /
+data tlim / 1858, 2099, 1, 12, 1, 31, 0, 23, 0, 59, 0, 59, 0, 99 /
+
+begin
+ # Allocate dynamic memory for error string
+
+ call smark (sp)
+ call salloc (errtxt, SZ_LINE, TY_CHAR)
+
+ datelen = strlen (date)
+ call aclri (time, TFIELDS)
+
+ # Convert the date string into integer fields
+
+ do it = 1, TFIELDS {
+
+ # Check for absence of optional fields
+
+ if (tpos[1,it] > datelen) {
+ if (it > REQFIELD)
+ break
+ else {
+ call sprintf (Memc[errtxt], SZ_LINE, badfmt)
+ call pargstr (date)
+ call error (SYNTAX, Memc[errtxt])
+ }
+ }
+
+ # Convert a field in the date string to an integer
+
+ do ic = tpos[1,it], tpos[2,it] {
+ if (IS_DIGIT(date[ic]))
+ time[it] = 10 * time[it] + TO_INTEG(date[ic])
+ else {
+ call sprintf (Memc[errtxt], SZ_LINE, badfmt)
+ call pargstr (date)
+ call error (SYNTAX, Memc[errtxt])
+ }
+ }
+
+ # Do bounds checking on the field
+ # Some errors can slip thru, e.g., Feb 30
+
+ if ((time[it] < tlim[1,it]) || (time[it] > tlim[2,it])) {
+ call sprintf (Memc[errtxt], SZ_LINE, badfmt)
+ call pargstr (date)
+ call error (SYNTAX, Memc[errtxt])
+ }
+ }
+
+ # Compute integer part of modified julian date
+ # From Van Flandern & Pulkkinen ApJ Sup 41:391-411 Nov 79
+
+ jd = 367 * time[1] - 7 * (time[1] + (time[2] + 9) / 12) / 4 -
+ 3 * ((time[1] + (time[2] - 9) / 7) / 100 + 1) / 4 +
+ 275 * time[2] / 9 + time[3] - 678971
+
+ # Compute fractional part of modified julian date
+ # N.B. julian date begins at noon, modified julian date at midnight
+
+ df = double (time[7] + 100 * (time[6] + 60 *
+ (time[5] + 60 * time[4]))) / 8640000.0
+
+ call sfree (sp)
+ return (jd + df)
+end
diff --git a/pkg/utilities/nttools/lib/mkpkg b/pkg/utilities/nttools/lib/mkpkg
new file mode 100644
index 00000000..3644f8e3
--- /dev/null
+++ b/pkg/utilities/nttools/lib/mkpkg
@@ -0,0 +1,33 @@
+# Update the library application code in the ttools package library
+# Author: B.Simon, 21-Dec-87
+
+$checkout libpkg.a ../
+$update libpkg.a
+$checkin libpkg.a ../
+$exit
+
+libpkg.a:
+ allcols.x <tbset.h>
+ allrows.x <tbset.h>
+ ftnexpr.x <ctype.h>
+ gettabcol.x <tbset.h>
+ inquotes.x <chars.h>
+ invert.x <tbset.h>
+ mjd.x <ctype.h> reloperr.h
+ newcolnam.x <ctype.h> <tbset.h> reloperr.h
+ reorder.x <tbset.h>
+ select.x reloperr.h
+ tabvar.x <tbset.h> "../tabvar.com"
+ tbfile.x <ctype.h>
+ tuopen.x
+ tbleval.x <config.h> <error.h> <evexpr.h> \
+ <xwhen.h> reloperr.h tblterm.com
+ tblsearch.x <config.h> <evexpr.h> <xwhen.h> \
+ reloperr.h tblterm.com
+ tblsort.x
+ tblterm.x <config.h> <evexpr.h> <tbset.h> \
+ <xwhen.h> reloperr.h tblterm.com
+ tctexp.x <tbset.h> <ctype.h> reloperr.h
+ tldtype.x
+ unique.x
+ ;
diff --git a/pkg/utilities/nttools/lib/movenulls.x b/pkg/utilities/nttools/lib/movenulls.x
new file mode 100644
index 00000000..5e961d20
--- /dev/null
+++ b/pkg/utilities/nttools/lib/movenulls.x
@@ -0,0 +1,35 @@
+# MOVENULLS -- Move all null elements to the end of the index array
+#
+# This procedure rearranges an array of row indices so that all rows with
+# nulls in a particular column are moved to the end of the index array.
+# The position of the nulls in the column is indicated by an array of null
+# flags, whose length might be greater than the length of the array of
+# indices, i.e., only a subset of the rows in a table might be in the index
+# array.
+#
+# B.Simon 15-Dec-87 First Code
+
+int procedure movenulls (nindex, nulflg, index)
+
+int nindex # i: Number of indices
+bool nulflg[ARB] # i: Array of null flags
+int index[ARB] # io: Array of row indices
+#--
+int nelem, idx, jdx
+
+begin
+ nelem = nindex
+
+ do idx = nindex, 1, -1 {
+ jdx = index[idx]
+ if (nulflg[jdx]) {
+ if (nelem != idx) {
+ index[idx] = index[nelem]
+ index[nelem] = jdx
+ }
+ nelem = nelem - 1
+ }
+ }
+
+ return (nelem)
+end
diff --git a/pkg/utilities/nttools/lib/msort.x b/pkg/utilities/nttools/lib/msort.x
new file mode 100644
index 00000000..2731351f
--- /dev/null
+++ b/pkg/utilities/nttools/lib/msort.x
@@ -0,0 +1,113 @@
+include "reloperr.h"
+
+# MSORT -- General merge sort for arbitrary objects. X is an integer array
+# indexing the array to be sorted. The user supplied COMPARE function is used
+# to compare objects indexed by X:
+#
+# -1,0,1 = compare (x1, x2)
+#
+# where the value returned by COMPARE has the following significance:
+#
+# -1 obj[x1] < obj[x2]
+# 0 obj[x1] == obj[x2]
+# 1 obj[x1] > obj[x2]
+#
+# MSORT reorders the elements of the X array, which must be of type integer.
+#
+# B.Simon 28-Sept-87 First Code
+
+procedure msort (x, nx, nelem, compare)
+
+int x[ARB] # array to be sorted
+int nx # length of array x (Must be >= 2 * nelem)
+int nelem # number of elements to be sorted
+extern compare() # function to be called to compare elements
+#--
+bool up
+int ielem, jelem, kelem, melem
+int runlen, ilen, jlen
+
+int compare()
+
+begin
+ if (2 * nelem > nx)
+ call error (BOUNDS, "Index array too small")
+
+ # Merging two sorted runs creates a new sorted run twice the length
+ # of the original run. Continue this process until the sorted run
+ # length is equal to the array length.
+
+ up = false
+ for (runlen = 1; runlen < nelem; runlen = 2 * runlen) {
+
+ # The runs are stored in one of two halves of the x array.
+ # Set the array pointers according to the half the runs are
+ # located in now.
+
+ if (! up) {
+ ielem = 1
+ jelem = runlen + 1
+ kelem = nx - nelem + 1
+ melem = nelem
+ } else {
+ ielem = nx - nelem + 1
+ jelem = runlen + ielem
+ kelem = 1
+ melem = nx
+ }
+
+ # Loop over each pair of runs in the array
+
+ while (ielem <= melem) {
+ ilen = min (runlen, melem-ielem+1)
+ jlen = min (runlen, melem-jelem+1)
+
+ # Merge the pair of runs into the other half of the x array
+
+ while (ilen > 0 && jlen > 0) {
+ if (compare (x[ielem], x[jelem]) <= 0) {
+ x[kelem] = x[ielem]
+ ielem = ielem + 1
+ kelem = kelem + 1
+ ilen = ilen - 1
+ } else {
+ x[kelem] = x[jelem]
+ jelem = jelem + 1
+ kelem = kelem + 1
+ jlen = jlen - 1
+ }
+ }
+
+ # Copy the remaining elements from i when j is exhausted
+
+ while (ilen > 0) {
+ x[kelem] = x[ielem]
+ ielem = ielem + 1
+ kelem = kelem + 1
+ ilen = ilen - 1
+ }
+
+ # Copy the remaining elements from j when i is exhausted
+
+ while (jlen > 0) {
+ x[kelem] = x[jelem]
+ jelem = jelem + 1
+ kelem = kelem + 1
+ jlen = jlen - 1
+ }
+
+ # Set array pointers to next set of runs
+
+ ielem = ielem + runlen
+ jelem = jelem + runlen
+ }
+ up = ! up
+ }
+
+ # If result is in the upper end of x array, move it to the lower
+ # end
+
+ if (up)
+ call amovi (x[nx-nelem+1], x[1], nelem)
+
+ end
diff --git a/pkg/utilities/nttools/lib/newcolnam.x b/pkg/utilities/nttools/lib/newcolnam.x
new file mode 100644
index 00000000..4179383d
--- /dev/null
+++ b/pkg/utilities/nttools/lib/newcolnam.x
@@ -0,0 +1,97 @@
+include <ctype.h>
+include <tbset.h>
+include "reloperr.h"
+
+# NEWCOLNAM -- Create a new, unique column name
+#
+# This procedure receives as input an array of column pointers from two or
+# more tables and an index into that array. If the name of the column pointed
+# to by that index is unique, it is output as the new name. If it is not
+# unique, a suffix of the form "_i" is appended to the name, where i is
+# a digit which (hopefully) makes the name unique.
+#
+# B.Simon 3-Nov-87 first code
+# B.Simon 4-Sep-90 Replaced call to strncmp with streq
+
+procedure newcolnam (numcol, colptr, colidx, newnam, maxch)
+
+int numcol # i: Number of column pointers
+pointer colptr[ARB] # i: Array of column pointers
+int colidx # i: Index to column to be renamed
+char newnam[ARB] # o: New column name
+int maxch # i: Maximum characters in new name
+#--
+int olen, nmatch, nbefore, icol
+pointer sp, oldnam, colnam, errtxt
+
+string notuniq "Cannot create a unique column name (%s)"
+
+bool streq()
+int strlen()
+
+begin
+ # Allocate dynamic memory for strings
+
+ call smark (sp)
+ call salloc (oldnam, SZ_COLNAME, TY_CHAR)
+ call salloc (colnam, SZ_COLNAME, TY_CHAR)
+ call salloc (errtxt, SZ_LINE, TY_CHAR)
+
+ # Read column name pointed to by index
+
+ call tbcigt (colptr[colidx], TBL_COL_NAME, Memc[oldnam], SZ_COLNAME)
+ call strupr (Memc[oldnam])
+
+ # See if the name is unique, and if not, how many columns with
+ # the same name precede this one
+
+ nmatch = 0
+ nbefore = 0
+ do icol = 1, numcol {
+ call tbcigt (colptr[icol], TBL_COL_NAME, Memc[colnam], SZ_COLNAME)
+ call strupr (Memc[colnam])
+
+ if (streq (Memc[colnam], Memc[oldnam])) {
+ nmatch = nmatch + 1
+ if (icol <= colidx)
+ nbefore = nbefore + 1
+ }
+ }
+
+ # If the name is not unique, add a suffix of the form "_i"
+
+ if (nmatch > 1) {
+
+ # Check for ridiculous values of maxch
+
+ olen = min (maxch-2, strlen(Memc[oldnam]))
+ if (olen < 1) {
+ call sprintf (Memc[errtxt], SZ_LINE, notuniq)
+ call pargstr (Memc[oldnam])
+ call error (SYNTAX, Memc[errtxt])
+ }
+
+ # Add the suffix
+
+ Memc[oldnam+olen] = '_'
+ Memc[oldnam+olen+1] = TO_DIGIT (nbefore)
+ Memc[oldnam+olen+2] = EOS
+
+ # Make sure it is unique
+
+ do icol = 1, numcol {
+ call tbcigt (colptr[icol], TBL_COL_NAME, Memc[colnam],
+ SZ_COLNAME)
+ if (streq (Memc[oldnam], Memc[colnam])) {
+ call sprintf (Memc[errtxt], SZ_LINE, notuniq)
+ call pargstr (Memc[oldnam])
+ call error (SYNTAX, Memc[errtxt])
+ }
+ }
+ }
+
+ # Copy to the output string
+
+ call strcpy (Memc[oldnam], newnam, maxch)
+ call sfree (sp)
+end
diff --git a/pkg/utilities/nttools/lib/reloperr.h b/pkg/utilities/nttools/lib/reloperr.h
new file mode 100644
index 00000000..6dff85c7
--- /dev/null
+++ b/pkg/utilities/nttools/lib/reloperr.h
@@ -0,0 +1,3 @@
+define SYNTAX 1
+define BOUNDS 2
+define PUTNULL 11
diff --git a/pkg/utilities/nttools/lib/reorder.x b/pkg/utilities/nttools/lib/reorder.x
new file mode 100644
index 00000000..cacd98e2
--- /dev/null
+++ b/pkg/utilities/nttools/lib/reorder.x
@@ -0,0 +1,60 @@
+include <tbset.h>
+
+# REORDER -- Reorder table rows according to an index array
+#
+# This procedure rearranges the rows of a table according to the contents
+# of an index array. The index array is produced by one of the two table
+# sort routines, tsort1 or tsortm. The algorithm used is taken from Knuth's
+# Sorting and Searching p.595.
+#
+# B.Simon 17-Sept-87 First Code
+# B.Simon 15-Jul-88 Rewritten
+# Phil Hodge 12-Sep-88 Don't include tbtables.h
+
+procedure reorder (tp, nindex, index)
+
+pointer tp # i: Table descriptor
+int nindex # i: Number of indices
+int index[ARB] # io: Array of row indices
+#--
+int idx, jdx, kdx, ndx
+int tbpsta()
+
+errchk tbrcpy
+
+begin
+ # Use the row after the end of the table for temporary storage
+
+ ndx = tbpsta (tp, TBL_NROWS) + 1
+
+ # Loop over all rows of the table, moving them into their proper
+ # order
+
+ do idx = 1, nindex {
+
+ # The index array forms one or more cycles. Move the first
+ # row in the cycle to the temporary location. Repeatedly
+ # move the remaining rows in the cycle until the final
+ # location of the first row is found. Move the first row
+ # from its temporary location to its final location. Update
+ # the index array to indicate which rows have been moved.
+
+ if (index[idx] != idx) {
+ call tbrcpy (tp, tp, idx, ndx)
+ jdx = idx
+ while (index[jdx] != idx) {
+ kdx = index[jdx]
+ call tbrcpy (tp, tp, kdx, jdx)
+ index[jdx] = jdx
+ jdx = kdx
+ }
+ call tbrcpy (tp, tp, ndx, jdx)
+ index[jdx] = jdx
+ }
+
+ }
+
+ # Remove the temporary row
+
+ call tbrdel (tp, ndx, ndx)
+end
diff --git a/pkg/utilities/nttools/lib/select.x b/pkg/utilities/nttools/lib/select.x
new file mode 100644
index 00000000..02cc73f8
--- /dev/null
+++ b/pkg/utilities/nttools/lib/select.x
@@ -0,0 +1,99 @@
+include "reloperr.h"
+
+# SELECT -- Select table rows according to expression
+#
+# This procedure evaluates a boolean expession for selected rows in a table.
+# If the expression is true and does not involve null elements, the index
+# of that row is kept in the index array.
+#
+# B.Simon 7-Oct-87 First Code
+# B.Simon 16-Dec-87 Changed to handle table subsets
+# B.Simon 06-Jan-93 Changed to use ftnexpr
+
+procedure select (tp, expr, nindex, index)
+
+pointer tp # i: Table descriptor
+char expr[ARB] # i: Algebraic expression used in selection
+int nindex # io: Number of rows selected
+int index[ARB] # io: Indices of selected rows
+#--
+char ch
+pointer sp, oldexp, newexp, ic, aryptr, nulptr
+int fd, sd, jc, dtype, nary, iary
+
+int open(), stropen(), stridx()
+
+errchk open, stropen, tbl_eval
+
+string badtype "Expression is not boolean"
+
+begin
+ # Allocate dynamic memory for strings
+
+ call smark (sp)
+ call salloc (oldexp, SZ_COMMAND, TY_CHAR)
+ call salloc (newexp, SZ_COMMAND, TY_CHAR)
+
+ # Check to see if the expression is a file name
+
+ if (expr[1] == '@') {
+
+ # Copy the file into a string
+
+ fd = open (expr[2], READ_ONLY, TEXT_FILE)
+ sd = stropen (Memc[oldexp], SZ_COMMAND, WRITE_ONLY)
+ call fcopyo (fd, sd)
+ call close (fd)
+ call strclose (sd)
+
+ # Replace the newlines with blanks
+
+ ic = oldexp
+ ch = '\n'
+ repeat {
+ jc = stridx (ch, Memc[ic])
+ if (jc == 0)
+ break
+ ic = ic + jc
+ Memc[ic-1] = ' '
+ }
+
+ # Convert Fortran relational operators to SPP
+
+ call ftnexpr (Memc[oldexp], Memc[newexp], SZ_COMMAND)
+
+ } else {
+
+ # Convert Fortran relational operators to SPP
+
+ call ftnexpr (expr, Memc[newexp], SZ_COMMAND)
+ }
+
+ # Evaluate the expression
+
+ dtype = TY_BOOL
+ call tbl_eval (tp, nindex, index, Memc[newexp], dtype, aryptr, nulptr)
+
+ # Check to see if result is boolean
+
+ if (dtype != TY_BOOL) {
+ call mfree (aryptr, dtype)
+ call mfree (nulptr, TY_BOOL)
+ call error (SYNTAX, badtype)
+ }
+
+ # Put indices of true, non-null rows in index array
+
+ nary = nindex
+ nindex = 0
+ do iary = 1, nary
+
+ if (Memb[aryptr+iary-1] && ! Memb[nulptr+iary-1]) {
+ nindex = nindex + 1
+ index[nindex] = index[iary]
+ }
+
+ call mfree (aryptr, dtype)
+ call mfree (nulptr, TY_BOOL)
+ call sfree (sp)
+end
diff --git a/pkg/utilities/nttools/lib/tabvar.x b/pkg/utilities/nttools/lib/tabvar.x
new file mode 100644
index 00000000..339f2d95
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tabvar.x
@@ -0,0 +1,118 @@
+include <tbset.h>
+
+# TABVAR -- Retrieve a table column given its name
+#
+# B.Simon 03-May-91 Original
+# B.Simon 23-Jun-97 Peicewise evaluation of column
+
+procedure tabvar (stack, colname)
+
+pointer stack # u: Expression stack pointer
+char colname[ARB] # i: Column name
+#--
+include "../tabvar.com"
+
+int i, coltype, nrows
+pointer sp, nullbuf, buffer, errmsg, cp
+
+string badcolnam "Column name not found (%s)"
+
+bool streq()
+int tbcigi()
+pointer stk_alloc()
+
+begin
+ # Allocate dynamic memory for strings
+
+ call smark (sp)
+ call salloc (errmsg, SZ_LINE, TY_CHAR)
+
+ # Get column pointer from name
+
+ call tbcfnd (tabptr, colname, cp, 1)
+ if (cp == NULL) {
+ if (streq (colname, "rownum")) {
+ call rowvar (stack)
+ return
+ } else {
+ call sprintf (Memc[errmsg], SZ_LINE, badcolnam)
+ call pargstr (colname)
+ call error (1, Memc[errmsg])
+ }
+ }
+
+ # Get column type
+
+ coltype = tbcigi (cp, TBL_COL_DATATYPE)
+ if (coltype == TY_BOOL || coltype == TY_SHORT || coltype == TY_LONG) {
+ coltype = TY_INT
+ } else if (coltype < 0) {
+ coltype = TY_DOUBLE
+ }
+
+ # Allocate a buffer on the expression evaluator stack
+
+ nrows = (lastrow - firstrow) + 1
+ call malloc (nullbuf, nrows, TY_BOOL)
+ buffer = stk_alloc (stack, nrows, coltype)
+
+ # Copy the table column into the buffer
+ # Substitute the user supplied vales for nulls
+
+ switch (coltype) {
+ case TY_SHORT, TY_INT, TY_LONG:
+ call tbcgti (tabptr, cp, Memi[buffer], Memb[nullbuf],
+ firstrow, lastrow)
+ do i = 0, nrows-1 {
+ if (Memb[nullbuf+i])
+ Memi[buffer+i] = nullval
+ }
+
+ case TY_REAL:
+ call tbcgtr (tabptr, cp, Memr[buffer], Memb[nullbuf],
+ firstrow, lastrow)
+ do i = 0, nrows-1 {
+ if (Memb[nullbuf+i])
+ Memr[buffer+i] = nullval
+ }
+ case TY_DOUBLE:
+ call tbcgtd (tabptr, cp, Memd[buffer], Memb[nullbuf],
+ firstrow, lastrow)
+ do i = 0, nrows-1 {
+ if (Memb[nullbuf+i])
+ Memd[buffer+i] = nullval
+ }
+ }
+
+ # Update the null array
+ call stk_ornull (stack, Memb[nullbuf], nrows)
+
+ call mfree (nullbuf, TY_BOOL)
+ call sfree (sp)
+
+end
+
+# ROWVAR -- Handle the variable "rownum"
+
+procedure rowvar (stack)
+
+pointer stack # u: Expression stack pointer
+#--
+include "../tabvar.com"
+
+int irow, nrows
+pointer buffer
+
+pointer stk_alloc()
+
+begin
+ # Allocate a buffer on the expression evaluator stack
+
+ nrows = (lastrow - firstrow) + 1
+ buffer = stk_alloc (stack, nrows, TY_INT)
+
+ # Fill the buffer with the row number
+ do irow = 0, nrows-1
+ Memi[buffer+irow] = firstrow + irow
+end
+
diff --git a/pkg/utilities/nttools/lib/tbfile.x b/pkg/utilities/nttools/lib/tbfile.x
new file mode 100644
index 00000000..d11e1720
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tbfile.x
@@ -0,0 +1,85 @@
+include <ctype.h> # for IS_ALNUM
+
+# tbfile -- get table and file name
+# This routine takes a table name as specified by a user and returns
+# the full table name, the full file name, and the filename extension
+# (including the dot; e.g. ".tab"). The filename extension may be the
+# null string if the file is a text table. The file name will be a
+# subset of the table name, as the table name may include a bracketed
+# expression giving EXTNAME or HDU number or table name in CDF file.
+#
+# Phil Hodge, 27-Jun-1995 Subroutine created.
+# Phil Hodge, 29-Sep-1997 No longer necessary to enclose extname expression
+# in brackets, as the brackets are now included.
+# Phil hodge, 16-Apr-1999 Remove ttype from calling sequence of tbparse.
+
+procedure tbfile (input, tabname, filename, extn, maxch)
+
+char input[ARB] # i: input table name
+char tabname[maxch] # o: full table name
+char filename[maxch] # o: name of file containing table
+char extn[maxch] # o: filename extension, including '.'
+int maxch # i: size of strings
+#--
+pointer sp
+pointer fname # full file name
+pointer brackets # for CDF or HDU name or number, and/or selectors
+int hdu # returned by tbparse and ignored
+int dotloc # location of last '.' in file name
+int i
+int strlen(), access()
+int tbparse()
+bool strne()
+errchk tbparse, tbtext
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_LINE, TY_CHAR)
+ call salloc (brackets, SZ_LINE, TY_CHAR)
+
+ # Separate filename from any bracketed expression (such as
+ # EXTNAME or HDU number) that may be present.
+ if (tbparse (input, Memc[fname], Memc[brackets], SZ_LINE, hdu) < 1) {
+ tabname[1] = EOS
+ filename[1] = EOS
+ extn[1] = EOS
+ call sfree (sp)
+ return
+ }
+
+ # Append default extension (if appropriate) to get full file name.
+ # A text table need not have an extension, so first check whether
+ # a file of the given name exists. If not, then append extension.
+ if (access (Memc[fname], 0, 0) == NO &&
+ strne (input, "STDIN") && strne (input, "STDOUT"))
+ call tbtext (Memc[fname], Memc[fname], SZ_LINE)
+
+ # At this point we have the full file name; copy it to output.
+ call strcpy (Memc[fname], filename, maxch)
+
+ # Append bracketed expression (if present) to get full table name,
+ # and copy it to output.
+ call strcpy (Memc[fname], tabname, maxch)
+ if (Memc[brackets] != EOS)
+ call strcat (Memc[brackets], tabname, maxch)
+
+ # Search for a filename extension. Look for a dot that is not
+ # followed by any special character.
+ dotloc = 0 # initial value
+ do i = strlen (Memc[fname]), 1, -1 {
+ if (Memc[fname+i-1] == '.') { # found it
+ dotloc = i
+ break
+ }
+ if (!IS_ALNUM(Memc[fname+i-1])) # stop at first special char
+ break
+ }
+
+ # If the file name includes an extension, copy it to output.
+ if (dotloc > 0)
+ call strcpy (Memc[fname+dotloc-1], extn, maxch)
+ else
+ extn[1] = EOS
+
+ call sfree (sp)
+end
diff --git a/pkg/utilities/nttools/lib/tbleval.x b/pkg/utilities/nttools/lib/tbleval.x
new file mode 100644
index 00000000..c9382699
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tbleval.x
@@ -0,0 +1,159 @@
+include <config.h>
+include <error.h>
+include <evexpr.h>
+include <xwhen.h>
+include "reloperr.h"
+
+# TBL_EVAL -- Evaluate an arbitrary expression over table columns
+#
+# This procedure receives as input a table descriptor, an index array, and
+# a character string containing an algebraic expression. The terms in the
+# expression are column names. The expression is evaluated for each row in
+# the index array using the values from the indicated columns and the results
+# stored in the output array (aryptr). The array pointed to by nulptr
+# contains null flags. A null flag is set to true if any of the table elements
+# in the expression is null or an arithmetic error ocurs during the
+# evaluation of the expression. Otherwise the null flag is set to false.
+# The type of the output array is determined by the type of the expression
+# unless all the elements are null, in which case the type input by the
+# calling routine is used. The two arrays pointed to by aryptr and nulptr
+# must be deallocated by the calling routine.
+#
+# B.Simon 29-Sept-87 First Code
+# B.Simon 16-Dec-87 Changed to handle table subsets
+# B.Simon 13-Apr-88 tbl_term, tbl_func moved to separate file
+
+procedure tbl_eval (tp, nindex, index, expr, dtype, aryptr, nulptr)
+
+pointer tp # i: Table descriptor
+int nindex # i: Number of elements in index array
+int index[ARB] # i: Array of row indices
+char expr[ARB] # i: Expression to be evaluated
+int dtype # io: Type of output array
+pointer aryptr # o: Array of output values
+pointer nulptr # o: Array of null flags
+#--
+include "tblterm.com"
+
+int iary, status, junk
+int old_handler, tbl_term_adr, tbl_func_adr
+pointer op
+
+string badtype "Character expressions not allowed"
+
+int locpr(), errcode()
+pointer evexpr()
+
+extern tbl_handler(), tbl_term(), tbl_func()
+
+begin
+ # Initialize output variables
+
+ aryptr = NULL
+ call malloc (nulptr, nindex, TY_BOOL)
+
+ # Set up error handler to catch arithmetic errors
+
+ call xwhen (X_ARITH, locpr(tbl_handler), old_handler)
+
+ table = tp
+ nterm = 0
+ constant = true
+
+ tbl_term_adr = locpr (tbl_term)
+ tbl_func_adr = locpr (tbl_func)
+
+ # Loop over all rows of the table
+
+ do iary = 1, nindex {
+
+ irow = index[iary]
+ iterm = 0
+
+ # Execution will resume here when an arithmetic error occurs
+
+ call zsvjmp (jumpbuf, status)
+
+ if (status != OK) {
+ Memb[nulptr+iary-1] = true
+
+ # Special case to speed up the evaluation of constant expressions
+
+ } else if (constant && (iary != 1)) {
+ Memb[nulptr+iary-1] = false
+ switch (dtype) {
+ case TY_BOOL:
+ Memb[aryptr+iary-1] = Memb[aryptr]
+ case TY_INT:
+ Memi[aryptr+iary-1] = Memi[aryptr]
+ case TY_REAL:
+ Memr[aryptr+iary-1] = Memr[aryptr]
+ }
+
+ # Evaluate the expression using the values in the current row
+
+ } else {
+ iferr {
+ op = evexpr (expr, tbl_term_adr, tbl_func_adr)
+ } then {
+
+ # Catch the error sent when a table element is null
+
+ if (errcode() == PUTNULL)
+ Memb[nulptr+iary-1] = true
+ else {
+ call mfree (nulptr, TY_BOOL)
+ call xwhen (X_ARITH, old_handler, junk)
+ call erract (EA_ERROR)
+ }
+
+ # Usual case
+
+ } else {
+
+ Memb[nulptr+iary-1] = false
+
+ # Determine array type from type of expression
+
+ if (aryptr == NULL) {
+ if (O_TYPE(op) == TY_CHAR) {
+ call mfree (nulptr, TY_BOOL)
+ call xwhen (X_ARITH, old_handler, junk)
+ call error (SYNTAX, badtype)
+ }
+ dtype = O_TYPE(op)
+ call calloc (aryptr, nindex, dtype)
+ }
+
+ # Assign the result of the expression to the output
+ # array
+
+ switch (dtype) {
+ case TY_BOOL:
+ Memb[aryptr+iary-1] = O_VALB(op)
+ case TY_INT:
+ Memi[aryptr+iary-1] = O_VALI(op)
+ case TY_REAL:
+ Memr[aryptr+iary-1] = O_VALR(op)
+ }
+
+ call mfree (op, TY_STRUCT) # Bug fix (BPS 04.20.93)
+ }
+ }
+ }
+
+ # Allocate array when all results are null
+
+ if (aryptr == NULL) {
+ if (dtype == TY_CHAR) {
+ call mfree (nulptr, TY_BOOL)
+ call xwhen (X_ARITH, old_handler, junk)
+ call error (SYNTAX, badtype)
+ }
+ call calloc (aryptr, nindex, dtype)
+ }
+ # Restore old error handler
+
+ call xwhen (X_ARITH, old_handler, junk)
+
+end
diff --git a/pkg/utilities/nttools/lib/tbljoin.x b/pkg/utilities/nttools/lib/tbljoin.x
new file mode 100644
index 00000000..c2a26fd6
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tbljoin.x
@@ -0,0 +1,168 @@
+include <tbset.h>
+define MAXPRI 7
+
+# TBL_JOIN -- Relational join of two tables
+#
+# This procedure peforms a relational join by sorting the two tables on
+# the column to be joined and then merging the tables on the basis of the
+# common column. An input tolerance is used to control the test for equality
+# in the merge. The variables which describe the two tables are the table
+# descriptors (tp1 & tp2), column descriptors (cp1 & cp2), row index arrays
+# (index1 & index2), and index array lengths (nindex1 & nindex2). The merged
+# output table is described by two index arrays which contain the row indices
+# from the respective input tables (index3 & index4) and the index array
+# lengths (nindex3 & nindex4). On input these lengths are the declared length
+# of the output index arrays, on output, they are the number of rows in
+# the merged output table. The total number of merged rows is output as
+# njoin. The output index arrays may not be large enough to hold the merged
+# table indices. In this case, the output index arrays will be filled as much
+# as possible. So if njoin is greater than nindex3 or nindex4, an error has
+# occured, but this error can be recovered from by reallocating the output
+# index arrays so that the can hold njoin elements and calling this procedure
+# again.
+#
+# B.Simon 03-Nov-87 First Code
+# B.Simon 16-Dec-87 Changed to handle table subsets
+# B.Simon 06-Feb-90 Changed to use tbtsrt
+
+procedure tbl_join (tol, casesens, tp1, tp2, cp1, cp2, nindex1, nindex2,
+ index1, index2, nindex3, nindex4, index3, index4, njoin)
+
+double tol # i: Tolerance used in testing for equality
+bool casesens # i: Join is case sensitive
+pointer tp1 # i: Table descriptor of first table
+pointer tp2 # i: Table descriptor of second table
+pointer cp1 # i: Column descriptor of merged column in first table
+pointer cp2 # i: Column descriptor of merged column in second table
+int nindex1 # i: Number of indices in first input array
+int nindex2 # i: Number of indices in second input array
+int index1 # i: Array of row indices for first input table
+int index2 # i: Array of row indices for second input table
+int nindex3 # io: Number of indices in first output array
+int nindex4 # io: Number of indices in second output array
+int index3 # o: Array of row indices for first output table
+int index4 # o: Array of row indices for second output table
+int njoin # o: Number of joined rows
+#--
+bool fold
+int dtype[2], spptype[2], lendata[2], colpri[2], nary[2], nidx[2]
+int itab, iary, nmax
+
+pointer nulptr, temptr, curptr
+pointer tp[2], cp[2], idxptr[2], aryptr[2]
+
+int priority[MAXPRI]
+data priority / TY_DOUBLE, TY_REAL, TY_LONG, TY_INT, TY_SHORT,
+ TY_CHAR, TY_BOOL /
+double mjd()
+int tbcigi()
+
+begin
+ # Move input variables into arrays
+
+ fold = ! casesens
+
+ tp[1] = tp1
+ tp[2] = tp2
+
+ cp[1] = cp1
+ cp[2] = cp2
+
+ nmax = min (nindex3, nindex4)
+
+ nidx[1] = nindex1
+ nidx[2] = nindex2
+
+ call malloc (idxptr[1], nindex1, TY_INT)
+ call amovi (index1, Memi[idxptr[1]], nindex1)
+
+ call malloc (idxptr[2], nindex2, TY_INT)
+ call amovi (index2, Memi[idxptr[2]], nindex2)
+
+ # Determine the data type of the merged column
+
+ do itab = 1, 2 {
+
+ dtype[itab] = tbcigi (cp[itab], TBL_COL_DATATYPE)
+
+ if (dtype[itab] < 0) {
+ lendata[itab] = - dtype[itab]
+ spptype[itab] = TY_CHAR
+ } else {
+ lendata[itab] = 1
+ spptype[itab] = dtype[itab]
+ }
+
+ for (colpri[itab] = 1;
+ spptype[itab] != priority[colpri[itab]];
+ colpri[itab] = colpri[itab] + 1
+ ) ;
+
+ }
+
+ if (colpri[1] < colpri[2]) {
+ spptype[2] = spptype[1]
+ lendata[2] = lendata[1]
+ } else if (colpri[2] < colpri[1]) {
+ spptype[1] = spptype[2]
+ lendata[1] = lendata[2]
+ }
+
+ # Read common columns into arrays and sort
+
+ do itab = 1, 2 {
+
+ # Sort the index array on the common column
+
+ call tbtsrt (tp[itab], 1, cp[itab], fold,
+ nidx[itab], Memi[idxptr[itab]])
+
+ # Read in the common column
+
+ if (spptype[itab] == TY_CHAR)
+ dtype[itab] = - lendata[itab]
+ else
+ dtype[itab] = spptype[itab]
+
+ call gettabcol (tp[itab], cp[itab], dtype[itab],
+ nary[itab], aryptr[itab], nulptr)
+
+ # If the tolerance of a string column is non-zero,
+ # interpret the column as a date
+
+ if (dtype[itab] < 0 && tol > 0.0) {
+
+ call malloc (temptr, nary[itab], TY_DOUBLE)
+ curptr = aryptr[itab]
+ do iary = 1, nary[itab] {
+ if (Memb[nulptr+iary-1])
+ Memd[temptr+iary-1] = INDEFD
+ else
+ Memd[temptr+iary-1] = mjd (Memc[curptr])
+ curptr = curptr + lendata[itab] + 1
+ }
+ call mfree (aryptr[itab], TY_CHAR)
+ dtype[itab] = TY_DOUBLE
+ spptype[itab] = TY_DOUBLE
+ lendata[itab] = 1
+ aryptr[itab] = temptr
+ }
+ }
+
+ # Merge the two tables
+
+ call tbl_merge (tol, dtype, nary, aryptr, nidx, idxptr,
+ nmax, njoin, index3, index4)
+
+ nindex3 = min (nmax, njoin)
+ nindex4 = min (nmax, njoin)
+
+ # Free dynamic memory
+
+ call mfree (nulptr, TY_BOOL)
+ do itab = 1, 2 {
+ call mfree (idxptr[itab], TY_INT)
+ call mfree (aryptr[itab], spptype[itab])
+ }
+
+end
diff --git a/pkg/utilities/nttools/lib/tblmerge.x b/pkg/utilities/nttools/lib/tblmerge.x
new file mode 100644
index 00000000..ead8cbe6
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tblmerge.x
@@ -0,0 +1,162 @@
+include "reloperr.h"
+
+# TBL_MERGE -- Merge two tables on the basis of a common column
+#
+# This procedure creates an array of row indices from two tables where the
+# row indices point to a pair of rows where the values stored in the two
+# columns are equal within an input tolerance. The column values are stored in
+# the two arrays pointed to by aryptr. The two columns must already be sorted
+# in ascending order with the row indices of the two columns stored in the
+# arrays pointed to by idxptr. This procedure keeps on going even when an
+# output array overflow condition is detected so that the caller knows how
+# large the output array must be.
+#
+# B.Simon 1-Nov-87 First code
+# B.Simon 16-Dec-87 Changed to handle table subsets
+
+procedure tbl_merge (tol, dtype, nary, aryptr, nidx, idxptr, nmax,
+ nmerge, index1, index2)
+
+double tol # i: Tolerance used in test for equality
+int dtype[2] # i: Data types of columns
+int nary[2] # i: Size of arrays containing columns
+pointer aryptr[2] # i: Pointers to column arrays
+int nidx[2] # i: Size of arrays containing row indices
+pointer idxptr[2] # i: Pointers to index arrays
+int nmax # i: Max size of arrays containing merged row indices
+int nmerge # o: Number of merged row indices
+int index1[ARB] # o: Array of merged row indices for first table
+int index2[ARB] # o: Array of merged row indices for second table
+#--
+double dbl_tol
+int itab, int_tol, idx, jdx, kdx, order, lendata[2], spptype[2]
+pointer ptr1, ptr2
+real real_tol
+
+bool strlt(), strgt()
+
+string badtype "Data types of the two columns to be merged must be equal"
+string badtol "Tolerance for boolean or character columns must be zero"
+
+begin
+ # Get data type and length from dtype
+
+ do itab = 1, 2 {
+ if (dtype[itab] < 0) {
+ lendata[itab] = 1 - dtype[itab]
+ spptype[itab] = TY_CHAR
+ } else {
+ lendata[itab] = 1
+ spptype[itab] = dtype[itab]
+ }
+ }
+
+ if (spptype[1] != spptype[2])
+ call error (SYNTAX, badtype)
+
+ # Convert tolerance to the same type as the data
+
+ switch (spptype[1]) {
+ case TY_BOOL, TY_CHAR:
+ if (tol > 0.0)
+ call error (SYNTAX, badtol)
+ case TY_SHORT, TY_INT, TY_LONG:
+ int_tol = tol
+ case TY_REAL:
+ real_tol = tol
+ case TY_DOUBLE:
+ dbl_tol = tol
+ }
+
+ idx = 1
+ jdx = 1
+ kdx = 1
+
+ nmerge = 0
+ while (idx <= nidx[1] && jdx <= nidx[2]) {
+
+ # Calculate addresses of array elements
+
+ ptr1 = aryptr[1] + lendata[1] * (Memi[idxptr[1]+idx-1] - 1)
+ ptr2 = aryptr[2] + lendata[2] * (Memi[idxptr[2]+jdx-1] - 1)
+
+ # Determine relative order of the two elements
+ # If mem[ptr1] < mem[ptr2], order = -1
+ # If mem[ptr1] == mem[ptr2], order = 0
+ # If mem[ptr1] > mem[ptr2], order = 1
+
+ switch (spptype[1]) {
+ case TY_BOOL:
+ # false < true
+
+ if (! Memb[ptr1] && Memb[ptr2])
+ order = -1
+ else if (Memb[ptr1] && ! Memb[ptr2])
+ order = 1
+ else
+ order = 0
+ case TY_CHAR:
+ if (strlt (Memc[ptr1], Memc[ptr2]))
+ order = -1
+ else if (strgt (Memc[ptr1], Memc[ptr2]))
+ order = 1
+ else
+ order = 0
+ case TY_SHORT,TY_INT, TY_LONG:
+ if (Memi[ptr1] + int_tol < Memi[ptr2])
+ order = -1
+ else if (Memi[ptr1] > Memi[ptr2] + int_tol)
+ order = 1
+ else
+ order = 0
+ case TY_REAL:
+ if (Memr[ptr1] + real_tol < Memr[ptr2])
+ order = -1
+ else if (Memr[ptr1] > Memr[ptr2] + real_tol)
+ order = 1
+ else
+ order = 0
+ case TY_DOUBLE:
+ if (Memd[ptr1] + dbl_tol < Memd[ptr2])
+ order = -1
+ else if (Memd[ptr1] > Memd[ptr2] + dbl_tol)
+ order = 1
+ else
+ order = 0
+ }
+
+ # Increment the indices to the two arrays and if a match is
+ # found, add it to the index array.
+
+ # The third index, kdx, tells where to fall back to when the
+ # value in the first array exceeds the value in the second array.
+ # Because the arrays are sorted in ascending order, the array
+ # element pointed to by idx exceeds all those previous to the
+ # element pointed to by kdx, so there is no use checking them.
+
+ switch (order) {
+ case -1:
+ idx = idx + 1
+ jdx = kdx
+ case 0:
+ nmerge = nmerge + 1
+ if (nmerge <= nmax) {
+ index1[nmerge] = Memi[idxptr[1]+idx-1]
+ index2[nmerge] = Memi[idxptr[2]+jdx-1]
+ }
+
+ # Keep fron reading past the end of the array
+
+ if (jdx < nidx[2]) {
+ jdx = jdx + 1
+ } else {
+ idx = idx + 1
+ jdx = kdx
+ }
+ case 1:
+ jdx = jdx + 1
+ kdx = jdx
+ }
+ }
+
+end
diff --git a/pkg/utilities/nttools/lib/tblsearch.x b/pkg/utilities/nttools/lib/tblsearch.x
new file mode 100644
index 00000000..87e17106
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tblsearch.x
@@ -0,0 +1,104 @@
+include <config.h>
+include <evexpr.h>
+include <xwhen.h>
+include "reloperr.h"
+
+# TBL_SEARCH -- Search table for a row which makes an expression true
+#
+# This procedure evaluates a boolean expession for the indicated rows in a
+# table. When it finds a row which makes the expression true, it returns
+# the row number. If it does not find any such row, it returns zero. If
+# there is a syntax error in the expression, it returns ERR.
+#
+# B.Simon 13-Apr-1988 First Code
+# Phil Hodge 4-Mar-2002 Free memory allocated by evexpr.
+# Phil Hodge 23-Apr-2002 Move xev_freeop and mfree.
+
+int procedure tbl_search (tp, expr, first, last)
+
+pointer tp # i: Table descriptor
+char expr[ARB] # i: Boolean expression used in search
+int first # i: First row to look at
+int last # i: Last row to look at
+#--
+include "tblterm.com"
+
+int old_handler, tbl_term_adr, tbl_func_adr
+int status, found, dir, iary, junk
+pointer sp, op, newexp
+bool done
+
+int locpr(), errcode()
+pointer evexpr()
+
+extern tbl_handler(), tbl_term(), tbl_func()
+
+begin
+ # Allocate dynamic memory for strings
+
+ call smark (sp)
+ call salloc (newexp, SZ_COMMAND, TY_CHAR)
+
+ # Convert Fortran relational operators to SPP
+
+ call ftnexpr (expr, Memc[newexp], SZ_COMMAND)
+
+ # Set up error handler to catch arithmetic errors
+
+ call xwhen (X_ARITH, locpr(tbl_handler), old_handler)
+
+ table = tp
+ nterm = 0
+ constant = false
+
+ tbl_term_adr = locpr (tbl_term)
+ tbl_func_adr = locpr (tbl_func)
+
+ found = 0
+ done = false
+
+ dir = sign (1, last - first)
+ do iary = first, last, dir {
+
+ irow = iary
+ iterm = 0
+
+ # Execution will resume here when an arithmetic error occurs
+
+ call zsvjmp (jumpbuf, status)
+
+ if (status != OK)
+ next
+
+ # Evaluate expression. Check if result is true
+
+ ifnoerr {
+ op = evexpr (Memc[newexp], tbl_term_adr, tbl_func_adr)
+ } then {
+
+ if (O_TYPE(op) != TY_BOOL) {
+ found = ERR
+ done = true
+ } else if (O_VALB(op)) {
+ found = irow
+ done = true
+ }
+ call xev_freeop (op)
+ call mfree (op, TY_STRUCT)
+
+ } else if (errcode() != PUTNULL) {
+ # Ignore errors caused by nulls
+ found = ERR
+ done = true
+ }
+ if (done)
+ break
+ }
+
+ # Restore old error handler
+
+ call xwhen (X_ARITH, old_handler, junk)
+ call sfree (sp)
+
+ return (found)
+end
diff --git a/pkg/utilities/nttools/lib/tblsort.x b/pkg/utilities/nttools/lib/tblsort.x
new file mode 100644
index 00000000..9af87bf6
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tblsort.x
@@ -0,0 +1,39 @@
+# TBL_SORT -- Sort a table on selected table columns
+#
+# B.Simon 06-Fab-90 First Code
+
+procedure tbl_sort (ascend, casesens, tp, numptr, colptr, nindex, index)
+
+bool ascend # i: Sort in ascending order
+bool casesens # i: Sort is case sensitive
+pointer tp # i: Table descriptor
+int numptr # i: Number of columns to sort on
+pointer colptr[ARB] # i: Array of column descriptors
+int nindex # i: Number of elements in index array
+int index[ARB] # io: Array of row indices to sort
+#--
+bool fold
+int idx, jdx, temp
+
+begin
+ # Call the sort routine in the table library
+
+ fold = ! casesens
+ call tbtsrt (tp, numptr, colptr, fold, nindex, index)
+
+ # Reorder the index array if ascend is false
+
+ if (! ascend) {
+ idx = 1
+ jdx = nindex
+ while (idx < jdx) {
+ temp = index[idx]
+ index[idx] = index[jdx]
+ index[jdx] = temp
+ idx = idx + 1
+ jdx = jdx - 1
+ }
+ }
+
+
+end
diff --git a/pkg/utilities/nttools/lib/tblsort1.x b/pkg/utilities/nttools/lib/tblsort1.x
new file mode 100644
index 00000000..5d68751b
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tblsort1.x
@@ -0,0 +1,157 @@
+include "reloperr.h"
+
+.help tbl_sort1
+.nf____________________________________________________________________________
+
+This file contains two routines that sort a table on a single column. Both
+routines put an existing array of row indices into sorted order. The first
+routine, tbl_sort1 has a simpler interface and is the routine to be used in
+a majority of cases. The second routine, tbl_qsort, requires that the calling
+routine read the table column into an array and handle null elements by
+itself. This routine should be used if the table column requires some
+special preprocessing before it can be sorted. One example of required
+preprocessing is conversion of dates from character strings to julian dates.
+Both routines use quick sort to sort the data. Quick is one of the fastest
+sorting routines, but it cannot be used to sort several table columns because
+it is not stable. This means that one sort destroys the ordering of a previous
+sort on a different column.
+
+.endhelp_______________________________________________________________________
+
+# TBL_SORT1 -- Sort a table on a single column
+#
+# This procedure rearranges an array of row indices into sorted order. The
+# order is from smallest to largest value if ascend is true, if ascend is
+# false, the order is from largest to smallest. In either case undefined
+# elements will be last in the array. For purposes of this routine boolean
+# false is considered to be less than true. If character strings are being
+# sorted, case can be ignored by setting casesens to false. The array of row
+# indices must be created before calling this procedure.
+#
+# B.Simon 16-Sept-87 First Code
+# B.Simon 15-Dec-87 Changed to handle table subsets
+
+procedure tbl_sort1 (ascend, casesens, tp, cp, nindex, index)
+
+bool ascend # i: Sort in ascending order
+bool casesens # i: Sort is case sensitive
+pointer tp # i: Table descriptor
+pointer cp # i: Column descriptor
+int nindex # io: Number of rows
+int index[ARB] # io: Array of row pointers in sorted order
+#--
+int dtype, spptype, lendata
+int nary, iary, nelem
+pointer idxptr, nulptr, aryptr, curptr
+
+int movenulls()
+
+begin
+ # Allocate storage for index array
+
+ call malloc (idxptr, nindex, TY_INT)
+
+ # Initialize the array of row indices
+
+ call amovi (index, Memi[idxptr], nindex)
+
+ # Read in the column of table values. Setting dtype to
+ # zero gets the actual data type of the column
+
+ dtype = 0
+ call gettabcol (tp, cp, dtype, nary, aryptr, nulptr)
+
+ if (dtype < 0) {
+ lendata = - dtype
+ spptype = TY_CHAR
+
+ if (! casesens) {
+ curptr = aryptr
+ do iary = 1, nary {
+ call strupr (Memc[curptr])
+ curptr = curptr + lendata + 1
+ }
+ }
+ } else {
+ lendata = 1
+ spptype = dtype
+ }
+
+ # Move all null elements to the end of the array
+
+ nelem = movenulls (nindex, Memb[nulptr], Memi[idxptr])
+
+ # Perform an indirect sort on the row indices using quicksort
+
+ call tbl_qsort (ascend, dtype, aryptr, nelem, idxptr)
+
+ # Move the row indices into the output array
+
+ call amovi (Memi[idxptr], index, nindex)
+
+ call mfree (idxptr, TY_INT)
+ call mfree (nulptr, TY_BOOL)
+ call mfree (aryptr, spptype)
+
+end
+
+# TBL_QSORT -- Indirect quick sort of a table column using an index array
+
+procedure tbl_qsort (ascend, dtype, aryptr, nelem, idxptr)
+
+bool ascend # i: Sort array in ascending order
+int dtype # i: Data type of array to be sorted
+pointer aryptr # i: Pointer to array to be sorted
+int nelem # i: Number of elements to be sorted
+pointer idxptr # o: Pointer to array of indices
+
+include "compare.com"
+
+int spptype
+
+extern compascb, compascd, compasci, compascr, compasct
+extern compdscb, compdscd, compdsci, compdscr, compdsct
+
+begin
+ dataptr = aryptr
+
+ # Convert the type to the SPP format
+
+ if (dtype < 0) {
+ lendata = - dtype
+ spptype = TY_CHAR
+ } else {
+ lendata = 1
+ spptype = dtype
+ }
+
+ # Call the quick sort procedure with the proper comparison routine
+
+ switch (spptype) {
+ case TY_BOOL:
+ if (ascend)
+ call qsort (Memi[idxptr], nelem, compascb)
+ else
+ call qsort (Memi[idxptr], nelem, compdscb)
+ case TY_CHAR:
+ if (ascend)
+ call qsort (Memi[idxptr], nelem, compasct)
+ else
+ call qsort (Memi[idxptr], nelem, compdsct)
+ case TY_SHORT,TY_INT,TY_LONG:
+ if (ascend)
+ call qsort (Memi[idxptr], nelem, compasci)
+ else
+ call qsort (Memi[idxptr], nelem, compdsci)
+ case TY_REAL:
+ if (ascend)
+ call qsort (Memi[idxptr], nelem, compascr)
+ else
+ call qsort (Memi[idxptr], nelem, compdscr)
+ case TY_DOUBLE:
+ if (ascend)
+ call qsort (Memi[idxptr], nelem, compascd)
+ else
+ call qsort (Memi[idxptr], nelem, compdscd)
+ }
+end
diff --git a/pkg/utilities/nttools/lib/tblsortm.x b/pkg/utilities/nttools/lib/tblsortm.x
new file mode 100644
index 00000000..1ece995d
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tblsortm.x
@@ -0,0 +1,168 @@
+include "reloperr.h"
+
+.help tbl_sortm
+.nf____________________________________________________________________________
+
+This file contains two routines that sort a table on multiple columns. Both
+routines put an existing array of row indices into sorted order. The first
+routine, tbl_sortm has a simpler interface and is the routine to be used in
+a majority of cases. The second routine, tbl_msort, requires that the calling
+routine read the table column into an array and handle null elements by
+itself. This routine should be used if the table column requires some
+special preprocessing before it can be sorted. One example of required
+preprocessing is conversion of dates from character strings to julian dates.
+Both routines use merge sort to sort the data. Merge sort is fast, though not
+as fast as quick sort, and stable, so it can be used to sort on multiple
+columns. Its disadvantage is that it requires additional work space to run.
+
+.endhelp_______________________________________________________________________
+
+# TBL_SORTM -- Sort a table on multiple columns
+#
+# This procedure rearranges an array of row indices into sorted order. The
+# order is from smallest to largest value if ascend is true, if ascend is
+# false, the order is from largest to smallest. In either case undefined
+# elements will be last in the array. For purposes of this routine boolean
+# false is considered to be less than true. If character strings are being
+# sorted, case can be ignored by setting casesens to false. The array of row
+# indices must be created before calling this procedure.
+#
+# B.Simon 28-Sept-87 First Code
+# B.Simon 15-Dec-87 Changed to handle table subsets
+
+procedure tbl_sortm (ascend, casesens, tp, numptr, colptr, nindex, index)
+
+bool ascend # i: Sort in ascending order
+bool casesens # i: Sort is case sensitive
+pointer tp # i: Table descriptor
+int numptr # i: Number of columns to sort on
+pointer colptr[ARB] # i: Array of column descriptors
+int nindex # io: Number of rows
+int index[ARB] # io: Array of row indices in sorted order
+#--
+int dtype, spptype, lendata
+int iptr, nary, iary, nelem, nidx
+pointer cp, idxptr, nulptr, aryptr, curptr
+
+int movenulls()
+
+begin
+ # Allocate storage for index array
+
+ nidx = 2 * nindex
+ call malloc (idxptr, nidx, TY_INT)
+
+ # Initialize the array of row indices
+
+ call amovi (index, Memi[idxptr], nindex)
+
+ # Loop over all columns to be sorted
+
+ do iptr = numptr, 1, -1 {
+
+ cp = colptr(iptr)
+
+ # Read in the column of table values. Setting dtype to zero
+ # gets the actual column type.
+
+ dtype = 0
+ call gettabcol (tp, cp, dtype, nary, aryptr, nulptr)
+
+ if (dtype < 0) {
+ lendata = - dtype
+ spptype = TY_CHAR
+
+ if (! casesens) {
+ curptr = aryptr
+ do iary = 1, nary {
+ call strupr (Memc[curptr])
+ curptr = curptr + lendata + 1
+ }
+ }
+ } else {
+ lendata = 1
+ spptype = dtype
+ }
+
+ # Move all null elements to the end of the array
+
+ nelem = movenulls (nindex, Memb[nulptr], Memi[idxptr])
+
+ # Perform an indirect sort on the row indices using merge sort
+
+ call tbl_msort (ascend, dtype, aryptr, nelem, nidx, idxptr)
+
+ # Free memory used to hold table column and null flags
+
+ call mfree (aryptr, spptype)
+ call mfree (nulptr, TY_BOOL)
+ }
+
+ # Move the row indices into the output array
+
+ call amovi (Memi[idxptr], index, nindex)
+ call mfree (idxptr, TY_INT)
+
+end
+
+# TBL_MSORT -- Indirect merge sort of a table column using an index array
+
+procedure tbl_msort (ascend, dtype, aryptr, nelem, nidx, idxptr)
+
+bool ascend # i: Sort array in ascending order
+int dtype # i: Data type of array to be sorted
+pointer aryptr # i: Pointer to array to be sorted
+int nelem # i: Number of array elements to be sorted
+int nidx # i: Size of index array
+pointer idxptr # o: Pointer to array of indices
+
+include "compare.com"
+
+int spptype
+
+extern compascb, compascd, compasci, compascr, compasct
+extern compdscb, compdscd, compdsci, compdscr, compdsct
+
+begin
+ dataptr = aryptr
+
+ if (dtype < 0) {
+ lendata = - dtype
+ spptype = TY_CHAR
+ } else {
+ lendata = 1
+ spptype = dtype
+ }
+
+ # Convert the type to the SPP format
+
+ # Call the merge sort procedure with the proper comparison routine
+
+ switch (spptype) {
+ case TY_BOOL:
+ if (ascend)
+ call msort (Memi[idxptr], nidx, nelem, compascb)
+ else
+ call msort (Memi[idxptr], nidx, nelem, compdscb)
+ case TY_CHAR:
+ if (ascend)
+ call msort (Memi[idxptr], nidx, nelem, compasct)
+ else
+ call msort (Memi[idxptr], nidx, nelem, compdsct)
+ case TY_SHORT,TY_INT,TY_LONG:
+ if (ascend)
+ call msort (Memi[idxptr], nidx, nelem, compasci)
+ else
+ call msort (Memi[idxptr], nidx, nelem, compdsci)
+ case TY_REAL:
+ if (ascend)
+ call msort (Memi[idxptr], nidx, nelem, compascr)
+ else
+ call msort (Memi[idxptr], nidx, nelem, compdscr)
+ case TY_DOUBLE:
+ if (ascend)
+ call msort (Memi[idxptr], nidx, nelem, compascd)
+ else
+ call msort (Memi[idxptr], nidx, nelem, compdscd)
+ }
+end
diff --git a/pkg/utilities/nttools/lib/tblterm.com b/pkg/utilities/nttools/lib/tblterm.com
new file mode 100644
index 00000000..56d6c564
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tblterm.com
@@ -0,0 +1,7 @@
+int jumpbuf[LEN_JUMPBUF]
+common /jmpcom/ jumpbuf
+
+bool constant
+int nterm, irow, iterm
+pointer table
+common /opcom/ constant, nterm, irow, iterm, table
diff --git a/pkg/utilities/nttools/lib/tblterm.x b/pkg/utilities/nttools/lib/tblterm.x
new file mode 100644
index 00000000..65904221
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tblterm.x
@@ -0,0 +1,256 @@
+include <config.h>
+include <evexpr.h>
+include <tbset.h>
+include <xwhen.h>
+include "reloperr.h"
+
+define MAXTERM 64
+
+# TBL_TERM -- Return the value of the term in the expression
+#
+# B.Simon 13-Apr-88 Separated from tbl_eval
+
+procedure tbl_term (term, op)
+
+char term[ARB] # i: The name of the term
+pointer op # o: A structure holding the term value and type
+#--
+include "tblterm.com"
+
+bool isnull
+int datalen[MAXTERM], datatype[MAXTERM], dtype
+pointer colptr[MAXTERM]
+pointer sp, errtxt
+
+string badname "Column name not found (%s)"
+string badnum "Too many terms in expression"
+string nulvalue "Null found in table element"
+
+int tbcigi()
+
+errchk tbcfnd, tbcigi, tbegtb, tbegtt, tbegti, tbegtr
+
+begin
+ # Allocate storage for character strings
+
+ call smark (sp)
+ call salloc (errtxt, SZ_LINE, TY_CHAR)
+
+ constant = false
+ iterm = iterm + 1
+ if (iterm > MAXTERM)
+ call error (BOUNDS, badnum)
+
+ # If this is a new term, get its column pointer, type, and length
+
+ if (iterm > nterm) {
+ nterm = iterm
+ call tbcfnd (table, term, colptr[iterm], 1)
+
+ if (colptr[iterm] == NULL) {
+ call sprintf (Memc[errtxt], SZ_LINE, badname)
+ call pargstr (term)
+ call error (SYNTAX, Memc[errtxt])
+ }
+
+ dtype = tbcigi (colptr[iterm], TBL_COL_DATATYPE)
+ switch (dtype) {
+ case TY_BOOL:
+ datalen[iterm] = 0
+ datatype[iterm] = TY_BOOL
+ case TY_CHAR:
+ datalen[iterm] = 1
+ datatype[iterm] = TY_CHAR
+ case TY_SHORT,TY_INT,TY_LONG:
+ datalen[iterm] = 0
+ datatype[iterm] = TY_INT
+ case TY_REAL,TY_DOUBLE:
+ datalen[iterm] = 0
+ datatype[iterm] = TY_REAL
+ default:
+ datalen[iterm] = - dtype
+ datatype[iterm] = TY_CHAR
+ }
+ }
+
+ # Read the table to get the value of term
+
+ call xev_initop (op, datalen[iterm], datatype[iterm])
+
+ switch (datatype[iterm]) {
+ case TY_BOOL:
+ call tbegtb (table, colptr[iterm], irow, O_VALB(op))
+ isnull = false
+ case TY_CHAR:
+ call tbegtt (table, colptr[iterm], irow, O_VALC(op),
+ datalen[iterm])
+ isnull = O_VALC(op) == EOS
+ case TY_SHORT,TY_INT,TY_LONG:
+ call tbegti (table, colptr[iterm], irow, O_VALI(op))
+ isnull = IS_INDEFI (O_VALI(op))
+ case TY_REAL,TY_DOUBLE:
+ call tbegtr (table, colptr[iterm], irow, O_VALR(op))
+ isnull = IS_INDEFR (O_VALR(op))
+ }
+
+ # Error exit if table element is null
+
+ if (isnull)
+ call error (PUTNULL, nulvalue)
+
+ call sfree (sp)
+end
+
+# TBL_FUNC -- Return the value of a nonstandard function in the expression
+
+procedure tbl_func (func_name, arg_ptr, nargs, op)
+
+char func_name[ARB] # i: String containing function name
+pointer arg_ptr[ARB] # i: Pointers to function arguments
+int nargs # i: Number of function arguments
+pointer op # o: Pointer to output structure
+#--
+include "tblterm.com"
+
+bool valflag
+int type, iarg
+pointer sp, errtxt
+
+string badtyp "Invalid argument type in %s"
+string badarg "Incorrect number of arguments for %s"
+string badfun "Unknown function named %s"
+
+bool streq()
+double mjd()
+
+errchk mjd()
+
+begin
+ # Allocate storage for character strings
+
+ call smark (sp)
+ call salloc (errtxt, SZ_LINE, TY_CHAR)
+
+ # Call appropriate function according to name
+
+ if (streq (func_name, "row")) {
+
+ # Table row number function: row()
+
+ constant = false
+ if (nargs != 0) {
+ call sprintf (Memc[errtxt], SZ_LINE, badarg)
+ call pargstr (func_name)
+ call error (SYNTAX, Memc[errtxt])
+ }
+ call xev_initop (op, 0, TY_INT)
+ O_VALI(op) = irow
+
+ } else if (streq (func_name, "delta")) {
+
+ # Difference between two Julian dates: mjd(date1) - mjd(date2)
+
+ if (nargs != 2) {
+ call sprintf (Memc[errtxt], SZ_LINE, badarg)
+ call pargstr (func_name)
+ call error (SYNTAX, Memc[errtxt])
+ }
+ if (O_TYPE(arg_ptr[1]) != TY_CHAR ||
+ O_TYPE(arg_ptr[2]) != TY_CHAR ) {
+ call sprintf (Memc[errtxt], SZ_LINE, badtyp)
+ call pargstr (func_name)
+ call error (SYNTAX, Memc[errtxt])
+ }
+ call xev_initop (op, 0, TY_REAL)
+ O_VALR(op) = mjd (O_VALC(arg_ptr[1])) - mjd (O_VALC(arg_ptr[2]))
+
+ } else if (streq (func_name, "match")) {
+ if (nargs < 2) {
+ call sprintf (Memc[errtxt], SZ_LINE, badarg)
+ call pargstr (func_name)
+ call error (SYNTAX, Memc[errtxt])
+ }
+
+ type = O_TYPE(arg_ptr[1])
+ do iarg = 2, nargs {
+ if (type != O_TYPE(arg_ptr[iarg])) {
+ call sprintf (Memc[errtxt], SZ_LINE, badtyp)
+ call pargstr (func_name)
+ call error (SYNTAX, Memc[errtxt])
+ }
+ }
+
+ valflag = false
+ call xev_initop (op, 0, TY_BOOL)
+
+ switch (type) {
+ case TY_BOOL:
+ if (O_VALB(arg_ptr[1])) {
+ do iarg = 2, nargs {
+ if (O_VALB(arg_ptr[iarg])) {
+ valflag = true
+ break
+ }
+ }
+ } else {
+ do iarg = 2, nargs {
+ if (! O_VALB(arg_ptr[iarg])) {
+ valflag = true
+ break
+ }
+ }
+ }
+ case TY_CHAR:
+ do iarg = 2, nargs {
+ if (streq (O_VALC(arg_ptr[1]), O_VALC(arg_ptr[iarg]))) {
+ valflag = true
+ break
+ }
+ }
+ case TY_SHORT,TY_INT,TY_LONG:
+ do iarg = 2, nargs {
+ if (O_VALI(arg_ptr[1]) == O_VALI(arg_ptr[iarg])) {
+ valflag = true
+ break
+ }
+ }
+ case TY_REAL:
+ do iarg = 2, nargs {
+ if (O_VALR(arg_ptr[1]) == O_VALR(arg_ptr[iarg])) {
+ valflag = true
+ break
+ }
+ }
+ }
+ O_VALB(op) = valflag
+
+ } else {
+
+ call sprintf (Memc[errtxt], SZ_LINE, badfun)
+ call pargstr (func_name)
+ call error (SYNTAX, Memc[errtxt])
+
+ }
+
+ call sfree (sp)
+end
+
+# TBL_HANDLER -- Error handler to catch arithmetic errors
+
+procedure tbl_handler (code, nxt_handler)
+
+int code # i: error code which trigerred this exception
+int nxt_handler # o: handler called after this handler exits
+#--
+include "tblterm.com"
+
+bool junk
+bool xerpop()
+
+begin
+ # Resume execution at zsvjmp
+
+ nxt_handler = X_IGNORE
+ junk = xerpop()
+ call zdojmp (jumpbuf, code)
+end
diff --git a/pkg/utilities/nttools/lib/tctexp.x b/pkg/utilities/nttools/lib/tctexp.x
new file mode 100644
index 00000000..263b18bc
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tctexp.x
@@ -0,0 +1,442 @@
+include <tbset.h>
+include <ctype.h>
+include "reloperr.h"
+
+define MAX_STACK 8
+define BLANK ' '
+define DELIM ','
+define ESCAPE '\\'
+define NEGCHAR '~' # negation character
+define ALT_NEGCHAR '!' # alternate negation character
+
+.help tctexp
+.nf___________________________________________________________________________
+Column template package
+
+This package contains subroutines to expand a column name template into
+an array of column pointers which match the template. The template is a
+list of column patterns separated by commas or whitespace. The column
+pattern is either a column name, a file name containing a list of column
+names, or a pattern using the usual IRAF pattern matching syntax. For
+example, the string
+
+ a[1-9], b, time*, @column.lis
+
+would be expanded as the column names a1 through a9, b, any column name
+beginning with "time", and all the column names in the file column.lis.
+If the column template is entirely whitespace, the array of column pointers
+will include all the columns in the table, as this seems the most reasonable
+default. If the first non-white character is the negation character (~),
+the array of column pointers will include all columns not matched by the
+template. The negation character only has this meaning as the first character
+in the column template, and is interpreted as part of a column name if
+found later in the template or in a file.
+
+.endhelp______________________________________________________________________
+
+# TCTEXP -- Expand a column template into an array of column pointers
+#
+# Given a table pointed to by a table descriptor and a column name template,
+# return an array of column pointers. The size of the column pointer array
+# is given by numcol and should be greater than or equal to the number of
+# columns in the table. The actual number of columns found that match the
+# template is returned as numptr.
+#
+# B.Simon 24-Jul-1987 First Code
+# Phil Hodge 1-Jun-1989 make search for columns case insensitive
+# Phil Hodge 28-Jan-1999 add ! as an alternate negation character
+
+procedure tctexp (tp, template, numcol, numptr, colptr)
+
+pointer tp # i: pointer to table descriptor
+char template[ARB] # i: column template
+int numcol # i: size of column pointer array
+int numptr # o: number of columns matched
+pointer colptr[ARB] # o: array of column pointers
+#--
+
+bool nometa # true if pattern does not contain metacharacters
+bool negate # true if template starts with negation character
+
+int fd_ptr # pointer to stack of open list file descriptors
+int ic # first non-white character in template
+
+pointer fd_stack[MAX_STACK]
+ # stack of file descriptors for open list files
+
+pointer sp, colpat, pattern, auxcol, fd
+
+string stkovflerr "List files are nested too deeply, stack overflow"
+
+int strlen(), tctgetpat()
+pointer stropen(), open()
+
+errchk salloc, stropen, open, close
+errchk tctgetpat, tctmakpat, tctstrmatch, tctpatmatch
+
+begin
+ numptr = 0
+ negate = false
+
+ call smark (sp)
+ call salloc (colpat, SZ_FNAME, TY_CHAR)
+ call salloc (pattern, SZ_FNAME, TY_CHAR)
+
+ # Check the column name template to find the first non-white character.
+
+ for (ic = 1; IS_WHITE (template[ic]); ic = ic + 1)
+ ;
+
+ if (template[ic] == EOS) {
+
+ # If the template is blank, include all columns in the array
+
+ call allcolumns (tp, numptr, auxcol)
+ call amovi (Memi[auxcol], colptr, numptr)
+ call mfree (auxcol, TY_INT)
+ fd_ptr = 0
+
+ } else {
+
+ # If the first non-white character is the negation character
+ # (either ~ or !), the meaning of the column name template is
+ # negated, that is, the array of column pointers will include
+ # those columns whose names were not matched by the column template
+
+ if (template[ic] == NEGCHAR || template[ic] == ALT_NEGCHAR) {
+ negate = true
+ ic = ic + 1
+ }
+
+ # Open the column name template as a file and push on
+ # the list file stack
+
+ fd_ptr = 1
+ fd_stack[1] =
+ stropen (template[ic], strlen(template[ic]), READ_ONLY)
+
+ }
+
+ while (fd_ptr > 0) {
+
+ # Pop file descriptor off of the list file stack
+
+ fd = fd_stack[fd_ptr]
+ fd_ptr = fd_ptr - 1
+
+ # Loop over all column patterns in the file
+
+ while (tctgetpat (fd, Memc[colpat], SZ_FNAME) > 0) {
+
+ if (Memc[colpat] == '@') {
+
+ # If this pattern is a list file name, push the
+ # current descriptor on the stack and open the file
+
+ if (fd_ptr == MAX_STACK)
+ call error (BOUNDS, stkovflerr)
+ fd_ptr = fd_ptr + 1
+ fd_stack[fd_ptr] = fd
+ fd = open (Memc[colpat+1], READ_ONLY, TEXT_FILE)
+
+ } else {
+
+ # Otherwise, encode the pattern and search the table
+ # for matching column names. To speed the search, use
+ # a special routine if the pattern does not include
+ # metacharacters
+
+ call strlwr (Memc[colpat]) # for case insensitivity
+ call tctmakpat (Memc[colpat], Memc[pattern], SZ_FNAME,
+ nometa)
+ if (nometa)
+ call tctstrmatch (tp, Memc[pattern], numcol,
+ numptr, colptr)
+ else
+ call tctpatmatch (tp, Memc[pattern], numcol,
+ numptr, colptr)
+ }
+ }
+ call close (fd)
+ }
+
+ if (negate)
+ call invert (tp, numptr, colptr)
+
+ call sfree (sp)
+end
+
+# TCTGETPAT -- Get next comma or whitespace delimeted pattern from file
+#
+# Copy characters into colpat until a field delimeter or the maximum number of
+# characters is reached. The number of characters in colpat is returned as the
+# value of the function, so the procedure which calls this one can test for
+# the last field in the template.
+#
+# B. Simon 24-Jul-87 First Code
+
+int procedure tctgetpat (fd, colpat, maxch)
+
+pointer fd # i: template file descriptor
+char colpat[ARB] # o: pattern from column name template
+int maxch # i: maximum number of characters in field
+#--
+char ch # next character from template
+int iq # pointer to character in colpat
+
+char getc()
+
+begin
+ # Skip leading whitespace or commas
+
+ ch = getc (fd, ch)
+ while (IS_CNTRL(ch) || ch == BLANK || ch == DELIM)
+ ch = getc (fd, ch)
+
+ # Copy characters to colpat. End when maxch is reached, or
+ # when comma, whitespace, or EOF is found
+
+ for (iq = 1; iq <= maxch; iq = iq + 1) {
+
+ if (IS_CNTRL(ch) || ch == BLANK || ch == DELIM || ch == EOF)
+ break
+
+ colpat[iq] = ch
+ ch = getc (fd, ch)
+ }
+ colpat[iq] = EOS
+
+ # If loop is terminated because of maxch, eat remaining characters
+ # in field
+
+ while (! IS_CNTRL(ch) && ch != BLANK && ch != DELIM && ch != EOF)
+ ch = getc (fd, ch)
+
+ # Return number of characters in colpat
+
+ return (iq-1)
+end
+
+# TCTMAKPAT -- Encode the column pattern
+#
+# Create the pattern used by the matching routines. Check for metacharacters
+# (unescaped pattern matching characters) to see if the faster constant
+# pattern routine can be used.
+#
+# B.Simon 24-Jul-87 First Code
+
+procedure tctmakpat (colpat, pattern, maxch, nometa)
+
+char colpat[ARB] # i: Column pattern string
+char pattern[ARB] # o: Encoded pattern string
+int maxch # i: Maximum length of encoded pattern string
+bool nometa # o: True if no metacharacters in string
+#--
+int ic, ip
+pointer sp, buffer, buffer2, errtxt, ib
+
+int stridx(), strlen(), patmake()
+
+string patovflerr "Column pattern too long (%s)"
+string badpaterr "Column pattern has bad syntax (%s)"
+
+begin
+ call smark (sp)
+ call salloc (buffer, maxch, TY_CHAR)
+ call salloc (buffer2, maxch, TY_CHAR)
+ call salloc (errtxt, SZ_LINE, TY_CHAR)
+
+ nometa = true
+ ib = buffer
+
+ # Copy the column pattern to a temporary buffer
+
+ for (ic = 1; colpat[ic] != EOS ; ic = ic + 1) {
+
+ # Copy escape sequences, but do not count as metacharacters
+
+ if (colpat[ic] == ESCAPE && colpat[ic+1] != EOS) {
+ Memc[ib] = ESCAPE
+ ib = ib + 1
+ ic = ic + 1
+
+ # Covert '*' to '?*', count as a metacharacter
+
+ } else if (colpat[ic] == '*') {
+ nometa = false
+ Memc[ib] = '?'
+ ib = ib + 1
+
+ # Check for the other metacharacters
+
+ } else if (stridx (colpat[ic], "[?{") > 0)
+ nometa = false
+
+ Memc[ib] = colpat[ic]
+ ib = ib + 1
+ }
+ Memc[ib] = EOS
+
+ # Check the buffer length against maximum pattern length
+
+ if (strlen (Memc[buffer]) > maxch) {
+ call sprintf (Memc[errtxt], SZ_LINE, patovflerr)
+ call pargstr (colpat)
+ call error (BOUNDS, Memc[errtxt])
+ }
+
+ # If no metacharacters, strip escape sequences
+
+ if (nometa) {
+ ip = 1
+ for (ib = buffer; Memc[ib] != EOS; ib = ib + 1) {
+ if (Memc[ib] == ESCAPE && Memc[ib+1] != EOS)
+ ib = ib + 1
+ pattern[ip] = Memc[ib]
+ ip = ip + 1
+ }
+ pattern[ip] = EOS
+
+ # Otherwise, encode with patmake
+
+ } else {
+ call sprintf (Memc[buffer2], maxch, "^%s$")
+ call pargstr (Memc[buffer])
+
+ if (patmake (Memc[buffer2], pattern, SZ_LINE) == ERR) {
+ call sprintf (Memc[errtxt], SZ_LINE, badpaterr)
+ call pargstr (colpat)
+ call error (SYNTAX, Memc[errtxt])
+ }
+ }
+
+ call sfree (sp)
+end
+
+# TCTSTRMATCH -- Add a column pointer for a column name to the array
+#
+# Used to match column names when the column pattern contains no
+# metacharacters.
+#
+# B. Simon 24-Jul-87 First Code
+
+procedure tctstrmatch (tp, pattern, numcol, numptr, colptr)
+
+pointer tp # i: pointer to table descriptor
+char pattern[ARB] # i: column pattern
+int numcol # i: size of column pointer array
+int numptr # o: number of columns matched
+pointer colptr[ARB] # o: array of column pointers
+#--
+int iptr
+pointer sp, errtxt, cp
+
+string maxcolerr "Maximum number of columns in table exceeded (%d)"
+
+errchk tbcfnd
+
+begin
+ call smark (sp)
+ call salloc (errtxt, SZ_LINE, TY_CHAR)
+
+ # Find the column pointer corresponding to the column name
+
+ call tbcfnd (tp, pattern, cp, 1)
+
+ # Pointer is null if column not found in table
+
+ if (cp == NULL)
+ return
+
+ # See if the column name has already been matched
+
+ for (iptr = 1; iptr <= numptr; iptr = iptr +1) {
+ if (cp == colptr[iptr])
+ break
+ }
+
+ # If not, add its pointer in the array of pointers
+ # after checking for array overflow
+
+ if (iptr > numptr) {
+ if (numptr >= numcol) {
+ call sprintf (Memc[errtxt], SZ_LINE, maxcolerr)
+ call pargi (numcol)
+ call error (BOUNDS, Memc[errtxt])
+ }
+ numptr = numptr + 1
+ colptr[numptr] = cp
+ }
+
+ call sfree (sp)
+end
+
+# TCTPATMATCH -- Find column pointers for columns matching a pattern
+#
+# This routine is called when the column pattern contains metacharacters.
+#
+# B.Simon 27-Jul-87 First Code
+
+procedure tctpatmatch (tp, pattern, numcol, numptr, colptr)
+
+pointer tp # i: pointer to table descriptor
+char pattern[ARB] # i: column pattern
+int numcol # i: size of column pointer array
+int numptr # o: number of columns matched
+pointer colptr[ARB] # o: array of column pointers
+#--
+int maxcol, icol, iptr
+pointer sp, errtxt, cp
+pointer colname
+
+string maxcolerr "Maximum number of columns in table exceeded (%d)"
+
+int tbpsta(), tbcnum(), patmatch()
+
+errchk tbpsta, tbcnum, tbcinf, patmatch
+
+begin
+ call smark (sp)
+ call salloc (colname, SZ_COLNAME, TY_CHAR)
+ call salloc (errtxt, SZ_LINE, TY_CHAR)
+
+ maxcol = tbpsta (tp, TBL_NCOLS)
+
+ # Compare the column pattern to each column name in the table
+
+ do icol = 1, maxcol {
+
+ # Get the next column name in the table
+
+ cp = tbcnum (tp, icol)
+ call tbcigt (cp, TBL_COL_NAME, Memc[colname], SZ_COLNAME)
+ call strlwr (Memc[colname]) # for case insensitivity
+
+ # Check the column name for a match
+
+ if (patmatch (Memc[colname], pattern) > 0) {
+ # See if the column name has already been matched
+
+ for (iptr = 1; iptr <= numptr; iptr = iptr +1) {
+ if (cp == colptr[iptr])
+ break
+ }
+
+ # If not, add its pointer in the array of pointers
+ # after checking for array overflow
+
+ if (iptr > numptr) {
+ if (numptr >= numcol) {
+ call sprintf (Memc[errtxt], SZ_LINE, maxcolerr)
+ call pargi (numcol)
+ call error (BOUNDS, Memc[errtxt])
+ }
+ numptr = numptr + 1
+ colptr[numptr] = cp
+ }
+ }
+ }
+
+ call sfree (sp)
+
+end
diff --git a/pkg/utilities/nttools/lib/tldtype.x b/pkg/utilities/nttools/lib/tldtype.x
new file mode 100644
index 00000000..52e35960
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tldtype.x
@@ -0,0 +1,70 @@
+define T_MAXDIM 7 # maximum dimension of array
+
+# tl_dtype -- data type and array size
+# Convert integer data type code to a character string. If the column
+# contains arrays, append the length of each axis, e.g. R[25,75].
+#
+# Phil Hodge, 9-Dec-1994 Moved from tlcol.x to ttools$lib/.
+# Phil Hodge, 19-Jul-1995 Add tp to calling sequence (needed for tbciga).
+
+procedure tl_dtype (tp, cp, datatype, nelem, chartyp, maxch)
+
+pointer tp # i: pointer to table descriptor
+pointer cp # i: pointer to column descriptor
+int datatype # i: integer code for data type
+int nelem # i: total array size
+char chartyp[maxch] # o: data type, possibly with array size
+int maxch # i: maximum size of chartyp string
+#--
+int nchar # number of characters
+int i # loop index
+int ndim # dimension of array
+int axlen[T_MAXDIM] # length of each axis
+int ip, itoc()
+int strlen()
+errchk tbciga
+
+begin
+ if (datatype > 0) { # numeric or Boolean
+
+ switch (datatype) {
+ case TY_REAL:
+ call strcpy ("R", chartyp, maxch)
+ case TY_DOUBLE:
+ call strcpy ("D", chartyp, maxch)
+ case TY_INT:
+ call strcpy ("I", chartyp, maxch)
+ case TY_SHORT:
+ call strcpy ("S", chartyp, maxch)
+ case TY_BOOL:
+ call strcpy ("B", chartyp, maxch)
+ default:
+ call error (1, "bad data type in table")
+ }
+
+ } else { # < 0 ==> char string
+
+ nchar = -datatype # length of string
+ call sprintf (chartyp, maxch, "CH*%d")
+ call pargi (nchar)
+ }
+
+ if (nelem > 1) {
+
+ # Get the dimension of array and size of each axis.
+ call tbciga (tp, cp, ndim, axlen, T_MAXDIM)
+
+ call strcat ("[", chartyp, maxch)
+
+ ip = strlen (chartyp) + 1 # points to EOS
+
+ do i = 1, ndim-1 {
+ nchar = itoc (axlen[i], chartyp[ip], maxch-ip+1)
+ call strcat (",", chartyp, maxch)
+ ip = ip + nchar + 1
+ }
+
+ nchar = itoc (axlen[ndim], chartyp[ip], maxch-ip+1)
+ call strcat ("]", chartyp, maxch)
+ }
+end
diff --git a/pkg/utilities/nttools/lib/tuopen.x b/pkg/utilities/nttools/lib/tuopen.x
new file mode 100644
index 00000000..7434a72b
--- /dev/null
+++ b/pkg/utilities/nttools/lib/tuopen.x
@@ -0,0 +1,197 @@
+# This file contains tu_open and tu_close, which are used to open
+# and close a temporary table.
+#
+# Phil Hodge, 28-Jun-1995 Subroutines created based on Bernie's tedit code.
+# Phil hodge, 16-Apr-1999 Remove ttype from calling sequence of tbparse.
+
+# tu_open -- open a temporary table
+# If the table is to be opened in-place, then it's just opened.
+# Otherwise, a copy of the table is made, and that table is opened
+# read-write. The name of the original table will be returned as
+# 'tabname' so it can be passed to tu_close, which needs the name of
+# the original file. If the filename extension is ".tab", then 'table'
+# might not include the extension. This is the reason we need a
+# separate output argument 'tabname'. Note, however, that text tables
+# need not have an extension. If 'table' does not include an extension,
+# and a file of that name exists, then ".tab" will not be appended when
+# copying to 'tabname'. 'tabname' can differ from the actual file name
+# by including a name or number in brackets after the file name.
+#
+# Note that it is an error if readonly=true but inplace=false.
+
+procedure tu_open (table, root, readonly, inplace, tp, tabname, maxch)
+
+char table[ARB] # i: name of table
+char root[ARB] # i: beginning of name for scratch file
+bool readonly # i: true if the table is to be opened read-only
+bool inplace # i: true if the table is to be opened in-place
+pointer tp # o: pointer to table struct
+char tabname[maxch] # o: full name of original table (incl extension, etc)
+int maxch # i: size of filename string
+#--
+pointer sp
+pointer temp # name of temporary table
+pointer tname, fname # full table and file names
+pointer extn # file extension, or EOS
+pointer tempdir # name of directory for temporary copy
+pointer errmess # scratch for error message
+int tlen, flen # length of table and file names
+int try # loop index
+int junk, fnldir()
+pointer tbtopn()
+int strlen()
+errchk tbtopn, tbtnam, tbfile, fcopy
+
+begin
+ if (readonly && !inplace)
+ call error (1, "readonly = yes, but inplace = no")
+
+ if (inplace) {
+
+ if (readonly) {
+ tp = tbtopn (table, READ_ONLY, NULL)
+ } else {
+ tp = tbtopn (table, READ_WRITE, NULL)
+ }
+
+ call tbtnam (tp, tabname, maxch) # get the full table name
+
+ } else {
+
+ call smark (sp)
+ call salloc (temp, SZ_LINE, TY_CHAR)
+ call salloc (tname, SZ_LINE, TY_CHAR)
+ call salloc (fname, SZ_LINE, TY_CHAR)
+ call salloc (extn, SZ_LINE, TY_CHAR)
+ call salloc (tempdir, SZ_LINE, TY_CHAR)
+
+ # Get the full table name, full file name, and extension (if any)
+ # of the original file. Copy the table name to output.
+ call tbfile (table, Memc[tname], Memc[fname], Memc[extn], SZ_LINE)
+ call strcpy (Memc[tname], tabname, maxch)
+
+ # Get the name of the directory containing the original file.
+ junk = fnldir (Memc[fname], Memc[tempdir], SZ_LINE)
+
+ # Copy the original file to a temporary file. First try to
+ # make the copy in the directory containing the original file.
+ # If that fails then copy the file to tmp$.
+ do try = 1, 2 {
+
+ # Construct the name of a temporary file by concatenating
+ # the directory, root, a random number, and the extension
+ # of the original file name.
+ call strcat (root, Memc[tempdir], SZ_LINE)
+ call mktemp (Memc[tempdir], Memc[temp], SZ_LINE)
+ call strcat (Memc[extn], Memc[temp], SZ_LINE)
+
+ # Copy the file.
+ ifnoerr (call fcopy (Memc[fname], Memc[temp]))
+ break
+
+ if (try == 1) {
+ # The first try failed. Copy the file to tmp$.
+ call strcpy ("tmp$", Memc[tempdir], SZ_LINE)
+ } else {
+ # The second try failed as well.
+ call salloc (errmess, SZ_LINE, TY_CHAR)
+ call sprintf (Memc[errmess], SZ_LINE,
+ "unable to make a temporary copy of %s")
+ call pargstr (Memc[fname])
+ call error (1, Memc[errmess])
+ }
+ }
+
+ # If there was a bracketed expression (e.g. EXTNAME) in the
+ # input table name, append it to the name of the temp file
+ # to convert the file name to a complete table name.
+ tlen = strlen (Memc[tname])
+ flen = strlen (Memc[fname])
+ if (tlen > flen)
+ call strcat (Memc[tname+flen], Memc[temp], SZ_LINE)
+
+ tp = tbtopn (Memc[temp], READ_WRITE, NULL)
+
+ call sfree (sp)
+ }
+end
+
+# tu_close -- close a temporary table
+# This routine first closes the table that was edited. If it was opened
+# inplace, then we have nothing further to do. Otherwise, we were editing
+# a temporary copy of the original. If the command was to quit without
+# saving changes, we delete the temporary file. If the command was to
+# exit, saving changes, we rename the copy back to the original.
+# The quit and tabname arguments will be ignored if inplace is true.
+
+procedure tu_close (tp, inplace, quit, tabname)
+
+pointer tp # i: pointer to table struct for edited table
+bool inplace # i: true if the table was edited inplace
+bool quit # i: true if we should quit without saving changes
+char tabname[ARB] # i: name of original table (not temp copy)
+#--
+pointer sp
+pointer temp # name of temporary file
+pointer tname # name of temporary table
+pointer filename # name of original file
+pointer cdfname # scratch
+pointer errmess # for error message
+int hdu # ignored
+int junk
+int errget()
+int tbparse()
+errchk tbparse, delete, rename
+
+begin
+ if (tp == NULL)
+ return
+
+ call smark (sp)
+ call salloc (tname, SZ_LINE, TY_CHAR)
+
+ # Get the name of the table that we edited, then close it.
+ call tbtnam (tp, Memc[tname], SZ_LINE)
+ call tbtclo (tp)
+
+ if (!inplace) {
+
+ call salloc (temp, SZ_LINE, TY_CHAR)
+ call salloc (filename, SZ_LINE, TY_CHAR)
+ call salloc (cdfname, SZ_LINE, TY_CHAR)
+
+ # Strip off brackets (if present) to get the file name
+ # for the table that we edited.
+ junk = tbparse (Memc[tname], Memc[temp],
+ Memc[cdfname], SZ_LINE, hdu)
+
+ # Strip off brackets (if present) to get the file name
+ # of the original table.
+ junk = tbparse (tabname, Memc[filename],
+ Memc[cdfname], SZ_LINE, hdu)
+
+ if (quit) {
+
+ call delete (Memc[temp]) # delete temp copy
+
+ } else {
+
+ iferr {
+ call delete (Memc[filename]) # delete original file
+ call rename (Memc[temp], Memc[filename])
+ } then {
+ call salloc (errmess, SZ_LINE, TY_CHAR)
+ junk = errget (Memc[errmess], SZ_LINE)
+ call eprintf ("%s\n")
+ call pargstr (Memc[errmess])
+ call sprintf (Memc[errmess], SZ_LINE,
+ "couldn't rename edited file %s to original %s\n")
+ call pargstr (Memc[temp])
+ call pargstr (Memc[filename])
+ call error (1, Memc[errmess])
+ }
+ }
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/utilities/nttools/lib/unique.x b/pkg/utilities/nttools/lib/unique.x
new file mode 100644
index 00000000..ca54f840
--- /dev/null
+++ b/pkg/utilities/nttools/lib/unique.x
@@ -0,0 +1,64 @@
+# UNIQUE -- Find unique rows in a table
+#
+# First, the table is sorted on columns input in the colptr array. The
+# results are stored in the index array. Then each row in the index array
+# is compared to the most recent unique row in the index array, column by
+# column. If any column differs, the row is also considered to be unique.
+# The index array is updated to reflect the new unique row and the number of
+# unique rows is incremented.
+#
+# B.Simon 19-Oct-87 First Code
+# B.Simon 14-Dec-87 Changed to handle table subsets
+# B.Simon 06-Feb-90 Changed to use tbtsrt and tbrcmp
+
+procedure unique (tp, numptr, colptr, nindex, index)
+
+pointer tp # i: Table descriptor
+int numptr # i: Number of column pointers
+pointer colptr[ARB] # i: Array of column pointers
+int nindex # io: Number of unique row indices returned
+int index[ARB] # io: Array of unique indices
+#--
+bool fold
+int order, idx, jdx, n, i
+
+int tbrcmp()
+
+begin
+
+ # Sort the array on the selected columns. The sort is in ascending
+ # order and case sensitive
+
+ fold = false
+ call tbtsrt (tp, numptr, colptr, fold, nindex, index)
+
+ # Search for unique rows
+
+ jdx = 0
+ n = nindex
+ nindex = 0
+
+ do i = 1, n {
+ idx = index[i]
+
+ # First row is always unique
+
+ if (i == 1)
+ order = 1
+ else
+ order = tbrcmp (tp, numptr, colptr, fold, idx, jdx)
+
+ # Update pointer to most recent unique row and modify index
+ # array in place
+
+ if (order != 0) {
+ jdx = idx
+ nindex = nindex + 1
+ index[nindex] = idx
+ }
+ }
+
+ do i = nindex+1, n
+ index[i] = 0
+
+end