aboutsummaryrefslogtreecommitdiff
path: root/pkg/utilities/nttools/texpand
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/utilities/nttools/texpand')
-rw-r--r--pkg/utilities/nttools/texpand/dbgrules.x164
-rw-r--r--pkg/utilities/nttools/texpand/lexer.x114
-rw-r--r--pkg/utilities/nttools/texpand/lexoper.h29
-rw-r--r--pkg/utilities/nttools/texpand/mkpkg21
-rw-r--r--pkg/utilities/nttools/texpand/mkrules.x48
-rw-r--r--pkg/utilities/nttools/texpand/movelem.x113
-rw-r--r--pkg/utilities/nttools/texpand/movtbrow.x43
-rw-r--r--pkg/utilities/nttools/texpand/parser.com6
-rw-r--r--pkg/utilities/nttools/texpand/parser.x283
-rw-r--r--pkg/utilities/nttools/texpand/pushstack.x226
-rw-r--r--pkg/utilities/nttools/texpand/span.x97
-rw-r--r--pkg/utilities/nttools/texpand/texpand.x94
-rw-r--r--pkg/utilities/nttools/texpand/userules.x286
-rw-r--r--pkg/utilities/nttools/texpand/x_texpand.x3
14 files changed, 1527 insertions, 0 deletions
diff --git a/pkg/utilities/nttools/texpand/dbgrules.x b/pkg/utilities/nttools/texpand/dbgrules.x
new file mode 100644
index 00000000..eeffb8e0
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/dbgrules.x
@@ -0,0 +1,164 @@
+include <tbset.h>
+
+define INT_DEFLEN 10
+define REAL_DEFLEN 14
+define DBL_DEFLEN 24
+
+# DBGRULES -- Write the non-null rows in a table to a debug file
+#
+# B.Simon 25-Apr-88 Original
+
+procedure dbg_rules (tp, title, row1, row2, dbg)
+
+pointer tp # i: Table descriptor
+char title[ARB] # i: Title to print above table
+int row1 # i: First row to print
+int row2 # i: Last row to print
+int dbg # i: File descriptor of debug file
+#--
+bool nullflg
+double dblval
+int pwidth, ncol, irow, icol, jcol, collen, totlen, intval
+pointer sp, col,strval, colname, colptr, typptr, lenptr
+real realval
+
+int tbpsta(), tbcnum(), tbcigi(), envgeti(), strlen()
+
+begin
+ # First, make sure there is something to print
+
+ if (row2 < row1 || dbg == NULL)
+ return
+
+ # Allocate dynamic memory for strings
+
+ call smark (sp)
+ call salloc (strval, SZ_LINE, TY_CHAR)
+ call salloc (colname, SZ_COLNAME, TY_CHAR)
+
+ # Allocate dynamic memory for column arrays
+
+ ncol = tbpsta (tp, TBL_NCOLS)
+ call salloc (typptr, ncol, TY_INT)
+ call salloc (colptr, ncol, TY_INT)
+ call salloc (lenptr, ncol, TY_INT)
+
+ # Get width of terminal screen
+
+ pwidth = envgeti ("ttyncols")
+
+ # Print title
+
+ call fprintf (dbg, "%s\n")
+ call pargstr (title)
+
+ # Compute width of each column in output
+
+ jcol = 0
+ totlen = 0
+ do icol = 1, ncol {
+
+ # Check to see if this column is excluded from the output
+
+ col = tbcnum (tp, icol)
+ call tbrgtt (tp, col, Memc[strval], nullflg, SZ_LINE, 1, row1)
+
+ if (row1 != row2 || ! nullflg) {
+ jcol = jcol + 1
+
+ call tbcigt (col, TBL_COL_NAME, Memc[colname], SZ_COLNAME)
+ Memi[colptr+jcol-1] = col
+ Memi[typptr+jcol-1] = tbcigi (col, TBL_COL_DATATYPE)
+
+ # Set column width to default for its type
+
+ switch (Memi[typptr+jcol-1]) {
+ case TY_SHORT, TY_INT, TY_LONG:
+ collen = INT_DEFLEN
+ case TY_REAL:
+ collen = REAL_DEFLEN
+ case TY_DOUBLE:
+ collen = DBL_DEFLEN
+ default:
+ collen = - Memi[typptr+jcol-1]
+ }
+
+ # Adjust width to allow room for column titles
+
+ collen = max (collen, strlen (Memc[colname]))
+ totlen = totlen + collen + 1
+
+ # Write the column titles
+
+ if (jcol > 1 && totlen > pwidth)
+ call fprintf (dbg, "\n")
+
+ if (Memi[typptr+jcol-1] > 0) {
+ call fprintf (dbg, " %*s")
+ call pargi (collen)
+ } else {
+ call fprintf (dbg, " %*s")
+ call pargi (-collen)
+ }
+ call strupr (Memc[colname])
+ call pargstr (Memc[colname])
+
+ # Set sign to indicate start of new line
+
+ if (jcol > 1 && totlen > pwidth) {
+ totlen = collen + 1
+ Memi[lenptr+jcol-2] = - Memi[lenptr+jcol-2]
+ }
+
+ Memi[lenptr+jcol-1] = collen
+ }
+ }
+
+ # Recompute number of columns and force newline at end of title row
+
+ ncol = jcol
+ if (ncol > 0)
+ Memi[lenptr+ncol-1] = - Memi[lenptr+ncol-1]
+ call fprintf (dbg, "\n")
+
+ # Read the data from the database and write the data to STDOUT
+
+ do irow = row1, row2 {
+ do jcol = 1, ncol {
+
+ col = Memi[colptr+jcol-1]
+ collen = abs (Memi[lenptr+jcol-1])
+
+ switch(Memi[typptr+jcol-1]) {
+ case TY_SHORT, TY_INT, TY_LONG:
+ call tbegti (tp, col, irow, intval)
+ call fprintf (dbg, " %*d")
+ call pargi (collen)
+ call pargi (intval)
+ case TY_REAL:
+ call tbegtr (tp, col, irow, realval)
+ call fprintf (dbg, " %*.7g")
+ call pargi (collen)
+ call pargr (realval)
+ case TY_DOUBLE:
+ call tbegtd (tp, col, irow, dblval)
+ call fprintf (dbg, " %*.16g")
+ call pargi (collen)
+ call pargd (dblval)
+ default:
+ call tbegtt (tp, col, irow, Memc[strval], SZ_LINE)
+ call fprintf (dbg, " %*s")
+ call pargi (-collen)
+ call pargstr (Memc[strval])
+ }
+
+ if (Memi[lenptr+jcol-1] < 0)
+ call fprintf (dbg, "\n")
+
+ }
+ }
+
+ call fprintf (dbg, "\n\n")
+ call sfree (sp)
+
+end
diff --git a/pkg/utilities/nttools/texpand/lexer.x b/pkg/utilities/nttools/texpand/lexer.x
new file mode 100644
index 00000000..cb4d5d7c
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/lexer.x
@@ -0,0 +1,114 @@
+include "lexoper.h"
+
+define start_ 90
+
+# LEXER -- Lexically analyze a rule base
+#
+# B.Simon 25-Apr-88 Original
+
+procedure lexer (rb, oper, value, maxch)
+
+pointer rb # i: Pointer to descriptor of rule base
+int oper # o: Operator type found
+char value[ARB] # o: Text of operator
+int maxch # i: Maximum length of string
+#--
+char dic_text[2]
+int junk, old_index, dic_index, dic_oper[5]
+pointer sp, ch, blanks
+
+data dic_oper /SEPOPR, IMPOPR, OROPR, ANDOPR, EQOPR/
+string dict "/;/=>/||/&&/=/"
+
+bool streq()
+int getline(), ctowrd(), span(), nospan(), strdic()
+
+begin
+ # Allocate an array to hold whitespace
+
+ call smark (sp)
+ call salloc (blanks, SZ_LINE, TY_CHAR)
+
+ # Skip over leading whitespace
+
+start_ junk = span (" \t", RB_LINE(rb), RB_INDEX(rb), Memc[blanks], SZ_LINE)
+
+ # Branch on first non-white character
+
+ ch = RB_CHARPTR(rb)
+
+ # End of line or beginning of comment
+
+ if (Memc[ch] == '\n' || Memc[ch] == '#' || Memc[ch] == EOS) {
+ if (getline (RB_FILE(rb), RB_LINE(rb)) == EOF) {
+ oper = ENDOPR
+ value[1] = EOS
+ } else {
+ RB_NLINE(rb) = RB_NLINE(rb) + 1
+ RB_INDEX(rb) = 1
+ goto start_
+ }
+
+ # Quoted identifier
+
+ } else if (Memc[ch] == '\'' || Memc[ch] == '"') {
+ junk = ctowrd (RB_LINE(rb), RB_INDEX(rb), value, maxch)
+ oper = IDOPR
+
+ # Unquoted identifier
+
+ } else if (nospan ("=&|; \t\n", RB_LINE(rb), RB_INDEX(rb),
+ value, maxch) > 0 ) {
+ oper = IDOPR
+
+ # Other operator
+
+ } else {
+ old_index = RB_INDEX(rb)
+ junk = span ("=>&|;", RB_LINE(rb), RB_INDEX(rb), value, 2)
+ dic_index = strdic (value, dic_text, 2, dict)
+ if (dic_index > 0 && streq (value, dic_text)) {
+ oper = dic_oper[dic_index]
+ } else {
+ RB_INDEX(rb) = old_index
+ junk = ctowrd (RB_LINE(rb), RB_INDEX(rb), value, maxch)
+ oper = IDOPR
+ }
+ }
+
+ call sfree (sp)
+end
+
+# LEXINIT -- Initialize the lexical analyzer
+
+procedure lexinit (rbase, rb)
+
+char rbase[ARB] # i: Name of rule base file
+pointer rb # o: Pointer to rule base descriptor
+#--
+
+int open()
+errchk calloc, open
+
+begin
+ call malloc (rb, RB_LENGTH, TY_INT)
+
+ RB_FILE(rb) = open (rbase, READ_ONLY, TEXT_FILE)
+ RB_INDEX(rb) = 1
+ RB_NLINE(rb) = 0
+ RB_LINE(rb) = EOS
+end
+
+#LEXCLOSE -- Close the lexical analyzer
+
+procedure lexclose (rb)
+
+pointer rb # i: Pointer to rule base descriptor
+#--
+
+errchk close, mfree
+
+begin
+ call close (RB_FILE(rb))
+ call mfree (rb, TY_INT)
+end
diff --git a/pkg/utilities/nttools/texpand/lexoper.h b/pkg/utilities/nttools/texpand/lexoper.h
new file mode 100644
index 00000000..a4011143
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/lexoper.h
@@ -0,0 +1,29 @@
+
+# LEXOPER.H -- Operators and identifiers used by the lexical analyzer
+
+# The value of the operator type is also its priority
+
+define ENDOPR 1
+define SEPOPR 2
+define IMPOPR 3
+define OROPR 4
+define ANDOPR 5
+define EQOPR 6
+define IDOPR 7
+
+# Pseudo-identifiers placed on the id stack
+
+define NAME 1 # Any identifier
+define NO_IDENT 0 # No identifier on stack
+define PHRASE -1 # idents joined by equals or ands
+define CLAUSE -2 # idents joined by equals, ands, or ors
+
+# Rule base data structure
+
+define RB_LENGTH (SZ_LINE / SZ_INT32 + 5)
+
+define RB_FILE Memi[$1]
+define RB_NLINE Memi[$1+1]
+define RB_INDEX Memi[$1+2]
+define RB_LINE Memc[P2C($1+3)]
+define RB_CHARPTR (P2C($1+3) + RB_INDEX($1) - 1)
diff --git a/pkg/utilities/nttools/texpand/mkpkg b/pkg/utilities/nttools/texpand/mkpkg
new file mode 100644
index 00000000..241203bf
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/mkpkg
@@ -0,0 +1,21 @@
+# Update the texpand application code in the ttools package library
+# Author: B.Simon, 20-APR-1989
+
+$checkout libpkg.a ../
+$update libpkg.a
+$checkin libpkg.a ../
+$exit
+
+libpkg.a:
+ dbgrules.x <tbset.h>
+ lexer.x "lexoper.h"
+ mkrules.x
+ movelem.x <tbset.h>
+ movtbrow.x <tbset.h>
+ parser.x "lexoper.h"
+ pushstack.x <tbset.h>
+ span.x
+ texpand.x
+ userules.x <tbset.h>
+ ;
+
diff --git a/pkg/utilities/nttools/texpand/mkrules.x b/pkg/utilities/nttools/texpand/mkrules.x
new file mode 100644
index 00000000..c1dd3aad
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/mkrules.x
@@ -0,0 +1,48 @@
+define SZ_COLVAL SZ_LINE
+
+# MKRULES -- Add a new rule to the target and action tables
+#
+# B.Simon 25-Apr-88 Original
+
+procedure mkrules (work, target, action)
+
+pointer work # i: Table containing parser results
+pointer target # i: Table containing patterns to be matched
+pointer action # i: Table containing possible expansions
+#--
+int nwork, naction, ntarget, iwork, iaction
+
+int numstack()
+
+errchk movtbrow, putstacki
+
+begin
+ # Check for null rules
+
+ nwork = numstack (work)
+ if (nwork <= 0)
+ return
+
+ # Move the first row from the work table to the target table
+
+ call pushstack (target)
+ naction = numstack (action)
+ ntarget = numstack (target)
+
+ call movtbrow (work, 1, target, ntarget)
+ call putstacki (target, "_FIRST", naction+1)
+ call putstacki (target, "_LAST", naction+nwork-1)
+ call putstacki (target, "_USED", NO)
+
+ # Move the remaining rows to the action table
+
+ iaction = naction
+ do iwork = 2, nwork {
+ call pushstack (action)
+ iaction = iaction + 1
+ call movtbrow (work, iwork, action, iaction)
+ }
+
+ call tbrdel (work, 1, nwork)
+
+end
diff --git a/pkg/utilities/nttools/texpand/movelem.x b/pkg/utilities/nttools/texpand/movelem.x
new file mode 100644
index 00000000..e9d1f8b4
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/movelem.x
@@ -0,0 +1,113 @@
+include <tbset.h>
+
+# MOV_ELEM -- Move an element in one row from one table to another
+#
+# B.Simon 15-Jan-99 Original
+# B.Simon 27-Jan-99 Renamed
+
+procedure mov_elem (rtp, rcp, rrow, wtp, wcp, wrow)
+
+pointer rtp # i: Table descriptor of table read from
+pointer rcp # i: Column descriptor of column read from
+int rrow # i: Row number of table read from
+pointer wtp # i: Table descriptor of table written to
+pointer wcp # i: Column descriptor of column written to
+int wrow # i: Row number of table written to
+#--
+int type, nelem, sz_elem, nlen
+pointer sp, buf
+
+int tbcigi(), tbagtb(), tbagtt(), tbagti(), tbagtr(), tbagtd(), tbagts()
+
+begin
+ # First, get the type and number of elements in the column
+
+ nelem = tbcigi (rcp, TBL_COL_LENDATA)
+ type = tbcigi (rcp, TBL_COL_DATATYPE)
+
+ if (type < 0) {
+ sz_elem = - type
+ type = TY_CHAR
+ } else {
+ sz_elem = 0
+ }
+
+ # Allocate buffer to hold values passed between tables
+
+ call smark (sp)
+ call salloc (buf, nelem*(sz_elem+1), type)
+
+ # Copy the values according to their actual type
+
+ if (nelem == 1) {
+ # Do not copy null scalar values
+
+ switch (type) {
+ case TY_BOOL:
+ call tbegtb (rtp, rcp, rrow, Memb[buf])
+ call tbeptb (wtp, wcp, wrow, Memb[buf])
+
+ case TY_CHAR:
+ call tbegtt (rtp, rcp, rrow, Memc[buf], sz_elem)
+ if (Memc[buf] != EOS)
+ call tbeptt (wtp, wcp, wrow, Memc[buf])
+
+ case TY_SHORT:
+ call tbegts (rtp, rcp, rrow, Mems[buf])
+ if (! IS_INDEFS (Mems[buf]))
+ call tbepts (wtp, wcp, wrow, Mems[buf])
+
+ case TY_INT, TY_LONG:
+ call tbegti (rtp, rcp, rrow, Memi[buf])
+ if (! IS_INDEFI (Memi[buf]))
+ call tbepti (wtp, wcp, wrow, Memi[buf])
+
+ case TY_REAL:
+ call tbegtr (rtp, rcp, rrow, Memr[buf])
+ if (! IS_INDEFR (Memr[buf]))
+ call tbeptr (wtp, wcp, wrow, Memr[buf])
+
+ case TY_DOUBLE:
+ call tbegtd (rtp, rcp, rrow, Memd[buf])
+ if (! IS_INDEFD (Memd[buf]))
+ call tbeptd (wtp, wcp, wrow, Memd[buf])
+ }
+
+ } else {
+ # Don't copy zero length arrays
+
+ switch (type) {
+ case TY_BOOL:
+ nlen = tbagtb (rtp, rcp, rrow, Memb[buf], 1, nelem)
+ call tbaptb (wtp, wcp, wrow, Memb[buf], 1, nlen)
+
+ case TY_CHAR:
+ nlen = tbagtt (rtp, rcp, rrow, Memc[buf], sz_elem, 1, nelem)
+ if (Memc[buf] != EOS)
+ call tbaptt (wtp, wcp, wrow, Memc[buf], sz_elem, 1, nlen)
+
+ case TY_SHORT:
+ nlen = tbagts (rtp, rcp, rrow, Mems[buf], 1, nelem)
+ if (! IS_INDEFS (Mems[buf]))
+ call tbapts (wtp, wcp, wrow, Mems[buf], 1, nlen)
+
+ case TY_INT, TY_LONG:
+ nlen = tbagti (rtp, rcp, rrow, Memi[buf], 1, nelem)
+ if (! IS_INDEFI (Memi[buf]))
+ call tbapti (wtp, wcp, wrow, Memi[buf], 1, nlen)
+
+ case TY_REAL:
+ nlen = tbagtr (rtp, rcp, rrow, Memr[buf], 1, nelem)
+ if (! IS_INDEFR (Memr[buf]))
+ call tbaptr (wtp, wcp, wrow, Memr[buf], 1, nlen)
+
+ case TY_DOUBLE:
+ nlen = tbagtd (rtp, rcp, rrow, Memd[buf], 1, nelem)
+ if (! IS_INDEFD (Memd[buf]))
+ call tbaptd (wtp, wcp, wrow, Memd[buf], 1, nlen)
+ }
+ }
+
+ call sfree (sp)
+end
+
diff --git a/pkg/utilities/nttools/texpand/movtbrow.x b/pkg/utilities/nttools/texpand/movtbrow.x
new file mode 100644
index 00000000..9f9bb3f1
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/movtbrow.x
@@ -0,0 +1,43 @@
+include <tbset.h>
+
+# MOVTBROW -- Move columns from one table to another where not null
+#
+# B.Simon 25-Apr-88 Original
+# B.Simon 15-Jan-99 now calls mov_elem
+
+procedure movtbrow (rtp, rrow, wtp, wrow)
+
+pointer rtp # i: Table descriptor of table read from
+int rrow # i: Row number of table read from
+pointer wtp # i: Table descriptor of table written to
+int wrow # i: Row number of table written to
+#--
+int ncol, icol
+pointer sp, rcp, wcp, colname
+
+pointer tbpsta(), tbcnum()
+
+begin
+ call smark (sp)
+ call salloc (colname, SZ_COLNAME, TY_CHAR)
+
+ ncol = tbpsta (rtp, TBL_NCOLS)
+ do icol = 1, ncol {
+
+ rcp = tbcnum (rtp, icol)
+ call tbcigt (rcp, TBL_COL_NAME, Memc[colname], SZ_COLNAME)
+ call tbcfnd (wtp, Memc[colname], wcp, 1)
+
+# Column names beginning with an underscore are for internal
+# use by the program and do not contain actual data
+
+ if (Memc[colname] != '_' && wcp != NULL) {
+
+ # Copy the row and column in its native type
+
+ call mov_elem (rtp, rcp, rrow, wtp, wcp, wrow)
+ }
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/utilities/nttools/texpand/parser.com b/pkg/utilities/nttools/texpand/parser.com
new file mode 100644
index 00000000..6dba2f6c
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/parser.com
@@ -0,0 +1,6 @@
+# PARSER.COM -- Common block containing pointer to temporary tables
+
+common / stkptr / ptgt, pact, pwrk
+
+pointer ptgt, pact, pwrk
+
diff --git a/pkg/utilities/nttools/texpand/parser.x b/pkg/utilities/nttools/texpand/parser.x
new file mode 100644
index 00000000..2dcba75e
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/parser.x
@@ -0,0 +1,283 @@
+include <tbset.h>
+include "lexoper.h"
+
+define MAXSTACK 100
+define SZ_VALSTACK 3*SZ_LINE
+
+# PARSER -- Parse a rule base file
+#
+# This procedure uses a simple operator precedence parser. Every token
+# retrieved from the file is either an identifier or an operator.
+# Identifiers are pushed onto an identifier stack. Operators are
+# pushed onto a separate operator stack. When an operator is read
+# whose priority is less than that of the operator on top of the
+# operator stack, the operator on the stack is popped and passed to
+# a procedure which performs the appropriate action, using the
+# identifiers on the identifier stack. This continues until all
+# operators of higher priority have been processed, or the stack is
+# empty. Syntax checking is done by checking that the identifier
+# stack contains the correct number and type of identifier and that
+# identifiers and operators alternate in the input. The priority of
+# each operator is implicit in the integer which is used to represent
+# it. For more information on operator precedence parsers, see "Writing
+# Interactive Compilers and Interpreters" by P.J Brown, pp. 149-151.
+#
+# B.Simon 25-Apr-88 Original
+# B.Simon 15-Jan-99 Skip rules with columns not in table
+
+procedure parser (rbase, itp, dbg, target, action)
+
+char rbase[ARB] # i: Rule base name
+pointer itp # i: Input table descriptor
+int dbg # i: Debug file descriptor
+pointer target # o: Target table descriptor
+pointer action # o: Action table descriptor
+#--
+include "parser.com"
+
+bool done, expect_id
+int idtop, optop, oper, tabtop, missing
+int opstack[MAXSTACK]
+pointer sp, rb, work, value, valstack, nxtval, colname, colval
+pointer idstack[MAXSTACK]
+
+string find_error "Column name or type mismatch"
+string stack_error "Stack overflow"
+string oper_error "Operator expected"
+string ident_error "Identifier expected"
+
+string wrkname "The parser changed the work table to the following:"
+
+int gstrcpy(), putstackt(), numstack()
+pointer initstack()
+
+errchk initstack, lexinit, lexer, lexclose, syntax, typecheck, mkrules
+
+begin
+ # Allocate dynamic memory for strings
+
+ call smark (sp)
+ call salloc (value, SZ_LINE, TY_CHAR)
+ call salloc (valstack, SZ_VALSTACK, TY_CHAR)
+
+ # Initialize the lexical analyzer
+
+ call lexinit (rbase, rb)
+
+ # Create tables used by parser
+
+ target = initstack (itp, "_FIRST,_LAST,_USED")
+ action = initstack (itp, "")
+ work = initstack (itp, "")
+
+ # Save copy of table pointers in common block
+
+ ptgt = target
+ pact = action
+ pwrk = work
+
+ # Initialize stack pointers
+
+ idtop = 0
+ optop = 0
+ nxtval = valstack
+
+ missing = NO
+ done = false
+ expect_id = true
+
+ repeat {
+
+ # Get next operator from rule base
+
+ call lexer (rb, oper, Memc[value], SZ_LINE)
+
+ # First case: operator is identifier, push on id stack
+
+ if (oper == IDOPR) {
+ if (expect_id) {
+ idtop = idtop + 1
+ if (idtop > MAXSTACK)
+ call syntax (rb, stack_error)
+
+ idstack[idtop] = nxtval
+ nxtval = gstrcpy (Memc[value], Memc[nxtval], SZ_LINE) +
+ nxtval + 1
+ } else {
+ call syntax (rb, oper_error)
+ }
+
+ # Second case: operator is not identifier
+
+ } else {
+ if (oper != ENDOPR && expect_id)
+ call syntax (rb, ident_error)
+
+ # Process all operators whose priorities are >=
+ # the operator just read from the rule base
+
+ repeat {
+ if (optop == 0)
+ break
+ if (oper > opstack[optop])
+ break
+
+ # Perform semantic actions associated with operators
+
+ switch (opstack[optop]) {
+ case ENDOPR:
+ call typecheck (rb, idstack, idtop, NO_IDENT, NO_IDENT)
+ done = true
+ case SEPOPR:
+ call typecheck (rb, idstack, idtop, NO_IDENT, NO_IDENT)
+ case IMPOPR:
+ call typecheck (rb, idstack, idtop, PHRASE, CLAUSE)
+ if (missing == NO) {
+ call mkrules (work, target, action)
+ } else {
+ missing = NO
+ tabtop = numstack (work)
+ call tbrdel (work, 1, tabtop)
+ }
+ idtop = idtop - 2
+ case OROPR:
+ call typecheck (rb, idstack, idtop, CLAUSE, CLAUSE)
+ idtop = idtop - 1
+ idstack[idtop] = CLAUSE
+ case ANDOPR:
+ call typecheck (rb, idstack, idtop, PHRASE, PHRASE)
+ if (missing == NO)
+ call andstack (work)
+
+ idtop = idtop - 1
+ idstack[idtop] = PHRASE
+ case EQOPR:
+ call typecheck (rb, idstack, idtop, NAME, NAME)
+ colval = idstack[idtop]
+ colname = idstack[idtop-1]
+ nxtval = colname
+ call pushstack (work)
+ if (putstackt (work, Memc[colname], Memc[colval])== NO)
+ missing = YES
+
+ idtop = idtop - 1
+ idstack[idtop] = PHRASE
+ }
+
+ optop = optop - 1
+
+ # Debug prints
+
+ tabtop = numstack (work)
+ call dbg_rules (work, wrkname, 1, tabtop, dbg)
+
+ } until (done)
+
+ # Push the operator just read on the operator stack
+
+ optop = optop + 1
+ if (optop > MAXSTACK)
+ call syntax (rb, stack_error)
+ opstack[optop] = oper
+ }
+
+ # Operators and identifiers should alternate in the input
+
+ expect_id = ! expect_id
+
+ } until (done)
+
+ call freestack (work)
+ call lexclose (rb)
+ call sfree (sp)
+end
+
+# TYPECHECK -- Check the number and type of identifiers on the stack
+
+procedure typecheck (rb, idstack, idtop, type1, type2)
+
+pointer rb # i: Rule base descriptor
+pointer idstack[ARB] # i: Identifier stack
+int idtop # i: Top of identifier stack
+pointer type1 # i: Type expected for one below stack top
+pointer type2 # i: Type expected for stack top
+#--
+int itype
+pointer id, type[2]
+
+string bad_type "Operator out of order"
+string too_few "Missing identifier"
+string too_many "Unexpected end of rule"
+
+begin
+ type[1] = type1
+ type[2] = type2
+
+ do itype = 1, 2 {
+ switch (type[itype]) {
+ case CLAUSE:
+
+ if (idtop < itype)
+ call syntax (rb, too_few)
+ id = idstack[idtop+itype-2]
+
+ # a phrase is also a clause
+
+ if (!(id == PHRASE || id == CLAUSE))
+ call syntax (rb, bad_type)
+
+ case PHRASE:
+
+ if (idtop < itype)
+ call syntax (rb, too_few)
+ id = idstack[idtop+itype-2]
+ if (id != PHRASE)
+ call syntax (rb, bad_type)
+
+ case NO_IDENT:
+
+ if (idtop >= itype)
+ call syntax (rb, too_many)
+
+ case NAME:
+
+ if (idtop < itype)
+ call syntax (rb, too_few)
+ id = idstack[idtop+itype-2]
+ if (id <= 0)
+ call syntax (rb, bad_type)
+
+ }
+ }
+
+end
+
+# SYNTAX -- Print a syntax error message
+
+procedure syntax (rb, errmsg)
+
+pointer rb # i: Rule base descriptor
+char errmsg[ARB] # i: Error message
+#--
+include "parser.com"
+
+begin
+ # Remove temporary tables
+
+ call freestack (ptgt)
+ call freestack (pact)
+ call freestack (pwrk)
+
+ # Print the line where the error was detected
+
+ call eprintf ("Syntax error on line %d\n%s%*t^\n")
+ call pargi (RB_NLINE(rb))
+ call pargstr (RB_LINE(rb))
+ call pargi (RB_INDEX(rb))
+
+ # Close the rules file and send the error message
+
+ call lexclose (rb)
+ call error (ERR, errmsg)
+
+end
diff --git a/pkg/utilities/nttools/texpand/pushstack.x b/pkg/utilities/nttools/texpand/pushstack.x
new file mode 100644
index 00000000..58d8b797
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/pushstack.x
@@ -0,0 +1,226 @@
+include <tbset.h>
+
+define SZ_COLVAL SZ_LINE
+
+# The following procedures treat a table as if it were a stack, that is,
+# all reading and writing is done at the end of the table. The end of the
+# table is indicated by TB_NROWS.
+#
+# B.Simon 25-Apr-88 Original
+# B.Simon 27-Jan-98 Drop temporary tables
+
+# PUSHSTACK -- Push a null row on the top of a table stack
+
+procedure pushstack (tp)
+
+pointer tp # i: Table descriptor
+#--
+int top
+int tbpsta()
+
+begin
+ top = tbpsta (tp, TBL_NROWS) + 1
+ call tbtwer (tp, top)
+end
+
+# POPSTACK -- Pop the top row from a table stack
+
+procedure popstack (tp)
+
+pointer tp # i: Table descriptor
+#--
+int top
+int tbpsta()
+
+begin
+ top = tbpsta (tp, TBL_NROWS)
+ if (top > 0)
+ call tbrdel (tp, top, top)
+end
+
+# NUMSTACK -- Return the number of rows in a table stack
+
+int procedure numstack (tp)
+
+pointer tp # i: Table descriptor
+#--
+int tbpsta()
+
+begin
+ return (tbpsta (tp, TBL_NROWS))
+end
+
+# INITSTACK -- Initialize a table stack and return its descriptor
+
+pointer procedure initstack (tp, extra)
+
+pointer tp # i: Table to use as a template for the table stack
+char extra[ARB] # i: Extra columns to add to the table stack
+#--
+char comma
+int ic, jc
+pointer sp, cp, stack, colname, colunits, colfmt, tmproot, tmpfile
+
+int stridx()
+pointer tbtopn()
+
+errchk tbtopn, tbtcre
+
+begin
+ # Set up arrays in dynamic memory
+
+ call smark (sp)
+ call salloc (colname, SZ_COLNAME, TY_CHAR)
+ call salloc (colunits, SZ_COLUNITS, TY_CHAR)
+ call salloc (colfmt, SZ_COLFMT, TY_CHAR)
+ call salloc (tmproot, SZ_FNAME, TY_CHAR)
+ call salloc (tmpfile, SZ_FNAME, TY_CHAR)
+
+ # Create the stack table
+
+ call mktemp ("tmp$stk", Memc[tmproot], SZ_FNAME)
+ call tbtext (Memc[tmproot], Memc[tmpfile], SZ_FNAME)
+ stack = tbtopn (Memc[tmpfile], NEW_COPY, tp)
+
+ # Set up column information that will not vary across columns
+
+ Memc[colunits] = EOS
+ Memc[colfmt] = EOS
+
+ # Add column names from the extra string
+
+ ic = 1
+ comma = ','
+ repeat {
+
+ # Copy the next comma delimeted column name
+
+ jc = stridx (comma, extra[ic])
+ if (jc == 0)
+ call strcpy (extra[ic], Memc[colname], SZ_COLNAME)
+ else
+ call strcpy (extra[ic], Memc[colname], jc-1)
+ ic = ic + jc
+
+ # Create the new column
+
+ if (Memc[colname] != EOS)
+ call tbcdef (stack, cp, Memc[colname], Memc[colunits],
+ Memc[colfmt], TY_INT, 1, 1)
+
+ } until (jc == 0)
+
+ # Return the stack table descriptor
+
+ call tbtcre (stack)
+ call sfree (sp)
+
+ return (stack)
+end
+
+# FREESTACK -- Close and delete a table stack
+
+procedure freestack (tp)
+
+pointer tp # i: Table descriptor
+#--
+pointer sp, table
+
+begin
+ call smark (sp)
+ call salloc (table, SZ_FNAME, TY_CHAR)
+
+ call tbtnam (tp, Memc[table], SZ_FNAME)
+ call tbtclo (tp)
+
+ call delete (Memc[table])
+ call sfree (sp)
+end
+
+# PUTSTACKT -- Put a text string in the top row of a table stack
+
+int procedure putstackt (tp, colname, colval)
+
+pointer tp # i: Table descriptor
+char colname[ARB] # i: Column name
+char colval[ARB] # i: Column value
+#--
+int top, found
+pointer cp
+
+int tbpsta()
+
+begin
+ top = tbpsta (tp, TBL_NROWS)
+ call tbcfnd (tp, colname, cp, 1)
+
+ found = NO
+ if (cp != NULL) {
+ ifnoerr {
+ call tbrptt (tp, cp, colval, ARB, 1, top)
+ } then {
+ found = YES
+ }
+ }
+
+ return (found)
+end
+
+# PUTSTACKI -- Put an integer in the top row of a table stack
+
+procedure putstacki (tp, colname, colval)
+
+pointer tp # i: Table descriptor
+char colname[ARB] # i: Column name
+int colval # i: Column value
+#--
+int top
+pointer cp
+
+int tbpsta()
+
+begin
+ top = tbpsta (tp, TBL_NROWS)
+
+ call tbcfnd (tp, colname, cp, 1)
+ call tbepti (tp, cp, top, colval)
+
+end
+
+# ANDSTACK -- Combine the top two rows of the table stack
+
+procedure andstack (tp)
+
+pointer tp # i: Table descriptor
+#--
+int top
+int tbpsta()
+
+begin
+ top = tbpsta (tp, TBL_NROWS)
+
+ call movtbrow (tp, top, tp, top-1)
+ call tbrdel (tp, top, top)
+end
+
+# MOVSTACK -- Move the top row of one table stack to another
+
+procedure movstack (rtp, wtp)
+
+pointer rtp # i: Table descriptor of table read from
+pointer wtp # i: Table descriptor of table written to
+#--
+int rtop, wtop
+
+int tbpsta()
+
+begin
+ call pushstack (wtp)
+
+ rtop = tbpsta (rtp, TBL_NROWS)
+ wtop = tbpsta (wtp, TBL_NROWS)
+
+ call movtbrow (rtp, rtop, wtp, wtop)
+ call tbrdel (rtp, rtop, rtop)
+
+end
diff --git a/pkg/utilities/nttools/texpand/span.x b/pkg/utilities/nttools/texpand/span.x
new file mode 100644
index 00000000..b8d69141
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/span.x
@@ -0,0 +1,97 @@
+# SPAN -- Copy characters while they match a set
+#
+# B.Simon 25-Apr-88 Original
+
+int procedure span (set, str, ic, outstr, maxch)
+
+char set[ARB] # i: Set of characters used in matching
+char str[ARB] # i: Input string
+int ic # io: Index to input string character
+char outstr[ARB] # o: Output string
+int maxch # i: Maximum length of output string
+#--
+bool match
+int jc, kc, setlen
+
+int strlen()
+
+begin
+ # Loop over characters in the input string
+
+ setlen = strlen (set)
+ for (jc = 1; str[ic] != EOS && jc <= maxch; ic = ic + 1) {
+
+ # See if the current character in the input string
+ # matches the characters in the set
+
+ match = false
+ do kc = 1, setlen {
+ if (str[ic] == set[kc]) {
+ match = true
+ break
+ }
+ }
+
+ # Copy character to the output string if it matches
+
+ if (! match)
+ break
+
+ outstr[jc] = str[ic]
+ jc = jc + 1
+
+ }
+
+ # Return number of characters in output string
+
+ outstr[jc] = EOS
+ return (jc - 1)
+end
+
+# NOSPAN -- Copy characters while they do not match a set
+
+int procedure nospan (set, str, ic, outstr, maxch)
+
+char set[ARB] # i: Set of characters used in matching
+char str[ARB] # i: Input string
+int ic # io: Index to input string character
+char outstr[ARB] # o: Output string
+int maxch # i: Maximum length of output string
+#--
+bool match
+int jc, kc, setlen
+
+int strlen()
+
+begin
+ # Loop over characters in the input string
+
+ setlen = strlen (set)
+ for (jc = 1; str[ic] != EOS && jc <= maxch; ic = ic + 1) {
+
+ # See if the current character in the input string
+ # matches the characters in the set
+
+ match = false
+ do kc = 1, setlen {
+ if (str[ic] == set[kc]) {
+ match = true
+ break
+ }
+ }
+
+ # Copy character to the output string if it does not match
+
+ if (match)
+ break
+
+ outstr[jc] = str[ic]
+ jc = jc + 1
+
+ }
+
+ # Return number of characters in output string
+
+ outstr[jc] = EOS
+ return (jc - 1)
+end
diff --git a/pkg/utilities/nttools/texpand/texpand.x b/pkg/utilities/nttools/texpand/texpand.x
new file mode 100644
index 00000000..c89075c1
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/texpand.x
@@ -0,0 +1,94 @@
+include <fset.h>
+
+# TEXPAND -- Expand the rows of a table according to a set of rules
+#
+# B.Simon 25-Apr-88 Original
+# Phil Hodge 4-Oct-95 Use table name template routines tbnopenp, etc.
+
+procedure texpand ()
+
+#--
+pointer ilist # Input file name template
+pointer olist # Output file name template
+pointer rbase # Name of file containing expansion rules
+pointer debug # Debug file name
+bool verbose # Diagnostic message flag
+
+int junk, dbg
+pointer sp, itp, otp, input, output, target, action
+
+bool clgetb()
+int open(), tbnlen(), tbnget()
+pointer tbnopenp(), tbtopn()
+
+begin
+ # Allocate dynamic memory for strings
+
+ call smark (sp)
+ call salloc (input, SZ_FNAME, TY_CHAR)
+ call salloc (output, SZ_FNAME, TY_CHAR)
+ call salloc (rbase, SZ_FNAME, TY_CHAR)
+ call salloc (debug, SZ_FNAME, TY_CHAR)
+
+ # Read the parameter file
+
+ ilist = tbnopenp ("input")
+ olist = tbnopenp ("output")
+ call clgstr ("rbase", Memc[rbase], SZ_FNAME)
+ call clgstr ("debug", Memc[debug], SZ_FNAME)
+ verbose = clgetb ("verbose")
+
+ # Open debug file
+
+ if (Memc[debug] == ' ' || Memc[debug] == EOS)
+ dbg = NULL
+ else
+ dbg = open (Memc[debug], NEW_FILE, TEXT_FILE)
+
+ # Check to see that input & output templates
+ # have same number of files
+
+ if (tbnlen (ilist) != tbnlen (olist))
+ call error (ERR, "Number of input and output tables do not match")
+
+ while (tbnget (ilist, Memc[input], SZ_FNAME) != EOF) {
+
+ junk = tbnget (olist, Memc[output], SZ_FNAME)
+
+ # Open input and output tables
+
+ itp = tbtopn (Memc[input], READ_ONLY, NULL)
+ otp = tbtopn (Memc[output], NEW_COPY, itp)
+ call tbtcre (otp)
+ call tbhcal (itp, otp)
+
+ # Create target and action tables from the rule base
+
+ call parser (Memc[rbase], itp, dbg, target, action)
+
+ # Expand the rows of the input table using the rules
+ # encoded in the target and action tables
+
+ call use_rules (itp, otp, target, action, dbg, verbose)
+
+ # Print diagnostic message and close tables
+
+ if (verbose) {
+ call tbtnam (itp, Memc[input], SZ_FNAME)
+ call tbtnam (otp, Memc[output], SZ_FNAME)
+
+ call printf ("%s -> %s\n")
+ call pargstr (Memc[input])
+ call pargstr (Memc[output])
+ call flush (STDOUT)
+ }
+
+ call tbtclo (itp)
+ call tbtclo (otp)
+ }
+
+ call close (dbg)
+ call tbnclose (ilist)
+ call tbnclose (olist)
+ call sfree (sp)
+end
diff --git a/pkg/utilities/nttools/texpand/userules.x b/pkg/utilities/nttools/texpand/userules.x
new file mode 100644
index 00000000..6195b148
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/userules.x
@@ -0,0 +1,286 @@
+include <tbset.h>
+
+# USE_RULES -- Use the rules to expand the input table rows
+#
+# B.Simon 25-Apr-88 Original
+# B.Simon 21-Jan-99 Modified to handle empty target tables
+
+procedure use_rules (itp, otp, target, action, dbg, verbose)
+
+pointer itp # i: Input table
+pointer otp # i: Output table
+pointer target # u: Table of rule targets
+pointer action # u: Table of rule actions
+int dbg # i: Debug file descriptor
+bool verbose # i: Print diagnostic message
+#--
+int top, nrow, irow
+pointer work
+
+int tbpsta()
+int initstack(), numstack(), find_rule(), apply_rule()
+
+string tgtname "The following is the target table:"
+string actname "The following is the action table:"
+string isstart "The following row is read from the input table:"
+string isdone "The following row is moved to the output table:"
+
+begin
+ # Do straight copy if target table is empty
+
+ top = numstack (target)
+ if (top == 0) {
+ call no_rule (itp, otp)
+ return
+ }
+
+ # Print target and action tables
+
+ call dbg_rules (target, tgtname, 1, top, dbg)
+
+ top = numstack (action)
+ call dbg_rules (action, actname, 1, top, dbg)
+
+ # Create a work table, which is used to store
+ # intermediate results
+
+ work = initstack (itp, "_TARGET,_INDEX")
+
+ # Loop over each row in the input table
+
+ nrow = tbpsta (itp, TBL_NROWS)
+ do irow = 1, nrow {
+
+ call dbg_rules (itp, isstart, irow, irow, dbg)
+
+ # Push the next row from the input table
+ # into the work table. If it does not match
+ # any rule, write it to the output table.
+
+ call pushstack (work)
+ call movtbrow (itp, irow, work, 1)
+ if (find_rule (target, work) == 0) {
+ top = numstack (work)
+ call dbg_rules (work, isdone, top, top, dbg)
+ call movstack (work, otp)
+ }
+
+ # Apply the next instance of the rule to the
+ # row on top of the stack. If the result of the
+ # application of the rule does not match any other
+ # rule, write it to the output table.
+
+ while (numstack (work) > 0) {
+ if (apply_rule (target, action, work, dbg) == 0) {
+ top = numstack (work)
+ call dbg_rules (work, isdone, top, top, dbg)
+ call movstack (work, otp)
+ if (verbose && mod (numstack (otp), 25) == 0) {
+ call printf ("\r%d rows written to output table")
+ call pargi (numstack (otp))
+ call flush (STDOUT)
+ }
+ }
+ }
+ }
+
+ if (verbose) {
+ call printf ("\r%39w\r")
+ call flush (STDOUT)
+ }
+
+ call freestack (target)
+ call freestack (action)
+ call freestack (work)
+end
+
+# APPLY_RULE -- Expand the top work table row according to a rule
+
+int procedure apply_rule (target, action, work, dbg)
+
+pointer target # i: Table of rule targets
+pointer action # i: Table of rule actions
+pointer work # i: Table of intermediate results
+int dbg # i: Debug file descriptor
+#--
+int wrow, trow, arow, last, rule
+pointer tgt_ptr, idx_ptr, lst_ptr, use_ptr
+
+string isrule "The following rule is applied:"
+string notdone "To produce the row:"
+
+int numstack(), find_rule()
+
+begin
+ # Get column pointers of special columns
+
+ call tbcfnd (work, "_TARGET", tgt_ptr, 1)
+ call tbcfnd (work, "_INDEX", idx_ptr, 1)
+ call tbcfnd (target, "_LAST", lst_ptr, 1)
+
+ # Get the current row numbers for the work, target,
+ # and action tables
+
+ wrow = numstack (work)
+ call tbegti (work, tgt_ptr, wrow, trow)
+ call tbegti (work, idx_ptr, wrow, arow)
+ call tbegti (target, lst_ptr, trow, last)
+
+ # If the action row number is greater than the last action
+ # associated with the target, all the expansions for this
+ # rule have been performed. Pop the work table and mark the
+ # target row as unused.
+
+ if (arow > last) {
+ call popstack (work)
+ call tbcfnd (target, "_USED", use_ptr, 1)
+ call tbepti (target, use_ptr, trow, NO)
+ rule = trow
+
+ # Otherwise, duplicate the top row of the work table and
+ # overwrite the appropriate columns with the values stored
+ # in the action row. Increment the action row for next time.
+ # Initialize the special columns in the new row of the work
+ # table.
+
+ } else {
+ call pushstack (work)
+ call movtbrow (work, wrow, work, wrow+1)
+ call movtbrow (action, arow, work, wrow+1)
+ call tbepti (work, idx_ptr, wrow, arow+1) ## should be wrow+1 ??
+ call dbg_rules (target, isrule, trow, trow, dbg)
+ call dbg_rules (work, notdone, wrow+1, wrow+1, dbg)
+ rule = find_rule (target, work)
+ }
+
+ return (rule)
+end
+
+# FIND_RULE -- Find the target row which matches the top work table row
+
+int procedure find_rule (target, work)
+
+pointer target # i: Table of rule targets
+pointer work # i: Table of intermediate results
+#--
+bool match, nullflg
+int icol, jcol, tcol, wcol, trow, irow, wrow, used, first
+pointer sp, tarptr, wrkptr, colname, tarval, wrkval
+pointer use_ptr, fst_ptr, tgt_ptr, idx_ptr, iw
+
+bool strne()
+int tbpsta(), strlen()
+pointer tbcnum(), numstack()
+
+begin
+ # Get number of columns in tables
+
+ tcol = tbpsta (target, TBL_NCOLS)
+ wcol = tbpsta (work, TBL_NCOLS)
+
+ # Allocate dynamic memory
+
+ call smark (sp)
+ call salloc (tarptr, tcol, TY_INT)
+ call salloc (wrkptr, tcol, TY_INT)
+ call salloc (colname, SZ_COLNAME, TY_CHAR)
+ call salloc (tarval, SZ_LINE, TY_CHAR)
+ call salloc (wrkval, SZ_LINE, TY_CHAR)
+
+ # Create arrays of corresponding column pointers
+ # in the target and work tables
+
+ jcol = 0
+ do icol = 1, tcol {
+ Memi[tarptr+jcol] = tbcnum (target, icol)
+ call tbcigt (Memi[tarptr+jcol), TBL_COL_NAME,
+ Memc[colname], SZ_COLNAME)
+ call tbcfnd (work, Memc[colname], Memi[wrkptr+jcol], 1)
+ if (Memc[colname] != '_' && Memi[wrkptr+jcol] != NULL)
+ jcol = jcol + 1
+ }
+
+ # Get pointers to special columns
+
+ call tbcfnd (target, "_USED", use_ptr, 1)
+ call tbcfnd (target, "_FIRST", fst_ptr, 1)
+ call tbcfnd (work, "_TARGET", tgt_ptr,1)
+ call tbcfnd (work, "_INDEX", idx_ptr, 1)
+
+ # Search for a match in the target table
+ # with the top row of the work table
+
+ match = false
+ wrow = numstack (work)
+ trow = tbpsta (target, TBL_NROWS)
+ do irow = 1, trow {
+
+ call tbegti (target, use_ptr, irow, used)
+ if (used == NO) {
+
+ # Compare each non-null column of the target row
+ # to the work row
+
+ match = true
+ do icol = 1, jcol {
+ call tbrgtt (target, Memi[tarptr+icol-1], Memc[tarval],
+ nullflg, SZ_LINE, 1, irow)
+
+ if (! nullflg) {
+ call tbegtt (work, Memi[wrkptr+icol-1], wrow,
+ Memc[wrkval], SZ_LINE)
+
+ iw = strlen (Memc[wrkval]) + wrkval - 1
+ while (Memc[iw] == ' ')
+ iw = iw - 1
+ Memc[iw+1] = EOS
+
+ if (strne (Memc[tarval], Memc[wrkval])) {
+ match = false
+ break
+ }
+ }
+ }
+
+ # If the rows match, mark the target row as used
+ # and initialize the special columns in the work row
+
+ if (match) {
+
+ call tbepti (target, use_ptr, irow, YES)
+
+ call tbegti (target, fst_ptr, irow, first)
+ call tbepti (work, idx_ptr, wrow, first)
+ call tbepti (work, tgt_ptr, wrow, irow)
+
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+
+ # If a match was found, return the target row number matched
+
+ if (match)
+ return (irow)
+ else
+ return (0)
+end
+
+# NO_RULE -- Do a straight copy when ther are no expansion rules
+
+procedure no_rule (itp, otp)
+
+pointer itp # i: Input table
+pointer otp # i: Output table
+#--
+int irow, nrow
+int tbpsta()
+
+begin
+ nrow = tbpsta (itp, TBL_NROWS)
+
+ do irow = 1, nrow
+ call tbrcpy (itp,otp, irow, irow)
+end
diff --git a/pkg/utilities/nttools/texpand/x_texpand.x b/pkg/utilities/nttools/texpand/x_texpand.x
new file mode 100644
index 00000000..37d7f51e
--- /dev/null
+++ b/pkg/utilities/nttools/texpand/x_texpand.x
@@ -0,0 +1,3 @@
+# X_TEXPAND -- Dummy main routine for texpand
+
+task texpand = texpand