aboutsummaryrefslogtreecommitdiff
path: root/sys/etc
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
commitfa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 (patch)
treebdda434976bc09c864f2e4fa6f16ba1952b1e555 /sys/etc
downloadiraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz
Initial commit
Diffstat (limited to 'sys/etc')
-rw-r--r--sys/etc/README4
-rw-r--r--sys/etc/brktime.x79
-rw-r--r--sys/etc/btoi.x14
-rw-r--r--sys/etc/clktime.x16
-rw-r--r--sys/etc/cnvdate.x52
-rw-r--r--sys/etc/cnvtime.x31
-rw-r--r--sys/etc/cputime.x14
-rw-r--r--sys/etc/doc/Proc.hlp22
-rw-r--r--sys/etc/doc/error.hlp51
-rw-r--r--sys/etc/doc/etc.hd29
-rw-r--r--sys/etc/doc/etc.men24
-rw-r--r--sys/etc/doc/psio.doc275
-rw-r--r--sys/etc/dtmcnv.x482
-rw-r--r--sys/etc/envgetb.x32
-rw-r--r--sys/etc/envgetd.x27
-rw-r--r--sys/etc/envgeti.x26
-rw-r--r--sys/etc/envgetr.x18
-rw-r--r--sys/etc/envgets.x62
-rw-r--r--sys/etc/envindir.x31
-rw-r--r--sys/etc/envinit.x27
-rw-r--r--sys/etc/environ.com8
-rw-r--r--sys/etc/environ.h28
-rw-r--r--sys/etc/environ.x315
-rw-r--r--sys/etc/envlist.x25
-rw-r--r--sys/etc/envnext.x53
-rw-r--r--sys/etc/envreset.x66
-rw-r--r--sys/etc/envscan.x149
-rw-r--r--sys/etc/erract.x93
-rw-r--r--sys/etc/errcode.x18
-rw-r--r--sys/etc/errget.x21
-rw-r--r--sys/etc/error.com7
-rw-r--r--sys/etc/error.x60
-rw-r--r--sys/etc/gen/miireadd.x50
-rw-r--r--sys/etc/gen/miireadi.x50
-rw-r--r--sys/etc/gen/miireadl.x50
-rw-r--r--sys/etc/gen/miireadr.x50
-rw-r--r--sys/etc/gen/miireads.x50
-rw-r--r--sys/etc/gen/miiwrited.x28
-rw-r--r--sys/etc/gen/miiwritei.x28
-rw-r--r--sys/etc/gen/miiwritel.x28
-rw-r--r--sys/etc/gen/miiwriter.x28
-rw-r--r--sys/etc/gen/miiwrites.x28
-rw-r--r--sys/etc/gen/mkpkg30
-rw-r--r--sys/etc/gen/nmireadb.x50
-rw-r--r--sys/etc/gen/nmireadd.x50
-rw-r--r--sys/etc/gen/nmireadi.x50
-rw-r--r--sys/etc/gen/nmireadl.x50
-rw-r--r--sys/etc/gen/nmireadr.x50
-rw-r--r--sys/etc/gen/nmireads.x50
-rw-r--r--sys/etc/gen/nmiwriteb.x28
-rw-r--r--sys/etc/gen/nmiwrited.x28
-rw-r--r--sys/etc/gen/nmiwritei.x28
-rw-r--r--sys/etc/gen/nmiwritel.x28
-rw-r--r--sys/etc/gen/nmiwriter.x28
-rw-r--r--sys/etc/gen/nmiwrites.x28
-rw-r--r--sys/etc/gethost.x13
-rw-r--r--sys/etc/getpid.x12
-rw-r--r--sys/etc/getuid.x24
-rw-r--r--sys/etc/gmtcnv.x35
-rw-r--r--sys/etc/gqsort.x84
-rw-r--r--sys/etc/intr.x54
-rw-r--r--sys/etc/itob.x14
-rw-r--r--sys/etc/lineoff.x113
-rw-r--r--sys/etc/locpr.x14
-rw-r--r--sys/etc/locva.x13
-rw-r--r--sys/etc/lpopen.x118
-rw-r--r--sys/etc/maideh.x76
-rw-r--r--sys/etc/main.x908
-rw-r--r--sys/etc/miiread.gx50
-rw-r--r--sys/etc/miireadc.x50
-rw-r--r--sys/etc/miiwrite.gx28
-rw-r--r--sys/etc/miiwritec.x28
-rw-r--r--sys/etc/mkpkg125
-rw-r--r--sys/etc/nmiread.gx50
-rw-r--r--sys/etc/nmireadb.x32
-rw-r--r--sys/etc/nmireadc.x50
-rw-r--r--sys/etc/nmiwrite.gx28
-rw-r--r--sys/etc/nmiwriteb.x21
-rw-r--r--sys/etc/nmiwritec.x28
-rw-r--r--sys/etc/onentry.x65
-rw-r--r--sys/etc/onerror.x96
-rw-r--r--sys/etc/onexit.x88
-rw-r--r--sys/etc/oscmd.x116
-rw-r--r--sys/etc/pagefiles.x1140
-rw-r--r--sys/etc/prc.com27
-rw-r--r--sys/etc/prchdir.x21
-rw-r--r--sys/etc/prclcpr.x33
-rw-r--r--sys/etc/prcldpr.x47
-rw-r--r--sys/etc/prclose.x32
-rw-r--r--sys/etc/prd.com8
-rw-r--r--sys/etc/prdone.x26
-rw-r--r--sys/etc/prenvfree.x36
-rw-r--r--sys/etc/prenvset.x24
-rw-r--r--sys/etc/prfilbuf.x38
-rw-r--r--sys/etc/prfindpr.x20
-rw-r--r--sys/etc/prgline.x204
-rw-r--r--sys/etc/prgredir.x19
-rw-r--r--sys/etc/prkill.x42
-rw-r--r--sys/etc/propcpr.x201
-rw-r--r--sys/etc/propdpr.x68
-rw-r--r--sys/etc/propen.x67
-rw-r--r--sys/etc/proscmd.x32
-rw-r--r--sys/etc/prpsio.x484
-rw-r--r--sys/etc/prpsload.x30
-rw-r--r--sys/etc/prredir.x32
-rw-r--r--sys/etc/prseti.x51
-rw-r--r--sys/etc/prsignal.x27
-rw-r--r--sys/etc/prstati.x49
-rw-r--r--sys/etc/prupdate.x61
-rw-r--r--sys/etc/psioisxt.x58
-rw-r--r--sys/etc/psioxfer.x33
-rw-r--r--sys/etc/qsort.x81
-rw-r--r--sys/etc/sttyco.x519
-rw-r--r--sys/etc/syserr.x49
-rw-r--r--sys/etc/sysid.x57
-rw-r--r--sys/etc/syspanic.x17
-rw-r--r--sys/etc/sysptime.x84
-rw-r--r--sys/etc/tsleep.x13
-rw-r--r--sys/etc/ttopen.x96
-rw-r--r--sys/etc/urlget.x384
-rw-r--r--sys/etc/votable.x304
-rw-r--r--sys/etc/xalloc.x197
-rw-r--r--sys/etc/xerfmt.x96
-rw-r--r--sys/etc/xerpop.x55
-rw-r--r--sys/etc/xerpue.x32
-rw-r--r--sys/etc/xerreset.x19
-rw-r--r--sys/etc/xerstmt.x66
-rw-r--r--sys/etc/xerverify.x21
-rw-r--r--sys/etc/xgdevlist.x49
-rw-r--r--sys/etc/xisatty.x38
-rw-r--r--sys/etc/xmjbuf.x20
-rw-r--r--sys/etc/xttysize.x51
-rw-r--r--sys/etc/xwhen.x13
-rw-r--r--sys/etc/zzdebug.x404
134 files changed, 10955 insertions, 0 deletions
diff --git a/sys/etc/README b/sys/etc/README
new file mode 100644
index 00000000..6a1ebe0a
--- /dev/null
+++ b/sys/etc/README
@@ -0,0 +1,4 @@
+This directory contains miscellaneous parts of the IRAF system code.
+These include the error handling facilities, the IRAF Main and associated
+routines (the default exception handler, and ONEXIT), environment access
+facilities, and time conversion facilities.
diff --git a/sys/etc/brktime.x b/sys/etc/brktime.x
new file mode 100644
index 00000000..dbd94a7c
--- /dev/null
+++ b/sys/etc/brktime.x
@@ -0,0 +1,79 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <time.h>
+
+define SECONDS_PER_DAY 86400
+define SECONDS_PER_HOUR 3600
+define SECONDS_PER_MINUTE 60
+define MONDAY 2
+
+# BRKTIME -- Break a long integer time (such as returned by GETTIME or FINFO)
+# into the fields of the structure defined in <time.h>. The procedure is
+# valid from 00:00:00 01-Jan-1980 to 23:23:59 28-Feb-2100.
+
+procedure brktime (ltime, tm)
+
+long ltime # seconds since 00:00:00 01-Jan-1980
+int tm[LEN_TMSTRUCT] # broken down time (output struct)
+
+long temp # working variable
+long seconds # seconds in current day
+long days # whole days since Monday 00-Jan-1980
+int nights # whole days since 00-Jan of current year
+
+int year # year counter
+int days_per_year # days per year
+
+int month # month counter
+int days_per_month[12] # days per month
+data days_per_month/31,0,31,30,31,30,31,31,30,31,30,31/
+
+begin
+ seconds = mod (ltime, SECONDS_PER_DAY)
+ days = ltime / SECONDS_PER_DAY + 1
+
+ # Break hours, minutes, seconds.
+
+ TM_HOUR(tm) = seconds / SECONDS_PER_HOUR
+ temp = seconds - TM_HOUR(tm) * SECONDS_PER_HOUR
+ TM_MIN(tm) = temp / SECONDS_PER_MINUTE
+ TM_SEC(tm) = temp - TM_MIN(tm) * SECONDS_PER_MINUTE
+
+ # Break day of week.
+
+ TM_WDAY(tm) = mod (days + MONDAY, 7)
+ if (TM_WDAY(tm) == 0)
+ TM_WDAY(tm) = 7
+
+ # Break year, day of year.
+
+ temp = 0 # whole days since 00-Jan-1980 on last day of last year
+ year = 1980
+ days_per_year = 366
+ while (days > temp + days_per_year) {
+ temp = temp + days_per_year
+ year = year + 1
+ if (mod (year, 4) == 0)
+ days_per_year = 366
+ else
+ days_per_year = 365
+ }
+ TM_YEAR(tm) = year
+ TM_YDAY(tm) = days - temp
+
+ # Break month, day of month.
+
+ nights = TM_YDAY(tm)
+ if (mod (TM_YEAR(tm), 4) == 0)
+ days_per_month[2] = 29
+ else
+ days_per_month[2] = 28
+ temp = 0 # whole days since 00-Jan on last day of last month
+ month = 1
+ while (nights > temp + days_per_month[month]) {
+ temp = temp + days_per_month[month]
+ month = month + 1
+ }
+ TM_MONTH(tm) = month
+ TM_MDAY(tm) = nights - temp
+end
diff --git a/sys/etc/btoi.x b/sys/etc/btoi.x
new file mode 100644
index 00000000..468ba352
--- /dev/null
+++ b/sys/etc/btoi.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# BTOI -- Convert boolean to integer.
+
+int procedure btoi (boolean_value)
+
+bool boolean_value
+
+begin
+ if (boolean_value)
+ return (YES)
+ else
+ return (NO)
+end
diff --git a/sys/etc/clktime.x b/sys/etc/clktime.x
new file mode 100644
index 00000000..84155919
--- /dev/null
+++ b/sys/etc/clktime.x
@@ -0,0 +1,16 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# CLKTIME -- Get the current clock time (local standard time) in units
+# of seconds since 00:00:00 01-Jan-80. This can be broken down into days,
+# hours, seconds, etc. with BRKTIME, or printed as a date/time string with
+# CNVTIME.
+
+long procedure clktime (old_time)
+
+long old_time, new_time
+long cpu_time
+
+begin
+ call zgtime (new_time, cpu_time)
+ return (new_time - old_time)
+end
diff --git a/sys/etc/cnvdate.x b/sys/etc/cnvdate.x
new file mode 100644
index 00000000..7e2cd4e9
--- /dev/null
+++ b/sys/etc/cnvdate.x
@@ -0,0 +1,52 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <time.h>
+
+define SZ_WEEKDAY 3
+define SZ_MONTH 3
+
+# CNVDATE -- Convert a time in integer seconds since midnight on Jan 1, 1980
+# into a short string such as "May 15 18:24". The length of the output
+# string is given by the parameter SZ_DATE in <time.h>. Note that CNVTIME
+# is also available if a longer, more informative string is desired.
+
+procedure cnvdate (ltime, outstr, maxch)
+
+long ltime # seconds since 00:00:00 01-Jan-1980
+char outstr[ARB]
+int maxch
+
+long one_year_ago
+int fd, tm[LEN_TMSTRUCT]
+
+long clktime()
+int stropen()
+string month "JanFebMarAprMayJunJulAugSepOctNovDec"
+data one_year_ago /0/
+errchk stropen
+
+begin
+ if (one_year_ago == 0)
+ one_year_ago = clktime (0) - 3600 * 24 * (365 - 31)
+
+ call brktime (ltime, tm)
+ fd = stropen (outstr, maxch, NEW_FILE)
+
+ call fprintf (fd, "%3.3s %2d ")
+ call pargstr (month [(TM_MONTH(tm) - 1) * SZ_MONTH + 1])
+ call pargi (TM_MDAY(tm))
+
+ # If time is recent (within the past year), print the time of day,
+ # otherwise print the year.
+
+ if (ltime > one_year_ago) {
+ call fprintf (fd, "%2d:%02d")
+ call pargi (TM_HOUR(tm))
+ call pargi (TM_MIN(tm))
+ } else {
+ call fprintf (fd, "%5d")
+ call pargi (TM_YEAR(tm))
+ }
+
+ call strclose (fd)
+end
diff --git a/sys/etc/cnvtime.x b/sys/etc/cnvtime.x
new file mode 100644
index 00000000..372daf03
--- /dev/null
+++ b/sys/etc/cnvtime.x
@@ -0,0 +1,31 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <time.h>
+
+define SZ_WEEKDAY 3
+define SZ_MONTH 3
+
+# CNVTIME -- Convert a time in integer seconds since midnight on Jan 1, 1980
+# into a string, i.e., "Mon 16:30:05 17-Mar-2001". The maximum length of the
+# output string is given by the parameter SZ_TIME in <time.h>.
+
+procedure cnvtime (ltime, outstr, maxch)
+
+long ltime # seconds since 00:00:00 01-Jan-1980
+char outstr[ARB]
+int maxch
+int tm[LEN_TMSTRUCT] # broken down time structure
+string weekday "SunMonTueWedThuFriSat"
+string month "JanFebMarAprMayJunJulAugSepOctNovDec"
+
+begin
+ call brktime (ltime, tm)
+ call sprintf (outstr, maxch, "%3.3s %02d:%02d:%02d %02d-%3.3s-%04d")
+ call pargstr (weekday [(TM_WDAY(tm) - 1) * SZ_WEEKDAY + 1])
+ call pargi (TM_HOUR(tm))
+ call pargi (TM_MIN(tm))
+ call pargi (TM_SEC(tm))
+ call pargi (TM_MDAY(tm))
+ call pargstr (month [(TM_MONTH(tm) - 1) * SZ_MONTH + 1])
+ call pargi (TM_YEAR(tm))
+end
diff --git a/sys/etc/cputime.x b/sys/etc/cputime.x
new file mode 100644
index 00000000..b56b7280
--- /dev/null
+++ b/sys/etc/cputime.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# CPUTIME -- Return the difference between the current cpu time consumed
+# and the argument, in long integer milliseconds.
+
+long procedure cputime (old_cputime)
+
+long old_cputime, new_cputime
+long clk_time
+
+begin
+ call zgtime (clk_time, new_cputime)
+ return (new_cputime - old_cputime)
+end
diff --git a/sys/etc/doc/Proc.hlp b/sys/etc/doc/Proc.hlp
new file mode 100644
index 00000000..023a5316
--- /dev/null
+++ b/sys/etc/doc/Proc.hlp
@@ -0,0 +1,22 @@
+ Process Control
+
+Connected Subprocesses
+
+ pid = propen (process, in, out)
+ stat = prclose (pid)
+ stat = prgetline (in, lbuf)
+ prredir (pid, stream, new_fd)
+ prsignal (pid, signal)
+ prupdate (message)
+
+ pid = propcpr (process, in, out)
+ stat = prclcpr (pid)
+
+
+
+Detached Processes
+
+ job = propdpr (process, bkgfile)
+ stat = prcldpr (job)
+ y/n = prdone (job)
+ prkill (job)
diff --git a/sys/etc/doc/error.hlp b/sys/etc/doc/error.hlp
new file mode 100644
index 00000000..7776093f
--- /dev/null
+++ b/sys/etc/doc/error.hlp
@@ -0,0 +1,51 @@
+
+.help error, fatal, errchk, erract, iferr 2 "Error Handling Strategy"
+.sh
+ERROR HANDLING
+
+ A recoverable error condition is asserted with ERROR. An irrecoverable
+error condition is asserted with FATAL. Error recovery is implemented
+using the IFERR and IFNOERR statements in the preprocessor language.
+ERRACT may be called in an IFERR statement to cause a warning to be issued,
+or to cause a particular error action to be taken. ERRCODE returns either
+OK or the integer code of the posted error.
+
+Language support includes the IFERR and IFNOERR statements and the ERRCHK
+declaration. The IFERR statement is grammatically equivalent to the IF
+statement. Note that the condition to be tested in an IFERR statement may
+be a procedure call or assignment statement, while the IF statement tests
+a boolean expression.
+
+
+.nf
+ errchk proc1, proc2, ... # errchk declaration
+
+ iferr (procedure call or assignment statement)
+ <error_action_statement>
+
+ iferr {
+ <any statements, including IFERR>
+ } then
+ <error_action_statement>
+
+
+Library procedures (ERROR and FATAL cause a RETURN):
+
+ error (errcode, error_message)
+ fatal (errcode, error_message)
+ erract (severity)
+ val = errcode ()
+
+
+ERRACT severity codes (<error.h>):
+
+ EA_WARN # issue a warning message
+ EA_ERROR # assert recoverable error
+ EA_FATAL # assert fatal error
+.fi
+
+
+An arithmetic exception (X_ARITH) will be trapped by an IFERR statement,
+provided the posted handler(s) return without causing error restart.
+X_INT and X_ACV may only be caught by posting an exception handler with
+XWHEN.
diff --git a/sys/etc/doc/etc.hd b/sys/etc/doc/etc.hd
new file mode 100644
index 00000000..759ba4e2
--- /dev/null
+++ b/sys/etc/doc/etc.hd
@@ -0,0 +1,29 @@
+# Help directory for the ETC (miscellaneous system stuff) package.
+
+$etc = "sys$etc/"
+
+brktime hlp = brktime.hlp, src = etc$brktime.x
+btoi hlp = btoi.hlp, src = etc$btoi.x
+clktime hlp = clktime.hlp, src = etc$clktime.x
+cnvdate hlp = cnvdate.hlp, src = etc$cnvdate.x
+cnvtime hlp = cnvtime.hlp, src = etc$cnvtime.x
+cputime hlp = cputime.hlp, src = etc$cputime.x
+envgetb hlp = envget.hlp, src = etc$envgetb.x
+envgeti hlp = envget.hlp, src = etc$envgeti.x
+envgets hlp = envget.hlp, src = etc$envgets.x
+erract hlp = erract.hlp, src = etc$erract.x
+errcode hlp = errcode.hlp, src = etc$errcode.x
+error hlp = error.hlp, src = etc$error.x
+getuid hlp = getuid.hlp, src = etc$getuid.x
+itob hlp = itob.hlp, src = etc$itob.x
+lpopen hlp = lpopen.hlp, src = etc$lpopen.x
+main hlp = main.hlp, src = etc$main.x
+onerror hlp = onerror.hlp, src = etc$onerror.x
+onexit hlp = onexit.hlp, src = etc$onexit.x
+oscmd hlp = oscmd.hlp, src = etc$oscmd.x
+qsort hlp = qsort.hlp, src = etc$qsort.x
+sys_ptime hlp = sys_ptime.hlp, src = etc$sys_ptime.x
+syserr hlp = syserr.hlp, src = etc$syserr.x
+tsleep hlp = tsleep.hlp, src = etc$tsleep.x
+urand hlp = urand.hlp, src = etc$urand.x
+xwhen hlp = xwhen.hlp, src = etc$xwhen.x
diff --git a/sys/etc/doc/etc.men b/sys/etc/doc/etc.men
new file mode 100644
index 00000000..5ed6f4ad
--- /dev/null
+++ b/sys/etc/doc/etc.men
@@ -0,0 +1,24 @@
+ brktime - Convert a long integer time into year, month, day, etc.
+ btoi - Boolean to integer
+ clktime - Get the clock time
+ cnvdate - Convert long integer time to date string (short format)
+ cnvtime - Convert long integer time to time string (long format)
+ cputime - Get the CPU time consumed by the process
+ envget[bis] - Get a boolean, integer, or string valued environment variable
+ erract - Take an error action for a previously posted error
+ errcode - Get the error code of the posted error
+ error - Post an error and take an error action
+ getuid - Get the name of the runtime user of a program
+ itob - Convert integer to boolean
+ lpopen - Open the line printer as a file
+ main - The IRAF Main
+ onerror - Post a procedure to be executed if error recovery occurs
+ onexit - Post a procedure to be executed upon process shutdown
+ oscmd - Send a command to the host operating system
+ qsort - General quick sort for any data structure
+ sys_mtime - Mark the time (for timing programs)
+ sys_ptime - Print the elapsed time since last mark
+ syserr - Post a system error and take an error action
+ tsleep - Delay process execution
+ urand - Uniform random number generator
+ xwhen - Post an exception handler
diff --git a/sys/etc/doc/psio.doc b/sys/etc/doc/psio.doc
new file mode 100644
index 00000000..d0f34c9a
--- /dev/null
+++ b/sys/etc/doc/psio.doc
@@ -0,0 +1,275 @@
+.help pr_psio
+.nf __________________________________________________________________________
+PR_PSIO -- Pseudofile i/o for a process. Process an i/o request for the
+specified pseudofile stream of the specified process. Called either to read
+command input from the CLIN of a process, or to process a read or write
+request to a pseudofile of a process.
+
+
+1. Introduction
+
+ Pseudofile i/o in a multiprocess configuration, e.g., for the graphics
+streams, is quite complex and difficult to explain briefly. I have tried to
+cover the major points here but warn the reader that it is not going to be easy
+to understand the flow of data and control involved. The problem is a difficult
+one due to the nature of the IPC protocol and the complexity of the three
+process architecture required when an external graphics kernel is used. The
+discussion herein is not complete but should as least give the reader some
+idea of what is going on.
+
+
+2. Pseudofile I/O
+
+ While a task is running the CL will be reading command input from the task.
+This read eventually resolves into a call to PR_PSIO on the CLIN for the
+process. When pseudofile i/o occurs, e.g., the process writes to STDOUT or
+STDERR, an XMIT or XFER directive will be seen in the CLIN input from the
+process. If we are directed to XMIT to an ordinary file our task is relatively
+easy, i.e., we read the data block from CLIN and write it to the output file.
+A directive to read from the standard input is also easy, i.e., we read from
+the standard input of the parent (assuming i/o is not redirected) and write
+the data block to the CLOUT of the process preceded by a count of the number
+of chars.
+
+
+2.1 I/O to a Graphics Stream
+
+ If we are directed to read or write a graphics stream our task is somewhat
+more difficult. The standard graphics streams STDGRAPH, STDIMAGE, and STDPLOT
+differ from other pseudofiles in that the streams are both readable and
+writable, provided all data is used up before switching modes. A graphics
+stream may be connected to a file if output is being spooled, to the builtin
+STDGRAPH kernel if the graphics device is the graphics terminal, or to a
+graphics kernel resident in an external subprocess.
+
+If a graphics stream is redirected to a spool file we merely copy output to
+the file and reading is forbidden. If output is to an external graphics
+kernel but is unfiltered (no workstation transformation, e.g., for STDPLOT),
+we merely copy data blocks on to the subprocess but the protocol involved
+is nontrivial. If output is to the builtin STDGRAPH kernel or to an
+external interactive kernel, output must be filtered through GIOTR before
+being written to the local or remote graphics kernel. Graphics input is
+also possible and is handled similarly but without need to call GIOTR.
+
+Before reading or writing a graphics stream GIO will send a special directive to
+PR_PSIO to connect a kernel to the stream. This directive is passed to PR_PSIO
+via an XMIT to the special pseudofile PSIOCONTROL. The data passed in the
+XMIT call will be the GKI control instruction to be executed by PSIO. There
+are currently three such directives, i.e., OPENWS, SETWCS, and GETWCS. Each
+such directive is included in the normal metacode stream as well, but by
+writing to a special pseudofile we avoid the need to have PR_PSIO scan each
+metacode stream for control instructions, a fairly expensive operation if a
+lot of data is involved.
+
+
+2.1.1 Graphics Stream Dataflow
+
+ A frame buffer is associated with each graphics stream in the parent
+process. If graphics output (metacode) is being filtered, each output record
+is appended to the frame buffer and then GIOTR is called to filter the new
+instructions. GIOTR writes the filtered metacode stream either directly to
+the builtin kernel or to the graphics output pseudofile stream of the parent
+process.
+
+Output to the builtin kernel is easy to understand: GIOTR merely calls the
+kernel to execute the transformed instruction. If output is to an external
+kernel we unfortunately cannot simply write to the kernel because we require
+that the graphics kernel task be a conventional task callable from either
+the CL or by the graphics system, i.e., by PL_PSIO. We must buffer the
+transformed output metacode and pass it on to the kernel process only when
+requested to do so by an XFER command from the kernel.
+
+This buffering is done in a somewhat tricky way which makes it look like we
+are writing to a simple file, and which allows us to use conventional READ and
+WRITE calls to access the graphics stream. GIOTR, if not writing to the
+builtin kernel, will write to one of the three graphics streams of the parent
+process, i.e., to STDGRAPH, STDIMAGE, or STDPLOT. The graphics stream of the
+parent is logically connected to the same stream in the kernel process. We
+arrange things such that data may be written or read into the FIO buffer
+associated with the stream, but the buffer will never actually be flushed,
+since this would cause the contents to appear as garbage on the user terminal.
+
+The sequence of events for an XMIT to STDGRAPH with an external kernel is as
+follows:
+
+
+ The parent process (CL) blocks, waiting for a read on the IPC
+ channel to the graphics task.
+
+ Graphics task writes to stdgraph.
+ FIO flushes stdgraph buffer through IPC channel.
+
+ PR_PSIO (in the parent) sees XMIT to stdgraph.
+ Parent reads data record from IPC channel, appending the
+ data record to the frame buffer for the stream.
+
+ PR_PSIO calls GIOTR to process the new metacode.
+ GIOTR writes the transformed metacode instructions to the stdgraph
+ stream of the parent and returns control to PR_PSIO.
+
+ PR_PSIO rewinds the stdgraph buffer in preparation for a read and
+ stacks the pending XMIT request and directs its command input
+ to the IPC of the kernel process.
+
+ The kernel process sends zero or more XMIT or XFER requests to
+ the parent to read or write pseudofile streams other than
+ stdgraph.
+ The kernel process sends an XFER request to the parent to read
+ from stdgraph.
+ The parent reads the data record from the stdgraph FIO buffer
+ and passes it on to the kernel, completing the XFER request
+ of the kernel as well as the original XMIT request of the
+ graphics task.
+
+ The parent process (CL) blocks, waiting for a read on the IPC
+ channel to the graphics task.
+
+
+The sequence of operations for an XFER request from the graphics task is
+as follows.
+
+ The parent process (CL) blocks, waiting for a read on the IPC
+ channel to the graphics task.
+
+ The parent receives an XFER request from the graphics task.
+ If there is any data in the stdgraph buffer the parent returns
+ that to the graphics task, otherwise the PR_PSIO procedure
+ pushes an XFER request and redirects its input to the
+ graphics kernel.
+
+ The kernel process sends zero or more XMIT or XFER requests to
+ the parent to read or write pseudofile streams other than
+ stdgraph.
+ The kernel process sends an XMIT request to the parent to write
+ to stdgraph.
+ The parent reads the data block from the IPC channel to the kernel
+ and writes it to stdgraph, completing the XMIT request.
+
+ The parent pops the XFER request and copies the data in the stdgraph
+ buffer to the graphics task, completing the original XFER request.
+
+ The parent process (CL) blocks, waiting for a read on the IPC
+ channel to the graphics task.
+
+
+In summary, the principal data buffers involved in pseudofile i/o to a graphics
+stream are the frame buffer, used by GIOTR to spool the metacode instructions
+for a graphics frame, and the FIO buffer for the graphics stream, used to
+pass data between XMIT/XFER request pairs from cooperating processes at
+opposite ends of a graphics stream.
+
+
+3. Summary
+
+ The actual code required to implement all this is probably easier to
+understand than the English description. To summarize the justification for
+the complexity of the scheme we have adopted:
+
+ [1] The graphics kernel task is a conventional CL callable task with
+ parameters etc., usable to process metacode from a metafile or from
+ a pipe as well as callable by PR_PSIO. The conventional IPC protocol
+ is used in the graphics kernel task. Other tasks may be resident in
+ the same process, saving disk and memory.
+
+ [2] The graphics kernel may read STDIN and write STDOUT and STDERR while
+ processing metacode, allowing access to the graphics terminal via the
+ CL process, output of debugging information during operation, and
+ output of error messages during operation.
+.endhelp ______________________________________________________________________
+
+
+# PR_PSIO -- Process an i/o request for the specified pseudofile stream
+# of the specified process.
+
+procedure pr_psio (pid, active_fd)
+
+pid process id
+fd process stream for which i/o is requested
+
+begin
+ in = pr.clin
+ fd = active_fd
+ clear stack
+
+ # Process i/o requests from the subprocess until a request is received
+ # and processed for pseudofile FD.
+
+ repeat {
+ while (filbuf (in) != EOF) {
+ determine type of request and destfd
+
+ if (xmit request to stream destfd) {
+ if (graphics filtering enabled) {
+ read data record and append to frame buffer
+ call giotr to filter output to destfd
+ } else {
+ read data record from process
+ write record to destfd
+ }
+ if (destination is a process) {
+ rewind destfd buffer for read
+ push (fd)
+ push (in)
+ push (xmit)
+ fd = destfd
+ in = newpr.clin
+ next
+ }
+
+ } else if (xfer request from stream destfd) {
+ if (destfd is a process and buffer is empty) {
+ push (fd)
+ push (in)
+ push (xfer)
+ fd = destfd
+ in = newpr.clin
+ next
+ } else {
+ read data record from destfd
+ write data record to process
+ }
+
+ } else if (gio directive) {
+ if (open workstation)
+ connect a kernel process to a graphics stream
+ else if (setwcs)
+ save wcs for the stream
+ else if (getwcs)
+ write wcs data to the process
+
+ } else {
+ destfd = CLIN
+ if (fd != CLIN)
+ error: unsolicited command input from the subprocess
+ }
+
+ if (destfd == fd) {
+ if (stack not empty) {
+ pop (request)
+ pop (in)
+ pop (fd)
+ if (request == xfer) {
+ read data record from fd
+ write data record to process owning "in"
+ }
+ } else
+ break
+ }
+ }
+ } until (stack is empty)
+end
+
+
+
+File routing:
+
+ Each pseudofile in a subprocess is associated with a stream in the parent
+process. A subprocess pseudofile may map to a real file or to a parent
+pseudofile. When a subprocess is connected as a graphics kernel graphics
+i/o will be via any one of the standard graphics streams STDGRAPH etc.,
+with said graphics stream connected to the same stream in the parent.
+The subprocess streams STDIN, STDOUT, and STDERR are by default connected
+to the same streams in the parent, allowing the subprocess to access the
+terminal, output error messages, and so on. A graphics kernel will be able
+to access the standard i/o streams even while connected as a subprocess
+to filter GKI metacode.
diff --git a/sys/etc/dtmcnv.x b/sys/etc/dtmcnv.x
new file mode 100644
index 00000000..63d89a12
--- /dev/null
+++ b/sys/etc/dtmcnv.x
@@ -0,0 +1,482 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <time.h>
+
+
+# DTMCNV.X -- Date and time conversions.
+#
+# The file contains the source for only the DTM routines listed below. All
+# the related system date and time routines are also summarized so that the
+# whole (rather scattered) interface can be viewed at a glance.
+#
+# FITS-Like Date and Time String Conversions
+#
+# status = dtm_decode (datestr, y,m,d, h, oldfits)
+# nchars = dtm_encode (datestr,maxch, y,m,d, h, precision, flags)
+# status = dtm_decode_hms (datestr, y,m,d, h,m,s, oldfits)
+# nchars = dtm_encode_hms (datestr,maxch, y,m,d, h,m,s, precision, flags)
+# status = dtm_ltime (datestr, ltime)
+#
+#
+# General Date and Time Conversions
+#
+# cnvdate (ltime, outstr, maxch)
+# cnvtime (ltime, outstr, maxch)
+# brktime (ltime, tm)
+#
+# System Time
+#
+# lval = clktime (old_time) # returns local time
+# lval = cputime (old_cputime) # process cpu time, seconds
+# gmt = lsttogmt (lst) # lst/gmt are in seconds
+# lst = gmttolst (gmt) # lst/gmt are in seconds
+#
+# sys_mtime (save_time) # mark/print cpu time used
+# sys_ptime (fd, opstr, save_time)
+#
+#
+# Kernel Support
+#
+# zgtime (clktime, cputime) # clock/cpu time in seconds
+# zgmtco (gmtcor) # GMT = LST + gmtco (seconds)
+#
+# LST here means local standard time (clock time), including any correction
+# for daylight savings time.
+
+
+
+# DTM_DECODE -- Decode the FITS format DATE-OBS string value into year,
+# month, day and time fields. OK is returned if the date string is
+# successfully decoded, ERR if it is not. The DATE-OBS string value may be
+# in any of the following forms: DD/MM/YY (flags = TF_OLDFITS), CCYY-MM-DD
+# (flags = 0, time = INDEFD), or CCYY-MM-DDTHH:MM:SS[.SSS...] (flags=0,
+# time = double precision number). This routine verifies only the syntax.
+# Routines in the SLALIB or ASTUTIL libraries can be used to check for
+# valid year, month, day, or time values.
+
+int procedure dtm_decode (datestr, year, month, day, time, flags)
+
+char datestr[ARB] #I the input date-obs string
+int year #O the output year (INDEFI if undefined)
+int month #O the output month (INDEFI if undefined)
+int day #O the output day (INDEFI if undefined)
+double time #O the output time in hours (INDEFD if undefined)
+int flags #O see <time.h>
+
+double dval
+int oldfits, ip, nchars, ival
+int ctoi(), ctod()
+
+begin
+ # Initialize.
+ year = INDEFI
+ month = INDEFI
+ day = INDEFI
+ time = INDEFD
+ flags = 0
+
+ # Determine whether the format is old or new and get the day or
+ # month accordingly.
+ ip = 1
+ nchars = ctoi (datestr, ip, ival)
+ if (nchars == 2) {
+ flags = or (flags, TF_OLDFITS)
+ oldfits = YES
+ day = ival
+ } else if (nchars == 4) {
+ flags = and (flags, not(TF_OLDFITS))
+ oldfits = NO
+ year = ival
+ } else
+ return (ERR)
+
+ # Check syntax.
+ if (oldfits == NO && datestr[ip] == '-')
+ ip = ip + 1
+ else if (oldfits == YES && datestr[ip] == '/')
+ ip = ip + 1
+ else
+ return (ERR)
+
+ # Get the month
+ nchars = ctoi (datestr, ip, ival)
+ if (nchars == 2) {
+ month = ival
+ } else
+ return (ERR)
+ if (oldfits == NO && datestr[ip] == '-')
+ ip = ip + 1
+ else if (oldfits == YES && datestr[ip] == '/')
+ ip = ip + 1
+ else
+ return (ERR)
+
+ # Get the year or day.
+ nchars = ctoi (datestr, ip, ival)
+ if (nchars == 2) {
+ if (oldfits == YES)
+ year = 1900 + ival
+ else
+ day = ival
+ } else
+ return (ERR)
+
+ if (datestr[ip] != 'T' || oldfits == YES)
+ return (OK)
+
+ # Get the time.
+ ip = ip + 1
+ nchars = ctod (datestr, ip, dval)
+ if (nchars < 8)
+ return (ERR)
+ else
+ time = dval
+
+ # Check for trailing garbage in the input string. Ignore whitespace.
+ while (IS_WHITE(datestr[ip]))
+ ip = ip + 1
+
+ if (datestr[ip] != EOS)
+ return (ERR)
+ else
+ return (OK)
+end
+
+
+# DTM_DECODE_HMS -- Decode a FITS format DATE-OBS string into year, month,
+# day, hours, minutes, and seconds fields. OK is returned if the date string
+# is successfully decoded, ERR if it is not. The DATE-OBS string value may
+# be in any of the following forms: DD/MM/YY (oldfits = YES), CCYY-MM-DD
+# (oldfits = NO, hours = INDEFI, minutes = INDEFI, seconds = INDEFD), or
+# CCYY-MM-DDTHH:MM:SS[.SSS...] (oldfits = NO, hours = integer, minutes =
+# integer, seconds = double precision number). This routine verifies only
+# that the syntax is correct. Routines in the SLALIB or ASTUTIL libraries
+# can be used to check for valid year, month, day, or time values.
+
+int procedure dtm_decode_hms (datestr,
+ year, month, day, hours, minutes, seconds, flags)
+
+char datestr[ARB] #I the input date-obs string
+int year #O the output year (INDEFI if undefined)
+int month #O the output month (INDEFI if undefined)
+int day #O the output day (INDEFI if undefined)
+int hours #O the output hours (INDEFI if undefined)
+int minutes #O the output minutes (INDEFI if undefined)
+double seconds #O the output seconds (INDEFD if undefined)
+int flags #O see <time.h>
+
+double dval
+int oldfits, ip, nchars, ival
+int ctoi(), ctod()
+
+begin
+ # Initialize.
+ year = INDEFI
+ month = INDEFI
+ day = INDEFI
+ hours = INDEFI
+ minutes = INDEFI
+ seconds = INDEFD
+ flags = 0
+
+ # Determine whether the format is old or new and get the day
+ # or month accordingly.
+ ip = 1
+ nchars = ctoi (datestr, ip, ival)
+ if (nchars == 2) {
+ flags = or (flags, TF_OLDFITS)
+ oldfits = YES
+ day = ival
+ } else if (nchars == 4) {
+ flags = and (flags, not(TF_OLDFITS))
+ oldfits = NO
+ year = ival
+ } else
+ return (ERR)
+
+ # Check syntax.
+ if (oldfits == NO && datestr[ip] == '-')
+ ip = ip + 1
+ else if (oldfits == YES && datestr[ip] == '/')
+ ip = ip + 1
+ else
+ return (ERR)
+
+ # Get the month.
+ nchars = ctoi (datestr, ip, ival)
+ if (nchars == 2) {
+ month = ival
+ } else
+ return (ERR)
+ if (oldfits == NO && datestr[ip] == '-')
+ ip = ip + 1
+ else if (oldfits == YES && datestr[ip] == '/')
+ ip = ip + 1
+ else
+ return (ERR)
+
+ # Get the year or day.
+ nchars = ctoi (datestr, ip, ival)
+ if (nchars == 2) {
+ if (oldfits == YES)
+ year = 1900 + ival
+ else
+ day = ival
+ } else
+ return (ERR)
+
+ if (datestr[ip] != 'T' || oldfits == YES)
+ return (OK)
+
+ # Get the hours.
+ ip = ip + 1
+ nchars = ctoi (datestr, ip, ival)
+ if (nchars == 2)
+ hours = ival
+ else
+ return (ERR)
+ if (datestr[ip] != ':')
+ return (ERR)
+
+ # Get the minutes.
+ ip = ip + 1
+ nchars = ctoi (datestr, ip, ival)
+ if (nchars == 2)
+ minutes = ival
+ else
+ return (ERR)
+ if (datestr[ip] != ':')
+ return (ERR)
+
+ # Get the seconds.
+ ip = ip + 1
+ nchars = ctod (datestr, ip, dval)
+ if (nchars < 2)
+ return (ERR)
+ else
+ seconds = dval
+
+ # Check for trailing garbage in the input string. Ignore whitespace.
+ while (IS_WHITE(datestr[ip]))
+ ip = ip + 1
+
+ if (datestr[ip] != EOS)
+ return (ERR)
+ else
+ return (OK)
+end
+
+
+# DTM_ENCODE -- Encode year, month, day and time fields into a valid FITS
+# format DATE-OBS string value. The number of characters in the output
+# string is returned as the function value. The returned DATE-OBS keyword
+# value may be in any of the following formats: DD/MM/YY (oldfits = YES,
+# 1900 <= year < 2000), CCYY-MM-DD (oldfits = NO, time = INDEFD), or
+# CCYY-MM-DDTHH:MM:SS[.SSS...] (oldfits = NO, time = double precision
+# number). This routine formats the string but does not check for valid
+# input values. Routines in the SLALIB or ASTUTIL libraries can be used
+# to create valid year, month, day, or time values.
+
+int procedure dtm_encode (datestr, maxch,
+ year, month, day, time, precision, flags)
+
+char datestr[ARB] #O the output date string
+int maxch #I the maximum length of the output date string
+int year #I the input year, e.g. 1999
+int month #I the input month, e.g. 1-12
+int day #I the input day, e.g. 1-31
+double time #I the input time in hours, INDEFD if undefined
+int precision #I the precision of the output time field
+int flags #I see <time.h>
+
+int oldfits, field
+int strlen(), btoi()
+
+begin
+ datestr[1] = EOS
+ oldfits = btoi (and (flags, TF_OLDFITS) != 0)
+
+ if (oldfits == YES) {
+ if (year >= 1900 && year < 2000) {
+ call sprintf (datestr, maxch, "%02d/%02d/%02d")
+ call pargi (day)
+ call pargi (month)
+ call pargi (mod (year, 1900))
+ }
+ } else if (IS_INDEFD(time)) {
+ call sprintf (datestr, maxch, "%04d-%02d-%02d")
+ call pargi (year)
+ call pargi (month)
+ call pargi (day)
+ } else {
+ if (precision <= 0)
+ field = 8
+ else
+ field = 9 + precision
+ call sprintf (datestr, maxch, "%04d-%02d-%02dT%0*.*h")
+ call pargi (year)
+ call pargi (month)
+ call pargi (day)
+ call pargi (field)
+ call pargi (precision)
+ call pargd (time)
+ }
+
+ return (strlen (datestr))
+end
+
+
+# DTM_ENCODE_HMS -- Encode year, month, day, hours, minutes, and seconds
+# fields into a valid FITS format DATE-OBS string value. The number of
+# characters in the output string is returned as the function value. The
+# returned DATE-OBS keyword value may be in any of the following formats:
+# DD/MM/YY (oldfits = YES, 1900 <= year < 2000), CCYY-MM-DD (oldfits = NO,
+# time = INDEFD), or CCYY-MM-DDTHH:MM:SS[.SSS...] (oldfits = NO, time =
+# double precision number). This routine formats the string but does not
+# check for valid input values. Routines in the SLALIB or ASTUTIL libraries
+# can be used to create valid year, month, day, or time values.
+
+int procedure dtm_encode_hms (datestr, maxch,
+ year, month, day, hours, minutes, seconds, precision, flags)
+char datestr[ARB] #O the output date string
+int maxch #I the maximum length of the output date string
+
+int year #I the input year, e.g. 1999
+int month #I the input month, e.g. 1-12
+int day #I the input day, e.g. 1-31
+int hours #I the input hours field, INDEFI if undefined
+int minutes #I the input minutes field, INDEFI if undefined
+double seconds #I the input seconds field, INDEFD if undefined
+int precision #I the precision of the output time field
+int flags #I see <time.h>
+
+int oldfits, field
+int strlen(), btoi()
+
+begin
+
+ datestr[1] = EOS
+ oldfits = btoi (and (flags, TF_OLDFITS) != 0)
+
+ if (oldfits == YES) {
+ if (year >= 1900 && year < 2000) {
+ call sprintf (datestr, maxch, "%02d/%02d/%02d")
+ call pargi (day)
+ call pargi (month)
+ call pargi (mod (year, 1900))
+ }
+ } else if (IS_INDEFI(hours) || IS_INDEFI(minutes) ||
+ IS_INDEFD(seconds)) {
+ call sprintf (datestr, maxch, "%04d-%02d-%02d")
+ call pargi (year)
+ call pargi (month)
+ call pargi (day)
+ } else {
+ if (precision <= 0)
+ field = 2
+ else
+ field = 3 + precision
+ call sprintf (datestr, maxch, "%04d-%02d-%02dT%02d:%02d:%0*.*f")
+ call pargi (year)
+ call pargi (month)
+ call pargi (day)
+ call pargi (hours)
+ call pargi (minutes)
+ call pargi (field)
+ call pargi (precision)
+ call pargd (seconds)
+ }
+
+ return (strlen (datestr))
+end
+
+
+# DTM_LTIME -- Decode a FITS format DATE-OBS string into the number of
+# seconds since 00:00:00 01-Jan-1980. OK is returned if the date string
+# is successfully decoded, ERR if it is not or if it is a negative value.
+# The 'datestr' string value may be in any of the following forms: DD/MM/YY
+# or CCYY-MM-DD (where time is INDEF and assumed to be midnight), or as
+# CCYY-MM-DDTHH:MM:SS[.SSS...].
+
+int procedure dtm_ltime (datestr, ltime)
+
+char datestr[ARB] #I the input date string
+long ltime #O seconds since 00:00:00 01-Jan-1980
+
+double sec
+int oldfits, ndays
+int hr, min, yr, mon, day
+
+double dtm_date_to_julday()
+int dtm_decode_hms()
+
+define START_IRAF_EPOCH 2444239.5 # JD of 00:00:00 01-Jan-1980
+define SECONDS_PER_DAY 86400
+define SECONDS_PER_HOUR 3600
+define SECONDS_PER_MINUTE 60
+
+begin
+ ltime = INDEFL # initialize
+
+ if (dtm_decode_hms (datestr, yr,mon,day, hr,min,sec, oldfits) == ERR)
+ return (ERR)
+
+ # Take care of the assumption that 2-digit years are 1900.
+ if (oldfits == YES)
+ yr = yr + 100
+
+ # If we had a time specified, convert it to the number of seconds
+ # that day.
+ if (IS_INDEFI(hr) || IS_INDEFI(min) || IS_INDEFD(sec))
+ ltime = 0
+ else
+ ltime = (hr * SECONDS_PER_HOUR) + (min * SECONDS_PER_MINUTE) + sec
+
+ # Compute the number of days since the start of the iraf epoch.
+ ndays = dtm_date_to_julday (yr, mon, day, 0.0d0) - START_IRAF_EPOCH
+
+ # Convert days to seconds, add to time from before.
+ ltime = ltime + (ndays * SECONDS_PER_DAY)
+
+ if (ltime >= 0)
+ return (OK)
+ else
+ return (ERR)
+end
+
+
+# DTM_DATE_TO_JULDAY -- Convert date to Julian day. Assumes dates after year 99.
+
+double procedure dtm_date_to_julday (year, month, day, t)
+
+int year # Year
+int month # Month (1-12)
+int day # Day of month
+double t # Time for date (mean solar day)
+
+double jd
+int y, m, d
+
+begin
+ if (year < 100)
+ y = 1900 + year
+ else
+ y = year
+
+ if (month > 2)
+ m = month + 1
+ else {
+ m = month + 13
+ y = y - 1
+ }
+
+ jd = int (365.25D0 * y) + int (30.6001 * m) + day + 1720995
+ if (day + 31 * (m + 12 * y) >= 588829) {
+ d = int (y / 100)
+ m = int (y / 400)
+ jd = jd + 2 - d + m
+ }
+ jd = jd - 0.5 + int (t * 360000. + 0.5) / 360000. / 24.
+ return (jd)
+end
diff --git a/sys/etc/envgetb.x b/sys/etc/envgetb.x
new file mode 100644
index 00000000..564aa67f
--- /dev/null
+++ b/sys/etc/envgetb.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# ENVGETB -- Check whether the named option is set in the environment.
+# Return true only if the option is defined in the environment and either has
+# no value string (i.e., the existence of the variable is what is significant)
+# or a value string which begins with the character 'y' or 'Y'.
+
+bool procedure envgetb (varname)
+
+char varname[ARB]
+bool answer
+pointer sp, def
+int envfind()
+
+begin
+ call smark (sp)
+ call salloc (def, SZ_LINE, TY_CHAR)
+
+ if (envfind (varname, Memc[def], SZ_LINE) < 0)
+ answer = false
+ else {
+ switch (Memc[def]) {
+ case 'y', 'Y', EOS:
+ answer = true
+ default: # abort not justified
+ answer = false
+ }
+ }
+
+ call sfree (sp)
+ return (answer)
+end
diff --git a/sys/etc/envgetd.x b/sys/etc/envgetd.x
new file mode 100644
index 00000000..749c3aef
--- /dev/null
+++ b/sys/etc/envgetd.x
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <mach.h>
+
+# ENVGETD -- Fetch an environment variable and try to interpret its value
+# as a double. Abort if variable is not found or cannot be converted to
+# a number.
+
+double procedure envgetd (varname)
+
+char varname[ARB]
+
+int ip
+double dval
+char val[MAX_DIGITS]
+int ctod(), envfind()
+errchk envfind, syserrs
+
+begin
+ ip = 1
+ if (envfind (varname, val, MAX_DIGITS) > 0)
+ if (ctod (val, ip, dval) > 0)
+ return (dval)
+
+ call syserrs (SYS_ENVNNUM, varname)
+end
diff --git a/sys/etc/envgeti.x b/sys/etc/envgeti.x
new file mode 100644
index 00000000..a3e14190
--- /dev/null
+++ b/sys/etc/envgeti.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <mach.h>
+
+# ENVGETI -- Fetch an environment variable and try to interpret its value
+# as an integer. Abort if variable is not found or cannot be converted to
+# a number.
+
+int procedure envgeti (varname)
+
+char varname[ARB]
+
+int ival, ip
+char val[MAX_DIGITS]
+int ctoi(), envfind()
+errchk envfind, syserrs
+
+begin
+ ip = 1
+ if (envfind (varname, val, MAX_DIGITS) > 0)
+ if (ctoi (val, ip, ival) > 0)
+ return (ival)
+
+ call syserrs (SYS_ENVNNUM, varname)
+end
diff --git a/sys/etc/envgetr.x b/sys/etc/envgetr.x
new file mode 100644
index 00000000..5ec04d58
--- /dev/null
+++ b/sys/etc/envgetr.x
@@ -0,0 +1,18 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# ENVGETR -- Fetch an environment variable and try to interpret its value
+# as a real. Abort if variable is not found or cannot be converted to
+# a number.
+
+real procedure envgetr (varname)
+
+char varname[ARB]
+double val, envgetd()
+
+begin
+ val = envgetd (varname)
+ if (IS_INDEFD(val))
+ return (INDEFR)
+ else
+ return (val)
+end
diff --git a/sys/etc/envgets.x b/sys/etc/envgets.x
new file mode 100644
index 00000000..969a660d
--- /dev/null
+++ b/sys/etc/envgets.x
@@ -0,0 +1,62 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <fset.h>
+include "environ.h"
+
+# ENVGETS -- Search the environment list for the named environment variable
+# and return the string value if found. If not found and the process input
+# is a terminal (the process is being run interactively in debug mode), generate
+# a query on the terminal, read the value of the environment variable, enter
+# it into the environment table, and return the value to the caller.
+
+int procedure envgets (key, value, maxch)
+
+char key[ARB] # environment variable name
+char value[maxch] # string value (output)
+int maxch
+
+char buf[SZ_FNAME]
+int nchars, ttydriver, junk, in, out, ip
+int gstrcpy(), envfind(), fstati(), strlen(), envputs()
+extern zgetty()
+
+begin
+ # Search the environment list first.
+ nchars = envfind (key, value, maxch)
+ if (nchars >= 0)
+ return (nchars)
+
+ # Key not found. If the process input CLIN is a terminal, query the
+ # user for the value of the environment variable. Only low level
+ # calls are used in the query to avoid the possibity of recursion.
+
+ call zlocpr (zgetty, ttydriver)
+ iferr {
+ out = fstati (CLOUT, F_CHANNEL)
+ in = fstati (CLIN, F_CHANNEL)
+ } then
+ return (0)
+
+ if (fstati (CLIN, F_DEVICE) == ttydriver) {
+ # Issue prompt, format "env.key: "
+ call zputty (out, "env.", 4, junk)
+ call zputty (out, key, strlen(key), junk)
+ call zputty (out, ": ", 2, junk)
+ call zflsty (out, junk)
+
+ # Get value and enter in envlist, excluding the trailing newline.
+
+ call zgetty (in, buf, SZ_FNAME, nchars)
+ if (nchars <= 0)
+ return (0)
+ for (ip=1; buf[ip] != '\n' && ip <= nchars; ip=ip+1)
+ ;
+ buf[ip] = EOS
+ junk = envputs (key, buf)
+
+ return (gstrcpy (buf, value, maxch))
+
+ } else
+ return (0)
+end
diff --git a/sys/etc/envindir.x b/sys/etc/envindir.x
new file mode 100644
index 00000000..48902eee
--- /dev/null
+++ b/sys/etc/envindir.x
@@ -0,0 +1,31 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+
+# ENVINDIR -- Return the name of an environment variable which may be given
+# by the value of another environment variable as "@envvar".
+
+procedure envindir (envvar, outstr, maxch)
+
+char envvar[ARB] # possibly indirect env. variable name
+char outstr[ARB] # receives value of variable
+int maxch
+
+pointer sp, envname
+int envfind()
+errchk syserrs
+
+begin
+ call smark (sp)
+ call salloc (envname, SZ_FNAME, TY_CHAR)
+
+ call strcpy (envvar, outstr, maxch)
+
+ while (outstr[1] == '@') {
+ call strcpy (outstr[2], Memc[envname], SZ_FNAME)
+ if (envfind (Memc[envname], outstr, maxch) <= 0)
+ call syserrs (SYS_ENVNF, Memc[envname])
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/etc/envinit.x b/sys/etc/envinit.x
new file mode 100644
index 00000000..3b58f444
--- /dev/null
+++ b/sys/etc/envinit.x
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "environ.h"
+
+# ENV_INIT -- Called by the IRAF main to initialize the environment common
+# upon process startup.
+
+procedure env_init()
+
+bool first_time
+int kmalloc()
+include "environ.com"
+data first_time /true/
+
+begin
+ if (first_time) {
+ if (kmalloc (envbuf, LEN_ENVBUF, TY_SHORT) == ERR)
+ call sys_panic (SYS_MFULL, "Out of memory")
+
+ call aclrs (threads, NTHREADS)
+ len_envbuf = LEN_ENVBUF
+ last = NULL
+ top = 1
+ first_time = false
+ }
+end
diff --git a/sys/etc/environ.com b/sys/etc/environ.com
new file mode 100644
index 00000000..4a2fae17
--- /dev/null
+++ b/sys/etc/environ.com
@@ -0,0 +1,8 @@
+# Common for the environment list package.
+
+pointer envbuf # buffer containing the environment list
+int len_envbuf # length of the envbuf buffer
+int last # index of the last list element entered
+int top # index of the next list element
+short threads[NTHREADS] # hashed threads through list
+common /envcom/ envbuf, len_envbuf, last, top, threads
diff --git a/sys/etc/environ.h b/sys/etc/environ.h
new file mode 100644
index 00000000..ccdc77c8
--- /dev/null
+++ b/sys/etc/environ.h
@@ -0,0 +1,28 @@
+# ENVIRON.H -- Global defines for the environment list package.
+
+# Strings may optionally be quoted in SET stmts with either ' or ".
+define IS_QUOTE ($1 == '\'' || $1 == '"')
+
+# Size limiting definitions.
+
+define NTHREADS 199 # number of hash threads
+define MAX_HASHCHARS 18 # max chars to use for hashing
+define LEN_ENVBUF 20480 # storage for environment list
+define INC_ENVBUF 4096 # increment if overflow occurs
+define MAX_SZKEY 32 # max chars in a key
+define MIN_SZVALUE 20 # min allocated space for value
+define MAX_SZVALUE 4096 # max chars in value string
+define MAX_LENLISTELEM (4+(MAX_SZKEY+1+MAX_SZVALUE+1+SZ_SHORT-1)/SZ_SHORT)
+
+# List element structure, stored in ENVBUF, which is allocated as an array of
+# type SHORT integer. Each list element is aligned on a short integer boundary
+# within the array. E_NEXT points to the next element in a thread, whereas
+# E_LASTELEM points to the last element in the envbuf (which is a stack).
+
+define E_NEXT Mems[$1] # next element in thread (list)
+define E_LASTELEM Mems[$1+1] # next element in envbuf
+define E_REDEF Mems[$1+2] # set if element is redefined
+define E_LEN Mems[$1+3] # nchars allocated for value string
+define E_SETP (($1+4-1)*SZ_SHORT+1) # char pointer to name field
+define E_SET Memc[E_SETP($1)] # "name=value" string
+define E_SETOFFSET 4
diff --git a/sys/etc/environ.x b/sys/etc/environ.x
new file mode 100644
index 00000000..24e6b814
--- /dev/null
+++ b/sys/etc/environ.x
@@ -0,0 +1,315 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <knet.h>
+include "environ.h"
+
+.help environ
+.nf ___________________________________________________________________________
+ENVIRON -- Routines for managing the environment list. The environment list
+is global in scope. A process, e.g., the CL, builds up the environment list
+and passes it on to a child process when the process is spawned.
+
+ nchars = envgets (name, value, maxch) # get value of envvar
+ redef = envputs (name, value) # set value of envvar
+ nchars = envfind (name, value, maxch) # get value of envvar if def
+ envmark (sp) # mark stack pointer
+ nredefs = envfree (sp, userfcn) # free back to marked posn
+
+ bool = envgetb (name) # get boolean value of envvar
+ int = envgeti (name) # get integer value of envvar
+ envlist (fd, prefix, show_redefs) # print envlist on file
+ nscan = envscan (input_source) # read SET stmts from a file
+
+ el = env_first (valp) # head of envlist
+ el = env_next (el, valp, show_redefs) # next element of envlist
+
+The environment list is maintained as a multi-threaded linked list. This
+provides the searching efficiency of a hash table plus stack like semantics
+for redefinitions and for freeing blocks of variables. There are two primary
+data structures internally, an array of pointers to the heads of the threads,
+and a buffer containing the list elements. These data structures are
+dynamically allocated and will be automatically reallocated at runtime if
+overflow occurs. The number of threads determines the hashing efficiency and
+is a compile time parameter.
+
+The ENVMARK and ENVFREE procedures mark and free storage on the environment
+list stack. All environment variables defined or redefined after a call to
+ENVMARK will be deleted and storage freed by a call to ENVFREE. If a redef
+is freed the next most recent definition becomes current. ENVFREE returns
+as its function value the number of redefined variables uncovered by the free
+operation. The calling program must mark and free in the correct order or the
+environment list may be trashed.
+
+The ENVLIST procedure prints the environment list on a file. Redefined values
+will only be printed if desired. The environment list is printed as a list of
+SET statements in most recent first order, i.e.,
+
+ set nameN=valueN
+ set nameM=valueM
+ ...
+ set name1=value1
+
+The ENVLIST function is used both to inspect the environment list and to pass
+the list on to a child process. Redefined variables are omitted when passing
+the list on to a child process, hence the order of definition does not matter.
+The output format is "prefix name=value", where the prefix string is supplied
+by the user.
+
+The ENVSCAN function parses one or more SET statements, calling ENVPUTS to
+enter the SET declarations into the environment list. The argument is either
+a SET declaration or a string of the form "set @filename", where "filename" is
+the name of a file containing set declarations.
+.endhelp ______________________________________________________________________
+
+
+# ENVFIND -- Search the environment list for the named environment variable
+# and return the string value if found.
+
+int procedure envfind (key, value, maxch)
+
+char key[ARB] # environment variable name
+char value[maxch] # string value (output)
+int maxch
+
+long sum
+pointer el, ep
+int head, ip, nchars
+int envputs(), gstrcpy()
+include "environ.com"
+
+begin
+ # Get index into envbuf of the first element of the thread.
+ if (key[1] == EOS)
+ head = NULL
+ else {
+ sum = 0
+ do ip = 1, MAX_HASHCHARS {
+ if (key[ip] == EOS)
+ break
+ sum = sum + (sum + key[ip])
+ }
+ head = threads[mod(sum,NTHREADS)+1]
+ }
+
+ # If thread is not empty search down it for the named key and return
+ # the value string if found. Note that the value of the E_NEXT pointer
+ # is given as an integer offset into envbuf to facilitate reallocation
+ # upon overflow.
+
+ if (head != NULL)
+ for (el = envbuf + head; el > envbuf; el = envbuf + E_NEXT(el)) {
+ ep = E_SETP(el)
+ for (ip=1; key[ip] == Memc[ep]; ip=ip+1)
+ ep = ep + 1
+ if (key[ip] == EOS && Memc[ep] == '=')
+ return (gstrcpy (Memc[ep+1], value, maxch))
+ }
+
+ # Key not found. Ask the host system for the value of the environment
+ # variable.
+
+ call strpak (key, value, maxch)
+ call zgtenv (value, value, maxch, nchars)
+
+ if (nchars >= 0) {
+ call strupk (value, value, maxch)
+ ip = envputs (key, value)
+ return (nchars)
+ } else {
+ value[1] = EOS
+ return (ERR)
+ }
+end
+
+
+# ENVPUTS -- Add a new SET definition to the environment list. A SET operation
+# is allowed to redefine a previously defined environment variable, but if the
+# new definition is a redef we return YES as the function value. If the set
+# is a no-op (null key, or redef with the same value as previously) the envlist
+# is not modified and NO is returned as the function value.
+
+int procedure envputs (key, value)
+
+char key[ARB] # environment variable name
+char value[ARB] # string value
+
+long sum
+int head, thread_index, redef, ip
+pointer el, op, ep
+
+bool streq()
+pointer coerce()
+int gstrcpy(), krealloc()
+include "environ.com"
+
+begin
+ if (key[1] == EOS)
+ return (NO)
+
+ # Get index into envbuf of the first element of the thread.
+ sum = 0
+ do ip = 1, MAX_HASHCHARS {
+ if (key[ip] == EOS)
+ break
+ sum = sum + (sum + key[ip])
+ }
+
+ thread_index = mod (sum, NTHREADS) + 1
+ head = threads[thread_index]
+
+ # If thread is not empty search down it for the named key to see if we
+ # have a redefinition. If we have a redef but the new value is the
+ # same as the old, do nothing. Otherwise flag the element being
+ # redefined as a redefinition (so that ENVLIST can ignore it).
+
+ redef = NO
+ if (head != NULL)
+ for (el = envbuf + head; el > envbuf; el = envbuf + E_NEXT(el)) {
+ ep = E_SETP(el)
+ for (ip=1; key[ip] == Memc[ep]; ip=ip+1)
+ ep = ep + 1
+ if (key[ip] == EOS && Memc[ep] == '=')
+ if (streq (Memc[ep+1], value))
+ return (NO)
+ else {
+ E_REDEF(el) = YES
+ redef = YES
+ break
+ }
+ }
+
+ # Append the new list element to the end of ENVBUF, increasing the size
+ # of the buffer if overflow occurs. The list structure must be aligned
+ # on a short integer boundary. Set the back link pointers for searches.
+
+ if (top + MAX_LENLISTELEM >= len_envbuf) {
+ len_envbuf = len_envbuf + INC_ENVBUF
+ if (krealloc (envbuf, len_envbuf, TY_SHORT) == ERR)
+ call sys_panic (SYS_MFULL, "Out of memory")
+ }
+
+ el = envbuf + top
+ E_NEXT(el) = head
+ E_LASTELEM(el) = last
+ E_REDEF(el) = NO
+
+ # Deposit the string "key=value" in the E_SET field. At least
+ # MIN_SZVALUE chars are allocated for the value string, to permit
+ # the value to be updated via ENVRESET (possibly changing size in
+ # the process).
+
+ op = E_SETP(el)
+ op = op + gstrcpy (key, Memc[op], MAX_SZKEY)
+ Memc[op] = '='
+ op = op + 1
+ E_LEN(el) = max (MIN_SZVALUE, gstrcpy(value,Memc[op],MAX_SZVALUE))
+ op = op + E_LEN(el) + 1
+
+ last = top
+ threads[thread_index] = last
+ top = coerce (op, TY_CHAR, TY_SHORT) - envbuf
+
+ # Update the environment in any connected kernel servers.
+ call ki_envreset (key, value)
+
+ return (redef)
+end
+
+
+# ENVMARK -- Mark the position in the environment list. A subsequent call
+# to ENVFREE with the marked position as argument will unset all elements
+# set after the marked position.
+
+procedure envmark (old_top)
+
+int old_top # top of envbuf stack
+include "environ.com"
+
+begin
+ old_top = top
+end
+
+
+# ENVFREE -- Free all environment list entries set since the matching call
+# to ENVMARK. Return as the function value the number of redefined environment
+# variables uncovered by the free operation. If the ZLOCPR integer entry point
+# address of the user supplied function USERFCN is nonnull the function will
+# be called with the name and value of each uncovered redefinition. The calling
+# sequence is as follows: subroutine userfcn (name, value)
+
+int procedure envfree (old_top, userfcn)
+
+int old_top # top of envbuf stack
+int userfcn # epa of function called for uncovered redefs
+
+int nredefs, head, i, j, t
+pointer sp, start, namep, el1, el2, ep1, ep2
+include "environ.com"
+
+begin
+ if (old_top < 1 || old_top >= top)
+ return (0)
+
+ call smark (sp)
+ call salloc (namep, SZ_FNAME, TY_CHAR)
+
+ nredefs = 0
+
+ # Clear the redef flags for all list elements that are redefined by
+ # elements above the new top, and count the number of uncovered redefs.
+ # Examine only non-empty threads.
+
+ for (t=1; t <= NTHREADS; t=t+1) {
+ head = threads[t]
+ if (head != NULL) {
+ # Examine only list elements in the thread which lie above the
+ # top we are reverting to.
+
+ for (j = head; j >= old_top; j = E_NEXT(el1)) {
+ el1 = envbuf + j
+
+ # Scan down the thread to see if this is a redefinition,
+ # and clear the redef flag if so.
+
+ for (i = j; i != NULL; i = E_NEXT(el2)) {
+ el2 = envbuf + i
+ if (E_REDEF(el2) == YES) {
+ ep1 = E_SETP(el1)
+ ep2 = E_SETP(el2)
+ start = ep2
+ while (Memc[ep1] == Memc[ep2] && Memc[ep1] != '=') {
+ ep1 = ep1 + 1
+ ep2 = ep2 + 1
+ }
+ if (Memc[ep1] == '=') {
+ E_REDEF(el2) = NO
+ nredefs = nredefs + 1
+ if (userfcn != NULL) {
+ call strcpy (Memc[start], Memc[namep],
+ ep2 - start)
+ call zcall2 (userfcn,
+ Memc[namep], Memc[ep2+1])
+ }
+ break
+ }
+ }
+ }
+ }
+
+ # Set the head of the thread to the first element below
+ # the new top.
+
+ threads[t] = j
+ }
+ }
+
+ # The variable OLD_TOP is the index of a list element. Make it the
+ # new top.
+
+ top = old_top
+ last = E_LASTELEM(envbuf+top)
+
+ call sfree (sp)
+ return (nredefs)
+end
diff --git a/sys/etc/envlist.x b/sys/etc/envlist.x
new file mode 100644
index 00000000..ebd904b6
--- /dev/null
+++ b/sys/etc/envlist.x
@@ -0,0 +1,25 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "environ.h"
+
+# ENVLIST -- Print the environment list on the output file as a sequence of
+# SET commands. The commands are given in the reverse of the order in which
+# they were originally entered. Printing of redefined variables may be
+# inhibited if desired.
+
+procedure envlist (fd, prefix, print_redefined_variables)
+
+int fd # output file
+char prefix[ARB] # prefix string to be prepended to each line
+int print_redefined_variables
+pointer el
+include "environ.com"
+
+begin
+ for (el = envbuf + last; el > envbuf; el = envbuf + E_LASTELEM(el))
+ if (E_REDEF(el) == NO || print_redefined_variables == YES) {
+ call putline (fd, prefix)
+ call putline (fd, E_SET(el))
+ call putci (fd, '\n')
+ }
+end
diff --git a/sys/etc/envnext.x b/sys/etc/envnext.x
new file mode 100644
index 00000000..80ddf226
--- /dev/null
+++ b/sys/etc/envnext.x
@@ -0,0 +1,53 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "environ.h"
+
+# ENV_FIRST -- Return a pointer to the first (most recently entered) entry
+# in the environment list. A pointer to the string definition of the entry
+# is returned as the output argument.
+
+pointer procedure env_first (valp)
+
+pointer valp # pointer to environment string
+pointer el
+include "environ.com"
+
+begin
+ el = envbuf + last
+ if (el > envbuf) {
+ valp = E_SETP(el)
+ return (el)
+ } else
+ return (NULL)
+end
+
+
+# ENV_NEXT -- Return a pointer to the next element in the environment list.
+# A pointer to the string value of the element is returned as the output
+# argument.
+
+pointer procedure env_next (last_el, valp, show_redefines)
+
+pointer last_el # pointer to last element returned
+pointer valp # receives charp of next element define string
+int show_redefines # do not skip redefined elements
+
+pointer el
+include "environ.com"
+
+begin
+ el = envbuf + E_LASTELEM(last_el)
+
+ while (el > envbuf) {
+ if (E_REDEF(el) == NO || show_redefines == YES)
+ break
+ else
+ el = envbuf + E_LASTELEM(el)
+ }
+
+ if (el > envbuf) {
+ valp = E_SETP(el)
+ return (el)
+ } else
+ return (NULL)
+end
diff --git a/sys/etc/envreset.x b/sys/etc/envreset.x
new file mode 100644
index 00000000..9ca8c5d7
--- /dev/null
+++ b/sys/etc/envreset.x
@@ -0,0 +1,66 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include "environ.h"
+
+# ENVRESET -- Update the value of the named environment variable in place.
+# This is used to permanently change the value of an environment variable,
+# unlike ENVPUTS which will create a temporary redefinition which can later
+# be discarded via ENVFREE. A fixed amount of string storage is allocated
+# for the value string when the environment variable is first defined; if
+# the new value won't fit we simply call ENVPUTS to redefine the variable
+# at the top of the environment stack. A more sophisticated storage
+# mechanism could be devised which could dynamically allocate more storage,
+# but the simpler scheme seems adequate at present.
+
+procedure envreset (key, value)
+
+char key[ARB] # environment variable name
+char value[ARB] # new string value
+
+long sum
+pointer el, ep
+int head, ip, junk, maxch
+int envputs(), strlen()
+include "environ.com"
+
+begin
+ # Get index into envbuf of the first element of the thread.
+ if (key[1] == EOS)
+ head = NULL
+ else {
+ sum = 0
+ do ip = 1, MAX_HASHCHARS {
+ if (key[ip] == EOS)
+ break
+ sum = sum + (sum + key[ip])
+ }
+ head = threads[mod(sum,NTHREADS)+1]
+ }
+
+ # If thread is not empty search down it for the named key; if the key
+ # is redefined the most recent entry is updated.
+
+ el = NULL
+ if (head != NULL)
+ for (el = envbuf + head; el > envbuf; el = envbuf + E_NEXT(el)) {
+ ep = E_SETP(el)
+ for (ip=1; key[ip] == Memc[ep]; ip=ip+1)
+ ep = ep + 1
+ if (key[ip] == EOS && Memc[ep] == '=')
+ break
+ }
+
+ # If the named key is not found or the new value won't fit add or
+ # redefine the variable, otherwise set the new value.
+
+ if (el <= envbuf)
+ junk = envputs (key, value)
+ else if (strlen(value) > E_LEN(el))
+ junk = envputs (key, value)
+ else {
+ maxch = E_LEN(el)
+ call strcpy (value, Memc[ep+1], maxch)
+ call ki_envreset (key, value)
+ }
+end
diff --git a/sys/etc/envscan.x b/sys/etc/envscan.x
new file mode 100644
index 00000000..06f7a411
--- /dev/null
+++ b/sys/etc/envscan.x
@@ -0,0 +1,149 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include "environ.h"
+
+define MAXLEV 8 # max nesting of includes
+define SZ_LBUF (SZ_COMMAND+SZ_LINE) # max length SET on a single line
+
+
+# ENVSCAN -- Parse one or more SET or RESET declarations and enter them into
+# the environment list.
+#
+# Syntax: (set|reset) name = value enter a definition
+# set @filename scan a file
+#
+# Comments, blank lines, and lines containing unrecognized statements are
+# ignored without warning.
+
+int procedure envscan (cmd)
+
+char cmd[ARB] # command text to begin scan
+
+char ch
+int fd, in, nset, lev, sv_fd[MAXLEV]
+pointer sp, ip, op, op_top, lbuf, name, value
+int open(), stropen(), getlline(), strmatch(), nowhite()
+errchk open, stropen, getlline, syserrs
+string s_reset "^#reset#"
+string s_set "^#set#"
+define again_ 91
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_LBUF, TY_CHAR)
+ call salloc (name, MAX_SZKEY, TY_CHAR)
+ call salloc (value, MAX_SZVALUE, TY_CHAR)
+
+ # Position to after the set or reset.
+ in = strmatch (cmd, s_set)
+ if (in == 0) {
+ in = strmatch (cmd, s_reset)
+ if (in == 0) {
+ call sfree (sp)
+ return (0)
+ }
+ }
+
+ # Open the input to be scanned.
+ if (cmd[in] == '@')
+ fd = open (cmd[in+1], READ_ONLY, TEXT_FILE)
+ else
+ fd = stropen (cmd, ARB, READ_ONLY)
+
+ # Process all SET or RESET statements in the file. Ignore all other
+ # statements.
+
+ nset = 0
+ lev = 0
+
+ repeat {
+ # Get the next SET statement into lbuf, leave IN at index of first
+ # char of the name field.
+
+ if (getlline (fd, Memc[lbuf], SZ_LBUF) == EOF) {
+ if (lev > 0) {
+ call close (fd)
+ fd = sv_fd[lev]
+ lev = lev - 1
+ next
+ } else
+ break
+ } else if (Memc[lbuf] == '\n' || Memc[lbuf] == '#') {
+ next
+ } else {
+ in = strmatch (Memc[lbuf], s_set)
+ if (in == 0)
+ in = strmatch (Memc[lbuf], s_reset)
+
+ if (in <= 0)
+ next
+ else if (Memc[lbuf+in-1] == '@') {
+ ch = nowhite (Memc[lbuf+in], Memc[lbuf], SZ_LINE)
+ lev = lev + 1
+ if (lev > MAXLEV)
+ call syserrs (SYS_FOPEN, Memc[lbuf])
+ sv_fd[lev] = fd
+ fd = open (Memc[lbuf], READ_ONLY, TEXT_FILE)
+ next
+ }
+ }
+
+ # Parse the name and value strings and enter into the environment
+ # list. Ignore optional quotes and whitespace. Ignore rest of
+ # line following the value field.
+
+ op = name
+ op_top = name + MAX_SZKEY
+ ip = lbuf + in - 1
+ for (ch=Memc[ip]; ch != '=' && ch != EOS; ch=Memc[ip]) {
+ if (!IS_QUOTE(ch) && !IS_WHITE(ch)) {
+ Memc[op] = Memc[ip]
+ op = min (op_top, op + 1)
+ }
+ ip = ip + 1
+ }
+ Memc[op] = EOS
+
+ op = value
+ if (Memc[ip] == '=') {
+ ip = ip + 1
+ while (IS_WHITE(Memc[ip]) || IS_QUOTE(Memc[ip]))
+ ip = ip + 1
+ op_top = value + MAX_SZVALUE
+
+ for (ch=Memc[ip]; ch != EOS; ch=Memc[ip]) {
+ if (IS_QUOTE(ch) || ch == '\n') {
+ break
+
+ } else if (ch == '\\' && Memc[ip+1] == '\n') {
+again_ if (getlline (fd, Memc[lbuf], SZ_LBUF) == EOF)
+ break
+
+ # Skip leading whitespace on the continuation line.
+ for (ip=lbuf; IS_WHITE(Memc[ip]); ip=ip+1)
+ ;
+ # Check for a commented out continuation line.
+ if (Memc[ip] == '#')
+ goto again_
+
+ } else {
+ Memc[op] = ch
+ op = min (op_top, op + 1)
+ ip = ip + 1
+ }
+ }
+ }
+ Memc[op] = EOS
+
+ # Enter the SET definition into the environment list.
+ call envreset (Memc[name], Memc[value])
+ nset = nset + 1
+ }
+
+ call close (fd)
+ call sfree (sp)
+
+ return (nset)
+end
diff --git a/sys/etc/erract.x b/sys/etc/erract.x
new file mode 100644
index 00000000..5400544d
--- /dev/null
+++ b/sys/etc/erract.x
@@ -0,0 +1,93 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <error.h>
+
+.help erract
+.nf _________________________________________________________________________
+ERRACT -- Take error action. Called by FATAL, and by ERROR if a handler
+is not posted. May be called by a user error handler to pass an error
+back up to the handler at the next level, or to change the severity of
+an error. Warning messages are posted to the standard error output,
+whereas fatal errors result in error recovery followed by transmission of
+the ERROR statement to the CL.
+
+Error restart consists of the following steps:
+
+ (1) The IRAF main is restarted with the error code as argument.
+ (2) The main goes through error recovery. Error recovery consists
+ of cleaning up the files system, i.e., closing open files and
+ deleting NEW_FILES and TEMP_FILES, clearing the stack, and calling
+ any procedures posted with ONERROR.
+ (3) The ERROR statement is sent to the CL. An example of the
+ error statment is "ERROR (501, "Access Violation")".
+ (4) The main either waits for the next command, or if run from the CL
+ and the error code is SYS_XINT (a CL kill in response to a keyboard
+ interrupt), the main returns, shutting the process down. Procedures
+ posted with ONEXIT are called when the process shuts down.
+
+Any errors occuring during error restart or while executing the ONEXIT
+procedures are fatal and result in immediate process termination, usually
+with a panic error message. This is necessary to prevent infinite error
+recursion. Also, if we are killed by the CL we should die and not hang up
+trying to send error messages to the CL.
+.endhelp ____________________________________________________________________
+
+procedure erract (severity)
+
+int severity
+int op, jumpbuf[LEN_JUMPBUF]
+char wmsg[SZ_LINE]
+int gstrcpy()
+include "error.com"
+common /JUMPCOM/ jumpbuf
+
+begin
+ # Clear error restart condition. Called by the IRAF Main
+ # after successful completion of error recovery.
+
+ if (severity == EA_RESTART) {
+ err_restart = err_restart + 1
+ xerflg = false
+ return
+ } else if (severity == OK) {
+ err_restart = 0
+ xerflg = false
+ return
+ }
+
+ # Any uncaught errors occuring during error restart are fatal and
+ # will result in process termination. This is necessary to prevent
+ # recursion and to ensure that a process killed by the CL dies if
+ # it cannot complete cleanup and shutdown without errors. If error
+ # recursion occurs we will be called repeatedly, causing the counter
+ # to be incremented until a panic abort occurs.
+
+ if (severity != EA_WARN && err_restart > 2) {
+ call xer_fmterrmsg (xermsg, xermsg, SZ_XERMSG)
+ call sys_panic (xercod, xermsg)
+ }
+
+ # If a handler is posted, set flag and return, deferring error
+ # recovery to the user handler. If warning message, merely put
+ # message to stderr. Otherwise initiate error recovery by restarting
+ # the IRAF main. This sounds reentrant, but it is not since it is an
+ # error restart using ZDOJMP. The ERROR statement is not sent to
+ # the CL until error recovery has completed.
+
+ if (severity == EA_ERROR && nhandlers > 0)
+ xerflg = true
+ else {
+ call xer_fmterrmsg (xermsg, xermsg, SZ_XERMSG)
+ if (severity == EA_WARN) {
+ op = gstrcpy ("Warning: ", wmsg, SZ_LINE) + 1
+ op = op + gstrcpy (xermsg, wmsg[op], SZ_LINE - op + 1)
+ wmsg[op] = '\n'
+ wmsg[op+1] = EOS
+ call xer_putline (STDERR, wmsg)
+ } else {
+ err_restart = err_restart + 1
+ call zdojmp (jumpbuf, xercod) # Restart IRAF main.
+ }
+ }
+end
diff --git a/sys/etc/errcode.x b/sys/etc/errcode.x
new file mode 100644
index 00000000..99df994b
--- /dev/null
+++ b/sys/etc/errcode.x
@@ -0,0 +1,18 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+
+# ERRCODE -- Return the integer code of the last error posted. The error
+# code is set to a positive nonnegative integer by a call to ERROR or
+# FATAL, and is cleared (set to OK) whenever an IFERR block is entered.
+# Note that if we are called from within an error handler (true part of
+# an IFERR block), xerflg is false, so we cannot test xerflg to see if
+# an error occurred.
+
+int procedure errcode()
+
+include "error.com"
+
+begin
+ return (xercod)
+end
diff --git a/sys/etc/errget.x b/sys/etc/errget.x
new file mode 100644
index 00000000..e2ba10a9
--- /dev/null
+++ b/sys/etc/errget.x
@@ -0,0 +1,21 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+
+# ERRGET -- Return the integer code and descriptive error message string
+# of the last error posted. The error code is set to a positive nonnegative
+# integer by a call to ERROR or FATAL, and is cleared (set to OK) whenever
+# an IFERR block is entered. Note that if we are called from within an error
+# handler (true part of an IFERR block), xerflg is false, so we cannot test
+# xerflg to see if an error occurred.
+
+int procedure errget (outstr, maxch)
+
+char outstr[maxch] # error message
+int maxch
+include "error.com"
+
+begin
+ call xer_fmterrmsg (xermsg, outstr, maxch)
+ return (xercod)
+end
diff --git a/sys/etc/error.com b/sys/etc/error.com
new file mode 100644
index 00000000..2a7257f1
--- /dev/null
+++ b/sys/etc/error.com
@@ -0,0 +1,7 @@
+
+bool xerflg # set when error is posted
+int xercod # error code
+int nhandlers # handler nesting level
+int err_restart # YES during error restart, NO otherwise
+char xermsg[SZ_XERMSG] # error message string
+common /xercom/ xerflg,xercod,nhandlers,err_restart,xermsg
diff --git a/sys/etc/error.x b/sys/etc/error.x
new file mode 100644
index 00000000..42c390b5
--- /dev/null
+++ b/sys/etc/error.x
@@ -0,0 +1,60 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+
+# ERROR -- Take an error action. A call to ERROR does not necessarily
+# terminate task execution, i.e., if an IFERR error handler is posted it
+# will receive control after the procedure call stack is unwound back
+# to the procedure containing the error handler.
+
+procedure error (error_code, message)
+
+int error_code # positive error code identifying error
+char message[ARB] # error message describing error
+include "error.com"
+
+begin
+ if (xerflg) { # error already posted?
+ if (max(error_code,1) == xercod)
+ return # same error again
+ else
+ call erract (EA_FATAL) # too many errors
+ }
+
+ call xeract (error_code, message, EA_ERROR)
+end
+
+
+# FATAL -- Called when a fatal (irrecoverable) error occurs. Fatal errors
+# cannot be caught by IFERR handlers. The calling task is terminated and
+# error recovery is initiated in the IRAF Main.
+
+procedure fatal (error_code, message)
+
+int error_code # positive error code identifying error
+char message[ARB] # error message describing error
+
+begin
+ call xeract (error_code, message, EA_FATAL)
+end
+
+
+# XERACT -- Take an error action; called by ERROR or FATAL.
+
+procedure xeract (error_code, message, severity)
+
+int error_code # positive error code identifying error
+char message[ARB] # error message describing error
+int severity # severity of the error
+include "error.com"
+
+begin
+ xerflg = true # post error
+ xercod = max (1, error_code)
+ call strcpy (message, xermsg, SZ_XERMSG)
+
+ if (nhandlers > 0 && severity == EA_ERROR) # is a handler posted?
+ return
+ else
+ call erract (severity) # take error action
+end
diff --git a/sys/etc/gen/miireadd.x b/sys/etc/gen/miireadd.x
new file mode 100644
index 00000000..de15b8c1
--- /dev/null
+++ b/sys/etc/gen/miireadd.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIREAD -- Read a block of data stored externally in MII format.
+# Data is returned in the format of the local host machine.
+
+int procedure mii_readd (fd, spp, maxelem)
+
+int fd #I input file
+double spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int miipksize(), miinelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = miipksize (maxelem, MII_DOUBLE)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_DOUBLE) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_DOUBLE))
+ call miiupkd (Memc[bp], spp, nelem, TY_DOUBLE)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_DOUBLE))
+ call miiupkd (spp, spp, nelem, TY_DOUBLE)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/miireadi.x b/sys/etc/gen/miireadi.x
new file mode 100644
index 00000000..666166e6
--- /dev/null
+++ b/sys/etc/gen/miireadi.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIREAD -- Read a block of data stored externally in MII format.
+# Data is returned in the format of the local host machine.
+
+int procedure mii_readi (fd, spp, maxelem)
+
+int fd #I input file
+int spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int miipksize(), miinelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = miipksize (maxelem, MII_INT)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_INT) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_INT))
+ call miiupki (Memc[bp], spp, nelem, TY_INT)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_INT))
+ call miiupki (spp, spp, nelem, TY_INT)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/miireadl.x b/sys/etc/gen/miireadl.x
new file mode 100644
index 00000000..7a43688c
--- /dev/null
+++ b/sys/etc/gen/miireadl.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIREAD -- Read a block of data stored externally in MII format.
+# Data is returned in the format of the local host machine.
+
+int procedure mii_readl (fd, spp, maxelem)
+
+int fd #I input file
+long spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int miipksize(), miinelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = miipksize (maxelem, MII_LONG)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_LONG) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_LONG))
+ call miiupkl (Memc[bp], spp, nelem, TY_LONG)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_LONG))
+ call miiupkl (spp, spp, nelem, TY_LONG)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/miireadr.x b/sys/etc/gen/miireadr.x
new file mode 100644
index 00000000..f3cded45
--- /dev/null
+++ b/sys/etc/gen/miireadr.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIREAD -- Read a block of data stored externally in MII format.
+# Data is returned in the format of the local host machine.
+
+int procedure mii_readr (fd, spp, maxelem)
+
+int fd #I input file
+real spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int miipksize(), miinelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = miipksize (maxelem, MII_REAL)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_REAL) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_REAL))
+ call miiupkr (Memc[bp], spp, nelem, TY_REAL)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_REAL))
+ call miiupkr (spp, spp, nelem, TY_REAL)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/miireads.x b/sys/etc/gen/miireads.x
new file mode 100644
index 00000000..acd7481a
--- /dev/null
+++ b/sys/etc/gen/miireads.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIREAD -- Read a block of data stored externally in MII format.
+# Data is returned in the format of the local host machine.
+
+int procedure mii_reads (fd, spp, maxelem)
+
+int fd #I input file
+short spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int miipksize(), miinelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = miipksize (maxelem, MII_SHORT)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_SHORT) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_SHORT))
+ call miiupks (Memc[bp], spp, nelem, TY_SHORT)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_SHORT))
+ call miiupks (spp, spp, nelem, TY_SHORT)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/miiwrited.x b/sys/etc/gen/miiwrited.x
new file mode 100644
index 00000000..0b8d45c1
--- /dev/null
+++ b/sys/etc/gen/miiwrited.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIWRITE -- Write a block of data to a file in MII format.
+# The input data is in the host system native binary format.
+
+procedure mii_writed (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int miipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = miipksize (nelem, MII_DOUBLE)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call miipakd (spp, Memc[bp], nelem, TY_DOUBLE)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/miiwritei.x b/sys/etc/gen/miiwritei.x
new file mode 100644
index 00000000..aa52be4a
--- /dev/null
+++ b/sys/etc/gen/miiwritei.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIWRITE -- Write a block of data to a file in MII format.
+# The input data is in the host system native binary format.
+
+procedure mii_writei (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int miipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = miipksize (nelem, MII_INT)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call miipaki (spp, Memc[bp], nelem, TY_INT)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/miiwritel.x b/sys/etc/gen/miiwritel.x
new file mode 100644
index 00000000..f9b800a5
--- /dev/null
+++ b/sys/etc/gen/miiwritel.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIWRITE -- Write a block of data to a file in MII format.
+# The input data is in the host system native binary format.
+
+procedure mii_writel (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int miipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = miipksize (nelem, MII_LONG)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call miipakl (spp, Memc[bp], nelem, TY_LONG)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/miiwriter.x b/sys/etc/gen/miiwriter.x
new file mode 100644
index 00000000..94dcec38
--- /dev/null
+++ b/sys/etc/gen/miiwriter.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIWRITE -- Write a block of data to a file in MII format.
+# The input data is in the host system native binary format.
+
+procedure mii_writer (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int miipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = miipksize (nelem, MII_REAL)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call miipakr (spp, Memc[bp], nelem, TY_REAL)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/miiwrites.x b/sys/etc/gen/miiwrites.x
new file mode 100644
index 00000000..ec2f48aa
--- /dev/null
+++ b/sys/etc/gen/miiwrites.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIWRITE -- Write a block of data to a file in MII format.
+# The input data is in the host system native binary format.
+
+procedure mii_writes (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int miipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = miipksize (nelem, MII_SHORT)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call miipaks (spp, Memc[bp], nelem, TY_SHORT)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/mkpkg b/sys/etc/gen/mkpkg
new file mode 100644
index 00000000..5437d80d
--- /dev/null
+++ b/sys/etc/gen/mkpkg
@@ -0,0 +1,30 @@
+# Make the ETC portion of the system library libsys.a.
+
+$checkout libsys.a lib$
+$update libsys.a
+$checkin libsys.a lib$
+$exit
+
+libsys.a:
+ miireadd.x <mii.h>
+ miireadi.x <mii.h>
+ miireadl.x <mii.h>
+ miireadr.x <mii.h>
+ miireads.x <mii.h>
+ miiwrited.x <mii.h>
+ miiwritei.x <mii.h>
+ miiwritel.x <mii.h>
+ miiwriter.x <mii.h>
+ miiwrites.x <mii.h>
+
+ nmireadd.x <nmi.h>
+ nmireadi.x <nmi.h>
+ nmireadl.x <nmi.h>
+ nmireadr.x <nmi.h>
+ nmireads.x <nmi.h>
+ nmiwrited.x <nmi.h>
+ nmiwritei.x <nmi.h>
+ nmiwritel.x <nmi.h>
+ nmiwriter.x <nmi.h>
+ nmiwrites.x <nmi.h>
+ ;
diff --git a/sys/etc/gen/nmireadb.x b/sys/etc/gen/nmireadb.x
new file mode 100644
index 00000000..c3c0f75e
--- /dev/null
+++ b/sys/etc/gen/nmireadb.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMIREAD -- Read a block of data stored externally in NMI format.
+# Data is returned in the format of the local host machine.
+
+int procedure nmi_readb (fd, spp, maxelem)
+
+int fd #I input file
+bool spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int nmipksize(), nminelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = nmipksize (maxelem, NMI_BOOL)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_BOOL) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_BOOL))
+ call nmiupkb (Memc[bp], spp, nelem, TY_BOOL)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_BOOL))
+ call nmiupkb (spp, spp, nelem, TY_BOOL)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/nmireadd.x b/sys/etc/gen/nmireadd.x
new file mode 100644
index 00000000..2d7c086a
--- /dev/null
+++ b/sys/etc/gen/nmireadd.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_READ -- Read a block of data stored externally in NMI format.
+# Data is returned in the format of the local host machine.
+
+int procedure nmi_readd (fd, spp, maxelem)
+
+int fd #I input file
+double spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int nmipksize(), nminelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = nmipksize (maxelem, NMI_DOUBLE)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_DOUBLE) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_DOUBLE))
+ call nmiupkd (Memc[bp], spp, nelem, TY_DOUBLE)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_DOUBLE))
+ call nmiupkd (spp, spp, nelem, TY_DOUBLE)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/nmireadi.x b/sys/etc/gen/nmireadi.x
new file mode 100644
index 00000000..c07d5914
--- /dev/null
+++ b/sys/etc/gen/nmireadi.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_READ -- Read a block of data stored externally in NMI format.
+# Data is returned in the format of the local host machine.
+
+int procedure nmi_readi (fd, spp, maxelem)
+
+int fd #I input file
+int spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int nmipksize(), nminelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = nmipksize (maxelem, NMI_INT)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_INT) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_INT))
+ call nmiupki (Memc[bp], spp, nelem, TY_INT)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_INT))
+ call nmiupki (spp, spp, nelem, TY_INT)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/nmireadl.x b/sys/etc/gen/nmireadl.x
new file mode 100644
index 00000000..888beedf
--- /dev/null
+++ b/sys/etc/gen/nmireadl.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_READ -- Read a block of data stored externally in NMI format.
+# Data is returned in the format of the local host machine.
+
+int procedure nmi_readl (fd, spp, maxelem)
+
+int fd #I input file
+long spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int nmipksize(), nminelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = nmipksize (maxelem, NMI_LONG)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_LONG) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_LONG))
+ call nmiupkl (Memc[bp], spp, nelem, TY_LONG)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_LONG))
+ call nmiupkl (spp, spp, nelem, TY_LONG)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/nmireadr.x b/sys/etc/gen/nmireadr.x
new file mode 100644
index 00000000..e8338400
--- /dev/null
+++ b/sys/etc/gen/nmireadr.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_READ -- Read a block of data stored externally in NMI format.
+# Data is returned in the format of the local host machine.
+
+int procedure nmi_readr (fd, spp, maxelem)
+
+int fd #I input file
+real spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int nmipksize(), nminelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = nmipksize (maxelem, NMI_REAL)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_REAL) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_REAL))
+ call nmiupkr (Memc[bp], spp, nelem, TY_REAL)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_REAL))
+ call nmiupkr (spp, spp, nelem, TY_REAL)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/nmireads.x b/sys/etc/gen/nmireads.x
new file mode 100644
index 00000000..190ce28f
--- /dev/null
+++ b/sys/etc/gen/nmireads.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_READ -- Read a block of data stored externally in NMI format.
+# Data is returned in the format of the local host machine.
+
+int procedure nmi_reads (fd, spp, maxelem)
+
+int fd #I input file
+short spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int nmipksize(), nminelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = nmipksize (maxelem, NMI_SHORT)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_SHORT) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_SHORT))
+ call nmiupks (Memc[bp], spp, nelem, TY_SHORT)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_SHORT))
+ call nmiupks (spp, spp, nelem, TY_SHORT)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/gen/nmiwriteb.x b/sys/etc/gen/nmiwriteb.x
new file mode 100644
index 00000000..9e3c19a0
--- /dev/null
+++ b/sys/etc/gen/nmiwriteb.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMIWRITE -- Write a block of data to a file in NMI format.
+# The input data is in the host system native binary format.
+
+procedure nmi_writeb (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int nmipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = nmipksize (nelem, NMI_BOOL)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call nmipakb (spp, Memc[bp], nelem, TY_BOOL)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/nmiwrited.x b/sys/etc/gen/nmiwrited.x
new file mode 100644
index 00000000..d357fe4c
--- /dev/null
+++ b/sys/etc/gen/nmiwrited.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_WRITE -- Write a block of data to a file in NMI format.
+# The input data is in the host system native binary format.
+
+procedure nmi_writed (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int nmipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = nmipksize (nelem, NMI_DOUBLE)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call nmipakd (spp, Memc[bp], nelem, TY_DOUBLE)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/nmiwritei.x b/sys/etc/gen/nmiwritei.x
new file mode 100644
index 00000000..98e33f12
--- /dev/null
+++ b/sys/etc/gen/nmiwritei.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_WRITE -- Write a block of data to a file in NMI format.
+# The input data is in the host system native binary format.
+
+procedure nmi_writei (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int nmipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = nmipksize (nelem, NMI_INT)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call nmipaki (spp, Memc[bp], nelem, TY_INT)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/nmiwritel.x b/sys/etc/gen/nmiwritel.x
new file mode 100644
index 00000000..0772b954
--- /dev/null
+++ b/sys/etc/gen/nmiwritel.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_WRITE -- Write a block of data to a file in NMI format.
+# The input data is in the host system native binary format.
+
+procedure nmi_writel (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int nmipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = nmipksize (nelem, NMI_LONG)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call nmipakl (spp, Memc[bp], nelem, TY_LONG)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/nmiwriter.x b/sys/etc/gen/nmiwriter.x
new file mode 100644
index 00000000..3f22404b
--- /dev/null
+++ b/sys/etc/gen/nmiwriter.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_WRITE -- Write a block of data to a file in NMI format.
+# The input data is in the host system native binary format.
+
+procedure nmi_writer (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int nmipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = nmipksize (nelem, NMI_REAL)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call nmipakr (spp, Memc[bp], nelem, TY_REAL)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gen/nmiwrites.x b/sys/etc/gen/nmiwrites.x
new file mode 100644
index 00000000..ed284024
--- /dev/null
+++ b/sys/etc/gen/nmiwrites.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_WRITE -- Write a block of data to a file in NMI format.
+# The input data is in the host system native binary format.
+
+procedure nmi_writes (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int nmipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = nmipksize (nelem, NMI_SHORT)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call nmipaks (spp, Memc[bp], nelem, TY_SHORT)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gethost.x b/sys/etc/gethost.x
new file mode 100644
index 00000000..029a9a0d
--- /dev/null
+++ b/sys/etc/gethost.x
@@ -0,0 +1,13 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GETHOST -- Get the network name of the host machine.
+
+procedure gethost (outstr, maxch)
+
+char outstr[maxch] # receives host name string
+int maxch
+
+begin
+ call zghost (outstr, maxch)
+ call strupk (outstr, outstr, maxch)
+end
diff --git a/sys/etc/getpid.x b/sys/etc/getpid.x
new file mode 100644
index 00000000..6665a55e
--- /dev/null
+++ b/sys/etc/getpid.x
@@ -0,0 +1,12 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GETPID -- Get the process id.
+
+int procedure getpid()
+
+int pid
+
+begin
+ call zgtpid (pid)
+ return (pid)
+end
diff --git a/sys/etc/getuid.x b/sys/etc/getuid.x
new file mode 100644
index 00000000..3da5bbba
--- /dev/null
+++ b/sys/etc/getuid.x
@@ -0,0 +1,24 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GETUID -- Get user id, i.e., return the name of the user. We do this by
+# creating a temporary file and calling fowner to get the name of the file
+# owner.
+
+procedure getuid (user_name, maxch)
+
+char user_name[ARB]
+int maxch
+pointer sp, tempfile
+int open()
+
+begin
+ call smark (sp)
+ call salloc (tempfile, SZ_FNAME, TY_CHAR)
+
+ call mktemp ("tmp$uid", Memc[tempfile], SZ_FNAME)
+ call close (open (Memc[tempfile], NEW_FILE, BINARY_FILE))
+ call fowner (Memc[tempfile], user_name, maxch)
+ call delete (Memc[tempfile])
+
+ call sfree (sp)
+end
diff --git a/sys/etc/gmtcnv.x b/sys/etc/gmtcnv.x
new file mode 100644
index 00000000..7713c9c0
--- /dev/null
+++ b/sys/etc/gmtcnv.x
@@ -0,0 +1,35 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# GMTCNV.X -- GMT (Greenwich mean time) to LST (local standard time, or clock
+# time) conversions.
+#
+# gmt = lsttogmt (lst) # lst/gmt are in seconds
+# lst = gmttolst (gmt) # lst/gmt are in seconds
+
+
+
+# GMTTOLST -- Convert a long integer value in GMT seconds to LST seconds.
+
+long procedure gmttolst (gmt)
+
+long gmt # GMT in seconds
+int gmtco
+
+begin
+ call zgmtco (gmtco)
+ return (gmt - gmtco)
+end
+
+
+
+# LSTTOGMT -- Convert a long integer value in LST seconds to GMT seconds.
+
+long procedure lsttogmt (lst)
+
+long lst # LST in seconds
+int gmtco
+
+begin
+ call zgmtco (gmtco)
+ return (lst + gmtco)
+end
diff --git a/sys/etc/gqsort.x b/sys/etc/gqsort.x
new file mode 100644
index 00000000..8092aa29
--- /dev/null
+++ b/sys/etc/gqsort.x
@@ -0,0 +1,84 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define LOGPTR 32 # log2(maxpts) (4e9)
+
+# GQSORT -- General quicksort for arbitrary objects. X is an integer array
+# indexing the array to be sorted. The user supplied COMPARE function is used
+# to compare objects indexed by X:
+#
+# -1,0,1 = compare (arg, x1, x2)
+#
+# where the value returned by COMPARE has the following significance:
+#
+# -1 obj[x1] < obj[x2]
+# 0 obj[x1] == obj[x2]
+# 1 obj[x1] > obj[x2]
+#
+# The value ARG is private to the compare routine and is merely passed on to
+# the compare routine by gqsort. This allows context data to be passed to
+# the compare routine without the need for initialization routines or commons.
+# QSORT reorders the elements of the X array, which must be of type integer.
+
+procedure gqsort (x, nelem, compare, arg)
+
+int x[ARB] #U array to be sorted
+int nelem #I number of elements in array
+extern compare() #I function to be called to compare elements
+int arg #I private data to be passed to compare func
+
+int i, j, k, lv[LOGPTR], p, pivot, uv[LOGPTR], temp
+define swap {temp=$1;$1=$2;$2=temp}
+int compare()
+
+begin
+ lv[1] = 1
+ uv[1] = nelem
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy loop to trigger the optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # subfile, to avoid quadratic behavior on an already
+ # sorted list.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (x[j], x[k])
+ pivot = x[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; compare (arg, x[i], pivot) < 0; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (compare (arg, x[j], pivot) <= 0)
+ break
+ if (i < j) # out of order pair
+ swap (x[i], x[j]) # interchange elements
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (x[i], x[j]) # interchange elements
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+
+ p = p + 1 # push onto stack
+ }
+ }
+end
diff --git a/sys/etc/intr.x b/sys/etc/intr.x
new file mode 100644
index 00000000..e30d610c
--- /dev/null
+++ b/sys/etc/intr.x
@@ -0,0 +1,54 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <xwhen.h>
+
+define LEN_SAVE 10
+
+# INTR_DISABLE, INTR_ENABLE -- Disable interrupts to protect a critical
+# section of code. The interrupt handler is saved on a stack and restored
+# when interupts are reenabled.
+
+procedure intr_disable()
+
+int sp
+int save[LEN_SAVE]
+common /zintde/ sp, save
+
+begin
+ sp = sp + 1
+ if (sp > LEN_SAVE)
+ call sys_panic (1, "interrupt save stack overflow")
+
+ call zxwhen (X_INT, X_IGNORE, save[sp])
+end
+
+
+# INTR_ENABLE -- Reenable interrupts (restore saved interrupt handler).
+
+procedure intr_enable()
+
+int junk
+int sp
+int save[LEN_SAVE]
+common /zintde/ sp, save
+
+begin
+ if (sp <= 0)
+ call sys_panic (1, "interrupt save stack underflow")
+
+ call zxwhen (X_INT, save[sp], junk)
+ sp = sp - 1
+end
+
+
+# INTR_RESET -- Clear the interrupt handler save stack.
+
+procedure intr_reset()
+
+int sp
+int save[LEN_SAVE]
+common /zintde/ sp, save
+
+begin
+ sp = 0
+end
diff --git a/sys/etc/itob.x b/sys/etc/itob.x
new file mode 100644
index 00000000..e01c6302
--- /dev/null
+++ b/sys/etc/itob.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# ITOB -- Convert integer to boolean.
+
+bool procedure itob (integer_value)
+
+int integer_value
+
+begin
+ if (integer_value == NO)
+ return (false)
+ else
+ return (true)
+end
diff --git a/sys/etc/lineoff.x b/sys/etc/lineoff.x
new file mode 100644
index 00000000..23fe9050
--- /dev/null
+++ b/sys/etc/lineoff.x
@@ -0,0 +1,113 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# LINEOFF -- Textfile line offset package. This is a simple little package
+# used to keep track of the file offsets of the lines in a text file.
+# The entry points are as follows.
+#
+# lp = lno_open (maxlines)
+# lno_close (lp)
+# lno_save (lp, line, loffset, tag)
+# OK|ERR = lno_fetch (lp, line, loffset, tag)
+#
+# The SAVE procedure is used to save line offsets in the database, and the
+# FETCH procedure is used to look up line offsets, returning ERR if the offset
+# of the line is not stored.
+
+define MIN_NLINES 64
+define LEN_LNODES 5
+define LNO_MAXLINES Memi[$1] # number of lines stored
+define LNO_SLOT Memi[$1+1] # cycles around available slots
+define LNO_LINENUMP Memi[$1+2] # pointer to array of line numbers
+define LNO_LINEOFFP Memi[$1+3] # pointer to array of line offsets
+define LNO_LINETAGP Memi[$1+4] # pointer to array of line tags
+
+# LNO_OPEN -- Open the line offset descriptor.
+
+pointer procedure lno_open (maxlines)
+
+int maxlines # max lines to store offsets for
+int nlines
+pointer lp
+errchk calloc, malloc
+
+begin
+ nlines = max (MIN_NLINES, maxlines)
+
+ call calloc (lp, LEN_LNODES, TY_STRUCT)
+ LNO_MAXLINES(lp) = nlines
+ call calloc (LNO_LINENUMP(lp), nlines, TY_LONG)
+ call malloc (LNO_LINEOFFP(lp), nlines, TY_LONG)
+ call malloc (LNO_LINETAGP(lp), nlines, TY_LONG)
+
+ return (lp)
+end
+
+
+# LNO_CLOSE -- Return the line offset descriptor.
+
+procedure lno_close (lp)
+
+pointer lp # line offset descriptor
+
+begin
+ call mfree (LNO_LINENUMP(lp), TY_LONG)
+ call mfree (LNO_LINEOFFP(lp), TY_LONG)
+ call mfree (LNO_LINETAGP(lp), TY_LONG)
+ call mfree (lp, TY_STRUCT)
+end
+
+
+# LNO_SAVE -- Save a line number/offset pair in the LNO database.
+
+procedure lno_save (lp, line, loffset, ltag)
+
+pointer lp # line offset descriptor
+int line # line number
+long loffset # line offset from NOTE
+long ltag # tag value assoc. with line
+int slot
+
+begin
+ slot = LNO_SLOT(lp) + 1
+ if (slot > LNO_MAXLINES(lp))
+ slot = 1
+ LNO_SLOT(lp) = slot
+
+ Memi[LNO_LINENUMP(lp)+slot-1] = line
+ Meml[LNO_LINEOFFP(lp)+slot-1] = loffset
+ Meml[LNO_LINETAGP(lp)+slot-1] = ltag
+end
+
+
+# LNO_FETCH -- Search the LNO database for an entry for the indicated line and
+# return its file offset if found. No assumptions are made about the ordering
+# of the data since lines could have been entered in any order. ERR is
+# returned if the line is not found in the database. A simple linear search
+# is sufficient given that the applications using this package are not expected
+# to look up a line often.
+
+int procedure lno_fetch (lp, line, loffset, ltag)
+
+pointer lp # line offset descriptor
+int line # line number to search for
+long loffset # receives line offset if entry for line is found
+long ltag # receives tag value assoc. with line
+
+int maxl, i
+pointer nump, offp, tagp
+
+begin
+ maxl = LNO_MAXLINES(lp) - 1
+ nump = LNO_LINENUMP(lp)
+ offp = LNO_LINEOFFP(lp)
+ tagp = LNO_LINETAGP(lp)
+
+ do i = 0, maxl
+ if (Memi[nump+i] == line) {
+ loffset = Meml[offp+i]
+ ltag = Meml[tagp+i]
+ return (OK)
+ }
+
+ return (ERR)
+end
diff --git a/sys/etc/locpr.x b/sys/etc/locpr.x
new file mode 100644
index 00000000..3af66d1d
--- /dev/null
+++ b/sys/etc/locpr.x
@@ -0,0 +1,14 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# LOCPR -- Return the entry point address of a procedure, suitable for input
+# to a ZCALL prcocedure to call the target procedure indirectly.
+
+int procedure locpr (proc)
+
+extern proc() # external procedure
+int epa
+
+begin
+ call zlocpr (proc, epa)
+ return (epa)
+end
diff --git a/sys/etc/locva.x b/sys/etc/locva.x
new file mode 100644
index 00000000..d3cc3bea
--- /dev/null
+++ b/sys/etc/locva.x
@@ -0,0 +1,13 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# LOCVA -- Return the address (in CHAR units) of a variable.
+
+int procedure locva (variable)
+
+int variable # data object to be addressed
+int address
+
+begin
+ call zlocva (variable, address)
+ return (address)
+end
diff --git a/sys/etc/lpopen.x b/sys/etc/lpopen.x
new file mode 100644
index 00000000..4754e779
--- /dev/null
+++ b/sys/etc/lpopen.x
@@ -0,0 +1,118 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <mach.h>
+
+define LP_INACTIVE 0
+define LP_READ 1
+define LP_WRITE 2
+define (LPCOM, common /lprcom/ lp_type, lp_nbytes)
+
+# LPOPEN -- Open the line printer device as a text or binary file. If opened
+# as a text file, we arrange for the chars to be packed upon output, but in all
+# cases the printer device appears to be a streaming binary file to FIO.
+# If the printer device is opened as a binary file, the data stream is passed
+# directly on to the device without modification. To simplify things a little
+# we permit only one printer to be open at a time; this restriction can easily
+# be removed should it prove desirable.
+
+int procedure lpopen (device, mode, type)
+
+char device[ARB]
+int mode, type
+
+int fd
+int lp_type, lp_nbytes
+bool streq()
+int fopnbf()
+extern zopnlp(), lp_zaread(), lp_zawrite(), lp_zawait(), zsttlp(), zclslp()
+LPCOM
+
+begin
+ # The TEXT device is special; it has a termcap entry and is used to
+ # format text for an ASCII textfile rather than a printer.
+
+ if (streq (device, "text"))
+ fd = STDOUT
+ else {
+ lp_type = type
+ lp_nbytes = ERR
+ fd = fopnbf (device, mode,
+ zopnlp, lp_zaread, lp_zawrite, lp_zawait, zsttlp, zclslp)
+ }
+
+ return (fd)
+end
+
+
+# LP_ZAREAD -- FIO z-aread routine for the line printer device. FIO calls
+# us with the size of the buffer in bytes. If the printer is opened as a
+# text file, we read a factor of SZB_CHAR less than that from the lowest
+# level, then unpack the data inplace in the FIO buffer.
+
+procedure lp_zaread (chan, buf, maxbytes, offset)
+
+int chan
+char buf[ARB]
+int maxbytes
+long offset # ignore, since lp is streaming device
+
+int nbytes
+int lp_type, lp_nbytes
+LPCOM
+
+begin
+ nbytes = maxbytes
+ if (lp_type == TEXT_FILE)
+ nbytes = nbytes / SZB_CHAR
+
+ call zardlp (chan, buf, nbytes, offset)
+ call zawtlp (chan, lp_nbytes)
+
+ if (lp_nbytes > 0 && lp_type == TEXT_FILE)
+ call chrupk (buf, 1, buf, 1, lp_nbytes)
+end
+
+
+# LP_ZAWRITE -- FIO z-awrite routine for the line printer device. FIO calls
+# us with the size of the buffer in bytes. If the printer is opened as a
+# text file, we first pack the data inplace in the FIO buffer, then write it
+# out to the device. It is ok to modify the data directly in the FIO buffer
+# since the device is a streaming device (no seeks).
+
+procedure lp_zawrite (chan, buf, nbytes, offset)
+
+int chan
+char buf[ARB]
+int nbytes
+long offset # ignore, since lp is streaming device
+
+int nbytes_to_write
+int lp_type, lp_nbytes
+LPCOM
+
+begin
+ nbytes_to_write = nbytes
+ if (lp_type == TEXT_FILE) {
+ nbytes_to_write = nbytes_to_write / SZB_CHAR
+ call chrpak (buf, 1, buf, 1, nbytes_to_write)
+ }
+
+ call zawrlp (chan, buf, nbytes_to_write, offset)
+ call zawtlp (chan, lp_nbytes)
+end
+
+
+# LP_ZAWAIT -- Wait for i/o to the line printer to complete. We do not bother
+# with truely asynchronous i/o for line printer devices.
+
+procedure lp_zawait (chan, nbytes)
+
+int chan
+int nbytes
+int lp_type, lp_nbytes
+LPCOM
+
+begin
+ nbytes = lp_nbytes
+end
diff --git a/sys/etc/maideh.x b/sys/etc/maideh.x
new file mode 100644
index 00000000..d0850deb
--- /dev/null
+++ b/sys/etc/maideh.x
@@ -0,0 +1,76 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <xwhen.h>
+include <syserr.h>
+include <fset.h>
+
+define SZ_ERRMSG SZ_LINE
+
+# MA_IDEH -- Iraf Main routine which installs the default exception handler.
+# A single handler processes all exceptions.
+
+procedure ma_ideh()
+
+extern xstdexh()
+int junk, i, epa_standard_handler
+int exception[4]
+data exception /X_ACV, X_INT, X_ARITH, X_IPC/
+
+begin
+ call zlocpr (xstdexh, epa_standard_handler)
+ do i = 1, 4
+ call xwhen (exception[i], epa_standard_handler, junk)
+
+ # Initialize the critical section protection stack.
+ call intr_reset()
+end
+
+
+# XSTDEXH -- Standard exception handler. Unless the user code posts a handler
+# for a particular exception, this handler will gain control.
+
+procedure xstdexh (exception, next_handler)
+
+int exception # code for exception
+int next_handler # EPA of next handler to be called
+
+char os_errmsg[SZ_ERRMSG]
+int os_errcode
+
+begin
+ # Get OS description of the exception.
+ call zxgmes (os_errcode, os_errmsg, SZ_ERRMSG)
+ call strupk (os_errmsg, os_errmsg, SZ_ERRMSG)
+
+ # Cancel any output and resync awaits.
+ call fseti (STDOUT, F_CANCEL, OK)
+ call fseti (CLOUT, F_CANCEL, OK)
+ call fseti (CLIN, F_CANCEL, OK)
+
+ # Set this here as error() will return immediately if it comes back.
+ next_handler = X_IGNORE
+
+ # Take error action.
+ switch (exception) {
+ case X_ACV:
+ if (os_errcode > 0)
+ call fatal (SYS_XACV, os_errmsg)
+ else
+ call fatal (SYS_XACV, "Access violation")
+ case X_ARITH:
+ if (os_errcode > 0)
+ call fatal (SYS_XARITH, os_errmsg)
+ else
+ call fatal (SYS_XARITH, "Arithmetic exception")
+ case X_INT:
+ if (os_errcode > 0)
+ call fatal (SYS_XINT, os_errmsg)
+ else
+ call fatal (SYS_XINT, "Keyboard interrupt")
+ case X_IPC:
+ call fatal (SYS_XIPC, "Write to IPC with no reader")
+
+ default:
+ call fatal (ERR, "Unknown exception")
+ }
+end
diff --git a/sys/etc/main.x b/sys/etc/main.x
new file mode 100644
index 00000000..db6937bc
--- /dev/null
+++ b/sys/etc/main.x
@@ -0,0 +1,908 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <error.h>
+include <syserr.h>
+include <clset.h>
+include <fset.h>
+include <ctype.h>
+include <printf.h>
+include <xwhen.h>
+include <knet.h>
+
+.help iraf_main
+.nf __________________________________________________________________________
+The IRAF MAIN
+
+ Task resident interpreter for interface to CL. Supervises process startup
+and shutdown, error restart, and task execution. A process may contain any
+number of tasks, which need not be related. The iraf main allows a process to
+be run either directly (interactively or in batch) or from the CL. A brief
+description of the operation of the Main is given here; additional documentation
+is given in the System Interface Reference Manual.
+
+
+EXECUTION
+
+[1] The process containing the IRAF Main is run. The PROCESS MAIN, a machine
+ dependent code segment, gains control initially. The process main
+ determines whether the task is being run from as a connected subprocess,
+ as a detached process, or as a host process, and opens the process
+ standard i/o channels. The process main then calls the IRAF Main, i.e., us.
+
+[2] The IRAF Main performs the initialization associated with process startup
+ and then enters the interpreter loop waiting for a command. A number of
+ special commands are implemented, e.g.:
+
+ ? print menu
+ bye shutdown process
+ chdir change directory
+ set set environment variable or variables
+
+ Any other command is assumed to be the name of a task. The syntax of a
+ task invocation statement is as follows:
+
+ [$]task [<[fname]], ([[stream[(T|B)]]>[fname]])|([[stream]>>[fname]])
+
+ Everything but the task name is optional. A leading $ enables printing of
+ the cpu time and clock time consumed by the process at termination. Any
+ combination of the standard i/o streams may be redirected on the command
+ line into a file. If the stream is redirected at the CL level redirection
+ is shown on the command line but the filename is omitted.
+
+[3] The communications protocol during task execution varies depending on
+ whether or not we are talking to the CL. If talking directly to the user,
+ the interpreter generates a prompt, and the standard input and output is
+ not blocked into XMIT and XFER commands. Interactive parameter requests
+ have the form "paramname: response" while CL/IPC requests have the form
+ "paramname=\nresponse", where "response" is the value entered by the user.
+
+[4] Task termination is indicated in interactive mode by generation of a prompt
+ for the next command and in CL/IPC mode by transmission of the command
+ "bye" to the parent process. If a task terminates abnormally the command
+ "error" is sent to the parent process or the terminal, and the Main reenters
+ the interpreter loop.
+
+A unique SYS_RUNTASK procedure is generated for each process at compile time by
+performing string substitution on a TASK statement appearing in the source code.
+The SYS_RUNTASK procedure contains the task dictionary, CALL statements for
+each task, plus the special task "?". The main itself, i.e. this file, is a
+precompiled library procedure which has no direct knowledge of the commands
+to be run.
+
+
+ERROR RECOVERY
+
+ If a task terminates abnormally two things can happen: [1] a panic abort
+occurs, causing immediate shutdown of the process (rare), or [2] the IRAF Main
+is reentered at the ZSVJMP statement by a corresponding call to ZDOJMP from
+elsewhere in the system, e.g., ERRACT in the error handling code.
+
+Error restart consists of the following steps:
+
+ (1) The IRAF main is reentered at the point just after the ZDOJMP statement,
+ with a nonzero error code identifying the error in STATUS.
+ (2) The main performs error recovery, cleaning up the files system (deleting
+ NEW_FILES and TEMP_FILES), clearing the stack, and calling any
+ procedures posted with ONERROR. At present the error recovery code does
+ not free heap buffers or clear posted exception handlers.
+ (3) The ERROR statement is sent to the CL. An example of the
+ error statment is "ERROR (501, "Access Violation")".
+ (4) The main reenters the interpreter loop awaiting the next command from
+ the CL.
+
+Any error occuring during error restart is fatal and results in immediate
+process termination, usually with a panic error message. This is necessary
+to prevent infinite error recursion.
+
+
+SHUTDOWN
+
+ The process does not shutdown when interrupted by the CL or during error
+recovery, unless a panic occurs. In normal operation shutdown only occurs when
+the command BYE is received from the parennt process, or when EOF is read from
+the process standard input. Procedures posted during execution with ONEXIT
+will be called during process shutdown. Any error occuring while executing
+an ONEXIT procedure is fatal and will result in a panic abort of the process.
+.endhelp _____________________________________________________________________
+
+define SZ_VALSTR SZ_COMMAND
+define SZ_CMDBUF (SZ_COMMAND+1024)
+define SZ_TASKNAME 32
+define TIMEIT_CHAR '$'
+define MAXFD 5 # max redirectable fd's
+define STARTUP 0 # stages of execution
+define SHUTDOWN 1
+define IDLE 2
+define EXECUTING 3
+define DUMMY finit # any procedure will do
+
+
+# IRAF_MAIN -- Execute commands read from the standard input until the special
+# command "bye" is received, initiating process shutdown. The arguments tell
+# the process type (connected, detached, or host) and identify the process
+# standard i/o channels and device driver to be used.
+
+int procedure iraf_main (a_cmd, a_inchan, a_outchan, a_errchan,
+ a_driver, a_devtype, prtype, bkgfile, jobcode, sys_runtask, onentry)
+
+char a_cmd[ARB] # command to be executed or null string
+int a_inchan # process standard input
+int a_outchan # process standard output
+int a_errchan # process standard error output
+int a_driver # ZLOCPR address of device driver
+int a_devtype # device type (text or binary)
+int prtype # process type (connected, detached, host)
+char bkgfile[ARB] # packed filename of bkg file if detached
+int jobcode # jobcode if detached process
+extern sys_runtask() # client task execution procedure
+extern onentry() # client onentry procedure
+
+bool networking
+int inchan, outchan, errchan, driver, devtype
+char cmd[SZ_CMDBUF], taskname[SZ_TASKNAME], bkgfname[SZ_FNAME]
+char irafinit[SZ_LINE]
+int arglist_offset, timeit, junk, interactive, builtin_task, cmdin
+int jumpbuf[LEN_JUMPBUF], status, errstat, state, interpret, i
+long save_time[2]
+pointer sp
+
+bool streq()
+extern DUMMY()
+int sys_getcommand(), sys_runtask(), oscmd()
+int access(), envscan(), onentry(), stropen(), envgets()
+errchk xonerror, fio_cleanup
+common /JUMPCOM/ jumpbuf
+string nullfile "dev$null"
+data networking /KNET/
+define shutdown_ 91
+
+# The following common is required on VMS systems to defeat the Fortran
+# optimizer, which would otherwise produce optimizations that would cause
+# a future return from ZSVJMP to fail. Beware that this trick may fail on
+# other systems with clever optimizers.
+
+common /zzfakecom/ state
+
+begin
+ # The following initialization code is executed upon process
+ # startup only.
+
+ errstat = OK
+ state = STARTUP
+ call mio_init()
+ call zsvjmp (jumpbuf, status)
+ if (status != OK)
+ call sys_panic (EA_FATAL, "fatal error during process startup")
+
+ # Install the standard exception handlers, but if we are a connected
+ # subprocess do not enable interrupts until process startup has
+ # completed.
+
+ call ma_ideh()
+ if (prtype == PR_CONNECTED)
+ call intr_disable()
+
+ inchan = a_inchan
+ outchan = a_outchan
+ errchan = a_errchan
+ driver = a_driver
+ devtype = a_devtype
+
+ # If the system is configured with networking initialize the network
+ # interface and convert the input channel codes and device driver
+ # code to their network equivalents.
+
+ if (networking)
+ call ki_init (inchan, outchan, errchan, driver, devtype)
+
+ # Other initializations.
+ call env_init()
+ call fmt_init (FMT_INITIALIZE) # init printf
+ call xer_reset() # init error checking
+ call erract (OK) # init error handling
+ call onerror (DUMMY) # init onerror
+ call onexit (DUMMY) # init onexit
+ call finit() # initialize FIO
+ call clopen (inchan, outchan, errchan, driver, devtype)
+ call clseti (CL_PRTYPE, prtype)
+ call clc_init() # init param cache
+ call strupk (bkgfile, bkgfname, SZ_FNAME)
+
+ # If we are running as a host process (no IRAF parent process) look
+ # for the file "zzsetenv.def" in the current directory and then in
+ # the system library, and initialize the environment from this file
+ # if found. This works because the variable "iraf$" is defined at
+ # the ZGTENV level.
+
+ interactive = NO
+ if (prtype == PR_HOST) {
+ interactive = YES
+ if (access ("zzsetenv.def",0,0) == YES) {
+ iferr (junk = envscan ("set @zzsetenv.def"))
+ ;
+ } else if (access ("hlib$zzsetenv.def",0,0) == YES) {
+ iferr (junk = envscan ("set @hlib$zzsetenv.def"))
+ ;
+ } else if (access ("host$hlib/zzsetenv.def",0,0) == YES) {
+ iferr (junk = envscan ("set @host$hlib/zzsetenv.def"))
+ ;
+ }
+
+ # Allow the 'irafinit' environment variable to point to a file
+ # that may partially override the system zzsetenv.def file.
+ if (envgets ("irafinit", irafinit, SZ_LINE) > 0) {
+ if (access (irafinit, 0, 0) == YES) {
+ call sprintf (cmd, SZ_CMDBUF, "set @%s")
+ call pargstr (cmd)
+ iferr (junk = envscan (cmd))
+ ;
+ }
+ }
+ }
+
+ # Save context for error restart. If an error occurs execution
+ # resumes just past the ZSVJMP statement with a nonzero status.
+
+ call smark (sp)
+ call zsvjmp (jumpbuf, status)
+
+ if (status != OK) {
+ errstat = status
+
+ # Give up if error occurs during shutdown.
+ if (state == SHUTDOWN)
+ call sys_panic (errstat, "fatal error during process shutdown")
+
+ # Tell error handling package that an error restart is in
+ # progress (necessary to avoid recursion).
+
+ call erract (EA_RESTART)
+
+ iferr {
+ # Call user cleanup routines and then clean up files system.
+ # Make sure that user cleanup routines are called FIRST.
+
+ call xonerror (status)
+ call ma_ideh()
+ call flush (STDERR)
+ do i = CLIN, STDPLOT
+ call fseti (i, F_CANCEL, OK)
+ call fio_cleanup (status)
+ call fmt_init (FMT_INITIALIZE)
+ call sfree (sp)
+ } then
+ call erract (EA_FATAL) # panic abort
+
+ # Send ERROR statement to the CL, telling the CL that the task
+ # has terminated abnormally. The CL will either kill us, resulting
+ # in error restart with status=SYS_XINT, or send us another command
+ # to execute. If we are connected but idle, do not send the ERROR
+ # statement because the CL will not read it until it executes the
+ # next task (which it will then mistakenly think has aborted).
+
+ if (!(prtype == PR_CONNECTED && state == IDLE))
+ call xer_send_error_statement_to_cl (status)
+
+ # Inform error handling code that error restart has completed,
+ # or next error call will result in a panic shutdown.
+
+ call erract (OK)
+ call xer_reset ()
+ status = OK
+ }
+
+ # During process startup and shutdown the parent is not listening to
+ # us, hence we dump STDOUT and STDERR into the null file. If this is
+ # not done and we write to CLOUT, deadlock may occur. During startup
+ # we also call the ONENTRY procedure. This is a no-op for connected
+ # and host subprocesses unless a special procedure is linked by the
+ # user (for detached processes the standard ONENTRY procedure opens
+ # the bkgfile as CLIN). The return value of ONENTRY determines whether
+ # the interpreter loop is entered. Note that ONENTRY permits complete
+ # bypass of the standard interpreter loop by an application (e.g. the
+ # IRAF CL).
+
+ if (state == STARTUP) {
+ # Redirect stderr and stdout to the null file.
+ if (prtype == PR_CONNECTED) {
+ call fredir (STDOUT, nullfile, WRITE_ONLY, TEXT_FILE)
+ call fredir (STDERR, nullfile, WRITE_ONLY, TEXT_FILE)
+ }
+
+ # Call the custom or default ONENTRY procedure. The lowest bit
+ # of the return value contains the PR_EXIT/PR_NOEXIT flag, higher
+ # bits may contain a more meaningful 7-bit status code which will
+ # be returned to the shell.
+
+ i = onentry (prtype, bkgfname, a_cmd)
+ if (mod(i, 2) == PR_EXIT) {
+ interpret = NO
+ errstat = i / 2
+ goto shutdown_
+ } else
+ interpret = YES
+
+ # Open the command input stream. If a command string was given on
+ # the command line then we read commands from that, otherwise we
+ # take commands from CLIN.
+
+ for (i=1; IS_WHITE(a_cmd[i]) || a_cmd[i] == '\n'; i=i+1)
+ ;
+ if (a_cmd[i] != EOS) {
+ cmdin = stropen (a_cmd, ARB, READ_ONLY)
+ call fseti (cmdin, F_KEEP, YES)
+ interpret = NO
+ interactive = NO
+ } else
+ cmdin = CLIN
+ }
+
+ # Interpreter loop of the IRAF Main. Execute named tasks until the
+ # command "bye" is received, or EOF is read on the process standard
+ # input (CLIN). Prompts and other perturbations in the CL/IPC protocol
+ # are generated if we are being run directly as a host process.
+
+ while (sys_getcommand (cmdin, cmd, taskname, arglist_offset,
+ timeit, prtype) != EOF) {
+
+ builtin_task = NO
+ if (streq (taskname, "bye")) {
+ # Initiate process shutdown.
+ break
+ } else if (streq (taskname, "set") || streq (taskname, "reset")) {
+ builtin_task = YES
+ } else if (streq (taskname, "cd") || streq (taskname, "chdir")) {
+ builtin_task = YES
+ } else if (prtype == PR_CONNECTED && streq (taskname, "_go_")) {
+ # Restore the normal standard output streams, following
+ # completion of process startup. Reenable interrupts.
+ call close (STDOUT)
+ call close (STDERR)
+ call intr_enable()
+ state = IDLE
+ next
+ } else if (taskname[1] == '!') {
+ # Send a command to the host system.
+ junk = oscmd (cmd[arglist_offset], "", "", "")
+ next
+ } else
+ state = EXECUTING
+
+ if (builtin_task == NO) {
+ if (timeit == YES)
+ call sys_mtime (save_time)
+
+ # Clear the parameter cache.
+ call clc_init()
+
+ # Set the name of the root pset.
+ call clc_newtask (taskname)
+
+ # Process the argument list, consisting of any mixture of
+ # parameter=value directives and i/o redirection directives.
+
+ call sys_scanarglist (cmdin, cmd[arglist_offset])
+ }
+
+ # Call sys_runtask (the code for which was generated automatically
+ # by the preprocessor in place of the TASK statement) to search
+ # the dictionary and run the named task.
+
+ errstat = OK
+ call mem_init (taskname)
+ if (sys_runtask (taskname,cmd,arglist_offset,interactive) == ERR) {
+ call flush (STDOUT)
+ call sprintf (cmd, SZ_CMDBUF,
+ "ERROR (0, \"Iraf Main: Unknown task name (%s)\")\n")
+ call pargstr (taskname)
+ call putline (CLOUT, cmd)
+ call flush (CLOUT)
+ state = IDLE
+ next
+ }
+ call mem_fini (taskname)
+
+ # Cleanup after successful termination of command. Flush the
+ # standard output, cancel any unread standard input so the next
+ # task won't try to read it, print elapsed time if enabled,
+ # check for an incorrect error handler, call any user posted
+ # termination procedures, close open files, close any redirected
+ # i/o and restore the normal standard i/o streams.
+
+ if (builtin_task == NO) {
+
+ call flush (STDOUT)
+ call fseti (STDIN, F_CANCEL, OK)
+
+ if (timeit == YES)
+ call sys_ptime (STDERR, taskname, save_time)
+
+ call xer_verify()
+ call xonerror (OK)
+ call fio_cleanup (OK)
+
+ if (prtype == PR_CONNECTED) {
+ call putline (CLOUT, "bye\n")
+ call flush (CLOUT)
+ }
+ if (state != STARTUP)
+ state = IDLE
+ }
+ }
+
+ # The interpreter has exited after receipt of "bye" or EOF. Redirect
+ # stdout and stderr to the null file (since the parent is no longer
+ # listening to us), call the user exit procedures if any, and exit.
+
+shutdown_
+ state = SHUTDOWN
+ if (prtype == PR_CONNECTED) {
+ call fredir (STDOUT, nullfile, WRITE_ONLY, TEXT_FILE)
+ call fredir (STDERR, nullfile, WRITE_ONLY, TEXT_FILE)
+ } else if (prtype == PR_HOST && cmd[1] == EOS && interpret == YES) {
+ call putci (CLOUT, '\n')
+ call flush (CLOUT)
+ }
+
+ call xonexit (OK)
+ call fio_cleanup (OK)
+ call clclose()
+
+ return (errstat)
+end
+
+
+# SYS_GETCOMMAND -- Get the next command from the input file. Ignore blank
+# lines and comment lines. Parse the command and return the components as
+# output arguments. EOF is returned as the function value when eof file is
+# reached on the input file.
+
+int procedure sys_getcommand (fd, cmd, taskname, arglist_offset, timeit, prtype)
+
+int fd #I command input file
+char cmd[SZ_CMDBUF] #O command line
+char taskname[SZ_TASKNAME] #O extracted taskname, lower case
+int arglist_offset #O offset into CMD of first argument
+int timeit #O if YES, time the command
+int prtype #I process type code
+
+int ip, op
+int getlline(), stridx()
+
+begin
+ repeat {
+ # Get command line. Issue prompt first if process is being run
+ # interactively.
+
+ if (prtype == PR_HOST && fd == CLIN) {
+ call putline (CLOUT, "> ")
+ call flush (CLOUT)
+ }
+ if (getlline (fd, cmd, SZ_CMDBUF) == EOF)
+ return (EOF)
+
+ # Check for timeit character and advance to first character of
+ # the task name.
+
+ timeit = NO
+ for (ip=1; cmd[ip] != EOS; ip=ip+1) {
+ if (cmd[ip] == TIMEIT_CHAR && timeit == NO)
+ timeit = YES
+ else if (!IS_WHITE (cmd[ip]))
+ break
+ }
+
+ # Skip blank lines and comment lines.
+ switch (cmd[ip]) {
+ case '#', '\n', EOS:
+ next
+ case '?', '!':
+ taskname[1] = cmd[ip]
+ taskname[2] = EOS
+ arglist_offset = ip + 1
+ return (OK)
+ }
+
+ # Extract task name.
+ op = 1
+ while (IS_ALNUM (cmd[ip]) || stridx (cmd[ip], "_.$") > 0) {
+ taskname[op] = cmd[ip]
+ ip = ip + 1
+ op = min (SZ_TASKNAME + 1, op + 1)
+ }
+ taskname[op] = EOS
+
+ # Determine index of argument list.
+ while (IS_WHITE (cmd[ip]))
+ ip = ip + 1
+ arglist_offset = ip
+
+ # Get rid of the newline.
+ for (; cmd[ip] != EOS; ip=ip+1)
+ if (cmd[ip] == '\n') {
+ cmd[ip] = EOS
+ break
+ }
+
+ return (OK)
+ }
+end
+
+
+# SYS_SCANARGLIST -- Parse the argument list of a task. At the level of the
+# iraf main the command syntax is very simple. There are two types of
+# arguments, parameter assignments (including switches) and i/o redirection
+# directives. All param assignments are of the form "param=value", where
+# PARAM must start with a lower case alpha and where VALUE is either quoted or
+# is delimited by one of the metacharacters [ \t\n<>\\]. A redirection argument
+# is anything which is not a parameter set argument, i.e., any argument which
+# does not start with a lower case alpha.
+
+procedure sys_scanarglist (cmdin, i_args)
+
+int cmdin # command input stream
+char i_args[ARB] # (first part of) argument list
+
+int fd
+char ch
+bool skip
+pointer sp, fname, args, ip, op
+int getlline()
+
+begin
+ call smark (sp)
+ call salloc (args, SZ_CMDBUF, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+
+ call strcpy (i_args, Memc[args], SZ_CMDBUF)
+
+ # Do not skip whitespace for param=value args on the command line.
+ skip = false
+
+ # Inform FIO that all standard i/o streams are unredirected (overridden
+ # below if redirected by an argument).
+
+ for (fd=1; fd < FIRST_FD; fd=fd+1)
+ call fseti (fd, F_REDIR, NO)
+
+ # Process each argument in the argument list. If the command line ends
+ # with an escaped newline then continuation is assumed. Arguments are
+ # delimited by whitespace.
+
+ for (ip=args; Memc[ip] != '\n' && Memc[ip] != EOS; ) {
+ # Advance to the next argument.
+ while (IS_WHITE (Memc[ip]))
+ ip = ip + 1
+
+ # Check for continuation.
+ ch = Memc[ip]
+ if (ch == '\\' && (Memc[ip+1] == '\n' || Memc[ip+1] == EOS)) {
+ if (getlline (cmdin, Memc[args], SZ_CMDBUF) == EOF) {
+ call sfree (sp)
+ return
+ }
+ ip = args
+ next
+ } else if (ch == '\n' || ch == EOS)
+ break
+
+ # If the argument begins with an alpha, _, or $ (e.g., $nargs)
+ # then it is a param=value argument, otherwise it must be a redir.
+ # The form @filename causes param=value pairs to be read from
+ # the named file.
+
+ if (ch == '@') {
+ op = fname
+ for (ip=ip+1; Memc[ip] != EOS; ip=ip+1)
+ if (IS_WHITE (Memc[ip]) || Memc[ip] == '\n')
+ break
+ else if (Memc[ip] == '\\' && Memc[ip+1] == '\n')
+ break
+ else {
+ Memc[op] = Memc[ip]
+ op = op + 1
+ }
+ Memc[op] = EOS
+ call sys_getpars (Memc[fname])
+
+ } else if (IS_ALPHA(ch) || ch == '_' || ch == '$') {
+ call sys_paramset (Memc, ip, skip)
+ } else
+ call sys_redirect (Memc, ip)
+ }
+
+ call sfree (sp)
+end
+
+
+# SYS_GETPARS -- Read a sequence of param=value parameter assignments from
+# the named file and enter them into the CLIO cache for the task.
+
+procedure sys_getpars (fname)
+
+char fname # pset file
+
+bool skip
+int lineno, fd
+pointer sp, lbuf, ip
+int open(), getlline()
+errchk open, getlline
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_CMDBUF, TY_CHAR)
+
+ fd = open (fname, READ_ONLY, TEXT_FILE)
+
+ # Skip whitespace for param = value args in a par file.
+ skip = true
+
+ lineno = 0
+ while (getlline (fd, Memc[lbuf], SZ_CMDBUF) != EOF) {
+ lineno = lineno + 1
+ for (ip=lbuf; IS_WHITE (Memc[ip]); ip=ip+1)
+ ;
+ if (Memc[ip] == '#' || Memc[ip] == '\n')
+ next
+ iferr (call sys_paramset (Memc, ip, skip)) {
+ for (; Memc[ip] != EOS && Memc[ip] != '\n'; ip=ip+1)
+ ;
+ Memc[ip] = EOS
+ call eprintf ("Bad param assignment, line %d: `%s'\n")
+ call pargi (lineno)
+ call pargstr (Memc[lbuf])
+ }
+ }
+
+ call close (fd)
+ call sfree (sp)
+end
+
+
+# SYS_PARAMSET -- Extract the param and value substrings from a param=value
+# or switch argument and enter them into the CL parameter cache. (see also
+# clio.clcache).
+
+procedure sys_paramset (args, ip, skip)
+
+char args[ARB] # argument list
+int ip # pointer to first char of argument
+bool skip # skip whitespace within "param=value" args
+
+pointer sp, param, value, op
+int stridx()
+
+begin
+ call smark (sp)
+ call salloc (param, SZ_FNAME, TY_CHAR)
+ call salloc (value, SZ_VALSTR, TY_CHAR)
+
+ # Extract the param field.
+ op = param
+ while (IS_ALNUM (args[ip]) || stridx (args[ip], "_.$") > 0) {
+ Memc[op] = args[ip]
+ op = op + 1
+ ip = ip + 1
+ }
+ Memc[op] = EOS
+
+ # Advance to the switch character or assignment operator.
+ while (IS_WHITE (args[ip]))
+ ip = ip + 1
+
+ switch (args[ip]) {
+ case '+':
+ # Boolean switch "yes".
+ ip = ip + 1
+ call strcpy ("yes", Memc[value], SZ_VALSTR)
+
+ case '-':
+ # Boolean switch "no".
+ ip = ip + 1
+ call strcpy ("no", Memc[value], SZ_VALSTR)
+
+ case '=':
+ # Extract the value field. This is either a quoted string or a
+ # string delimited by any of the metacharacters listed below.
+
+ ip = ip + 1
+ if (skip) {
+ while (IS_WHITE (args[ip]))
+ ip = ip + 1
+ }
+ call sys_gstrarg (args, ip, Memc[value], SZ_VALSTR)
+
+ default:
+ call error (1, "IRAF Main: command syntax error")
+ }
+
+ # Enter the param=value pair into the CL parameter cache.
+ call clc_enter (Memc[param], Memc[value])
+
+ call sfree (sp)
+end
+
+
+# SYS_REDIRECT -- Process a single redirection argument. The syntax of an
+# argument to redirect the standard input is
+#
+# < [fname]
+#
+# If the filename is omitted it is understood that STDIN has been redirected
+# in the CL. The syntax to redirect a standard output stream is
+#
+# [45678][TB](>|>>)[fname]
+#
+# where [4567] is the FD number of a standard output stream (STDOUT, STDERR,
+# STDGRAPH, STDIMAGE, or STDPLOT), and [TB] indicates if the file is text or
+# binary. If the stream is redirected at the CL level the output filename
+# will be given as `$', serving only to indicate that the stream is redirected.
+
+procedure sys_redirect (args, ip)
+
+char args[ARB] # argument list
+int ip # pointer to first char of redir arg
+
+pointer sp, fname
+int fd, mode, type
+int ctoi()
+define badredir_ 91
+errchk fredir, fseti
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+
+ # Get number of stream (0 if not given).
+ if (ctoi (args, ip, fd) <= 0)
+ fd = 0
+
+ # Get file type (optional).
+ while (IS_WHITE (args[ip]))
+ ip = ip + 1
+
+ switch (args[ip]) {
+ case 'T', 't':
+ type = TEXT_FILE
+ ip = ip + 1
+ case 'B', 'b':
+ type = BINARY_FILE
+ ip = ip + 1
+ default:
+ type = 0
+ }
+
+ # Check for "<", ">", or ">>".
+ while (IS_WHITE (args[ip]))
+ ip = ip + 1
+
+ switch (args[ip]) {
+ case '<':
+ ip = ip + 1
+ mode = READ_ONLY
+ if (fd == 0)
+ fd = STDIN
+ else if (fd != STDIN || fd != CLIN)
+ goto badredir_
+
+ case '>':
+ ip = ip + 1
+ if (args[ip] == '>') {
+ ip = ip + 1
+ mode = APPEND
+ } else
+ mode = NEW_FILE
+
+ if (fd == 0)
+ fd = STDOUT
+ else {
+ switch (fd) {
+ case CLOUT, STDOUT, STDERR, STDGRAPH, STDIMAGE, STDPLOT:
+ ;
+ default:
+ goto badredir_
+ }
+ }
+
+ default:
+ # Not a redirection argument.
+ call error (1, "IRAF Main: command syntax error")
+ }
+
+ # Set default file type for given stream if no type specified.
+ if (type == 0)
+ switch (fd) {
+ case CLIN, CLOUT, STDIN, STDOUT, STDERR:
+ type = TEXT_FILE
+ default:
+ type = BINARY_FILE
+ }
+
+ # Extract the filename, if any. If the CL has redirected the output
+ # and is merely using the redirection syntax to inform us of this,
+ # the metafilename "$" is given.
+
+ while (IS_WHITE (args[ip]))
+ ip = ip + 1
+
+ if (args[ip] == '$') {
+ Memc[fname] = EOS
+ ip = ip + 1
+ } else
+ call sys_gstrarg (args, ip, Memc[fname], SZ_FNAME)
+
+ # At this point we have FD, FNAME, MODE and TYPE. If no file is
+ # named the stream has already been redirected by the parent and
+ # all we need to is inform FIO that the stream has been redirected.
+ # Otherwise we redirect the stream in the local process. A locally
+ # redirected stream will be closed and the normal direction restored
+ # during FIO cleanup, at program termination or during error
+ # recovery.
+
+ if (Memc[fname] != EOS)
+ call fredir (fd, Memc[fname], mode, type)
+ else
+ call fseti (fd, F_REDIR, YES)
+
+ call sfree (sp)
+ return
+
+badredir_
+ call error (2, "IRAF Main: illegal redirection")
+end
+
+
+# SYS_GSTRARG -- Extract a string field. This is either a quoted string or a
+# string delimited by any of the metacharacters " \t\n<>\\".
+
+procedure sys_gstrarg (args, ip, outstr, maxch)
+
+char args[ARB] # input string
+int ip # pointer into input string
+char outstr[maxch] # receives string field
+int maxch
+
+char delim, ch
+int op
+int stridx()
+
+begin
+ op = 1
+ if (args[ip] == '"' || args[ip] == '\'') {
+ # Quoted value string.
+
+ delim = args[ip]
+ for (ip=ip+1; args[ip] != delim && args[ip] != EOS; ip=ip+1) {
+ if (args[ip] == '\n') {
+ break
+ } else if (args[ip] == '\\' && args[ip+1] == delim) {
+ outstr[op] = delim
+ op = op + 1
+ ip = ip + 1
+ } else {
+ outstr[op] = args[ip]
+ op = op + 1
+ }
+ }
+
+ } else {
+ # Nonquoted value string.
+
+ for (delim=-1; args[ip] != EOS; ip=ip+1) {
+ ch = args[ip]
+ if (ch == '\\' && (args[ip+1] == '\n' || args[ip+1] == EOS))
+ break
+ else if (stridx (ch, " \t\n<>\\") > 0)
+ break
+ else {
+ outstr[op] = ch
+ op = op + 1
+ }
+ }
+ }
+
+ outstr[op] = EOS
+ if (args[ip] == delim)
+ ip = ip + 1
+end
diff --git a/sys/etc/miiread.gx b/sys/etc/miiread.gx
new file mode 100644
index 00000000..a3efff2d
--- /dev/null
+++ b/sys/etc/miiread.gx
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIREAD -- Read a block of data stored externally in MII format.
+# Data is returned in the format of the local host machine.
+
+int procedure mii_read$t (fd, spp, maxelem)
+
+int fd #I input file
+PIXEL spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int miipksize(), miinelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = miipksize (maxelem, MII_PIXEL)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_PIXEL) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_PIXEL))
+ call miiupk$t (Memc[bp], spp, nelem, TY_PIXEL)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, miinelem (nchars, MII_PIXEL))
+ call miiupk$t (spp, spp, nelem, TY_PIXEL)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/miireadc.x b/sys/etc/miireadc.x
new file mode 100644
index 00000000..9354307c
--- /dev/null
+++ b/sys/etc/miireadc.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIREADC -- Read a block of character data stored externally in MII format.
+# Data is returned in the machine independent character format.
+
+int procedure mii_readc (fd, spp, maxchars)
+
+int fd # input file
+int spp[ARB] # receives data
+int maxchars # max number of chars to be read
+
+pointer sp, bp
+int pksize, nchars
+int miipksize(), miinelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = miipksize (maxchars, MII_BYTE)
+ nchars = max (maxchars, pksize)
+
+ if (nchars > maxchars) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, nchars, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nchars = min (maxchars, miinelem (nchars, MII_BYTE))
+ call miiupk8 (Memc[bp], spp, nchars, TY_CHAR)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nchars = min (maxchars, miinelem (nchars, MII_BYTE))
+ call miiupk8 (spp, spp, nchars, TY_CHAR)
+ }
+ }
+
+ return (nchars)
+end
diff --git a/sys/etc/miiwrite.gx b/sys/etc/miiwrite.gx
new file mode 100644
index 00000000..0bfce225
--- /dev/null
+++ b/sys/etc/miiwrite.gx
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIWRITE -- Write a block of data to a file in MII format.
+# The input data is in the host system native binary format.
+
+procedure mii_write$t (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int miipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = miipksize (nelem, MII_PIXEL)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call miipak$t (spp, Memc[bp], nelem, TY_PIXEL)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/miiwritec.x b/sys/etc/miiwritec.x
new file mode 100644
index 00000000..bdc20818
--- /dev/null
+++ b/sys/etc/miiwritec.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mii.h>
+
+# MIIWRITEC -- Write a block of character data to a file in MII format.
+# The input data is assumed to be in a machine independent format.
+
+procedure mii_writec (fd, spp, nchars)
+
+int fd # output file
+int spp[ARB] # data to be written
+int nchars # number of chars units to be written
+
+pointer sp, bp
+int bufsize
+int miipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = miipksize (nchars, MII_BYTE)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call miipak8 (spp, Memc[bp], nchars, TY_CHAR)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/mkpkg b/sys/etc/mkpkg
new file mode 100644
index 00000000..578c290b
--- /dev/null
+++ b/sys/etc/mkpkg
@@ -0,0 +1,125 @@
+# Make the ETC portion of the system library libsys.a.
+
+$checkout libsys.a lib$
+$update libsys.a
+$checkin libsys.a lib$
+$exit
+
+generic:
+ $ifolder (gen/miireadi.x, miiread.gx)
+ $generic -k -p gen/ -t silrd miiread.gx
+ $endif
+ $ifolder (gen/miiwritei.x, miiwrite.gx)
+ $generic -k -p gen/ -t silrd miiwrite.gx
+ $endif
+ $ifolder (gen/nmireadi.x, nmiread.gx)
+ $generic -k -p gen/ -t silrd nmiread.gx
+ $endif
+ $ifolder (gen/nmiwritei.x, nmiwrite.gx)
+ $generic -k -p gen/ -t silrd nmiwrite.gx
+ $endif
+ ;
+
+libsys.a:
+ $ifeq (USE_GENERIC, yes) $call generic $endif
+ @gen
+
+ brktime.x <time.h>
+ btoi.x
+ clktime.x
+ cnvdate.x <time.h>
+ cnvtime.x <time.h>
+ cputime.x
+ dtmcnv.x <time.h> <ctype.h>
+ envgetb.x
+ envgetd.x <mach.h>
+ envgeti.x <mach.h>
+ envgetr.x
+ envgets.x environ.h <fset.h> <knet.h>
+ envindir.x
+ envinit.x environ.com environ.h
+ environ.x environ.com environ.h <knet.h>
+ envlist.x environ.com environ.h
+ envnext.x environ.com environ.h
+ envreset.x environ.com environ.h <knet.h>
+ envscan.x environ.h <ctype.h>
+ erract.x error.com <config.h> <error.h>
+ errcode.x error.com <error.h>
+ errget.x error.com <error.h>
+ error.x error.com <error.h>
+ gethost.x
+ getpid.x
+ getuid.x
+ gmtcnv.x
+ gqsort.x
+ intr.x <xwhen.h>
+ itob.x
+ lineoff.x
+ locpr.x
+ locva.x
+ lpopen.x <knet.h> <mach.h>
+ maideh.x <fset.h> <xwhen.h>
+ main.x <clset.h> <config.h> <ctype.h> <error.h> <fset.h>\
+ <knet.h> <printf.h> <xwhen.h>
+ miireadc.x <mii.h>
+ miiwritec.x <mii.h>
+ nmireadb.x <nmi.h>
+ nmireadc.x <nmi.h>
+ nmiwriteb.x <nmi.h>
+ nmiwritec.x <nmi.h>
+ onentry.x <clset.h> <error.h> <fset.h> <knet.h>
+ onerror.x <config.h> <error.h>
+ onexit.x <config.h> <error.h>
+ oscmd.x <clset.h> <ctype.h> <error.h> <knet.h>
+ pagefiles.x <chars.h> <ctype.h> <error.h> <finfo.h> <fset.h>\
+ <mach.h> <ttyset.h>
+ prchdir.x
+ prclcpr.x prc.com <config.h>
+ prcldpr.x prd.com <config.h> <knet.h>
+ prclose.x prc.com <config.h> <prstat.h>
+ prdone.x prd.com <config.h>
+ prenvfree.x
+ prenvset.x
+ prfilbuf.x prc.com <config.h> <fio.h>
+ prfindpr.x prc.com <config.h>
+ prgline.x prc.com <config.h> <ctype.h> <fset.h>
+ prgredir.x prc.com <config.h>
+ prkill.x prd.com <config.h> <knet.h>
+ propcpr.x prc.com <config.h> <fset.h> <knet.h> <prstat.h>\
+ <xwhen.h>
+ propdpr.x prd.com <config.h> <knet.h>
+ propen.x <knet.h>
+ proscmd.x prc.com <config.h>
+ prpsio.x prc.com <chars.h> <config.h> <error.h> <fio.com>\
+ <fio.h> <fset.h> <gio.h>
+ prpsload.x prc.com <config.h>
+ prredir.x prc.com <config.h>
+ prseti.x prc.com <config.h> <prstat.h>
+ prsignal.x prc.com <config.h> <knet.h>
+ prstati.x prc.com <config.h> <prstat.h>
+ prupdate.x prc.com <config.h> <error.h> <prstat.h>
+ psioisxt.x <ctype.h> <gio.h>
+ psioxfer.x
+ qsort.x
+ sttyco.x <ctype.h> <error.h> <ttset.h> <ttyset.h>
+ syserr.x
+ sysid.x
+ syspanic.x
+ sysptime.x <ctype.h>
+ tsleep.x
+ ttopen.x <fset.h>
+ urlget.x <ctype.h> <fset.h> <mach.h>
+ votable.x
+ xalloc.x <ctype.h> <knet.h> <xalloc.h>
+ xerfmt.x <ctype.h>
+ xerpop.x error.com <error.h>
+ xerpue.x <config.h> <fio.com> <fio.h> <mach.h>
+ xerreset.x error.com <error.h>
+ xerstmt.x error.com <ctype.h> <error.h>
+ xerverify.x error.com <error.h>
+ xgdevlist.x <xalloc.h>
+ xisatty.x <clset.h> <fset.h>
+ xmjbuf.x <config.h>
+ xttysize.x <clset.h>
+ xwhen.x
+ ;
diff --git a/sys/etc/nmiread.gx b/sys/etc/nmiread.gx
new file mode 100644
index 00000000..401f1c2c
--- /dev/null
+++ b/sys/etc/nmiread.gx
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_READ -- Read a block of data stored externally in NMI format.
+# Data is returned in the format of the local host machine.
+
+int procedure nmi_read$t (fd, spp, maxelem)
+
+int fd #I input file
+PIXEL spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int nmipksize(), nminelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = nmipksize (maxelem, NMI_PIXEL)
+ nelem = EOF
+
+ if (pksize > maxelem * SZ_PIXEL) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, pksize, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_PIXEL))
+ call nmiupk$t (Memc[bp], spp, nelem, TY_PIXEL)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nelem = min (maxelem, nminelem (nchars, NMI_PIXEL))
+ call nmiupk$t (spp, spp, nelem, TY_PIXEL)
+ }
+ }
+
+ return (nelem)
+end
diff --git a/sys/etc/nmireadb.x b/sys/etc/nmireadb.x
new file mode 100644
index 00000000..dc23866c
--- /dev/null
+++ b/sys/etc/nmireadb.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+
+# NMI_READB -- Read a block of data stored externally in NMI format.
+# Data is returned in the format of the local host machine.
+
+int procedure nmi_readb (fd, spp, maxelem)
+
+int fd #I input file
+bool spp[ARB] #O receives data
+int maxelem # max number of data elements to be read
+
+pointer sp, bp
+int pksize, nchars, nelem
+int nminelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = nminelem (maxelem, NMI_INT)
+ nelem = EOF
+
+ # Read data into user buffer and unpack in place.
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF)
+ nelem = min (maxelem, pksize)
+
+ return (nelem)
+end
diff --git a/sys/etc/nmireadc.x b/sys/etc/nmireadc.x
new file mode 100644
index 00000000..be65b9dd
--- /dev/null
+++ b/sys/etc/nmireadc.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMIREADC -- Read a block of character data stored externally in NMI format.
+# Data is returned in the native machine character format.
+
+int procedure nmi_readc (fd, spp, maxchars)
+
+int fd # input file
+int spp[ARB] # receives data
+int maxchars # max number of chars to be read
+
+pointer sp, bp
+int pksize, nchars
+int nmipksize(), nminelem(), read()
+errchk read()
+
+long note()
+
+begin
+ pksize = nmipksize (maxchars, NMI_BYTE)
+ nchars = max (maxchars, pksize)
+
+ if (nchars > maxchars) {
+ # Read data into local buffer and unpack into user buffer.
+
+ call smark (sp)
+ call salloc (bp, nchars, TY_CHAR)
+
+ nchars = read (fd, Memc[bp], pksize)
+ if (nchars != EOF) {
+ nchars = min (maxchars, nminelem (nchars, NMI_BYTE))
+ call nmiupk8 (Memc[bp], spp, nchars, TY_CHAR)
+ }
+
+ call sfree (sp)
+
+ } else {
+ # Read data into user buffer and unpack in place.
+
+ nchars = read (fd, spp, pksize)
+ if (nchars != EOF) {
+ nchars = min (maxchars, nminelem (nchars, NMI_BYTE))
+ call nmiupk8 (spp, spp, nchars, TY_CHAR)
+ }
+ }
+
+ return (nchars)
+end
diff --git a/sys/etc/nmiwrite.gx b/sys/etc/nmiwrite.gx
new file mode 100644
index 00000000..1efc4e45
--- /dev/null
+++ b/sys/etc/nmiwrite.gx
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMI_WRITE -- Write a block of data to a file in NMI format.
+# The input data is in the host system native binary format.
+
+procedure nmi_write$t (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+pointer sp, bp
+int bufsize
+int nmipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = nmipksize (nelem, NMI_PIXEL)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call nmipak$t (spp, Memc[bp], nelem, TY_PIXEL)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/nmiwriteb.x b/sys/etc/nmiwriteb.x
new file mode 100644
index 00000000..2819d1e8
--- /dev/null
+++ b/sys/etc/nmiwriteb.x
@@ -0,0 +1,21 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+
+# NMI_WRITEB -- Write a block of data to a file in NMI format.
+# The input data is in the host system native binary format.
+
+procedure nmi_writeb (fd, spp, nelem)
+
+int fd #I output file
+int spp[ARB] #I native format data to be written
+int nelem #I number of data elements to be written
+
+int bufsize
+int nminelem()
+
+begin
+ bufsize = nminelem (nelem, NMI_INT)
+ call write (fd, spp, bufsize)
+end
diff --git a/sys/etc/nmiwritec.x b/sys/etc/nmiwritec.x
new file mode 100644
index 00000000..16dc572c
--- /dev/null
+++ b/sys/etc/nmiwritec.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <nmi.h>
+
+# NMIWRITEC -- Write a block of character data to a file in NMI format.
+# The input data is assumed to be in a native machine format.
+
+procedure nmi_writec (fd, spp, nchars)
+
+int fd # output file
+int spp[ARB] # data to be written
+int nchars # number of chars units to be written
+
+pointer sp, bp
+int bufsize
+int nmipksize()
+
+begin
+ call smark (sp)
+
+ bufsize = nmipksize (nchars, NMI_BYTE)
+ call salloc (bp, bufsize, TY_CHAR)
+
+ call nmipak8 (spp, Memc[bp], nchars, TY_CHAR)
+ call write (fd, Memc[bp], bufsize)
+
+ call sfree (sp)
+end
diff --git a/sys/etc/onentry.x b/sys/etc/onentry.x
new file mode 100644
index 00000000..6a2e823d
--- /dev/null
+++ b/sys/etc/onentry.x
@@ -0,0 +1,65 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <error.h>
+include <clset.h>
+include <fset.h>
+
+define NFD 2
+
+# ONENTRY -- Default procedure called by the IRAF Main during process startup,
+# before entering the interpreter loop. If desired the user can supply their
+# own ONENTRY procedure; this will be used instead of the system default if
+# specified on the link line before the iraf libraries are searched.
+# This procedure is a no-op for a connected or host process. For a detached
+# process the default action is to redirect the standard input to the bkgfile,
+# which is assumed to be a text file containing commands to be executed by
+# the Main.
+#
+# The basic host calling sequence for an iraf process is as follows:
+#
+# x_file.e [-c | -d bkgfile ] [ command ]
+#
+# This is parsed by the zmain (host level main), returning the process type
+# in PRTYPE, the bkgfile string in BKGFILE if the process type is detached,
+# and anything remaining on the command line in CMD. If a custom onentry
+# procedure is used CMD can be anything; all the iraf main does is concatenate
+# the arguments into a string and pass it to the onentry procedure as CMD.
+
+int procedure onentry (prtype, bkgfile, cmd)
+
+int prtype #I process type (connected, detached, host)
+char bkgfile[ARB] #I osfn of bkg file, if detached process
+char cmd[ARB] #I command argument string, if any
+
+char osfn[SZ_FNAME]
+int chan, loc_zgettx, i, fd[NFD]
+data fd[1] /CLIN/, fd[2] /STDIN/
+extern zgettx()
+
+begin
+ if (prtype == PR_DETACHED) {
+ # Open the bkgfile and connect it to CLIN and STDIN. The stdin
+ # supplied by the process main is not used in this mode.
+ # We assume that no i/o has yet occurred on either file. Note
+ # that we do not wish to use FREDIR as that would preclude
+ # redirection on the command line.
+
+ call strpak (bkgfile, osfn, SZ_FNAME)
+ call zopntx (osfn, READ_ONLY, chan)
+ if (chan == ERR)
+ call sys_panic (EA_FATAL, "Cannot open bkgfile")
+ call zlocpr (zgettx, loc_zgettx)
+
+ do i = 1, NFD {
+ call fseti (fd[i], F_CHANNEL, chan)
+ call fseti (fd[i], F_DEVICE, loc_zgettx)
+ call fseti (fd[i], F_TYPE, TEXT_FILE)
+ }
+ }
+
+ # If PR_EXIT is returned the interpreter loop is bypassed and process
+ # shutdown occurs immediately.
+
+ return (PR_NOEXIT)
+end
diff --git a/sys/etc/onerror.x b/sys/etc/onerror.x
new file mode 100644
index 00000000..d8bd36f3
--- /dev/null
+++ b/sys/etc/onerror.x
@@ -0,0 +1,96 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <syserr.h>
+include <error.h>
+
+# ONERROR -- Give system the EPA of a procedure to be executed when task
+# termination occurs (either normal task termination or task termination
+# via error recovery). Each procedure will be called with the task termination
+# status, i.e., OK for normal termination, else the ERRCODE argument to ERROR.
+
+procedure onerror (user_proc)
+
+extern user_proc() #I procedure to be posted
+
+int epa, i
+bool first_time
+int proc_list[MAX_ONERROR], nprocs
+common /onercm/ nprocs, proc_list
+data first_time /true/
+
+begin
+ # The first call is by the IRAF main at process startup time, with
+ # a dummy argument.
+
+ if (first_time) {
+ nprocs = 0
+ first_time = false
+ return
+ }
+
+ call zlocpr (user_proc, epa)
+
+ # Ignore the call if the procedure has already been posted.
+ # Otherwise tack address of proc onto list and return.
+
+ for (i=1; i <= nprocs; i=i+1)
+ if (epa == proc_list[i])
+ return
+
+ nprocs = nprocs + 1
+ if (nprocs > MAX_ONERROR)
+ iferr (call syserr (SYS_SONERROVFL))
+ call erract (EA_WARN)
+
+ proc_list[nprocs] = epa
+end
+
+
+# ONERROR_REMOVE -- Remove a previously posted ONERROR procedure.
+
+procedure onerror_remove (user_proc)
+
+extern user_proc() #I procedure to be posted
+
+int epa, i
+int proc_list[MAX_ONERROR], nprocs
+common /onercm/ nprocs, proc_list
+
+begin
+ call zlocpr (user_proc, epa)
+ for (i=1; i <= nprocs; i=i+1)
+ if (proc_list[i] == epa)
+ proc_list[i] = 0
+end
+
+
+# XONERROR -- Called at task termination by the IRAF Main to execute each of
+# the posted user error cleanup procedures (if any). Procedures are executed
+# in the order in which they were posted. The task termination status is
+# passed to the called procedure as the single argument to the procedure.
+# The list of termination handlers is cleared when finished.
+
+procedure xonerror (status)
+
+int status #I task termination status (OK or error code)
+
+int nprocs_to_execute, i
+int proc_list[MAX_ONERROR], nprocs
+common /onercm/ nprocs, proc_list
+errchk zcall1
+
+begin
+ # Clear "nprocs" before calling user procedures, to ensure that
+ # a reentrant call does not lead to an infinite loop (i.e., in the
+ # event of an error during execution of a cleanup procedure).
+ # In principle this should not be necessary, since an error occurring
+ # during error restart should result in a panic abort.
+
+ nprocs_to_execute = nprocs
+ nprocs = 0
+
+ for (i=1; i <= nprocs_to_execute; i=i+1)
+ if (proc_list[i] != 0)
+ call zcall1 (proc_list[i], status)
+end
diff --git a/sys/etc/onexit.x b/sys/etc/onexit.x
new file mode 100644
index 00000000..44212cf1
--- /dev/null
+++ b/sys/etc/onexit.x
@@ -0,0 +1,88 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <syserr.h>
+include <error.h>
+
+# ONEXIT -- Give system the EPA of a procedure to be executed when process
+# shutdown occurs.
+
+procedure onexit (user_proc)
+
+extern user_proc() #I procedure to be posted
+bool first_time
+int epa, i
+int proc_list[MAX_ONEXIT], nprocs
+common /onexcm/ nprocs, proc_list
+data first_time /true/
+
+begin
+ # The first call is by the IRAF main at process startup time, with
+ # a dummy argument.
+
+ if (first_time) {
+ nprocs = 0
+ first_time = false
+ return
+ }
+
+ call zlocpr (user_proc, epa)
+
+ # Ignore the call if the procedure has already been posted.
+ # Otherwise tack address of proc onto list and return.
+
+ for (i=1; i <= nprocs; i=i+1)
+ if (epa == proc_list[i])
+ return
+
+ nprocs = nprocs + 1
+ if (nprocs > MAX_ONEXIT)
+ iferr (call syserr (SYS_SONEXITOVFL))
+ call erract (EA_WARN)
+
+ proc_list[nprocs] = epa
+end
+
+
+# ONEXIT_REMOVE -- Remove a previously posted ONEXIT procedure.
+
+procedure onexit_remote (user_proc)
+
+extern user_proc() #I procedure to be posted
+
+int epa, i
+int proc_list[MAX_ONERROR], nprocs
+common /onexcm/ nprocs, proc_list
+
+begin
+ call zlocpr (user_proc, epa)
+ for (i=1; i <= nprocs; i=i+1)
+ if (proc_list[i] == epa)
+ proc_list[i] = 0
+end
+
+
+# XONEXIT -- Called at process shutdown time by the IRAF main to execute
+# each posted user exit procedure. Exit procedures are called in the order
+# in which they were posted. Try to survive errors so that all exit
+# procedures may be called. Do not take an error action or issue a warning
+# message, since by the time we are called the CL has stopped listening to
+# us (it might possibly be safer to panic).
+
+procedure xonexit (exit_code)
+
+int exit_code #I passed to exit handlers
+int nprocs_to_execute, i
+int proc_list[MAX_ONEXIT], nprocs
+common /onexcm/ nprocs, proc_list
+errchk zcall1
+
+begin
+ nprocs_to_execute = nprocs
+ nprocs = 0
+
+ for (i=1; i <= nprocs_to_execute; i=i+1)
+ if (proc_list[i] != 0)
+ iferr (call zcall1 (proc_list[i], exit_code))
+ ;
+end
diff --git a/sys/etc/oscmd.x b/sys/etc/oscmd.x
new file mode 100644
index 00000000..78b723c2
--- /dev/null
+++ b/sys/etc/oscmd.x
@@ -0,0 +1,116 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include <clset.h>
+include <error.h>
+include <knet.h>
+
+# OSCMD -- Send a (machine dependent) command to the host operating system.
+# Try to spool the standard output and error output in the named files if
+# nonnull names for the files are given. OK is returned if the command
+# executes successfully.
+
+int procedure oscmd (cmd, infile, outfile, errfile)
+
+char cmd[ARB] # host command
+char infile[ARB] # name of host command input file
+char outfile[ARB] # name of file to receive output
+char errfile[ARB] # name of file to receive error output
+
+int status, ip, ch
+pointer sp, cmdbuf, osin, osout, oserr, ostmp, op
+errchk fmapfn, mktemp, fclobber, flush, putline
+int clstati(), getci()
+bool fnullfile()
+
+begin
+ call smark (sp)
+ call salloc (cmdbuf, SZ_COMMAND, TY_CHAR)
+ call salloc (osin, SZ_PATHNAME, TY_CHAR)
+ call salloc (osout, SZ_PATHNAME, TY_CHAR)
+ call salloc (oserr, SZ_PATHNAME, TY_CHAR)
+ call salloc (ostmp, SZ_PATHNAME, TY_CHAR)
+
+ # If we are called from the root process, e.g., the CL, the ZOSCMD
+ # primitive is called directly to transmit the host command, otherwise
+ # the OS command is sent up to the parent (root) process which calls
+ # ZOSCMD. This is necessary because the ZOSCMD primitive will not
+ # work from a subprocess on some systems, due to difficulties trying
+ # to spawn the host command interpreter.
+
+ if (clstati (CL_PRTYPE) != PR_CONNECTED) {
+ # Root process: send command directly to the host command
+ # interpreter.
+
+ # Pack command string and get OS versions of the filenames.
+ call strpak (cmd, Memc[cmdbuf], SZ_COMMAND)
+ if (infile[1] == EOS)
+ call strpak ("", Memc[osin], SZ_PATHNAME)
+ else
+ call fmapfn (infile, Memc[osin], SZ_PATHNAME)
+
+ # If output is directed to dev$null, save in temp file and delete.
+ if (fnullfile(outfile) || fnullfile(errfile))
+ call mktemp ("tmp$null", Memc[ostmp], SZ_PATHNAME)
+ else
+ Memc[ostmp] = EOS
+
+ if (outfile[1] == EOS)
+ call strpak ("", Memc[osout], SZ_PATHNAME)
+ else if (fnullfile (outfile))
+ call fmapfn (Memc[ostmp], Memc[osout], SZ_PATHNAME)
+ else {
+ call fclobber (outfile)
+ call fmapfn (outfile, Memc[osout], SZ_PATHNAME)
+ }
+
+ if (errfile[1] == EOS)
+ call strpak ("", Memc[oserr], SZ_PATHNAME)
+ else if (fnullfile (errfile))
+ call fmapfn (Memc[ostmp], Memc[oserr], SZ_PATHNAME)
+ else {
+ call fclobber (errfile)
+ call fmapfn (errfile, Memc[oserr], SZ_PATHNAME)
+ }
+
+ # Execute the command and wait for completion.
+ call zoscmd (Memc[cmdbuf], Memc[osin], Memc[osout], Memc[oserr],
+ status)
+
+ # Discard output directed to dev$null.
+ if (Memc[ostmp] != EOS)
+ iferr (call delete (Memc[ostmp]))
+ call erract (EA_WARN)
+
+ } else {
+ # Connected subprocess. Send the command to the parent process to
+ # be processed as a system directive by the pseudofile i/o system
+ # in the parent process. Synchronous execution is desired, so wait
+ # for a status return from the parent process before returning.
+ # The redirection files are ignored in this mode.
+
+ call flush (CLOUT)
+
+ # Send command.
+ Memc[cmdbuf] = '!'
+ op = cmdbuf + 1
+ for (ip=1; cmd[ip] != EOS && cmd[ip] != '\n'; ip=ip+1) {
+ Memc[op] = cmd[ip]
+ op = op + 1
+ }
+ Memc[op] = '\n'
+ Memc[op+1] = EOS
+ call putline (CLOUT, Memc[cmdbuf])
+ call flush (CLOUT)
+
+ # Get the return status, encoded as a nonnegative decimal integer.
+ for (status=0; getci (CLIN, ch) != EOF; )
+ if (ch == '\n')
+ break
+ else
+ status = status * 10 + TO_INTEG(ch)
+ }
+
+ call sfree (sp)
+ return (status)
+end
diff --git a/sys/etc/pagefiles.x b/sys/etc/pagefiles.x
new file mode 100644
index 00000000..22ef4840
--- /dev/null
+++ b/sys/etc/pagefiles.x
@@ -0,0 +1,1140 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ttyset.h>
+include <error.h>
+include <ctype.h>
+include <chars.h>
+include <mach.h>
+include <finfo.h>
+include <fset.h>
+
+# PAGEFILES.X -- Page through a file or set of files. Both backwards and
+# forwards traversals of files and file lists are supported, but not
+# (currently) backwards paging of a pipe.
+#
+# This program is a hack as it was coded starting from the original PAGE
+# program, which was much simpler. TODO: Add upscrolling and the ability
+# to buffer input and scoll backwards on a pipe. The present program is
+# monolithic and should be restructured if these features are added.
+
+define CC_PREFIX '^'
+define MAKE_PRINTABLE ($1+'A'-1)
+define SZ_QUERYMSG 80
+define SZ_KEYSTR 80
+define LNO_MAXLINES 2048
+define SZ_LONGLINE 4096
+define MAX_PAGE 100
+define MAX_PBCMD 100
+define UKEYS "ukey" # CL parameter for keyboard input
+
+# Command keystrokes.
+
+define HELPTXT "[q=quit,e=edit,d=dn,u=up,f|sp=fpg,b=bpg,j|cr=dnln,k=upln,.=bof,N=nfile,P=pfile]"
+
+define HELP '?' # print helptxt
+define QUIT 'q' # return to CL
+define EDIT 'e' # edit current file
+define FWD_SCREEN 'f' # forward one full screen
+define BACK_SCREEN 'b' # back one full screen
+define SCROLL_DOWN 'd' # forward half a screen
+define SCROLL_UP 'u' # back half a screen
+define PREV_LINE 'k' # back one line
+define NEXT_LINE 'j' # forward one line
+define TO_BOF '.' # to beginning of file
+define TO_EOF 'G' # to end of file
+define TO_EOF_ALT 'g' # to end of file
+define SEARCH 'n' # search for next occurrence of pattern
+define REDRAW '\014' # redraw screen
+
+define NEXT_FILE 'N' # goto next file in list
+define PREV_FILE 'P' # goto previous file in list
+define NEXT_FILE_ALT '\016' # <ctrl/n>
+define PREV_FILE_ALT '\020' # <ctrl/p>
+
+define LCMD ':' # colon commands
+define TO_FILE 'F' # ":file filename"
+
+
+# PAGEFILES -- Display a text file or files on the standard output (the user
+# terminal) one screen at a time, pausing after each screen has been filled.
+# The program is keystroke driven in raw mode, and currently recognizes the
+# keystrokes defined above.
+#
+# If map_cc is enabled, all unknown control characters will be converted into
+# printable sequences. The following control character sequences have a
+# special significance in IRAF textfiles: FF=formfeed, SO=set standout mode,
+# SI=clear standout mode. These sequences are mapped into whatever the output
+# device requires upon output by the TTY subroutines.
+
+procedure pagefiles (files)
+
+char files[ARB] # file template
+
+string device "terminal"
+string prompt ""
+int first_page
+int clear_screen
+int map_cc
+
+begin
+ first_page = 1
+ clear_screen = YES
+ map_cc = YES
+
+ call xpagefiles (files, device,
+ prompt, first_page, clear_screen, map_cc)
+end
+
+
+# PAGEFILE -- Page a single file; an alternate entry point to the more general
+# routine. A prompt string different than the filename may be specified and
+# the screen is not cleared when scrolling downward.
+
+procedure pagefile (fname, prompt)
+
+char fname[ARB] # name of file to be paged
+char prompt[ARB] # prompt string, if different than fname
+
+string device "terminal"
+int first_page
+int clear_screen
+int map_cc
+
+begin
+ first_page = 1
+ clear_screen = NO
+ map_cc = YES
+
+ call xpagefiles (fname, device,
+ prompt, first_page, clear_screen, map_cc)
+end
+
+
+# XPAGEFILES -- Generalized file pager.
+
+procedure xpagefiles (files, device, prompt, first_page, clear_screen, map_cc)
+
+char files[ARB] # file template
+char device[ARB] # output device name
+char prompt[ARB] # prompt string (filename if null)
+int first_page # first page to be displayed
+int clear_screen # clear screen between pages
+int map_cc # map control chars on output
+
+bool redirin, useroot
+pointer sp, fname, newfname, tty, lbuf
+int spoolfd, list, nfiles, cmd, i, j, n, o
+
+pointer ttyodes()
+bool ttygetb()
+int strncmp(), strlen()
+int fntopnb(), fntrfnb(), fntlenb(), fnldir()
+int pg_getcmd(), pg_pagefile(), fstati()
+errchk fntopnb, ttyodes, ttygetb, fntrfnb, pg_pagefile, pg_getcmd
+define err_ 91
+
+begin
+ call smark (sp)
+ call salloc (newfname, SZ_FNAME, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (lbuf, SZ_LINE, TY_CHAR)
+
+ list = fntopnb (files, YES)
+ nfiles = fntlenb (list)
+ spoolfd = NULL
+
+ tty = ttyodes (device)
+ redirin = (fstati (STDIN, F_REDIR) == YES)
+
+ # If terminal cannot scroll, set clear_screen to true regardless of the
+ # value given above.
+
+ if (ttygetb (tty, "ns"))
+ clear_screen = YES
+
+ cmd = NEXT_FILE
+ for (i=1; i <= nfiles && cmd != QUIT; i=i+1) {
+ # Get next filename.
+ if (fntrfnb (list, i, Memc[fname], SZ_FNAME) == EOF)
+ break
+
+ # Page the file.
+ cmd = pg_pagefile (tty, Memc[fname], Memc[newfname], prompt,
+ clear_screen, first_page, map_cc, i, nfiles, redirin, spoolfd)
+
+ # Decide what to do next.
+ while (cmd != QUIT) {
+ switch (cmd) {
+ case NEXT_FILE, BLANK, NEXT_LINE, CR, LF:
+ if (i >= nfiles)
+ cmd = pg_getcmd (tty, "no more files", 0,0,0,i,nfiles)
+ else
+ break
+ case PREV_FILE:
+ if (i <= 1)
+ cmd = pg_getcmd (tty, "at first file", 0,0,0,i,nfiles)
+ else {
+ i = i - 2
+ break
+ }
+
+ case TO_FILE:
+ # Position within the file list. If the user specified a
+ # logical directory in the filename, perform the compares
+ # on the raw filenames in the list, otherwise use only the
+ # root filename.
+
+ useroot = (fnldir(Memc[newfname],Memc[fname],SZ_FNAME) <= 0)
+ n = strlen (Memc[newfname])
+
+ for (j=1; j <= nfiles; j=j+1)
+ if (fntrfnb (list, j, Memc[lbuf], SZ_FNAME) > 0) {
+ if (useroot)
+ o = fnldir (Memc[lbuf], Memc[fname], SZ_FNAME)
+ else
+ o = 0
+ if (strncmp (Memc[lbuf+o], Memc[newfname], n) >= 0)
+ break
+ }
+
+ if (j > nfiles)
+ i = nfiles - 1
+ else
+ i = j - 1
+ break
+
+ case LCMD:
+ # Colon escape. Only :file is recognized at this level.
+
+ call pg_getstr (Memc[newfname], SZ_FNAME)
+ cmd = TO_FILE
+
+ case HELP:
+ cmd = pg_getcmd (tty, HELPTXT, 0,0,0,0,0)
+
+ default:
+err_ if (!redirin) {
+ call eprintf ("\07")
+ call flush (STDERR)
+ i = i - 1 # redisplay current file
+ }
+ break
+ }
+ }
+ }
+
+ if (spoolfd != NULL)
+ call close (spoolfd)
+
+ call fntclsb (list)
+ call sfree (sp)
+end
+
+
+# PG_PAGEFILE -- Display the named file on the standard output, page by
+# page, pausing for user response between pages.
+
+int procedure pg_pagefile (tty, fname, newfname, u_prompt, clear_screen,
+ first_page, map_cc, fileno, nfiles, redirin, spoolfd)
+
+pointer tty
+char fname[ARB] # file to be paged
+char newfname[ARB] # next file to be page (ret. by :file)
+char u_prompt[ARB] # prompt string, if not same as filename
+int clear_screen # clear screen between pages?
+int first_page # first page of file to be displayed
+int map_cc # map control characters?
+int fileno # current file number
+int nfiles # number of files to be paged
+bool redirin # reading from the standard input
+int spoolfd # fd if spooling output in a file
+
+char patbuf[SZ_LINE]
+int nlines, ncols, maxlines, maxcols
+long fi[LEN_FINFO], nchars, totchars, loffset
+pointer sp, lbuf, prompt, token, cmdbuf, ip, op, lp
+long pgoff[MAX_PAGE], pgnch[MAX_PAGE], pglno[MAX_PAGE]
+int fd, lineno, linelen, nleft, destline, toklen, lnout, i
+bool ateof, first_call, redirout, pushback, upline, upline_ok
+int o_loffset, o_nchars, o_lineno, o_pageno, junk, pageno, cmd, ch, n
+
+long note()
+pointer lno_open()
+bool streq(), ttygetb()
+int pg_getcmd(), ctoi(), strncmp(), patmake(), patmatch(), pg_peekcmd()
+int open(), finfo(), strlen(), pg_getline(), getci()
+int lno_fetch(), fstati(), ttyctrl()
+data first_call /true/
+
+define err_ 91
+define quit_ 92
+define search_ 93
+define destline_ 94
+
+begin
+ call smark (sp)
+ call salloc (lbuf, SZ_LONGLINE, TY_CHAR)
+ call salloc (cmdbuf, SZ_LINE, TY_CHAR)
+ call salloc (prompt, SZ_FNAME, TY_CHAR)
+ call salloc (token, SZ_FNAME, TY_CHAR)
+
+ if (first_call) {
+ # The pattern buffer is retained indefinitely.
+ patbuf[1] = EOS
+ spoolfd = NULL
+ first_call = false
+ }
+
+ call pg_setprompt (Memc[prompt], u_prompt, fname)
+ call xttysize (ncols, nlines)
+ maxlines = nlines - 1
+ maxcols = ncols
+
+ redirout = (fstati (STDOUT, F_REDIR) == YES)
+ upline_ok = (!redirout && ttygetb(tty,"cm") && ttygetb(tty,"al"))
+ call pg_pushcmd (NULL)
+
+ # Get file size for (xx%) info in nomore. If reading from the
+ # standard input, file size is not known.
+
+ nchars = 0
+ if (streq (fname, "STDIN")) {
+ totchars = -1
+ } else if (finfo (fname, fi) == ERR) {
+ call sprintf (Memc[lbuf], SZ_LINE, "Cannot access file `%s'")
+ call pargstr (fname)
+ cmd = pg_getcmd (tty, Memc[lbuf], 0, 0, 0, fileno, nfiles)
+ call sfree (sp)
+ return (cmd)
+ } else
+ totchars = FI_SIZE(fi)
+
+ # If file is empty, return immediately without clearing screen.
+ if (totchars == 0) {
+ call sprintf (Memc[lbuf], SZ_LINE, "Null length file `%s'")
+ call pargstr (fname)
+ cmd = pg_getcmd (tty, Memc[lbuf], 0, 0, 0, fileno, nfiles)
+ call sfree (sp)
+ return (cmd)
+ }
+
+ # Open the file.
+ iferr (fd = open (fname, READ_ONLY, TEXT_FILE)) {
+ call sprintf (Memc[lbuf], SZ_LINE, "Cannot open file `%s'")
+ call pargstr (fname)
+ cmd = pg_getcmd (tty, Memc[lbuf], 0, 0, 0, fileno, nfiles)
+ call sfree (sp)
+ return (cmd)
+ }
+
+ # Open the line offset save/fetch database.
+ lp = lno_open (LNO_MAXLINES)
+
+ # Advance to the first page of the file to be displayed. Pages are
+ # marked by FF chararacters in the text. If the first page is number
+ # one, do nothing. If the first character in the file is FF, do not
+ # count it. This is necessary to count pages correctly whether or not
+ # the first page is preceeded by a FF.
+
+ pageno = 1
+ lineno = 1
+ pgoff[1] = BOF
+ pgnch[1] = nchars
+ pglno[1] = lineno
+
+ if (first_page > 1) {
+ junk = getci (fd, ch)
+ nchars = nchars + 1
+
+ while (pageno < first_page) {
+ while (getci (fd, ch) != '\f') {
+ nchars = nchars + 1
+ if (ch == '\n')
+ lineno = lineno + 1
+ if (ch == EOF) {
+ call close (fd)
+ call lno_close (lp)
+ call sfree (sp)
+ return
+ }
+ }
+ pageno = pageno + 1
+ nchars = nchars + 1
+ pgoff[pageno] = note (fd)
+ pgnch[pageno] = nchars
+ pglno[pageno] = lineno
+ }
+ }
+
+ # Always clear the screen between files; the "clear_screen" param
+ # applies only to the pages of a single file.
+
+ if (!redirout) {
+ call ttyclear (STDERR, tty)
+ call flush (STDERR)
+ }
+
+ # Output lines, mapping control characters if enabled. Pause at the
+ # end of every screen, or when FF is encountered in the text.
+
+ pushback= false
+ ateof = false
+ nleft = maxlines # nlines left to display before prompt
+ lnout = 0
+
+ repeat {
+ # Fetch and display the next line of the file.
+
+ if (pushback)
+ pushback = false
+ else
+ loffset = note (fd)
+
+ if (pg_getline (fd, Memc[lbuf]) == EOF) {
+ if ((nfiles==1 && lineno <= maxlines) || redirin || redirout) {
+ # Simply quit if a single small file or the standard input
+ # is being paged.
+
+ call close (fd)
+ call lno_close (lp)
+ call sfree (sp)
+ return (QUIT)
+
+ } else {
+ nchars = totchars + SZ_LINE
+ ateof = true
+ nleft = 0
+ }
+
+ } else if (Memc[lbuf] == '\f') {
+ # Formfeed encountered; pause for the prompt and print the
+ # remainder of the line on the next screen. If we have not
+ # yet written anything on the screen (nleft=maxlines) don't
+ # bother to prompt again.
+
+ pageno = pageno + 1
+ pgoff[pageno] = loffset
+ pglno[pageno] = lineno
+ pgnch[pageno] = nchars
+ call ungetline (fd, Memc[lbuf+1])
+ pushback = true
+
+ if (nleft == maxlines)
+ next
+ else
+ nleft = 0
+
+ } else {
+ # Output line, processing all escapes as req'd by the device.
+ # Keep track of position in file for %done message in prompt,
+ # and of position on screen so that we know when to prompt.
+
+ call lno_save (lp, lineno, loffset, nchars)
+ linelen = strlen (Memc[lbuf])
+ nchars = nchars + linelen
+ lineno = lineno + 1
+
+ # Count the number of printed columns in the output text.
+ n = 1
+ do i = 1, linelen
+ if (ch >= ' ')
+ n = n + 1
+ else if (ch == '\t') {
+ n = n + 1
+ while (mod (n-1, 8) != 0)
+ n = n + 1
+ }
+
+ # Decrement lines left on screen.
+ nleft = nleft - max (1, ((n + maxcols-1) / maxcols))
+
+ if (spoolfd != NULL)
+ call putline (spoolfd, Memc[lbuf])
+
+ # Cancel upline if line is too long.
+ if (upline && lnout <= 0 && linelen >= maxcols) {
+ call ttyclear (STDERR, tty)
+ call flush (STDERR)
+ upline = false
+ lnout = 0
+ }
+
+ if (!(upline && lnout > 0))
+ call ttyputline (STDOUT, tty, Memc[lbuf], map_cc)
+ lnout = min (maxlines, lnout + 1)
+ }
+
+ if (nleft <= 0) {
+ # Move cursor to query line at end of line insert sequence.
+ if (upline) {
+ # Don't bother if the next command is another insert.
+ if (pg_peekcmd() != PREV_LINE) {
+ call ttygoto (STDOUT, tty, 1, lnout + 1)
+ call ttyclearln (STDOUT, tty)
+ }
+ upline = false
+ }
+
+ # Pause and get next keystroke from the user.
+ cmd = pg_getcmd (tty, Memc[prompt], nchars, totchars, lineno,
+ fileno, nfiles)
+
+ # Allow use of the space bar to advance to the next file,
+ # when at the end of the current file.
+
+ if (ateof && nfiles > 1 && (cmd == BLANK || cmd == FWD_SCREEN))
+ cmd = NEXT_FILE
+
+ repeat {
+ switch (cmd) {
+ case NEXT_FILE:
+ # This really means the next file if multiple files.
+ if (nfiles > 1)
+ goto quit_
+ else if (pushback) {
+ cmd = FWD_SCREEN
+ next
+ }
+
+ # Otherwise we want the next page (formfeed).
+ o_loffset = note (fd)
+ o_nchars = nchars
+ o_lineno = lineno
+
+ repeat {
+ loffset = note (fd)
+ n = pg_getline (fd, Memc[lbuf])
+ if (n == EOF) {
+ if (!redirin) {
+ call seek (fd, o_loffset)
+ pushback = false
+ nchars = o_nchars
+ lineno = o_lineno
+ ateof = false
+ }
+ cmd = pg_getcmd (tty, "No more pages",
+ nchars,totchars, lineno, fileno,nfiles)
+ Memc[lbuf] = EOS
+ break
+ }
+
+ call lno_save (lp, lineno, loffset, nchars)
+
+ if (Memc[lbuf] == '\f') {
+ pageno = min (MAX_PAGE, pageno + 1)
+ pgoff[pageno] = loffset
+ pgnch[pageno] = nchars
+ pglno[pageno] = lineno
+ if (!redirout) {
+ call ttyclear (STDERR, tty)
+ call flush (STDERR)
+ lnout = 0
+ }
+ call ungetline (fd, Memc[lbuf+1])
+ pushback = true
+ nleft = maxlines
+ break
+ }
+
+ nchars = nchars + n
+ lineno = lineno + 1
+ }
+
+ if (n == EOF)
+ next
+ else
+ break
+
+ case PREV_FILE:
+ # If there are multiple files go to previous file,
+ # otherwise, go to previous page (formfeed).
+
+ if (nfiles > 1)
+ goto quit_
+ if (redirin)
+ goto err_
+
+ # Special case - just reached beginning of next
+ # page, but still displaying previous page.
+
+ if (pglno[pageno] == lineno)
+ pageno = max (1, pageno - 1)
+
+ # If the beginning of the current page is not on
+ # the screen, go back to the beginning of the page.
+
+ if (lineno <= pglno[pageno]+maxlines)
+ pageno = max (1, pageno - 1)
+
+ # Go there.
+ call seek (fd, pgoff[pageno])
+ nchars = pgnch[pageno]
+ lineno = pglno[pageno]
+ pushback = false
+
+ if (!redirout) {
+ call ttyclear (STDERR, tty)
+ call flush (STDERR)
+ lnout = 0
+ }
+ if (getci (fd, ch) != '\f')
+ call ungetci (fd, ch)
+ nleft = maxlines
+ break
+
+ case QUIT:
+quit_ call close (fd)
+ call lno_close (lp)
+ call sfree (sp)
+ return (cmd)
+
+ case TO_BOF:
+ if (redirin)
+ goto err_
+
+ call pg_setprompt (Memc[prompt], u_prompt, fname)
+ call seek (fd, BOFL)
+ pushback = false
+ Memc[lbuf] = EOS
+ ateof = false
+ lineno = 1
+ nchars = 0
+ nleft = maxlines
+ pageno = 1
+
+ if (!redirout) {
+ call ttyclear (STDERR, tty)
+ call flush (STDERR)
+ lnout = 0
+ }
+ break
+
+ case FWD_SCREEN, BLANK:
+ if (!ateof && clear_screen == YES && !redirout) {
+ call ttyclear (STDERR, tty)
+ call flush (STDERR)
+ lnout = 0
+ }
+ nleft = maxlines
+ break
+
+ case TO_EOF, TO_EOF_ALT:
+ destline = MAX_INT
+ goto destline_
+ case SCROLL_DOWN:
+ nleft = (maxlines + 1) / 2
+ break
+ case SCROLL_UP:
+ if (redirin)
+ goto err_
+ if (upline_ok) {
+ destline = lineno - 2
+ do i = 1, ((maxlines + 1) / 2 - 1)
+ if (lineno - lnout - i > 1)
+ call pg_pushcmd (PREV_LINE)
+ } else
+ destline = lineno - ((maxlines + 1) / 2) - 1
+ goto destline_
+ case BACK_SCREEN:
+ if (redirin)
+ goto err_
+ destline = lineno - maxlines - 1
+ goto destline_
+ case PREV_LINE:
+ if (redirin)
+ goto err_
+ destline = lineno - 2
+ goto destline_
+ case REDRAW:
+ if (redirin)
+ goto err_
+ destline = lineno
+ goto destline_
+ case NEXT_LINE, CR, LF:
+ nleft = 1
+ break
+ case SEARCH:
+ # Stop at next line containing current pattern.
+ goto search_
+
+ case HELP:
+ cmd = pg_getcmd (tty, HELPTXT, 0, 0, 0, 0, 0)
+ # get another command
+
+ case EDIT:
+ # Edit the file being paged.
+ if (redirin)
+ goto err_
+
+ # Close file and LNO database.
+ call close (fd)
+ call lno_close (lp)
+ call flush (STDOUT)
+ call flush (STDERR)
+
+ # Command the CL to edit the file.
+ call sprintf (Memc[lbuf], SZ_LINE, "edit (\"%s\")")
+ call pargstr (fname)
+ iferr (call clcmdw (Memc[lbuf]))
+ call erract (EA_WARN)
+
+ # Reopen the file and LNO database.
+ iferr (fd = open (fname, READ_ONLY, TEXT_FILE)) {
+ call sfree (sp)
+ return (NEXT_FILE)
+ } else
+ lp = lno_open (LNO_MAXLINES)
+
+ # Redisplay the file at the BOF.
+ if (!redirout) {
+ call ttyclear (STDERR, tty)
+ call flush (STDERR)
+ lnout = 0
+ }
+ Memc[lbuf] = EOS
+ nchars = 0
+ lineno = 1
+ nleft = maxlines
+ break
+
+ case LCMD:
+ # Colon escape.
+ call pg_getstr (Memc[cmdbuf], SZ_LINE)
+ for (ip=cmdbuf; IS_WHITE (Memc[ip]); ip=ip+1)
+ ;
+
+ if (Memc[ip] == '!') {
+ # Send a command to the CL.
+
+ iferr (call clcmdw (Memc[cmdbuf+1]))
+ call erract (EA_WARN)
+ cmd = pg_getcmd (tty, Memc[prompt],
+ nchars, totchars, lineno, fileno, nfiles)
+ Memc[lbuf] = EOS
+ next
+
+ } else if (Memc[ip] == '/') {
+ # Search for a line containing the given pattern.
+
+ if (patmake (Memc[ip+1], patbuf, SZ_LINE) == ERR)
+ goto err_
+search_
+ if (patbuf[1] == EOS) {
+ cmd = pg_getcmd (tty, "No current pattern",
+ 0, 0, 0, fileno, nfiles)
+ Memc[lbuf] = EOS
+ next
+ }
+
+ o_loffset = note (fd)
+ o_nchars = nchars
+ o_lineno = lineno
+ o_pageno = pageno
+
+ repeat {
+ loffset = note (fd)
+ n = pg_getline (fd, Memc[lbuf])
+ if (n == EOF) {
+ if (!redirin) {
+ call seek (fd, o_loffset)
+ pushback = false
+ nchars = o_nchars
+ lineno = o_lineno
+ pageno = o_pageno
+ ateof = false
+ }
+ cmd = pg_getcmd (tty, "Pattern not found",
+ nchars,totchars,lineno,fileno,nfiles)
+ Memc[lbuf] = EOS
+ break
+ }
+
+ call lno_save (lp, lineno, loffset, nchars)
+ if (Memc[lbuf] == '\f') {
+ pageno = pageno + 1
+ pgoff[pageno] = loffset
+ pgnch[pageno] = nchars
+ pglno[pageno] = lineno
+ }
+
+ if (patmatch (Memc[lbuf], patbuf) > 0) {
+ if (redirin) {
+ call ungetline (fd, Memc[lbuf])
+ pushback = true
+ nleft = maxlines
+ break
+ } else {
+ destline = lineno
+ nchars = nchars + n
+ lineno = lineno + 1
+ goto destline_
+ }
+ }
+
+ nchars = nchars + n
+ lineno = lineno + 1
+ }
+
+ if (n == EOF)
+ next
+ else
+ break
+ }
+
+ # Case ":cmd arg".
+ for (op=token; IS_ALPHA (Memc[ip]); ip=ip+1) {
+ Memc[op] = Memc[ip]
+ op = op + 1
+ }
+ for (; IS_WHITE (Memc[ip]); ip=ip+1)
+ ;
+ Memc[op] = EOS
+ toklen = op - token
+
+ # Print help if no : string given.
+ if (toklen <= 0) {
+ call strcpy ("help", Memc[token], SZ_FNAME)
+ toklen = 4
+ }
+
+ if (strncmp (Memc[token], "line", toklen) == 0) {
+ # Move to the destination line, expressed as
+ # ":line N" for an absolute line, or ":line +/-N"
+ # for a relative move.
+
+ destline = lineno
+ if (Memc[ip] == '+') {
+ ip = ip + 1
+ if (ctoi (Memc, ip, n) > 0)
+ destline = lineno + n
+ } else if (Memc[ip] == '-') {
+ ip = ip + 1
+ if (ctoi (Memc, ip, n) > 0)
+ destline = lineno - n
+ } else if (ctoi (Memc, ip, n) > 0)
+ destline = n
+destline_
+ # Upscroll one line?
+ if (upline_ok && destline == lineno-2)
+ upline = true
+
+ # Determine line at top of new screen.
+ nleft = maxlines
+ if (destline < lineno && destline >= lineno-lnout-1)
+ destline = destline - lnout + 1
+ else
+ destline = destline - nleft + 1
+
+ # Don't upscroll off the top of the screen.
+ if (destline < 1) {
+ destline = 1
+ upline = false
+ }
+
+ # Look up the desired line offset in the database
+ # and go directly there if found, otherwise either
+ # advance forward or rewind the file and advance
+ # forward to the indicated line.
+
+ if (lno_fetch(lp,destline,loffset,nchars)==ERR) {
+ if (!redirin && destline < lineno) {
+ call seek (fd, BOFL)
+ pushback = false
+ lineno = 1
+ pageno = 1
+ nchars = 0
+ ateof = false
+ call pg_setprompt (Memc[prompt],
+ u_prompt, fname)
+ }
+
+ while (lineno < destline) {
+ loffset = note (fd)
+ n = pg_getline (fd, Memc[lbuf])
+ if (n == EOF) {
+ destline = lineno - 1 # goto EOF
+ goto destline_
+ }
+ call lno_save (lp, lineno, loffset, nchars)
+ if (Memc[lbuf] == '\f') {
+ pageno = pageno + 1
+ pgoff[pageno] = loffset
+ pgnch[pageno] = nchars
+ pglno[pageno] = lineno
+ }
+ nchars = nchars + n
+ lineno = lineno + 1
+ }
+ } else if (!redirin) {
+ call seek (fd, loffset)
+ pushback = false
+ lineno = destline
+ ateof = false
+
+ # Determine which page we are in.
+ do i = 2, MAX_PAGE
+ if (pglno[i] <= 0) {
+ pageno = i - 1
+ break
+ } else if (pglno[i] >= lineno) {
+ pageno = i
+ break
+ }
+
+ call pg_setprompt (Memc[prompt],u_prompt,fname)
+ }
+
+ # Prepare to draw the screen. Upline mode means
+ # we want to insert a line at the top of the screen
+ # and then skip to the page prompt; otherwise we
+ # clear the screen and output a full page of text.
+
+ if (!redirout) {
+ if (upline) {
+ # Clear screen if backing up over a page.
+ if (destline+1 == pglno[pageno])
+ call ttyclear (STDERR, tty)
+ call ttygoto (STDOUT, tty, 1, 1)
+ junk = ttyctrl (STDOUT, tty, "al", maxlines)
+ lnout = 0
+ } else {
+ call ttyclear (STDERR, tty)
+ upline = false
+ lnout = 0
+ }
+ call flush (STDERR)
+ }
+ Memc[lbuf] = EOS
+ break
+
+ } else if (strncmp (Memc[token], "file", toklen) == 0) {
+ # Position to the named file (must be in file list).
+
+ call strcpy (Memc[ip], newfname, SZ_FNAME)
+ call close (fd)
+ call lno_close (lp)
+ call sfree (sp)
+ return (TO_FILE)
+
+ } else if (strncmp (Memc[token],"spool",toklen) == 0) {
+ # Begin spooling output in a file.
+
+ if (spoolfd != NULL) {
+ call close (spoolfd)
+ spoolfd = NULL
+ }
+
+ if (Memc[ip] == EOS) {
+ ;
+ } else iferr {
+ spoolfd = open (Memc[ip], APPEND, TEXT_FILE)
+ } then {
+ spoolfd = NULL
+ call erract (EA_WARN)
+ }
+
+ # Get next keystroke from the user.
+ cmd = pg_getcmd (tty, Memc[prompt],
+ nchars, totchars, lineno, fileno, nfiles)
+
+ } else {
+ cmd = pg_getcmd (tty,
+ "colon cmds: :!cmd :/pat :line L :file F :spool F",
+ 0, 0, 0, 0, 0)
+ }
+
+ default:
+err_ call eprintf ("\07")
+ call flush (STDERR)
+ cmd = pg_getcmd (tty, Memc[prompt], nchars, totchars,
+ lineno, fileno, nfiles)
+ }
+ }
+ }
+ }
+end
+
+
+# PG_SETPROMPT -- Set the prompt string for the ukey end-of-page query.
+# The name of the file currently being paged is used unless a prompt string
+# is given.
+
+procedure pg_setprompt (prompt, u_prompt, fname)
+
+char prompt[SZ_FNAME] # receives prompt string
+char u_prompt[ARB] # user prompt string
+char fname[ARB] # file being paged
+
+int gstrcpy()
+
+begin
+ if (gstrcpy (u_prompt, prompt, SZ_FNAME) <= 0)
+ call strcpy (fname, prompt, SZ_FNAME)
+end
+
+
+# PG_GETLINE -- Get a line from the input file. Accumulates very long lines
+# (requiring several getline calls to read) into a single string.
+
+int procedure pg_getline (fd, lbuf)
+
+int fd # input file
+char lbuf[SZ_LONGLINE] # output buffer
+
+int nchars, op
+int getline()
+errchk getline
+
+begin
+ for (op=1; op + SZ_LINE < SZ_LONGLINE; op=op+nchars) {
+ nchars = getline (fd, lbuf[op])
+ if (nchars == EOF) {
+ if (op == 1)
+ return (EOF)
+ else
+ return (op - 1)
+ } else if (lbuf[op+nchars-1] == '\n')
+ break
+ }
+
+ return (op + nchars - 1)
+end
+
+
+# PG_GETCMD -- Query the user for a single character command keystroke.
+# A prompt naming the current file and our position in it is printed,
+# we read the single character command keystroke in raw mode, and then
+# the prompt line is cleared and we return.
+
+int procedure pg_getcmd (tty, fname, nchars, totchars, lineno, fileno, nfiles)
+
+pointer tty # tty descriptor
+char fname[ARB] # prefix string
+long nchars # position in file
+long totchars # size of file
+int lineno # current line number
+int fileno # current file number
+int nfiles # nfiles being paged through
+
+char keystr[SZ_KEYSTR]
+int key, pb, pbcmd[MAX_PBCMD]
+common /pgucom/ key, pb, pbcmd, keystr
+int clgkey(), fstati()
+
+begin
+ # If any commands have been pushed, return the next pushed command
+ # without generating a query.
+
+ if (pb > 0) {
+ key = pbcmd[pb]
+ pb = pb - 1
+ return (key)
+ }
+
+ # If the standard output is redirected, skip the query and just go on
+ # to the next page.
+
+ if (fstati (STDOUT, F_REDIR) == YES)
+ return (FWD_SCREEN)
+
+ # Ensure synchronization with the standard output.
+ call flush (STDOUT)
+
+ # Print query in standout mode, preceded by %done info.
+ call ttyso (STDERR, tty, YES)
+ call eprintf ("%s")
+ call pargstr (fname)
+ if (totchars > 0) {
+ if (nchars >= totchars + SZ_LINE)
+ call eprintf ("-(EOF)")
+ else {
+ call eprintf ("-(%02d%%)")
+ call pargi (max(0, min(99, nchars * 100 / totchars)))
+ }
+ }
+ if (lineno > 0) {
+ call eprintf ("-line %d")
+ call pargi (lineno - 1)
+ }
+ if (fileno > 0 && nfiles > 0) {
+ call eprintf ("-file %d of %d")
+ call pargi (fileno)
+ call pargi (nfiles)
+ }
+ call ttyso (STDERR, tty, NO)
+ call flush (STDERR)
+
+ call fseti (STDIN, F_SETREDRAW, REDRAW)
+
+ # Read the user's response, normally a single keystroke.
+ if (clgkey (UKEYS, key, keystr, SZ_KEYSTR) == EOF)
+ key = INTCHAR
+
+ call fseti (STDIN, F_SETREDRAW, 0)
+
+ if (key == INTCHAR)
+ key = QUIT
+ else if (key == NEXT_FILE_ALT)
+ key = NEXT_FILE
+ else if (key == PREV_FILE_ALT)
+ key = PREV_FILE
+
+ # Erase the prompt and return.
+ call eprintf ("\r")
+ call ttyclearln (STDERR, tty)
+ call flush (STDERR)
+
+ return (key)
+end
+
+
+# PG_GETSTR -- Called after receipt of a : key to get the string value.
+
+procedure pg_getstr (strval, maxch)
+
+char strval[maxch] # receives string
+int maxch
+
+char keystr[SZ_KEYSTR]
+int key, pb, pbcmd[MAX_PBCMD]
+common /pgucom/ key, pb, pbcmd, keystr
+
+begin
+ call strcpy (keystr, strval, maxch)
+end
+
+
+# PG_PUSHCMD -- Push back a command keystroke.
+
+procedure pg_pushcmd (cmd)
+
+int cmd #I command to be pushed
+
+char keystr[SZ_KEYSTR]
+int key, pb, pbcmd[MAX_PBCMD]
+common /pgucom/ key, pb, pbcmd, keystr
+
+begin
+ if (cmd <= 0)
+ pb = 0
+ else {
+ pb = min (MAX_PBCMD, pb + 1)
+ pbcmd[pb] = cmd
+ }
+end
+
+
+# PG_PEEKCMD -- Peek at any pushed back command keystroke.
+
+int procedure pg_peekcmd()
+
+char keystr[SZ_KEYSTR]
+int key, pb, pbcmd[MAX_PBCMD]
+common /pgucom/ key, pb, pbcmd, keystr
+
+begin
+ if (pb <= 0)
+ return (ERR)
+ else
+ return (pbcmd[pb])
+end
diff --git a/sys/etc/prc.com b/sys/etc/prc.com
new file mode 100644
index 00000000..5893ef10
--- /dev/null
+++ b/sys/etc/prc.com
@@ -0,0 +1,27 @@
+define MAX_PS 10 # maximum pseudofiles
+
+# Process table common.
+
+int pr_pid[MAX_CHILDPROCS] # process id
+int pr_status[MAX_CHILDPROCS] # process status
+int pr_inchan[MAX_CHILDPROCS] # input IPC channel from child
+int pr_infd[MAX_CHILDPROCS] # fd of input IPC
+int pr_outchan[MAX_CHILDPROCS] # output IPC channel to child
+int pr_outfd[MAX_CHILDPROCS] # fd of output IPC
+int pr_nopen[MAX_CHILDPROCS] # number of open channels
+int pr_pstofd[MAX_CHILDPROCS,MAX_PS] # pseudofile -> FD
+int pr_last_exit_code # exit code of last process closed
+int pr_lastio # index of last active process
+int pr_index # index of current process
+int pr_oldipc # old X_IPC handler
+int epa_giotr # gio.cursor driver entry points
+int epa_control # " "
+int epa_gflush # " "
+int epa_writep # " "
+int epa_readtty # " "
+int epa_writetty # " "
+
+common /prccom/ pr_pid, pr_status, pr_inchan, pr_infd, pr_outchan, pr_outfd,
+ pr_nopen, pr_pstofd, pr_lastio, pr_last_exit_code, pr_index, pr_oldipc,
+ epa_giotr, epa_control, epa_gflush, epa_writep, epa_readtty,
+ epa_writetty
diff --git a/sys/etc/prchdir.x b/sys/etc/prchdir.x
new file mode 100644
index 00000000..d11c903a
--- /dev/null
+++ b/sys/etc/prchdir.x
@@ -0,0 +1,21 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# PRCHDIR -- Change the current working directory of a connected child
+# process, or of all connected subprocesses if pid=0.
+
+procedure prchdir (pid, newdir)
+
+int pid # process id of child, or 0 for all subprocesses
+char newdir[ARB] # new directory
+pointer sp, cmd
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_COMMAND, TY_CHAR)
+
+ call strcpy ("chdir ", Memc[cmd], SZ_COMMAND)
+ call strcat (newdir, Memc[cmd], SZ_COMMAND)
+
+ call prupdate (pid, Memc[cmd], YES)
+ call sfree (sp)
+end
diff --git a/sys/etc/prclcpr.x b/sys/etc/prclcpr.x
new file mode 100644
index 00000000..e5dd397c
--- /dev/null
+++ b/sys/etc/prclcpr.x
@@ -0,0 +1,33 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <syserr.h>
+
+# PRCLCPR -- Close a connected subprocess. Given the PID of the child process
+# we get the input and output file descriptors from the process table and
+# close both files. When the second file is closed the process is disconnected
+# by the PR_ZCLSPR procedure, which leaves the exit status in the process table
+# common.
+
+int procedure prclcpr (pid)
+
+int pid # process id of child process
+
+int child
+int pr_findproc()
+include "prc.com"
+errchk syserr
+
+begin
+ # Search process table for the PID of the child process and close it
+ # if found. Return process termination code to parent.
+
+ child = pr_findproc (pid)
+ if (child == ERR)
+ call syserr (SYS_PRNOTFOUND)
+
+ call close (pr_infd[child])
+ call close (pr_outfd[child])
+
+ return (pr_last_exit_code)
+end
diff --git a/sys/etc/prcldpr.x b/sys/etc/prcldpr.x
new file mode 100644
index 00000000..479fffb9
--- /dev/null
+++ b/sys/etc/prcldpr.x
@@ -0,0 +1,47 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <config.h>
+include <syserr.h>
+
+define NOKILL NO
+define KILL YES
+
+# PRCLDPR -- Close a detached process. Called following process termination
+# to obtain the exit status and free up any system resources still allocated
+# to the job. If called prior to job termination execution of the current
+# process will be suspended until the bkg job terminates. PRDONE should be
+# called to determine if the job is still running if waiting is not desired.
+
+int procedure prcldpr (job)
+
+int job # slot number of job in prd.com table
+int exit_status
+include "prd.com"
+
+begin
+ # Wait for process to terminate if it is still active. If we are
+ # interrupted the process table is left unmodified. If the process
+ # has been killed the exit status will have been left in the table
+ # and ZCLDPR should not be called again.
+
+ if (pr_jobcode[job] == NULL)
+ call syserr (SYS_PRBKGNF)
+ else if (pr_active[job] == YES)
+ call zcldpr (pr_jobcode[job], NOKILL, exit_status)
+ else
+ exit_status = pr_exit_status[job]
+
+ # Free all remaining resources allocated to job. The bkgfile should
+ # already have been deleted by the process but if not, e.g., in the
+ # event of abnormal process termination, we delete it ourselves. The
+ # buffer for the bkgfile filename is freed as is the slot in the
+ # process table.
+
+ iferr (call delete (Memc[pr_bkgfile[job]]))
+ ;
+ call mfree (pr_bkgfile[job], TY_CHAR)
+ pr_jobcode[job] = NULL
+
+ return (exit_status)
+end
diff --git a/sys/etc/prclose.x b/sys/etc/prclose.x
new file mode 100644
index 00000000..26e2702c
--- /dev/null
+++ b/sys/etc/prclose.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <syserr.h>
+include <prstat.h>
+
+# PRCLOSE -- Close a connected subprocess. Send the command "bye" to the
+# child to initiate process shutdown, then close the IPC channels and
+# wait for the child to terminate, returning the termination status to
+# our caller. Note that process shutdown may take an arbitrarily long
+# time, depending on the number and nature of ONEXIT procedures posted by
+# tasks in the the child process.
+
+int procedure prclose (pid)
+
+int pid # process-id of child process
+
+int child
+int pr_findproc(), prclcpr()
+include "prc.com"
+errchk syserr
+
+begin
+ child = pr_findproc (pid)
+ if (child == ERR)
+ call syserr (SYS_PRNOTFOUND)
+
+ if (pr_status[child] != P_DONE && pr_status[child] != P_DEAD)
+ call putline (pr_outfd[child], "bye\n")
+
+ return (prclcpr (pid))
+end
diff --git a/sys/etc/prd.com b/sys/etc/prd.com
new file mode 100644
index 00000000..599dc6b6
--- /dev/null
+++ b/sys/etc/prd.com
@@ -0,0 +1,8 @@
+# Job table for detached processes.
+
+int pr_jobcode[MAX_BKGJOBS] # job code assigned by host system
+int pr_active[MAX_BKGJOBS] # set to NO if job is killed
+int pr_exit_status[MAX_BKGJOBS] # exit status of process
+pointer pr_bkgfile[MAX_BKGJOBS] # bkgfile filename (signals job term)
+
+common /prdcom/ pr_jobcode, pr_active, pr_exit_status, pr_bkgfile
diff --git a/sys/etc/prdone.x b/sys/etc/prdone.x
new file mode 100644
index 00000000..2df2dccc
--- /dev/null
+++ b/sys/etc/prdone.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <syserr.h>
+
+# PRDONE -- Determine if a detached process (background job) has completed.
+# This is a difficult process to perform portably at the system call level,
+# hence the deletion of the bkgfile is used to signal the completion of a
+# detached process. If the detached process fails to delete its bkgfile
+# for some reason, PRCLDPR will do so if the process has indeed terminated.
+
+int procedure prdone (job)
+
+int job # job number (slot number in job table)
+int access()
+include "prd.com"
+
+begin
+ if (pr_jobcode[job] == NULL)
+ call syserr (SYS_PRBKGNF)
+
+ if (access (Memc[pr_bkgfile[job]], 0, 0) == YES)
+ return (NO)
+ else
+ return (YES)
+end
diff --git a/sys/etc/prenvfree.x b/sys/etc/prenvfree.x
new file mode 100644
index 00000000..90908789
--- /dev/null
+++ b/sys/etc/prenvfree.x
@@ -0,0 +1,36 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# PRENVFREE -- Free any recently defined or redefined environment variables,
+# updating the values of any redefined variables uncovered by the free
+# operation in the specified connected subprocesses.
+
+int procedure prenvfree (pid, marker)
+
+int pid # pid of process to be updated, or 0 for all subprocs
+int marker # stack pointer returned by ENVMARK
+
+int ev_pid
+common /prvcom/ ev_pid
+int locpr(), envfree()
+extern prv_reset()
+
+begin
+ ev_pid = pid
+ return (envfree (marker, locpr (prv_reset)))
+end
+
+
+# PRV_RESET -- Reset the value of an environment variable in the specified
+# connected subprocesses.
+
+procedure prv_reset (name, value)
+
+char name[ARB] # name of variable to be reset
+char value[ARB] # new value
+
+int ev_pid
+common /prvcom/ ev_pid
+
+begin
+ call prenvset (ev_pid, name, value)
+end
diff --git a/sys/etc/prenvset.x b/sys/etc/prenvset.x
new file mode 100644
index 00000000..6c21133d
--- /dev/null
+++ b/sys/etc/prenvset.x
@@ -0,0 +1,24 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# PRENVSET -- Change the value of an environment variable in a connected child
+# process, or in all connected subprocesses if pid=0.
+
+procedure prenvset (pid, envvar, valuestr)
+
+int pid # process id of child, or 0 for all subprocesses
+char envvar[ARB] # name of environment variable
+char valuestr[ARB] # value of environment variable
+pointer sp, cmd
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_COMMAND, TY_CHAR)
+
+ call strcpy ("set ", Memc[cmd], SZ_COMMAND)
+ call strcat (envvar, Memc[cmd], SZ_COMMAND)
+ call strcat ("=", Memc[cmd], SZ_COMMAND)
+ call strcat (valuestr, Memc[cmd], SZ_COMMAND)
+
+ call prupdate (pid, Memc[cmd], NO)
+ call sfree (sp)
+end
diff --git a/sys/etc/prfilbuf.x b/sys/etc/prfilbuf.x
new file mode 100644
index 00000000..4ec09907
--- /dev/null
+++ b/sys/etc/prfilbuf.x
@@ -0,0 +1,38 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <fio.h>
+
+# PRFILBUF -- Fill the FIO buffer from a process. The function is equivalent
+# to the ordinary FIO filbuf with the exception that pseudofile read and
+# write directives are intercepted and processed. Hence, the reader sees a
+# stream of application specific commands need not know about pseudofile i/o.
+
+int procedure prfilbuf (fd)
+
+int fd # parent's input IPC from child process
+
+int pr
+int filbuf(), prpsio()
+include "prc.com"
+
+begin
+ # Determine which process has the given file as its CLIN stream.
+ # If FD not associated with a process call ordinary FILBUF, otherwise
+ # call PR_PSIO. To minimize searches of the process table we keep
+ # track of the slot number of the last active pid.
+
+ if (pr_infd[pr_lastio] == fd && pr_pid[pr_lastio] != NULL)
+ pr = pr_lastio
+ else {
+ for (pr=1; pr <= MAX_CHILDPROCS; pr=pr+1)
+ if (pr_pid[pr] != NULL)
+ if (pr_infd[pr] == fd)
+ break
+ if (pr > MAX_CHILDPROCS)
+ return (filbuf (fd)) # normal file
+ pr_lastio = pr
+ }
+
+ return (prpsio (pr_pid[pr], CLIN, FF_READ))
+end
diff --git a/sys/etc/prfindpr.x b/sys/etc/prfindpr.x
new file mode 100644
index 00000000..3df73ed2
--- /dev/null
+++ b/sys/etc/prfindpr.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+
+# PR_FINDPROC -- Search the process table for the given PID of a child process,
+# returning the index of the process if found.
+
+int procedure pr_findproc (pid)
+
+int pid # process id of child process
+int pr
+include "prc.com"
+
+begin
+ for (pr=1; pr <= MAX_CHILDPROCS; pr=pr+1)
+ if (pr_pid[pr] == pid)
+ return (pr)
+
+ return (ERR)
+end
diff --git a/sys/etc/prgline.x b/sys/etc/prgline.x
new file mode 100644
index 00000000..21ec1780
--- /dev/null
+++ b/sys/etc/prgline.x
@@ -0,0 +1,204 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <syserr.h>
+include <ctype.h>
+include <fset.h>
+
+define XMIT 0
+define XFER 1
+
+# PRGETLINE -- Get a line of text from a process. The function is equivalent
+# to the ordinary FIO getline with the exception that pseudofile read and
+# write directives are intercepted and processed. Hence, the reader sees a
+# stream of application specific commands need not know about pseudofile i/o.
+# The function of PRGETLINE is such that it can be used in place of GETLINE
+# on any file; pseudofile directives are recognized and process only if the
+# FD is associated with a connected subprocess.
+
+int procedure prgetline (fd, lbuf)
+
+int fd # parent's input IPC from child process
+char lbuf[SZ_LINE] # output line buffer
+
+char ch
+int nchars, maxchars, nchars_read, raw_mode_set, ndigits
+int bufsize, outfd, destfd, pr, pseudofile, line_type, offset
+pointer sp, buf, ip
+
+char getc()
+int getline(), read(), fstati(), ctoi(), itoc()
+errchk syserr, getline, fstati, read, write, pr_decodeargs, putc, getc
+include "prc.com"
+
+begin
+ call smark (sp)
+
+ pr = 0
+ buf = NULL
+ raw_mode_set = 0
+
+ repeat {
+ nchars = getline (fd, lbuf)
+
+ # Return immediately if not XMIT or XFER directive. This code is
+ # exercised heavily when performing raw mode i/o, hence some
+ # clarity is sacrificed for the sake of efficiency.
+ #
+ # Syntax: "xmit(P,NNN)" or "xfer(P,NNN)"
+ # 12345678 12345678
+ #
+ # where P is the pseudofile code (0<P<10) and NNN is the size of
+ # the data block in chars. In the following code all explicit
+ # integer constants refer to the character offsets shown above.
+
+ if (lbuf[1] != 'x' || nchars == EOF) {
+ break
+ } else if (lbuf[2] == 'm') {
+ if (lbuf[3] == 'i' && lbuf[4] == 't' && lbuf[5] == '(') {
+ line_type = XMIT
+ pseudofile = TO_INTEG (lbuf[6])
+ } else
+ break
+ } else if (lbuf[2] == 'f') {
+ if (lbuf[3] == 'e' && lbuf[4] == 'r' && lbuf[5] == '(') {
+ line_type = XFER
+ pseudofile = TO_INTEG (lbuf[6])
+ } else
+ break
+ } else
+ break
+
+ # Ignore directive if FD not associated with a process. To minimize
+ # searches of the process table we keep track of the slot number
+ # of the last active pid.
+
+ if (pr == 0) {
+ if (pr_infd[pr_lastio] == fd && pr_pid[pr_lastio] != NULL)
+ pr = pr_lastio
+ else {
+ for (pr=1; pr <= MAX_CHILDPROCS; pr=pr+1)
+ if (pr_pid[pr] != NULL)
+ if (pr_infd[pr] == fd)
+ break
+ if (pr > MAX_CHILDPROCS)
+ break
+ pr_lastio = pr
+ }
+ outfd = pr_outfd[pr]
+ }
+
+ # Map pseudofile code to a file descriptor in the local process.
+
+ destfd = pr_pstofd[pr,pseudofile]
+
+
+ # RAW mode transfers are handled as a special case to minimize the
+ # per-character overhead.
+
+ if (lbuf[8] == '1' && lbuf[9] == ')') {
+ if (line_type == XMIT) {
+ # XMIT
+ if (getc (fd, ch) == EOF)
+ call syserr (SYS_PRIPCSYNTAX)
+
+ # Clear RAW input mode if newline is encountered in output.
+ # Only works for STDIN/STDOUT, but that is all raw mode is
+ # used for with pseudofiles.
+
+ if (ch == '\n')
+ if (destfd == STDOUT) {
+ call fseti (STDIN, F_RAW, NO)
+ if (raw_mode_set == STDIN)
+ raw_mode_set = 0
+ }
+
+ call putc (destfd, ch)
+ call flush (destfd)
+
+ } else {
+ # XFER
+ if (raw_mode_set != destfd) {
+ call fseti (destfd, F_RAW, YES)
+ raw_mode_set = destfd
+ }
+
+ if (getc (destfd, ch) == EOF)
+ call putline (outfd, "0\n")
+ else {
+ call putline (outfd, "1\n")
+ call flush (outfd)
+ call putc (outfd, ch)
+ }
+ call flush (outfd)
+ }
+ next
+ }
+
+
+ # GENERAL XMIT or XFER directive. Read a block of data from one
+ # stream and transmit it to the other stream.
+
+ if (buf == NULL) {
+ bufsize = fstati (fd, F_BUFSIZE)
+ call salloc (buf, bufsize, TY_CHAR)
+ }
+
+ offset = 8
+ if (ctoi (lbuf, offset, nchars) <= 0)
+ call syserr (SYS_PRIPCSYNTAX)
+
+ if (line_type == XMIT) {
+ # XMIT -- Copy the block of data from the IPC channel to the
+ # destination file.
+
+ nchars_read = read (fd, Memc[buf], nchars)
+ if (nchars_read != nchars)
+ call syserr (SYS_PRIPCSYNTAX)
+ else {
+ # Clear RAW input mode if set and newline is encountered
+ # in the output stream.
+
+ if (destfd == STDOUT)
+ if (fstati (STDIN, F_RAW) == YES)
+ for (ip=buf+nchars-1; ip >= buf; ip=ip-1)
+ if (Memc[ip] == '\n') {
+ call fseti (STDIN, F_RAW, NO)
+ if (raw_mode_set == STDIN)
+ raw_mode_set = 0
+ break
+ }
+
+ call write (destfd, Memc[buf], nchars)
+ call flush (destfd)
+ }
+ next
+
+ } else {
+ # XFER -- Read up to maxchars chars from the input file and
+ # pass them on to the output IPC channel.
+
+ maxchars = min (nchars, bufsize)
+ nchars = read (destfd, Memc[buf], maxchars)
+ if (nchars == EOF)
+ nchars = 0
+
+ # Write the byte count record followed by the data record.
+ # These must be written as two separate records or deadlock
+ # will occur (with the reader waiting for the second record).
+
+ ndigits = itoc (nchars, lbuf, SZ_LINE)
+ lbuf[ndigits+1] = '\n'
+ call write (outfd, lbuf, ndigits + 1)
+ call flush (outfd)
+
+ call write (outfd, Memc[buf], nchars)
+ call flush (outfd)
+
+ next
+ }
+ }
+
+ call sfree (sp)
+ return (nchars)
+end
diff --git a/sys/etc/prgredir.x b/sys/etc/prgredir.x
new file mode 100644
index 00000000..c5ffceee
--- /dev/null
+++ b/sys/etc/prgredir.x
@@ -0,0 +1,19 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+
+# PR_GETREDIR -- Get the pseudofile redirection code for a process.
+
+int procedure pr_getredir (pid, stream)
+
+int pid # process id
+int stream # stream for which redirection info is needed
+
+int pr
+int pr_findproc()
+include "prc.com"
+
+begin
+ pr = pr_findproc (pid)
+ return (pr_pstofd[pr,stream])
+end
diff --git a/sys/etc/prkill.x b/sys/etc/prkill.x
new file mode 100644
index 00000000..f6938923
--- /dev/null
+++ b/sys/etc/prkill.x
@@ -0,0 +1,42 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <config.h>
+include <syserr.h>
+
+define NOKILL NO
+define KILL YES
+
+# PRKILL -- Kill a detached process. Control does not return until the process
+# has terminated, unless the kill fails for some reason. If the process exists
+# and is terminated by the kill directive, the exit status is left in the
+# process table for return by PRCLDPR.
+
+procedure prkill (job)
+
+int job # slot number of job in prd.com table
+include "prd.com"
+errchk syserr
+
+begin
+ # Kill the process if there is such a process and it is still active.
+ # It is an error to try to kill a nonexistent process or a process
+ # which has already been killed.
+
+ if (pr_jobcode[job] == NULL)
+ call syserr (SYS_PRBKGNF)
+ else if (pr_active[job] == NO)
+ call syserr (SYS_PRBKGNOKILL)
+ else {
+ call zcldpr (pr_jobcode[job], KILL, pr_exit_status[job])
+ if (pr_exit_status[job] == ERR)
+ call syserr (SYS_PRBKGNOKILL)
+ else
+ pr_active[job] = NO
+ }
+
+ # Delete the bkgfile if the process has not already done so during
+ # shutdown.
+ iferr (call delete (Memc[pr_bkgfile[job]]))
+ ;
+end
diff --git a/sys/etc/propcpr.x b/sys/etc/propcpr.x
new file mode 100644
index 00000000..b7b394a1
--- /dev/null
+++ b/sys/etc/propcpr.x
@@ -0,0 +1,201 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <config.h>
+include <syserr.h>
+include <fset.h>
+include <xwhen.h>
+include <prstat.h>
+
+# PROPCPR -- Open a connected subprocess, i.e., spawn the subprocess and
+# connect the two IPC channels to FIO file descriptors. No i/o is done on
+# the IPC channels, hence this relatively low level command is independent
+# of the IPC protocol used. PROPEN should be used if the standard IPC
+# protocol is followed, so that the environment and current working directory
+# may be passed to the subprocess.
+
+int procedure propcpr (process, in, out)
+
+char process[ARB] # filename of executable process file
+int in # fd of IPC input from child (output)
+int out # fd of IPC output to child (output)
+
+bool first_time
+int pr, loc_onipc
+pointer sp, fname
+int fopnbf()
+extern pr_onipc(), pr_dummy_open(), pr_zclspr()
+extern zardpr(), zawrpr(), zawtpr(), zsttpr()
+errchk xwhen, fmapfn, syserr
+include "prc.com"
+
+define cleanup1_ 91
+define cleanup2_ 92
+define cleanup3_ 93
+data first_time /true/
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_PATHNAME, TY_CHAR)
+
+ # Initialize the process table. Post exception handler to recover
+ # from write to IPC with no reader.
+
+ if (first_time) {
+ do pr = 1, MAX_CHILDPROCS
+ pr_pid[pr] = NULL
+ pr_lastio = 1 # any legal slot number will do
+ pr_last_exit_code = OK
+ call zlocpr (pr_onipc, loc_onipc)
+ call xwhen (X_IPC, loc_onipc, pr_oldipc)
+ first_time = false
+ }
+
+ # Find empty process slot.
+ for (pr=1; pr <= MAX_CHILDPROCS; pr=pr+1)
+ if (pr_pid[pr] == NULL)
+ break
+ if (pr > MAX_CHILDPROCS)
+ call syserr (SYS_PROVFL)
+ pr_index = pr
+
+ # Initialize the mapping of pseudofile codes to file descriptor numbers.
+ # PS codes begin at 1, corresponding to STDIN.
+
+ pr_pstofd[pr,STDIN] = STDIN
+ pr_pstofd[pr,STDOUT] = STDOUT
+ pr_pstofd[pr,STDERR] = STDERR
+ pr_pstofd[pr,STDGRAPH] = STDGRAPH
+ pr_pstofd[pr,STDIMAGE] = STDIMAGE
+ pr_pstofd[pr,STDPLOT] = STDPLOT
+
+ # Spawn process and open IPC channels.
+ call fmapfn (process, Memc[fname], SZ_PATHNAME)
+ call zopcpr (Memc[fname], pr_inchan[pr], pr_outchan[pr], pr_pid[pr])
+ if (pr_pid[pr] == ERR)
+ goto cleanup3_
+ pr_nopen[pr] = 2
+
+ # Set up file descriptors for the two IPC channels.
+
+ call strcpy (process, Memc[fname], SZ_PATHNAME)
+ call strcat (".in", Memc[fname], SZ_PATHNAME)
+ iferr (in = fopnbf (Memc[fname], READ_ONLY,
+ pr_dummy_open, zardpr, zawrpr, zawtpr, zsttpr, pr_zclspr))
+ goto cleanup2_
+ pr_infd[pr] = in
+
+ call strcpy (process, Memc[fname], SZ_PATHNAME)
+ call strcat (".out", Memc[fname], SZ_PATHNAME)
+ iferr (out = fopnbf (Memc[fname], WRITE_ONLY,
+ pr_dummy_open, zardpr, zawrpr, zawtpr, zsttpr, pr_zclspr))
+ goto cleanup1_
+ pr_outfd[pr] = out
+
+ pr_status[pr] = P_RUNNING
+ call sfree (sp)
+ return (pr_pid[pr])
+
+cleanup1_
+ iferr (call close (out))
+ ;
+cleanup2_
+ iferr (call close (in))
+ ;
+cleanup3_
+ call sfree (sp)
+ pr_pid[pr] = NULL
+ call syserrs (SYS_PROPEN, process)
+end
+
+
+# PR_DUMMY_OPEN -- Dummy ZOPNPR procedure called by FIO to "open" the IPC
+# channel to a subprocess. Our only function is to return the appropriate
+# channel code to FIO, since the channel has already been opened by ZOPCPR.
+
+procedure pr_dummy_open (osfn, mode, chan)
+
+char osfn[ARB] # not used
+int mode # used to select read or write IPC channel
+int chan # returned to FIO
+include "prc.com"
+
+begin
+ if (mode == READ_ONLY)
+ chan = pr_inchan[pr_index]
+ else
+ chan = pr_outchan[pr_index]
+end
+
+
+# PR_ZCLSPR -- Dummy "zclspr" routine called by FIO to close an IPC channel
+# when CLOSE is called on the corresponding FIO file descriptor. A subprocess
+# opened with PROPCPR is normally closed with PRCLCPR, but if error recovery
+# takes place CLOSE will automatically be called by the system to close both
+# file descriptors. We decrement the count of open channels and close the
+# process and both IPC channels when the count reaches zero. Hence, a
+# connected subprocess may be closed either by calling PRCLCPR or by closing
+# the IN and OUT file descriptors.
+
+procedure pr_zclspr (chan, status)
+
+int chan # either inchan or outchan of process
+int status # OK or ERR on output
+int pr
+include "prc.com"
+
+begin
+ do pr = 1, MAX_CHILDPROCS
+ if (pr_pid[pr] != NULL)
+ if (pr_inchan[pr] == chan || pr_outchan[pr] == chan) {
+ pr_nopen[pr] = pr_nopen[pr] - 1
+ if (pr_nopen[pr] == 0) {
+ call zclcpr (pr_pid[pr], pr_last_exit_code)
+ pr_pid[pr] = NULL
+ }
+ status = OK
+ return
+ }
+
+ status = ERR
+end
+
+
+# PR_ONIPC -- Exception handler for the X_IPC (write to IPC with no reader)
+# exception. This exception occurs when the child process dies unexpectedly.
+# Determine what process was being written to, cancel any output (to prevent
+# a cascade of onipcs trying to flush the output buffer), and cause FIO to
+# return a file write error. We cannot do any more than that w/o reentrancy
+# problems.
+
+procedure pr_onipc (vex, next_handler)
+
+int vex # virtual exception
+int next_handler # next handler in chain
+int pr, fd
+int fstati()
+include "prc.com"
+
+begin
+ # Chain to next exception handler, if any.
+ next_handler = pr_oldipc
+
+ # Get the FD of the file being written to at the time that the
+ # exception occurred. We assume that the write operation is still
+ # in progress when the exception takes place.
+
+ fd = fstati (0, F_LASTREFFILE)
+ for (pr=1; pr <= MAX_CHILDPROCS; pr=pr+1)
+ if (pr_pid[pr] != NULL)
+ if (pr_outfd[pr] == fd)
+ break
+ if (pr > MAX_CHILDPROCS)
+ return
+
+ # Cancel any buffered output and remove write permission to ensure
+ # that we will not get a cascade of X_IPC exceptions.
+
+ call fseti (fd, F_CANCEL, ERR)
+ call fseti (fd, F_MODE, READ_ONLY)
+ pr_status[pr] = P_DEAD
+end
diff --git a/sys/etc/propdpr.x b/sys/etc/propdpr.x
new file mode 100644
index 00000000..79690a60
--- /dev/null
+++ b/sys/etc/propdpr.x
@@ -0,0 +1,68 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+include <config.h>
+include <syserr.h>
+
+# PROPDPR -- Open a detached process. A detached process runs independently of
+# and asynchronous with the parent, with no direct communications (i.e., like
+# the IPC channels of connected subprocesses). The bkgfile is prepared by the
+# parent and read by the detached process, telling the detached process what
+# to do. There are no restrictions on the format or contents of the bkgfile
+# other than those placed by the implementor of the two processes (either a
+# text or binary file may be used). Deletion of the bkgfile is however assumed
+# to signify that the bkg process has terminated.
+
+int procedure propdpr (process, bkgfile, bkgmsg)
+
+char process[ARB] # vfn of executable process file
+char bkgfile[ARB] # vfn of background file
+char bkgmsg[ARB] # control string for kernel
+
+bool first_time
+int jobcode, pr
+pointer sp, process_osfn, bkgfile_osfn, pk_bkgmsg
+data first_time /true/
+errchk fmapfn, syserrs, malloc
+include "prd.com"
+
+begin
+ call smark (sp)
+ call salloc (process_osfn, SZ_PATHNAME, TY_CHAR)
+ call salloc (bkgfile_osfn, SZ_PATHNAME, TY_CHAR)
+ call salloc (pk_bkgmsg, SZ_LINE, TY_CHAR)
+
+ # First time initialization of the job table.
+ if (first_time) {
+ do pr = 1, MAX_BKGJOBS
+ pr_jobcode[pr] = NULL
+ first_time = false
+ }
+
+ # Get job slot.
+ for (pr=1; pr <= MAX_BKGJOBS; pr=pr+1)
+ if (pr_jobcode[pr] == NULL)
+ break
+ if (pr > MAX_BKGJOBS)
+ call syserrs (SYS_PRBKGOVFL, process)
+
+ # Map file names.
+ call fmapfn (process, Memc[process_osfn], SZ_PATHNAME)
+ call fmapfn (bkgfile, Memc[bkgfile_osfn], SZ_PATHNAME)
+ call strpak (bkgmsg, Memc[pk_bkgmsg], SZ_LINE)
+
+ # Spawn or enqueue detached process.
+ call zopdpr (Memc[process_osfn], Memc[bkgfile_osfn], Memc[pk_bkgmsg],
+ jobcode)
+ if (jobcode == ERR)
+ call syserrs (SYS_PRBKGOPEN, process)
+
+ # Set up bkg job descriptor.
+ pr_jobcode[pr] = jobcode
+ pr_active[pr] = YES
+ call malloc (pr_bkgfile[pr], SZ_FNAME, TY_CHAR)
+ call strcpy (bkgfile, Memc[pr_bkgfile[pr]], SZ_FNAME)
+
+ call sfree (sp)
+ return (pr)
+end
diff --git a/sys/etc/propen.x b/sys/etc/propen.x
new file mode 100644
index 00000000..21bdf334
--- /dev/null
+++ b/sys/etc/propen.x
@@ -0,0 +1,67 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <knet.h>
+
+# PROPEN -- Open a connected subprocess. Call PROPCPR to spawn the child
+# process and connect the input and output IPC channels to FIO file
+# descriptors, then pass the environment list and current working directory
+# to the child. The child is left waiting for the next command from the
+# parent, which is eventually written to the OUT channel by the parent.
+#
+# N.B.: If the child cannot process a SET or CHDIR command it is will take
+# a panic exit, raising the X_IPC exception in the parent. This is necessary
+# to avoid filling the IN (childs out) IPC channel, which would cause deadlock
+# if the parent were to fill the other channel with SET commands. Output of
+# SET commands w/o handshaking is desirable to minimize context switches and
+# IPC records and hence speed up process startup.
+
+int procedure propen (process, in, out)
+
+char process[ARB] # filename of executable file
+int in, out # input, output file descriptors to child
+
+int pid, print_redefined_variables, status
+pointer sp, cwd
+int propcpr()
+data print_redefined_variables /NO/
+errchk propcpr, envlist, putline, putci
+
+begin
+ call smark (sp)
+ call salloc (cwd, SZ_PATHNAME, TY_CHAR)
+
+ # Connect the subprocess with read and write IPC channels.
+
+ pid = propcpr (process, in, out)
+
+ # Pass the environment list to the child, omitting all but the most
+ # recent definitions of each variable. The list is passed in the
+ # opposite order from which it was redefined, but it does not matter
+ # since there is only one entry for each variable.
+
+ call envlist (out, "set ", print_redefined_variables)
+
+ # Set the current working directory in the child, in case the OS does
+ # not do so, and to save the child the need to ask the kernel for the
+ # cwd name, an expensive operation on some systems.
+
+ call zfgcwd (Memc[cwd], SZ_PATHNAME, status)
+ call strupk (Memc[cwd], Memc[cwd], SZ_PATHNAME)
+ call putline (out, "chdir ")
+ call putline (out, Memc[cwd])
+ call putci (out, '\n')
+
+ # The command "_go_" must be sent to the child to signal that process
+ # startup is completed. The process STDOUT and STDERR are redirected
+ # into the nullfile during startup, hence we will see no output if
+ # this command is not sent.
+
+ call putline (out, "_go_\n")
+
+ # Flush the output so the child can munch on this while we go off and
+ # do something else.
+ call flush (out)
+
+ call sfree (sp)
+ return (pid)
+end
diff --git a/sys/etc/proscmd.x b/sys/etc/proscmd.x
new file mode 100644
index 00000000..cf1e5bb3
--- /dev/null
+++ b/sys/etc/proscmd.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+
+# PROSCMD -- Process an OS escape command from a subprocess. Execute the
+# command and return the exit status to the subprocess via IPC.
+
+procedure proscmd (pr, cmd)
+
+int pr # subprocess process slot number
+char cmd[ARB] # host command to be executed
+
+char statbuf[5]
+int fd, status, op
+int itoc(), oscmd()
+include "prc.com"
+
+begin
+ fd = pr_outfd[pr]
+
+ # Execute the command (waits for completion).
+ status = oscmd (cmd, "", "", "")
+
+ # Encode the return status.
+ op = itoc (status, statbuf, 5) + 1
+ statbuf[op] = '\n'
+ statbuf[op+1] = EOS
+
+ # Return the status to the subprocess.
+ call write (fd, statbuf, op)
+ call flush (fd)
+end
diff --git a/sys/etc/prpsio.x b/sys/etc/prpsio.x
new file mode 100644
index 00000000..99050481
--- /dev/null
+++ b/sys/etc/prpsio.x
@@ -0,0 +1,484 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <gio.h>
+include <fio.h>
+include <fset.h>
+include <chars.h>
+include <error.h>
+include <config.h>
+include <syserr.h>
+
+define LEN_STACK 5 # depth one level, four fields
+define LEN_STACKFRAME 5 # size of one pushed stack frame
+
+define S_PR stack[$1]
+define S_IN stack[$1+1]
+define S_DESTFD stack[$1+2]
+define S_STREAM stack[$1+3]
+define S_REQUEST stack[$1+4]
+
+define push {stkp=stkp+1;stack[stkp]=($1)}
+define pop {$1=stack[stkp];stkp=stkp-1}
+
+
+# PR_PSIO -- Pseudofile i/o for a process. Process an i/o request for the
+# specified pseudofile stream of the specified process. Called either to read
+# command input from the CLIN of a process, or to process a read or write
+# request to a pseudofile of a process. I/O to STDIN, STDOUT, and STDERR
+# consists of simple binary copies of records. Output to the graphics streams
+# STDGRAPH, STDIMAGE, or STDPLOT consists of GKI metacode and is optionally
+# spooled and filtered with GIOTR (to apply the workstation transformation)
+# before be passed to a graphics kernel. Process to process i/o is tricky
+# since we must wait for a process to read or write (send us an XMIT or XFER)
+# before transferring a data record. A process may read or write to streams
+# of its own choosing before satisfying our request. A graphics kernel
+# connected as a subprocess reads and writes metacode on a graphics stream
+# and is free to read and write STDIN, STDOUT, etc. during execution as if
+# the kernel were being run as a CL task.
+#
+# Note: This code is far more subtle than it appears. I was unable to express
+# the subtleties in comments, eventually concluding that the code was easier
+# to understand than the explanation (see Notes.psio for the attempts). Beware
+# that the code is not completely general and assumes certain restrictions on
+# process configurations.
+
+int procedure pr_psio (pid, fd, rwflag)
+
+int pid # process id number
+int fd # file for which request is desired
+int rwflag # type of transfer to wait for
+
+pointer ip, op
+int stack[LEN_STACK], stkp, nchars, nleft
+int stream, pr, in, record_type, rq, iotype
+int pseudofile, destfd, destpr, destination, ps, flags
+bool filter_gki, graphics_stream, xmit_pending, ioctrl
+
+int filbuf(), read(), strncmp(), fstati()
+int pr_findproc(), psio_isxmit(), zfunc2(), zfunc3()
+errchk epa_writep, epa_giotr, epa_writetty, epa_readtty, epa_gflush
+errchk pr_findproc, psio_xfer, filbuf, read, write, flush, syserr
+include <fio.com>
+include "prc.com"
+
+begin
+ stream = fd
+ pr = pr_findproc (pid)
+ in = pr_infd[pr]
+ stkp = 0
+
+ if (rwflag == FF_WRITE) {
+ # We have been called to write to a subprocess. Simulate a
+ # pending XMIT request by pushing an XMIT on the stack. This
+ # causes XFER requests to be processed from the subprocess
+ # until the FIO buffer for destfd is exhausted.
+
+ push (pr)
+ push (in)
+ push (fd)
+ push (stream)
+ push (XMIT)
+ }
+
+# call putline (STDERR, "----------------------------------------------\n")
+# call eprintf ("PSIO (pid=%d, pr=%d, stream=%d)\n")
+# call pargi(pid); call pargi(pr); call pargi(stream)
+
+ # Process i/o requests from the subprocess until a request is received
+ # for the stream FD. Unsolicited requests from the process do not
+ # satisfy the request we were called for, e.g., a process may read from
+ # STDIN or write to STDERR before reading from fd=STDGRAPH.
+
+ repeat {
+ repeat {
+# call eprintf ("\n.....fill buffer: in=%d, pr=%d, stream=%d, stkp=%d\n")
+# call pargi(in); call pargi(pr); call pargi(stream); call pargi(stkp)
+ nleft = itop[in] - iop[in]
+ if (nleft <= 0)
+ nleft = filbuf (in)
+
+# for(ip=iop[in];ip<itop[in];ip=ip+1)call putc(STDERR, Memc[ip])
+ if (nleft == EOF) {
+ stkp = 0
+ nchars = 0
+ break
+ } else
+ ip = iop[in]
+
+ # Determine the type of directive, e.g., OS escape, xmit/xfer,
+ # or data (anything other than a pseudofile directive).
+
+ if (Memc[ip] == '!')
+ record_type = OSCMD
+ else if (Memc[ip] != 'x')
+ record_type = DATA
+ else
+ record_type = psio_isxmit (Memc[ip], pseudofile, nchars)
+
+# call eprintf ("record_type=%d, ps=%d, nchars=%d\n")
+# call pargi(record_type); call pargi(pseudofile); call pargi(nchars)
+
+ if (record_type == OSCMD) {
+ itop[in] = ip
+ nchars = nleft
+
+ } else if (record_type == DATA) {
+ pseudofile = CLIN
+ nchars = nleft
+
+ } else if (pseudofile < PSIOCTRL) {
+ # Decode the destination code into the destination FD,
+ # process slot number, and GKI filter flag.
+
+ destination = pr_pstofd[pr,pseudofile]
+# call eprintf ("redir code = %d\n"); call pargi (destination)
+ if (destination <= 0) {
+ destination = -destination
+ filter_gki = true
+ } else
+ filter_gki = false
+
+ if (destination > KSHIFT) {
+ destfd = mod (destination, KSHIFT)
+ destpr = destination / KSHIFT
+ } else {
+ destfd = destination
+ destpr = 0
+ }
+
+ graphics_stream = (destfd >= STDGRAPH && destfd <= STDPLOT)
+
+ # Discard the xmit or xfer directive. We will reuse the
+ # buffer at Memc[ip] later for temporary storage.
+
+ itop[in] = ip
+# call eprintf ("filter=%b, destfd=%d, destpr=%d\n")
+# call pargb(filter_gki); call pargi (destfd); call pargi (destpr)
+
+ } else {
+ # Pseudofile control directive.
+ record_type = PSIO
+ itop[in] = ip
+ }
+
+ # Process the record (data or PSIO directive).
+ switch (record_type) {
+ case DATA:
+ # Ordinary data record. If the requested fd is not CLIN
+ # we have an unsolicited command input error. If this
+ # occurs on a graphics stream, reset the stream (close the
+ # kernel and free all storage) to avoid leaving the graphics
+ # system in a funny state.
+
+ iotype = FF_READ
+ destfd = CLIN
+
+ if (stream != destfd) {
+ # Reset graphics stream.
+ if (graphics_stream)
+ call zcall1 (epa_gflush, stream)
+
+ # Take error action.
+ Memc[ip+nchars] = EOS
+ call putline (STDERR, Memc[ip])
+ call syserr (SYS_PRPSIOUCI)
+ }
+
+ case XMIT:
+ # Write to a process pseudofile.
+ iotype = FF_READ
+
+ # If pseudofile output is not connected to a stream read
+ # and discard the data block. This situation occurs
+ # whenever a task in interrupted.
+
+ if (destfd == 0) {
+ if (read (in, Memc[ip], nchars) < nchars)
+ call syserr (SYS_PRIPCSYNTAX)
+ next
+ }
+
+ if (filter_gki) {
+ # Process a block of GKI metacode. Append the block
+ # to the frame buffer for the stream and call GIOTR
+ # to process the metacode.
+
+ op = zfunc2 (epa_writep, destfd, nchars)
+ if (read (in, Memc[op], nchars) < nchars)
+ call syserr (SYS_PRIPCSYNTAX)
+# call eprintf ("___giotr, %d chars\n")
+# call pargi (nchars)
+
+ # Call GIOTR to process the graphics data. Any data
+ # to be returned to the client is spooled in the
+ # graphics stream to be read by a subsequent XFER.
+
+ call fseti (destfd, F_CANCEL, OK)
+ call zcall1 (epa_giotr, destfd)
+ call seek (destfd, BOFL)
+
+ } else {
+ # Binary transfer.
+
+ if (read (in, Memc[ip], nchars) < nchars)
+ call syserr (SYS_PRIPCSYNTAX)
+
+ # If writing to a standard stream and a raw mode i/o
+ # control sequence is seen, do a fseti on STDIN in
+ # this process to set up raw mode at the FIO level
+ # on the stream. If we don't do this, raw mode will
+ # be set in the driver but will be disabled in the
+ # first i/o operation by a nchars>1 read request from
+ # FIO.
+
+ if (destfd >= STDIN && destfd <= STDERR) {
+ ioctrl = (Memc[ip] == ESC &&
+ (nchars==LEN_RAWCMD || nchars==LEN_RAWCMD+1))
+ if (ioctrl) {
+ if (strncmp(Memc[ip],RAWOFF,LEN_RAWCMD) == 0) {
+ flags = IO_NORMAL
+ } else if (strncmp (Memc[ip],
+ RAWON, LEN_RAWCMD) == 0) {
+ flags = IO_RAW
+ if (Memc[ip+LEN_RAWCMD] == 'N')
+ flags = flags + IO_NDELAY
+ } else
+ ioctrl = false
+ }
+
+ if (ioctrl)
+ call fseti (STDIN, F_IOMODE, flags)
+ else {
+ call zcall3 (epa_writetty, destfd, Memc[ip],
+ nchars)
+ }
+ } else {
+ call write (destfd, Memc[ip], nchars)
+ if (!graphics_stream)
+ call flush (destfd)
+ }
+ }
+
+ # If writing to another process, push the current request
+ # and transfer command input to the destination process.
+ # Rewind the destfd file buffer so that it may be read by
+ # the process in a subsequent XFER call on the stream.
+
+ if (destpr != 0) {
+ if (graphics_stream)
+ call seek (destfd, BOFL)
+
+ push (pr)
+ push (in)
+ push (destfd)
+ push (stream)
+ push (XMIT)
+
+ pr = destpr
+ in = pr_infd[pr]
+ stream = destfd
+# call eprintf ("push XMIT, new in = %d, new pr = %d\n")
+# call pargi (in); call pargi(pr)
+
+ } else if (stkp > 0) {
+ # If the XMIT just completed satisfies a pending XFER
+ # request, complete the XFER request and pop it from
+ # the stack.
+
+ rq = stkp - LEN_STACKFRAME + 1
+ if (S_REQUEST(rq) == XFER &&
+ S_DESTFD(rq) == destfd) {
+
+ pop (record_type)
+ pop (stream)
+ pop (destfd)
+ pop (in)
+ pop (pr)
+
+ call seek (stream, BOFL)
+ nchars = itop[stream] - iop[stream]
+ if (nchars <= 0)
+ nchars = 0
+ else
+ nchars = read (stream, Memc[ip], nchars)
+# call eprintf ("XFER completed from fd=%d to pr=%d, %d chars\n")
+# call pargi(stream); call pargi(pr); call pargi(nchars)
+ call psio_xfer (pr_outfd[pr], Memc[ip], nchars)
+
+ # The stream buffer should now be empty as only
+ # one IPC record at a time is buffered for an
+ # XFER request. Mark the buffer empty.
+
+ call fseti (stream, F_CANCEL, OK)
+ }
+ }
+
+ case XFER:
+ # Read from a pseudofile.
+ iotype = FF_WRITE
+
+ if (destpr != 0 && iop[destfd] >= itop[destfd]) {
+ # Read from another process. Pseudofile FIO buffer
+ # is empty. Push the current request and transfer
+ # command input to the second process to give it an
+ # opportunity to write data into the buffer so that
+ # we can complete the XFER request.
+
+ push (pr)
+ push (in)
+ push (destfd)
+ push (stream)
+ push (XFER)
+
+ pr = destpr
+ in = pr_infd[pr]
+ stream = destfd
+# call eprintf ("push XFER, new in = %d, new pr = %d\n")
+# call pargi (in); call pargi(pr)
+
+ } else {
+ # Binary transfer. If reading from the stream
+ # associated with a pushed XMIT and the stream
+ # buffer is empty, the XMIT has been completed
+ # and must be popped.
+
+ xmit_pending = false
+ if (stkp > 0) {
+ rq = stkp - LEN_STACKFRAME + 1
+ if (S_REQUEST(rq) == XMIT && S_DESTFD(rq) == destfd)
+ xmit_pending = true
+ }
+
+# call eprintf ("in XFER: req=%d, str=%d, xmp=%b, iop=%d, itop=%d\n")
+# call pargi(S_REQUEST(rq)); call pargi(S_STREAM(rq))
+# call pargb(xmit_pending); call pargi(iop[destfd]); call pargi(itop[destfd])
+
+ if (xmit_pending && iop[destfd] >= itop[destfd]) {
+ # The pending XMIT has been completed (the stream
+ # buffer has been emptied by the reading process).
+ # Push the current XFER request back into the
+ # process input stream since we are not prepared
+ # to deal with it now.
+
+ itop[in] = iop[in] + nleft
+
+ # Pop the XMIT request.
+ pop (record_type)
+ pop (stream)
+ pop (destfd)
+ pop (in)
+ pop (pr)
+ ip = iop[in]
+
+ # Empty the fully read stream buffer.
+ call fseti (destfd, F_CANCEL, OK)
+# call eprintf ("XFER pops XMIT; push back XFER for later\n")
+
+ } else {
+ # Satisfy XFER by reading a data record and
+ # returning it to the requesting process.
+ # A request to read a single char enables raw
+ # mode, just as it does for ZGETTX.
+
+# This should not be necessary since the raw mode control sequence is
+# intercepted above. Also it is incorrect since F_NDELAY is not supported.
+# if (nchars == 1)
+# call fseti (destfd, F_RAW, YES)
+
+ if (destfd == STDIN) {
+ nchars = zfunc3 (epa_readtty,
+ destfd, Memc[ip], nchars)
+ } else
+ nchars = read (destfd, Memc[ip], nchars)
+ if (nchars == EOF)
+ nchars = 0
+# call eprintf ("XFER completed from fd=%d to pr=%d, %d chars\n")
+# call pargi(destfd); call pargi(pr); call pargi(nchars)
+ call psio_xfer (pr_outfd[pr], Memc[ip], nchars)
+ }
+ }
+
+
+ case PSIO:
+ # Pseudofile i/o control directive. These directives are
+ # used to connect graphics kernels to streams, to set and
+ # get WCS, set cursor mode parameters, etc. An XMIT to the
+ # pseudofile PSIOCTRL is use to pass control instructions
+ # via us to GTR_CONTROL, below. Note that the PSIOCTRL
+ # pseudofile is used to control all the graphics pseudofile
+ # streams. The number of the graphics pseudofile stream
+ # to be operated upon by gtr_control is passed as the first
+ # integer word of the data block.
+
+ # Read pseudofile number.
+ iotype = 0
+ if (read (in, ps, SZ_INT32) < SZ_INT32)
+ call syserr (SYS_PRIPCSYNTAX)
+
+ # Read data block.
+ nchars = nchars - SZ_INT32
+ if (read (in, Memc[ip], nchars) < nchars)
+ call syserr (SYS_PRIPCSYNTAX)
+
+ # Call gtr_control to process the control directives.
+ iferr (call zcall3 (epa_control,ps,Memc[ip],pr_pid[pr]))
+ call erract (EA_WARN)
+
+ # When writing to a graphics subkernel gtr_control may
+ # leave graphics metacode spooled in the graphics stream
+ # which we need to pass on to the subkernel. This is
+ # done by pushing an XMIT on the psio control stack to
+ # cause the subkernel process to be polled to see if it
+ # wants the spooled data.
+
+ nchars = fstati (ps, F_FILESIZE)
+ if (nchars > 0) {
+ destination = abs(pr_pstofd[pr,ps])
+ if (destination > KSHIFT) {
+ destfd = mod (destination, KSHIFT)
+ destpr = destination / KSHIFT
+ } else {
+ destfd = destination
+ destpr = 0
+ }
+
+ if (destpr != 0) {
+ call seek (destfd, BOFL)
+
+ push (pr)
+ push (in)
+ push (destfd)
+ push (stream)
+ push (XMIT)
+
+ pr = destpr
+ in = pr_infd[pr]
+ stream = destfd
+ }
+ }
+
+ case OSCMD:
+ # OS escape directive. There are portability problems
+ # with issuing ZOSCMD os escapes from subprocesses, so
+ # subprocesses send OS escapes to us (the parent process)
+ # as !cmd commands on CLOUT.
+
+ Memc[ip+nchars] = EOS
+ call proscmd (pr, Memc[ip+1])
+ }
+
+ } until (stkp <= 0 && record_type != OSCMD)
+
+# call eprintf ("termination ps(%d)=st(%d), io(%d)=rw(%d)\n")
+# call pargi(pseudofile); call pargi(stream)
+# call pargi(iotype); call pargi(rwflag)
+ } until (pseudofile == stream && iotype == rwflag)
+
+# call putline (STDERR, "----------------------------------------------\n")
+# call eprintf ("EXIT PSIO\n")
+
+ if (nchars == 0)
+ return (EOF)
+ else
+ return (nchars)
+end
diff --git a/sys/etc/prpsload.x b/sys/etc/prpsload.x
new file mode 100644
index 00000000..c44eea03
--- /dev/null
+++ b/sys/etc/prpsload.x
@@ -0,0 +1,30 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+
+# PRPSLOAD -- Must be called at process startup time to initialize pseudofile
+# i/o for the graphics streams, if any i/o to the graphics pseudofiles is
+# expected. The arguments are the LOCPR entry point addresses of the graphics
+# driver (gio.cursor) procedures to be called to process i/o requests on the
+# graphics streams.
+
+procedure prpsload (giotr, control, gflush, writep, readtty, writetty)
+
+extern giotr() #I gio.cursor driver procedures
+extern control() #I " "
+extern gflush() #I " "
+extern writep() #I " "
+extern readtty() #I " "
+extern writetty() #I " "
+
+int locpr()
+include "prc.com"
+
+begin
+ epa_giotr = locpr (giotr)
+ epa_control = locpr (control)
+ epa_gflush = locpr (gflush)
+ epa_writep = locpr (writep)
+ epa_readtty = locpr (readtty)
+ epa_writetty = locpr (writetty)
+end
diff --git a/sys/etc/prredir.x b/sys/etc/prredir.x
new file mode 100644
index 00000000..baf68b48
--- /dev/null
+++ b/sys/etc/prredir.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <syserr.h>
+
+# PRREDIR -- Redirect the pseudofile stream of a connected subprocess. A newly
+# connected subprocess inherits the pseudofile streams of the parent, i.e.,
+# a write to STDOUT by the child will be directed to the STDOUT of the parent.
+# Note that unlike FREDIR, the destination stream must already be open and
+# is unaffected by the redirection of the pseudofile (the pseudofile stream is
+# redirected into the existing stream). The destination file need not be of
+# the same type (binary) as the pseudofile, unless the pseudofile stream
+# contains binary data.
+
+procedure prredir (pid, stream, new_fd)
+
+int pid # process-id of child
+int stream # stream to be redirected (STDIN, STDOUT, etc)
+int new_fd # destination FD (already opened)
+
+int pr
+int pr_findproc()
+include "prc.com"
+errchk syserr
+
+begin
+ pr = pr_findproc (pid)
+ if (pr == ERR)
+ call syserr (SYS_PRNOTFOUND)
+
+ pr_pstofd[pr,stream] = new_fd
+end
diff --git a/sys/etc/prseti.x b/sys/etc/prseti.x
new file mode 100644
index 00000000..ad798d42
--- /dev/null
+++ b/sys/etc/prseti.x
@@ -0,0 +1,51 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <config.h>
+include <prstat.h>
+
+# PRSETI -- Set the value of a parameter for a connected subprocess.
+
+procedure prseti (pid, param, value)
+
+int pid #I process id of connected subprocess
+int param #I parameter to be set
+int value #I new parameter value
+
+int pr
+int pr_findproc()
+include "prc.com"
+errchk syserr
+
+begin
+ pr = pr_findproc (pid)
+ if (pr == ERR)
+ call syserr (SYS_PRNOTFOUND)
+
+ switch (param) {
+ case PR_STATUS:
+ pr_status[pr] = value
+ case PR_INCHAN:
+ pr_inchan[pr] = value
+ case PR_INFD:
+ pr_infd[pr] = value
+ case PR_OUTCHAN:
+ pr_outchan[pr] = value
+ case PR_OUTFD:
+ pr_outfd[pr] = value
+ case PR_STDIN:
+ pr_pstofd[pr,STDIN] = value
+ case PR_STDERR:
+ pr_pstofd[pr,STDERR] = value
+ case PR_STDOUT:
+ pr_pstofd[pr,STDOUT] = value
+ case PR_STDGRAPH:
+ pr_pstofd[pr,STDGRAPH] = value
+ case PR_STDIMAGE:
+ pr_pstofd[pr,STDIMAGE] = value
+ case PR_STDPLOT:
+ pr_pstofd[pr,STDPLOT] = value
+ default:
+ call syserr (SYS_PRSTAT)
+ }
+end
diff --git a/sys/etc/prsignal.x b/sys/etc/prsignal.x
new file mode 100644
index 00000000..0ec647a0
--- /dev/null
+++ b/sys/etc/prsignal.x
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <syserr.h>
+include <knet.h>
+
+# PRSIGNAL -- Send a signal (interrupt) to a child process. It is an error
+# if the pid given is not found in the process table.
+
+procedure prsignal (pid, signal)
+
+int pid # process-id of child process
+int signal # code of signal to be sent (e.g. X_INT)
+
+int child
+int pr_findproc()
+include "prc.com"
+errchk syserr
+
+begin
+ child = pr_findproc (pid)
+ if (child != ERR)
+ call zintpr (pid, signal, child)
+
+ if (child == ERR)
+ call syserr (SYS_PRSIGNAL)
+end
diff --git a/sys/etc/prstati.x b/sys/etc/prstati.x
new file mode 100644
index 00000000..bd3eb221
--- /dev/null
+++ b/sys/etc/prstati.x
@@ -0,0 +1,49 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <config.h>
+include <prstat.h>
+
+# PRSTATI -- Get the value of a parameter for a connected subprocess.
+
+int procedure prstati (pid, param)
+
+int pid # process id of connected subprocess
+int param # parameter for which status is desired
+int pr
+int pr_findproc()
+include "prc.com"
+errchk syserr
+
+begin
+ pr = pr_findproc (pid)
+ if (pr == ERR)
+ call syserr (SYS_PRNOTFOUND)
+
+ switch (param) {
+ case PR_STATUS:
+ return (pr_status[pr])
+ case PR_INCHAN:
+ return (pr_inchan[pr])
+ case PR_INFD:
+ return (pr_infd[pr])
+ case PR_OUTCHAN:
+ return (pr_outchan[pr])
+ case PR_OUTFD:
+ return (pr_outfd[pr])
+ case PR_STDIN:
+ return (pr_pstofd[pr,STDIN])
+ case PR_STDERR:
+ return (pr_pstofd[pr,STDERR])
+ case PR_STDOUT:
+ return (pr_pstofd[pr,STDOUT])
+ case PR_STDGRAPH:
+ return (pr_pstofd[pr,STDGRAPH])
+ case PR_STDIMAGE:
+ return (pr_pstofd[pr,STDIMAGE])
+ case PR_STDPLOT:
+ return (pr_pstofd[pr,STDPLOT])
+ default:
+ call syserr (SYS_PRSTAT)
+ }
+end
diff --git a/sys/etc/prupdate.x b/sys/etc/prupdate.x
new file mode 100644
index 00000000..49aa7e56
--- /dev/null
+++ b/sys/etc/prupdate.x
@@ -0,0 +1,61 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include <config.h>
+include <prstat.h>
+
+# PRUPDATE -- Broadcast a message to a process, or if pid=0, to all connected
+# subprocesses. Used primarily to incrementally pass SET and CHDIR commands to
+# subprocesses, eliminating the need to reconnect each process. Note that the
+# child process does not return "bye" in response to one of the builtin
+# functions SET and CHDIR. NOTE: if a child process is marked "busy" the
+# message is not sent to that process; only idle processes receive the message.
+
+procedure prupdate (pid, message, flushout)
+
+int pid #I process to be updated, or 0 for all procs
+char message[ARB] #I message to be broadcast to each child
+int flushout #I flush output
+
+int pr, status
+pointer sp, cmd, op
+int gstrcpy(), prstati()
+include "prc.com"
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_COMMAND, TY_CHAR)
+
+ # Make sure that the message string is non-null and is newline
+ # delimited.
+
+ op = cmd + gstrcpy (message, Memc[cmd], SZ_COMMAND)
+ if (op == cmd) {
+ call sfree (sp)
+ return
+ } else if (Memc[op-1] != '\n') {
+ Memc[op] = '\n'
+ Memc[op+1] = EOS
+ }
+
+ # Broadcast the message. If the child fails to process the command
+ # and returns the ERROR statement, the error will not be detected until
+ # the next user command is sent to the process (and indeed may corrupt
+ # the protocol). The parent should execute the SET or CHDIR prior
+ # to sending it to the child to make sure it is valid.
+
+ for (pr=1; pr <= MAX_CHILDPROCS; pr=pr+1)
+ if ((pid != NULL && pr_pid[pr] == pid) ||
+ (pid == NULL && pr_pid[pr] != NULL)) {
+
+ status = prstati (pr_pid[pr], PR_STATUS)
+ if (status == P_RUNNING) {
+ iferr (call putline (pr_outfd[pr], Memc[cmd]))
+ call erract (EA_WARN)
+ else if (flushout == YES)
+ call flush (pr_outfd[pr])
+ }
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/etc/psioisxt.x b/sys/etc/psioisxt.x
new file mode 100644
index 00000000..ddebd7df
--- /dev/null
+++ b/sys/etc/psioisxt.x
@@ -0,0 +1,58 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include <gio.h>
+
+
+# PSIO_ISXMIT -- Test for a pseudofile directive. Return XMIT, XFER, or DATA
+# as the function value, and if we do have a pseudofile, decode the pseudofile
+# number and char count.
+#
+# Syntax: "xmit(P,NNN)" or "xfer(P,NNN)"
+# 12345678 12345678
+#
+# where P is the pseudofile code (0<P<10) and NNN is the size of the data block
+# in chars. In the following code all explicit integer constants refer to the
+# character offsets shown above.
+
+int procedure psio_isxmit (lbuf, pseudofile, nchars)
+
+char lbuf[ARB] # text
+int pseudofile # pseudofile code (output)
+int nchars # block size (output)
+int line_type, ip
+int ctoi()
+errchk syserr
+
+begin
+ # Decode line type. If we are called we have already determined that
+ # lbuf[1] is 'x'.
+
+ if (lbuf[2] == 'm') {
+ if (lbuf[3] == 'i' && lbuf[4] == 't' && lbuf[5] == '(')
+ line_type = XMIT
+ else
+ return (DATA)
+ } else if (lbuf[2] == 'f') {
+ if (lbuf[3] == 'e' && lbuf[4] == 'r' && lbuf[5] == '(')
+ line_type = XFER
+ else
+ return (DATA)
+ } else
+ return (DATA)
+
+ # Get pseudofile code.
+ ip = 6
+ if (ctoi (lbuf, ip, pseudofile) <= 0)
+ call syserr (SYS_PRIPCSYNTAX)
+
+ while (lbuf[ip] == ',' || IS_WHITE(lbuf[ip]))
+ ip = ip + 1
+
+ # Get char size of data block.
+ if (ctoi (lbuf, ip, nchars) <= 0)
+ call syserr (SYS_PRIPCSYNTAX)
+
+ return (line_type)
+end
diff --git a/sys/etc/psioxfer.x b/sys/etc/psioxfer.x
new file mode 100644
index 00000000..ca286512
--- /dev/null
+++ b/sys/etc/psioxfer.x
@@ -0,0 +1,33 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define SZ_NUMBUF 8 # encoded count for an XFER
+
+
+# PSIO_XFER -- Transfer a data record to a process to complete an XFER
+# request. Write the byte count record followed by the data record.
+# These must be written as two separate records or deadlock
+# will occur (with the reader waiting for the second record).
+
+procedure psio_xfer (fd, buf, nchars)
+
+int fd # output file
+char buf[ARB] # buffer containing record to be written
+int nchars # length of record
+
+int ndigits
+char numbuf[SZ_NUMBUF]
+int itoc()
+
+begin
+ if (nchars >= 0) {
+ ndigits = itoc (nchars, numbuf, SZ_NUMBUF)
+ numbuf[ndigits+1] = '\n'
+ call write (fd, numbuf, ndigits + 1)
+ call flush (fd)
+
+ if (nchars > 0) {
+ call write (fd, buf, nchars)
+ call flush (fd)
+ }
+ }
+end
diff --git a/sys/etc/qsort.x b/sys/etc/qsort.x
new file mode 100644
index 00000000..f7efc4d3
--- /dev/null
+++ b/sys/etc/qsort.x
@@ -0,0 +1,81 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define LOGPTR 32 # log2(maxpts) (4e9)
+
+# QSORT -- General quicksort for arbitrary objects. X is an integer array
+# indexing the array to be sorted. The user supplied COMPARE function is used
+# to compare objects indexed by X:
+#
+# -1,0,1 = compare (x1, x2)
+#
+# where the value returned by COMPARE has the following significance:
+#
+# -1 obj[x1] < obj[x2]
+# 0 obj[x1] == obj[x2]
+# 1 obj[x1] > obj[x2]
+#
+# QSORT reorders the elements of the X array, which must be of type integer.
+# **NOTE** - See also gqsort.x, a more recent version of this routine.
+
+procedure qsort (x, nelem, compare)
+
+int x[ARB] # array to be sorted
+int nelem # number of elements in array
+extern compare() # function to be called to compare elements
+
+int i, j, k, lv[LOGPTR], p, pivot, uv[LOGPTR], temp
+define swap {temp=$1;$1=$2;$2=temp}
+int compare()
+
+begin
+ lv[1] = 1
+ uv[1] = nelem
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy loop to trigger the optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # subfile, to avoid quadratic behavior on an already
+ # sorted list.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (x[j], x[k])
+ pivot = x[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; compare (x[i], pivot) < 0; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (compare (x[j], pivot) <= 0)
+ break
+ if (i < j) # out of order pair
+ swap (x[i], x[j]) # interchange elements
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (x[i], x[j]) # interchange elements
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+
+ p = p + 1 # push onto stack
+ }
+ }
+end
diff --git a/sys/etc/sttyco.x b/sys/etc/sttyco.x
new file mode 100644
index 00000000..54f2c5ce
--- /dev/null
+++ b/sys/etc/sttyco.x
@@ -0,0 +1,519 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include <syserr.h>
+include <ctype.h>
+include <ttyset.h>
+include <ttset.h>
+
+.help sttyco
+.nf ---------------------------------------------------------------------------
+STTYCO -- Set/stat VOS terminal driver options via command string. This is a
+high level front-end to the ttset/ttstat procedures, used to set or query the
+individual terminal driver parameters. Since STTYCO is driven by a command
+string it may be called either as a task or as a subroutine. When called as
+a task, e.g, as the STTY task in the CL, the argument list is simply
+concatenated into a long string and passed to STTYCO for processing.
+
+The argument list consists of zero or more argument strings, as follows:
+
+ reset Reset default terminal settings
+ init Send initialization sequence to the terminal
+ show Show terminal settings
+ all Show all parameters, even if not in use
+ <nullarglist> Show terminal settings
+ <unknown> Assumed to be the termcap name of a terminal.
+ Set envvars `terminal', `ttyncols', and
+ `ttynlines' for the named terminal.
+ baud=N Set envvar `ttybaud=N'.
+ ncols=N Set envvar `ttyncols=N'.
+ nlines=N Set envvar `ttynlines=N'.
+ resize Reset the screen size parameters.
+
+ clear Disable named driver functions.
+
+ ucasein Map input to lower case.
+ ucaseout Map output to upper case.
+
+ logio [=logiofile] Log all i/o to the terminal in a file.
+ login [=loginfile] Log input from terminal in a file.
+ logout [=logoutfile] Log output to terminal in a file.
+
+ playback [=pbfile] Read terminal input from a logfile.
+ verify Pause at newline when in playback mode.
+ delay=N Msec delay in playback mode, verify disabled.
+
+Simply naming a parameter like ucasein, logio, etc., causes that function to
+be enabled, or disabled if preceeded by the keyword `clear'. Any of the
+sequences +, -, =yes, =no may also be appended to turn the function on or off.
+The logging functions may also take a filename argument, e.g., logio=file
+enables i/o logging into the named file. The default filenames are as follows:
+
+ logio home$ttyio.log
+ login home$ttin.log
+ logout home$ttout.log
+ playback home$ttin.log
+
+If verify is disabled a delay will precede each record returned from the
+input file; the default delay is quite short. Pause mode is terminated by
+typing a space or carriage return to continue execution, or `q' to terminate
+playback mode.
+.endhelp ----------------------------------------------------------------------
+
+define STTY_KEYWORDS "|reset|init|all|show|baud|ncols|nlines|resize|clear|\
+ |ucasein|ucaseout|logio|login|logout|playback|verify|delay|"
+
+define RESET 1 # reset default terminal settings
+define INIT 2 # send initialization sequence to the terminal
+define ALL 3 # show all parameters
+define SHOW 4 # show parameters
+define BAUD 5 # set envvar `ttybaud'
+define NCOLS 6 # set envvar `ttyncols'
+define NLINES 7 # set envvar `ttynlines'
+define RESIZE 8 # reset the screen size parameters
+define CLEAR 9 # set default action to NO rather than YES
+# newline 10
+define UCASEIN 11 # map input to lower case
+define UCASEOUT 12 # map output to upper case
+define LOGIO 13 # log all i/o in a file
+define LOGIN 14 # log input in a file
+define LOGOUT 15 # log output in a file
+define PLAYBACK 16 # take input from a file
+define VERIFY 17 # wait for user to type a key after each record
+define DELAY 18 # msec delay after each record in playback mode
+
+
+# STTYCO -- Main entry point. Input consists of an argument list of arbitrary
+# length. Output is to the given file descriptor.
+
+procedure sttyco (args, ttin, ttout, outfd)
+
+char args[ARB] # argument list
+int ttin, ttout # tty file descriptors
+int outfd # write task output here
+
+pointer sp, keyw, value, tty
+int startcol, ival, nargs, yesno, defact, show, all, ip
+int stty_getarg(), strdic(), ctoi()
+pointer ttyodes()
+
+string keywords STTY_KEYWORDS
+errchk ttseti, ttsets, stty_ttyinit
+define argerr_ 91
+
+begin
+ call smark (sp)
+ call salloc (keyw, SZ_FNAME, TY_CHAR)
+ call salloc (value, SZ_FNAME, TY_CHAR)
+
+ defact = YES
+ show = NO
+ all = NO
+ ip = 1
+
+ # Process successive keyword=value arguments.
+
+ for (nargs=0; stty_getarg (args, ip, Memc[keyw], SZ_FNAME, Memc[value],
+ SZ_FNAME, defact, yesno) != EOF; nargs = nargs + 1) {
+
+ switch (strdic (Memc[keyw], Memc[keyw], SZ_FNAME, keywords)) {
+ case RESET:
+ call ttseti (ttin, TT_INITIALIZE, yesno)
+ case INIT:
+ call stty_ttyinit (ttin, ttout, "terminal")
+
+ case ALL:
+ all = yesno
+ show = YES
+ case SHOW:
+ show = yesno
+ case BAUD:
+ if (IS_DIGIT (Memc[value]))
+ call stty_envreset ("ttybaud", Memc[value])
+ else
+ goto argerr_
+ case NCOLS:
+ if (IS_DIGIT (Memc[value]))
+ call stty_envreset ("ttyncols", Memc[value])
+ else
+ goto argerr_
+ case NLINES:
+ if (IS_DIGIT (Memc[value]))
+ call stty_envreset ("ttynlines", Memc[value])
+ else
+ goto argerr_
+
+ case RESIZE:
+ tty = ttyodes ("terminal")
+ call stty_setsize (ttin, ttout, tty)
+ call ttycdes (tty)
+
+ case CLEAR:
+ defact = NO
+
+ case UCASEIN:
+ call ttseti (ttin, TT_UCASEIN, yesno)
+ case UCASEOUT:
+ call ttseti (ttin, TT_UCASEOUT, yesno)
+
+ case LOGIO:
+ if (yesno == YES && Memc[value] != EOS)
+ call ttsets (ttin, TT_IOFILE, Memc[value])
+ call ttseti (ttin, TT_LOGIO, yesno)
+ case LOGIN:
+ if (yesno == YES && Memc[value] != EOS)
+ call ttsets (ttin, TT_INFILE, Memc[value])
+ call ttseti (ttin, TT_LOGIN, yesno)
+ case LOGOUT:
+ if (yesno == YES && Memc[value] != EOS)
+ call ttsets (ttin, TT_OUTFILE, Memc[value])
+ call ttseti (ttin, TT_LOGOUT, yesno)
+ case PLAYBACK:
+ if (yesno == YES && Memc[value] != EOS)
+ call ttsets (ttin, TT_PBFILE, Memc[value])
+ call ttseti (ttin, TT_PLAYBACK, yesno)
+
+ case VERIFY:
+ call ttseti (ttin, TT_PBVERIFY, yesno)
+ case DELAY:
+ startcol = 1
+ if (ctoi (Memc[value], startcol, ival) > 0)
+ call ttseti (ttin, TT_PBDELAY, ival)
+ else
+ goto argerr_
+
+ default:
+ # If not keyword, must be a terminal name.
+ iferr (call stty_newterm (ttin, ttout, Memc[keyw]))
+ call erract (EA_WARN)
+ }
+ }
+
+ # If the argument list was null or the SHOW flag was set, show
+ # the current terminal settings.
+
+ if (nargs == 0 || show == YES)
+ call stty_showterm (ttin, ttout, outfd, all)
+
+ call sfree (sp)
+ return
+
+argerr_
+ call syserrs (SYS_STTYNUMARG, Memc[keyw])
+ call sfree (sp)
+end
+
+
+# STTY_NEWTERM -- Configure the environment for a new type of terminal.
+
+procedure stty_newterm (ttin, ttout, terminal)
+
+int ttin, ttout # tty file descriptors
+char terminal[ARB] # termcap name of new terminal
+
+pointer sp, vp, tty
+pointer ttyodes()
+
+bool ttygetb()
+int ttygets()
+errchk ttyodes, stty_envreset, ttygsize
+
+begin
+ call smark (sp)
+ call salloc (vp, SZ_FNAME, TY_CHAR)
+
+ tty = ttyodes (terminal)
+
+ # Set the terminal parameters.
+ call stty_envreset ("terminal", terminal)
+ call stty_setsize (ttin, ttout, tty)
+
+ # Set the stdgraph device name for this terminal, if given.
+ if (ttygetb (tty, "gd")) {
+ if (ttygets (tty, "gd", Memc[vp], SZ_FNAME) <= 0)
+ call strcpy (terminal, Memc[vp], SZ_FNAME)
+ } else
+ call strcpy ("none", Memc[vp], SZ_FNAME)
+ call stty_envreset ("stdgraph", Memc[vp])
+
+ call ttycdes (tty)
+ call sfree (sp)
+end
+
+
+# STTY_SETSIZE -- Determine the terminal screen size in characters, and set
+# up the environment appropriately.
+
+procedure stty_setsize (ttin, ttout, tty)
+
+int ttin, ttout # tty file descriptors
+pointer tty
+
+char num[4]
+int ncols, nlines, n
+int itoc()
+
+begin
+ call ttygsize (ttin, ttout, tty, ncols, nlines)
+
+ n = itoc (ncols, num, 4)
+ call stty_envreset ("ttyncols", num)
+ call ttyseti (tty, TTY_NCOLS, ncols)
+
+ n = itoc (nlines, num, 4)
+ call stty_envreset ("ttynlines", num)
+ call ttyseti (tty, TTY_NLINES, nlines)
+end
+
+
+# STTY_TTYINIT -- Output the initialization string and the contents of
+# the initialization file to the terminal, if either is specified in the
+# termcap entry for the device.
+
+procedure stty_ttyinit (ttin, ttout, terminal)
+
+int ttin, ttout # tty file descriptors
+char terminal[ARB] # termcap name of new terminal
+
+pointer tty
+pointer ttyodes()
+
+begin
+ tty = ttyodes (terminal)
+ call ttyinit (ttout, tty)
+ call flush (ttout)
+ call ttycdes (tty)
+end
+
+
+# STTY_ENVRESET -- Set the value of an environment variable in the current
+# process and in all connected subprocesses.
+
+procedure stty_envreset (envvar, value)
+
+char envvar[ARB] # environment variable to be set
+char value[ARB] # new value
+
+errchk envreset
+
+begin
+ call envreset (envvar, value)
+ call prenvset (0, envvar, value)
+end
+
+
+# STTY_SHOWTERM -- Show the current terminal driver status.
+
+procedure stty_showterm (ttin, ttout, fd, all)
+
+int ttin, ttout # tty file descriptors
+int fd # where the output goes
+int all # show all params, even if not in use
+
+int junk
+pointer sp, val
+bool ucasein, ucaseout, shift, logio, login, logout, playback, showall
+int ttstati(), ttstats(), envfind()
+
+string unknown "[unknown]"
+string on "on"
+string off "off"
+
+begin
+ call smark (sp)
+ call salloc (val, SZ_FNAME, TY_CHAR)
+
+ ucasein = (ttstati (ttin, TT_UCASEIN) == YES)
+ ucaseout = (ttstati (ttin, TT_UCASEOUT) == YES)
+ shift = (ttstati (ttin, TT_SHIFTLOCK) == YES)
+ logio = (ttstati (ttin, TT_LOGIO) == YES)
+ login = (ttstati (ttin, TT_LOGIN) == YES)
+ logout = (ttstati (ttin, TT_LOGOUT) == YES)
+ playback = (ttstati (ttin, TT_PLAYBACK) == YES)
+ showall = (all == YES)
+
+ # Show tty environment variables.
+
+ if (envfind ("terminal", Memc[val], SZ_FNAME) <= 0)
+ call strcpy ("?", Memc[val], SZ_FNAME)
+ call fprintf (fd, "%s ")
+ call pargstr (Memc[val])
+
+ if (envfind ("ttyncols", Memc[val], SZ_FNAME) <= 0)
+ call strcpy ("?", Memc[val], SZ_FNAME)
+ call fprintf (fd, "ncols=%s ")
+ call pargstr (Memc[val])
+
+ if (envfind ("ttynlines", Memc[val], SZ_FNAME) <= 0)
+ call strcpy ("?", Memc[val], SZ_FNAME)
+ call fprintf (fd, "nlines=%s ")
+ call pargstr (Memc[val])
+
+ if (showall || ucasein || ucaseout) {
+ if (envfind ("ttybaud", Memc[val], SZ_FNAME) <= 0)
+ call strcpy ("?", Memc[val], SZ_FNAME)
+ call fprintf (fd, "baudrate=%s ")
+ call pargstr (Memc[val])
+
+ call fprintf (fd, "ucasein=%b ")
+ call pargb (ucasein)
+ call fprintf (fd, "ucaseout=%b ")
+ call pargb (ucaseout)
+ call fprintf (fd, "shift=%b\n")
+ call pargb (shift)
+ } else
+ call fprintf (fd, "\n")
+
+ # Show internal driver state variables in `show all' mode.
+
+ if (showall) {
+ call fprintf (fd,
+ "kichan=%d kochan=%d lichan=%d lochan=%d pbchan=%d ")
+ call pargi (ttstati (ttin, TT_KINCHAN))
+ call pargi (ttstati (ttin, TT_KOUTCHAN))
+ call pargi (ttstati (ttin, TT_LOGINCHAN))
+ call pargi (ttstati (ttin, TT_LOGOUTCHAN))
+ call pargi (ttstati (ttin, TT_PBINCHAN))
+
+ call fprintf (fd, "raw=%b passthru=%b\n")
+ call pargb (ttstati (ttin, TT_RAWMODE) == YES)
+ call pargb (ttstati (ttin, TT_PASSTHRU) == YES)
+ }
+
+ # Show the status of the logging options.
+
+ if (logio || showall) {
+ junk = ttstats (ttin, TT_IOFILE, Memc[val], SZ_FNAME)
+ call fprintf (fd, "logio=%s [%s]\n")
+ call pargstr (Memc[val])
+ if (logio)
+ call pargstr (on)
+ else
+ call pargstr (off)
+ }
+
+ if (!logio || showall) {
+ if (login || showall) {
+ junk = ttstats (ttin, TT_INFILE, Memc[val], SZ_FNAME)
+ call fprintf (fd, "login=%s [%s]\n")
+ call pargstr (Memc[val])
+ if (login)
+ call pargstr (on)
+ else
+ call pargstr (off)
+ }
+ if (logout || showall) {
+ junk = ttstats (ttin, TT_OUTFILE, Memc[val], SZ_FNAME)
+ call fprintf (fd, "logout=%s [%s]\n")
+ call pargstr (Memc[val])
+ if (logout)
+ call pargstr (on)
+ else
+ call pargstr (off)
+ }
+ }
+
+ if (playback || showall) {
+ junk = ttstats (ttin, TT_PBFILE, Memc[val], SZ_FNAME)
+ call fprintf (fd, "playback=%s [%s] ")
+ call pargstr (Memc[val])
+ if (playback)
+ call pargstr (on)
+ else
+ call pargstr (off)
+
+ call fprintf (fd, "verify=%b ")
+ call pargb (ttstati (ttin, TT_PBVERIFY) == YES)
+ call fprintf (fd, "delay=%d (msec)")
+ call pargi (ttstati (ttin, TT_PBDELAY))
+ call fprintf (fd, "\n")
+ }
+
+ if (playback) {
+ call fprintf (fd, "script recorded with terminal=%s, stdgraph=%s\n")
+ if (ttstats (ttin, TT_TDEVICE, Memc[val], SZ_FNAME) <= 0)
+ call pargstr (unknown)
+ else
+ call pargstr (Memc[val])
+ if (ttstats (ttin, TT_GDEVICE, Memc[val], SZ_FNAME) <= 0)
+ call pargstr (unknown)
+ else
+ call pargstr (Memc[val])
+ }
+
+ call sfree (sp)
+end
+
+
+# STTY_GETARG -- Get the next argument from an argument list. Arguments are of
+# the form keyw, keyw+, keyw-, keyw=(yes|y), keyw=(no|n), or keyw=value.
+# All forms are reduced to a flag YESNO and a value string VALUE. The forms
+# keyw=yes, key=no, etc., cause YESNO to be set but not VALUE. If only the
+# keyword name is given YESNO is set to YES and VALUE to the null string.
+# The number of characters in the keyword=value argument string is returned
+# as the function value, or EOF when the argument list is exhausted.
+
+int procedure stty_getarg (args, ip, keyw, maxkc, value, maxvc, defact, yesno)
+
+char args[ARB] # argument string
+int ip # index into argument string [RW]
+char keyw[ARB] # receives keyword name
+int maxkc # max chars in keyw string
+char value[ARB] # receives value string or EOS
+int maxvc # max chars in value string
+int defact # default action (yes/no) if only keyword given
+int yesno # boolean value of parameter
+
+int op
+int ip_save
+bool streq()
+
+begin
+ while (IS_WHITE (args[ip]))
+ ip = ip + 1
+
+ ip_save = ip
+
+ # Get keyword name.
+ for (op=1; IS_ALNUM (args[ip]); ip=ip+1) {
+ keyw[op] = args[ip]
+ op = min (maxkc, op + 1)
+ }
+ keyw[op] = EOS
+
+ while (IS_WHITE (args[ip]))
+ ip = ip + 1
+
+ value[1] = EOS
+ yesno = defact
+
+ if (args[ip] == '=') {
+ # Extract value string.
+ op = 1
+ for (ip=ip+1; args[ip] > ' '; ip=ip+1) {
+ value[op] = args[ip]
+ op = min (maxvc, op + 1)
+ }
+ value[op] = EOS
+
+ # Check for keyw=[yes|no].
+ if (streq (value, "yes") || streq (value, "y")) {
+ yesno = YES
+ value[1] = EOS
+ } else if (streq (value, "no") || streq (value, "n")) {
+ yesno = NO
+ value[1] = EOS
+ }
+ } else if (args[ip] == '+') {
+ yesno = YES
+ ip = ip + 1
+ } else if (args[ip] == '-') {
+ yesno = NO
+ ip = ip + 1
+ }
+
+ if (ip <= ip_save)
+ return (EOF)
+ else
+ return (ip - ip_save)
+end
diff --git a/sys/etc/syserr.x b/sys/etc/syserr.x
new file mode 100644
index 00000000..a14d516d
--- /dev/null
+++ b/sys/etc/syserr.x
@@ -0,0 +1,49 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define SZ_ERRMSG SZ_LINE
+
+# SYSERR -- Process a system error. No arguments; print only the error
+# message from the system error message file.
+
+procedure syserr (errcode)
+
+int errcode
+
+begin
+ call syserrs (errcode, "")
+end
+
+
+# SYSERRS -- System error, with a user supplied string argument. We do not
+# want to search the system error message file until ERRACT is called to
+# output the error message and initiate error recovery, because if an IFERR
+# error handler is posted the message will never be used. Hence we encode
+# an error message of the form "123 user_string", where "123" is the encoded
+# system error message number. If a message is ever actually output the
+# 123 will be expanded into a readable error message.
+
+procedure syserrs (errcode, user_string)
+
+int errcode
+char user_string[ARB]
+
+char buf[SZ_ERRMSG]
+int ip, op
+int itoc()
+
+begin
+ # Encode error code, to be used to search error message file.
+ op = itoc (errcode, buf, SZ_ERRMSG) + 1
+
+ if (user_string[1] != EOS) {
+ buf[op] = ' '
+ op = op + 1
+ for (ip=1; op <= SZ_ERRMSG && user_string[ip] != EOS; ip=ip+1) {
+ buf[op] = user_string[ip]
+ op = op + 1
+ }
+ }
+ buf[op] = EOS
+
+ call error (errcode, buf)
+end
diff --git a/sys/etc/sysid.x b/sys/etc/sysid.x
new file mode 100644
index 00000000..94bd77bc
--- /dev/null
+++ b/sys/etc/sysid.x
@@ -0,0 +1,57 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# SYSID -- Return a line of text identifying the current user, machine, and
+# version of IRAF, and containing the current date and time. The format is
+# as follows:
+#
+# NOAO/IRAF V1.3 username@lyra Tue 09:47:50 27-Aug-85
+#
+# The string "NOAO/IRAF V1.3" is given by the value of the environment variable
+# "version", defined in lib$clpackage.cl (unless redefined by the user). The
+# string "username" is the value of the environment variable "userid", defined
+# by the user in the login.cl file. The output string is not terminated by a
+# newline.
+
+procedure sysid (outstr, maxch)
+
+char outstr[maxch] # receives id string
+int maxch
+
+pointer sp, buf
+int op, nchars
+int envfind(), gstrcpy()
+long clktime()
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ nchars = envfind ("version", outstr, maxch)
+ if (nchars <= 0)
+ nchars = gstrcpy ("NOAO/IRAF", outstr, maxch)
+
+ op = nchars + 1
+ outstr[op] = ' '
+ op = op + 1
+
+ # The variable "userid" is defined in the user's login.cl file. This
+ # gives the user the opportunity to set the value of this string to
+ # something other than their host system login name.
+
+ nchars = envfind ("userid", Memc[buf], SZ_LINE)
+
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ outstr[op] = '@'
+ op = op + 1
+
+ call gethost (Memc[buf], SZ_LINE)
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ outstr[op] = ' '
+ op = op + 1
+
+ call cnvtime (clktime(long(0)), Memc[buf], SZ_LINE)
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ outstr[op] = EOS
+
+ call sfree (sp)
+end
diff --git a/sys/etc/syspanic.x b/sys/etc/syspanic.x
new file mode 100644
index 00000000..8ddfdb81
--- /dev/null
+++ b/sys/etc/syspanic.x
@@ -0,0 +1,17 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# SYS_PANIC -- Unconditionally abort process execution. Called when an error
+# condition occurs so serious that process execution cannot continue reliably.
+
+procedure sys_panic (errcode, errmsg)
+
+int errcode # error code
+char errmsg[ARB] # error message
+
+begin
+ # Since process termination is imminent we may as well overwrite the
+ # error message string by packing it in place.
+
+ call strpak (errmsg, errmsg, ARB)
+ call zpanic (errcode, errmsg)
+end
diff --git a/sys/etc/sysptime.x b/sys/etc/sysptime.x
new file mode 100644
index 00000000..3a2657c2
--- /dev/null
+++ b/sys/etc/sysptime.x
@@ -0,0 +1,84 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+define SZ_OBUF 8
+define CPU 1
+define CLK 2
+
+
+# SYS_MTIME -- Mark the time, i.e., save the current clock and cpu times in
+# the save buffer.
+
+procedure sys_mtime (save_time)
+
+long save_time[2] # mark time buffer
+
+begin
+ call zgtime (save_time[CLK], save_time[CPU])
+end
+
+
+# SYS_PTIME -- Print the cpu and clock time consumed since the last call to
+# SYS_MTIME.
+
+procedure sys_ptime (fd, opstr, save_time)
+
+int fd # output file
+char opstr[ARB] # optional operand name string
+long save_time[2] # mark time buffer
+
+int op, junk
+char obuf[SZ_OBUF]
+long new_clk, new_cpu
+int d_clk, d_cpu, msec, sec, percent
+int itoc()
+
+begin
+ call zgtime (new_clk, new_cpu)
+ d_clk = (new_clk - save_time[CLK]) # clk seconds
+ d_cpu = (new_cpu - save_time[CPU]) # cpu msec
+
+ call putline (fd, "Time ")
+ if (opstr[1] != EOS) {
+ call putci (fd, '(')
+ call putline (fd, opstr)
+ call putline (fd, ") ")
+ }
+
+ # Output the cpu time in seconds.
+ op = itoc (d_cpu / 1000, obuf, SZ_OBUF) + 1
+ obuf[op] = '.'; op = op + 1
+ msec = mod (d_cpu, 1000)
+ if (msec < 100) {
+ obuf[op] = '0'; op = op + 1
+ }
+ if (msec < 10) {
+ obuf[op] = '0'; op = op + 1
+ }
+ if (msec > 0)
+ op = op + itoc (msec, obuf[op], SZ_OBUF-op+1)
+ call putline (fd, obuf)
+
+ # Output the clock time in minutes and seconds.
+ call putci (fd, ' ')
+ op = itoc (d_clk / 60, obuf, SZ_OBUF) + 1
+ obuf[op] = ':'; op = op + 1
+ sec = mod (d_clk, 60)
+ obuf[op] = TO_DIGIT(sec/10); op = op + 1
+ obuf[op] = TO_DIGIT(mod(sec,10)); op = op + 1
+ obuf[op] = EOS
+ call putline (fd, obuf)
+
+ # Output the percent cpu utilization.
+ call putci (fd, ' ')
+ if (d_clk < 1)
+ call strcpy ("99", obuf, SZ_OBUF)
+ else {
+ percent = min (99, d_cpu / d_clk / 10)
+ junk = itoc (percent, obuf, SZ_OBUF)
+ }
+
+ call putline (fd, obuf)
+ call putline (fd, "%\n")
+end
diff --git a/sys/etc/tsleep.x b/sys/etc/tsleep.x
new file mode 100644
index 00000000..d1dbccb1
--- /dev/null
+++ b/sys/etc/tsleep.x
@@ -0,0 +1,13 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# TSLEEP -- Suspend execution of the calling task for the specified number
+# of seconds.
+
+procedure tsleep (seconds)
+
+int seconds
+
+begin
+ if (seconds > 0)
+ call zwmsec (seconds * 1000)
+end
diff --git a/sys/etc/ttopen.x b/sys/etc/ttopen.x
new file mode 100644
index 00000000..979d6f67
--- /dev/null
+++ b/sys/etc/ttopen.x
@@ -0,0 +1,96 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fset.h>
+
+# TTOPEN -- Open a terminal for direct i/o. The logical device "dev$tty"
+# denotes the user terminal. Note that this string is passed on to the
+# kernel without modification, despite the apparent use of a logical directory.
+# (See also fio$zfiott.x, the logical terminal driver).
+
+int procedure ttopen (terminal, mode)
+
+char terminal[ARB] # device to be opened
+int mode
+
+int fopntx()
+extern zopntt(), zgettt(), zputtt(), zflstt(), zstttt(), zclstt(),
+ zsektt(), znottt()
+
+begin
+ return (fopntx (terminal, mode, zopntt, zgettt, zputtt, zflstt,
+ zstttt, zclstt, zsektt, znottt))
+end
+
+
+# TTSETI -- Set special terminal driver options. The regular FIO options
+# are set using FSETI.
+
+procedure ttseti (fd, param, value)
+
+int fd # file descriptor
+int param # parameter to be set
+int value # new value
+
+int channel
+int fstati()
+
+begin
+ channel = fstati (fd, F_CHANNEL)
+ call zsettt (channel, param, value)
+end
+
+
+# TTSTATI -- Stat special terminal driver options.
+
+int procedure ttstati (fd, param)
+
+int fd # file descriptor
+int param # parameter to be set
+
+long lvalue
+int channel
+int fstati()
+
+begin
+ channel = fstati (fd, F_CHANNEL)
+ call zstttt (channel, param, lvalue)
+ return (lvalue)
+end
+
+
+# TTSETS -- Set special terminal driver option, type string. The regular FIO
+# options are set using FSETI.
+
+procedure ttsets (fd, param, svalue)
+
+int fd # file descriptor
+int param # parameter to be set
+char svalue[ARB] # new string value
+
+int channel
+int fstati()
+
+begin
+ channel = fstati (fd, F_CHANNEL)
+ call zsestt (channel, param, svalue)
+end
+
+
+# TTSTATS -- Stat special terminal driver options, type string.
+
+int procedure ttstats (fd, param, outstr, maxch)
+
+int fd # file descriptor
+int param # parameter to be set
+char outstr[maxch] # receives parameter value
+int maxch
+
+int nchars
+int channel
+int fstati()
+
+begin
+ channel = fstati (fd, F_CHANNEL)
+ call zststt (channel, param, outstr, maxch, nchars)
+ return (nchars)
+end
diff --git a/sys/etc/urlget.x b/sys/etc/urlget.x
new file mode 100644
index 00000000..23270fed
--- /dev/null
+++ b/sys/etc/urlget.x
@@ -0,0 +1,384 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include <mach.h>
+include <fset.h>
+
+
+# HTTP error codes we care about
+
+define HTTP_OK 200 # Success
+define HTTP_CREATED 201 # Created
+define HTTP_ACCEPTED 202 # Accepted
+define HTTP_PARTIAL 203 # Partial Information
+define HTTP_NORESP 204 # No Response
+
+define HTTP_MOVED 301 # Moved
+define HTTP_FOUND 302 # Found
+define HTTP_SEEOTHER 303 # Method
+define HTTP_NOTMOD 304 # Not Modified
+
+define HTTP_BADREQ 400 # Bad Request
+define HTTP_UNAUTH 401 # Unauthorized
+define HTTP_PAYMENT 402 # Payment Required
+define HTTP_FORBIDDEN 403 # Forbidden
+define HTTP_NOTFOUND 404 # Not Found
+
+define HTTP_INTERR 500 # Internal Error
+define HTTP_NOTIMP 501 # Not Implemented
+define HTTP_OVERLOAD 502 # Service Temporarily Overloaded
+define HTTP_GWTIMEOUT 503 # Gateway Timeout
+
+define SZ_BUF 8192 # download buffer
+
+define DBG_HDRS FALSE
+
+
+
+# URL_GET -- Do an HTTP GET on the given URL, save the results to the named
+# file. If a 'reply' pointer is given, return the request reply string (must
+# be allocated at least SZ_PATHNAME).
+
+int procedure url_get (url, fname, reply)
+
+char url[ARB] #i URL to access
+char fname[ARB] #i local filename
+pointer reply #u pointer to reply string
+
+char protocol[SZ_FNAME], host[SZ_FNAME], path[SZ_BUF], emsg[SZ_PATHNAME]
+char inurl[SZ_PATHNAME], outname[SZ_PATHNAME]
+int port, stat
+pointer buf
+
+int url_access(), strcmp()
+bool url_redirect()
+
+define redirect_ 99
+
+begin
+ # Breakup the URL into usable pieces.
+ call strcpy (url, inurl, SZ_PATHNAME)
+redirect_
+ call url_break (inurl, protocol, host, port, path)
+
+ # Check for a supported protocol.
+ if (strcmp (protocol, "http") != 0) {
+ call aclrc (emsg, SZ_PATHNAME)
+ call sprintf (emsg, SZ_PATHNAME, "Unsupported URI protocol (%s)")
+ call pargstr (protocol)
+ call error (0, emsg)
+ }
+
+ # Download the file to the given name
+ call strcpy (fname, outname, SZ_PATHNAME)
+
+ if (reply == NULL) {
+ call calloc (buf, SZ_LINE, TY_CHAR)
+ stat = url_access (host, port, path, outname, buf)
+ if (url_redirect (stat, buf, inurl)) { # check for a redirection
+ call mfree (buf, TY_CHAR)
+ goto redirect_
+ }
+ call mfree (buf, TY_CHAR)
+
+ } else {
+ stat = url_access (host, port, path, outname, reply)
+ if (url_redirect (stat, reply, inurl)) # check for a redirection
+ goto redirect_
+ }
+
+ # URL Error Codes are returned as negative values, positive values
+ # are the number of bytes read. We let the caller decode the return
+ # value, if desired, using the url_errcode() procedure.
+
+ return (stat)
+end
+
+
+# URL_REDIRECT -- Check for a redirection reply code and modify the URL so
+# we can try again.
+
+bool procedure url_redirect (stat, reply, url)
+
+int stat #i status code
+pointer reply #i pointer to reply string
+char url[ARB] #u access url
+
+int code, loc
+pointer ip, op
+char inurl[SZ_LINE]
+
+int strsearch()
+bool streq()
+
+begin
+ code = - stat
+
+ if (code == HTTP_MOVED || code == HTTP_FOUND || code == HTTP_SEEOTHER) {
+ loc = strsearch (Memc[reply], "Location:")
+ if (loc > 0) {
+ call aclrc (inurl, SZ_LINE)
+ call strcpy (url, inurl, SZ_LINE)
+ for (ip=reply+loc; IS_WHITE(Memc[ip]); ip=ip+1)
+ ;
+ for (op=1; Memc[ip] != '\n'; op=op+1) {
+ url[op] = Memc[ip]
+ ip = ip + 1
+ }
+ url[op-1] = EOS
+
+ if (streq (inurl, url))
+ return (FALSE)
+
+ return (TRUE)
+ }
+ }
+
+ return (FALSE)
+end
+
+
+# URL_BREAK -- Break the URL into components needed to make the netpath.
+
+procedure url_break (url, protocol, host, port, path)
+
+char url[SZ_BUF] #i url to parse
+char protocol[ARB] #o URL protocol (only HTTP, for now)
+char host[ARB] #o host name
+int port #o server port (if specified, or 80)
+char path[ARB] #o path part of URL, including args
+
+int i, nch, ip
+int ctoi()
+
+begin
+ port = 80 # set default port number
+
+ # Pull out the protocol part of the URL.
+ for (ip=1; url[ip] != ':'; ip = ip + 1)
+ protocol[ip] = url[ip]
+ protocol[ip] = '\0'
+
+ # Skip the "://" separator.
+ while (url[ip] == ':' || url[ip] == '/')
+ ip = ip + 1
+
+ # Get the host name.
+ for (i=1; url[ip] != ':' && url[ip] != '/' && url[ip] != EOS; i=i+1) {
+ host[i] = url[ip]
+ ip = ip + 1
+ }
+ host[i] = '\0'
+
+ if (url[ip] == EOS) {
+ call strcpy ("/", path, 2)
+ return
+ }
+
+ # Extract a port number of specified
+ if (url[ip] == ':') {
+ ip = ip + 1
+ nch = ctoi (url, ip, port)
+ }
+
+ # Get the remaining path.
+ for (i=1; url[ip] != EOS; i = i + 1) {
+ path[i] = url[ip]
+ ip = ip + 1
+ }
+ path[i] = '\0'
+end
+
+
+# URL_ACCESS -- Do an HTTP GET of a resource to the named file.
+
+int procedure url_access (host, port, path, fname, reply)
+
+char host[ARB] #i host name
+int port #i server port number
+char path[ARB] #i resource path
+char fname[ARB] #i saved file path
+pointer reply #i reply buffer
+
+pointer rep
+int in, out, nchars, totchars, retcode, clen, ip
+char buf[SZ_BUF], netpath[SZ_PATHNAME], request[SZ_BUF], hd[SZ_PATHNAME]
+bool done
+
+int open(), access(), ndopen(), getline(), read(), strlen(), ctoi()
+int strncmp(), url_retcode()
+
+begin
+ # Connect to server on the given host.
+ call sprintf (netpath, SZ_PATHNAME, "inet:%d:%s:%s")
+ call pargi (port)
+ call pargstr (host)
+ call pargstr ("text")
+
+ iferr (in = ndopen (netpath, READ_WRITE)) {
+ call eprintf ("cannot access host '%s:%d'\n")
+ call pargstr (host)
+ call pargi (port)
+ return (- HTTP_NOTFOUND)
+ }
+
+ # Format the request header.
+ call aclrc (request, SZ_BUF)
+ call sprintf (request, SZ_BUF, "GET %s HTTP/1.0\n")
+ call pargstr (path)
+ call strcat ("Accept: */*\n", request, SZ_BUF)
+ call strcat ("User-Agent: IRAF/urlget\n", request, SZ_BUF)
+ call strcat ("Host: ", request, SZ_BUF)
+ call strcat ( host, request, SZ_BUF)
+ call strcat ("\n", request, SZ_BUF)
+ call strcat ("Connection: keep-alive\n\n", request, SZ_BUF)
+
+ # Send the GET-url request to the server.
+ nchars = strlen (request)
+ call write (in, request, nchars)
+ call flush (in)
+ call fseti (in, F_CANCEL, OK)
+
+ if (DBG_HDRS) {
+ call eprintf ("request [%d]:\n%s\n")
+ call pargi (nchars)
+ call pargstr (request)
+ }
+
+ # Read the reply. Read the HTTP header assuming it ends with a \n or
+ # a \r\n. and then validate it will return the request correctly.
+ done = false
+ clen = -1
+ call calloc (rep, SZ_PATHNAME, TY_CHAR)
+ repeat {
+ call aclrc (hd, SZ_PATHNAME)
+ nchars = getline (in, hd)
+ if (nchars <= 0)
+ break
+ call strcat (hd, Memc[rep], SZ_PATHNAME)
+ if (strncmp (hd, "Content-Length:", 15) == 0) {
+ ip = 16
+ nchars = ctoi (hd, ip, clen)
+ }
+ } until ((hd[1] == '\r' && hd[2] == '\n') || (hd[1] == '\n'))
+
+ if (DBG_HDRS) {
+ call eprintf ("reply: %s\nclen = %d\n")
+ call pargstr (Memc[rep])
+ call pargi(clen)
+ }
+
+ # Make sure we have a valid file.
+ retcode = url_retcode (Memc[rep])
+
+ if (reply != NULL)
+ call strcpy (Memc[rep], Memc[reply], SZ_PATHNAME)
+ call mfree (rep, TY_CHAR)
+ if (retcode != HTTP_OK)
+ return (- retcode)
+
+
+ # Open the named output file.
+ if (access (fname, 0, 0) == YES)
+ call syserrs (SYS_FCLOBBER, fname)
+ iferr (out = open (fname, NEW_FILE, TEXT_FILE))
+ call syserrs (SYS_FOPEN, fname)
+
+ # Now read the resource and save it to the named file.
+ totchars = 0
+ done = false
+ repeat {
+ call aclrc (buf, SZ_BUF)
+ nchars = read (in, buf, SZ_BUF)
+ if (nchars > 0) {
+ call write (out, buf, nchars)
+ call flush (out)
+ totchars = totchars + nchars
+ done = false
+ } else
+ done = true
+
+ if (clen > 0 && totchars >= clen)
+ break
+ } until (done)
+
+ call close (in) # clean up
+ call close (out)
+
+ return (totchars) # return number of chars read
+end
+
+
+# URL_RETCODE -- Get the return code from the HTTP header reply.
+
+int procedure url_retcode (reply)
+
+char reply[ARB] #i reply string
+
+int ip, len, code, ctoi()
+
+begin
+ for (ip=1; !IS_WHITE(reply[ip]); ip=ip+1)
+ ;
+ len = ctoi (reply, ip, code)
+
+ return (code)
+end
+
+
+# URL_ERRCODE - Convert between an HTTP return code and the equivalent
+# syserr() code value.
+
+int procedure url_errcode (code)
+
+int code #i http return code
+
+begin
+ # Note: Not all error codes are implemented in syserr. In this
+ # case we just return the input code.
+
+ switch (code) {
+ case HTTP_OK: # Success
+ ;
+ case HTTP_CREATED: # Created
+ ;
+ case HTTP_ACCEPTED: # Accepted
+ ;
+ case HTTP_PARTIAL: # Partial Information
+ ;
+ case HTTP_NORESP: # No Response
+ ;
+
+ case HTTP_MOVED: # Moved
+ return (SYS_URLREDIRECT);
+ case HTTP_FOUND: # Found
+ return (SYS_URLREDIRECT);
+ case HTTP_SEEOTHER: # See Other
+ return (SYS_URLREDIRECT);
+ case HTTP_NOTMOD: # Not Modified
+ ;
+
+ case HTTP_BADREQ: # Bad Request
+ return (SYS_URLBADREQUEST)
+ case HTTP_UNAUTH: # Unauthorized
+ ;
+ case HTTP_PAYMENT: # Payment Required
+ ;
+ case HTTP_FORBIDDEN: # Forbidden
+ return (SYS_URLFORBIDDEN)
+ case HTTP_NOTFOUND: # Not Found
+ return (SYS_URLNOTFOUND)
+
+ case HTTP_INTERR: # Internal Error
+ return (SYS_URLINTERROR)
+ case HTTP_NOTIMP: # Not Implemented
+ ;
+ case HTTP_OVERLOAD: # Service Temporarily Overloaded
+ ;
+ case HTTP_GWTIMEOUT: # Gateway Timeout
+ ;
+ }
+
+ return (code)
+end
diff --git a/sys/etc/votable.x b/sys/etc/votable.x
new file mode 100644
index 00000000..ec931030
--- /dev/null
+++ b/sys/etc/votable.x
@@ -0,0 +1,304 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fio.h>
+include <votParse_spp.h>
+
+
+# VOTABLE.X -- Utility procedures for dealing with VOTables.
+
+
+# VOTINIT -- Initialize the VOT struct, parse the document and save the
+# summary. We return the VOT struct pointer itself, the caller is
+# responsible for accessing the VOT_ROOT to get the raw root handle
+
+pointer procedure votinit (votable)
+
+char votable[ARB] #i VOTable file name
+
+pointer vot, vot_handle
+
+# Declare the libVOTable functions we'll be using.
+int vx_openVOTABLE(), vx_getRESOURCE(), vx_getTABLE(), vx_getDATA()
+int vx_getTABLEDATA(), vx_getFIELD(), vx_getINFO(), vx_getPARAM()
+int vx_getNCols(), vx_getNRows(), vx_getLength()
+
+begin
+ # Allocate the structure.
+ call calloc (vot, SZ_VOT_STRUCT, TY_STRUCT)
+
+ # Open and parse the votable.
+ vot_handle = vx_openVOTABLE (votable)
+ if (vot_handle <= 0) {
+ call eprintf ("Cannot open file: '%s'\n")
+ call pargstr (votable)
+ return (NULL)
+ }
+ VOT_ROOT(vot) = vot_handle
+
+ # Now get various handles from the table.
+ VOT_RES(vot) = vx_getRESOURCE (vot_handle)
+ VOT_TAB(vot) = vx_getTABLE (VOT_RES(vot))
+ VOT_DATA(vot) = vx_getDATA (VOT_TAB(vot))
+ VOT_TDATA(vot) = vx_getTABLEDATA (VOT_DATA(vot))
+
+ VOT_INFO(vot) = vx_getINFO (VOT_RES(vot))
+ VOT_PARAM(vot) = vx_getPARAM (VOT_RES(vot))
+ VOT_FIELD(vot) = vx_getFIELD (VOT_TAB(vot))
+
+ VOT_NRES(vot) = vx_getLength (VOT_RES(vot))
+ VOT_NCOLS(vot) = vx_getNCols (VOT_TDATA(vot))
+ VOT_NROWS(vot) = vx_getNRows (VOT_TDATA(vot))
+
+ return (vot) # return the struct pointer
+end
+
+
+# VOTCLOSE -- Close the VOT struct and free any resources.
+
+procedure votclose (vot)
+
+pointer vot #i VOT struct pointer
+
+begin
+ call vx_closeVOTABLE (VOT_ROOT(vot))
+ call mfree (vot, TY_STRUCT)
+end
+
+# IS_VOTABLE -- Utility routine to determine if the named file is a VOTable
+# XML document.
+
+define VOT_MAXLINES 10
+
+bool procedure is_votable (fname)
+
+char fname[ARB] #i local filename
+
+int i, fd, nchars
+bool stat
+char buf[SZ_LINE]
+
+int open (), access (), getline (), strsearch ()
+
+begin
+ stat = FALSE
+
+ if (access (fname, 0, 0) == NO)
+ return (stat)
+
+ iferr {
+ fd = open (fname, READ_ONLY, TEXT_FILE)
+
+ # Look for a "<VOTABLE>" element in the first 10 lines of the file.
+ for (i=0; i < VOT_MAXLINES; i = i + 1) {
+ call aclrc (buf, SZ_LINE)
+ nchars = getline (fd, buf)
+ if (nchars == EOF)
+ break
+
+ call strupr (buf)
+ if (strsearch (buf, "<VOTABLE") > 0)
+ stat = TRUE
+ }
+ call close (fd)
+ } then
+ stat = FALSE
+
+ return (stat)
+end
+
+
+# VOT_CONVERT -- Convert a VOTable to some other format.
+
+define VOT_FMTS "|ascii|asv|bsv|csv|tsv|html|shtml|fits|xml|raw|votable"
+
+define ASCII 1 # ascii separated values
+define ASV 2 # ascii separated values
+define BSV 3 # bar separated values
+define CSV 4 # comma separated values
+define TSV 5 # tab separated values
+define HTML 6 # standalone HTML document
+define SHTML 7 # single HTML <table> element
+define FITS 8 # FITS binary table
+define XML 9 # VOTable alias
+define RAW 10 # " "
+define VOTBL 11 # " "
+
+int procedure vot_convert (in, out, fmt)
+
+char in[ARB] #i VOTable file name
+char out[ARB] #i FITS bintable file name
+char fmt[ARB] #i format name
+
+pointer sp, nodename, buf
+char osfn[SZ_PATHNAME], cnvname[SZ_PATHNAME], format[SZ_LINE]
+int vfd, status, ip, opt, delim, infile, outfile
+
+int vfnopen(), vfnmapu(), access(), ki_gnode(), strdic(), strncmp()
+int open(), getline()
+bool streq()
+
+begin
+ call smark (sp)
+ call salloc (nodename, SZ_FNAME, TY_CHAR)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+
+ # Map input VFN to OSFN.
+ ip = 1
+ if (strncmp (in, "http://", 7) == 0) {
+ call strcpy (in, osfn, SZ_PATHNAME)
+ } else {
+ vfd = vfnopen (in, READ_ONLY)
+ status = vfnmapu (vfd, osfn, SZ_PATHNAME)
+ call vfnclose (vfd, VFN_NOUPDATE)
+
+ # If the file resides on the local node strip the node name,
+ # returning a legal host system filename as the result.
+ if (ki_gnode (osfn, Memc[nodename], delim) == 0)
+ ip = delim + 1
+ }
+
+ # Create a tempfile name for the converted output file.
+ call mktemp ("/tmp/vo", cnvname, SZ_PATHNAME)
+ call strcat (".", cnvname, SZ_PATHNAME)
+ call strcat (fmt, cnvname, SZ_PATHNAME)
+
+
+ # Validate the format.
+ opt = strdic (fmt, format, SZ_LINE, VOT_FMTS)
+ if (opt == 0) {
+ call eprintf ("Invalid output format '%s'\n")
+ call pargstr (fmt)
+ call sfree (sp)
+ return (ERR)
+ }
+ if (opt == VOTBL || opt == XML || opt == RAW)
+ call strcpy ("vot", format, SZ_FNAME)
+ if (opt == ASCII)
+ call strcpy ("asv", format, SZ_FNAME)
+
+
+ # Convert the file from VOTable to FITS bintable.
+ call vx_vocopy (5, "-f", format, "-o", cnvname, osfn[ip])
+
+ if (access (cnvname,0,0) == NO) {
+ call eprintf ("Cannot convert %s to '%s'\n")
+ call pargstr (osfn[ip])
+ call pargstr (fmt)
+ return (ERR)
+ }
+
+ # Delete the downloaded XML file, copy the bintable into its
+ # place and delete the converted output filename.
+ if (streq (in, out))
+ call delete (in)
+
+
+ # Copy converted file to output file. Works for STDOUT/STDERR as
+ # well.
+ infile = open (cnvname, READ_ONLY, TEXT_FILE)
+ outfile = open (out, NEW_FILE, TEXT_FILE)
+
+ while (getline (infile, Memc[buf]) != EOF)
+ call putline (outfile, Memc[buf])
+
+ call close (infile)
+ call close (outfile)
+
+ call delete (cnvname) # delete the temporary converted file
+ call sfree (sp)
+ return (OK)
+end
+
+
+# VOT_TO_FITS -- Convert a VOTable to a FITS bintable.
+
+int procedure vot_to_fits (in, out)
+
+char in[ARB] #i VOTable file name
+char out[ARB] #i FITS bintable file name
+
+pointer sp, nodename
+char osfn[SZ_PATHNAME], cnvname[SZ_PATHNAME]
+int vfd, status, ip, delim
+
+int vfnopen(), vfnmapu(), access(), ki_gnode()
+bool streq()
+
+begin
+ call smark (sp)
+ call salloc (nodename, SZ_FNAME, TY_CHAR)
+
+ # Map input VFN to OSFN.
+ vfd = vfnopen (in, READ_ONLY)
+ status = vfnmapu (vfd, osfn, SZ_PATHNAME)
+ call vfnclose (vfd, VFN_NOUPDATE)
+
+ # If the file resides on the local node strip the node name,
+ # returning a legal host system filename as the result.
+ if (ki_gnode (osfn, Memc[nodename], delim) == 0)
+ ip = delim + 1
+ else
+ ip = 1
+
+
+ # Create a tempfile name for the converted output file.
+ call mktemp ("/tmp/vo", cnvname, SZ_PATHNAME)
+ call strcat (".fits", cnvname, SZ_PATHNAME)
+
+ # Convert the file from VOTable to FITS bintable.
+ call vx_vocopy (5, "-f", "fits", "-o", cnvname, osfn[ip])
+
+ if (access (cnvname,0,0) == NO)
+ return (ERR)
+
+ # Delete the downloaded XML file, copy the bintable into its
+ # place and delete the converted output filename.
+ if (streq (in, out))
+ call delete (in)
+
+ call fcopy (cnvname, out) # copy converted file to output file
+ call delete (cnvname) # delete the temporary converted file
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# VOT_FROM_FITS -- Convert from a FITS bintable to a VOTable.
+
+int procedure vot_from_fits (in, out)
+
+char in[ARB] #i FITS bintable file name
+char out[ARB] #i VOTable file name
+
+char osfn[SZ_PATHNAME], cnvname[SZ_PATHNAME]
+int vfd, status
+
+int vfnopen(), vfnmapu()
+bool streq()
+
+begin
+ # Map input VFN to OSFN.
+ vfd = vfnopen (in, READ_ONLY)
+ status = vfnmapu (vfd, osfn, SZ_PATHNAME)
+ call vfnclose (vfd, VFN_NOUPDATE)
+
+ # Create a tempfile name for the converted output file.
+ call mktemp ("/tmp/vo", cnvname, SZ_PATHNAME)
+ call strcat (".xml", cnvname, SZ_PATHNAME)
+
+ # Convert the file from VOTable to FITS bintable.
+ call vx_vocopy (5, "-f", "votable", "-o", cnvname, osfn)
+
+ # Delete the downloaded XML file, copy the bintable into its
+ # place and delete the converted output filename.
+ if (streq (in, out))
+ call delete (in)
+
+ call fcopy (cnvname, out) # copy converted file to output file
+ call delete (cnvname) # delete the temporary converted file
+
+ return (OK)
+end
diff --git a/sys/etc/xalloc.x b/sys/etc/xalloc.x
new file mode 100644
index 00000000..63be577a
--- /dev/null
+++ b/sys/etc/xalloc.x
@@ -0,0 +1,197 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <xalloc.h>
+include <syserr.h>
+include <ctype.h>
+include <knet.h>
+
+.helpsys xalloc
+.nf _________________________________________________________________________
+XALLOC -- Device allocation package.
+
+ xallocate (device)
+ xdeallocate (device, rewind)
+ xdevowner (device, owner, maxch)
+ xdevstatus (device, out)
+ xgdevlist (device, devlist, maxch, onedev)
+
+status:
+
+ DV_DEVFREE device is free and can be allocated
+ DV_DEVALLOC device is already allocated
+ DV_DEVINUSE device is in use by someone else
+ DV_DEVNOTFOUND device is not in device table
+
+The allocatable devices are defined in the text file dev$tapecap.
+.endhelp ____________________________________________________________________
+
+define SZ_DEVLIST 256
+define ALLOCATE 1
+define DEALLOCATE 0
+
+
+# XALLOCATE -- Attempt to allocate the named device, i.e., allocate the device
+# for exclusive i/o, and ready it for i/o following some sort of OPEN call.
+# Allocate performs the function called "mount" on some systems, as well as
+# allocating the device.
+
+int procedure xallocate (device)
+
+char device[ARB] #I device to be allocated
+
+pointer sp, devlist
+int status, onedev
+int xgdevlist(), mtfile()
+errchk xgdevlist, mtallocate
+define done_ 91
+
+begin
+ call smark (sp)
+ call salloc (devlist, SZ_DEVLIST, TY_CHAR)
+
+ # Fetch the device list for the named device.
+ onedev = NO
+ status = xgdevlist (device, Memc[devlist], SZ_DEVLIST, onedev)
+ if (status != OK)
+ goto done_
+
+ # Attempt to allocate the device at the host system level.
+ call strpak (Memc[devlist], Memc[devlist], SZ_DEVLIST)
+ call zdvall (Memc[devlist], ALLOCATE, status)
+
+ # If that worked and the device is a magtape, call MTIO to complete
+ # the allocation process.
+
+ if (status == OK && mtfile (device) == YES)
+ call mtallocate (device)
+done_
+ call sfree (sp)
+ return (status)
+end
+
+
+# XDEALLOCATE -- Deallocate the named device.
+
+int procedure xdeallocate (device, rewind)
+
+char device[ARB] #I device to be deallocated
+int rewind #I rewind if magtape?
+
+int status, onedev
+pointer sp, devlist, osdev, owner
+int xgdevlist(), mtfile()
+errchk xgdevlist, syserrs
+define done_ 91
+
+begin
+ call smark (sp)
+ call salloc (devlist, SZ_DEVLIST, TY_CHAR)
+ call salloc (osdev, SZ_FNAME, TY_CHAR)
+ call salloc (owner, SZ_FNAME, TY_CHAR)
+
+ # Get the i/o device name.
+ onedev = YES
+ status = xgdevlist (device, Memc[osdev], SZ_FNAME, onedev)
+ if (status != OK)
+ goto done_
+
+ # Verify that the device is actually allocated. If the device is a
+ # magtape, call MTIO to conditionally rewind the drive and deallocate
+ # the drive in MTIO.
+
+ call strpak (Memc[osdev], Memc[osdev], SZ_FNAME)
+ call zdvown (Memc[osdev], Memc[owner], SZ_FNAME, status)
+ if (status != DV_DEVALLOC)
+ call syserrs (SYS_MTNOTALLOC, device)
+ else if (mtfile (device) == YES)
+ call mtdeallocate (device, rewind)
+
+ # Fetch the device list for the named device.
+ onedev = NO
+ status = xgdevlist (device, Memc[devlist], SZ_DEVLIST, onedev)
+ if (status != OK)
+ goto done_
+
+ # Physically deallocate the device.
+ call strpak (Memc[devlist], Memc[devlist], SZ_DEVLIST)
+ call zdvall (Memc[devlist], DEALLOCATE, status)
+done_
+ call sfree (sp)
+ return (status)
+end
+
+
+# XDEVSTATUS -- Print the status of the named device on the output file.
+
+procedure xdevstatus (device, out)
+
+char device[ARB] #I device
+int out #I output file
+
+int status
+char owner[SZ_FNAME]
+int xdevowner(), mtfile()
+errchk xdevowner, mtfile
+
+begin
+ status = xdevowner (device, owner, SZ_FNAME)
+
+ switch (status) {
+ case DV_DEVFREE:
+ call fprintf (out, "device %s is not currently allocated\n")
+ call pargstr (device)
+ if (mtfile (device) == YES)
+ iferr (call mtstatus (out, device))
+ ;
+ case DV_DEVINUSE:
+ call fprintf (out, "device %s is currently allocated to %s\n")
+ call pargstr (device)
+ call pargstr (owner)
+ case DV_DEVALLOC:
+ if (mtfile (device) == YES)
+ call mtstatus (out, device)
+ else {
+ call fprintf (out, "device %s is allocated\n")
+ call pargstr (device)
+ }
+ default:
+ call fprintf (out, "cannot get device status for `%s'\n")
+ call pargstr (device)
+ }
+end
+
+
+# XDEVOWNER -- Determine whether or not the named device is already
+# allocated, and if the device is currently allocated to someone else,
+# return the owner name.
+
+int procedure xdevowner (device, owner, maxch)
+
+char device[ARB] #I device to be deallocated
+char owner[maxch] #O receives owner name
+int maxch #I max chars out
+
+pointer sp, devlist
+int status, onedev
+int xgdevlist()
+errchk xgdevlist
+define done_ 91
+
+begin
+ call smark (sp)
+ call salloc (devlist, SZ_DEVLIST, TY_CHAR)
+
+ # Fetch the device list for the named device.
+ onedev = YES
+ status = xgdevlist (device, Memc[devlist], SZ_DEVLIST, onedev)
+ if (status != OK)
+ goto done_
+
+ # Query device allocation.
+ call strpak (Memc[devlist], Memc[devlist], SZ_DEVLIST)
+ call zdvown (Memc[devlist], owner, maxch, status)
+ call strupk (owner, owner, maxch)
+done_
+ call sfree (sp)
+ return (status)
+end
diff --git a/sys/etc/xerfmt.x b/sys/etc/xerfmt.x
new file mode 100644
index 00000000..596e6ce5
--- /dev/null
+++ b/sys/etc/xerfmt.x
@@ -0,0 +1,96 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# XER_FMTERRMSG -- Expand error message encoded as "123 user_string" into
+# a full error message by looking the error message string up in lib$syserrmsg.
+# If the first character of ERRMSG is nonnumeric no processing is done.
+# The conversion may be performed in place, i.e., errmsg and outstr may be
+# the same array.
+
+procedure xer_fmterrmsg (errmsg, outstr, maxch)
+
+char errmsg[ARB] # encoded error message.
+char outstr[maxch] # output string
+int maxch
+
+char buf[SZ_LINE], user_string[SZ_FNAME]
+int codelen, nchars, chan, ip, op, junk
+int strncmp(), envfind(), gstrcpy()
+define nofile_ 91
+
+begin
+ # Determine ndigits in error code.
+ for (ip=1; IS_DIGIT (errmsg[ip]); ip=ip+1)
+ ;
+ codelen = ip - 1
+
+ # Output message as is if no error code. Copy into local buf first
+ # in case errmsg and outstr overlap.
+
+ if (codelen == 0) {
+ call strcpy (errmsg, buf, SZ_LINE)
+ call strcpy (buf, outstr, maxch)
+ return
+ }
+
+ # Extract the user string into a local buffer.
+ while (IS_WHITE (errmsg[ip]))
+ ip = ip + 1
+ for (op=1; errmsg[ip] != EOS && errmsg[ip] != '\n'; ip=ip+1) {
+ user_string[op] = errmsg[ip]
+ op = op + 1
+ }
+ user_string[op] = EOS
+
+ # Generate the OS pathname of the "lib$syserrmsg" file.
+ if (envfind ("iraf", buf, SZ_LINE) > 0) {
+ call zfsubd (buf, SZ_LINE, "lib", nchars)
+ call strcat ("syserrmsg", buf, SZ_LINE)
+ call strpak (buf, buf, SZ_LINE)
+ } else
+ goto nofile_
+
+ # Open and search the system error message file.
+ call zopntx (buf, READ_ONLY, chan)
+ if (chan == ERR)
+ goto nofile_
+
+ repeat {
+ call zgettx (chan, buf, SZ_LINE, nchars)
+ if (nchars == 0 || nchars == ERR) {
+ call zclstx (chan, junk)
+ goto nofile_
+ } else if (strncmp (buf, errmsg, codelen) == 0) {
+ call zclstx (chan, junk)
+ break
+ }
+ }
+
+ # Skip the error code prefix and the blank which follows.
+ for (ip=codelen+1; IS_WHITE(buf[ip]); ip=ip+1)
+ ;
+
+ # Output system error message.
+ for (op=1; buf[ip] != '\n' && buf[ip] != EOS; ip=ip+1) {
+ outstr[op] = buf[ip]
+ op = op + 1
+ }
+
+ # Add user operand, if supplied, enclosed in parens.
+ if (user_string[1] != EOS) {
+ outstr[op] = ' '
+ outstr[op+1] = '('
+ op = op + 2
+ op = op + gstrcpy (user_string, outstr[op], maxch-op+1)
+ outstr[op] = ')'
+ op = op + 1
+ }
+
+ outstr[op] = EOS
+ return
+
+nofile_
+ call strcpy (errmsg, buf, SZ_LINE)
+ call strcpy (buf, outstr, maxch)
+end
diff --git a/sys/etc/xerpop.x b/sys/etc/xerpop.x
new file mode 100644
index 00000000..a4723508
--- /dev/null
+++ b/sys/etc/xerpop.x
@@ -0,0 +1,55 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+
+# XERPSH -- Push an error handler on the error "stack". All we really need
+# do is keep track of the number of nested handlers. If an error condition
+# already exists when we are called, an error has occurred which was not
+# caught, probably because of a missing errchk declaration.
+
+procedure xerpsh()
+
+include "error.com"
+
+begin
+ if (xerflg) # error not caught
+ call erract (EA_FATAL)
+ nhandlers = nhandlers + 1
+ xercod = OK
+end
+
+
+# XERPOP -- Pop an error handler, and return the error status flag (true if
+# an error occurred).
+
+bool procedure xerpop()
+
+bool error_status
+include "error.com"
+
+begin
+ nhandlers = nhandlers - 1
+ error_status = xerflg
+ xerflg = false
+
+ return (error_status)
+end
+
+
+# XERPOPI -- Integer version of XERPOP.
+
+int procedure xerpopi()
+
+bool error_status
+include "error.com"
+
+begin
+ nhandlers = nhandlers - 1
+ error_status = xerflg
+ xerflg = false
+
+ if (error_status)
+ return (1)
+ else
+ return (0)
+end
diff --git a/sys/etc/xerpue.x b/sys/etc/xerpue.x
new file mode 100644
index 00000000..9cc995ee
--- /dev/null
+++ b/sys/etc/xerpue.x
@@ -0,0 +1,32 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+include <mach.h>
+include <fio.h>
+
+# XER_PUTLINE -- Put a line to the output file (STDERR), using only low level
+# routines. It is important to use run time indirection through the device
+# table here, to avoid linking the entire IPC and KI into non-IRAF programs
+# that use error handlers, e.g., HSI or IMFORT programs.
+
+procedure xer_putline (fd, text)
+
+int fd
+char text[ARB]
+
+long offset
+int nchars, junk
+int strlen()
+include <fio.com>
+
+begin
+ nchars = strlen (text)
+ fp = fiodes[fd]
+
+ if (FTYPE(fp) == BINARY_FILE) {
+ offset = 0
+ call zcall4 (ZAWRBF(fp), FCHAN(fp), text, nchars * SZB_CHAR, offset)
+ call zcall2 (ZAWTBF(fp), FCHAN(fp), junk)
+ } else
+ call zcall4 (ZPUTTX(fp), FCHAN(fp), text, nchars, junk)
+end
diff --git a/sys/etc/xerreset.x b/sys/etc/xerreset.x
new file mode 100644
index 00000000..773e38f9
--- /dev/null
+++ b/sys/etc/xerreset.x
@@ -0,0 +1,19 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+
+# XER_RESET -- Called to initialize error handling. Used during startup and
+# during error recovery (e.g. in an interrupt handler) to reset the state of
+# the error handling code.
+
+procedure xer_reset()
+
+include "error.com"
+
+begin
+ xerflg = false
+ xercod = OK
+ err_restart = NO
+ nhandlers = 0
+ xermsg[1] = EOS
+end
diff --git a/sys/etc/xerstmt.x b/sys/etc/xerstmt.x
new file mode 100644
index 00000000..0a28167b
--- /dev/null
+++ b/sys/etc/xerstmt.x
@@ -0,0 +1,66 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <error.h>
+include <ctype.h>
+
+define SZ_NUMBUF 6
+
+# XERSTMT -- Format and issue an error statement to the CL. Note that this is
+# a command issued to the CL, not a line written to STDERR. The error code and
+# error message string output were posted in the last call to ERROR or FATAL.
+#
+# Example: ERROR (501, "Access Violation")
+#
+# The actual concatentation and transmission of the error message is carried
+# out by the primitive XERPUTC, rather than by PUTLINE and PUTC calls to CLOUT,
+# to avoid recursion in the FIO routines, probably leading to error recursion.
+
+procedure xer_send_error_statement_to_cl (errcode)
+
+int errcode
+char numbuf[SZ_NUMBUF]
+int ip, junk, itoc()
+include "error.com"
+
+begin
+ # The error code is passed as an argument rather than taken from the
+ # xercom common because XERPOP clears the error code before we are
+ # called by the IRAF Main.
+
+ junk = itoc (errcode, numbuf, SZ_NUMBUF)
+
+ # Format the ERROR statement and sent it to the CL.
+
+ call xerpstr ("ERROR (")
+ call xerpstr (numbuf)
+ call xerpstr (", \"")
+
+ # Output error message string, omitting characters like newline or
+ # quote which could cause syntax problems.
+
+ for (ip=1; xermsg[ip] != EOS; ip=ip+1)
+ if (IS_PRINT (xermsg[ip]) && xermsg[ip] != '"')
+ call xerputc (xermsg[ip])
+
+ # Ring terminal bell if unexpected error (anything other than
+ # a keyboard interrupt).
+
+ if (xercod != SYS_XINT)
+ call xerpstr ("\7")
+ call xerpstr ("\")\n")
+end
+
+
+# XERPSTR -- Put a string to the CL (special routine, to avoid recursion).
+# Use PUTLINE in normal code.
+
+procedure xerpstr (str)
+
+char str[ARB]
+int ip
+
+begin
+ for (ip=1; str[ip] != EOS; ip=ip+1)
+ call xerputc (str[ip])
+end
diff --git a/sys/etc/xerverify.x b/sys/etc/xerverify.x
new file mode 100644
index 00000000..dfb4e88a
--- /dev/null
+++ b/sys/etc/xerverify.x
@@ -0,0 +1,21 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+
+# XER_VERIFY -- The following procedure is called by the iraf main after
+# a task completes, to verify that NHANDLERS is zero, indicating that an XERPOP
+# was executed for each XERPSH. Note that a transfer out of an IFERR block
+# (a programming error) could prevent XERPOP from being called.
+
+procedure xer_verify()
+
+include "error.com"
+
+begin
+ if (xerflg)
+ call erract (EA_FATAL)
+ if (nhandlers != 0) {
+ nhandlers = 0
+ call putline (STDERR, "Warning: Transfer out of IFERR block\n")
+ }
+end
diff --git a/sys/etc/xgdevlist.x b/sys/etc/xgdevlist.x
new file mode 100644
index 00000000..d20b68a9
--- /dev/null
+++ b/sys/etc/xgdevlist.x
@@ -0,0 +1,49 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <xalloc.h>
+
+# XGDEVLIST -- Fetch the allocation string for the named logical device from
+# the device table (tapecap file). DV_DEVNOTFOUND is returned there is no
+# entry in the device table for the device. An error action is taken if there
+# is any problem reading the device entry.
+#
+# This routine is a bit of an anachronism in the days of tapecap, but is
+# left pretty much as it was originally to minimize code modifications.
+# In principle the allocation code can be used to allocate any device, not
+# just tape drives. This is still the case, given an entry for the device
+# in the tapecap file.
+
+int procedure xgdevlist (device, outstr, maxch, onedev)
+
+char device[ARB] #I logical device name
+char outstr[maxch] #O receives device list
+int maxch #I max chars out
+int onedev #I return i/o device instead?
+
+pointer gty
+int nchars
+pointer mtcap()
+int gtygets(), strlen()
+errchk syserrs
+
+begin
+ # Fetch the tapecap entry for the named device. Do not close the GTY
+ # descriptor. mtcap always keeps the last one in an internal cache.
+
+ iferr (gty = mtcap (device))
+ return (DV_DEVNOTFOUND)
+
+ if (onedev == YES)
+ nchars = gtygets (gty, "dv", outstr, maxch)
+ else
+ nchars = gtygets (gty, "al", outstr, maxch)
+
+ call ki_xnode (device, outstr, maxch)
+ nchars = strlen (outstr)
+
+ if (nchars <= 0)
+ call syserrs (SYS_MTTAPECAP, device)
+
+ return (OK)
+end
diff --git a/sys/etc/xisatty.x b/sys/etc/xisatty.x
new file mode 100644
index 00000000..177bec67
--- /dev/null
+++ b/sys/etc/xisatty.x
@@ -0,0 +1,38 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <clset.h>
+include <fset.h>
+
+# XISATTY -- Test if the given file is a terminal.
+
+int procedure xisatty (fd)
+
+int fd # file descriptor of candidate device
+int epa, epa_tt, epa_ty
+extern zgettt(), zgetty()
+int fstati(), clstati()
+
+begin
+ # If we are a connected subprocess, the referenced file is a standard
+ # stream, and i/o has not been redirected, assume that the file behaves
+ # as a terminal.
+
+ if (clstati(CL_PRTYPE) == PR_CONNECTED)
+ if (fd == STDIN || fd == STDOUT || fd == STDERR)
+ if (fstati (fd, F_REDIR) == NO)
+ return (YES)
+ else
+ return (NO)
+
+ # Otherwise, the use of the terminal driver tells us if the file is
+ # open on a terminal device.
+
+ epa = fstati (fd, F_DEVICE)
+ call zlocpr (zgettt, epa_tt)
+ call zlocpr (zgetty, epa_ty)
+
+ if (epa == epa_tt || epa == epa_ty)
+ return (YES)
+ else
+ return (NO)
+end
diff --git a/sys/etc/xmjbuf.x b/sys/etc/xmjbuf.x
new file mode 100644
index 00000000..45585060
--- /dev/null
+++ b/sys/etc/xmjbuf.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <config.h>
+
+# XMJBUF -- Return a char pointer to the IRAF main interpreter context restart
+# buffer (for ZDOJMP restarts).
+
+procedure xmjbuf (bp)
+
+pointer bp #O pointer to jumpbuf
+
+int a_jb, a_mem
+int jumpbuf[LEN_JUMPBUF]
+common /JUMPCOM/ jumpbuf
+
+begin
+ call zlocva (jumpbuf[1], a_jb)
+ call zlocva (Memc, a_mem)
+ bp = a_jb - a_mem + 1
+end
diff --git a/sys/etc/xttysize.x b/sys/etc/xttysize.x
new file mode 100644
index 00000000..4c95056e
--- /dev/null
+++ b/sys/etc/xttysize.x
@@ -0,0 +1,51 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <clset.h>
+
+# XTTYSIZE -- Query the size of the terminal screen in characters. This is
+# different than simply reading the screen size from the environment or from
+# termcap, because the screen size will be queried at runtime if the terminal
+# has a screen which can change size at runtime. Note that when this routine
+# is called, the variables ttyncols and ttynlines are updated in the IRAF
+# environment, allowing ordinary envgeti calls to be used thereafter to query
+# the screen size. The XTTYSIZE routine should not be called all over the
+# place, because it may involve i/o to the terminal.
+
+procedure xttysize (width, height)
+
+int width # width of screen (out)
+int height # height of screen (out)
+
+int junk, i
+pointer sp, buf, tty
+pointer ttyodes()
+int clstati(), getline(), envgeti(), envscan()
+errchk clcmd, getline, envgeti, ttyodes
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ # If we are a connected subprocess it is difficult to write directly
+ # to the terminal, since the terminal is opened by the CL. Hence we
+ # have the CL run the STTY task instead to reset the screen size
+ # parameters in the environment. If we are not a connected subprocess
+ # we query the terminal size directly, assuming that the terminal is
+ # opened on the process standard input and output.
+
+ if (clstati (CL_PRTYPE) == PR_CONNECTED) {
+ call clcmd ("stty resize")
+ do i = 1, 2
+ if (getline (CLIN, Memc[buf]) != EOF)
+ junk = envscan (Memc[buf])
+ width = envgeti ("ttyncols")
+ height = envgeti ("ttynlines")
+
+ } else {
+ tty = ttyodes ("terminal")
+ call ttygsize (STDIN, STDOUT, tty, width, height)
+ call ttycdes (tty)
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/etc/xwhen.x b/sys/etc/xwhen.x
new file mode 100644
index 00000000..948eca8f
--- /dev/null
+++ b/sys/etc/xwhen.x
@@ -0,0 +1,13 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# XWHEN -- Post an exception handler.
+
+procedure xwhen (signal, handler, old_handler)
+
+int signal # signal to be caught
+int handler # epa of user supplied exception handler
+int old_handler # epa of old handler, if any
+
+begin
+ call zxwhen (signal, handler, old_handler)
+end
diff --git a/sys/etc/zzdebug.x b/sys/etc/zzdebug.x
new file mode 100644
index 00000000..05324872
--- /dev/null
+++ b/sys/etc/zzdebug.x
@@ -0,0 +1,404 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fset.h>
+include <chars.h>
+
+# Debug the ENVIRON environment list package. The following definitions are
+# from the header of "environ.x" and are used by envdebug to examine the
+# environment list data structures; these should be compared to the defs in
+# environ.x to make sure they agree. Use of a header file is not warranted
+# since we really do not want the environ.x data structures known outside
+# the package.
+
+task get = t_get,
+ put = t_put,
+ list = t_list,
+ mark = t_mark,
+ free = t_free,
+ debug = t_debug,
+ spawn = t_spawn,
+ edit = t_edit,
+ tty = t_tty,
+ urlget = t_urlget
+
+
+# Strings may optionally be quoted in SET stmts with either ' or ".
+define IS_QUOTE ($1 == '\'' || $1 == '"')
+
+# Size limiting definitions.
+
+define NTHREADS 100 # number of hash threads
+define HASH_FACTOR 1637 # divisor for hash function
+define NHASHCHARS 6 # no. chars used for hashing
+define LEN_ENVBUF 1500 # storage for environment list
+define INC_ENVBUF 500 # increment if overflow occurs
+define MAX_SZKEY 32 # max chars in a key
+define MAX_SZVALUE 80 # max chars in value string
+define MAX_LENLISTELEM (3+(MAX_SZKEY+1+MAX_SZVALUE+1+SZ_SHORT-1)/SZ_SHORT)
+
+# List element structure, stored in ENVBUF, which is allocated as an array of
+# type SHORT integer. Each list element is aligned on a short integer boundary
+# within the array. E_NEXT points to the next element in a thread, whereas
+# E_LASTELEM points to the last element in the envbuf (which is a stack).
+
+define E_NEXT Mems[$1] # next element in thread (list)
+define E_LASTELEM Mems[$1+1] # next element in envbuf
+define E_REDEF Mems[$1+2] # set if element is redefined
+define E_SETP P2C($1+3) # char pointer to name field
+define E_SET Memc[E_SETP($1)] # "name=value" string
+define E_SETOFFSET 3
+
+
+# GET -- Lookup the definition of an environment variable.
+
+procedure t_get()
+
+char name[SZ_FNAME]
+char value[SZ_LINE]
+int envgets()
+
+begin
+ call clgstr ("name", name, SZ_FNAME)
+ if (envgets (name, value, SZ_LINE) <= 0) {
+ call printf ("%s not found\n")
+ call pargstr (name)
+ } else {
+ call printf ("%s = %s\n")
+ call pargstr (name)
+ call pargstr (value)
+ }
+end
+
+
+# PUT -- Enter a new environment variable or list of variables into the
+# environment list. Enter "stmt: set name=value" to enter a single variable,
+# or "stmt: set @filename" to process set statements from a file.
+
+procedure t_put()
+
+char stmt[SZ_LINE]
+int envscan()
+
+begin
+ call clgstr ("statement", stmt, SZ_LINE)
+ call printf ("%d set statements processed\n")
+ call pargi (envscan (stmt))
+end
+
+
+# LIST -- Print the environment list.
+
+procedure t_list()
+
+bool clgetb()
+int btoi()
+
+begin
+ call envlist (STDOUT, " ", btoi (clgetb ("show_redefs")))
+end
+
+
+# MARK -- Mark the end of the environment list for later restoration by
+# the FREE task.
+
+procedure t_mark()
+
+int top
+common /xxx/ top
+
+begin
+ call envmark (top)
+ call printf ("top = %d\n")
+ call pargi (top)
+end
+
+
+# FREE -- Free the environment list back to the last position marked.
+
+procedure t_free()
+
+int top
+int envfree()
+common /xxx/ top
+
+begin
+ call printf ("free uncovers %d redefs\n")
+ call pargi (envfree (top, 0))
+end
+
+
+# DEBUG -- Print the internal data structures (the hash table) of the
+# environment list package.
+
+procedure t_debug()
+
+begin
+ call envdebug (STDOUT)
+end
+
+
+# ENVDEBUG -- Print the contents of the environment list data structures for
+# debugging the code.
+
+procedure envdebug (fd)
+
+int fd # output file
+int i, t, head
+pointer el, ep
+include "environ.com"
+
+begin
+ call fprintf (fd, "envbuf at %d, len %d, last=%d, top=%d, %d%% full\n")
+ call pargi (envbuf)
+ call pargi (len_envbuf)
+ call pargi (last)
+ call pargi (top)
+ call pargr (real(top) / real(len_envbuf) * 100.0)
+
+ for (t=1; t <= NTHREADS; t=t+1) {
+ call fprintf (fd, "%6d"); call pargi (t)
+ head = threads[t]
+ if (head != NULL)
+ for (i=head; i != NULL; i=E_NEXT(el)) {
+ el = envbuf + i
+ call putci (fd, ' ')
+ for (ep=E_SETP(el); Memc[ep] != '='; ep=ep+1)
+ call putc (fd, Memc[ep])
+ }
+ call putci (fd, '\n')
+ }
+end
+
+
+# SPAWN -- Spawn a connected subprocess. Used to test process control and
+# interprocess communication.
+
+procedure t_spawn()
+
+char process[SZ_FNAME]
+char lbuf[SZ_LINE]
+int in, out, pid
+int prgetline(), propen(), prclose(), strmatch()
+define done_ 91
+
+begin
+ call clgstr ("process", process, SZ_FNAME)
+ pid = propen (process, in, out)
+
+ call putline (STDERR, "-> ")
+ call flush (STDERR)
+
+ while (prgetline (STDIN, lbuf) != EOF) {
+ if (strmatch (lbuf, "^bye") > 0)
+ break
+ else {
+ call putline (out, lbuf)
+ call flush (out)
+ }
+
+ while (prgetline (in, lbuf) != EOF) {
+ call putline (STDERR, lbuf)
+
+ if (strmatch (lbuf, "^bye") > 0)
+ break
+ else {
+ call putline (STDERR, ">> ")
+ call flush (STDERR)
+ if (prgetline (STDIN, lbuf) == EOF)
+ goto done_
+ call putline (out, lbuf)
+ }
+
+ call flush (STDERR)
+ call flush (out)
+ }
+
+ call putline (STDERR, "------------\n")
+ call putline (STDERR, "-> ")
+ call flush (STDERR)
+ }
+
+done_
+ call putline (STDERR, "\n")
+ call eprintf ("termination code %d\n")
+ call pargi (prclose (pid))
+end
+
+
+# EDIT -- Test raw mode to a terminal.
+
+procedure t_edit()
+
+char lbuf[SZ_LINE], temp[SZ_LINE], ch
+int i, stdline
+
+char getchar()
+int envgets(), ttygeti()
+pointer tty, ttyodes()
+define accum_ 91
+define done_ 92
+
+begin
+ # Set terminal to raw mode.
+ call fseti (STDIN, F_RAW, YES)
+
+ # Open termcap for terminal.
+ if (envgets ("terminal", lbuf, SZ_LINE) <= 0)
+ call strcpy ("vt100", lbuf, SZ_LINE)
+ tty = ttyodes (lbuf)
+ stdline = ttygeti (tty, "li")
+
+ # Edit loop. The variable I is the character position within the
+ # line. Start out in insert mode, with line displayed at bottom
+ # of terminal screen.
+
+ lbuf[1] = EOS
+ i = 1
+ call ttygoto (STDOUT, tty, 1, stdline)
+ call flush (STDOUT)
+ goto accum_
+
+ while (getchar (ch) != EOF) {
+ switch (ch) {
+
+ case 'h':
+ # Move left one column.
+ if (i <= 1)
+ call putci (STDOUT, BEL)
+ else {
+ call putci (STDOUT, BS)
+ i = i - 1
+ }
+
+ case 'l':
+ # Move right one column.
+ if (lbuf[i+1] == EOS)
+ call putci (STDOUT, BEL)
+ else {
+ call putc (STDOUT, lbuf[i])
+ i = i + 1
+ }
+
+ case 'x':
+ # Delete a character.
+ call strcpy (lbuf[i+1], lbuf[i], SZ_LINE-i+1)
+ call putline (STDOUT, lbuf[i])
+ call putci (STDOUT, BLANK)
+ call ttygoto (STDOUT, tty, i, STDLINE)
+
+ if (i > 1 && lbuf[i] == EOS) {
+ call putci (STDOUT, BS)
+ i = i - 1
+ }
+
+ case 'i':
+ # Insert a character.
+accum_
+ while (getchar (ch) != ESC) {
+ call putc (STDOUT, ch)
+ if (ch == '\r')
+ goto done_
+
+ # Insert char in line buffer.
+ call strcpy (lbuf[i], temp, SZ_LINE)
+ lbuf[i] = ch
+ i = i + 1
+ call strcpy (temp, lbuf[i], SZ_LINE-i+1)
+
+ # Redraw right portion of line.
+ call putline (STDOUT, lbuf[i])
+ call ttygoto (STDOUT, tty, i, STDLINE)
+ call flush (STDOUT)
+ }
+
+ if (i > 1) {
+ call putci (STDOUT, BS)
+ i = i - 1
+ }
+
+ case '\f':
+ # Redraw line.
+ call printf ("\r%s")
+ call pargstr (lbuf)
+ call ttygoto (STDOUT, tty, i, STDLINE)
+
+ case '\r':
+ break
+
+ default:
+ call putci (STDOUT, BEL)
+ }
+
+ call flush (STDOUT)
+ }
+
+done_
+ call fseti (STDIN, F_RAW, NO)
+ call putci (STDOUT, '\n')
+ call ttycdes (tty)
+end
+
+
+# TTY -- Test direct terminal i/o.
+
+procedure t_tty()
+
+int in, out, ch
+int ttopen(), getci()
+bool clgetb()
+
+begin
+ if (clgetb ("dualstreams")) {
+ in = ttopen ("dev$tty", READ_ONLY)
+ out = ttopen ("dev$tty", WRITE_ONLY)
+ } else {
+ in = ttopen ("dev$tty", READ_WRITE) # NOT SUPPORTED
+ out = in
+ }
+
+ call fseti (in, F_RAW, YES)
+ while (getci (in, ch) > 0) {
+ call fprintf (out, "%c\r\n")
+ call pargi (ch)
+ call flush (out)
+ if (ch == EOFCHAR)
+ break
+ }
+
+ if (in == out)
+ call close (in)
+ else {
+ call close (out)
+ call close (in)
+ }
+end
+
+
+# URL_GET -- Do an HTTP GET of a URL
+
+procedure t_urlget ()
+
+pointer reply
+char url[SZ_LINE], fname[SZ_FNAME]
+bool hdr
+int nread
+
+int url_get()
+
+begin
+ call clgstr ("url", url, SZ_LINE) # get the parameters
+ call clgstr ("fname", fname, SZ_FNAME)
+ hdr = clgetb ("hdr")
+
+ call calloc (reply, SZ_LINE, TY_CHAR)
+
+ nread = url_get (url, fname, reply)
+
+ call eprintf ("File '%s', downloaded %d bytes.\n")
+ call pargstr (fname)
+ call pargi (nread)
+
+ if (hdr)
+ call eprintf (Memc[reply])
+ call mfree (reply, TY_CHAR)
+end