aboutsummaryrefslogtreecommitdiff
path: root/pkg/utilities/nttools/tunits
diff options
context:
space:
mode:
authorJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
committerJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
commit40e5a5811c6ffce9b0974e93cdd927cbcf60c157 (patch)
tree4464880c571602d54f6ae114729bf62a89518057 /pkg/utilities/nttools/tunits
downloadiraf-osx-40e5a5811c6ffce9b0974e93cdd927cbcf60c157.tar.gz
Repatch (from linux) of OSX IRAF
Diffstat (limited to 'pkg/utilities/nttools/tunits')
-rw-r--r--pkg/utilities/nttools/tunits/abrev.tab62
-rw-r--r--pkg/utilities/nttools/tunits/abrev.x113
-rw-r--r--pkg/utilities/nttools/tunits/convertcol.x68
-rw-r--r--pkg/utilities/nttools/tunits/factor.x125
-rw-r--r--pkg/utilities/nttools/tunits/mkpkg19
-rw-r--r--pkg/utilities/nttools/tunits/parseunits.com9
-rw-r--r--pkg/utilities/nttools/tunits/parseunits.x624
-rw-r--r--pkg/utilities/nttools/tunits/parseunits.y322
-rw-r--r--pkg/utilities/nttools/tunits/tuniterr.x24
-rw-r--r--pkg/utilities/nttools/tunits/tunits.h14
-rw-r--r--pkg/utilities/nttools/tunits/tunits.x112
-rw-r--r--pkg/utilities/nttools/tunits/unhash.x212
-rw-r--r--pkg/utilities/nttools/tunits/units.tab60
-rw-r--r--pkg/utilities/nttools/tunits/units.x162
-rw-r--r--pkg/utilities/nttools/tunits/unstr.x381
15 files changed, 2307 insertions, 0 deletions
diff --git a/pkg/utilities/nttools/tunits/abrev.tab b/pkg/utilities/nttools/tunits/abrev.tab
new file mode 100644
index 00000000..90b8b58c
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/abrev.tab
@@ -0,0 +1,62 @@
+# Abbreviation table for tunits task
+#
+# Many units have more than one name or abbreviation. This table lists
+# the standard abbreviation for each set of units. The abbreviation is
+# used internally when computing the conversion factor. Case is not
+# significant in names and regular plurals (made by adding an "s") are
+# converted to the singular. Names should contain only alphabetic characters.
+# Blanks, underscores and digits are not allowed.
+#
+# name abbreviation
+#----------------------------
+meter m
+centimeter cm
+kilometer km
+millimeter mm
+micrometer um
+micron um
+nanometer nm
+metre m
+centimetre cm
+kilometre km
+millimetre mm
+micrometre um
+nanometre nm
+kilogram kg
+gram g
+gm g
+milligram mg
+second s
+sec s
+minute min
+hour hr
+year yr
+radian rad
+degree deg
+arcminute amin
+arcmin amin
+arcsecond asec
+arcsec asec
+angstrom a
+parsec pc
+kiloparsec kpc
+megaparsec mpc
+hertz hz
+kilohertz khz
+megahertz mhz
+gigahertz ghz
+lightyear ly
+newton n
+joule j
+watt w
+calorie c
+kilocalorie kc
+inch in
+inches in
+foot ft
+feet ft
+ounce oz
+pound lb
+liter l
+jansky jy
+millijansky mjy
diff --git a/pkg/utilities/nttools/tunits/abrev.x b/pkg/utilities/nttools/tunits/abrev.x
new file mode 100644
index 00000000..f40ca944
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/abrev.x
@@ -0,0 +1,113 @@
+include <tbset.h>
+include "tunits.h"
+
+#* HISTORY *
+#* B.Simon 07-Jan-99 Original
+
+# FIND_ABREV -- Find the abbreviation for a units string
+
+int procedure find_abrev (ab, units, abrev, maxch)
+
+pointer ab # i: abbreviation hash table descriptor
+char units[ARB] # i: units string
+char abrev[ARB] # o: abbreviation string
+int maxch # i: maximum length of abbreviation string
+#--
+int status
+pointer ptr
+
+int get_unhash()
+
+begin
+ status = get_unhash (ab, units, ptr)
+ if (status == NO) {
+ abrev[1] = EOS
+ } else {
+ call strcpy (Memc[ptr], abrev, maxch)
+ }
+
+ return (status)
+end
+
+# FREE_ABREV -- Free the abbreviation hash table
+
+procedure free_abrev (ab)
+
+pointer ab # i: abbreviation hash table descriptor
+#--
+int index
+pointer sp, keyword, value
+
+int each_unhash()
+
+begin
+ call smark (sp)
+ call salloc (keyword, LEN_UNIT, TY_CHAR)
+
+ index = 0
+ while (each_unhash (ab, index, Memc[keyword],
+ value, LEN_UNIT) != EOF) {
+ if (value != NULL)
+ call mfree (value, TY_CHAR)
+ }
+
+
+ call free_unhash (ab)
+ call sfree (sp)
+end
+
+# READ_ABREV -- Read abbreviations from a table and load into a hash
+
+pointer procedure read_abrev (abrevtab)
+
+char abrevtab[ARB] # i: abbreviation table name
+#--
+int irow, nrow
+pointer tp, c1, c2, sp, units, abrev, ab
+
+string nocolumn "The abbreviation table must have two coulmns"
+
+int tbpsta()
+pointer tbtopn(), tbcnum(), new_unhash()
+
+begin
+ # Dynamic memory for strings
+
+ call smark (sp)
+ call salloc (units, LEN_UNIT, TY_CHAR)
+
+ # Refer to columns numerically because
+ # this is supposed to be a text file
+
+ tp = tbtopn (abrevtab, READ_ONLY, NULL)
+ c1 = tbcnum (tp, 1)
+ c2 = tbcnum (tp, 2)
+
+ if (c1 == NULL || c2 == NULL)
+ call tuniterr (nocolumn, abrevtab)
+
+ # Create hash
+
+ nrow = tbpsta (tp, TBL_NROWS)
+ ab = new_unhash (nrow, LEN_UNIT)
+
+ # Read each row into hash
+
+ do irow = 1, nrow {
+ call malloc (abrev, LEN_UNIT, TY_CHAR)
+
+ call tbegtt (tp, c1, irow, Memc[units], LEN_UNIT)
+ call tbegtt (tp, c2, irow, Memc[abrev], LEN_UNIT)
+
+ call strlwr (Memc[units])
+ call strlwr (Memc[abrev])
+
+ call add_unhash (ab, Memc[units], abrev)
+ }
+
+ # Close table and free memory
+
+ call tbtclo (tp)
+ call sfree (sp)
+ return (ab)
+end
diff --git a/pkg/utilities/nttools/tunits/convertcol.x b/pkg/utilities/nttools/tunits/convertcol.x
new file mode 100644
index 00000000..976ea888
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/convertcol.x
@@ -0,0 +1,68 @@
+include <tbset.h>
+
+#* HISTORY *
+#* B.Simon 07-Jan-99 Original
+
+# CONVERT_COL -- Convert the units of a table column
+
+procedure convert_col (tp, cp, newunits, factor)
+
+pointer tp # i: table descriptor
+pointer cp # i: column descriptor
+char newunits[ARB] # i: new column units
+double factor # i: conversion factor
+#--
+double value
+int nrow, nelem, irow, nlen, ilen
+pointer sp, buffer
+
+int tbpsta(), tbcigi(), tbagtd()
+
+begin
+ # Change column units
+
+ call tbcnit (tp, cp, newunits)
+
+ # Get column dimensions
+
+ nrow = tbpsta (tp, TBL_NROWS)
+ nelem = tbcigi (cp, TBL_COL_LENDATA)
+
+ # Allocate buffer to hold array elements
+
+ call smark (sp)
+ call salloc (buffer, nelem, TY_DOUBLE)
+
+ # Multiply column values by conversion factor
+
+ if (nelem == 1) {
+ # Scalar column, use element get and put
+
+ do irow = 1, nrow {
+ call tbegtd (tp, cp, irow, value)
+ if (! IS_INDEFD (value)) {
+ value = factor * value
+ call tbeptd (tp, cp, irow, value)
+ }
+ }
+
+ } else {
+ # Array element, use array get and put
+
+ do irow = 1, nrow {
+ nlen = tbagtd (tp, cp, irow, Memd[buffer], 1, nelem)
+
+ do ilen = 0, nlen-1 {
+ if (! IS_INDEFD (Memd[buffer+ilen])) {
+ Memd[buffer+ilen] = factor * Memd[buffer+ilen]
+ }
+ }
+
+ call tbaptd (tp, cp, irow, Memd[buffer], 1, nlen)
+ }
+ }
+
+ call sfree (sp)
+end
+
+
diff --git a/pkg/utilities/nttools/tunits/factor.x b/pkg/utilities/nttools/tunits/factor.x
new file mode 100644
index 00000000..3c9a91ac
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/factor.x
@@ -0,0 +1,125 @@
+include "tunits.h"
+
+#* HISTORY *
+#* B.Simon 07-Jan-99 Original
+
+# FIND_FACTOR -- Find conversion factor between two sets of units
+
+double procedure find_factor (ut, punit1, punit2, verbose)
+
+pointer ut # i: units hash descriptor
+pointer punit1 # i: old set of units
+pointer punit2 # i: new set of units
+bool verbose # i: diagnostic message flag
+#--
+double factor
+pointer punit3, punit4, punit5
+
+string noconvert "The old and new units are not compatible"
+
+pointer reduce_factor(), div_unstr()
+
+begin
+ # Reduce old and new units to a common form
+
+ punit3 = reduce_factor (ut, punit1, verbose)
+ punit4 = reduce_factor (ut, punit2, verbose)
+
+ # The conversion factor is the ratio of
+ # the two sets of units when in common form
+
+ punit5 = div_unstr (punit3, punit4)
+
+ # Check to make sure units actually have a common form
+
+ if (TUN_UNPTR(punit5,1) != NULL)
+ call error (1, noconvert)
+
+ factor = TUN_FACTOR (punit5)
+
+ # Print conversion factor
+ if (verbose) {
+ call eprintf ("The conversion factor is %g\n")
+ call pargd (factor)
+ }
+
+ # Free temporary units descriptors
+
+ call free_unstr (punit3)
+ call free_unstr (punit4)
+ call free_unstr (punit5)
+
+ return (factor)
+end
+
+# REDUCE_FACTOR -- Reduce units descriptor to a common set of units (mks)
+
+pointer procedure reduce_factor (ut, punit, verbose)
+
+pointer ut # i: Units hash descriptor
+pointer punit # i: Units string descriptor
+bool verbose # i: diagnostic message flag
+#--
+bool done
+int idx
+pointer sp, units, punit1, punit2, punit3, punit4
+
+int find_units()
+pointer copy_unstr(), pow_unstr(), mul_unstr()
+
+begin
+ # Allocate memory for units string
+
+ call smark (sp)
+ call salloc (units, SZ_FNAME, TY_CHAR)
+
+ # Loop until no more reductions can be performed
+
+ punit1 = copy_unstr (punit)
+
+ repeat {
+ if (verbose) {
+ call str_unstr (punit1, Memc[units], SZ_FNAME)
+ call eprintf ("%s")
+ call pargstr (Memc[units])
+ }
+
+ # Search for a reduction for any term
+
+ done = true
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit1,idx) == NULL)
+ break
+
+ if (find_units (ut, TUN_UNITS(punit1,idx), punit2) ==YES) {
+ # Reduction found. Raise conversion factor to
+ # degree of term in descriptor and then multiply
+ # the units by it
+
+ punit3 = pow_unstr (punit2, TUN_POWER(punit1,idx))
+ punit4 = mul_unstr (punit1, punit3)
+
+ call free_unstr (punit1)
+ call free_unstr (punit3)
+
+ punit1 = punit4
+ done = false
+ break
+ }
+ }
+
+ if (verbose) {
+ if (done) {
+ call eprintf ("\n")
+ } else {
+ call eprintf (" is \n")
+ }
+ }
+ } until (done)
+
+ if (verbose)
+ call eprintf ("\n")
+
+ call sfree (sp)
+ return (punit1)
+end
diff --git a/pkg/utilities/nttools/tunits/mkpkg b/pkg/utilities/nttools/tunits/mkpkg
new file mode 100644
index 00000000..3ec0d98b
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/mkpkg
@@ -0,0 +1,19 @@
+# Update the tunits task in the ttools package library
+# Author: Bernie Simon, 11-jan-99
+
+$checkout libpkg.a ../
+$update libpkg.a
+$checkin libpkg.a ../
+$exit
+
+libpkg.a:
+ abrev.x <tbset.h> "tunits.h"
+ convertcol.x <tbset.h>
+ factor.x "tunits.h"
+ parseunits.x <ctype.h> "parseunits.com"
+ tuniterr.x
+ tunits.x <tbset.h> "tunits.h"
+ unhash.x
+ units.x <tbset.h> "tunits.h"
+ unstr.x "tunits.h"
+ ;
diff --git a/pkg/utilities/nttools/tunits/parseunits.com b/pkg/utilities/nttools/tunits/parseunits.com
new file mode 100644
index 00000000..d5c5bf48
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/parseunits.com
@@ -0,0 +1,9 @@
+# PARSEUNITS.COM -- Global variables used by parse_units
+
+common / parse / tun, abrev, tokbuf, nxttok, debug
+
+pointer tun # descriptor containing results of parse
+pointer abrev # hash table of unit abbreviations
+pointer tokbuf # buffer holding tokens parsed from units string
+int nxttok # index to next free space in token buffer
+int debug # debugging message flag
diff --git a/pkg/utilities/nttools/tunits/parseunits.x b/pkg/utilities/nttools/tunits/parseunits.x
new file mode 100644
index 00000000..1a420785
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/parseunits.x
@@ -0,0 +1,624 @@
+include <ctype.h>
+
+#* B.Simon ?? original
+# Phil Hodge 12-Jul-2005 add 'bool yydebug' and 'int get_token()' to
+# parse_units
+
+define YYMAXDEPTH 32
+define YYOPLEN 1
+define yyparse unit_parse
+
+define SZ_SHORTSTR 31
+
+define Y_WRONG 257
+define Y_DONE 258
+define Y_LPAR 259
+define Y_RPAR 260
+define Y_CU 261
+define Y_SQ 262
+define Y_ID 263
+define Y_NUM 264
+define Y_DIV 265
+define Y_MUL 266
+define Y_POW 267
+define yyclearin yychar = -1
+define yyerrok yyerrflag = 0
+define YYMOVE call amovi (Memi[$1], Memi[$2], YYOPLEN)
+define YYERRCODE 256
+
+# line 159 "parseunits.y"
+
+
+# PARSE_UNITS -- Parse a units string into the internal format
+
+pointer procedure parse_units (ab, units)
+
+pointer ab # i: abbreviation hash table
+char units[ARB] # i: expression to be parsed
+#--
+include "parseunits.com"
+
+int len, fd
+pointer sp
+
+string syntax "Syntax error in units"
+
+bool yydebug
+int strlen(), stropen(), yyparse()
+int get_token()
+extern get_token
+
+begin
+ len = strlen (units) + 1
+ fd = stropen (units, len, READ_ONLY)
+
+ call smark (sp)
+ call salloc (tokbuf, SZ_FNAME, TY_CHAR)
+
+ debug = NO
+ yydebug = (debug == YES)
+ nxttok = 0
+ abrev = ab
+ tun = NULL
+
+ if (yyparse (fd, yydebug, get_token) == ERR)
+ call tuniterr (syntax, units)
+
+ call close (fd)
+ call sfree (sp)
+ return (tun)
+end
+
+# GET_TOKEN -- Retrieve next token from units string
+
+int procedure get_token (fd, value)
+
+int fd # i: File containing expression to be lexed
+pointer value # o: Address on parse stack to store token
+#--
+include "parseunits.com"
+
+char ch
+int type, index, powers[4]
+pointer sp, typename, token
+
+string pownames "sq,square,cu,cubic"
+data powers / Y_SQ, Y_SQ, Y_CU, Y_CU /
+
+bool streq()
+int getc(), word_match()
+
+begin
+ call smark (sp)
+ call salloc (typename, SZ_FNAME, TY_CHAR)
+
+ token = tokbuf + nxttok
+ Memi[value] = token
+
+ repeat {
+ ch = getc (fd, ch)
+ } until (ch != ' ' && ch != '\t')
+
+ if (ch == EOF) {
+ type = Y_DONE
+ call strcpy ("END", Memc[typename], SZ_FNAME)
+
+ } else if (IS_ALPHA (ch)) {
+ type = Y_ID
+ call strcpy ("IDENT", Memc[typename], SZ_FNAME)
+
+ while (IS_ALPHA (ch)) {
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+ ch = getc (fd, ch)
+ }
+ call ungetc (fd, ch)
+
+ Memc[tokbuf+nxttok] = EOS
+ index = word_match (Memc[token], pownames)
+
+ if (index > 0) {
+ type = powers[index]
+ call strcpy ("POWER", Memc[typename], SZ_FNAME)
+
+ } else if (streq (Memc[token], "per")) {
+ type = Y_DIV
+ call strcpy ("DIV", Memc[typename], SZ_FNAME)
+ }
+
+ } else if (ch == '-' || IS_DIGIT (ch)) {
+ type = Y_NUM
+ call strcpy ("NUMBER", Memc[typename], SZ_FNAME)
+
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+ ch = getc (fd, ch)
+
+ while (IS_DIGIT (ch)) {
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+ ch = getc (fd, ch)
+ }
+ call ungetc (fd, ch)
+
+ } else {
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+
+ switch (ch) {
+ case '*':
+ ch = getc (fd, ch)
+ if (ch == '*') {
+ type = Y_POW
+ call strcpy ("EXPON", Memc[typename], SZ_FNAME)
+
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+ } else {
+ type = Y_MUL
+ call strcpy ("MUL", Memc[typename], SZ_FNAME)
+
+ call ungetc (fd, ch)
+ }
+
+ case '/':
+ type = Y_DIV
+ call strcpy ("DIV", Memc[typename], SZ_FNAME)
+
+ case '^':
+ type = Y_POW
+ call strcpy ("EXPON", Memc[typename], SZ_FNAME)
+
+ default:
+ type = Y_WRONG
+ call strcpy ("ERROR", Memc[typename], SZ_FNAME)
+ }
+ }
+
+ Memc[tokbuf+nxttok] = EOS
+ nxttok = nxttok + 1
+
+ if (debug == YES) {
+ call eprintf ("Token is %s [%s]\n")
+ if (Memc[token] == EOS) {
+ call pargstr ("EOS")
+ } else {
+ call pargstr (Memc[token])
+ }
+ call pargstr (Memc[typename])
+ }
+
+ call sfree (sp)
+ return (type)
+end
+define YYNPROD 15
+define YYLAST 43
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# Parser for yacc output, translated to the IRAF SPP language. The contents
+# of this file form the bulk of the source of the parser produced by Yacc.
+# Yacc recognizes several macros in the yaccpar input source and replaces
+# them as follows:
+# A user suppled "global" definitions and declarations
+# B parser tables
+# C user supplied actions (reductions)
+# The remainder of the yaccpar code is not changed.
+
+define yystack_ 10 # statement labels for gotos
+define yynewstate_ 20
+define yydefault_ 30
+define yyerrlab_ 40
+define yyabort_ 50
+
+define YYFLAG (-1000) # defs used in user actions
+define YYERROR goto yyerrlab_
+define YYACCEPT return (OK)
+define YYABORT return (ERR)
+
+
+# YYPARSE -- Parse the input stream, returning OK if the source is
+# syntactically acceptable (i.e., if compilation is successful),
+# otherwise ERR. The parameters YYMAXDEPTH and YYOPLEN must be
+# supplied by the caller in the %{ ... %} section of the Yacc source.
+# The token value stack is a dynamically allocated array of operand
+# structures, with the length and makeup of the operand structure being
+# application dependent.
+
+int procedure yyparse (fd, yydebug, yylex)
+
+int fd # stream to be parsed
+bool yydebug # print debugging information?
+int yylex() # user-supplied lexical input function
+extern yylex()
+
+short yys[YYMAXDEPTH] # parser stack -- stacks tokens
+pointer yyv # pointer to token value stack
+pointer yyval # value returned by action
+pointer yylval # value of token
+int yyps # token stack pointer
+pointer yypv # value stack pointer
+int yychar # current input token number
+int yyerrflag # error recovery flag
+int yynerrs # number of errors
+
+short yyj, yym # internal variables
+pointer yysp, yypvt
+short yystate, yyn
+int yyxi, i
+errchk salloc, yylex
+
+
+include "parseunits.com"
+
+char units[SZ_FNAME]
+
+int num_unstr()
+pointer mul_unstr(), div_unstr(), pow_unstr(), set_unstr()
+
+short yyexca[6]
+data (yyexca(i),i= 1, 6) / -1, 1, 0, -1, -2, 0/
+short yyact[43]
+data (yyact(i),i= 1, 8) / 23, 10, 11, 13, 13, 12, 11, 13/
+data (yyact(i),i= 9, 16) / 12, 11, 13, 17, 3, 24, 16, 4/
+data (yyact(i),i= 17, 24) / 22, 8, 9, 7, 4, 19, 8, 9/
+data (yyact(i),i= 25, 32) / 7, 8, 9, 7, 18, 2, 6, 5/
+data (yyact(i),i= 33, 40) / 1, 0, 14, 0, 15, 0, 0, 0/
+data (yyact(i),i= 41, 43) / 0, 20, 21/
+short yypact[25]
+data (yypact(i),i= 1, 8) /-244,-1000,-257,-1000,-239,-236,-1000,-253/
+data (yypact(i),i= 9, 16) /-235,-242,-1000,-239,-239,-248,-260,-1000/
+data (yypact(i),i= 17, 24) /-251,-1000,-1000,-1000,-263,-264,-1000,-1000/
+data (yypact(i),i= 25, 25) /-1000/
+short yypgo[5]
+data (yypgo(i),i= 1, 5) / 0, 32, 29, 31, 30/
+short yyr1[15]
+data (yyr1(i),i= 1, 8) / 0, 1, 1, 2, 2, 2, 2, 2/
+data (yyr1(i),i= 9, 15) / 3, 3, 4, 4, 4, 4, 4/
+short yyr2[15]
+data (yyr2(i),i= 1, 8) / 0, 2, 1, 3, 3, 3, 3, 1/
+data (yyr2(i),i= 9, 15) / 2, 1, 3, 2, 2, 2, 1/
+short yychk[25]
+data (yychk(i),i= 1, 8) /-1000, -1, -2, 256, 259, -3, -4, 263/
+data (yychk(i),i= 9, 16) / 261, 262, 258, 266, 265, 267, -2, -4/
+data (yychk(i),i= 17, 24) / 267, 264, 263, 263, -2, -2, 264, 260/
+data (yychk(i),i= 25, 25) / 264/
+short yydef[25]
+data (yydef(i),i= 1, 8) / 0, -2, 0, 2, 0, 7, 9, 14/
+data (yydef(i),i= 9, 16) / 0, 0, 1, 0, 0, 0, 0, 8/
+data (yydef(i),i= 17, 24) / 0, 11, 12, 13, 4, 5, 6, 3/
+data (yydef(i),i= 25, 25) / 10/
+
+begin
+ call smark (yysp)
+ call salloc (yyv, (YYMAXDEPTH+2) * YYOPLEN, TY_STRUCT)
+
+ # Initialization. The first element of the dynamically allocated
+ # token value stack (yyv) is used for yyval, the second for yylval,
+ # and the actual stack starts with the third element.
+
+ yystate = 0
+ yychar = -1
+ yynerrs = 0
+ yyerrflag = 0
+ yyps = 0
+ yyval = yyv
+ yylval = yyv + YYOPLEN
+ yypv = yylval
+
+yystack_
+ # SHIFT -- Put a state and value onto the stack. The token and
+ # value stacks are logically the same stack, implemented as two
+ # separate arrays.
+
+ if (yydebug) {
+ call printf ("state %d, char 0%o\n")
+ call pargs (yystate)
+ call pargi (yychar)
+ }
+ yyps = yyps + 1
+ yypv = yypv + YYOPLEN
+ if (yyps > YYMAXDEPTH) {
+ call sfree (yysp)
+ call eprintf ("yacc stack overflow\n")
+ return (ERR)
+ }
+ yys[yyps] = yystate
+ YYMOVE (yyval, yypv)
+
+yynewstate_
+ # Process the new state.
+ yyn = yypact[yystate+1]
+
+ if (yyn <= YYFLAG)
+ goto yydefault_ # simple state
+
+ # The variable "yychar" is the lookahead token.
+ if (yychar < 0) {
+ yychar = yylex (fd, yylval)
+ if (yychar < 0)
+ yychar = 0
+ }
+ yyn = yyn + yychar
+ if (yyn < 0 || yyn >= YYLAST)
+ goto yydefault_
+
+ yyn = yyact[yyn+1]
+ if (yychk[yyn+1] == yychar) { # valid shift
+ yychar = -1
+ YYMOVE (yylval, yyval)
+ yystate = yyn
+ if (yyerrflag > 0)
+ yyerrflag = yyerrflag - 1
+ goto yystack_
+ }
+
+yydefault_
+ # Default state action.
+
+ yyn = yydef[yystate+1]
+ if (yyn == -2) {
+ if (yychar < 0) {
+ yychar = yylex (fd, yylval)
+ if (yychar < 0)
+ yychar = 0
+ }
+
+ # Look through exception table.
+ yyxi = 1
+ while ((yyexca[yyxi] != (-1)) || (yyexca[yyxi+1] != yystate))
+ yyxi = yyxi + 2
+ for (yyxi=yyxi+2; yyexca[yyxi] >= 0; yyxi=yyxi+2) {
+ if (yyexca[yyxi] == yychar)
+ break
+ }
+
+ yyn = yyexca[yyxi+1]
+ if (yyn < 0) {
+ call sfree (yysp)
+ return (OK) # ACCEPT -- all done
+ }
+ }
+
+
+ # SYNTAX ERROR -- resume parsing if possible.
+
+ if (yyn == 0) {
+ switch (yyerrflag) {
+ case 0, 1, 2:
+ if (yyerrflag == 0) { # brand new error
+ call eprintf ("syntax error\n")
+yyerrlab_
+ yynerrs = yynerrs + 1
+ # fall through...
+ }
+
+ # case 1:
+ # case 2: incompletely recovered error ... try again
+ yyerrflag = 3
+
+ # Find a state where "error" is a legal shift action.
+ while (yyps >= 1) {
+ yyn = yypact[yys[yyps]+1] + YYERRCODE
+ if ((yyn >= 0) && (yyn < YYLAST) &&
+ (yychk[yyact[yyn+1]+1] == YYERRCODE)) {
+ # Simulate a shift of "error".
+ yystate = yyact[yyn+1]
+ goto yystack_
+ }
+ yyn = yypact[yys[yyps]+1]
+
+ # The current yyps has no shift on "error", pop stack.
+ if (yydebug) {
+ call printf ("error recovery pops state %d, ")
+ call pargs (yys[yyps])
+ call printf ("uncovers %d\n")
+ call pargs (yys[yyps-1])
+ }
+ yyps = yyps - 1
+ yypv = yypv - YYOPLEN
+ }
+
+ # ABORT -- There is no state on the stack with an error shift.
+yyabort_
+ call sfree (yysp)
+ return (ERR)
+
+
+ case 3: # No shift yet; clobber input char.
+
+ if (yydebug) {
+ call printf ("error recovery discards char %d\n")
+ call pargi (yychar)
+ }
+
+ if (yychar == 0)
+ goto yyabort_ # don't discard EOF, quit
+ yychar = -1
+ goto yynewstate_ # try again in the same state
+ }
+ }
+
+
+ # REDUCE -- Reduction by production yyn.
+
+ if (yydebug) {
+ call printf ("reduce %d\n")
+ call pargs (yyn)
+ }
+ yyps = yyps - yyr2[yyn+1]
+ yypvt = yypv
+ yypv = yypv - yyr2[yyn+1] * YYOPLEN
+ YYMOVE (yypv + YYOPLEN, yyval)
+ yym = yyn
+
+ # Consult goto table to find next state.
+ yyn = yyr1[yyn+1]
+ yyj = yypgo[yyn+1] + yys[yyps] + 1
+ if (yyj >= YYLAST)
+ yystate = yyact[yypgo[yyn+1]+1]
+ else {
+ yystate = yyact[yyj+1]
+ if (yychk[yystate+1] != -yyn)
+ yystate = yyact[yypgo[yyn+1]+1]
+ }
+
+ # Perform action associated with the grammar rule, if any.
+ switch (yym) {
+
+case 1:
+# line 28 "parseunits.y"
+{
+ # Normal exit. Return pointer to units structure
+ if (debug == YES)
+ call eprintf ("\n")
+
+ tun = Memi[yypvt-YYOPLEN]
+ return (OK)
+ }
+case 2:
+# line 36 "parseunits.y"
+{
+ # Syntax error
+ if (debug == YES)
+ call eprintf ("\n")
+
+ return (ERR)
+ }
+case 3:
+# line 45 "parseunits.y"
+{
+ # Parenthesized expression
+ Memi[yyval] = Memi[yypvt-2*YYOPLEN]
+ }
+case 4:
+# line 49 "parseunits.y"
+{
+ # Multiply two units expressions
+ Memi[yyval] = mul_unstr (Memi[yypvt-2*YYOPLEN], Memi[yypvt])
+ call free_unstr (Memi[yypvt-2*YYOPLEN])
+ call free_unstr (Memi[yypvt])
+
+ if (debug == YES) {
+ call str_unstr (Memi[yyval], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+case 5:
+# line 61 "parseunits.y"
+{
+ # Divide two units expressions
+ Memi[yyval] = div_unstr (Memi[yypvt-2*YYOPLEN], Memi[yypvt])
+ call free_unstr (Memi[yypvt-2*YYOPLEN])
+ call free_unstr (Memi[yypvt])
+
+ if (debug == YES) {
+ call str_unstr (Memi[yyval], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+case 6:
+# line 73 "parseunits.y"
+{
+ # Raise expression to a power
+ Memi[yyval] = pow_unstr (Memi[yypvt-2*YYOPLEN], num_unstr (Memc[Memi[yypvt]]))
+ call free_unstr (Memi[yypvt-2*YYOPLEN])
+
+ if (debug == YES) {
+ call str_unstr (Memi[yyval], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+case 7:
+# line 84 "parseunits.y"
+{
+ # List of terms
+ Memi[yyval] = Memi[yypvt]
+ }
+case 8:
+# line 89 "parseunits.y"
+{
+ # Implicit multiplication
+ Memi[yyval] = mul_unstr (Memi[yypvt-YYOPLEN], Memi[yypvt])
+ call free_unstr (Memi[yypvt-YYOPLEN])
+ call free_unstr (Memi[yypvt])
+
+ if (debug == YES) {
+ call str_unstr (Memi[yyval], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+case 9:
+# line 101 "parseunits.y"
+{
+ # Simple term
+ Memi[yyval] = Memi[yypvt]
+ }
+case 10:
+# line 106 "parseunits.y"
+{
+ # Raise units to a power
+ Memi[yyval] = set_unstr (abrev, Memc[Memi[yypvt-2*YYOPLEN]],
+ num_unstr (Memc[Memi[yypvt]]))
+
+ if (debug == YES) {
+ call str_unstr (Memi[yyval], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+case 11:
+# line 117 "parseunits.y"
+{
+ # Implicitly raise to a power
+ Memi[yyval] = set_unstr (abrev, Memc[Memi[yypvt-YYOPLEN]],
+ num_unstr (Memc[Memi[yypvt]]))
+
+ if (debug == YES) {
+ call str_unstr (Memi[yyval], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+case 12:
+# line 128 "parseunits.y"
+{
+ # Cubic prefix
+ Memi[yyval] = set_unstr (abrev, Memc[Memi[yypvt]], 3)
+
+ if (debug == YES) {
+ call str_unstr (Memi[yyval], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+case 13:
+# line 138 "parseunits.y"
+{
+ # Square prefix
+ Memi[yyval] = set_unstr (abrev, Memc[Memi[yypvt]], 2)
+
+ if (debug == YES) {
+ call str_unstr (Memi[yyval], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+case 14:
+# line 148 "parseunits.y"
+{
+ # Simple name
+ Memi[yyval] = set_unstr (abrev, Memc[Memi[yypvt]], 1)
+
+ if (debug == YES) {
+ call str_unstr (Memi[yyval], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ } }
+
+ goto yystack_ # stack new state and value
+end
diff --git a/pkg/utilities/nttools/tunits/parseunits.y b/pkg/utilities/nttools/tunits/parseunits.y
new file mode 100644
index 00000000..088395ac
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/parseunits.y
@@ -0,0 +1,322 @@
+%{
+include <ctype.h>
+
+define YYMAXDEPTH 32
+define YYOPLEN 1
+define yyparse unit_parse
+
+define SZ_SHORTSTR 31
+
+%L
+include "parseunits.com"
+
+char units[SZ_FNAME]
+
+int num_unstr()
+pointer mul_unstr(), div_unstr(), pow_unstr(), set_unstr()
+
+%}
+
+%token Y_WRONG Y_DONE Y_LPAR Y_RPAR Y_CU Y_SQ Y_ID Y_NUM
+
+%left Y_DIV
+%left Y_MUL
+%right Y_POW
+
+%%
+
+unit : expr Y_DONE {
+ # Normal exit. Return pointer to units structure
+ if (debug == YES)
+ call eprintf ("\n")
+
+ tun = Memi[$1]
+ return (OK)
+ }
+ | error {
+ # Syntax error
+ if (debug == YES)
+ call eprintf ("\n")
+
+ return (ERR)
+ }
+ ;
+
+expr : Y_LPAR expr Y_RPAR {
+ # Parenthesized expression
+ Memi[$$] = Memi[$1]
+ }
+ | expr Y_MUL expr {
+ # Multiply two units expressions
+ Memi[$$] = mul_unstr (Memi[$1], Memi[$3])
+ call free_unstr (Memi[$1])
+ call free_unstr (Memi[$3])
+
+ if (debug == YES) {
+ call str_unstr (Memi[$$], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+ | expr Y_DIV expr {
+ # Divide two units expressions
+ Memi[$$] = div_unstr (Memi[$1], Memi[$3])
+ call free_unstr (Memi[$1])
+ call free_unstr (Memi[$3])
+
+ if (debug == YES) {
+ call str_unstr (Memi[$$], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+ | expr Y_POW Y_NUM {
+ # Raise expression to a power
+ Memi[$$] = pow_unstr (Memi[$1], num_unstr (Memc[Memi[$3]]))
+ call free_unstr (Memi[$1])
+
+ if (debug == YES) {
+ call str_unstr (Memi[$$], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+ | termlist {
+ # List of terms
+ Memi[$$] = Memi[$1]
+ }
+ ;
+termlist: termlist term {
+ # Implicit multiplication
+ Memi[$$] = mul_unstr (Memi[$1], Memi[$2])
+ call free_unstr (Memi[$1])
+ call free_unstr (Memi[$2])
+
+ if (debug == YES) {
+ call str_unstr (Memi[$$], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+ | term {
+ # Simple term
+ Memi[$$] = Memi[$1]
+ }
+ ;
+term : Y_ID Y_POW Y_NUM {
+ # Raise units to a power
+ Memi[$$] = set_unstr (abrev, Memc[Memi[$1]],
+ num_unstr (Memc[Memi[$3]]))
+
+ if (debug == YES) {
+ call str_unstr (Memi[$$], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+ | Y_ID Y_NUM {
+ # Implicitly raise to a power
+ Memi[$$] = set_unstr (abrev, Memc[Memi[$1]],
+ num_unstr (Memc[Memi[$2]]))
+
+ if (debug == YES) {
+ call str_unstr (Memi[$$], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+ | Y_CU Y_ID {
+ # Cubic prefix
+ Memi[$$] = set_unstr (abrev, Memc[Memi[$2]], 3)
+
+ if (debug == YES) {
+ call str_unstr (Memi[$$], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+ | Y_SQ Y_ID {
+ # Square prefix
+ Memi[$$] = set_unstr (abrev, Memc[Memi[$2]], 2)
+
+ if (debug == YES) {
+ call str_unstr (Memi[$$], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+ | Y_ID {
+ # Simple name
+ Memi[$$] = set_unstr (abrev, Memc[Memi[$1]], 1)
+
+ if (debug == YES) {
+ call str_unstr (Memi[$$], units, SZ_FNAME)
+ call eprintf ("Units are %s\n")
+ call pargstr (units)
+ }
+ }
+ ;
+%%
+
+# PARSE_UNITS -- Parse a units string into the internal format
+
+pointer procedure parse_units (ab, units)
+
+pointer ab # i: abbreviation hash table
+char units[ARB] # i: expression to be parsed
+#--
+include "parseunits.com"
+
+int len, fd
+pointer sp
+
+string syntax "Syntax error in units"
+
+bool yydebug
+int strlen(), stropen(), yyparse()
+int get_token()
+extern get_token
+
+begin
+ len = strlen (units) + 1
+ fd = stropen (units, len, READ_ONLY)
+
+ call smark (sp)
+ call salloc (tokbuf, SZ_FNAME, TY_CHAR)
+
+ debug = NO
+ yydebug = (debug == YES)
+ nxttok = 0
+ abrev = ab
+ tun = NULL
+
+ if (yyparse (fd, yydebug, get_token) == ERR)
+ call tuniterr (syntax, units)
+
+ call close (fd)
+ call sfree (sp)
+ return (tun)
+end
+
+# GET_TOKEN -- Retrieve next token from units string
+
+int procedure get_token (fd, value)
+
+int fd # i: File containing expression to be lexed
+pointer value # o: Address on parse stack to store token
+#--
+include "parseunits.com"
+
+char ch
+int type, index, powers[4]
+pointer sp, typename, token
+
+string pownames "sq,square,cu,cubic"
+data powers / Y_SQ, Y_SQ, Y_CU, Y_CU /
+
+bool streq()
+int getc(), word_match()
+
+begin
+ call smark (sp)
+ call salloc (typename, SZ_FNAME, TY_CHAR)
+
+ token = tokbuf + nxttok
+ Memi[value] = token
+
+ repeat {
+ ch = getc (fd, ch)
+ } until (ch != ' ' && ch != '\t')
+
+ if (ch == EOF) {
+ type = Y_DONE
+ call strcpy ("END", Memc[typename], SZ_FNAME)
+
+ } else if (IS_ALPHA (ch)) {
+ type = Y_ID
+ call strcpy ("IDENT", Memc[typename], SZ_FNAME)
+
+ while (IS_ALPHA (ch)) {
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+ ch = getc (fd, ch)
+ }
+ call ungetc (fd, ch)
+
+ Memc[tokbuf+nxttok] = EOS
+ index = word_match (Memc[token], pownames)
+
+ if (index > 0) {
+ type = powers[index]
+ call strcpy ("POWER", Memc[typename], SZ_FNAME)
+
+ } else if (streq (Memc[token], "per")) {
+ type = Y_DIV
+ call strcpy ("DIV", Memc[typename], SZ_FNAME)
+ }
+
+ } else if (ch == '-' || IS_DIGIT (ch)) {
+ type = Y_NUM
+ call strcpy ("NUMBER", Memc[typename], SZ_FNAME)
+
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+ ch = getc (fd, ch)
+
+ while (IS_DIGIT (ch)) {
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+ ch = getc (fd, ch)
+ }
+ call ungetc (fd, ch)
+
+ } else {
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+
+ switch (ch) {
+ case '*':
+ ch = getc (fd, ch)
+ if (ch == '*') {
+ type = Y_POW
+ call strcpy ("EXPON", Memc[typename], SZ_FNAME)
+
+ Memc[tokbuf+nxttok] = ch
+ nxttok = nxttok + 1
+ } else {
+ type = Y_MUL
+ call strcpy ("MUL", Memc[typename], SZ_FNAME)
+
+ call ungetc (fd, ch)
+ }
+
+ case '/':
+ type = Y_DIV
+ call strcpy ("DIV", Memc[typename], SZ_FNAME)
+
+ case '^':
+ type = Y_POW
+ call strcpy ("EXPON", Memc[typename], SZ_FNAME)
+
+ default:
+ type = Y_WRONG
+ call strcpy ("ERROR", Memc[typename], SZ_FNAME)
+ }
+ }
+
+ Memc[tokbuf+nxttok] = EOS
+ nxttok = nxttok + 1
+
+ if (debug == YES) {
+ call eprintf ("Token is %s [%s]\n")
+ if (Memc[token] == EOS) {
+ call pargstr ("EOS")
+ } else {
+ call pargstr (Memc[token])
+ }
+ call pargstr (Memc[typename])
+ }
+
+ call sfree (sp)
+ return (type)
+end
diff --git a/pkg/utilities/nttools/tunits/tuniterr.x b/pkg/utilities/nttools/tunits/tuniterr.x
new file mode 100644
index 00000000..25a750c5
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/tuniterr.x
@@ -0,0 +1,24 @@
+#* HISTORY *
+#* B.Simon 07-Jan-99 Original
+
+# TUNITERR -- Print error message for tunits
+
+procedure tuniterr (errstr, errval)
+
+char errstr[ARB] # i: error message string
+char errval[ARB] # i: value which caused error
+#--
+pointer sp, errmsg
+
+begin
+ call smark (sp)
+ call salloc (errmsg, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[errmsg], SZ_LINE, "%s (%s)")
+ call pargstr (errstr)
+ call pargstr (errval)
+
+ call error (1, Memc[errmsg])
+
+ call sfree (sp)
+end
diff --git a/pkg/utilities/nttools/tunits/tunits.h b/pkg/utilities/nttools/tunits/tunits.h
new file mode 100644
index 00000000..f5c2162a
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/tunits.h
@@ -0,0 +1,14 @@
+# TUNITS.H -- Structure and constants used by tunits
+
+define MAXUNIT 8
+define LEN_UNIT 15
+
+define LEN_TUNSTRUCT (SZ_DOUBLE+2*MAXUNIT)
+
+define TUN_FACTOR Memd[P2D($1)] # conversion factor
+define TUN_UNPTR Memi[$1+SZ_DOUBLE+$2] # ptr to units string
+define TUN_POWER Memi[$1+SZ_DOUBLE+MAXUNIT+$2] # units power
+
+define TUN_UNITS Memc[TUN_UNPTR($1,$2)] # units string
+
+define FINALS "m,kg,s,rad,hz"
diff --git a/pkg/utilities/nttools/tunits/tunits.x b/pkg/utilities/nttools/tunits/tunits.x
new file mode 100644
index 00000000..526bca11
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/tunits.x
@@ -0,0 +1,112 @@
+include <tbset.h>
+include "tunits.h"
+
+#* HISTORY *
+#* B.Simon 07-Jan-99 Original
+
+# TUNITS -- Convert table column from one set of units to another
+
+procedure tunits ()
+
+#--
+pointer table # table name
+pointer column # column name
+pointer newunits # new column units
+pointer oldunits # old column units
+pointer abrevtab # table of unit abbreviations
+pointer unittab # table of unit conversions
+bool verbose # print diagnostic messages
+
+double factor
+int type
+pointer sp, tp, cp, ab, ut, punit1, punit2
+
+string nocolumn "Column not found"
+string unitblank "Units parameter is blank"
+string notfloat "Table column is not floating point"
+
+bool clgetb(), isblank()
+double find_factor()
+int tbcigi()
+pointer tbtopn(), read_abrev(), read_units(), parse_units()
+
+begin
+ # Allocate memory for temporary strings
+
+ call smark (sp)
+ call salloc (table, SZ_FNAME, TY_CHAR)
+ call salloc (column, SZ_FNAME, TY_CHAR)
+ call salloc (newunits, SZ_FNAME, TY_CHAR)
+ call salloc (oldunits, SZ_FNAME, TY_CHAR)
+ call salloc (abrevtab, SZ_FNAME, TY_CHAR)
+ call salloc (unittab, SZ_FNAME, TY_CHAR)
+
+ # Read required task parameters
+
+ call clgstr ("table", Memc[table], SZ_FNAME)
+ call clgstr ("column", Memc[column], SZ_FNAME)
+ call clgstr ("newunits", Memc[newunits], SZ_FNAME)
+ call clgstr ("oldunits", Memc[oldunits], SZ_FNAME)
+ call clgstr ("abrevtab", Memc[abrevtab], SZ_FNAME)
+ call clgstr ("unittab", Memc[unittab], SZ_FNAME)
+ verbose = clgetb ("verbose")
+
+ # Open table, find column
+
+ tp = tbtopn (Memc[table], READ_WRITE, NULL)
+ call tbcfnd (tp, Memc[column], cp, 1)
+ if (cp == NULL)
+ call tuniterr (nocolumn, Memc[column])
+
+ # Read column units if old units are blank
+
+ if (isblank (Memc[oldunits]))
+ call tbcigt (cp, TBL_COL_UNITS, Memc[oldunits], SZ_FNAME)
+
+ call strlwr (Memc[oldunits])
+ call strlwr (Memc[newunits])
+
+ # Check to see if units are not blank
+
+ if (isblank (Memc[oldunits]))
+ call tuniterr (unitblank, "oldunits")
+
+ if (isblank (Memc[newunits]))
+ call tuniterr (unitblank, "newunits")
+
+ # Check to see if column is floating point
+
+ type = tbcigi (cp, TBL_COL_DATATYPE)
+ if (type != TY_REAL && type != TY_DOUBLE)
+ call tuniterr (notfloat, Memc[column])
+
+ # Read units and abbreviation tables into hashes
+
+ ab = read_abrev (Memc[abrevtab])
+ ut = read_units (ab, Memc[unittab])
+
+ # Convert units to internal form
+
+ punit1 = parse_units (ab, Memc[oldunits])
+ punit2 = parse_units (ab, Memc[newunits])
+
+ # Find conversion factor between units
+
+ factor = find_factor (ut, punit1, punit2, verbose)
+
+ # Apply conversion factor to table column
+
+ call convert_col (tp, cp, Memc[newunits], factor)
+
+ # Close table and free allocated memory
+
+ call tbtclo (tp)
+
+ call free_abrev (ab)
+ call free_units (ut)
+
+ call free_unstr (punit1)
+ call free_unstr (punit2)
+
+ call sfree (sp)
+end
diff --git a/pkg/utilities/nttools/tunits/unhash.x b/pkg/utilities/nttools/tunits/unhash.x
new file mode 100644
index 00000000..3ae9c24e
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/unhash.x
@@ -0,0 +1,212 @@
+# A set of procedures that implement a generic hash table. The hash table
+# stores the key, plus a pointer to the value structure. It should be
+# wrapped in a more specific set of calls that can read the value structure
+
+define LEN_UNHSTRUCT 5
+
+define UNH_SIZE Memi[$1]
+define UNH_NEXT Memi[$1+1]
+define UNH_KEYBUF Memi[$1+2]
+define UNH_VALBUF Memi[$1+3]
+define UNH_STRBUF Memi[$1+4]
+
+define UNH_KEY Memi[UNH_KEYBUF($1)+$2]
+define UNH_VALUE Memi[UNH_VALBUF($1)+$2]
+
+#* HISTORY *
+#* B.Simon 07-Jan-99 Original
+
+# ADD_UNHASH -- Add a new keyword and value to the hash table
+
+procedure add_unhash (hash, keyword, value)
+
+pointer hash # i: Descriptor of hash table
+char keyword[ARB] # i: Keyword to add to hash table
+pointer value # i: Value descriptor
+#--
+int index, nc
+
+string duplicate "Cannot add duplicate keyword to hash table"
+
+int gstrcpy(), loc_unhash()
+
+begin
+ # Find where keyword should be inserted
+
+ index = loc_unhash (hash, keyword)
+
+ # Adding duplicate keywords is not allowed
+
+ if (UNH_KEY(hash,index) != NULL) {
+ call tuniterr (duplicate, keyword)
+
+ } else {
+ UNH_KEY(hash,index) = UNH_NEXT(hash)
+ UNH_VALUE(hash,index) = value
+
+ nc = gstrcpy (keyword, Memc[UNH_NEXT(hash)], ARB)
+ UNH_NEXT(hash) = UNH_NEXT(hash) + nc + 1
+ }
+end
+
+# CALC_UNHASH -- Calculate hash index and step size from keyword
+
+procedure calc_unhash (hash, keyword, index, step)
+
+pointer hash # i: Descriptor of hash table
+char keyword[ARB] # i: Keyword to search for in hash table
+int index # o: Location to place keyword at in hash
+int step # o: Step size in case location is filled
+#--
+int ic
+
+begin
+ # Standard hash table function based on munging characters
+
+ index = 0
+ step = 0
+
+ for (ic = 1; keyword[ic] != EOS; ic = ic + 1) {
+ index = 2 * index + keyword[ic]
+ step = step + keyword[ic]
+ }
+
+ # This line ensures the step size is odd
+
+ step = step - mod (step, 2) + 1
+end
+
+# EACH_UNHASH -- Retrieve values from hash table serially
+
+int procedure each_unhash (hash, index, keyword, value, maxch)
+
+pointer hash # i: Descriptor of hash table
+int index # u: Index of next slot in hash table to examine
+char keyword[ARB] # o: Keyword name
+pointer value # o: Keyword value
+int maxch # i: Maximum length of keyword
+#--
+
+begin
+ while (index < UNH_SIZE(hash)) {
+ if (UNH_KEY(hash,index) != NULL) {
+ call strcpy (Memc[UNH_KEY(hash,index)], keyword, maxch)
+ value = UNH_VALUE(hash,index)
+ index = index + 1
+ return (OK)
+ }
+
+ index = index + 1
+ }
+
+ return (EOF)
+end
+
+# FREE_UNHASH -- Free a hash table
+
+procedure free_unhash (hash)
+
+pointer hash # i: hash table descriptor
+#--
+
+begin
+ # This code assumes that all memory associated
+ # with the values has already been freed
+
+ call mfree (UNH_STRBUF(hash), TY_CHAR)
+ call mfree (UNH_VALBUF(hash), TY_INT)
+ call mfree (UNH_KEYBUF(hash), TY_INT)
+ call mfree (hash, TY_INT)
+end
+
+# GET_UNHASH -- Return a keyword's value from a hash
+
+int procedure get_unhash (hash, keyword, value)
+
+pointer hash # i: Descriptor of hash table
+char keyword[ARB] # i: Keyword to add to hash table
+pointer value # o: pointer to hash table value
+#--
+int index, status
+
+int loc_unhash ()
+
+begin
+ # The keyword is found if its slot is not null
+
+ index = loc_unhash (hash, keyword)
+
+ if (UNH_KEY(hash,index) == NULL) {
+ value = NULL
+ status = NO
+ } else {
+ value = UNH_VALUE(hash,index)
+ status = YES
+ }
+
+ return (status)
+end
+
+# LOC_UNHASH -- Return index of location where a key should be inserted
+
+int procedure loc_unhash (hash, keyword)
+
+pointer hash # i: Descriptor of hash table
+char keyword[ARB] # i: Keyword to add to hash table
+#--
+int index, step
+
+bool streq()
+
+begin
+ # Calculate initial guess at position in hash table
+ # and step size in case guessed position is already filled
+
+ call calc_unhash (hash, keyword, index, step)
+ index = mod (index, UNH_SIZE(hash))
+
+ # Loop until an empty slot is found or the keyword is matched
+
+ repeat {
+ if (UNH_KEY(hash,index) == NULL) {
+ break
+
+ } else if (streq (Memc[UNH_KEY(hash,index)], keyword)) {
+ break
+ }
+
+ index = mod (index + step, UNH_SIZE(hash))
+ }
+
+ return (index)
+end
+
+# NEW_UNHASH -- Create a new hash table
+
+pointer procedure new_unhash (nkey, keysize)
+
+int nkey # i: number of keywords in the hash
+int keysize # i: maximum length of a key
+#--
+int size
+pointer hash
+
+begin
+ # Find a power of two greater than the number of keywords
+
+ for (size = 1; size < 2 * nkey; size = 2 * size)
+ ;
+
+ # Allocate structure for hash and set initial values
+
+ call malloc (hash, LEN_UNHSTRUCT, TY_INT)
+ call calloc (UNH_KEYBUF(hash), size, TY_INT)
+ call calloc (UNH_VALBUF(hash), size, TY_INT)
+ call malloc (UNH_STRBUF(hash), size*(keysize+1), TY_CHAR)
+
+ UNH_SIZE(hash) = size
+ UNH_NEXT(hash) = UNH_STRBUF(hash)
+
+ return (hash)
+end
+
diff --git a/pkg/utilities/nttools/tunits/units.tab b/pkg/utilities/nttools/tunits/units.tab
new file mode 100644
index 00000000..4d2c1f9c
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/units.tab
@@ -0,0 +1,60 @@
+# Conversion factors for tunits task
+#
+# Read this table as "There are <factor> <from> in a <to>."
+# For example, "There are 100 centimeters in a meter."
+# The last column, swap, does not change the sense of the sentence
+# but does change the direction that the conversion is applied,
+# For example, "60 seconds in a minute" is actually a conversion
+# from minutes to seconds because swap is yes. Unit conversions
+# should set swap to yes when the desired conversion is not an exact
+# value, but its inverse is. Only one conversion is allowed per
+# unit, which simplifies the program logic considerably. Conversions
+# should be chosen so that they ultimately resolve to MKS units. To
+# prevent endless loops conversions from the fundamental units are
+# checked for and forbidden. However, the program does not check for
+# other loops, so be careful when adding new conversions!
+#
+# factor from to swap?
+#----------------------------------------------------
+100 centimeter meter no
+1e-3 kilometer meter no
+1e3 millimeter meter no
+1e6 micron meter no
+1e9 nanometer meter no
+1e10 angstrom meter no
+1e3 gram kilogram no
+1e6 milligram kilogram no
+60 second minute yes
+60 minute hour yes
+24 hour day yes
+365.2421897 day year yes
+57.2957795131 degree radian no
+60 arcminute degree no
+60 arcsecond arcminute no
+1.4959787066e11 meter au yes
+206264.806247 au parsec yes
+1e3 parsec kiloparsec yes
+1e6 parsec megaparsec yes
+9.46073047e15 meter lightyear yes
+1e3 liter m^3 no
+1 newton kg*m/s^2 no
+1 joule kg*m^2/s^2 no
+1 watt kg*m^2/s^3 no
+1 dyne gm*cm/s^2 no
+1 erg gm*cm^2/s^2 no
+2.54 centimeter inch yes
+12 inch foot yes
+5280 foot mile yes
+16 ounce pound no
+0.45359237 pound kilogram no
+1054.4 btu joule no
+4.184 calorie joule no
+1e-3 kilocalorie calorie no
+6.24150648e18 ev joule no
+1e-3 kev ev no
+1e-6 mev ev no
+1e23 jansky erg/s*cm^2*hz no
+1e3 millijansky jansky no
+1e-3 kilohertz hertz no
+1e-6 megahertz hertz no
+1e-9 gigahertz hertz no
diff --git a/pkg/utilities/nttools/tunits/units.x b/pkg/utilities/nttools/tunits/units.x
new file mode 100644
index 00000000..6f4374df
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/units.x
@@ -0,0 +1,162 @@
+include <tbset.h>
+include "tunits.h"
+
+#* HISTORY *
+#* B.Simon 07-Jan-99 Original
+
+# FIND_UNITS -- Find the conversion factor for a set of units
+
+int procedure find_units (ut, units, punit)
+
+pointer ut # i: units hash table descriptor
+char units[ARB] # i: units string
+pointer punit # o: conversion factor as units structure
+#--
+int get_unhash()
+
+begin
+ return (get_unhash (ut, units, punit))
+end
+
+# FREE_UNITS -- Free the abbreviation hash table
+
+procedure free_units (ut)
+
+pointer ut # i: units hash table descriptor
+#--
+int index
+pointer sp, units, punit
+
+int each_unhash()
+
+begin
+ call smark (sp)
+ call salloc (units, LEN_UNIT, TY_CHAR)
+
+ index = 0
+ while (each_unhash (ut, index, Memc[units],
+ punit, LEN_UNIT) != EOF) {
+ if (punit != NULL)
+ call free_unstr (punit)
+ }
+
+ call free_unhash (ut)
+ call sfree (sp)
+end
+
+# READ_UNITS -- Read units conversions from a table and load into a hash
+
+pointer procedure read_units (ab, unittab)
+
+pointer ab # i: abbreviation table descriptor
+char unittab[ARB] # i: units conversion table name
+#--
+bool swap, verbose
+double factor
+int irow, nrow
+pointer sp, temp, oldunits, newunits
+pointer tp, c1, c2, c3, c4
+pointer ut, punit1, punit2, punit3
+
+data verbose / false /
+
+string nocolumn "The units conversion table must have four columns"
+string badfactor "Error in units table: factor must be greater than zero"
+string nofinal "Error in units table: conversion from final units not allowed"
+
+int tbpsta(), word_match()
+pointer tbtopn(), tbcnum(), new_unhash()
+pointer parse_units(), div_unstr()
+
+begin
+ # Dynamic memory for strings
+
+ call smark (sp)
+ call salloc (temp, SZ_FNAME, TY_CHAR)
+ call salloc (oldunits, SZ_FNAME, TY_CHAR)
+ call salloc (newunits, SZ_FNAME, TY_CHAR)
+
+ # Refer to columns numerically because
+ # this is supposed to be a text file
+
+ tp = tbtopn (unittab, READ_ONLY, NULL)
+ c1 = tbcnum (tp, 1)
+ c2 = tbcnum (tp, 2)
+ c3 = tbcnum (tp, 3)
+ c4 = tbcnum (tp, 4)
+
+ if (c1 == NULL || c2 == NULL || c3 == NULL || c4 == NULL)
+ call tuniterr (nocolumn, unittab)
+
+ # Create hash
+
+ nrow = tbpsta (tp, TBL_NROWS)
+ ut = new_unhash (nrow, LEN_UNIT)
+
+ # Read each row into hash
+
+ do irow = 1, nrow {
+ # Read table columns
+
+ call tbegtd (tp, c1, irow, factor)
+ call tbegtt (tp, c2, irow, Memc[oldunits], SZ_FNAME)
+ call tbegtt (tp, c3, irow, Memc[newunits], SZ_FNAME)
+ call tbegtb (tp, c4, irow, swap)
+
+ # Check conversion factor
+
+ if (factor <= 0.0)
+ call tuniterr (badfactor, Memc[oldunits])
+
+ # Swap the units string and the conversion factor
+
+ if (swap) {
+ call strcpy (Memc[oldunits], Memc[temp], SZ_FNAME)
+ call strcpy (Memc[newunits], Memc[oldunits], SZ_FNAME)
+ call strcpy (Memc[temp], Memc[newunits], SZ_FNAME)
+ }
+
+ # Check to see that old units aren't one of the final forms
+
+ if (word_match (Memc[oldunits], FINALS) != 0)
+ call tuniterr (nofinal, Memc[oldunits])
+
+ # Parse the old and new units strings
+
+ call strlwr (Memc[newunits])
+ punit1 = parse_units (ab, Memc[newunits])
+
+ call strlwr (Memc[oldunits])
+ punit2 = parse_units (ab, Memc[oldunits])
+
+ # The conversion factor is ratio of the two sets of units
+
+ punit3 = div_unstr (punit1, punit2)
+ if (swap) {
+ TUN_FACTOR(punit3) = factor
+ } else {
+ TUN_FACTOR(punit3) = 1.0 / factor
+ }
+
+ if (verbose) {
+ call str_unstr (punit3, Memc[temp], SZ_FNAME)
+
+ call eprintf ("The conversion factor is %s\n\n")
+ call pargstr (Memc[temp])
+ }
+
+ # Add it to the hash
+
+ call abrev_unstr (ab, Memc[oldunits], Memc[temp], SZ_FNAME)
+ call add_unhash (ut, Memc[temp], punit3)
+
+ call free_unstr (punit1)
+ call free_unstr (punit2)
+ }
+
+ # Close table and free memory
+
+ call tbtclo (tp)
+ call sfree (sp)
+ return (ut)
+end
diff --git a/pkg/utilities/nttools/tunits/unstr.x b/pkg/utilities/nttools/tunits/unstr.x
new file mode 100644
index 00000000..80bd65ba
--- /dev/null
+++ b/pkg/utilities/nttools/tunits/unstr.x
@@ -0,0 +1,381 @@
+include "tunits.h"
+
+#* HISTORY *
+#* B.Simon 07-Jan-99 Original
+
+# ABREV_UNSTR -- Replace units string with its abbreviation
+
+procedure abrev_unstr (ab, instr, outstr, maxch)
+
+pointer ab # i: abbreviation hash descriptor
+char instr[ARB] # i: string to be abbreviated
+char outstr[ARB] # o: abbreviated string
+int maxch # i: max length of abbreviated string
+#--
+int nc
+pointer sp, temp
+
+int gstrcpy(), find_abrev ()
+
+begin
+ if (find_abrev (ab, instr, outstr, maxch) == YES)
+ return
+
+ call smark (sp)
+ call salloc (temp, LEN_UNIT, TY_CHAR)
+
+ nc = gstrcpy (instr, Memc[temp], LEN_UNIT)
+ if (nc == 1 || instr[nc] != 's') {
+ call strcpy (instr, outstr, maxch)
+
+ } else {
+ Memc[temp+nc-1] = EOS
+ if (find_abrev (ab, Memc[temp], outstr, maxch) == NO)
+ call strcpy (Memc[temp], outstr, maxch)
+ }
+
+ call sfree (sp)
+end
+
+# COPY_UNSTR -- Copy a units descriptor
+
+pointer procedure copy_unstr (punit1)
+
+pointer punit1 # i: units descriptor to be copied
+#--
+int idx
+pointer punit2
+
+begin
+ # Allocate structure to hold units
+
+ call calloc (punit2, LEN_TUNSTRUCT, TY_INT)
+
+ # Copy numeric factor
+
+ TUN_FACTOR(punit2) = TUN_FACTOR(punit1)
+
+ # Copy units and their powers
+
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit1,idx) == NULL)
+ break
+
+ call malloc (TUN_UNPTR(punit2,idx), LEN_UNIT, TY_CHAR)
+ call strcpy (TUN_UNITS(punit1,idx), TUN_UNITS(punit2,idx),
+ LEN_UNIT)
+
+ TUN_POWER(punit2,idx) = TUN_POWER(punit1,idx)
+ }
+
+ return (punit2)
+end
+# DIV_UNSTR -- Divide one set of units by another
+
+pointer procedure div_unstr (punit1, punit2)
+
+pointer punit1 # i: descriptor for first set of units
+pointer punit2 # i: descriptor for second set of units
+#--
+int idx, jdx, kdx, power
+pointer punit3
+
+int find_unstr()
+
+begin
+ # Allocate structure to hold units
+
+ call calloc (punit3, LEN_TUNSTRUCT, TY_INT)
+
+ # Compute the new factor
+
+ TUN_FACTOR(punit3) = TUN_FACTOR(punit1) / TUN_FACTOR(punit2)
+
+ # Find units in both descriptors and subtract their powers
+
+ jdx = 1
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit1,idx) == NULL)
+ break
+
+ kdx = find_unstr (punit2, TUN_UNITS(punit1, idx))
+ if (kdx == 0)
+ next
+
+ power = TUN_POWER(punit1,idx) - TUN_POWER(punit2,kdx)
+ if (power == 0)
+ next
+
+ call malloc (TUN_UNPTR(punit3,jdx), LEN_UNIT, TY_CHAR)
+ call strcpy (TUN_UNITS(punit1,idx), TUN_UNITS(punit3,jdx),
+ LEN_UNIT)
+
+ TUN_POWER(punit3,jdx) = power
+ jdx = jdx + 1
+
+ }
+
+ # Find units only in a single descriptor and add them to the
+ # new descriptor
+
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit1,idx) == NULL)
+ break
+
+ if (find_unstr (punit2, TUN_UNITS(punit1, idx)) == 0) {
+ call malloc (TUN_UNPTR(punit3,jdx), LEN_UNIT, TY_CHAR)
+ call strcpy (TUN_UNITS(punit1,idx), TUN_UNITS(punit3,jdx),
+ LEN_UNIT)
+
+ TUN_POWER(punit3,jdx) = TUN_POWER(punit1,idx)
+ jdx = jdx + 1
+ }
+ }
+
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit2,idx) == NULL)
+ break
+
+ if (find_unstr (punit1, TUN_UNITS(punit2, idx)) == 0) {
+ call malloc (TUN_UNPTR(punit3,jdx), LEN_UNIT, TY_CHAR)
+ call strcpy (TUN_UNITS(punit2,idx), TUN_UNITS(punit3,jdx),
+ LEN_UNIT)
+
+ TUN_POWER(punit3,jdx) = - TUN_POWER(punit2,idx)
+ jdx = jdx + 1
+ }
+ }
+
+ return (punit3)
+end
+
+# FIND_UNSTR -- Find location of units string in descriptor
+
+int procedure find_unstr (punit, units)
+
+pointer punit # i: units descriptor
+char units[ARB] # i: units string to search for
+#--
+int idx
+bool streq()
+
+begin
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit,idx) == NULL)
+ break
+
+ if (streq (TUN_UNITS(punit,idx), units))
+ return (idx)
+ }
+
+ return (0)
+end
+
+# FREE_UNSTR -- Release memory used by a units descriptor
+
+procedure free_unstr (punit)
+
+pointer punit # i: units descriptor
+#--
+int idx
+
+begin
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit,idx) == NULL)
+ break
+
+ call mfree (TUN_UNPTR(punit,idx), TY_CHAR)
+ }
+
+ call mfree (punit, TY_INT)
+end
+
+# MUL_UNSTR -- Multiply two sets of units together
+
+pointer procedure mul_unstr (punit1, punit2)
+
+pointer punit1 # i: descriptor for first set of units
+pointer punit2 # i: descriptor for second set of units
+#--
+int idx, jdx, kdx, power
+pointer punit3
+
+int find_unstr()
+
+begin
+ # Allocate structure to hold units
+
+ call calloc (punit3, LEN_TUNSTRUCT, TY_INT)
+
+ # Compute the new factor
+
+ TUN_FACTOR(punit3) = TUN_FACTOR(punit1) * TUN_FACTOR(punit2)
+
+ # Find units in both descriptors and add their powers
+
+ jdx = 1
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit1,idx) == NULL)
+ break
+
+ kdx = find_unstr (punit2, TUN_UNITS(punit1, idx))
+ if (kdx == 0)
+ next
+
+ power = TUN_POWER(punit1,idx) + TUN_POWER(punit2,kdx)
+ if (power == 0)
+ next
+
+ call malloc (TUN_UNPTR(punit3,jdx), LEN_UNIT, TY_CHAR)
+ call strcpy (TUN_UNITS(punit1,idx), TUN_UNITS(punit3,jdx),
+ LEN_UNIT)
+
+ TUN_POWER(punit3,jdx) = power
+ jdx = jdx + 1
+
+ }
+
+ # Find units only in a single descriptor and add them to the
+ # new descriptor
+
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit1,idx) == NULL)
+ break
+
+ if (find_unstr (punit2, TUN_UNITS(punit1, idx)) == 0) {
+ call malloc (TUN_UNPTR(punit3,jdx), LEN_UNIT, TY_CHAR)
+ call strcpy (TUN_UNITS(punit1,idx), TUN_UNITS(punit3,jdx),
+ LEN_UNIT)
+
+ TUN_POWER(punit3,jdx) = TUN_POWER(punit1,idx)
+ jdx = jdx + 1
+ }
+ }
+
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit2,idx) == NULL)
+ break
+
+ if (find_unstr (punit1, TUN_UNITS(punit2, idx)) == 0) {
+ call malloc (TUN_UNPTR(punit3,jdx), LEN_UNIT, TY_CHAR)
+ call strcpy (TUN_UNITS(punit2,idx), TUN_UNITS(punit3,jdx),
+ LEN_UNIT)
+
+ TUN_POWER(punit3,jdx) = TUN_POWER(punit2,idx)
+ jdx = jdx + 1
+ }
+ }
+
+ return (punit3)
+end
+
+# NUM_UNSTR -- Convert a token to an integer
+
+int procedure num_unstr (value)
+
+char value[ARB] # i: string containing token value
+#--
+int ic, nc, num
+
+int ctoi()
+
+begin
+ ic = 1
+ nc = ctoi (value, ic, num)
+ return (num)
+end
+
+# POW_UNSTR -- Raise a set of units to an integer power
+
+pointer procedure pow_unstr (punit1, power)
+
+pointer punit1 # i: units descriptor to be raised to power
+int power
+#--
+int idx
+pointer punit2
+
+begin
+ # Allocate structure to hold units
+
+ call calloc (punit2, LEN_TUNSTRUCT, TY_INT)
+
+ # Compute the new factor
+
+ TUN_FACTOR(punit2) = TUN_FACTOR(punit1) ** power
+
+ # Find units in both descriptors and add their powers
+
+ if (power != 0) {
+ for (idx = 1; idx <= MAXUNIT; idx = idx + 1) {
+ if (TUN_UNPTR(punit1,idx) == NULL)
+ break
+
+ call malloc (TUN_UNPTR(punit2,idx), LEN_UNIT, TY_CHAR)
+ call strcpy (TUN_UNITS(punit1,idx), TUN_UNITS(punit2,idx),
+ LEN_UNIT)
+
+ TUN_POWER(punit2,idx) = TUN_POWER(punit1,idx) * power
+ }
+ }
+
+ return (punit2)
+end
+
+# SET_UNSTR -- Make a new units description from a units string and its power
+
+pointer procedure set_unstr (ab, units, power)
+
+pointer ab # i: hash of units abbreviations
+char units[ARB] # i: units string
+int power # i: power of the units
+#--
+pointer punit
+
+begin
+ # Allocate structure to hold units
+
+ call calloc (punit, LEN_TUNSTRUCT, TY_INT)
+ call malloc (TUN_UNPTR(punit,1), LEN_UNIT, TY_CHAR)
+
+ # Set the first slot in the structure to hold the string
+ # and power passed to this procedure
+
+ TUN_FACTOR(punit) = 1.0
+ TUN_POWER(punit,1) = power
+ call abrev_unstr (ab, units, TUN_UNITS(punit,1), LEN_UNIT)
+
+ return (punit)
+end
+
+# STR_UNSTR -- Convert units structure into a string
+
+procedure str_unstr (punit, str, maxch)
+
+pointer punit # i: units descriptor
+char str[ARB] # o: string representation of units
+int maxch # i: max length of string
+#--
+int ic, idx
+
+int strlen(), gstrcpy(), itoc()
+
+begin
+ call sprintf (str, maxch, "%g")
+ call pargd (TUN_FACTOR(punit))
+
+ ic = strlen (str) + 1
+
+ do idx = 1, MAXUNIT {
+ if (TUN_UNPTR(punit,idx) == NULL)
+ break
+
+ ic = ic + gstrcpy ("*", str[ic], maxch-ic+1)
+ ic = ic + gstrcpy (TUN_UNITS(punit,idx), str[ic], maxch+ic-1)
+
+ if (TUN_POWER(punit,idx) != 1) {
+ ic = ic + gstrcpy ("^", str[ic], maxch-ic+1)
+ ic = ic + itoc (TUN_POWER(punit,idx), str[ic], maxch-ic+1)
+ }
+ }
+end