aboutsummaryrefslogtreecommitdiff
path: root/sys/qpoe
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/qpoe
downloadiraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz
Initial commit
Diffstat (limited to 'sys/qpoe')
-rw-r--r--sys/qpoe/QPDEFS60
-rw-r--r--sys/qpoe/QPOE.hlp1201
-rw-r--r--sys/qpoe/README323
-rw-r--r--sys/qpoe/gen/mkpkg47
-rw-r--r--sys/qpoe/gen/qpaddb.x29
-rw-r--r--sys/qpoe/gen/qpaddc.x29
-rw-r--r--sys/qpoe/gen/qpaddd.x29
-rw-r--r--sys/qpoe/gen/qpaddi.x29
-rw-r--r--sys/qpoe/gen/qpaddl.x29
-rw-r--r--sys/qpoe/gen/qpaddr.x29
-rw-r--r--sys/qpoe/gen/qpadds.x29
-rw-r--r--sys/qpoe/gen/qpaddx.x29
-rw-r--r--sys/qpoe/gen/qpexattrld.x127
-rw-r--r--sys/qpoe/gen/qpexattrli.x127
-rw-r--r--sys/qpoe/gen/qpexattrlr.x127
-rw-r--r--sys/qpoe/gen/qpexcoded.x370
-rw-r--r--sys/qpoe/gen/qpexcodei.x423
-rw-r--r--sys/qpoe/gen/qpexcoder.x368
-rw-r--r--sys/qpoe/gen/qpexparsed.x372
-rw-r--r--sys/qpoe/gen/qpexparsei.x363
-rw-r--r--sys/qpoe/gen/qpexparser.x372
-rw-r--r--sys/qpoe/gen/qpexsubd.x63
-rw-r--r--sys/qpoe/gen/qpexsubi.x63
-rw-r--r--sys/qpoe/gen/qpexsubr.x63
-rw-r--r--sys/qpoe/gen/qpgetc.x63
-rw-r--r--sys/qpoe/gen/qpgetd.x63
-rw-r--r--sys/qpoe/gen/qpgeti.x63
-rw-r--r--sys/qpoe/gen/qpgetl.x63
-rw-r--r--sys/qpoe/gen/qpgetr.x63
-rw-r--r--sys/qpoe/gen/qpgets.x63
-rw-r--r--sys/qpoe/gen/qpiogetev.x1968
-rw-r--r--sys/qpoe/gen/qpiorpixi.x150
-rw-r--r--sys/qpoe/gen/qpiorpixs.x150
-rw-r--r--sys/qpoe/gen/qpputc.x74
-rw-r--r--sys/qpoe/gen/qpputd.x74
-rw-r--r--sys/qpoe/gen/qpputi.x74
-rw-r--r--sys/qpoe/gen/qpputl.x74
-rw-r--r--sys/qpoe/gen/qpputr.x74
-rw-r--r--sys/qpoe/gen/qpputs.x74
-rw-r--r--sys/qpoe/gen/qprlmerged.x134
-rw-r--r--sys/qpoe/gen/qprlmergei.x134
-rw-r--r--sys/qpoe/gen/qprlmerger.x134
-rw-r--r--sys/qpoe/mkpkg133
-rw-r--r--sys/qpoe/qpaccess.x26
-rw-r--r--sys/qpoe/qpaccessf.x24
-rw-r--r--sys/qpoe/qpadd.gx29
-rw-r--r--sys/qpoe/qpaddf.x173
-rw-r--r--sys/qpoe/qpastr.x35
-rw-r--r--sys/qpoe/qpbind.x48
-rw-r--r--sys/qpoe/qpclose.x26
-rw-r--r--sys/qpoe/qpcopy.x28
-rw-r--r--sys/qpoe/qpcopyf.x48
-rw-r--r--sys/qpoe/qpctod.x34
-rw-r--r--sys/qpoe/qpctoi.x34
-rw-r--r--sys/qpoe/qpdelete.x20
-rw-r--r--sys/qpoe/qpdeletef.x35
-rw-r--r--sys/qpoe/qpdsym.x56
-rw-r--r--sys/qpoe/qpdtype.x57
-rw-r--r--sys/qpoe/qpelsize.x20
-rw-r--r--sys/qpoe/qpex.h164
-rw-r--r--sys/qpoe/qpexattrl.gx127
-rw-r--r--sys/qpoe/qpexclose.x25
-rw-r--r--sys/qpoe/qpexcode.gx484
-rw-r--r--sys/qpoe/qpexdata.x210
-rw-r--r--sys/qpoe/qpexdebug.x441
-rw-r--r--sys/qpoe/qpexdel.x58
-rw-r--r--sys/qpoe/qpexeval.x362
-rw-r--r--sys/qpoe/qpexgetat.x61
-rw-r--r--sys/qpoe/qpexgetfil.x50
-rw-r--r--sys/qpoe/qpexmodfil.x247
-rw-r--r--sys/qpoe/qpexopen.x67
-rw-r--r--sys/qpoe/qpexpand.x60
-rw-r--r--sys/qpoe/qpexparse.gx410
-rw-r--r--sys/qpoe/qpexsub.gx67
-rw-r--r--sys/qpoe/qpget.gx67
-rw-r--r--sys/qpoe/qpgetb.x26
-rw-r--r--sys/qpoe/qpgettok.x687
-rw-r--r--sys/qpoe/qpgetx.x26
-rw-r--r--sys/qpoe/qpgmsym.x76
-rw-r--r--sys/qpoe/qpgnfn.x240
-rw-r--r--sys/qpoe/qpgpar.x101
-rw-r--r--sys/qpoe/qpgpsym.x90
-rw-r--r--sys/qpoe/qpgstr.x42
-rw-r--r--sys/qpoe/qpinherit.x57
-rw-r--r--sys/qpoe/qpio.h140
-rw-r--r--sys/qpoe/qpioclose.x49
-rw-r--r--sys/qpoe/qpiogetev.gx467
-rw-r--r--sys/qpoe/qpiogetfil.x123
-rw-r--r--sys/qpoe/qpiogetrg.x19
-rw-r--r--sys/qpoe/qpiolmask.x119
-rw-r--r--sys/qpoe/qpiolwcs.x50
-rw-r--r--sys/qpoe/qpiomkidx.x299
-rw-r--r--sys/qpoe/qpioopen.x392
-rw-r--r--sys/qpoe/qpioparse.x374
-rw-r--r--sys/qpoe/qpioputev.x104
-rw-r--r--sys/qpoe/qpiorb.x44
-rw-r--r--sys/qpoe/qpiorpix.gx86
-rw-r--r--sys/qpoe/qpiosetfil.x59
-rw-r--r--sys/qpoe/qpioseti.x90
-rw-r--r--sys/qpoe/qpiosetr.x30
-rw-r--r--sys/qpoe/qpiosetrg.x34
-rw-r--r--sys/qpoe/qpiostati.x84
-rw-r--r--sys/qpoe/qpiostatr.x29
-rw-r--r--sys/qpoe/qpiosync.x78
-rw-r--r--sys/qpoe/qpiowb.x131
-rw-r--r--sys/qpoe/qplenf.x26
-rw-r--r--sys/qpoe/qploadwcs.x38
-rw-r--r--sys/qpoe/qpmacro.x832
-rw-r--r--sys/qpoe/qpmkfname.x23
-rw-r--r--sys/qpoe/qpoe.h115
-rw-r--r--sys/qpoe/qpopen.x132
-rw-r--r--sys/qpoe/qpparse.x70
-rw-r--r--sys/qpoe/qpparsefl.x149
-rw-r--r--sys/qpoe/qppclose.x27
-rw-r--r--sys/qpoe/qppopen.x62
-rw-r--r--sys/qpoe/qpppar.x136
-rw-r--r--sys/qpoe/qppstr.x47
-rw-r--r--sys/qpoe/qpput.gx74
-rw-r--r--sys/qpoe/qpputb.x31
-rw-r--r--sys/qpoe/qpputx.x31
-rw-r--r--sys/qpoe/qpqueryf.x91
-rw-r--r--sys/qpoe/qpread.x80
-rw-r--r--sys/qpoe/qprebuild.x21
-rw-r--r--sys/qpoe/qprename.x25
-rw-r--r--sys/qpoe/qprenamef.x48
-rw-r--r--sys/qpoe/qprlmerge.gx134
-rw-r--r--sys/qpoe/qpsavewcs.x35
-rw-r--r--sys/qpoe/qpseti.x62
-rw-r--r--sys/qpoe/qpsetr.x24
-rw-r--r--sys/qpoe/qpsizeof.x46
-rw-r--r--sys/qpoe/qpstati.x76
-rw-r--r--sys/qpoe/qpstatr.x29
-rw-r--r--sys/qpoe/qpsync.x51
-rw-r--r--sys/qpoe/qpwrite.x79
-rw-r--r--sys/qpoe/zzdebug.x1696
135 files changed, 20355 insertions, 0 deletions
diff --git a/sys/qpoe/QPDEFS b/sys/qpoe/QPDEFS
new file mode 100644
index 00000000..863337fd
--- /dev/null
+++ b/sys/qpoe/QPDEFS
@@ -0,0 +1,60 @@
+# QPDEFS -- SAMPLE global macro definition file for QPOE. This should be
+# modified (and a copy placed somewhere outside the QPOE sources) to establish
+# the defaults for a given site. Each user may also have their own private
+# copy of this file. The environment variable `qmfiles' defines the list of
+# QPDEFS files to be read to define the runtime environment for QPOE.
+
+# Interface parameters (defaults shown).
+#set bucketlen 1024
+#set cachesize 8
+#set indexlen 100
+#set maxlfiles 128
+#set pagesize 512
+#set sbufsize 2048
+#set stablen 2048
+#set progbuflen 1024
+#set databuflen 4096
+#set maxfrlutlen 8192
+#set maxrrlutlen 1024
+#set lutminranges 5
+#set lutscale 15
+#set maxpushback 8192
+#set blockfactor 8
+#set optbufsize 524288
+#set debuglevel 0
+
+set lutscale 32
+set blockfactor 4
+
+# The event structure used in qpoe$zzdebug.x for s/w testing.
+define evfields {s:x,s:y,s,s,d,s,s}
+define x s0
+define y s2
+define pha s4
+define pi s6
+define time d8
+define dx s16
+define dy s18
+
+# An alias.
+define t time
+
+# Some test filters.
+define box x=(400:800),y=(200:400)
+define dbox dx=(400:800),dy=(200:400)
+define eventimes (30:31,32:33,34:35,36:37,38:39,40:41,42:43)
+define oddtimes (31:32,33:34,35:36,37:38,39:40,41:42,43:44)
+define alltimes (eventimes,oddtimes)
+
+# The following is for integer LUT tests; a bitwise test would be more
+# suitable for testing for an even integer.
+
+define even (2,4,6,8,10,12,14,16,18,20,22,24,26,28,30)
+
+# Test argument substitution in macros.
+define eq $1=($2)
+define ne $1=!($2)
+define le $1=(:$2)
+define lt $1=(!($2:))
+define ge $1=($2:)
+define gt $1=(!(:$2))
diff --git a/sys/qpoe/QPOE.hlp b/sys/qpoe/QPOE.hlp
new file mode 100644
index 00000000..cbb0ee5b
--- /dev/null
+++ b/sys/qpoe/QPOE.hlp
@@ -0,0 +1,1201 @@
+.help QPOE Jun90 "Quick POE Design"
+.ce
+\fBQuick-POE (Position Ordered Event File) Interface Design\fR
+.ce
+Doug Tody
+.ce
+July, 1988
+
+.NH
+Introduction
+
+ The POE (Position Ordered Event file) facility is an interface and file
+structure used to store and access the event (photon) lists generated
+by event counting detectors. Each event is described by a unique position,
+time, energy, and possibly other parameters (e.g., polarization, position in
+other coordinate systems, or instrument related parameters). In the case of
+an imaging event counting detector, the "image" generated consists of this list
+of discrete events, rather than the regular matrix produced by a conventional
+sampling detector. Both types of detectors are fundamental to astronomy.
+
+The POE interface is a stand alone interface built upon the standard VOS
+interfaces DFIO (in a future release), PLIO, SYMTAB, FIO, and other lower
+level interfaces. The POE interface may be called directly by applications
+code to create and access POE datafiles, for event file specific processing.
+In addition, an IMIO image kernel is provided so that POE files may be
+accessed as (read only) images, allowing existing IRAF image tasks to be
+used to access POE files. The main function of the POE image kernel is to
+filter and sample the event list in real time, returning a conventional
+sampled grid (image matrix) to the high level applications code.
+The parameters controlling the filtering and sampling operations may be
+specified by the user when the image (POE file) is accessed, making runtime
+filtering of events possible in connection with any general image processing
+task.
+
+.NH 2
+Important Concepts
+
+ The primary object dealt with by this interface is the \fIevent file\fR,
+consisting of a free format \fIfile header\fR and the main \fIevent list\fR.
+The event list is a collection of \fIevent structures\fR, e.g., photons
+hitting an imaging detector during the period of observation recorded by the
+event file. Each event is characterized by a standard set of attributes such
+as the position of the event in detector, sky, or other coordinates, the time
+at which the event was recorded, the energy of the event, and so on, plus
+optionally additional instrument dependent attributes (in general the event
+structure cannot be fixed, and the nomenclature may vary depending upon the
+science being performed).
+
+The events may appear in the event list in any order, but since most access
+to image data tends to be spatial in nature, access will be most efficient if
+the event list is position ordered. This is the convention chosen by QPOE
+and hence the name \fIpoe\fRfile, or \fIp\fRosition \fIo\fRrdered \fIe\fRvent
+file. An important alternative ordering is time ordering, which preserves
+the order in which the events were originally recorded, but which requires
+a complete scan of the event list to accumulate the events in a region of
+interest during analysis. There are cases where time ordering might be
+preferable to position ordering, e.g., for time series analysis of a long
+observation.
+
+Of fundamental importance to the analysis of data from event counting
+detectors is the concept of \fIfiltering\fR. It is in the nature of event
+counting detectors that they are often used to observe very faint objects
+for very long periods of integration. The total amount of data (number of
+events) may be limited, so one wants to preserve all events, but since the
+quality of the data may vary both with time and with position, it is common
+to want to reject a portion of the data. Conversely, the analysis being
+performed may require one to examine only a portion of the data, e.g., only
+events with a certain range of energies or arrival times, occurring within a
+given region of the image. Often the analysis will be repeated many times
+wtih different filters.
+
+Hence, most analysis of event counting data typically involves both
+\fIrejection\fR and \fIregion of interest\fR filtering. Rejection filtering
+depends mostly upon the data itself, hence the rejection filter for an image
+is a part of the image and should be in effect by default whenever the image
+is accessed, although we would like to physically record all data and be able
+to change the rejection filter either temporarily or indefinitely if desired.
+Region of interest filtering, on the other hand, depends upon the scientific
+analysis being performed rather than upon the data, hence is highly variable
+and should be controlled by the user, independently of the data.
+
+.NH 2
+Interface Requirements
+
+ Given the description of the problem to be solved presented in the previous
+section, we can make the following observations regarding the POE interface:
+.ls 4 o
+A flexible binary file header supporting both scalar and variable length vector
+fields is essential. Examples of vector fields include the aspect and temporal
+records (actually arrays of records, or subtables), the processing history
+(probably stored as a single variable length text buffer), and the rejection
+and region of interest filters (PLIO external format byte sequences stored as
+opaque binary arrays).
+.le
+.ls o
+In the general case the details of the event structure depend upon the
+instrument for which data is being stored. The minimum requirement is that
+the event structure consist of a set of standard fields (x, y, time, energy)
+followed by a variable length, instrument dependent area (hence the size of
+the event structure, while fixed for a given datafile, should be allowed to
+vary depending upon the data). Ideally all fields should be named and
+accessible for filtering, the names chosen should be variables rather than
+constants, and the set of fields used to describe an event should be allowed
+to vary depending upon the data.
+.le
+.ls o
+Runtime access to the event list, including event-attribute and spatial
+filtering, should be as efficient as possible since this is likely to be
+by far the most time consuming part of the interface. Header access
+efficiency is much less important and is not expected to be a problem.
+.le
+.ls o
+For most efficient access the event list should be stored sorted upon some
+primary key, with an index maintained by the interface for that key,
+and used for efficient retrieval. The minimum requirement is that the primary
+or sort key be the Y coordinate (corresponding to image lines). Ideally it
+should be possible for the sort key to be any field of the event structure,
+or any combination of fields (e.g., Y+X, or T). Ideally the interface itself
+should be responsible for maintaining the event list in sort order; this is
+not a requirement since writing to event files is much less common than
+reading. Ideally it should be possible for the event list to be unordered,
+and it should be possible to transparently access the event list regardless
+of the ordering.
+.le
+.ls o
+Rejection filtering is typically required by the data, yet we wish to retain
+all the data and be able to override or replace the default rejection mask.
+The rejection filter is logically associated with the data and should be
+stored with the data. The minimum requirement is for the interface to be able
+to store all the data plus the rejection mask, and be able to return only
+the "good" data at runtime. The interface is not required to perform rejection
+filtering at runtime, although it would be desirable to be able to do so if
+the efficiency penalty were not too great. Alternatively, the event list
+could be prefiltered, and the "good" and "bad" events stored in different
+places in the datafile, requiring that the entire file be rebuilt to change
+the rejection filter.
+.le
+.ls o
+Region of interest filtering is a common operation for event data, and can be
+difficult to implement efficiently, hence should be supported directly by the
+interface. At a minimum it should be possible to filter events by defining a
+range of acceptable or unacceptable values for each of some subset of the event
+attributes (e.g., energy or time). Ideally it should also be possible to
+specify an arbitrarily long list of ranges of acceptable values. Ideally
+spatial filtering should be supported as well; this is required for rejection
+filtering, but is at most a desirable option for spatial region of interest
+filtering (there is nothing about spatial filtering which is unique to event
+data, hence it might be more appropriate to implement it at a higher level,
+but on the other hand it might be more efficient to implement it at the event
+i/o level since the event list is position ordered and can be very large).
+.le
+.ls o
+It should be possible to specify the various filtering options both
+transparently to applications programs (via a symbolic expression passed
+into the interface by the user as part of the file specification),
+or procedurally, via \fIset\fR-parameter calls issued by the client program.
+.le
+
+The above issues need to be adequately addressed in order to have a useful
+interface. In the longer term there are many other considerations, e.g.,
+it is also desirable for the data format to be machine independent, and the
+data format should be flexible, to accommodate the inevitable evolutionary
+revisions as well as to accommodate data from a variety of instruments or
+projects. The event files can be very large, hence efficiency is a major
+consideration for event i/o and filtering.
+
+.NH
+The QPOE Interface
+.NH 2
+Implementation Strategy
+
+ A two step operation is planned for implementing the POE interface within
+IRAF. The first step is the so called quick-POE interface. The objective of
+quick-POE is to provide the necessary functionality so that applications
+development can proceed immediately, without waiting for the general interface
+to be developed. Once quick-POE is in place development of the fully general
+POE interface can proceed at a more leisurely pace consistent with the plan to
+provide most of the functionality of the generalized POE interface with other
+standard IRAF facilities currently under development, most notably the datafile
+i/o interface (DFIO), a general purpose binary file record manager to be used
+in the new images structures project and elsewhere in IRAF.
+
+The main facilities provided by POE are for access to general header fields,
+access to the variable length aspect and temporal records, event list i/o,
+and event filtering. The POE header is a type of record, and the aspect and
+temporal "records" are arrays of records (tables), as is the event list itself.
+Any record access problem involving large records commonly involves the
+related problems of indexing and selection based on a user supplied predicate
+(boolean expression or filter in our case).
+
+In the general case, we would like the POE interface to be able to support
+data from a variety of detectors or instruments, not only those used in high
+energy astrophysics but those used in optical and radio observatories as well,
+hence the details of the data structures must be allowed to vary without
+affecting the interface itself. Ideally the POE files should be maintained
+in a machine independent format, and the applications programs using POE
+should not be affected by changes to the data format, and indeed should be
+usable directly with any of several similar data formats, or with data formats
+that evolve over time.
+
+All these observations lead us to conclude that a general implementation of
+the POE interface has much in common with the general record access problem,
+hence the association of POE with DFIO. Quick-POE will provide much the same
+functionality at the applications level but will be less general, i.e., the
+binary record structures as seen by applications will be mapped directly onto
+external storage (the main contribution of DFIO is data independence and
+flexibility). This means that initially the applications will be tied to
+a specific format datafile, changes to applications structures will require
+reformatting the data, and the datafiles will probably be machine dependent.
+
+This approach would be unacceptable in the long run, as the need arises to
+support a variety of instruments, but is viable for initial applications
+development provided a DFIO based implementation eventually replaces the
+initial interface. There should be little difference in terms of functionality
+and efficiency between QPOE and POE; in fact QPOE may have the edge over POE
+in terms of efficiency, since it will be less general. It is likely that
+applications developed for QPOE will be usable with POE with few if any
+changes, e.g., by reimplementing QPOE as a layer on top of the more general
+POE interface. The main motivation for implementing a DFIO based POE will be
+to provide increased data independence, a machine independent binary data
+format, and the ability to support a variety of instruments with a single
+interface.
+
+Although some throw away code will have to be written to implement the QPOE
+interface, most of the complexity of the interface lies in the event filtering
+code, which should be reusable in the final interface. Low level, custom
+(non-DFIO) selection code is required for POE due to the unusual requirements
+for region and temporal filtering, and the potentially extremely high data
+volume (>10**6 event records). This implies that DFIO itself will have to be
+a layered interface, supporting low level access to the packed data records
+for applications with unusual efficiency requirements (it will be). Finally,
+it is already evident that the low level file manager required by QPOE has
+much in common with the access method code planned for DFIO, hence can serve
+as a prototype for the DFIO file manager.
+
+The remainder of this document will deal only with the details of the
+QPOE interface. The DFIO interface has already been specified,
+including a sketch of a data definition for a POE file. See \fIThe IRAF
+Datafile I/O Interface\fR, February 1988. The region filtering
+code in POE will make use of the Pixel List I/O (PLIO) interface, described
+in \fIThe IRAF Pixel List Package\fR, February 1988. The latter interface
+has already been implemented, as have all other interfaces (e.g., SYMTAB)
+required by QPOE.
+
+.NH 2
+Architecture
+
+ The architecture of IRAF as it pertains to the QPOE (and POE) interface
+is summarized in the figure below.
+
+.ks
+.nf
+ IMIO
+ IKI
+ IK-POE
+ POE
+ PLIO
+ [DFIO]
+ SYMTAB
+ FIO
+.fi
+.ke
+
+As indicated in the figure, QPOE depends most heavily on the VOS interfaces
+PLIO (pixel list i/o), used for spatial filtering, SYMTAB (the general symbol
+table package), used to manage the file header and aspect and temporal records,
+and FIO (file i/o), used to access the binary file in
+which the QPOE data is stored (low level, unbuffered asynchronous i/o is
+used by the QPOE file manager). The QPOE interface is accessed both directly
+by applications code, and by the IMIO interface via an image kernel, shown
+as IK-POE in the figure. IK-POE and QPOE comprise the code to be written to
+implement the QPOE interface.
+
+The QPOE interface consists of the POE file itself, a binary data structure
+[largely] private to the QPOE interface, and a set of procedures for creating,
+writing into, and reading from POE files. The procedures fall into several
+categories, i.e.,
+.ls 4
+.ls o
+General QPOE file management procedures. These include routines for creating,
+deleting, renaming, opening, and closing POE files, plus set/stat routines
+for setting and querying the file parameters and interface options.
+.le
+.ls o
+General header access procedures. These include a conventional set of keyword
+driven typed scalar get/put routines, plus get/put routines for accessing
+variable length typed and opaque binary arrays (e.g., history records and the
+aspect and temporal records).
+.le
+.ls o
+Event i/o procedures. These are routines for initially preparing and
+subsequently reading (sequentially with seek) the main event list, e.g.,
+the raw "get next photon" routine.
+.le
+.ls o
+The selection subpackage. Included are routines for opening and incrementally
+compiling a user supplied selection predicate (filter) input as a formatted
+text string, and for testing individual event records to see whether they
+satisfy the given expression.
+.le
+.le
+
+All binary data structures other than simple scalar variables, e.g., the
+aspect and temporal records and the event structure, are described in QPOE
+by compile time bound SPP binary data structure definitions, provided in a
+standard interface include file (\fI<qpset.h>\fR) referenced by both QPOE and
+selected applications. When the interface is layered upon DFIO these
+structures could continue to be used, since DFIO will have the ability to
+define runtime mappings of conventional application defined structures onto
+the physical data datafile (POE file) structures.
+
+.NH 2
+Interface Specification
+
+ The quick-POE interface (QPOE, package prefix `qp') is a set of procedures
+for accessing \fIpoefiles\fR, or position ordered event files. Each poefile
+consists of a \fBheader\fR of arbitrary size and content containing zero or
+more named scalar or variable length (opaque, typeless) fields, plus an
+\fBevent list\fR consisting of zero or more event structures.
+The event structure is fixed at compile time via a conventional SPP structure
+declaration in the include file \fB<qpset.h>\fR.
+
+.NH 3
+Interface Procedures
+
+ The QPOE procedures fall into three main categories, the primary user
+interface procedures (general datafile management, header access, and filtered
+event i/o), the low level or raw event i/o procedures, and the low level
+selection expression compile and evaluate procedures.
+
+.NH 4
+Header Access Procedures
+
+ The routines described in this section are used to create, open, or
+otherwise manipulate poefiles, to define new header parameters or query the
+existing parameter set, and to read and write the values of both scalar and
+vector parameters of various standard and poefile-specific datatypes.
+These operators are summarized in the figure below.
+
+The function of most of these procedures should be obvious.
+The \fIqp_access\fR, \fIqp_delete\fR, \fIqp_rename\fR, and \fIqp_copy\fR
+operators perform the implied operation on the named poefile.
+The poefile may be rebuilt with \fIqp_rebuild\fR, recovering any unused
+space and rendering storage for the internal data structures (logically)
+contiguous in the process (a rebuild is just a copy/rename/delete).
+
+The \fIqp_open\fR procedure must be called to open or create a poefile,
+before it can be accessed. The NEW_FILE and NEW_COPY modes are supported
+for creating new files. If NEW_COPY mode is specified, a reference file
+may be specified (via the descriptor \fIo_qp\fR) from which the new file
+is to inherit the header but no data (no event list).
+The \fIqp_seti\fR and \fIqp_stati\fR procedures are used to set and stat
+any parameters affecting QPOE i/o, and \fIqp_sync\fR updates an opened
+poefile on disk.
+
+The \fIqp_get\fR and \fIqp_put\fR scalar functions behave as for the other
+VOS interfaces, e.g., they will abort if the named parameter does not exist,
+or if the implied datatype conversion is illegal. The \fIqp_add\fR
+procedures are equivalent to the \fIqp_put\fR procedures except that they
+will create the named parameter if it does not already exist (see also
+\fIqp_addf\fR, discussed below).
+
+.nf
+ yes|no = qp_access (poefile, mode)
+ qp_copy (poefile, newfile)
+ qp_rename (poefile, newfile)
+ qp_rebuild (poefile)
+ qp_delete (poefile)
+
+ qp = qp_open (poefile, mode, o_qp)
+ qp_seti (qp, param, ival)
+ ival = qp_stati (qp, param)
+ qp_sync (qp)
+ qp_close (qp)
+
+ val = qp_get[bcsilrdx] (qp, param)
+ qp_gstr (qp, param, outstr, maxch)
+ qp_put[bcsilrdx] (qp, param, val)
+ qp_pstr (qp, param, strval)
+ qp_add[bcsilrdx] (qp, param, defval, comment)
+ qp_astr (qp, param, strval, comment)
+
+ fd = qp_popen (qp, param, mode, type)
+ nelem = qp_read (qp, param, buf, nelem, first, dtype)
+ qp_write (qp, param, buf, nelem, first, dtype)
+
+ yes|no = qp_accessf (qp, param)
+ qp_deletef (qp, param)
+ qp_renamef (qp, param, newname)
+ qp_addf (qp, param, dtype, maxelem, comment, flags)
+ nelem = qp_queryf (qp, param, dtype, maxelem, comment, flags)
+
+ list = qp_ofnl[su] (qp, template)
+ nch|EOF = qp_gnfn (list, outstr, maxch)
+ qp_cfnl (list)
+.fi
+
+Array valued parameters may be randomly read with \fIqp_read\fR and
+written with \fIqp_write\fR; arrays may be any length, and will be
+automatically extended in a write. The only way to shorten an array
+parameter is to copy it and delete the old parameter. The typed read and
+write functions allow automatic type conversions, and external storage of
+the data in a machine independent form (should the interface choose to do so).
+In addition to the standard SPP types, QPOE supports the special types
+TY_EVENT, TY_ASPECT, and TY_TEMPORAL. Finally, the type TY_OPAQUE denotes
+an array of element size SZ_CHAR, which will be copied to and from external
+storage without the data being modified in any way (note that opaque data
+is machine independent only if the application encodes it that way).
+
+Alternatively, an array valued parameter may be opened as a random access
+\fIfile\fR with \fIqp_popen\fR, and then read or written with conventional
+FIO calls. The value of the \fItype\fR parameter must be TEXT_FILE or
+BINARY_FILE, as for a conventional file. If the type is TEXT_FILE then
+only text data may be stored in the file, and text data will be byte packed
+on disk. The BINARY_FILE type is equivalent to the QPOE type TY_OPAQUE.
+File i/o to a QPOE parameter is equivalent to file i/o to a conventional
+binary file in terms of both efficiency and semantics, i.e., the data is not
+modified in any way, and the "files" may be any size (the main semantic
+difference is that deleting the parameter does not immediately free the space).
+A parameter opened as a file with \fIqp_popen\fR is closed with the FIO
+\fIclose\fR routine.
+
+Although new parameters may be defined when first written to by calling one of
+the typed \fIqp_add\fR functions, the most general procedure for adding new
+parameters is \fIqp_addf\fR, which allows the datatype and vector length of
+the parameter to be explicitly specified, along with a comment describing the
+new parameter. The procedure \fIqp_accessf\fR tests if the named parameter
+exists, and \fIqp_deletef\fR and \fIqp_renamef\fR make it possible to delete
+and rename parameters, e.g., for implementing array copy procedures.
+The \fIqp_queryf\fR procedure returns the datatype, allocated vector length,
+current vector length, and comment field of the named parameter.
+
+The field name list procedures (\fIqp_ofnl[su]\fR etc.) are used to obtain
+the names of all header parameters matching the given template; a null or "*"
+template returns the names of all header parameters. This is the only way by
+which an application without apriori knowledge of the field names can determine
+what is in the header, e.g., to list or copy the header.
+
+.NH 4
+Filtered Event I/O Procedures
+
+ The \fBevent i/o\fR subpacke provides sequential i/o facilities for the
+main event list of the poefile. These procedures, known as the QPIO (QPOE
+event i/o) package, provide read or write (append) access to the event list,
+optionally filtered when reading to select events spatially or by event
+attribute.
+
+Under QPOE, an event list is stored as a variable length array (i.e., as a
+named header parameter) of type \fIevent\fR. The QPIO package takes this
+basic object and adds additional structure for more efficient i/o, e.g., events
+are blocked into large, fixed size \fIbuckets\fR of N events, the first two
+events of each bucket containing the minimum and maximum event values for that
+bucket. If the event list is sorted an \fIindex\fR may be maintained for the
+list; this index, plus the min/max event values maintained for a bucket, are
+used to optimize basic event i/o and filtering. During event i/o the raw
+event list may be filtered spatially or by event attribute. All this is
+transparent to the application, which merely opens the event list parameter
+and begins reading (or writing) blocks of events.
+
+Before i/o can take place the named event list parameter is opened with
+\fIqpio_open\fR. The selection filter to be used by QPIO may be specified
+via a selection expression passed in by the user at poefile open time (as
+part of the poefile name), at QPIO open time (as part of the parameter name),
+or in subsequent calls to \fIqp_addfilter\fR (each call incrementally modifies
+the current filter) or to \fIqp_setfilter\fR (each call replaces the affected
+portion of the current filter). A region mask may also be specified with
+\fIqp_setmask\fR; if no mask is specified, the default rejection mask is used
+(or more precisely, its inverse).
+
+.nf
+ qpio = qpio_open (qp, param, mode)
+ qpio_mkindex (qpio, key, nelem)
+ qpio_setrange (qpio, vs, ve, ndim)
+ qpio_[add|set]filter (qpio, selexpr)
+ nchars = qpio_getfilter (qpio, outstr, maxch)
+ qpio_setmask (qpio, pl)
+ pl = qpio_getmask (qpio)
+ nev|EOF = qpio_getevents (qpio, ev, maskval, maxevents)
+ qpio_putevents (qpio, ev, nevents)
+ qpio_readpix (qpio, obuf, vs, ve, dxim, xblock, yblock)
+ qpio_close (qpio)
+.fi
+
+Events are read sequentially with \fIqp_getevents\fR, which fills in the
+pointer array \fIev[maxevents]\fR with one or more pointers to event structs,
+returning the number of events read as the function value, or EOF when the
+event list is exhausted. Events are returned in the order in which they are
+stored in the main event list. If a region mask is used for spatial
+filtering, the mask value associated with the output events is returned in
+\fImaskval\fR. Filtering and subranges are supported only for reading;
+\fIqp_putevents\fR may only be used to append to the output poefile.
+At present, poefiles are not randomly updatable, as this would require
+runtime editing of the compressed event lists and it is not clear how useful
+such a feature would be.
+
+If less then the entire image is to be accessed then \fIqp_setrange\fR
+may be called to specify the region of the poe image from which events are
+to be read (the vector coordinates \fIvs\fR and \fIve\fR are specified
+relative to the predefined primary event coordinate system, i.e., the PO
+coordinates). Repeated calls to \fIqp_setrange\fR may be made to access
+multiple regions of the image, or to rewind the i/o pointer for a region.
+
+An alternative to event i/o is provided by \fIqp_readpix\fR, which samples
+the event list using the current filters and blocking factor,
+generating \fInpix\fR pixels beginning with the pixel at the image coordinates
+specified by the vector \fIv\fR. This is the routine used by the POE IMIO
+image kernel to read from a poefile. Only integer pixels are supported.
+On output, each pixel value is a count of the number of filtered events
+mapping into that pixel. A region mask may be used to filter the event list,
+but the ability to discriminate between different regions by the mask value
+is lost.
+
+A poefile may contain any number of event list parameters, although most
+files are expected to store only the main event list.
+As an alternative to QPIO, event-array parameters may be accessed directly
+via the normal header access parameters, e.g., \fIqp_read\fR, but i/o may be
+somewhat less efficient (due to the copyout), the bucket structure will be
+visible, and no filtering is possible. In short, the raw event list will
+be accessed as an array, returning the min/max events in each bucket along
+with the data events.
+
+In the current implementation, the event structure is a fixed, predefined
+binary structure, and all event i/o is expressed in terms of pointers to event
+structures. The event structure is defined in the include file
+\fB<qpset.h>\fR, discussed in the appendix.
+
+.NH 4
+Selection Subsystem Procedures
+
+ The \fBselection subsystem\fR is a facility used to perform runtime
+filtering of the event list, returning to the calling program only those
+events satisfying some user defined selection criteria. The selection
+subsystem is driven by a selection expression provided by the user as a
+formatted string, normally at image (poefile) open time. The selection
+expression syntax itself is independent of the procedural interface and is
+described separately in section 2.3.2. The selection procedures are
+summarized in the figure below.
+
+.nf
+ ex = qpex_open (qp, expr)
+ ok|err = qpex_modfilter (ex, exprlist)
+ nchars = qpex_getfilter (ex, outstr, maxch)
+ nev = qpex_evaluate (ex, i_ev, o_ev, nev)
+ qpex_close (ex)
+.fi
+
+A selection expression, input as the string \fIexpr\fR, is compiled with
+\fIqpex_open\fR, which returns a pointer to the runtime descriptor (filter)
+for the given compiled selection expression. If \fIexpr\fR is the null
+string the filter returned will pass all events.
+
+An active filter may be modified with \fIqpex_modfilter\fR, which combines
+the expression \fIexpr\fR into the current filter, allowing complex
+filters to be built up in several calls, or allowing an application to modify
+a base filter without knowledge of the base filter. The expression consists
+of a list of comma delimited "attribute = exprlist" terms. If the assignment
+is specified as "=" the term for the given attribute is replaced; if the
+assignment is "+=" the term for the given attribute is further qualified.
+A text representation for the current filter may be obtained at any time with
+\fIqpex_getfilter\fR. The boolean function \fIqpex_evaluate\fR is called to
+test whether a specific event meets the selection criteria, i.e., to test
+whether or not \fIexpr\fR is true for the given event.
+
+Selection is normally performed transparently to the application by the QPIO
+interface, which calls the routines described in this section to create and
+apply event-attribute filters.
+
+.NH 3
+Spatial and Event Attribute Filtering
+.NH 4
+Selection Syntax
+
+ Selection expressions are used to construct \fIfilters\fR to specify the
+rejection mask and region of interest filters before reading the event list
+via QPIO. Due to the complexity of event attribute selection, selection
+predicates are specified syntactically, i.e., as an expression input as a
+text string. These filters may be input by the user as part of the image
+or poefile specification (object name), transparently to applications code,
+or they may be constructed by the application via calls to the QPIO or
+selection routines. Complex or frequently referenced filters may be stored
+in text files and referenced by filename if desired. It does not matter
+whether a filter is input all at once, or compiled incrementally.
+
+An event attribute filter consists of a set of filters for each event
+attribute. By default, i.e., if no filter is specified, all attribute values
+are passed. If a filter is specified for an attribute, the filter specifies
+either a bitwise mask value, or a list of acceptable values or ranges of values
+for the attribute. An event is passed if and only if all event attribute
+filters pass the event. If a list of acceptable values are specified, the
+list may be any length, with little impact on filtering efficiency.
+
+The basic event attribute filter syntax consists of a list of attribute
+filters, e.g.:
+
+ attribute = values [, attribute = values ...]
+
+where \fIattribute\fR is the attribute name, e.g., a position attribute,
+time, energy, and so on, and \fIvalues\fR is a mask or list of values.
+Mask values are integers prefixed by `%', e.g.,
+
+ attribute = %1003B
+
+Note that the mask may be specified in decimal (the default), octal (`b' or
+`B' suffix), or hex (`x' or `X' suffix), in accord with the usual IRAF
+conventions. The meta-characters used in selection expressions have been
+selected to avoid or at least minimize the need to quote such expressions
+in CL commands.
+
+A specific value or list of values may be specified as a
+simple integer constant, or comma delimited list of constants, e.g.:
+
+.nf
+ attribute = 3
+or
+ attribute = 3, 5, 20X
+.fi
+
+Ranges are specified using the `:' notation, e.g.,
+
+ attribute = 3, 5, 8:11
+
+A `!' may be prepended to indicate the opposite, i.e., "everything but":
+
+.nf
+ attribute = !%14B
+or
+ attribute = 3, !1:10
+.fi
+
+Open ended ranges may be used to indicate that the range includes all values
+less than or equal to or greater than or equal to the given value, e.g.,
+
+ attribute = :100
+
+denotes all values less than or equal to 100.
+
+File inclusion or \fImacro expansion\fR is denoted by a C-like function call
+notation, e.g.,
+
+.nf
+ macro()
+or
+ macro(a,b)
+.fi
+
+Any arguments are expanded via string substitution when the text of the macro
+or include file is expanded. Include files should have the extension ".qpm".
+Macros are permitted only if the variable \fBqpinit\fR is defined in the user's
+environment, the string value consisting if the filename of the user's QPOE
+macro file. In a reference to a macro \fImacro\fR, QPOE will look first in
+the macro definitions file pointed to by \fIqpinit\fR for the named macro,
+then it will look for the file "\fImacro\fR.qpm" in the current directory.
+
+Parenthesis are optional and may be included to, for example, make attribute
+value lists more easily identifiable. If a line ends in a comma or backslash
+continuation is assumed; blank lines and comment lines are ignored.
+The syntax for attributes with floating point values is identical to that
+for integers except that mask values are not allowed.
+
+.NH 4
+Region Specification
+
+ QPOE does not itself contain any syntax-level support for specifying
+region masks, e.g., via a list of include and exclude circles and other
+shapes. The reason for this is that it is too difficult to come up with
+a sufficiently general scheme at the level of an interface like QPOE;
+there are too many ways to specify regions, hence in general such region
+specification must be done at the applications level. QPOE does however
+include very general and efficient support for region analysis provided the
+region mask is input already encoded into a PLIO binary mask. Since PLIO
+includes high level primitives for defining masks in terms of include and
+exclude circles, boxes, lines, polygons, etc., it is easy to extend QPOE at
+the applications level to include support for a region specification language
+tailored to the specific application.
+
+While QPOE cannot itself process a user defined region description to create
+new region masks, it is possible to \fIselect\fR from any number of region
+masks if these are prepared in advance using other systems facilities or
+applications programs. PLIO region masks may be stored in the QPOE header
+as named parameters of type opaque binary array, or they may be stored in
+external binary files. The region mask to be used may be specified by
+including an assignment of the form
+
+ mask=[\fIparam\fR|\fIfile\fR.pl]
+
+in the selection expression. Unless otherwise specified, this region mask
+will be combined with the default rejection mask for the poefile (the final
+mask will be the region mask \fIand\fR-ed with the \fInot\fR of the rejection
+mask).
+
+.NH 4
+Predefined Selection Keywords
+
+ While the syntax of a selection expression is an inherent part of the
+QPOE interface, the names of the event attributes used in selection
+expressions are logically part of the event structure, and ideally should
+be stored with the data and used by the interface only to determine the
+attribute datatypes and offsets into the event structure when the selection
+expression is compiled at runtime. We should not really be documenting the
+specifics of the POE external data structures here, but in QPOE these data
+structures are wired into the interface, so it is appropriate to do so.
+
+The following \fIstandard event attributes\fR are defined. Minimum match
+abbreviations are of course permitted. The keyword \fIpi\fR is an acceptable
+alias for \fIenergy\fR (\fIpi\fR and \fIpha\fR are examples of discipline
+dependent terminology which should be associated with the data and not the
+interface).
+
+.nf
+ X short range in X (PO coords)
+ Y short range in Y (PO coords)
+ TIME real time event was recorded
+ ENERGY,PI int energy of event
+ PHA int pulse height
+.fi
+
+The event attributes may also be referred to using the generic notation
+[\fIsir\fR]\fIN\fR, which refers to each attribute by its datatype and
+struct offset (byte units, zero indexed) rather than by name. For example,
+if the event attributes shown above are assumed to be shown in the order
+in which the fields are stored in the event struct, the \fItime\fR field
+could also be referred to as \fIR4\fR, and \fIpha\fR as \fII10\fR. This
+crude but effective technique may be used to reference any private
+(nonstandard) fields of the event struct in selection expressions.
+
+The following additional, non-event keywords are defined:
+
+.nf
+ BLOCK int \fIqp_readpix\fR blocking factor
+ MASK string region mask to be used
+ FILTER string region filter to be used
+ REJMASK string rejection mask to be used
+ REJFILTER string rejection filter to be used
+.fi
+
+The default values for these parameters are taken from datafile header
+parameters of the same name, if such are found. Masks are specified either
+by the name of a header parameter of type opaque binary array (containing
+an encoded PLIO mask), or by the name of a PLIO mask file, extension ".pl".
+Named filters are specified by the name of a header parameter of type char
+array, or by the name of a text file (extension ".qpf"), where in either
+case the named object contains the selection expression text.
+
+.NH 3
+Interface Set/Stat Parameters
+
+ The internal parameters for the QPOE interface, and all user accessible
+data structures, e.g., the event and other structures, are defined in the
+global system include file \fB<qpset.h>\fR. This file should be referred to
+for up to date documentation on these definitions and structures; the
+discussion which follows may not be kept up to date.
+
+The following interface parameters may be accessed via the \fIqp_seti\fR and
+\fIqp_stati\fR procedures:
+
+.nf
+ QP_XRESOLUTION resolution of an event x-coordinate
+ QP_YRESOLUTION resolution of an event y-coordinate
+ QP_LENEVENT length of an event structure
+ QP_LENINDEX resolution of the event list index
+ QP_BUCKETSIZE event list bucket size, nevents
+ QP_PAGESIZE datafile page size, bytes
+ QP_CACHESIZE number of buffers in data buffer cache
+ QP_MAXFILES max lfiles in datafile (fixed)
+ QP_NFILES query number of lfiles in datafile
+ QP_NPAGES query number of pages in datafile
+ QP_FREEPAGES query number of free pages in datafile
+.fi
+
+The parameters shown above may be set only at datafile creation time.
+The X and Y resolution parameters define the range of event x,y coordinates.
+The resolution of the event list index is set by QP_LENINDEX, and may be less
+than the full resolution of the event pixel Y-coordinate.
+The remaining parameters control how storage is physically allocated in the
+datafile and in any event lists.
+
+.NH 2
+Detailed Design
+.NH 3
+Event Attribute Filtering
+
+ The point of event-attribute filtering is to test an event to see if it
+satisfies a user defined event selection expression. A selection expression
+may be decomposed into a list of simple, independent expressions for the
+individual event attributes; the event satisfies the full expression only
+if the value of each event attribute satisfies the associated attribute
+expression. Currently, attribute expressions are limited to bitmasks, lists
+of acceptable values, or lists of ranges (inclusive) of acceptable values.
+
+ attr1=expr, attr2=expr, ...
+
+The highly constrained nature of event-attribute expressions makes expression
+evaluation straightforward and fast. Expression evaluation is implemented
+by logically negating each attribute expression and testing each in turn;
+expression evaluation ends either when an attribute test fails, in which case
+the event is rejected, or when the end of the attribute expression list is
+reached, in which case the event is passed.
+
+The obvious way to implement such an expression evaluator is with a simple
+interpreter. The expression is parsed and compiled to produce a simple
+interpreter program, using the instructions shown in the figure below.
+
+.ks
+.nf
+ \fIinstruction arguments\fR
+
+ MSK[sir] offset maskval bitwise mask test
+ EQL[sir] offset value equality test
+ LEQ[sir] offset value less than or equal
+ GEQ[sir] offset value greater than or equal
+ RNG[sir] offset lowval highval range test (inclusive)
+ LUT[sir] offset lut lookup table
+ NOT invert test
+ RET return, pass event
+.fi
+.ke
+
+An interpreter program consists of a series of these instructions. The
+\fIoffset\fR argument gives the offset of the event attribute (field) to
+be tested; the datatype of this field must match that of the instruction
+and of the data argument or arguments, if any. Only event attributes for
+which restricted values were specified are tested, hence the cost of the
+evaluator depends only upon the complexity of the expression to be evaluated.
+An interpreter of this type can be coded very efficiently as a switch-case
+statement (jump table) within an optimized DO-loop.
+
+Simple attribute value tests are most efficiently coded as several \fIMSK\fR,
+\fIEQL\fR, etc., instructions. The only case of any complexity is where the
+attribute has a long list of acceptable values or ranges of values. This is
+most efficiently coded using a lookup table, using the \fILUT\fR instruction
+shown in the figure. A lookup table test may be preceded by a range test to
+limit the size of the lookup table required. For an integer or short integer
+attribute, the lookup table will be a \fIboolean\fR table containing one entry
+for each possible value of the attribute in the range spanned by the table.
+
+Use of a lookup table for floating point attributes is more difficult since
+an enormous lookup table might be required to preserve the resolution of
+the floating point numbers used to define ranges. The solution is to employ
+an \fIinteger\fR (rather than boolean) lookup table of \fIreduced resolution\fR.
+The floating point value of the attribute to be tested is mapped into a bin of
+the lookup table. The integer value of the table entry has one of the
+following values:
+
+.ks
+.nf
+ 0 Reject all FP numbers mapping to this bin.
+ 1 Accept all FP numbers mapping to this bin.
+
+ N Some of the FP numbers mapping to this bin are
+ legal, and some are not. The value N is the
+ address of a segment of interpreter code to be
+ executed to test a FP value mapped to this bin.
+.fi
+.ke
+
+The performance of this algorithm for floating point table lookup depends
+upon the frequency with which 0 or 1 is encountered as the table value during
+lookup; if 0 or 1 is encountered most of the time, then a floating point LUT
+test is comparable in expense to an integer LUT test. But since we already
+have to map a floating point number into an integer space of reduced
+resolution, we can easily vary the resolution of the lookup table,
+increasing the resolution of the table until the desired level of efficiency
+is reached (the interpreter execution time for case N is pretty fast in any
+case, so this is not critical).
+
+In summary, the expense of event-attribute filtering is directly proportional
+to the number of attribute tests to be performed. An arbitrary number of
+values or ranges of values may be specified for an attribute with little if
+any affect on performance, even for floating point attributes (e.g., for
+time-tagged quality filtering).
+
+.NH 3
+Region Filtering
+
+ Region filtering is implemented in QPOE by the PLIO interface, which is
+documented elsewhere. PLIO permits regions of arbitrary complexity to be
+described and used for event filtering, with little overhead beyond that
+already present for i/o on a large event list with no region filtering.
+This assumes only that the event list is position ordered, and that the
+region mask is specified in the PO (position ordered) coordinate system.
+This makes it possible for QPIO to use the mask to reduce the number of
+events to be examined; event attribute filtering is performed only on those
+events read through the region mask (this is similar to masked image i/o,
+i.e., the MIO package).
+
+If the event structure supports multiple coordinate systems and the region
+mask refers to a non-PO coordinate system, then the only approach is to first
+perform event-attribute filtering on the non-positional event attributes,
+then for each event passing the event-attribute filter, fetch the mask value
+corresponding to the x,y coordinates of the event. This is still an efficient
+technique since only mask pixel lookup is required (no complicated region
+list traversal is involved), but it will be significantly less efficient than
+PO region filtering since we cannot take advantage of position ordering to
+reduce the number of events to be examined, and the overhead of accessing
+the region mask will be greater.
+
+.NH 3
+Datafile Layout and Access
+.NH 4
+File Structure
+
+ The QPOE file structure is private to the QPOE interface and is discussed
+here only for the purpose of detailing (and documenting) the design of the
+interface. The QPOE file is a random access, dynamically extendable, binary
+file. Under QPOE these files will be partially, but not completely, machine
+independent, hence file sharing by machines of different architectures will
+not be provided initially. This will be rectified when management of the
+datafile is later turned over to DFIO.
+
+To provide a reasonable degree of flexibility, QPOE contains many
+variable length data structures, e.g., there may be any number of header
+parameters, including array valued parameters of arbitrary size. New header
+parameters may be added at any time, and new data may be appended to array
+parameters at any time. This flexibility places certain demands upon the
+low level file manager used to maintain these data structures in the datafile.
+
+All access to the physical datafile is via a low level binary
+\fIfile manager\fR. The purpose of the file manager is to implement a
+restricted implementation of the binary file abstraction upon a single host
+level binary file. This provides the "lightweight" binary file mechanism we
+need for QPOE. Since the file manager is a low level facility, it is
+implemented using only the low level asynchronous i/o facilities provided
+by FIO to read and write file pages, once the file has been opened.
+
+The file manager provides routines for creating new datafiles, and for
+creating, deleting, etc., \fIlightweight files\fR (lfiles) within a datafile.
+Storage for lfiles is allocated in units of datafile \fIpages\fR. For each
+data page in the datafile there is an entry in the datafile \fIpage table\fR.
+The page table itself is stored as an lfile (\fIlfile zero\fR) in the data
+pages. Files at the file manager level are known only by their file number;
+association of these file numbers with file names is left up to the higher
+level code, and in the case of QPOE is done with the symbol table (which is
+also stored as an lfile).
+
+.ks
+.nf
+ +-----------------+
+ datafile header fixed size
+ +-----------------+
+ file table fixed size
+ +-----------------+
+ |
+ data pages data and page table pages
+ | (arbitrarily large)
+ v
+.fi
+.ke
+
+The page table is a vector mapping datafile pages by file offset onto lfile
+file numbers; the value of each page table entry is the file number of the
+lfile to which the page is assigned. When the file manager opens an lfile
+it scans the page table, extracting the page numbers of the pages assigned
+to the lfile, to form a vector mapping lfile page offsets directly onto
+datafile page offsets. New pages are always allocated at the end of the
+datafile, and new lfiles are always allocated at the end of the file table,
+hence lfile deletion will leave "holes" (unused storage) in both the datafile
+pages and file table. A \fIrebuild\fR operation is required to reclaim the
+space occupied by these holes. Deleted files are recoverable by merely
+revalidating their file table entries.
+
+Every variable size object managed by QPOE is stored in the datafile as a
+distinct lfile. Since storage for lfiles is allocated in units of file pages,
+the minimum amount of storage used by a variable length object is 0 or 1 page.
+Examples of variable size objects are the SYMTAB symbol table
+used to describe the contents of the datafile header (and any other symbols
+used by QPOE), the static data storage area (used to store the values of
+scalar and static array valued header parameters), and individual variable
+length arrays. Note that each variable length array is stored in the datafile
+as a separate lfile; if the maximum size of an array is less then the page
+size, it will be more efficient to store it as a static array.
+
+The most important example of a variable length array is the main event list
+of the poefile. To improve i/o efficiency and speed selection, the event
+structs stored in an event list are grouped together into \fIbuckets\fR,
+as discussed earlier in section 2.3.1.2. Each bucket will always occupy an
+integral number of file pages. Storage for buckets is allocated contiguously
+in the datafile, and buckets are always read and written to disk in a single
+i/o transfer.
+
+The most important physical datafile parameters are hence the page size and
+the bucket size. A larger page size can improve i/o efficiency and reduce the
+size of the page table, but can lead to significant wasted space if there are
+many variable length arrays. Since the i/o system will move entire large
+blocks of pages to and from disk whenever possible, use of a small page is
+normally preferred. A large bucket size improves i/o efficiency for event
+lists, but if the bucket size is too large then bucket searching takes longer,
+and selection efficiency may decrease.
+
+.NH 4
+File Manager
+
+ The function of the file manager is to map a set of lfiles onto a single
+random access host binary file. The file manager must keep track of the size
+and type of each file, and whether or not it has been deleted.
+In addition, the file manager must maintain a page table for the entire
+datafile, noting the lfile to which each page is assigned. While an lfile
+is open the file manager must maintain the page vector for that lfile so that
+lfile offsets may be mapped directly onto datafile offsets.
+
+The number of lfiles is fixed at datafile creation time, and lfiles are
+referred to by file number. File number zero is the datafile page table;
+the first user lfile is number one. A datafile with a max file count of one
+would actually contain two lfiles, counting the page table.
+
+The file manager interface is summarized in the figure below. A new datafile
+may be created or an existing datafile opened with \fIfm_open\fR.
+If a new datafile is being created the page size and max file count may be
+changed from their default values with calls to \fIfm_seti\fR, and the values
+of these and other parameters may be queried at any time with \fIfm_stati\fR.
+An opened datafile may be copied with \fIfm_copyo\fR, omitting deleted lfiles
+and rendering file segments contiguous. The page size and max file count
+may be changed in a copy operation if desired.
+
+The \fIfm_access\fR, \fIfm_rename\fR, and \fIfm_delete\fR routines perform
+the indicated operation upon the named datafile. The \fIfm_rebuild\fR
+routine rebuilds a datafile, discarding deleted structures and coalescing
+storage for objects. This routine, as well as \fIfm_copy\fR, are built upon
+on the lower level routine \fIfm_copyo\fR, which does the real work, and
+which allows the structural attributes of the new datafile to be specified
+in \fIfm_seti\fR cals.
+
+All i/o to lfiles is via the six routines beginning with \fIfm_lfopen\fR in
+the figure below. These routines constitute a FIO binary file driver for
+lfiles, and may be called directly, or passed to the FIO routine \fIfopnbf\fR
+to open an lfile as a binary file (\fIfm_lfname\fR should be called first
+to construct a pseudo-filename for the lfile so that \fIfm_lfopen\fR can
+reconstruct the file manager descriptor, lfile number, and lfile type).
+Note that the lfile driver routines are unbuffered and (potentially)
+asynchronous, and that i/o must be in units of datafile pages.
+(See the buffer cache routines described in the next section for a higher
+level facility for i/o to lfiles).
+
+.nf
+ yes|no = fm_access (datafile, mode)
+ fm_rename (datafile, newname)
+ fm_copy (datafile, newname)
+ fm_delete (datafile)
+ fm_rebuild (datafile)
+
+ fm = fm_open (datafile, mode)
+ fm_seti (fm, param, ival)
+ ival = fm_stati (fm, param)
+ fm_debug (fm, out, what)
+ fm_copyo (fm, fm_to)
+ fm_sync (fm)
+ fm_close (fm)
+
+ lfile = fm_nextlfile (fm)
+ fm_lfname (fm, lfile, type, lfname, maxch)
+
+ fm_lfopen (lfname, mode, lf)
+ fm_lfstati (lf, param, ival)
+ fm_lfaread (lf, buf, nbytes, offset, status)
+ fm_lfawrite (lf, buf, nbytes, offset, status)
+ fm_lfawait (lf, status)
+ fm_lfclose (lf, status)
+
+ fm_lfstat (fm, lfile, statbuf)
+ fm_lfdelete (fm, lfile)
+ fm_lfundelete (fm, lfile)
+.fi
+
+In a sense, all lfiles exist as zero length files when the datafile is
+created, since the lfile descriptors are preallocated and the files are
+known only by number. Lfiles become interesting when they are opened as
+files with \fIfm_lfopen\fR, and data is written into the file. An lfile
+may be deleted with \fIfm_lfstat\fR. All this does is set the delete bit
+in the lfile descriptor, hence a deleted lfile may later be undeleted with
+\fIfm_lfundelete\fR. The data in a deleted lfile is not lost until the lfile
+is again opened and written into, or the datafile is rebuilt. Information
+on a specific lfile (size, type, etc.) may be obtained with \fIfm_lfstat\fR.
+
+There is nothing about the file manager which is specific to QPOE, so it is
+implemented as a separate, standalone facility, and may be used in applications
+other than QPOE.
+
+.NH 4
+Buffer Cache
+
+ For reasons of efficiency, QPOE maintains portions of the datafile in
+memory buffers while a datafile is open. The main QPOE descriptor, symbol
+table, and file manager descriptor and page table are maintained in special
+runtime data structures internal to the respective interfaces. All other
+data is stored in lfiles and accessed only upon demand. In particular,
+storage for all static (non variable length) QPOE header parameters is
+maintained in a single lfile, and storage for each variable length parameter
+is allocated in a separate lfile.
+
+Since most access to QPOE header parameters is via simple gets and puts to
+named parameters, lfile access is handled by QPOE transparently to the client
+applications program. To avoid excessive disk i/o when randomly accessing
+the datafile, it is desirable for QPOE to maintain a cache of several lfile
+data buffers, e.g., so that accesses to a series of static parameters or
+repeated accesses to read or write different parts of an array parameter
+should incur minimal disk accesses. This buffer cache is implemented in
+QPOE by simply opening each lfile as a file under FIO, leaving it up to FIO to
+manage the file buffer, and maintaining a LRU cache of open lfiles in QPOE.
+The number of buffers (open lfiles) is controlled by the QP_CACHESIZE
+parameter. Since the lfile buffer cache is a general datafile related
+facility, it is implemented by the file manager.
+
+.ks
+.nf
+ fd = fm_getfd (fm, lfile, mode, type)
+ fm_retfd (fm, lfile)
+ fm_lockout (fm, lfile)
+ fm_debugfd (fm, out)
+.fi
+.ke
+
+The \fIfm_getfd\fR routine maps an lfile onto a file descriptor. A file
+descriptor is opened on the lfile only when necessary. Once opened, an lfile
+remains in the cache until forced out by the LRU replacement algorithm,
+or the datafile is closed. Removal of an lfile from the cache (closing the
+associated file descriptor) is permitted only after a call to \fIfm_retfd\fR;
+calling this routine does not immediately close the file, it only permits it
+to be closed. Most calls to \fIfm_getfd\fR should return a file descriptor
+immediately, with very little overhead, with an already active file buffer,
+hence repeated calls to the cache manager and FIO may be made without
+incurring any disk accesses.
+
+Note that lfiles may be opened on file descriptors via direct calls to the
+file manager, regardless of whether these lfiles are already open in the
+buffer cache. This allows two or more independent file buffers to be
+simultaneously active on the same lfile, but opens the possibility of loss
+of data if the buffers overlap. If this is a problem, the routine
+\fIfm_lockoutfd\fR may be called to prevent inadvertent use of an lfile by
+the cache. This should be followed by a call to \fIfm_retfd\fR to clear the
+lockout bit once the reason for the lockout (usually a noncached lfile open)
+is gone. The routine \fIfm_debugfd\fR will print information on \fIout\fR
+describing the contents of the buffer cache.
+
+.tp 24
+.NH 3
+Interface Structure
+.NH 4 1
+Header Access Package (QP Routines)
+
+ The structure of the general QPOE routines (mostly header access) is
+illustrated in the figure below.
+
+.ks
+.nf
+ +--------+
+ | QP |
+ +--------+
+ / \
+ / \
+ +--------+ +--------+
+ | SYMTAB | | BCACHE |
+ +--------+ +--------+
+ |
+ +--------------+
+ | FILE MANAGER |
+ +--------------+
+
+ Figure 1. Structure of the Header Access Routines
+.fi
+.ke
+
+To fulfill a get or put header access, QPOE will access the symbol table
+(SYMTAB) to lookup the symbol name and determine the symbol datatype, nelem,
+lfile number, and lfile file offset where the value is stored. The buffer
+cache (BCACHE) and FIO are then called to access the value of the parameter
+in the datafile.
+
+.NH 4
+Filtered Event I/O Package (QPIO)
+
+ The structure of the filtered event i/o package (QPIO) is illustrated in
+the figure below.
+
+.ks
+.nf
+ +--------+
+ | QPIO |
+ +--------+
+ / | \
+ __________/ | \__________
+ / | \
+ +--------+ +--------+ +--------+
+ | PLIO | | QPEX | | BCACHE |
+ +--------+ +--------+ +--------+
+ | |
+ +--------+ +--------------+
+ | SYMTAB | | FILE MANAGER |
+ +--------+ +--------------+
+
+ Figure 2. Structure of QPIO Routines
+.fi
+.ke
+
+In the typical \fIgetevents\fR call, QPIO will call PLIO to determine the
+next region of the stored image (event list) to access, then if the event
+data is not already in a data buffer, FIO is called to read the data (bucket),
+using the event list index, an integer array valued parameter, to determine
+what bucket to read. The events in the bucket are then examined and optionally
+filtered via calls to QPEX, returning pointers to the passed events in an
+output argument. This process terminates when either the mask value changes
+or at least one event has been returned and a new bucket is required to
+continue reading.
diff --git a/sys/qpoe/README b/sys/qpoe/README
new file mode 100644
index 00000000..dd48198b
--- /dev/null
+++ b/sys/qpoe/README
@@ -0,0 +1,323 @@
+QPOE -- Prototype POE (Position Ordered Event file) Interface.
+See QPOE.hlp for detailed information.
+-----------------------------------------------------------------
+
+
+1. QPOE (General QPOE file access)
+
+ [ --- external routines --- ]
+
+ qp_parse (expr, poefile, sz_poefile, paramex, sz_paramex)
+ yes|no = qp_access (poefile, mode)
+ qp_copy (o_poefile, n_poefile)
+ qp_rename (o_poefile, n_poefile)
+ qp_rebuild (poefile)
+ qp_delete (poefile)
+
+ ptr = qp_open (poefile, mode, o_qp)
+ qp_set[ir] (qp, param, value)
+ val = qp_stat[ir] (qp, param)
+ qp_debug (qp, out, what)
+ qp_sync (qp)
+ qp_close (qp)
+
+ qp_add[bcsilrdx] (qp, param, value, comment)
+ qp_astr (qp, param, value, comment)
+ val = qp_get[bcsilrdx] (qp, param)
+ nchars = qp_gstr (qp, param, outstr, maxch)
+ qp_put[bcsilrdx] (qp, param, value)
+ qp_pstr (qp, param, strval)
+
+ n = qp_read (qp, param, buf, maxelem, first, datatype)
+ qp_write (qp, param, buf, nelem, first, datatype)
+ fd = qp_popen (qp, param, mode, type)
+ mw = qp_loadwcs (qp)
+ qp_savewcs (qp, mw)
+
+ yes|no = qp_accessf (qp, param)
+ qp_deletef (qp, param)
+ qp_renamef (qp, param, newname)
+ qp_copyf (o_qp, o_param, n_qp, n_param)
+ qp_addf (qp, param, datatype, maxelem, comment, flags)
+ nelem = qp_queryf (qp, param, datatype, maxelem, comment, flags)
+ nelem = qp_lenf (qp, param)
+ nchars = qp_expandtext (qp, s1, s2, maxch)
+
+ ptr = qp_ofnl[su] (qp, template)
+ ptr = qp_ofnl (qp, template, sort)
+ n|EOF = qp_gnfn (fl, outstr, maxch)
+ len = qp_lenfnl (fl)
+ qp_seekfnl (fl, pos)
+ qp_cfnl (fl)
+
+ [ --- internal routines --- ]
+
+ qp_bind (qp)
+ dtype = qp_dtype (qp, datatype, dsym)
+nchars = qp_elementsize (qp, datatype)
+ nchars = qp_sizeof (qp, dtype, dsym)
+ qp_mkfname (poefile, extn, fname, maxch)
+ ival = qp_ctoi (str, ip, ival)
+ dval = qp_ctod (str, ip, dval)
+
+ ptr = qp_gmsym (qp, macro, textp)
+ ptr = qp_gpsym (qp, param)
+ nfields = qp_parsefl (qp, fieldlist, dd)
+ qp_inherit (n_qp, o_qp, out)
+ dtype = qp_getparam (qp, param, o_pp)
+ dtype = qp_putparam (qp, param, o_pp)
+ qp_flushpar (qp)
+
+ gt = qp_opentext (qp, text) # token i/o
+ token = qp_nexttok (gt)
+ token = qp_gettok (gt, tokbuf, maxch)
+ token = qp_rawtok (gt, outstr, maxch)
+ nargs = qp_arglist (gt, argbuf, maxch)
+ qp_closetext (gt)
+
+ ptr = qm_access () # macros
+ ch = qm_getc (fd, ch)
+ qm_scan (qm, fname, flags)
+ qm_scano (qm, fd, flags)
+ qm_[set|upd]defaults (qm, qp)
+ qm_setparam (qm, param, valstr)
+ ptr = qm_symtab (qm)
+
+
+2. QPIO (Event I/O)
+
+ [ --- external routines --- ]
+
+ io = qpio_open (qp, paramex, mode)
+ qpio_close (io)
+
+ qpio_setrange (io, vs, ve, ndim)
+ ndim = qpio_getrange (io, vs, ve, maxdim)
+ qpio_setfilter (io, expr)
+nchars = qpio_getfilter (io, outstr, maxch)
+ qpio_set[ir] (io, param, value)
+ val = qpio_stat[ir] (io, param)
+ mw = qpio_loadwcs (io)
+ qpio_mkindex (io, key)
+
+ qpio_putevents (io, i_ev, nevents)
+ n|EOF = qpio_getevents (io, o_ev, maskval, maxev, nev)
+ nev = qpio_readpix[si] (io, obuf, vs, ve, ndim, xblock, yblock)
+
+ [ --- internal routines --- ]
+
+ ok|err = qpio_parse (io, expr, filter, sz_filter, mask, sz_mask)
+ qpio_loadmask (io, mask, mergeflg)
+bkno|EOF = qpio_rbucket (io, bkno)
+ qpio_wbucket (io, n_bkno)
+ qpio_sync (io)
+
+
+3. QPEX (Event Attribute Filtering)
+
+ [ --- external routines --- ]
+
+ ex = qpex_open (qp, expr)
+ok|err = qpex_modfilter (ex, exprlist)
+nchars = qpex_getfilter (ex, outstr, maxch)
+ nc = qpex_getattribute (ex, attribute, outstr, maxch)
+ nr = qpex_attrl[ird] (ex, attribute, xs, xe, xlen)
+ nev = qpex_evaluate (ex, i_ev, o_ev, nev)
+ qpex_close (ex)
+
+ [ --- internal routines --- ]
+
+ v = qpex_parse[dir] (expr, xs, xe, xlen)
+ v = qpex_sublist[dir] (x1, x2, xs,xe,nranges,ip, o_xs,o_xe)
+ v = qpex_codegen[dir] (ex, atname, assignop, expr, offset, dtype)
+ qpex_delete (ex, offset, dtype)
+
+ ptr = qpex_pbpos (ex)
+ offset = qpex_refd (ex, value)
+ ptr = qpex_dballoc (ex, nelem, dtype)
+ ptr = qpex_dbpstr (ex, strval)
+ qpex_pbpin (ex, opcode, arg1, arg2, arg3)
+ qpex_mark (ex, pb_save, db_save)
+ qpex_free (ex, pb_save, db_save)
+
+ nr = qp_rlmerge[dir] (os,oe,olen, xs,xe,nx, ys,ye,ny)
+
+
+ 4. INTERFACE SYNTAX
+
+Default parameter and domain names:
+
+ "deffilt" # default event filter (all event lists)
+ "defmask" # default region mask (all event lists)
+ "deffilt.<evl>" # default event filter for event list <evl>
+ "defmask.<evl>" # default region mask for event list <evl>
+ "event" # default name of user event datatype
+ "events" # default event-list parameter
+
+QPIO expression syntax:
+
+ [ evl-param ][ `[' [`!'] keyword [(`:='|`='|`+=') expr], ...`]' ]
+
+where <evl-param> defaults to "events" if not given, and where <keyword> may
+be any of the following, or a term of an event attribute expression.
+
+ block # blocking factor for image matrix
+ debug # debug level (integer, 0=nodebug)
+ filter # event attribute filter (expression)
+ key # event key (Y,X) fields (e.g.(s10,s8))
+ noindex # don't use index even if present
+ param # name of event list header parameter
+ mask # region mask
+ rect # subregion of image, e.g, rect=[*,100:400]
+
+Any unrecognized keyword=expr terms are passed on to the event attribute
+filter, hence the "filter=(expr)" syntax is optional.
+
+QPEX expression syntax (the <expr> in "filter=<expr>" above):
+
+ '(' attribute=expr [, attribute=expr...] ')'
+
+where <attribute> is the "physical" name (type code plus byte offset) of
+a field of the event structure, and expr is
+
+ %N bitwise mask test
+ !%N negated bitwise mask test
+
+or some combination of
+
+ N equality test
+ :N open range (less than or equal to N)
+ N: open range (greater than or equal to N)
+ M:N range (M to N inclusive)
+ expr,expr,... list of values or ranges
+ '(' expr ')' parenthesized expr
+ '!' expr
+
+MACRO syntax:
+
+ macro replace <macro> by defined value
+ macro(arg,...) replace <macro> with argument substitution
+ @file replace <@file> by contents of file
+ `cmd` replace <`cmd`> by output of the CL command "cmd",
+ replacing all newlines by spaces
+ @file(arg,...) file pushback with argument substitution
+ `cmd`(arg,...) command output pushback with argument substitution
+
+Macro define syntax:
+
+ define macro replacement-text
+ set parameter value
+
+where <macro> is any identifier, and <replacement-text> is literal text to
+be pushed back into the input and rescanned when <macro> is encountered in
+the input stream. <replacement-text> may contain symbols of the form '$N'
+denoting places where argument substitution is to be performed during pushback.
+The special builtin macro $DFN will be replaced by the datafile name, returned
+as a string token.
+
+<parameter> denotes a QPOE interface parameter the value of which is to be
+set, e.g., to set the size of a buffer to be created at run time. The
+following interface parameters are defined:
+
+ "bucketlen" QPIO bucket length, nevents
+ "cachesize" number of file descriptors in lfile cache
+ "indexlen" number of hash entries in symbol table index
+ "maxlfiles" maximum number of lfiles in datafile
+ "pagesize" page size, bytes, of datafile
+ "sbufsize" initial symbol table size, su
+ "stablen" initial symbol table string buffer size, chars
+ "progbuflen" QPEX program buffer size (compiled instructions)
+ "databuflen" QPEX data buffer size (initialized data space)
+ "nodeffilt" disable the use of any default event filters
+ "nodefmask" disable the use of any default region masks
+ "maxpushback" max characters pushback (for macro expansion)
+ "maxfrlutlen" max full resolution lookup table length
+ "maxrrlutlen" max reduced resolution lookup table length
+ "lutminranges" min ranges required before a lookup table is used
+ "lutscale" scale factor to convert nranges to n LUT bins
+ "blockfactor" QPIO blocking factor for output pixel arrays
+ "optbufsize" FIO i/o buffer size for IMIO access to QPOE file, chars
+ "debuglevel" debug level, 0 for no runtime debug messages
+
+Environment:
+
+ 'qmfiles' an environment variable listing a set of macro
+ define files defining the global macros to be
+ used by QPOE
+
+ 'qmsave' an environment variable defining the name of a
+ file to be used to store the compiled macros
+ (defaults to uparm$qpoe.msv).
+
+
+ 5. INTERFACE PARAMETERS
+
+# QPSET.H -- User accessible definitions for the QPOE package.
+
+define SZ_COMMENT 79 # max size comment string
+define SZ_DATATYPE 79 # max size datatype name string
+
+# QPOE Read-Write Parameters.
+define QPOE_BLOCKFACTOR 1 # blocking factor for pixel arrays
+define QPOE_BUCKETLEN 2 # event list bucket length, nevents
+define QPOE_CACHESIZE 3 # lfile (buffer) cache size, nlfiles
+define QPOE_DATABUFLEN 4 # QPEX data buffer length, chars
+define QPOE_DEBUGLEVEL 5 # debug level (0 = no messages)
+define QPOE_DEFLUTLEN 6 # default lookup table length (bins)
+define QPOE_INDEXLEN 7 # symbol table hash index length
+define QPOE_LUTMINRANGES 8 # min ranges before using LUT
+define QPOE_LUTSCALE 9 # scale nranges to LUT bins
+define QPOE_MAXFRLUTLEN 10 # max full-res LUT length
+define QPOE_MAXLFILES 11 # max lfiles in datafile
+define QPOE_MAXPUSHBACK 12 # max amount of pushed back macro data
+define QPOE_MAXRRLUTLEN 13 # max reduced-res LUT length
+define QPOE_OPTBUFSIZE 14 # optimum buffer size for IMIO/QPF/FIO
+define QPOE_PAGESIZE 15 # page size of datafile, bytes
+define QPOE_PROGBUFLEN 16 # QPEX program buffer length, ints
+define QPOE_SBUFSIZE 17 # symtab string buf size, chars (init)
+define QPOE_STABLEN 18 # symtab data area size, su (init)
+define QPOE_NODEFFILT 19 # disable use of default filter
+define QPOE_NODEFMASK 20 # disable use of default mask
+
+# QPOE Read-Only Parameters.
+define QPOE_FM 21 # FMIO descriptor
+define QPOE_MODE 22 # poefile access mode
+define QPOE_ST 23 # SYMTAB symbol table descriptor
+define QPOE_VERSION 24 # QPOE version number
+
+# Parameter flags (for qp_addf).
+define QPF_NONE (-1) # no flags (0 gives default flags)
+define QPF_INHERIT 0002B # copy parameter in a NEW_COPY open
+
+
+# QPIOSET.H -- QPIO User accessible interface parameters.
+
+define qpio_stati qpiost # (name collision with qpio_seti)
+
+# Read-Write Parameters.
+define QPIO_BLOCKFACTOR 1 # blocking factor for image matrices
+define QPIO_BUCKETLEN 2 # event list bucket size, nevents
+define QPIO_DEBUG 3 # debug level, debug=0 for no messages
+define QPIO_EVXOFF 4 # short offset of X field of event
+define QPIO_EVYOFF 5 # short offset of Y field of event
+define QPIO_EX 6 # QPEX descriptor (event attr. filter)
+define QPIO_NODEFFILT 7 # disable use of default filter
+define QPIO_NODEFMASK 8 # disable use of default mask
+define QPIO_NOINDEX 9 # flag to disable use of index
+define QPIO_OPTBUFSIZE 10 # optimum buffer size for IMIO/QPF/FIO
+define QPIO_PL 11 # PLIO descriptor (pixel mask)
+
+# Read-Only Parameters.
+define QPIO_EVENTLEN 12 # length of event struct, shorts
+define QPIO_FD 13 # file descriptor of event list lfile
+define QPIO_INDEXLEN 14 # event list index length (0=noindex)
+define QPIO_IXXOFF 15 # short offset of X field used in index
+define QPIO_IXYOFF 16 # short offset of Y field used in index
+define QPIO_LF 17 # lfile in which event list is stored
+define QPIO_MASKP 18 # char pointer to mask-name buffer
+define QPIO_MAXEVP 19 # pointer to MAX-event fields struct
+define QPIO_MINEVP 20 # pointer to MIN-event fields struct
+define QPIO_NCOLS 21 # number of columns in image
+define QPIO_NLINES 22 # number of lines in image
+define QPIO_PARAMP 23 # char pointer to param-name buffer
+define QPIO_QP 24 # backpointer to QPOE descriptor
diff --git a/sys/qpoe/gen/mkpkg b/sys/qpoe/gen/mkpkg
new file mode 100644
index 00000000..8c08a1f7
--- /dev/null
+++ b/sys/qpoe/gen/mkpkg
@@ -0,0 +1,47 @@
+# Update the generically expanded files in libex.a.
+
+$checkout libex.a lib$
+$update libex.a
+$checkin libex.a lib$
+$exit
+
+libex.a:
+ qpaddb.x ../qpoe.h
+ qpaddc.x ../qpoe.h
+ qpaddd.x ../qpoe.h
+ qpaddi.x ../qpoe.h
+ qpaddl.x ../qpoe.h
+ qpaddr.x ../qpoe.h
+ qpadds.x ../qpoe.h
+ qpaddx.x ../qpoe.h
+ qpexattrld.x ../qpex.h <ctype.h> <mach.h>
+ qpexattrli.x ../qpex.h <ctype.h> <mach.h>
+ qpexattrlr.x ../qpex.h <ctype.h> <mach.h>
+ qpexcoded.x ../qpex.h <mach.h>
+ qpexcodei.x ../qpex.h <mach.h>
+ qpexcoder.x ../qpex.h <mach.h>
+ qpexparsed.x ../qpex.h <ctype.h> <mach.h>
+ qpexparsei.x ../qpex.h <ctype.h> <mach.h>
+ qpexparser.x ../qpex.h <ctype.h> <mach.h>
+ qpexsubd.x ../qpex.h <mach.h>
+ qpexsubi.x ../qpex.h <mach.h>
+ qpexsubr.x ../qpex.h <mach.h>
+ qpgetc.x ../qpoe.h
+ qpgetd.x ../qpoe.h
+ qpgeti.x ../qpoe.h
+ qpgetl.x ../qpoe.h
+ qpgetr.x ../qpoe.h
+ qpgets.x ../qpoe.h
+ qpiogetev.x ../qpio.h <pmset.h>
+ qpiorpixi.x ../qpio.h <mach.h>
+ qpiorpixs.x ../qpio.h <mach.h>
+ qpputc.x ../qpoe.h
+ qpputd.x ../qpoe.h
+ qpputi.x ../qpoe.h
+ qpputl.x ../qpoe.h
+ qpputr.x ../qpoe.h
+ qpputs.x ../qpoe.h
+ qprlmerged.x ../qpex.h <mach.h>
+ qprlmergei.x ../qpex.h <mach.h>
+ qprlmerger.x ../qpex.h <mach.h>
+ ;
diff --git a/sys/qpoe/gen/qpaddb.x b/sys/qpoe/gen/qpaddb.x
new file mode 100644
index 00000000..1291824a
--- /dev/null
+++ b/sys/qpoe/gen/qpaddb.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../qpoe.h"
+
+# QP_ADD -- Set the value of a parameter, creating the parameter if it does
+# not already exist. This works for the most common case of simple scalar
+# valued header parameters, although any parameter may be written into it it
+# already exists. Additional control over the parameter attributes is possible
+# if the parameter is explicitly created with qp_addf before being written into.
+
+procedure qp_addb (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+bool value #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+char datatype[1]
+errchk qp_accessf, qp_addf
+string dtypes SPPTYPES
+int qp_accessf()
+
+begin
+ if (qp_accessf (qp, param) == NO) {
+ datatype[1] = dtypes[TY_BOOL]
+ call qp_addf (qp, param, datatype, 1, comment, 0)
+ }
+ call qp_putb (qp, param, value)
+end
diff --git a/sys/qpoe/gen/qpaddc.x b/sys/qpoe/gen/qpaddc.x
new file mode 100644
index 00000000..64264e20
--- /dev/null
+++ b/sys/qpoe/gen/qpaddc.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../qpoe.h"
+
+# QP_ADD -- Set the value of a parameter, creating the parameter if it does
+# not already exist. This works for the most common case of simple scalar
+# valued header parameters, although any parameter may be written into it it
+# already exists. Additional control over the parameter attributes is possible
+# if the parameter is explicitly created with qp_addf before being written into.
+
+procedure qp_addc (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char value #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+char datatype[1]
+errchk qp_accessf, qp_addf
+string dtypes SPPTYPES
+int qp_accessf()
+
+begin
+ if (qp_accessf (qp, param) == NO) {
+ datatype[1] = dtypes[TY_CHAR]
+ call qp_addf (qp, param, datatype, 1, comment, 0)
+ }
+ call qp_putc (qp, param, value)
+end
diff --git a/sys/qpoe/gen/qpaddd.x b/sys/qpoe/gen/qpaddd.x
new file mode 100644
index 00000000..61db744e
--- /dev/null
+++ b/sys/qpoe/gen/qpaddd.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../qpoe.h"
+
+# QP_ADD -- Set the value of a parameter, creating the parameter if it does
+# not already exist. This works for the most common case of simple scalar
+# valued header parameters, although any parameter may be written into it it
+# already exists. Additional control over the parameter attributes is possible
+# if the parameter is explicitly created with qp_addf before being written into.
+
+procedure qp_addd (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+double value #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+char datatype[1]
+errchk qp_accessf, qp_addf
+string dtypes SPPTYPES
+int qp_accessf()
+
+begin
+ if (qp_accessf (qp, param) == NO) {
+ datatype[1] = dtypes[TY_DOUBLE]
+ call qp_addf (qp, param, datatype, 1, comment, 0)
+ }
+ call qp_putd (qp, param, value)
+end
diff --git a/sys/qpoe/gen/qpaddi.x b/sys/qpoe/gen/qpaddi.x
new file mode 100644
index 00000000..47d746c6
--- /dev/null
+++ b/sys/qpoe/gen/qpaddi.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../qpoe.h"
+
+# QP_ADD -- Set the value of a parameter, creating the parameter if it does
+# not already exist. This works for the most common case of simple scalar
+# valued header parameters, although any parameter may be written into it it
+# already exists. Additional control over the parameter attributes is possible
+# if the parameter is explicitly created with qp_addf before being written into.
+
+procedure qp_addi (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+int value #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+char datatype[1]
+errchk qp_accessf, qp_addf
+string dtypes SPPTYPES
+int qp_accessf()
+
+begin
+ if (qp_accessf (qp, param) == NO) {
+ datatype[1] = dtypes[TY_INT]
+ call qp_addf (qp, param, datatype, 1, comment, 0)
+ }
+ call qp_puti (qp, param, value)
+end
diff --git a/sys/qpoe/gen/qpaddl.x b/sys/qpoe/gen/qpaddl.x
new file mode 100644
index 00000000..f5e0cac2
--- /dev/null
+++ b/sys/qpoe/gen/qpaddl.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../qpoe.h"
+
+# QP_ADD -- Set the value of a parameter, creating the parameter if it does
+# not already exist. This works for the most common case of simple scalar
+# valued header parameters, although any parameter may be written into it it
+# already exists. Additional control over the parameter attributes is possible
+# if the parameter is explicitly created with qp_addf before being written into.
+
+procedure qp_addl (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+long value #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+char datatype[1]
+errchk qp_accessf, qp_addf
+string dtypes SPPTYPES
+int qp_accessf()
+
+begin
+ if (qp_accessf (qp, param) == NO) {
+ datatype[1] = dtypes[TY_LONG]
+ call qp_addf (qp, param, datatype, 1, comment, 0)
+ }
+ call qp_putl (qp, param, value)
+end
diff --git a/sys/qpoe/gen/qpaddr.x b/sys/qpoe/gen/qpaddr.x
new file mode 100644
index 00000000..ec367ab7
--- /dev/null
+++ b/sys/qpoe/gen/qpaddr.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../qpoe.h"
+
+# QP_ADD -- Set the value of a parameter, creating the parameter if it does
+# not already exist. This works for the most common case of simple scalar
+# valued header parameters, although any parameter may be written into it it
+# already exists. Additional control over the parameter attributes is possible
+# if the parameter is explicitly created with qp_addf before being written into.
+
+procedure qp_addr (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+real value #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+char datatype[1]
+errchk qp_accessf, qp_addf
+string dtypes SPPTYPES
+int qp_accessf()
+
+begin
+ if (qp_accessf (qp, param) == NO) {
+ datatype[1] = dtypes[TY_REAL]
+ call qp_addf (qp, param, datatype, 1, comment, 0)
+ }
+ call qp_putr (qp, param, value)
+end
diff --git a/sys/qpoe/gen/qpadds.x b/sys/qpoe/gen/qpadds.x
new file mode 100644
index 00000000..67036fda
--- /dev/null
+++ b/sys/qpoe/gen/qpadds.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../qpoe.h"
+
+# QP_ADD -- Set the value of a parameter, creating the parameter if it does
+# not already exist. This works for the most common case of simple scalar
+# valued header parameters, although any parameter may be written into it it
+# already exists. Additional control over the parameter attributes is possible
+# if the parameter is explicitly created with qp_addf before being written into.
+
+procedure qp_adds (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+short value #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+char datatype[1]
+errchk qp_accessf, qp_addf
+string dtypes SPPTYPES
+int qp_accessf()
+
+begin
+ if (qp_accessf (qp, param) == NO) {
+ datatype[1] = dtypes[TY_SHORT]
+ call qp_addf (qp, param, datatype, 1, comment, 0)
+ }
+ call qp_puts (qp, param, value)
+end
diff --git a/sys/qpoe/gen/qpaddx.x b/sys/qpoe/gen/qpaddx.x
new file mode 100644
index 00000000..d147748e
--- /dev/null
+++ b/sys/qpoe/gen/qpaddx.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../qpoe.h"
+
+# QP_ADD -- Set the value of a parameter, creating the parameter if it does
+# not already exist. This works for the most common case of simple scalar
+# valued header parameters, although any parameter may be written into it it
+# already exists. Additional control over the parameter attributes is possible
+# if the parameter is explicitly created with qp_addf before being written into.
+
+procedure qp_addx (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+complex value #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+char datatype[1]
+errchk qp_accessf, qp_addf
+string dtypes SPPTYPES
+int qp_accessf()
+
+begin
+ if (qp_accessf (qp, param) == NO) {
+ datatype[1] = dtypes[TY_COMPLEX]
+ call qp_addf (qp, param, datatype, 1, comment, 0)
+ }
+ call qp_putx (qp, param, value)
+end
diff --git a/sys/qpoe/gen/qpexattrld.x b/sys/qpoe/gen/qpexattrld.x
new file mode 100644
index 00000000..5954cbe4
--- /dev/null
+++ b/sys/qpoe/gen/qpexattrld.x
@@ -0,0 +1,127 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+include "../qpex.h"
+
+# QPEX_ATTRL -- Get the good-value range list for the named attribute, as a
+# binary range list of the indicated type. This range list is a simplified
+# version of the original filter expression, which may have contained
+# multiple fields, some negated or overlapping, in any order, subsequently
+# modified or deleted with qpex_modfilter, etc. The final resultant range
+# list is ordered and consists of discreet nonoverlapping ranges.
+#
+# Upon input the variables XS and XE should either point to a pair of
+# preallocated buffers of length XLEN, or they should be set to NULL.
+# The routine will reallocate the buffers as necessary to allow for long
+# range lists, updating XLEN so that it always contains the actual length
+# of the arrays (which may not be completely full). Each list element
+# consists of a pair of values (xs[i],xe[i]) defining the start and end
+# points of the range. If xs[1] is INDEF the range is open to the left,
+# if xe[nranges] is INDEF the range is open to the right. The number of
+# ranges output is returned as the function value.
+
+int procedure qpex_attrld (ex, attribute, xs, xe, xlen)
+
+pointer ex #I QPEX descriptor
+char attribute[ARB] #I attribute name
+pointer xs #U pointer to array of start values
+pointer xe #U pointer to array of end values
+int xlen #U length of xs/xe arrays
+
+pointer ps, pe, qs, qe
+pointer sp, expr, ip, ep
+int plen, qlen, np, nq, nx
+int neterms, nchars, maxch
+int qpex_getattribute(), qpex_parsed(), qp_rlmerged()
+
+begin
+ call smark (sp)
+
+ # Get attribute filter expression. In the unlikely event that the
+ # expression is too large to fit in our buffer, repeat with a buffer
+ # twice as large until it fits.
+
+ maxch = DEF_SZEXPRBUF
+ nchars = 0
+
+ repeat {
+ maxch = maxch * 2
+ call salloc (expr, maxch, TY_CHAR)
+ nchars = qpex_getattribute (ex, attribute, Memc[expr], maxch)
+ if (nchars <= 0)
+ break
+ } until (nchars < maxch)
+
+ # Parse expression to produce a range list. If the expression
+ # contains multiple eterms each is parsed separately and merged
+ # into the final output range list.
+
+ nx = 0
+ neterms = 0
+
+ if (nchars > 0) {
+ # Get range list storage.
+ plen = DEF_XLEN
+ call malloc (ps, plen, TY_DOUBLE)
+ call malloc (pe, plen, TY_DOUBLE)
+ qlen = DEF_XLEN
+ call malloc (qs, qlen, TY_DOUBLE)
+ call malloc (qe, qlen, TY_DOUBLE)
+
+ # Parse each subexpression and merge into output range list.
+ for (ip=expr; Memc[ip] != EOS; ) {
+ # Get next subexpression.
+ while (IS_WHITE (Memc[ip]))
+ ip = ip + 1
+ for (ep=ip; Memc[ip] != EOS; ip=ip+1)
+ if (Memc[ip] == ';') {
+ Memc[ip] = EOS
+ ip = ip + 1
+ break
+ }
+ if (Memc[ep] == EOS)
+ break
+
+ # Copy output range list to X list temporary.
+ if (max(nx,1) > plen) {
+ plen = max(xlen,1)
+ call realloc (ps, plen, TY_DOUBLE)
+ call realloc (pe, plen, TY_DOUBLE)
+ }
+ if (neterms <= 0) {
+ Memd[ps] = LEFTD
+ Memd[pe] = RIGHTD
+ np = 1
+ } else {
+ call amovd (Memd[xs], Memd[ps], nx)
+ call amovd (Memd[xe], Memd[pe], nx)
+ np = nx
+ }
+
+ # Parse next eterm into Y list temporary.
+ nq = qpex_parsed (Memc[ep], qs, qe, qlen)
+
+ # Merge the X and Y lists, leaving result in output list.
+ nx = qp_rlmerged (xs,xe,xlen,
+ Memd[ps], Memd[pe], np, Memd[qs], Memd[qe], nq)
+
+ neterms = neterms + 1
+ }
+
+ # Free temporary range list storage.
+ call mfree (ps, TY_DOUBLE); call mfree (pe, TY_DOUBLE)
+ call mfree (qs, TY_DOUBLE); call mfree (qe, TY_DOUBLE)
+
+ # Convert LEFT/RIGHT magic values to INDEF.
+ if (nx > 0) {
+ if (IS_LEFTD (Memd[xs]))
+ Memd[xs] = INDEFD
+ if (IS_RIGHTD (Memd[xe+nx-1]))
+ Memd[xe+nx-1] = INDEFD
+ }
+ }
+
+ call sfree (sp)
+ return (nx)
+end
diff --git a/sys/qpoe/gen/qpexattrli.x b/sys/qpoe/gen/qpexattrli.x
new file mode 100644
index 00000000..706aecc8
--- /dev/null
+++ b/sys/qpoe/gen/qpexattrli.x
@@ -0,0 +1,127 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+include "../qpex.h"
+
+# QPEX_ATTRL -- Get the good-value range list for the named attribute, as a
+# binary range list of the indicated type. This range list is a simplified
+# version of the original filter expression, which may have contained
+# multiple fields, some negated or overlapping, in any order, subsequently
+# modified or deleted with qpex_modfilter, etc. The final resultant range
+# list is ordered and consists of discreet nonoverlapping ranges.
+#
+# Upon input the variables XS and XE should either point to a pair of
+# preallocated buffers of length XLEN, or they should be set to NULL.
+# The routine will reallocate the buffers as necessary to allow for long
+# range lists, updating XLEN so that it always contains the actual length
+# of the arrays (which may not be completely full). Each list element
+# consists of a pair of values (xs[i],xe[i]) defining the start and end
+# points of the range. If xs[1] is INDEF the range is open to the left,
+# if xe[nranges] is INDEF the range is open to the right. The number of
+# ranges output is returned as the function value.
+
+int procedure qpex_attrli (ex, attribute, xs, xe, xlen)
+
+pointer ex #I QPEX descriptor
+char attribute[ARB] #I attribute name
+pointer xs #U pointer to array of start values
+pointer xe #U pointer to array of end values
+int xlen #U length of xs/xe arrays
+
+pointer ps, pe, qs, qe
+pointer sp, expr, ip, ep
+int plen, qlen, np, nq, nx
+int neterms, nchars, maxch
+int qpex_getattribute(), qpex_parsei(), qp_rlmergei()
+
+begin
+ call smark (sp)
+
+ # Get attribute filter expression. In the unlikely event that the
+ # expression is too large to fit in our buffer, repeat with a buffer
+ # twice as large until it fits.
+
+ maxch = DEF_SZEXPRBUF
+ nchars = 0
+
+ repeat {
+ maxch = maxch * 2
+ call salloc (expr, maxch, TY_CHAR)
+ nchars = qpex_getattribute (ex, attribute, Memc[expr], maxch)
+ if (nchars <= 0)
+ break
+ } until (nchars < maxch)
+
+ # Parse expression to produce a range list. If the expression
+ # contains multiple eterms each is parsed separately and merged
+ # into the final output range list.
+
+ nx = 0
+ neterms = 0
+
+ if (nchars > 0) {
+ # Get range list storage.
+ plen = DEF_XLEN
+ call malloc (ps, plen, TY_INT)
+ call malloc (pe, plen, TY_INT)
+ qlen = DEF_XLEN
+ call malloc (qs, qlen, TY_INT)
+ call malloc (qe, qlen, TY_INT)
+
+ # Parse each subexpression and merge into output range list.
+ for (ip=expr; Memc[ip] != EOS; ) {
+ # Get next subexpression.
+ while (IS_WHITE (Memc[ip]))
+ ip = ip + 1
+ for (ep=ip; Memc[ip] != EOS; ip=ip+1)
+ if (Memc[ip] == ';') {
+ Memc[ip] = EOS
+ ip = ip + 1
+ break
+ }
+ if (Memc[ep] == EOS)
+ break
+
+ # Copy output range list to X list temporary.
+ if (max(nx,1) > plen) {
+ plen = max(xlen,1)
+ call realloc (ps, plen, TY_INT)
+ call realloc (pe, plen, TY_INT)
+ }
+ if (neterms <= 0) {
+ Memi[ps] = LEFTI
+ Memi[pe] = RIGHTI
+ np = 1
+ } else {
+ call amovi (Memi[xs], Memi[ps], nx)
+ call amovi (Memi[xe], Memi[pe], nx)
+ np = nx
+ }
+
+ # Parse next eterm into Y list temporary.
+ nq = qpex_parsei (Memc[ep], qs, qe, qlen)
+
+ # Merge the X and Y lists, leaving result in output list.
+ nx = qp_rlmergei (xs,xe,xlen,
+ Memi[ps], Memi[pe], np, Memi[qs], Memi[qe], nq)
+
+ neterms = neterms + 1
+ }
+
+ # Free temporary range list storage.
+ call mfree (ps, TY_INT); call mfree (pe, TY_INT)
+ call mfree (qs, TY_INT); call mfree (qe, TY_INT)
+
+ # Convert LEFT/RIGHT magic values to INDEF.
+ if (nx > 0) {
+ if (IS_LEFTI (Memi[xs]))
+ Memi[xs] = INDEFI
+ if (IS_RIGHTI (Memi[xe+nx-1]))
+ Memi[xe+nx-1] = INDEFI
+ }
+ }
+
+ call sfree (sp)
+ return (nx)
+end
diff --git a/sys/qpoe/gen/qpexattrlr.x b/sys/qpoe/gen/qpexattrlr.x
new file mode 100644
index 00000000..c13a7511
--- /dev/null
+++ b/sys/qpoe/gen/qpexattrlr.x
@@ -0,0 +1,127 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+include "../qpex.h"
+
+# QPEX_ATTRL -- Get the good-value range list for the named attribute, as a
+# binary range list of the indicated type. This range list is a simplified
+# version of the original filter expression, which may have contained
+# multiple fields, some negated or overlapping, in any order, subsequently
+# modified or deleted with qpex_modfilter, etc. The final resultant range
+# list is ordered and consists of discreet nonoverlapping ranges.
+#
+# Upon input the variables XS and XE should either point to a pair of
+# preallocated buffers of length XLEN, or they should be set to NULL.
+# The routine will reallocate the buffers as necessary to allow for long
+# range lists, updating XLEN so that it always contains the actual length
+# of the arrays (which may not be completely full). Each list element
+# consists of a pair of values (xs[i],xe[i]) defining the start and end
+# points of the range. If xs[1] is INDEF the range is open to the left,
+# if xe[nranges] is INDEF the range is open to the right. The number of
+# ranges output is returned as the function value.
+
+int procedure qpex_attrlr (ex, attribute, xs, xe, xlen)
+
+pointer ex #I QPEX descriptor
+char attribute[ARB] #I attribute name
+pointer xs #U pointer to array of start values
+pointer xe #U pointer to array of end values
+int xlen #U length of xs/xe arrays
+
+pointer ps, pe, qs, qe
+pointer sp, expr, ip, ep
+int plen, qlen, np, nq, nx
+int neterms, nchars, maxch
+int qpex_getattribute(), qpex_parser(), qp_rlmerger()
+
+begin
+ call smark (sp)
+
+ # Get attribute filter expression. In the unlikely event that the
+ # expression is too large to fit in our buffer, repeat with a buffer
+ # twice as large until it fits.
+
+ maxch = DEF_SZEXPRBUF
+ nchars = 0
+
+ repeat {
+ maxch = maxch * 2
+ call salloc (expr, maxch, TY_CHAR)
+ nchars = qpex_getattribute (ex, attribute, Memc[expr], maxch)
+ if (nchars <= 0)
+ break
+ } until (nchars < maxch)
+
+ # Parse expression to produce a range list. If the expression
+ # contains multiple eterms each is parsed separately and merged
+ # into the final output range list.
+
+ nx = 0
+ neterms = 0
+
+ if (nchars > 0) {
+ # Get range list storage.
+ plen = DEF_XLEN
+ call malloc (ps, plen, TY_REAL)
+ call malloc (pe, plen, TY_REAL)
+ qlen = DEF_XLEN
+ call malloc (qs, qlen, TY_REAL)
+ call malloc (qe, qlen, TY_REAL)
+
+ # Parse each subexpression and merge into output range list.
+ for (ip=expr; Memc[ip] != EOS; ) {
+ # Get next subexpression.
+ while (IS_WHITE (Memc[ip]))
+ ip = ip + 1
+ for (ep=ip; Memc[ip] != EOS; ip=ip+1)
+ if (Memc[ip] == ';') {
+ Memc[ip] = EOS
+ ip = ip + 1
+ break
+ }
+ if (Memc[ep] == EOS)
+ break
+
+ # Copy output range list to X list temporary.
+ if (max(nx,1) > plen) {
+ plen = max(xlen,1)
+ call realloc (ps, plen, TY_REAL)
+ call realloc (pe, plen, TY_REAL)
+ }
+ if (neterms <= 0) {
+ Memr[ps] = LEFTR
+ Memr[pe] = RIGHTR
+ np = 1
+ } else {
+ call amovr (Memr[xs], Memr[ps], nx)
+ call amovr (Memr[xe], Memr[pe], nx)
+ np = nx
+ }
+
+ # Parse next eterm into Y list temporary.
+ nq = qpex_parser (Memc[ep], qs, qe, qlen)
+
+ # Merge the X and Y lists, leaving result in output list.
+ nx = qp_rlmerger (xs,xe,xlen,
+ Memr[ps], Memr[pe], np, Memr[qs], Memr[qe], nq)
+
+ neterms = neterms + 1
+ }
+
+ # Free temporary range list storage.
+ call mfree (ps, TY_REAL); call mfree (pe, TY_REAL)
+ call mfree (qs, TY_REAL); call mfree (qe, TY_REAL)
+
+ # Convert LEFT/RIGHT magic values to INDEF.
+ if (nx > 0) {
+ if (IS_LEFTR (Memr[xs]))
+ Memr[xs] = INDEFR
+ if (IS_RIGHTR (Memr[xe+nx-1]))
+ Memr[xe+nx-1] = INDEFR
+ }
+ }
+
+ call sfree (sp)
+ return (nx)
+end
diff --git a/sys/qpoe/gen/qpexcoded.x b/sys/qpoe/gen/qpexcoded.x
new file mode 100644
index 00000000..63ec2541
--- /dev/null
+++ b/sys/qpoe/gen/qpexcoded.x
@@ -0,0 +1,370 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QPEX_CODEGEN -- Generate interpreter metacode to evaluate the given
+# expression. The new code is appended to the current compiled program,
+# adding additional constraints which a data event will have to meet to
+# pass the filter.
+
+int procedure qpex_codegend (ex, atname, assignop, expr, offset, dtype)
+
+pointer ex #I qpex descriptor
+char atname[ARB] #I attribute name (for expr regeneration)
+char assignop[ARB] #I "=" or "+=" (for expr regeneration)
+char expr[ARB] #I expression to be compiled
+int offset #I typed offset of referenced attribute
+int dtype #I datatype of referenced attribute
+
+int nbins, bin, xp
+pointer lt, lut, lutx, pb
+double x1, x2, xmin, xmax
+int xlen, nranges, n_nranges, level, opcode, ip, i
+pointer pb_save, db_save, xs_buf, xe_buf, xs, xe, n_xs, n_xe, et, prev
+
+double sv_xs[MAX_LEVELS], sv_xe[MAX_LEVELS]
+pointer sv_lt[MAX_LEVELS], sv_lut[MAX_LEVELS], sv_lutx[MAX_LEVELS]
+int sv_xp[MAX_LEVELS], sv_nranges[MAX_LEVELS], sv_bin[MAX_LEVELS]
+int sv_nbins[MAX_LEVELS]
+
+double xoffset, xscale
+double sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS]
+int d_x1, d_x2
+int qpex_refd()
+
+bool fp_equald()
+
+
+int qpex_parsed()
+int stridxs(), btoi(), qpex_sublistd()
+pointer qpex_dballoc(), qpex_dbpstr(), qpex_pbpos()
+errchk qpex_dballoc, qpex_pbpin, malloc, calloc, realloc, qpex_parsed
+
+string qpexwarn "QPEX Warning"
+define error_ 91
+define next_ 92
+define null_ 93
+define resume_ 94
+define bbmask_ 95
+define continue_ 96
+define XS Memd[xs+($1)-1]
+define XE Memd[xe+($1)-1]
+
+begin
+ pb = EX_PB(ex)
+
+ # Save the program state in case we have to abort.
+ call qpex_mark (ex, pb_save, db_save)
+
+ # Allocate and initialize a new expression term descriptor, linking
+ # it onto the tail of the ETTERMs list.
+
+ et = qpex_dballoc (ex, LEN_ETDES, TY_STRUCT)
+
+ ET_ATTTYPE(et) = dtype
+ ET_ATTOFF(et) = offset
+ ET_ATNAME(et) = qpex_dbpstr (ex, atname)
+ ET_ASSIGNOP(et) = qpex_dbpstr (ex, assignop)
+ ET_EXPRTEXT(et) = qpex_dbpstr (ex, expr)
+ ET_PROGPTR(et) = qpex_pbpos (ex)
+ ET_DELETED(et) = NO
+
+ prev = EX_ETTAIL(ex)
+ if (prev != NULL)
+ ET_NEXT(prev) = et
+ ET_NEXT(et) = NULL
+ EX_ETTAIL(ex) = et
+ if (EX_ETHEAD(ex) == NULL)
+ EX_ETHEAD(ex) = et
+
+ ip = stridxs ("%", expr)
+ # Bitmask tests are meaningless for floating point data.
+ if (ip > 0) {
+ call eprintf ("%s: bitmasks not permitted for floating data\n")
+ call pargstr (qpexwarn)
+ goto error_
+ }
+
+ # Compile a general range list expression. The basic procedure is
+ # to parse the expression to produce an optimized binary range list,
+ # then either compile the range list as an explicit series of
+ # instructions or as a lookup table, depending upon the number of
+ # ranges.
+
+ xlen = DEF_XLEN
+ call malloc (xs_buf, xlen, TY_DOUBLE)
+ call malloc (xe_buf, xlen, TY_DOUBLE)
+
+ # Convert expr to a binary range list and set up the initial context.
+ # Ensure that the range list buffers are large enough to hold any
+ # sublists extracted during compilation.
+
+ nranges = qpex_parsed (expr, xs_buf, xe_buf, xlen)
+ if (xlen < nranges * 2) {
+ xlen = nranges * 2
+ call realloc (xs_buf, xlen, TY_DOUBLE)
+ call realloc (xe_buf, xlen, TY_DOUBLE)
+ }
+
+ xs = xs_buf
+ xe = xe_buf
+ level = 0
+
+ repeat {
+next_
+ # Compile a new range list (or sublist).
+ if (nranges <= 0) {
+ # This shouldn't happen.
+null_ call eprintf ("%s: null range list\n")
+ call pargstr (qpexwarn)
+ call qpex_pbpin (ex, PASS, 0, 0, 0)
+
+ } else if (nranges == 1) {
+ # Output an instruction to load the data, perform the range
+ # test, and conditionally exit all in a single instruction.
+
+ x1 = XS(1); x2 = XE(1)
+ d_x1 = qpex_refd (ex, x1)
+ d_x2 = qpex_refd (ex, x2)
+
+ if (dtype == TY_SHORT) {
+ if (IS_LEFTD(x1) && IS_RIGHTD(x2))
+ ; # pass everything (no tests)
+ else if (IS_LEFTD(x1))
+ call qpex_pbpin (ex, LEQXS, offset, d_x2, 0)
+ else if (IS_RIGHTD(x2))
+ call qpex_pbpin (ex, GEQXS, offset, d_x1, 0)
+ else if (fp_equald (x1, x2))
+ call qpex_pbpin (ex, EQLXS, offset, d_x1, d_x2)
+ else
+ call qpex_pbpin (ex, RNGXS, offset, d_x1, d_x2)
+ } else {
+ if (IS_LEFTD(x1) && IS_RIGHTD(x2))
+ ; # pass everything (no tests)
+ else if (IS_LEFTD(x1))
+ call qpex_pbpin (ex, LEQXD, offset, d_x2, 0)
+ else if (IS_RIGHTD(x2))
+ call qpex_pbpin (ex, GEQXD, offset, d_x1, 0)
+ else if (fp_equald (x1, x2))
+ call qpex_pbpin (ex, EQLXD, offset, d_x1, d_x2)
+ else
+ call qpex_pbpin (ex, RNGXD, offset, d_x1, d_x2)
+ }
+
+ } else if (nranges < EX_LUTMINRANGES(ex)) {
+ # If the number of ranges to be tested for the data is small,
+ # compile explicit code to perform the range tests directly.
+ # Otherwise skip forward and compile a lookup table instead.
+ # In either case, the function of the instructions compiled
+ # is to test the data loaded into the register above, setting
+ # the value of PASS to true if the data lies in any of the
+ # indicated ranges.
+
+ # Check for !X, which is indicated in range list form by a
+ # two element list bracketing the X on each side.
+
+ if (nranges == 2)
+ if (IS_LEFTD(XS(1)) && IS_RIGHTD(XE(2)))
+ if (fp_equald (XE(1), XS(2))) {
+ call qpex_pbpin (ex, NEQXD, offset,
+ qpex_refd(ex,XE(1)), 0)
+ goto resume_
+ }
+
+ # If at level zero, output instruction to load data into
+ # register and initialize PASS to false. Don't bother if
+ # compiling a subprogram, as these operations will already
+ # have been performed by the caller.
+
+ if (level == 0) {
+ opcode = LDDD
+ call qpex_pbpin (ex, opcode, offset, 0, 0)
+ }
+
+ # Compile a series of equality or range tests.
+ do i = 1, nranges {
+ x1 = XS(i); x2 = XE(i)
+ d_x1 = qpex_refd (ex, x1)
+ d_x2 = qpex_refd (ex, x2)
+
+ if (IS_LEFTD(x1))
+ call qpex_pbpin (ex, LEQD, d_x2, 0, 0)
+ else if (IS_RIGHTD(x2))
+ call qpex_pbpin (ex, GEQD, d_x1, 0, 0)
+ else if (fp_equald (x1, x2))
+ call qpex_pbpin (ex, EQLD, d_x1, d_x2, 0)
+ else
+ call qpex_pbpin (ex, RNGD, d_x1, d_x2, 0)
+ }
+
+ # Compile a test and exit instruction.
+ call qpex_pbpin (ex, XIFF, 0, 0, 0)
+
+ } else {
+ # Compile a lookup table test. Lookup tables may be
+ # either compressed or fully resolved. If compressed
+ # (the resolution of the table is less than that of the
+ # range data, e.g., for floating point lookup tables) a
+ # LUT bin may have as its value, in addition to the
+ # usual 0 or 1, the address of an interpreter subprogram
+ # to be executed to test data values mapping to that bin.
+ # The subprogram pointed to may in turn be another lookup
+ # table, hence in the general case a tree of lookup tables
+ # and little code segments may be compiled to implement
+ # a complex range list test.
+
+ # Get the data range of the lookup table.
+ xmin = XS(1)
+ if (IS_LEFTD(xmin))
+ xmin = XE(1)
+ xmax = XE(nranges)
+ if (IS_RIGHTD(xmax))
+ xmax = XS(nranges)
+
+ # Get the lookup table size. Use a fully resolved table
+ # if the data is integer and the number of bins required
+ # is modest.
+
+ nbins = min (EX_MAXRRLUTLEN(ex), nranges * EX_LUTSCALE(ex))
+
+ # Determine the mapping from data space to table space.
+ xoffset = xmin
+ xscale = nbins / (xmax - xmin)
+
+ # Allocate and initialize the lookup table descriptor.
+ lt = qpex_dballoc (ex, LEN_LTDES, TY_STRUCT)
+ call calloc (lut, nbins, TY_SHORT)
+
+ LT_NEXT(lt) = EX_LTHEAD(ex)
+ EX_LTHEAD(ex) = lt
+ LT_TYPE(lt) = TY_DOUBLE
+ LT_LUTP(lt) = lut
+ LT_NBINS(lt) = nbins
+ LT_D0(lt) = xoffset
+ LT_DS(lt) = xscale
+ LT_LEFT(lt) = btoi (IS_LEFTD(XS(1)))
+ LT_RIGHT(lt) = btoi (IS_RIGHTD(XE(nranges)))
+
+ # Compile the LUTX test instruction. Save a back pointer
+ # to the instruction so that we can edit the jump field in
+ # case a subprogram is compiled after the LUTXt.
+
+ lutx = qpex_pbpos (ex)
+ if (dtype == TY_SHORT)
+ call qpex_pbpin (ex, LUTXS, offset, lt, 0)
+ else
+ call qpex_pbpin (ex, LUTXD, offset, lt, 0)
+
+ xp = 1
+ bin = 1
+continue_
+ n_xs = xs + nranges
+ n_xe = xe + nranges
+
+ # Initialize the lookup table.
+ do i = bin, nbins {
+ x1 = (i-1) / xscale + xoffset
+ x2 = i / xscale + xoffset
+
+ # Get sub-rangelist for range x1:x2.
+ n_nranges = qpex_sublistd (x1, x2,
+ Memd[xs], Memd[xe], nranges, xp,
+ Memd[n_xs], Memd[n_xe])
+
+ if (n_nranges <= 0) {
+ Mems[lut+i-1] = 0
+
+ } else if (n_nranges == 1 && IS_LEFTD(Memd[n_xs]) &&
+ IS_RIGHTD(Memd[n_xe])) {
+
+ Mems[lut+i-1] = 1
+
+ } else {
+ # Compile the sub-rangelist as a subprogram.
+
+ # First set the LUT bin to point to the subprogram.
+ # We cannot use the IP directly here since the LUT
+ # bins are short integer, so store the offset into
+ # the pb instead (guaranteed to be >= 4).
+
+ Mems[lut+i-1] = qpex_pbpos(ex) - pb
+
+ # Push a new context.
+ level = level + 1
+ if (level > MAX_LEVELS) {
+ call eprintf ("%s: ")
+ call pargstr (qpexwarn)
+ call eprintf ("Excessive LUT nesting\n")
+ goto error_
+ }
+
+ # Save current LUT compilation context.
+ sv_xs[level] = xs
+ sv_xe[level] = xe
+ sv_xp[level] = xp
+ sv_xoffset[level] = xoffset
+ sv_xscale[level] = xscale
+ sv_nranges[level] = nranges
+ sv_lt[level] = lt
+ sv_bin[level] = i
+ sv_nbins[level] = nbins
+ sv_lut[level] = lut
+ sv_lutx[level] = lutx
+
+ # Set up context for the new rangelist.
+ xs = n_xs
+ xe = n_xe
+ nranges = n_nranges
+
+ goto next_
+ }
+ }
+
+ # Compile a test and exit instruction if the LUT calls any
+ # subprograms.
+
+ if (qpex_pbpos(ex) - lutx > LEN_INSTRUCTION)
+ call qpex_pbpin (ex, XIFF, 0, 0, 0)
+ }
+resume_
+ # Resume lookup table compilation if exiting due to LUT-bin
+ # subprogram compilation.
+
+ if (level > 0) {
+ # Pop saved context.
+ xs = sv_xs[level]
+ xe = sv_xe[level]
+ xp = sv_xp[level]
+ xoffset = sv_xoffset[level]
+ xscale = sv_xscale[level]
+ nranges = sv_nranges[level]
+ lt = sv_lt[level]
+ bin = sv_bin[level]
+ nbins = sv_nbins[level]
+ lut = sv_lut[level]
+ lutx = sv_lutx[level]
+
+ # Compile a return from subprogram.
+ call qpex_pbpin (ex, RET, 0, 0, 0)
+
+ # Patch up the original LUTX instruction to jump over the
+ # subprogram we have just finished compiling.
+
+ IARG3(lutx) = qpex_pbpos (ex)
+
+ # Resume compilation at the next LUT bin.
+ bin = bin + 1
+ level = level - 1
+ goto continue_
+ }
+ } until (level <= 0)
+
+ # Finish setting up the eterm descriptor.
+ ET_NINSTR(et) = (qpex_pbpos(ex) - ET_PROGPTR(et)) / LEN_INSTRUCTION
+
+ return (OK)
+error_
+ call qpex_free (ex, pb_save, db_save)
+ return (ERR)
+end
diff --git a/sys/qpoe/gen/qpexcodei.x b/sys/qpoe/gen/qpexcodei.x
new file mode 100644
index 00000000..db8cbc72
--- /dev/null
+++ b/sys/qpoe/gen/qpexcodei.x
@@ -0,0 +1,423 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QPEX_CODEGEN -- Generate interpreter metacode to evaluate the given
+# expression. The new code is appended to the current compiled program,
+# adding additional constraints which a data event will have to meet to
+# pass the filter.
+
+int procedure qpex_codegeni (ex, atname, assignop, expr, offset, dtype)
+
+pointer ex #I qpex descriptor
+char atname[ARB] #I attribute name (for expr regeneration)
+char assignop[ARB] #I "=" or "+=" (for expr regeneration)
+char expr[ARB] #I expression to be compiled
+int offset #I typed offset of referenced attribute
+int dtype #I datatype of referenced attribute
+
+int nbins, bin, xp
+pointer lt, lut, lutx, pb
+int x1, x2, xmin, xmax
+int xlen, nranges, n_nranges, level, opcode, ip, i
+pointer pb_save, db_save, xs_buf, xe_buf, xs, xe, n_xs, n_xe, et, prev
+
+int sv_xs[MAX_LEVELS], sv_xe[MAX_LEVELS]
+pointer sv_lt[MAX_LEVELS], sv_lut[MAX_LEVELS], sv_lutx[MAX_LEVELS]
+int sv_xp[MAX_LEVELS], sv_nranges[MAX_LEVELS], sv_bin[MAX_LEVELS]
+int sv_nbins[MAX_LEVELS]
+
+int d_x1, d_x2
+real xoffset, xscale
+real sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS]
+
+define fp_equali($1==$2)
+
+bool complement
+int maskval
+int qp_ctoi()
+
+int qpex_parsei()
+int stridxs(), btoi(), qpex_sublisti()
+pointer qpex_dballoc(), qpex_dbpstr(), qpex_pbpos()
+errchk qpex_dballoc, qpex_pbpin, malloc, calloc, realloc, qpex_parsei
+
+string qpexwarn "QPEX Warning"
+define error_ 91
+define next_ 92
+define null_ 93
+define resume_ 94
+define bbmask_ 95
+define continue_ 96
+define XS Memi[xs+($1)-1]
+define XE Memi[xe+($1)-1]
+
+begin
+ pb = EX_PB(ex)
+
+ # Save the program state in case we have to abort.
+ call qpex_mark (ex, pb_save, db_save)
+
+ # Allocate and initialize a new expression term descriptor, linking
+ # it onto the tail of the ETTERMs list.
+
+ et = qpex_dballoc (ex, LEN_ETDES, TY_STRUCT)
+
+ ET_ATTTYPE(et) = dtype
+ ET_ATTOFF(et) = offset
+ ET_ATNAME(et) = qpex_dbpstr (ex, atname)
+ ET_ASSIGNOP(et) = qpex_dbpstr (ex, assignop)
+ ET_EXPRTEXT(et) = qpex_dbpstr (ex, expr)
+ ET_PROGPTR(et) = qpex_pbpos (ex)
+ ET_DELETED(et) = NO
+
+ prev = EX_ETTAIL(ex)
+ if (prev != NULL)
+ ET_NEXT(prev) = et
+ ET_NEXT(et) = NULL
+ EX_ETTAIL(ex) = et
+ if (EX_ETHEAD(ex) == NULL)
+ EX_ETHEAD(ex) = et
+
+ ip = stridxs ("%", expr)
+ # Attempt to compile a bitmask test if `%' is found in the
+ # expression. Since bitmasks cannot be mixed with range list
+ # expressions, this case is handled separately.
+
+ if (ip > 0) {
+ complement = false
+ level = 0
+
+ # Parse expression (very limited for this case).
+ for (ip=1; expr[ip] != EOS; ip=ip+1) {
+ switch (expr[ip]) {
+ case '!':
+ complement = !complement
+ case '(', '[':
+ level = level + 1
+ case ')', ']':
+ level = level - 1
+ case '%':
+ ip = ip + 1
+ if (qp_ctoi (expr, ip, maskval) < 0)
+ goto bbmask_
+ else
+ ip = ip - 1
+ default:
+ goto bbmask_
+ }
+ }
+
+ # Verify paren level, handle errors.
+ if (level != 0) {
+bbmask_ call eprintf ("%s: bad bitmask expression `%s'\n")
+ call pargstr (qpexwarn)
+ call pargstr (expr)
+ goto error_
+ }
+
+ # Compile the bitmask test.
+ if (complement)
+ maskval = not(maskval)
+ if (dtype == TY_SHORT)
+ call qpex_pbpin (ex, BTTXS, offset, maskval, 0)
+ else
+ call qpex_pbpin (ex, BTTXI, offset, maskval, 0)
+
+ # Finish setting up the eterm descriptor.
+ ET_NINSTR(et) = 1
+ return (OK)
+ }
+
+ # Compile a general range list expression. The basic procedure is
+ # to parse the expression to produce an optimized binary range list,
+ # then either compile the range list as an explicit series of
+ # instructions or as a lookup table, depending upon the number of
+ # ranges.
+
+ xlen = DEF_XLEN
+ call malloc (xs_buf, xlen, TY_INT)
+ call malloc (xe_buf, xlen, TY_INT)
+
+ # Convert expr to a binary range list and set up the initial context.
+ # Ensure that the range list buffers are large enough to hold any
+ # sublists extracted during compilation.
+
+ nranges = qpex_parsei (expr, xs_buf, xe_buf, xlen)
+ if (xlen < nranges * 2) {
+ xlen = nranges * 2
+ call realloc (xs_buf, xlen, TY_INT)
+ call realloc (xe_buf, xlen, TY_INT)
+ }
+
+ xs = xs_buf
+ xe = xe_buf
+ level = 0
+
+ repeat {
+next_
+ # Compile a new range list (or sublist).
+ if (nranges <= 0) {
+ # This shouldn't happen.
+null_ call eprintf ("%s: null range list\n")
+ call pargstr (qpexwarn)
+ call qpex_pbpin (ex, PASS, 0, 0, 0)
+
+ } else if (nranges == 1) {
+ # Output an instruction to load the data, perform the range
+ # test, and conditionally exit all in a single instruction.
+
+ x1 = XS(1); x2 = XE(1)
+ d_x1 = x1
+ d_x2 = x2
+
+ if (dtype == TY_SHORT) {
+ if (IS_LEFTI(x1) && IS_RIGHTI(x2))
+ ; # pass everything (no tests)
+ else if (IS_LEFTI(x1))
+ call qpex_pbpin (ex, LEQXS, offset, d_x2, 0)
+ else if (IS_RIGHTI(x2))
+ call qpex_pbpin (ex, GEQXS, offset, d_x1, 0)
+ else if (fp_equali (x1, x2))
+ call qpex_pbpin (ex, EQLXS, offset, d_x1, d_x2)
+ else
+ call qpex_pbpin (ex, RNGXS, offset, d_x1, d_x2)
+ } else {
+ if (IS_LEFTI(x1) && IS_RIGHTI(x2))
+ ; # pass everything (no tests)
+ else if (IS_LEFTI(x1))
+ call qpex_pbpin (ex, LEQXI, offset, d_x2, 0)
+ else if (IS_RIGHTI(x2))
+ call qpex_pbpin (ex, GEQXI, offset, d_x1, 0)
+ else if (fp_equali (x1, x2))
+ call qpex_pbpin (ex, EQLXI, offset, d_x1, d_x2)
+ else
+ call qpex_pbpin (ex, RNGXI, offset, d_x1, d_x2)
+ }
+
+ } else if (nranges < EX_LUTMINRANGES(ex)) {
+ # If the number of ranges to be tested for the data is small,
+ # compile explicit code to perform the range tests directly.
+ # Otherwise skip forward and compile a lookup table instead.
+ # In either case, the function of the instructions compiled
+ # is to test the data loaded into the register above, setting
+ # the value of PASS to true if the data lies in any of the
+ # indicated ranges.
+
+ # Check for !X, which is indicated in range list form by a
+ # two element list bracketing the X on each side.
+
+ if (nranges == 2)
+ if (IS_LEFTI(XS(1)) && IS_RIGHTI(XE(2)))
+ if (XE(1)+1 == XS(2)-1) {
+ if (dtype == TY_SHORT)
+ opcode = NEQXS
+ else
+ opcode = NEQXI
+ call qpex_pbpin (ex, opcode, offset, XE(1)+1, 0)
+ goto resume_
+ }
+
+ # If at level zero, output instruction to load data into
+ # register and initialize PASS to false. Don't bother if
+ # compiling a subprogram, as these operations will already
+ # have been performed by the caller.
+
+ if (level == 0) {
+ if (dtype == TY_SHORT)
+ opcode = LDSI
+ else
+ opcode = LDII
+ call qpex_pbpin (ex, opcode, offset, 0, 0)
+ }
+
+ # Compile a series of equality or range tests.
+ do i = 1, nranges {
+ x1 = XS(i); x2 = XE(i)
+ d_x1 = x1
+ d_x2 = x2
+
+ if (IS_LEFTI(x1))
+ call qpex_pbpin (ex, LEQI, d_x2, 0, 0)
+ else if (IS_RIGHTI(x2))
+ call qpex_pbpin (ex, GEQI, d_x1, 0, 0)
+ else if (fp_equali (x1, x2))
+ call qpex_pbpin (ex, EQLI, d_x1, d_x2, 0)
+ else
+ call qpex_pbpin (ex, RNGI, d_x1, d_x2, 0)
+ }
+
+ # Compile a test and exit instruction.
+ call qpex_pbpin (ex, XIFF, 0, 0, 0)
+
+ } else {
+ # Compile a lookup table test. Lookup tables may be
+ # either compressed or fully resolved. If compressed
+ # (the resolution of the table is less than that of the
+ # range data, e.g., for floating point lookup tables) a
+ # LUT bin may have as its value, in addition to the
+ # usual 0 or 1, the address of an interpreter subprogram
+ # to be executed to test data values mapping to that bin.
+ # The subprogram pointed to may in turn be another lookup
+ # table, hence in the general case a tree of lookup tables
+ # and little code segments may be compiled to implement
+ # a complex range list test.
+
+ # Get the data range of the lookup table.
+ xmin = XS(1)
+ if (IS_LEFTI(xmin))
+ xmin = XE(1)
+ xmax = XE(nranges)
+ if (IS_RIGHTI(xmax))
+ xmax = XS(nranges)
+
+ # Get the lookup table size. Use a fully resolved table
+ # if the data is integer and the number of bins required
+ # is modest.
+
+ nbins = xmax - xmin + 1
+ if (nbins > EX_MAXFRLUTLEN(ex))
+ nbins = min (EX_MAXRRLUTLEN(ex),
+ nranges * EX_LUTSCALE(ex))
+
+ # Determine the mapping from data space to table space.
+ xoffset = xmin
+ xscale = nbins / (xmax - xmin + 1)
+
+ # Allocate and initialize the lookup table descriptor.
+ lt = qpex_dballoc (ex, LEN_LTDES, TY_STRUCT)
+ call calloc (lut, nbins, TY_SHORT)
+
+ LT_NEXT(lt) = EX_LTHEAD(ex)
+ EX_LTHEAD(ex) = lt
+ LT_TYPE(lt) = TY_INT
+ LT_LUTP(lt) = lut
+ LT_NBINS(lt) = nbins
+ LT_I0(lt) = xoffset
+ LT_IS(lt) = xscale
+ LT_LEFT(lt) = btoi (IS_LEFTI(XS(1)))
+ LT_RIGHT(lt) = btoi (IS_RIGHTI(XE(nranges)))
+
+ # Compile the LUTX test instruction. Save a back pointer
+ # to the instruction so that we can edit the jump field in
+ # case a subprogram is compiled after the LUTXt.
+
+ lutx = qpex_pbpos (ex)
+ if (dtype == TY_SHORT)
+ call qpex_pbpin (ex, LUTXS, offset, lt, 0)
+ else
+ call qpex_pbpin (ex, LUTXI, offset, lt, 0)
+
+ xp = 1
+ bin = 1
+continue_
+ n_xs = xs + nranges
+ n_xe = xe + nranges
+
+ # Initialize the lookup table.
+ do i = bin, nbins {
+ x1 = (i-1) / xscale + xoffset
+ x2 = i / xscale + xoffset - 1
+
+ # Get sub-rangelist for range x1:x2.
+ n_nranges = qpex_sublisti (x1, x2,
+ Memi[xs], Memi[xe], nranges, xp,
+ Memi[n_xs], Memi[n_xe])
+
+ if (n_nranges <= 0) {
+ Mems[lut+i-1] = 0
+
+ } else if (n_nranges == 1 && IS_LEFTI(Memi[n_xs]) &&
+ IS_RIGHTI(Memi[n_xe])) {
+
+ Mems[lut+i-1] = 1
+
+ } else {
+ # Compile the sub-rangelist as a subprogram.
+
+ # First set the LUT bin to point to the subprogram.
+ # We cannot use the IP directly here since the LUT
+ # bins are short integer, so store the offset into
+ # the pb instead (guaranteed to be >= 4).
+
+ Mems[lut+i-1] = qpex_pbpos(ex) - pb
+
+ # Push a new context.
+ level = level + 1
+ if (level > MAX_LEVELS) {
+ call eprintf ("%s: ")
+ call pargstr (qpexwarn)
+ call eprintf ("Excessive LUT nesting\n")
+ goto error_
+ }
+
+ # Save current LUT compilation context.
+ sv_xs[level] = xs
+ sv_xe[level] = xe
+ sv_xp[level] = xp
+ sv_xoffset[level] = xoffset
+ sv_xscale[level] = xscale
+ sv_nranges[level] = nranges
+ sv_lt[level] = lt
+ sv_bin[level] = i
+ sv_nbins[level] = nbins
+ sv_lut[level] = lut
+ sv_lutx[level] = lutx
+
+ # Set up context for the new rangelist.
+ xs = n_xs
+ xe = n_xe
+ nranges = n_nranges
+
+ goto next_
+ }
+ }
+
+ # Compile a test and exit instruction if the LUT calls any
+ # subprograms.
+
+ if (qpex_pbpos(ex) - lutx > LEN_INSTRUCTION)
+ call qpex_pbpin (ex, XIFF, 0, 0, 0)
+ }
+resume_
+ # Resume lookup table compilation if exiting due to LUT-bin
+ # subprogram compilation.
+
+ if (level > 0) {
+ # Pop saved context.
+ xs = sv_xs[level]
+ xe = sv_xe[level]
+ xp = sv_xp[level]
+ xoffset = sv_xoffset[level]
+ xscale = sv_xscale[level]
+ nranges = sv_nranges[level]
+ lt = sv_lt[level]
+ bin = sv_bin[level]
+ nbins = sv_nbins[level]
+ lut = sv_lut[level]
+ lutx = sv_lutx[level]
+
+ # Compile a return from subprogram.
+ call qpex_pbpin (ex, RET, 0, 0, 0)
+
+ # Patch up the original LUTX instruction to jump over the
+ # subprogram we have just finished compiling.
+
+ IARG3(lutx) = qpex_pbpos (ex)
+
+ # Resume compilation at the next LUT bin.
+ bin = bin + 1
+ level = level - 1
+ goto continue_
+ }
+ } until (level <= 0)
+
+ # Finish setting up the eterm descriptor.
+ ET_NINSTR(et) = (qpex_pbpos(ex) - ET_PROGPTR(et)) / LEN_INSTRUCTION
+
+ return (OK)
+error_
+ call qpex_free (ex, pb_save, db_save)
+ return (ERR)
+end
diff --git a/sys/qpoe/gen/qpexcoder.x b/sys/qpoe/gen/qpexcoder.x
new file mode 100644
index 00000000..30e1d85b
--- /dev/null
+++ b/sys/qpoe/gen/qpexcoder.x
@@ -0,0 +1,368 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QPEX_CODEGEN -- Generate interpreter metacode to evaluate the given
+# expression. The new code is appended to the current compiled program,
+# adding additional constraints which a data event will have to meet to
+# pass the filter.
+
+int procedure qpex_codegenr (ex, atname, assignop, expr, offset, dtype)
+
+pointer ex #I qpex descriptor
+char atname[ARB] #I attribute name (for expr regeneration)
+char assignop[ARB] #I "=" or "+=" (for expr regeneration)
+char expr[ARB] #I expression to be compiled
+int offset #I typed offset of referenced attribute
+int dtype #I datatype of referenced attribute
+
+int nbins, bin, xp
+pointer lt, lut, lutx, pb
+real x1, x2, xmin, xmax
+int xlen, nranges, n_nranges, level, opcode, ip, i
+pointer pb_save, db_save, xs_buf, xe_buf, xs, xe, n_xs, n_xe, et, prev
+
+real sv_xs[MAX_LEVELS], sv_xe[MAX_LEVELS]
+pointer sv_lt[MAX_LEVELS], sv_lut[MAX_LEVELS], sv_lutx[MAX_LEVELS]
+int sv_xp[MAX_LEVELS], sv_nranges[MAX_LEVELS], sv_bin[MAX_LEVELS]
+int sv_nbins[MAX_LEVELS]
+
+real d_x1, d_x2
+real xoffset, xscale
+real sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS]
+
+bool fp_equalr()
+
+
+int qpex_parser()
+int stridxs(), btoi(), qpex_sublistr()
+pointer qpex_dballoc(), qpex_dbpstr(), qpex_pbpos()
+errchk qpex_dballoc, qpex_pbpin, malloc, calloc, realloc, qpex_parser
+
+string qpexwarn "QPEX Warning"
+define error_ 91
+define next_ 92
+define null_ 93
+define resume_ 94
+define bbmask_ 95
+define continue_ 96
+define XS Memr[xs+($1)-1]
+define XE Memr[xe+($1)-1]
+
+begin
+ pb = EX_PB(ex)
+
+ # Save the program state in case we have to abort.
+ call qpex_mark (ex, pb_save, db_save)
+
+ # Allocate and initialize a new expression term descriptor, linking
+ # it onto the tail of the ETTERMs list.
+
+ et = qpex_dballoc (ex, LEN_ETDES, TY_STRUCT)
+
+ ET_ATTTYPE(et) = dtype
+ ET_ATTOFF(et) = offset
+ ET_ATNAME(et) = qpex_dbpstr (ex, atname)
+ ET_ASSIGNOP(et) = qpex_dbpstr (ex, assignop)
+ ET_EXPRTEXT(et) = qpex_dbpstr (ex, expr)
+ ET_PROGPTR(et) = qpex_pbpos (ex)
+ ET_DELETED(et) = NO
+
+ prev = EX_ETTAIL(ex)
+ if (prev != NULL)
+ ET_NEXT(prev) = et
+ ET_NEXT(et) = NULL
+ EX_ETTAIL(ex) = et
+ if (EX_ETHEAD(ex) == NULL)
+ EX_ETHEAD(ex) = et
+
+ ip = stridxs ("%", expr)
+ # Bitmask tests are meaningless for floating point data.
+ if (ip > 0) {
+ call eprintf ("%s: bitmasks not permitted for floating data\n")
+ call pargstr (qpexwarn)
+ goto error_
+ }
+
+ # Compile a general range list expression. The basic procedure is
+ # to parse the expression to produce an optimized binary range list,
+ # then either compile the range list as an explicit series of
+ # instructions or as a lookup table, depending upon the number of
+ # ranges.
+
+ xlen = DEF_XLEN
+ call malloc (xs_buf, xlen, TY_REAL)
+ call malloc (xe_buf, xlen, TY_REAL)
+
+ # Convert expr to a binary range list and set up the initial context.
+ # Ensure that the range list buffers are large enough to hold any
+ # sublists extracted during compilation.
+
+ nranges = qpex_parser (expr, xs_buf, xe_buf, xlen)
+ if (xlen < nranges * 2) {
+ xlen = nranges * 2
+ call realloc (xs_buf, xlen, TY_REAL)
+ call realloc (xe_buf, xlen, TY_REAL)
+ }
+
+ xs = xs_buf
+ xe = xe_buf
+ level = 0
+
+ repeat {
+next_
+ # Compile a new range list (or sublist).
+ if (nranges <= 0) {
+ # This shouldn't happen.
+null_ call eprintf ("%s: null range list\n")
+ call pargstr (qpexwarn)
+ call qpex_pbpin (ex, PASS, 0, 0, 0)
+
+ } else if (nranges == 1) {
+ # Output an instruction to load the data, perform the range
+ # test, and conditionally exit all in a single instruction.
+
+ x1 = XS(1); x2 = XE(1)
+ d_x1 = x1
+ d_x2 = x2
+
+ if (dtype == TY_SHORT) {
+ if (IS_LEFTR(x1) && IS_RIGHTR(x2))
+ ; # pass everything (no tests)
+ else if (IS_LEFTR(x1))
+ call qpex_pbpin (ex, LEQXS, offset, d_x2, 0)
+ else if (IS_RIGHTR(x2))
+ call qpex_pbpin (ex, GEQXS, offset, d_x1, 0)
+ else if (fp_equalr (x1, x2))
+ call qpex_pbpin (ex, EQLXS, offset, d_x1, d_x2)
+ else
+ call qpex_pbpin (ex, RNGXS, offset, d_x1, d_x2)
+ } else {
+ if (IS_LEFTR(x1) && IS_RIGHTR(x2))
+ ; # pass everything (no tests)
+ else if (IS_LEFTR(x1))
+ call qpex_pbpin (ex, LEQXR, offset, d_x2, 0)
+ else if (IS_RIGHTR(x2))
+ call qpex_pbpin (ex, GEQXR, offset, d_x1, 0)
+ else if (fp_equalr (x1, x2))
+ call qpex_pbpin (ex, EQLXR, offset, d_x1, d_x2)
+ else
+ call qpex_pbpin (ex, RNGXR, offset, d_x1, d_x2)
+ }
+
+ } else if (nranges < EX_LUTMINRANGES(ex)) {
+ # If the number of ranges to be tested for the data is small,
+ # compile explicit code to perform the range tests directly.
+ # Otherwise skip forward and compile a lookup table instead.
+ # In either case, the function of the instructions compiled
+ # is to test the data loaded into the register above, setting
+ # the value of PASS to true if the data lies in any of the
+ # indicated ranges.
+
+ # Check for !X, which is indicated in range list form by a
+ # two element list bracketing the X on each side.
+
+ if (nranges == 2)
+ if (IS_LEFTR(XS(1)) && IS_RIGHTR(XE(2)))
+ if (fp_equalr (XE(1), XS(2))) {
+ call qpex_pbpin (ex, NEQXR, offset, XE(1), 0)
+ goto resume_
+ }
+
+ # If at level zero, output instruction to load data into
+ # register and initialize PASS to false. Don't bother if
+ # compiling a subprogram, as these operations will already
+ # have been performed by the caller.
+
+ if (level == 0) {
+ opcode = LDRR
+ call qpex_pbpin (ex, opcode, offset, 0, 0)
+ }
+
+ # Compile a series of equality or range tests.
+ do i = 1, nranges {
+ x1 = XS(i); x2 = XE(i)
+ d_x1 = x1
+ d_x2 = x2
+
+ if (IS_LEFTR(x1))
+ call qpex_pbpin (ex, LEQR, d_x2, 0, 0)
+ else if (IS_RIGHTR(x2))
+ call qpex_pbpin (ex, GEQR, d_x1, 0, 0)
+ else if (fp_equalr (x1, x2))
+ call qpex_pbpin (ex, EQLR, d_x1, d_x2, 0)
+ else
+ call qpex_pbpin (ex, RNGR, d_x1, d_x2, 0)
+ }
+
+ # Compile a test and exit instruction.
+ call qpex_pbpin (ex, XIFF, 0, 0, 0)
+
+ } else {
+ # Compile a lookup table test. Lookup tables may be
+ # either compressed or fully resolved. If compressed
+ # (the resolution of the table is less than that of the
+ # range data, e.g., for floating point lookup tables) a
+ # LUT bin may have as its value, in addition to the
+ # usual 0 or 1, the address of an interpreter subprogram
+ # to be executed to test data values mapping to that bin.
+ # The subprogram pointed to may in turn be another lookup
+ # table, hence in the general case a tree of lookup tables
+ # and little code segments may be compiled to implement
+ # a complex range list test.
+
+ # Get the data range of the lookup table.
+ xmin = XS(1)
+ if (IS_LEFTR(xmin))
+ xmin = XE(1)
+ xmax = XE(nranges)
+ if (IS_RIGHTR(xmax))
+ xmax = XS(nranges)
+
+ # Get the lookup table size. Use a fully resolved table
+ # if the data is integer and the number of bins required
+ # is modest.
+
+ nbins = min (EX_MAXRRLUTLEN(ex), nranges * EX_LUTSCALE(ex))
+
+ # Determine the mapping from data space to table space.
+ xoffset = xmin
+ xscale = nbins / (xmax - xmin)
+
+ # Allocate and initialize the lookup table descriptor.
+ lt = qpex_dballoc (ex, LEN_LTDES, TY_STRUCT)
+ call calloc (lut, nbins, TY_SHORT)
+
+ LT_NEXT(lt) = EX_LTHEAD(ex)
+ EX_LTHEAD(ex) = lt
+ LT_TYPE(lt) = TY_REAL
+ LT_LUTP(lt) = lut
+ LT_NBINS(lt) = nbins
+ LT_R0(lt) = xoffset
+ LT_RS(lt) = xscale
+ LT_LEFT(lt) = btoi (IS_LEFTR(XS(1)))
+ LT_RIGHT(lt) = btoi (IS_RIGHTR(XE(nranges)))
+
+ # Compile the LUTX test instruction. Save a back pointer
+ # to the instruction so that we can edit the jump field in
+ # case a subprogram is compiled after the LUTXt.
+
+ lutx = qpex_pbpos (ex)
+ if (dtype == TY_SHORT)
+ call qpex_pbpin (ex, LUTXS, offset, lt, 0)
+ else
+ call qpex_pbpin (ex, LUTXR, offset, lt, 0)
+
+ xp = 1
+ bin = 1
+continue_
+ n_xs = xs + nranges
+ n_xe = xe + nranges
+
+ # Initialize the lookup table.
+ do i = bin, nbins {
+ x1 = (i-1) / xscale + xoffset
+ x2 = i / xscale + xoffset
+
+ # Get sub-rangelist for range x1:x2.
+ n_nranges = qpex_sublistr (x1, x2,
+ Memr[xs], Memr[xe], nranges, xp,
+ Memr[n_xs], Memr[n_xe])
+
+ if (n_nranges <= 0) {
+ Mems[lut+i-1] = 0
+
+ } else if (n_nranges == 1 && IS_LEFTR(Memr[n_xs]) &&
+ IS_RIGHTR(Memr[n_xe])) {
+
+ Mems[lut+i-1] = 1
+
+ } else {
+ # Compile the sub-rangelist as a subprogram.
+
+ # First set the LUT bin to point to the subprogram.
+ # We cannot use the IP directly here since the LUT
+ # bins are short integer, so store the offset into
+ # the pb instead (guaranteed to be >= 4).
+
+ Mems[lut+i-1] = qpex_pbpos(ex) - pb
+
+ # Push a new context.
+ level = level + 1
+ if (level > MAX_LEVELS) {
+ call eprintf ("%s: ")
+ call pargstr (qpexwarn)
+ call eprintf ("Excessive LUT nesting\n")
+ goto error_
+ }
+
+ # Save current LUT compilation context.
+ sv_xs[level] = xs
+ sv_xe[level] = xe
+ sv_xp[level] = xp
+ sv_xoffset[level] = xoffset
+ sv_xscale[level] = xscale
+ sv_nranges[level] = nranges
+ sv_lt[level] = lt
+ sv_bin[level] = i
+ sv_nbins[level] = nbins
+ sv_lut[level] = lut
+ sv_lutx[level] = lutx
+
+ # Set up context for the new rangelist.
+ xs = n_xs
+ xe = n_xe
+ nranges = n_nranges
+
+ goto next_
+ }
+ }
+
+ # Compile a test and exit instruction if the LUT calls any
+ # subprograms.
+
+ if (qpex_pbpos(ex) - lutx > LEN_INSTRUCTION)
+ call qpex_pbpin (ex, XIFF, 0, 0, 0)
+ }
+resume_
+ # Resume lookup table compilation if exiting due to LUT-bin
+ # subprogram compilation.
+
+ if (level > 0) {
+ # Pop saved context.
+ xs = sv_xs[level]
+ xe = sv_xe[level]
+ xp = sv_xp[level]
+ xoffset = sv_xoffset[level]
+ xscale = sv_xscale[level]
+ nranges = sv_nranges[level]
+ lt = sv_lt[level]
+ bin = sv_bin[level]
+ nbins = sv_nbins[level]
+ lut = sv_lut[level]
+ lutx = sv_lutx[level]
+
+ # Compile a return from subprogram.
+ call qpex_pbpin (ex, RET, 0, 0, 0)
+
+ # Patch up the original LUTX instruction to jump over the
+ # subprogram we have just finished compiling.
+
+ IARG3(lutx) = qpex_pbpos (ex)
+
+ # Resume compilation at the next LUT bin.
+ bin = bin + 1
+ level = level - 1
+ goto continue_
+ }
+ } until (level <= 0)
+
+ # Finish setting up the eterm descriptor.
+ ET_NINSTR(et) = (qpex_pbpos(ex) - ET_PROGPTR(et)) / LEN_INSTRUCTION
+
+ return (OK)
+error_
+ call qpex_free (ex, pb_save, db_save)
+ return (ERR)
+end
diff --git a/sys/qpoe/gen/qpexparsed.x b/sys/qpoe/gen/qpexparsed.x
new file mode 100644
index 00000000..ec625bd8
--- /dev/null
+++ b/sys/qpoe/gen/qpexparsed.x
@@ -0,0 +1,372 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include <mach.h>
+include "../qpex.h"
+
+.help qpexparse
+.nf --------------------------------------------------------------------------
+QPEXPARSE -- Code to parse an event attribute expression, producing a binary
+range list as output.
+
+ nranges = qpex_parse[ird] (expr, xs, xe, xlen)
+
+The calling sequence for the parse routine is shown above. The arguments XS
+and XE are pointers to dynamically allocated arrays of length XLEN and type
+[IRD]. These arrays should be allocated in the calling program before calling
+the parser, and deallocated when no longer needed. Reallocation to increase
+the array length is automatic if the arrays fill during parsing. DTYPE should
+be the same datatype as the attribute with which the list is associated.
+
+The form of an event attribute expression may be a list of values,
+
+ attribute = n
+or
+ attribute = m, n, ...
+
+a list of inclusive or exclusive ranges,
+
+ attribute = m:n, !p:q
+
+including open ranges,
+
+ attribute = :n, p:q
+
+or any combination of the above (excluding combinations of bitmasks and values
+or ranges, which are mutually exclusive):
+
+ attribute = :n, a, b, p:q, !(m, e:f)
+
+Parenthesis may be used for grouping where desired, e.g.,
+
+ attribute = (:n, a, b, p:q, !(m, e:f))
+
+An additional form of the event attribute expression allows use of a bitmask
+to specify the acceptable values, e.g.,
+
+ attribute = %17B
+or
+ attribute = !%17B
+
+however, bitmasks are incompatible with range lists, and should be recognized
+and dealt with elsewhere (bitmasks may not be combined with range lists in
+the same expression term).
+
+We are concerned here only with the attribute value list itself, i.e.,
+everything to the right of the equals sign in the examples above. This list
+should be extracted and placed into a string containing a single line of
+text before we are called. Attribute value lists may be any length, but
+backslash continuation, file inclusion (or whatever means is used to form
+the attribute value list) is assumed to be handled at a higher level.
+
+The output of this package is an ordered boolean valued binary range list
+with type integer, real, or double breakpoints (i.e., the breakpoints are the
+same datatype as the attribute itself, but the range values are zero or one).
+The range list defines the initial value, final value, and any interior
+breakpoints where the attribute value changes state. Expression optimization
+is used to minimize the number of breakpoints (i.e., eliminate redundant
+breakpoints, such as a range within a range).
+
+Output range list format:
+
+ xs[1] xe[1]
+ xs[2] xe[2]
+ ...
+ xs[N] xe[N]
+
+Where each range is inclusive and only "true" ranges are shown. If XS[1] is
+LEFT a open-left range (:n) is indicated; if XE[N] is RIGHT an open-right
+range (n:) is indicated. In an integer range list, isolated points appear
+as a single range with (xe[i]=xs[i]). In a real or double range list,
+isolated points are represented as finite ranges with a width on the order of
+the machine epsilon.
+.endhelp ---------------------------------------------------------------------
+
+define DEF_XLEN 256 # default output range list length
+define INC_XLEN 256 # increment to above
+define DEF_VLEN 512 # default breakpoint list length
+define INC_VLEN 512 # increment to above
+define MAX_NEST 20 # parser stack depth
+
+define STEP 1 # step at boundary of closed range
+define ZERO 1000 # step at boundary of open range
+
+define XV Memd[xv+($1)-1] # reference x position values
+define UV Memi[uv+($1)-1] # unique flags for x value pairs
+define SV Memi[sv+($1)-1] # reference breakpoint step values
+
+
+# QPEX_PARSE -- Convert the given attribute value list into a binary
+# range list, returning the number of ranges as the function value.
+
+int procedure qpex_parsed (expr, xs, xe, xlen)
+
+char expr[ARB] #I attribute value list to be parsed
+pointer xs #U pointer to array of start-range values
+pointer xe #U pointer to array of end-range values
+int xlen #U allocated length of XS, XE arrays
+
+bool range
+pointer xv, uv, sv
+double xstart, xend, xmin, temp, x, n_xs, n_xe
+int vlen, nrg, ip, op, ch, ip_start, i, j, jval, r1, r2, y, v, ov, dy
+int token[MAX_NEST], tokval[MAX_NEST], lev, itemp, umin
+errchk syserr, malloc, realloc
+define pop_ 91
+
+double dtemp
+bool bval, fp_equald()
+int qp_ctod()
+
+begin
+ vlen = DEF_VLEN
+ call malloc (xv, vlen, TY_DOUBLE)
+ call malloc (uv, vlen, TY_INT)
+ call malloc (sv, vlen, TY_INT)
+
+ lev = 0
+ nrg = 0
+
+ # Parse the expression string and compile the raw, unoptimized
+ # breakpoint list in the order in which the breakpoints occur in
+ # the string.
+
+ for (ip=1; expr[ip] != EOS; ) {
+ # Skip whitespace.
+ for (ch=expr[ip]; IS_WHITE(ch) || ch == '\n'; ch=expr[ip])
+ ip = ip + 1
+
+ # Extract and process token.
+ switch (ch) {
+ case EOS:
+ # At end of string.
+ if (lev > 0)
+ goto pop_
+ else
+ break
+
+ case ',':
+ # Comma list token delmiter.
+ ip = ip + 1
+ goto pop_
+
+ case '!', '(':
+ # Syntactical element - push on stack.
+ ip = ip + 1
+ lev = lev + 1
+ if (lev > MAX_NEST)
+ call syserr (SYS_QPEXLEVEL)
+ token[lev] = ch
+ tokval[lev] = nrg + 1
+
+ case ')':
+ # Close parenthesized group and pop parser stack.
+ ip = ip + 1
+ if (lev < 1)
+ call syserr (SYS_QPEXMLP)
+ else if (token[lev] != '(')
+ call syserr (SYS_QPEXRPAREN)
+ lev = lev - 1
+ goto pop_
+
+ default:
+ # Process a range term.
+ ip_start = ip
+
+ # Scan the M in M:N.
+ if (qp_ctod (expr, ip, dtemp) <= 0)
+ xstart = LEFTD
+ else
+ xstart = dtemp
+
+ # Scan the : in M:N. The notation M-N is also accepted,
+ # provided the token - immediately follows the token M.
+
+ while (IS_WHITE(expr[ip]))
+ ip = ip + 1
+ range = (expr[ip] == ':')
+ if (range)
+ ip = ip + 1
+ else if (!IS_LEFTD (xstart)) {
+ range = (expr[ip] == '-')
+ if (range)
+ ip = ip + 1
+ }
+
+ # Scan the N in M:N.
+ if (range) {
+ if (qp_ctod (expr, ip, dtemp) <= 0)
+ xend = RIGHTD
+ else
+ xend = dtemp
+ } else
+ xend = xstart
+
+ # Fix things if the user entered M:M explicitly.
+ if (range)
+ if (fp_equald (xstart, xend))
+ range = false
+
+ # Expand a single point into a range. For an integer list
+ # this produces M:M+1; for a floating list M-eps:M+eps.
+ # Verify ordering and that something recognizable was scanned.
+
+ if (!range) {
+ if (IS_LEFTD(xstart))
+ call syserr (SYS_QPEXBADRNG)
+ } else {
+ if (xstart > xend) {
+ temp = xstart; xstart = xend; xend = temp
+ }
+ }
+
+ # Make more space if vectors fill up.
+ if (nrg+4 > vlen) {
+ vlen = vlen + INC_VLEN
+ call realloc (xv, vlen, TY_DOUBLE)
+ call realloc (uv, vlen, TY_INT)
+ call realloc (sv, vlen, TY_INT)
+ }
+
+ # Save range on intermediate breakpoint list.
+ nrg = nrg + 1
+ XV(nrg) = xstart
+ UV(nrg) = 0
+ SV(nrg) = STEP
+
+ nrg = nrg + 1
+ XV(nrg) = xend
+ UV(nrg) = 1
+ SV(nrg) = -STEP
+pop_
+ # Pop parser stack.
+ if (lev > 0)
+ if (token[lev] == '!') {
+ # Invert a series of breakpoints.
+ do i = tokval[lev], nrg {
+ if (SV(i) == STEP) # invert
+ SV(i) = -ZERO
+ else if (SV(i) == -STEP)
+ SV(i) = ZERO
+ else if (SV(i) == ZERO) # undo
+ SV(i) = -STEP
+ else if (SV(i) == -ZERO)
+ SV(i) = STEP
+ }
+ lev = lev - 1
+ }
+ }
+ }
+
+ # If the first range entered by the user is an exclude range,
+ # e.g., "(!N)" or "(!(expr))" this implies that all other values
+ # are acceptable. Add the open range ":" to the end of the range
+ # list to indicate this, i.e., convert "!N" to ":,!N".
+
+ if (SV(1) == -ZERO) {
+ nrg = nrg + 1
+ XV(nrg) = LEFTD
+ UV(nrg) = 0
+ SV(nrg) = STEP
+
+ nrg = nrg + 1
+ XV(nrg) = RIGHTD
+ UV(nrg) = 1
+ SV(nrg) = -STEP
+ }
+
+ # Sort the breakpoint list.
+ do j = 1, nrg {
+ xmin = XV(j); umin = UV(j)
+ jval = j
+ do i = j+1, nrg {
+ bval = (XV(i) < xmin)
+ if (!bval)
+ if (abs (XV(i) - xmin) < 1.0E-5)
+ bval = (fp_equald(XV(i),xmin) && UV(i) < umin)
+ if (bval) {
+ xmin = XV(i); umin = UV(i)
+ jval = i
+ }
+ }
+ if (jval != j) {
+ temp = XV(j); XV(j) = XV(jval); XV(jval) = temp
+ itemp = UV(j); UV(j) = UV(jval); UV(jval) = itemp
+ itemp = SV(j); SV(j) = SV(jval); SV(jval) = itemp
+ }
+ }
+
+ # Initialize the output arrays if they were passed in as null.
+ if (xlen <= 0) {
+ xlen = DEF_XLEN
+ call malloc (xs, xlen, TY_DOUBLE)
+ call malloc (xe, xlen, TY_DOUBLE)
+ }
+
+ # Collapse sequences of redundant breakpoints into a single
+ # breakpoint, clipping the running sum value to the range 0-1.
+ # Accumulate and output successive nonzero ranges.
+
+ op = 1
+ ov = 0
+ y = 0
+
+ for (r1=1; r1 <= nrg; r1=r2+1) {
+ # Get a range of breakpoint entries for a single XV position.
+ for (r2=r1; r2 <= nrg; r2=r2+1) {
+ bval = (UV(r2) != UV(r1))
+ if (!bval) {
+ bval = (abs (XV(r2) - XV(r1)) > 1.0E-5)
+ if (!bval)
+ bval = !fp_equald(XV(r2),XV(r1))
+ }
+ if (bval)
+ break
+ }
+ r2 = r2 - 1
+
+ # Collapse into a single breakpoint.
+ x = XV(r1)
+ dy = SV(r1)
+ do i = r1 + 1, r2
+ dy = dy + SV(i)
+ y = y + dy
+
+ # Clip value to the range 0-1.
+ v = max(0, min(1, y))
+
+ # Accumulate a range of nonzero value. This eliminates redundant
+ # points lying within a range which is already set high.
+
+ if (v == 1 && ov == 0) {
+ n_xs = x
+ ov = 1
+ } else if (v == 0 && ov == 1) {
+ n_xe = x
+ ov = 2
+ }
+
+ # Output a range.
+ if (ov == 2) {
+ if (op > xlen) {
+ xlen = xlen + INC_XLEN
+ call realloc (xs, xlen, TY_DOUBLE)
+ call realloc (xe, xlen, TY_DOUBLE)
+ }
+
+ Memd[xs+op-1] = n_xs
+ Memd[xe+op-1] = n_xe
+ op = op + 1
+
+ ov = 0
+ }
+ }
+
+ # All done; discard breakpoint buffers.
+ call mfree (xv, TY_DOUBLE)
+ call mfree (uv, TY_INT)
+ call mfree (sv, TY_INT)
+
+ return (op - 1)
+end
diff --git a/sys/qpoe/gen/qpexparsei.x b/sys/qpoe/gen/qpexparsei.x
new file mode 100644
index 00000000..17d6a569
--- /dev/null
+++ b/sys/qpoe/gen/qpexparsei.x
@@ -0,0 +1,363 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include <mach.h>
+include "../qpex.h"
+
+.help qpexparse
+.nf --------------------------------------------------------------------------
+QPEXPARSE -- Code to parse an event attribute expression, producing a binary
+range list as output.
+
+ nranges = qpex_parse[ird] (expr, xs, xe, xlen)
+
+The calling sequence for the parse routine is shown above. The arguments XS
+and XE are pointers to dynamically allocated arrays of length XLEN and type
+[IRD]. These arrays should be allocated in the calling program before calling
+the parser, and deallocated when no longer needed. Reallocation to increase
+the array length is automatic if the arrays fill during parsing. DTYPE should
+be the same datatype as the attribute with which the list is associated.
+
+The form of an event attribute expression may be a list of values,
+
+ attribute = n
+or
+ attribute = m, n, ...
+
+a list of inclusive or exclusive ranges,
+
+ attribute = m:n, !p:q
+
+including open ranges,
+
+ attribute = :n, p:q
+
+or any combination of the above (excluding combinations of bitmasks and values
+or ranges, which are mutually exclusive):
+
+ attribute = :n, a, b, p:q, !(m, e:f)
+
+Parenthesis may be used for grouping where desired, e.g.,
+
+ attribute = (:n, a, b, p:q, !(m, e:f))
+
+An additional form of the event attribute expression allows use of a bitmask
+to specify the acceptable values, e.g.,
+
+ attribute = %17B
+or
+ attribute = !%17B
+
+however, bitmasks are incompatible with range lists, and should be recognized
+and dealt with elsewhere (bitmasks may not be combined with range lists in
+the same expression term).
+
+We are concerned here only with the attribute value list itself, i.e.,
+everything to the right of the equals sign in the examples above. This list
+should be extracted and placed into a string containing a single line of
+text before we are called. Attribute value lists may be any length, but
+backslash continuation, file inclusion (or whatever means is used to form
+the attribute value list) is assumed to be handled at a higher level.
+
+The output of this package is an ordered boolean valued binary range list
+with type integer, real, or double breakpoints (i.e., the breakpoints are the
+same datatype as the attribute itself, but the range values are zero or one).
+The range list defines the initial value, final value, and any interior
+breakpoints where the attribute value changes state. Expression optimization
+is used to minimize the number of breakpoints (i.e., eliminate redundant
+breakpoints, such as a range within a range).
+
+Output range list format:
+
+ xs[1] xe[1]
+ xs[2] xe[2]
+ ...
+ xs[N] xe[N]
+
+Where each range is inclusive and only "true" ranges are shown. If XS[1] is
+LEFT a open-left range (:n) is indicated; if XE[N] is RIGHT an open-right
+range (n:) is indicated. In an integer range list, isolated points appear
+as a single range with (xe[i]=xs[i]). In a real or double range list,
+isolated points are represented as finite ranges with a width on the order of
+the machine epsilon.
+.endhelp ---------------------------------------------------------------------
+
+define DEF_XLEN 256 # default output range list length
+define INC_XLEN 256 # increment to above
+define DEF_VLEN 512 # default breakpoint list length
+define INC_VLEN 512 # increment to above
+define MAX_NEST 20 # parser stack depth
+
+define STEP 1 # step at boundary of closed range
+define ZERO 1000 # step at boundary of open range
+
+define XV Memi[xv+($1)-1] # reference x position values
+define UV Memi[uv+($1)-1] # unique flags for x value pairs
+define SV Memi[sv+($1)-1] # reference breakpoint step values
+
+
+# QPEX_PARSE -- Convert the given attribute value list into a binary
+# range list, returning the number of ranges as the function value.
+
+int procedure qpex_parsei (expr, xs, xe, xlen)
+
+char expr[ARB] #I attribute value list to be parsed
+pointer xs #U pointer to array of start-range values
+pointer xe #U pointer to array of end-range values
+int xlen #U allocated length of XS, XE arrays
+
+bool range
+pointer xv, uv, sv
+int xstart, xend, xmin, temp, x, n_xs, n_xe
+int vlen, nrg, ip, op, ch, ip_start, i, j, jval, r1, r2, y, v, ov, dy
+int token[MAX_NEST], tokval[MAX_NEST], lev, itemp, umin
+errchk syserr, malloc, realloc
+define pop_ 91
+
+int qp_ctoi()
+define fp_equali($1==$2)
+
+begin
+ vlen = DEF_VLEN
+ call malloc (xv, vlen, TY_INT)
+ call malloc (uv, vlen, TY_INT)
+ call malloc (sv, vlen, TY_INT)
+
+ lev = 0
+ nrg = 0
+
+ # Parse the expression string and compile the raw, unoptimized
+ # breakpoint list in the order in which the breakpoints occur in
+ # the string.
+
+ for (ip=1; expr[ip] != EOS; ) {
+ # Skip whitespace.
+ for (ch=expr[ip]; IS_WHITE(ch) || ch == '\n'; ch=expr[ip])
+ ip = ip + 1
+
+ # Extract and process token.
+ switch (ch) {
+ case EOS:
+ # At end of string.
+ if (lev > 0)
+ goto pop_
+ else
+ break
+
+ case ',':
+ # Comma list token delmiter.
+ ip = ip + 1
+ goto pop_
+
+ case '!', '(':
+ # Syntactical element - push on stack.
+ ip = ip + 1
+ lev = lev + 1
+ if (lev > MAX_NEST)
+ call syserr (SYS_QPEXLEVEL)
+ token[lev] = ch
+ tokval[lev] = nrg + 1
+
+ case ')':
+ # Close parenthesized group and pop parser stack.
+ ip = ip + 1
+ if (lev < 1)
+ call syserr (SYS_QPEXMLP)
+ else if (token[lev] != '(')
+ call syserr (SYS_QPEXRPAREN)
+ lev = lev - 1
+ goto pop_
+
+ default:
+ # Process a range term.
+ ip_start = ip
+
+ # Scan the M in M:N.
+ if (qp_ctoi (expr, ip, xstart) <= 0)
+ xstart = LEFTI
+
+ # Scan the : in M:N. The notation M-N is also accepted,
+ # provided the token - immediately follows the token M.
+
+ while (IS_WHITE(expr[ip]))
+ ip = ip + 1
+ range = (expr[ip] == ':')
+ if (range)
+ ip = ip + 1
+ else if (!IS_LEFTI (xstart)) {
+ range = (expr[ip] == '-')
+ if (range)
+ ip = ip + 1
+ }
+
+ # Scan the N in M:N.
+ if (range) {
+ if (qp_ctoi (expr, ip, xend) <= 0)
+ xend = RIGHTI
+ } else
+ xend = xstart
+
+ # Fix things if the user entered M:M explicitly.
+ if (range)
+ if (fp_equali (xstart, xend))
+ range = false
+
+ # Expand a single point into a range. For an integer list
+ # this produces M:M+1; for a floating list M-eps:M+eps.
+ # Verify ordering and that something recognizable was scanned.
+
+ if (!range) {
+ if (IS_LEFTI(xstart))
+ call syserr (SYS_QPEXBADRNG)
+ xend = xstart + 1
+ } else {
+ if (xstart > xend) {
+ temp = xstart; xstart = xend; xend = temp
+ }
+ if (!IS_RIGHTI(xend))
+ xend = xend + 1
+ }
+
+ # Make more space if vectors fill up.
+ if (nrg+4 > vlen) {
+ vlen = vlen + INC_VLEN
+ call realloc (xv, vlen, TY_INT)
+ call realloc (uv, vlen, TY_INT)
+ call realloc (sv, vlen, TY_INT)
+ }
+
+ # Save range on intermediate breakpoint list.
+ nrg = nrg + 1
+ XV(nrg) = xstart
+ UV(nrg) = 0
+ SV(nrg) = STEP
+
+ nrg = nrg + 1
+ XV(nrg) = xend
+ UV(nrg) = 1
+ SV(nrg) = -STEP
+pop_
+ # Pop parser stack.
+ if (lev > 0)
+ if (token[lev] == '!') {
+ # Invert a series of breakpoints.
+ do i = tokval[lev], nrg {
+ if (SV(i) == STEP) # invert
+ SV(i) = -ZERO
+ else if (SV(i) == -STEP)
+ SV(i) = ZERO
+ else if (SV(i) == ZERO) # undo
+ SV(i) = -STEP
+ else if (SV(i) == -ZERO)
+ SV(i) = STEP
+ }
+ lev = lev - 1
+ }
+ }
+ }
+
+ # If the first range entered by the user is an exclude range,
+ # e.g., "(!N)" or "(!(expr))" this implies that all other values
+ # are acceptable. Add the open range ":" to the end of the range
+ # list to indicate this, i.e., convert "!N" to ":,!N".
+
+ if (SV(1) == -ZERO) {
+ nrg = nrg + 1
+ XV(nrg) = LEFTI
+ UV(nrg) = 0
+ SV(nrg) = STEP
+
+ nrg = nrg + 1
+ XV(nrg) = RIGHTI
+ UV(nrg) = 1
+ SV(nrg) = -STEP
+ }
+
+ # Sort the breakpoint list.
+ do j = 1, nrg {
+ xmin = XV(j); umin = UV(j)
+ jval = j
+ do i = j+1, nrg {
+ if (XV(i) < xmin || (XV(i) == xmin && UV(i) < umin)) {
+ xmin = XV(i); umin = UV(i)
+ jval = i
+ }
+ }
+ if (jval != j) {
+ temp = XV(j); XV(j) = XV(jval); XV(jval) = temp
+ itemp = UV(j); UV(j) = UV(jval); UV(jval) = itemp
+ itemp = SV(j); SV(j) = SV(jval); SV(jval) = itemp
+ }
+ }
+
+ # Initialize the output arrays if they were passed in as null.
+ if (xlen <= 0) {
+ xlen = DEF_XLEN
+ call malloc (xs, xlen, TY_INT)
+ call malloc (xe, xlen, TY_INT)
+ }
+
+ # Collapse sequences of redundant breakpoints into a single
+ # breakpoint, clipping the running sum value to the range 0-1.
+ # Accumulate and output successive nonzero ranges.
+
+ op = 1
+ ov = 0
+ y = 0
+
+ for (r1=1; r1 <= nrg; r1=r2+1) {
+ # Get a range of breakpoint entries for a single XV position.
+ for (r2=r1; r2 <= nrg; r2=r2+1) {
+ if (XV(r2) != XV(r1))
+ break
+ }
+ r2 = r2 - 1
+
+ # Collapse into a single breakpoint.
+ x = XV(r1)
+ dy = SV(r1)
+ do i = r1 + 1, r2
+ dy = dy + SV(i)
+ y = y + dy
+
+ # Clip value to the range 0-1.
+ v = max(0, min(1, y))
+
+ # Accumulate a range of nonzero value. This eliminates redundant
+ # points lying within a range which is already set high.
+
+ if (v == 1 && ov == 0) {
+ n_xs = x
+ ov = 1
+ } else if (v == 0 && ov == 1) {
+ if (IS_RIGHTI(x))
+ n_xe = x
+ else
+ n_xe = x - 1
+ ov = 2
+ }
+
+ # Output a range.
+ if (ov == 2) {
+ if (op > xlen) {
+ xlen = xlen + INC_XLEN
+ call realloc (xs, xlen, TY_INT)
+ call realloc (xe, xlen, TY_INT)
+ }
+
+ Memi[xs+op-1] = n_xs
+ Memi[xe+op-1] = n_xe
+ op = op + 1
+
+ ov = 0
+ }
+ }
+
+ # All done; discard breakpoint buffers.
+ call mfree (xv, TY_INT)
+ call mfree (uv, TY_INT)
+ call mfree (sv, TY_INT)
+
+ return (op - 1)
+end
diff --git a/sys/qpoe/gen/qpexparser.x b/sys/qpoe/gen/qpexparser.x
new file mode 100644
index 00000000..bf4c849e
--- /dev/null
+++ b/sys/qpoe/gen/qpexparser.x
@@ -0,0 +1,372 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include <mach.h>
+include "../qpex.h"
+
+.help qpexparse
+.nf --------------------------------------------------------------------------
+QPEXPARSE -- Code to parse an event attribute expression, producing a binary
+range list as output.
+
+ nranges = qpex_parse[ird] (expr, xs, xe, xlen)
+
+The calling sequence for the parse routine is shown above. The arguments XS
+and XE are pointers to dynamically allocated arrays of length XLEN and type
+[IRD]. These arrays should be allocated in the calling program before calling
+the parser, and deallocated when no longer needed. Reallocation to increase
+the array length is automatic if the arrays fill during parsing. DTYPE should
+be the same datatype as the attribute with which the list is associated.
+
+The form of an event attribute expression may be a list of values,
+
+ attribute = n
+or
+ attribute = m, n, ...
+
+a list of inclusive or exclusive ranges,
+
+ attribute = m:n, !p:q
+
+including open ranges,
+
+ attribute = :n, p:q
+
+or any combination of the above (excluding combinations of bitmasks and values
+or ranges, which are mutually exclusive):
+
+ attribute = :n, a, b, p:q, !(m, e:f)
+
+Parenthesis may be used for grouping where desired, e.g.,
+
+ attribute = (:n, a, b, p:q, !(m, e:f))
+
+An additional form of the event attribute expression allows use of a bitmask
+to specify the acceptable values, e.g.,
+
+ attribute = %17B
+or
+ attribute = !%17B
+
+however, bitmasks are incompatible with range lists, and should be recognized
+and dealt with elsewhere (bitmasks may not be combined with range lists in
+the same expression term).
+
+We are concerned here only with the attribute value list itself, i.e.,
+everything to the right of the equals sign in the examples above. This list
+should be extracted and placed into a string containing a single line of
+text before we are called. Attribute value lists may be any length, but
+backslash continuation, file inclusion (or whatever means is used to form
+the attribute value list) is assumed to be handled at a higher level.
+
+The output of this package is an ordered boolean valued binary range list
+with type integer, real, or double breakpoints (i.e., the breakpoints are the
+same datatype as the attribute itself, but the range values are zero or one).
+The range list defines the initial value, final value, and any interior
+breakpoints where the attribute value changes state. Expression optimization
+is used to minimize the number of breakpoints (i.e., eliminate redundant
+breakpoints, such as a range within a range).
+
+Output range list format:
+
+ xs[1] xe[1]
+ xs[2] xe[2]
+ ...
+ xs[N] xe[N]
+
+Where each range is inclusive and only "true" ranges are shown. If XS[1] is
+LEFT a open-left range (:n) is indicated; if XE[N] is RIGHT an open-right
+range (n:) is indicated. In an integer range list, isolated points appear
+as a single range with (xe[i]=xs[i]). In a real or double range list,
+isolated points are represented as finite ranges with a width on the order of
+the machine epsilon.
+.endhelp ---------------------------------------------------------------------
+
+define DEF_XLEN 256 # default output range list length
+define INC_XLEN 256 # increment to above
+define DEF_VLEN 512 # default breakpoint list length
+define INC_VLEN 512 # increment to above
+define MAX_NEST 20 # parser stack depth
+
+define STEP 1 # step at boundary of closed range
+define ZERO 1000 # step at boundary of open range
+
+define XV Memr[xv+($1)-1] # reference x position values
+define UV Memi[uv+($1)-1] # unique flags for x value pairs
+define SV Memi[sv+($1)-1] # reference breakpoint step values
+
+
+# QPEX_PARSE -- Convert the given attribute value list into a binary
+# range list, returning the number of ranges as the function value.
+
+int procedure qpex_parser (expr, xs, xe, xlen)
+
+char expr[ARB] #I attribute value list to be parsed
+pointer xs #U pointer to array of start-range values
+pointer xe #U pointer to array of end-range values
+int xlen #U allocated length of XS, XE arrays
+
+bool range
+pointer xv, uv, sv
+real xstart, xend, xmin, temp, x, n_xs, n_xe
+int vlen, nrg, ip, op, ch, ip_start, i, j, jval, r1, r2, y, v, ov, dy
+int token[MAX_NEST], tokval[MAX_NEST], lev, itemp, umin
+errchk syserr, malloc, realloc
+define pop_ 91
+
+double dtemp
+bool bval, fp_equalr()
+int qp_ctod()
+
+begin
+ vlen = DEF_VLEN
+ call malloc (xv, vlen, TY_REAL)
+ call malloc (uv, vlen, TY_INT)
+ call malloc (sv, vlen, TY_INT)
+
+ lev = 0
+ nrg = 0
+
+ # Parse the expression string and compile the raw, unoptimized
+ # breakpoint list in the order in which the breakpoints occur in
+ # the string.
+
+ for (ip=1; expr[ip] != EOS; ) {
+ # Skip whitespace.
+ for (ch=expr[ip]; IS_WHITE(ch) || ch == '\n'; ch=expr[ip])
+ ip = ip + 1
+
+ # Extract and process token.
+ switch (ch) {
+ case EOS:
+ # At end of string.
+ if (lev > 0)
+ goto pop_
+ else
+ break
+
+ case ',':
+ # Comma list token delmiter.
+ ip = ip + 1
+ goto pop_
+
+ case '!', '(':
+ # Syntactical element - push on stack.
+ ip = ip + 1
+ lev = lev + 1
+ if (lev > MAX_NEST)
+ call syserr (SYS_QPEXLEVEL)
+ token[lev] = ch
+ tokval[lev] = nrg + 1
+
+ case ')':
+ # Close parenthesized group and pop parser stack.
+ ip = ip + 1
+ if (lev < 1)
+ call syserr (SYS_QPEXMLP)
+ else if (token[lev] != '(')
+ call syserr (SYS_QPEXRPAREN)
+ lev = lev - 1
+ goto pop_
+
+ default:
+ # Process a range term.
+ ip_start = ip
+
+ # Scan the M in M:N.
+ if (qp_ctod (expr, ip, dtemp) <= 0)
+ xstart = LEFTR
+ else
+ xstart = dtemp
+
+ # Scan the : in M:N. The notation M-N is also accepted,
+ # provided the token - immediately follows the token M.
+
+ while (IS_WHITE(expr[ip]))
+ ip = ip + 1
+ range = (expr[ip] == ':')
+ if (range)
+ ip = ip + 1
+ else if (!IS_LEFTR (xstart)) {
+ range = (expr[ip] == '-')
+ if (range)
+ ip = ip + 1
+ }
+
+ # Scan the N in M:N.
+ if (range) {
+ if (qp_ctod (expr, ip, dtemp) <= 0)
+ xend = RIGHTR
+ else
+ xend = dtemp
+ } else
+ xend = xstart
+
+ # Fix things if the user entered M:M explicitly.
+ if (range)
+ if (fp_equalr (xstart, xend))
+ range = false
+
+ # Expand a single point into a range. For an integer list
+ # this produces M:M+1; for a floating list M-eps:M+eps.
+ # Verify ordering and that something recognizable was scanned.
+
+ if (!range) {
+ if (IS_LEFTR(xstart))
+ call syserr (SYS_QPEXBADRNG)
+ } else {
+ if (xstart > xend) {
+ temp = xstart; xstart = xend; xend = temp
+ }
+ }
+
+ # Make more space if vectors fill up.
+ if (nrg+4 > vlen) {
+ vlen = vlen + INC_VLEN
+ call realloc (xv, vlen, TY_REAL)
+ call realloc (uv, vlen, TY_INT)
+ call realloc (sv, vlen, TY_INT)
+ }
+
+ # Save range on intermediate breakpoint list.
+ nrg = nrg + 1
+ XV(nrg) = xstart
+ UV(nrg) = 0
+ SV(nrg) = STEP
+
+ nrg = nrg + 1
+ XV(nrg) = xend
+ UV(nrg) = 1
+ SV(nrg) = -STEP
+pop_
+ # Pop parser stack.
+ if (lev > 0)
+ if (token[lev] == '!') {
+ # Invert a series of breakpoints.
+ do i = tokval[lev], nrg {
+ if (SV(i) == STEP) # invert
+ SV(i) = -ZERO
+ else if (SV(i) == -STEP)
+ SV(i) = ZERO
+ else if (SV(i) == ZERO) # undo
+ SV(i) = -STEP
+ else if (SV(i) == -ZERO)
+ SV(i) = STEP
+ }
+ lev = lev - 1
+ }
+ }
+ }
+
+ # If the first range entered by the user is an exclude range,
+ # e.g., "(!N)" or "(!(expr))" this implies that all other values
+ # are acceptable. Add the open range ":" to the end of the range
+ # list to indicate this, i.e., convert "!N" to ":,!N".
+
+ if (SV(1) == -ZERO) {
+ nrg = nrg + 1
+ XV(nrg) = LEFTR
+ UV(nrg) = 0
+ SV(nrg) = STEP
+
+ nrg = nrg + 1
+ XV(nrg) = RIGHTR
+ UV(nrg) = 1
+ SV(nrg) = -STEP
+ }
+
+ # Sort the breakpoint list.
+ do j = 1, nrg {
+ xmin = XV(j); umin = UV(j)
+ jval = j
+ do i = j+1, nrg {
+ bval = (XV(i) < xmin)
+ if (!bval)
+ if (abs (XV(i) - xmin) < 1.0E-5)
+ bval = (fp_equalr(XV(i),xmin) && UV(i) < umin)
+ if (bval) {
+ xmin = XV(i); umin = UV(i)
+ jval = i
+ }
+ }
+ if (jval != j) {
+ temp = XV(j); XV(j) = XV(jval); XV(jval) = temp
+ itemp = UV(j); UV(j) = UV(jval); UV(jval) = itemp
+ itemp = SV(j); SV(j) = SV(jval); SV(jval) = itemp
+ }
+ }
+
+ # Initialize the output arrays if they were passed in as null.
+ if (xlen <= 0) {
+ xlen = DEF_XLEN
+ call malloc (xs, xlen, TY_REAL)
+ call malloc (xe, xlen, TY_REAL)
+ }
+
+ # Collapse sequences of redundant breakpoints into a single
+ # breakpoint, clipping the running sum value to the range 0-1.
+ # Accumulate and output successive nonzero ranges.
+
+ op = 1
+ ov = 0
+ y = 0
+
+ for (r1=1; r1 <= nrg; r1=r2+1) {
+ # Get a range of breakpoint entries for a single XV position.
+ for (r2=r1; r2 <= nrg; r2=r2+1) {
+ bval = (UV(r2) != UV(r1))
+ if (!bval) {
+ bval = (abs (XV(r2) - XV(r1)) > 1.0E-5)
+ if (!bval)
+ bval = !fp_equalr(XV(r2),XV(r1))
+ }
+ if (bval)
+ break
+ }
+ r2 = r2 - 1
+
+ # Collapse into a single breakpoint.
+ x = XV(r1)
+ dy = SV(r1)
+ do i = r1 + 1, r2
+ dy = dy + SV(i)
+ y = y + dy
+
+ # Clip value to the range 0-1.
+ v = max(0, min(1, y))
+
+ # Accumulate a range of nonzero value. This eliminates redundant
+ # points lying within a range which is already set high.
+
+ if (v == 1 && ov == 0) {
+ n_xs = x
+ ov = 1
+ } else if (v == 0 && ov == 1) {
+ n_xe = x
+ ov = 2
+ }
+
+ # Output a range.
+ if (ov == 2) {
+ if (op > xlen) {
+ xlen = xlen + INC_XLEN
+ call realloc (xs, xlen, TY_REAL)
+ call realloc (xe, xlen, TY_REAL)
+ }
+
+ Memr[xs+op-1] = n_xs
+ Memr[xe+op-1] = n_xe
+ op = op + 1
+
+ ov = 0
+ }
+ }
+
+ # All done; discard breakpoint buffers.
+ call mfree (xv, TY_REAL)
+ call mfree (uv, TY_INT)
+ call mfree (sv, TY_INT)
+
+ return (op - 1)
+end
diff --git a/sys/qpoe/gen/qpexsubd.x b/sys/qpoe/gen/qpexsubd.x
new file mode 100644
index 00000000..2fab50fd
--- /dev/null
+++ b/sys/qpoe/gen/qpexsubd.x
@@ -0,0 +1,63 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QPEX_SUBLIST -- Extract a sublist spanning the indicated range from a
+# larger range list. The number of ranges extracted is returned as the
+# function value.
+
+int procedure qpex_sublistd (x1, x2, xs,xe,nranges,ip, o_xs,o_xe)
+
+double x1, x2 #I range to be extracted
+double xs[nranges],xe[nranges] #I input range list
+int nranges #I nranges in input list
+int ip #U start position in input list
+double o_xs[ARB],o_xe[ARB] #O output sublist
+
+double tol
+int op, i
+
+begin
+ tol = (EPSILOND * 10.0D0)
+
+ # Determine the range containing or immediately following the
+ # start point of the range of interest.
+
+ while (x1 < xs[ip] && ip > 1)
+ ip = ip - 1
+ while (x1 >= xs[ip])
+ if (x1 <= xe[ip] || ip >= nranges)
+ break
+ else
+ ip = ip + 1
+
+ # Check for an empty output range list.
+ if (xs[ip] > x2)
+ return (0)
+
+ # At least one input range contributes something to the output region.
+ # Copy a portion of the input range list to the ouput range list.
+
+ op = 1
+ do i = ip, nranges {
+ if (xs[i] <= x1)
+ o_xs[op] = LEFTD - tol
+ else
+ o_xs[op] = xs[i]
+
+ if ((xe[i] - x2) >= tol) {
+ o_xe[op] = RIGHTD + tol
+ op = op + 1
+ break
+ } else
+ o_xe[op] = xe[i]
+
+ op = op + 1
+ if (xs[i+1] > x2)
+ break
+ }
+
+ ip = i
+ return (op - 1)
+end
diff --git a/sys/qpoe/gen/qpexsubi.x b/sys/qpoe/gen/qpexsubi.x
new file mode 100644
index 00000000..62ce5087
--- /dev/null
+++ b/sys/qpoe/gen/qpexsubi.x
@@ -0,0 +1,63 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QPEX_SUBLIST -- Extract a sublist spanning the indicated range from a
+# larger range list. The number of ranges extracted is returned as the
+# function value.
+
+int procedure qpex_sublisti (x1, x2, xs,xe,nranges,ip, o_xs,o_xe)
+
+int x1, x2 #I range to be extracted
+int xs[nranges],xe[nranges] #I input range list
+int nranges #I nranges in input list
+int ip #U start position in input list
+int o_xs[ARB],o_xe[ARB] #O output sublist
+
+int tol
+int op, i
+
+begin
+ tol = 0
+
+ # Determine the range containing or immediately following the
+ # start point of the range of interest.
+
+ while (x1 < xs[ip] && ip > 1)
+ ip = ip - 1
+ while (x1 >= xs[ip])
+ if (x1 <= xe[ip] || ip >= nranges)
+ break
+ else
+ ip = ip + 1
+
+ # Check for an empty output range list.
+ if (xs[ip] > x2)
+ return (0)
+
+ # At least one input range contributes something to the output region.
+ # Copy a portion of the input range list to the ouput range list.
+
+ op = 1
+ do i = ip, nranges {
+ if (xs[i] <= x1)
+ o_xs[op] = LEFTI - tol
+ else
+ o_xs[op] = xs[i]
+
+ if ((xe[i] - x2) >= tol) {
+ o_xe[op] = RIGHTI + tol
+ op = op + 1
+ break
+ } else
+ o_xe[op] = xe[i]
+
+ op = op + 1
+ if (xs[i+1] > x2)
+ break
+ }
+
+ ip = i
+ return (op - 1)
+end
diff --git a/sys/qpoe/gen/qpexsubr.x b/sys/qpoe/gen/qpexsubr.x
new file mode 100644
index 00000000..147bf14b
--- /dev/null
+++ b/sys/qpoe/gen/qpexsubr.x
@@ -0,0 +1,63 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QPEX_SUBLIST -- Extract a sublist spanning the indicated range from a
+# larger range list. The number of ranges extracted is returned as the
+# function value.
+
+int procedure qpex_sublistr (x1, x2, xs,xe,nranges,ip, o_xs,o_xe)
+
+real x1, x2 #I range to be extracted
+real xs[nranges],xe[nranges] #I input range list
+int nranges #I nranges in input list
+int ip #U start position in input list
+real o_xs[ARB],o_xe[ARB] #O output sublist
+
+real tol
+int op, i
+
+begin
+ tol = (EPSILONR * 10.0)
+
+ # Determine the range containing or immediately following the
+ # start point of the range of interest.
+
+ while (x1 < xs[ip] && ip > 1)
+ ip = ip - 1
+ while (x1 >= xs[ip])
+ if (x1 <= xe[ip] || ip >= nranges)
+ break
+ else
+ ip = ip + 1
+
+ # Check for an empty output range list.
+ if (xs[ip] > x2)
+ return (0)
+
+ # At least one input range contributes something to the output region.
+ # Copy a portion of the input range list to the ouput range list.
+
+ op = 1
+ do i = ip, nranges {
+ if (xs[i] <= x1)
+ o_xs[op] = LEFTR - tol
+ else
+ o_xs[op] = xs[i]
+
+ if ((xe[i] - x2) >= tol) {
+ o_xe[op] = RIGHTR + tol
+ op = op + 1
+ break
+ } else
+ o_xe[op] = xe[i]
+
+ op = op + 1
+ if (xs[i+1] > x2)
+ break
+ }
+
+ ip = i
+ return (op - 1)
+end
diff --git a/sys/qpoe/gen/qpgetc.x b/sys/qpoe/gen/qpgetc.x
new file mode 100644
index 00000000..1b6ce6fe
--- /dev/null
+++ b/sys/qpoe/gen/qpgetc.x
@@ -0,0 +1,63 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_GET -- Return the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# returned by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+char procedure qp_getc (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer pp
+int dtype
+char value
+int qp_getparam()
+errchk qp_getparam, syserrs
+
+begin
+ # Lookup the parameter and it's value.
+ dtype = qp_getparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ # Set default value of INDEF or NULL.
+ value = (NULL)
+
+ # Get a valid parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ value = (Memc[pp])
+ case TY_SHORT:
+ if (!IS_INDEFS(Mems[pp]))
+ value = (Mems[pp])
+ case TY_INT:
+ if (!IS_INDEFI(Memi[pp]))
+ value = (Memi[pp])
+ case TY_LONG:
+ if (!IS_INDEFL(Meml[pp]))
+ value = (Meml[pp])
+ case TY_REAL:
+ if (!IS_INDEFR(Memr[pp]))
+ value = (Memr[pp])
+ case TY_DOUBLE:
+ if (!IS_INDEFD(Memd[pp]))
+ value = (Memd[pp])
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n")
+ call pargstr (param)
+ call pargi (dtype)
+ call pargi (TY_CHAR)
+ call pargc (value)
+ }
+
+ return (value)
+end
diff --git a/sys/qpoe/gen/qpgetd.x b/sys/qpoe/gen/qpgetd.x
new file mode 100644
index 00000000..fea90d0f
--- /dev/null
+++ b/sys/qpoe/gen/qpgetd.x
@@ -0,0 +1,63 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_GET -- Return the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# returned by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+double procedure qp_getd (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer pp
+int dtype
+double value
+int qp_getparam()
+errchk qp_getparam, syserrs
+
+begin
+ # Lookup the parameter and it's value.
+ dtype = qp_getparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ # Set default value of INDEF or NULL.
+ value = (INDEFD)
+
+ # Get a valid parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ value = (Memc[pp])
+ case TY_SHORT:
+ if (!IS_INDEFS(Mems[pp]))
+ value = (Mems[pp])
+ case TY_INT:
+ if (!IS_INDEFI(Memi[pp]))
+ value = (Memi[pp])
+ case TY_LONG:
+ if (!IS_INDEFL(Meml[pp]))
+ value = (Meml[pp])
+ case TY_REAL:
+ if (!IS_INDEFR(Memr[pp]))
+ value = (Memr[pp])
+ case TY_DOUBLE:
+ if (!IS_INDEFD(Memd[pp]))
+ value = (Memd[pp])
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n")
+ call pargstr (param)
+ call pargi (dtype)
+ call pargi (TY_DOUBLE)
+ call pargd (value)
+ }
+
+ return (value)
+end
diff --git a/sys/qpoe/gen/qpgeti.x b/sys/qpoe/gen/qpgeti.x
new file mode 100644
index 00000000..c40d5de6
--- /dev/null
+++ b/sys/qpoe/gen/qpgeti.x
@@ -0,0 +1,63 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_GET -- Return the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# returned by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+int procedure qp_geti (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer pp
+int dtype
+int value
+int qp_getparam()
+errchk qp_getparam, syserrs
+
+begin
+ # Lookup the parameter and it's value.
+ dtype = qp_getparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ # Set default value of INDEF or NULL.
+ value = (INDEFI)
+
+ # Get a valid parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ value = (Memc[pp])
+ case TY_SHORT:
+ if (!IS_INDEFS(Mems[pp]))
+ value = (Mems[pp])
+ case TY_INT:
+ if (!IS_INDEFI(Memi[pp]))
+ value = (Memi[pp])
+ case TY_LONG:
+ if (!IS_INDEFL(Meml[pp]))
+ value = (Meml[pp])
+ case TY_REAL:
+ if (!IS_INDEFR(Memr[pp]))
+ value = (Memr[pp])
+ case TY_DOUBLE:
+ if (!IS_INDEFD(Memd[pp]))
+ value = (Memd[pp])
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n")
+ call pargstr (param)
+ call pargi (dtype)
+ call pargi (TY_INT)
+ call pargi (value)
+ }
+
+ return (value)
+end
diff --git a/sys/qpoe/gen/qpgetl.x b/sys/qpoe/gen/qpgetl.x
new file mode 100644
index 00000000..804e2def
--- /dev/null
+++ b/sys/qpoe/gen/qpgetl.x
@@ -0,0 +1,63 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_GET -- Return the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# returned by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+long procedure qp_getl (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer pp
+int dtype
+long value
+int qp_getparam()
+errchk qp_getparam, syserrs
+
+begin
+ # Lookup the parameter and it's value.
+ dtype = qp_getparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ # Set default value of INDEF or NULL.
+ value = (INDEFL)
+
+ # Get a valid parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ value = (Memc[pp])
+ case TY_SHORT:
+ if (!IS_INDEFS(Mems[pp]))
+ value = (Mems[pp])
+ case TY_INT:
+ if (!IS_INDEFI(Memi[pp]))
+ value = (Memi[pp])
+ case TY_LONG:
+ if (!IS_INDEFL(Meml[pp]))
+ value = (Meml[pp])
+ case TY_REAL:
+ if (!IS_INDEFR(Memr[pp]))
+ value = (Memr[pp])
+ case TY_DOUBLE:
+ if (!IS_INDEFD(Memd[pp]))
+ value = (Memd[pp])
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n")
+ call pargstr (param)
+ call pargi (dtype)
+ call pargi (TY_LONG)
+ call pargl (value)
+ }
+
+ return (value)
+end
diff --git a/sys/qpoe/gen/qpgetr.x b/sys/qpoe/gen/qpgetr.x
new file mode 100644
index 00000000..1990a413
--- /dev/null
+++ b/sys/qpoe/gen/qpgetr.x
@@ -0,0 +1,63 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_GET -- Return the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# returned by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+real procedure qp_getr (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer pp
+int dtype
+real value
+int qp_getparam()
+errchk qp_getparam, syserrs
+
+begin
+ # Lookup the parameter and it's value.
+ dtype = qp_getparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ # Set default value of INDEF or NULL.
+ value = (INDEFR)
+
+ # Get a valid parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ value = (Memc[pp])
+ case TY_SHORT:
+ if (!IS_INDEFS(Mems[pp]))
+ value = (Mems[pp])
+ case TY_INT:
+ if (!IS_INDEFI(Memi[pp]))
+ value = (Memi[pp])
+ case TY_LONG:
+ if (!IS_INDEFL(Meml[pp]))
+ value = (Meml[pp])
+ case TY_REAL:
+ if (!IS_INDEFR(Memr[pp]))
+ value = (Memr[pp])
+ case TY_DOUBLE:
+ if (!IS_INDEFD(Memd[pp]))
+ value = (Memd[pp])
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n")
+ call pargstr (param)
+ call pargi (dtype)
+ call pargi (TY_REAL)
+ call pargr (value)
+ }
+
+ return (value)
+end
diff --git a/sys/qpoe/gen/qpgets.x b/sys/qpoe/gen/qpgets.x
new file mode 100644
index 00000000..3f6500ef
--- /dev/null
+++ b/sys/qpoe/gen/qpgets.x
@@ -0,0 +1,63 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_GET -- Return the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# returned by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+short procedure qp_gets (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer pp
+int dtype
+short value
+int qp_getparam()
+errchk qp_getparam, syserrs
+
+begin
+ # Lookup the parameter and it's value.
+ dtype = qp_getparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ # Set default value of INDEF or NULL.
+ value = (INDEFS)
+
+ # Get a valid parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ value = (Memc[pp])
+ case TY_SHORT:
+ if (!IS_INDEFS(Mems[pp]))
+ value = (Mems[pp])
+ case TY_INT:
+ if (!IS_INDEFI(Memi[pp]))
+ value = (Memi[pp])
+ case TY_LONG:
+ if (!IS_INDEFL(Meml[pp]))
+ value = (Meml[pp])
+ case TY_REAL:
+ if (!IS_INDEFR(Memr[pp]))
+ value = (Memr[pp])
+ case TY_DOUBLE:
+ if (!IS_INDEFD(Memd[pp]))
+ value = (Memd[pp])
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n")
+ call pargstr (param)
+ call pargi (dtype)
+ call pargi (TY_SHORT)
+ call pargs (value)
+ }
+
+ return (value)
+end
diff --git a/sys/qpoe/gen/qpiogetev.x b/sys/qpoe/gen/qpiogetev.x
new file mode 100644
index 00000000..7d029a94
--- /dev/null
+++ b/sys/qpoe/gen/qpiogetev.x
@@ -0,0 +1,1968 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <pmset.h>
+include "../qpio.h"
+
+define RLI_NEXTLINE 9998
+define RLI_INITIALIZE 9999
+define SZ_CODE 7
+
+# QPIO_GETEVENTS -- Return a sequence of events sharing the same mask value
+# which satisfy the current event attribute filter. The returned events will
+# be only those in a rectangular subregion of the image (specified by a prior
+# call to qpio_setrange) which are also visible through the current mask.
+# Sequences of events are returned in storage order until the region is
+# exhausted, at which time EOF is returned.
+#
+# NOTE - If debug statements (printfs) are placed in this code they will cause
+# i/o problems at runtime due to reentrancy, since this routine is called in
+# a low level FIO pseudodevice driver (QPF). This is also true of any of the
+# routines called by this procedure, and of the related routine QPIO_READPIX.
+
+int procedure qpio_gvtevents (io, o_ev, maskval, maxev, o_nev)
+
+pointer io #I QPIO descriptor
+pointer o_ev[maxev] #O receives the event struct pointers
+int maskval #O receives the mask value of the events
+int maxev #I max events out
+int o_nev #O same as function value (nev_out|EOF)
+
+int status
+char code[SZ_CODE]
+int qpx_gvs(), qpx_gvi(), qpx_gvl(), qpx_gvr(), qpx_gvd()
+errchk syserrs
+define err_ 91
+
+begin
+ # The generic routines currently require that X,Y be the same type.
+ # It wouldn't be hard to remove this restriction if necessary, but
+ # it simplifies things and I doubt if a mixed types feature would
+ # be used very often.
+
+ if (IO_EVXTYPE(io) != IO_EVYTYPE(io))
+ goto err_
+
+ # Get the events.
+ switch (IO_EVXTYPE(io)) {
+ case TY_SHORT:
+ status = qpx_gvs (io, o_ev, maskval, maxev, o_nev)
+ case TY_INT:
+ status = qpx_gvi (io, o_ev, maskval, maxev, o_nev)
+ case TY_LONG:
+ status = qpx_gvl (io, o_ev, maskval, maxev, o_nev)
+ case TY_REAL:
+ status = qpx_gvr (io, o_ev, maskval, maxev, o_nev)
+ case TY_DOUBLE:
+ status = qpx_gvd (io, o_ev, maskval, maxev, o_nev)
+ default:
+err_ call sprintf (code, SZ_CODE, "%d")
+ call pargi (IO_EVXTYPE(io))
+ call syserrs (SYS_QPINVEVT, code)
+ }
+
+ return (status)
+end
+
+
+
+
+# QPX_GV -- Internal generic code for qpio_getevents. There is one copy
+# of this routine for each event coordinate datatype. The optimization
+# strategy used here assumes that executing qpio_gv is much more expensive
+# than building the call in qpio_getevents. This will normally be the case
+# for a large event list or a complex expression, otherwise the operation
+# is likely to be fast enough that it doesn't matter anyway.
+
+int procedure qpx_gvs (io, o_ev, maskval, maxev, o_nev)
+
+pointer io #I QPIO descriptor
+pointer o_ev[maxev] #O receives the event struct pointers
+int maskval #O receives the mask value of the events
+int maxev #I max events out
+int o_nev #O same as function value (nev_out|EOF)
+
+int x1, x2, y1, y2, xs, xe, ys, ye, x, y
+pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp
+bool useindex, lineio, bbused, rmused, nodata
+int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj
+int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i
+int ev_xoff, ev_yoff
+
+pointer plr_open()
+bool pl_linenotempty(), pl_sectnotempty()
+int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix()
+
+define swap {temp=$1;$1=$2;$2=temp}
+define putevent_ 91
+define again_ 92
+define done_ 93
+define exit_ 94
+
+begin
+ pl = IO_PL(io) # pixel list (region mask) descriptor
+ rl = IO_RL(io) # range list buffer
+ bp = IO_BP(io) # bucket buffer (type short)
+ ex = IO_EX(io) # QPEX (EAF) descriptor
+
+ # The following is executed when the first i/o is performed on a new
+ # region, to select the most efficient type of i/o to be performed,
+ # and initialize the i/o parameters for that case. The type of i/o
+ # to be performed depends upon whether or not an index can be used,
+ # and whether or not there is a region mask (RM) or bounding box (BB).
+ # The presence or absence of an event attribute filter (EAF) is not
+ # separated out as a special case, as it is quick and easy to test
+ # for the presence of an EAF and apply one it if it exists.
+
+ if (IO_ACTIVE(io) == NO) {
+ # Check for an index. We have an index if the event list is
+ # indexed, and the index is defined on the Y-coordinate we will
+ # be using for extraction.
+
+ useindex = (IO_INDEXLEN(io) == IO_NLINES(io) &&
+ IO_EVYOFF(io) == IO_IXYOFF(io) &&
+ IO_NOINDEX(io) == NO)
+
+ # Initialize the V and VN vectors.
+ do i = 1, NDIM {
+ IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1
+ if (IO_VN(io,i) < 0) {
+ swap (IO_VS(io,i), IO_VE(io,i))
+ IO_VN(io,i) = -IO_VN(io,i)
+ }
+ }
+ call amovi (IO_VS(io,1), IO_V(io,1), NDIM)
+
+ # Determine if full lines are to be accessed, and if a bounding
+ # box (subraster of the image) is defined.
+
+ lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io))
+ bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io))
+
+ # Determine if region mask data is to be used and if there is any
+ # data to be read.
+
+ nodata = (IO_NEVENTS(io) <= 0)
+ rmused = false
+
+ if (pl != NULL)
+ if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM))
+ rmused = true
+ else
+ nodata = true
+
+ # Select the optimal type of i/o to be used for extraction.
+ if (nodata) {
+ IO_IOTYPE(io) = NoDATA_NoAREA
+ useindex = false
+ bbused = false
+
+ } else if (bbused || rmused) {
+ if (useindex)
+ IO_IOTYPE(io) = INDEX_RMorBB
+ else
+ IO_IOTYPE(io) = NoINDEX_RMorBB
+
+ } else {
+ # If we are reading the entire image (no bounding box) and
+ # we are not using a mask, then there is no point in using
+ # indexed i/o.
+
+ IO_IOTYPE(io) = NoINDEX_NoRMorBB
+ useindex = false
+ }
+
+ # Initialize the range list data if it will be used.
+ if (useindex) {
+ # Dummy range specifying full line segment.
+ RLI_LEN(rl) = RL_FIRST
+ RLI_AXLEN(rl) = IO_NCOLS(io)
+
+ rp = rl + ((RL_FIRST - 1) * RL_LENELEM)
+ Memi[rp+RL_XOFF] = IO_VS(io,1)
+ Memi[rp+RL_NOFF] = IO_VN(io,1)
+ Memi[rp+RL_VOFF] = 1
+
+ IO_RLI(io) = RLI_INITIALIZE
+ }
+
+ # Open the mask for random access if i/o is not indexed and
+ # a region mask is used.
+
+ bbmask = IO_BBMASK(io)
+ if (bbmask != NULL)
+ call plr_close (bbmask)
+
+ if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) {
+ bbmask = plr_open (pl, v, 0) # (v is never referenced)
+ call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2),
+ IO_VE(io,1),IO_VE(io,2))
+ call plr_getlut (bbmask,
+ bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock)
+ }
+
+ # Update the QPIO descriptor.
+ IO_LINEIO(io) = btoi(lineio)
+ IO_RMUSED(io) = btoi(rmused)
+ IO_BBUSED(io) = btoi(bbused)
+ IO_BBMASK(io) = bbmask
+
+ IO_EVI(io) = 1
+ IO_BKNO(io) = 0
+ IO_BKLASTEV(io) = 0
+
+ IO_ACTIVE(io) = YES
+ }
+
+ # Initialize event extraction parameters.
+ szs_event = IO_EVENTLEN(io)
+ maskval = 0
+ nev = 0
+
+ ev_xoff = IO_EVXOFF(io)
+ ev_yoff = IO_EVYOFF(io)
+
+ # Extract events using the most efficient type of i/o for the given
+ # selection critera (index, mask, BB, EAF, etc.).
+again_
+ switch (IO_IOTYPE(io)) {
+ case NoDATA_NoAREA:
+ # We know in advance that there are no events to be returned,
+ # either because there is no data, or the area of the region
+ # mask within the bounding box is empty.
+
+ goto exit_
+
+ case NoINDEX_NoRMorBB:
+ # This is the simplest case; no index, region mask, or bounding
+ # box. Read and output all events in sequence.
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Copy out the event pointers.
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1)
+
+ do i = 1, nev {
+ o_ev[i] = ev
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = IO_EVI(io) + nev
+ maskval = 1
+
+ case NoINDEX_RMorBB:
+ # Fully general selection, including any combination of bounding
+ # box, region mask, or EAF, but no index, either because there is
+ # no index for this event list, or the index is for a different Y
+ # attribute than the one being used for extraction.
+
+ bbused = (IO_BBUSED(io) == YES)
+ x1 = IO_VS(io,1); x2 = IO_VE(io,1)
+ y1 = IO_VS(io,2); y2 = IO_VE(io,2)
+
+ # Refill the event bucket?
+ while (IO_EVI(io) > IO_BKLASTEV(io)) {
+ # Get the next bucket.
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Reject buckets that do not contain any events lying
+ # within the specified bounding box, if any.
+
+ if (bbused) {
+ ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_SHORT + 1
+ xs = Mems[ev_p+ev_xoff]
+ ys = Mems[ev_p+ev_yoff]
+
+ ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_SHORT + 1
+ xe = Mems[ev_p+ev_xoff]
+ ye = Mems[ev_p+ev_yoff]
+
+ if (xs > x2 || xe < x1 || ys > y2 || ye < y1)
+ IO_EVI(io) = IO_BKLASTEV(io) + 1
+ }
+ }
+
+ # Copy out any events which pass the region mask and which share
+ # the same mask value. Note that in this case, to speed mask
+ # value lookup at random mask coordinates, the region mask for
+ # the bounding box is stored as a populated array in the QPIO
+ # descriptor.
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event
+ bbmask = IO_BBMASK(io)
+ mval = 0
+
+ do i = IO_EVI(io), IO_BKLASTEV(io) {
+ # Get event x,y coordinates in whatever coord system.
+ ev = ev + szs_event
+ ev_p = (ev - 1) * SZ_SHORT / SZ_SHORT + 1
+
+ x = Mems[ev_p+ev_xoff]
+ y = Mems[ev_p+ev_yoff]
+
+ # Reject events lying outside the bounding box.
+ if (bbused)
+ if (x < x1 || x > x2 || y < y1 || y > y2)
+ next
+
+ # Take a shortcut if no region mask is in effect for this BB.
+ if (bbmask == NULL)
+ goto putevent_
+
+ # Get the mask pixel associated with this event.
+ ii = (x - 1) / bb_xblock
+ jj = (y - 1) / bb_yblock
+ mval = Memi[bb_bufp + jj*bb_xsize + ii]
+ if (mval < 0)
+ mval = plr_getpix (bbmask, x, y)
+
+ # Accumulate points lying in the first nonzero mask range
+ # encountered.
+
+ if (mval != 0) {
+ if (maskval == 0)
+ maskval = mval
+ if (mval == maskval) {
+putevent_ if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ } else
+ break
+ }
+ }
+
+ IO_EVI(io) = i
+
+ case INDEX_NoRMorBB, INDEX_RMorBB:
+ # General extraction for indexed data. Process successive ranges
+ # and range lists until we get at least one event which lies within
+ # the bounding box, within a range, and which passes the event
+ # attribute filter, if one is in use.
+
+ # If the current range list (mask line) has been exhausted, advance
+ # to the next line which contains both ranges and events. A range
+ # list is used to specify the bounding box even if we don't have
+ # a nonempty region mask within the BB.
+
+ if (IO_RLI(io) > RLI_LEN(rl)) {
+ repeat {
+ y = IO_V(io,2)
+ if (IO_RLI(io) == RLI_INITIALIZE)
+ IO_RLI(io) = RL_FIRST
+ else
+ y = y + 1
+
+ if (y > IO_VE(io,2)) {
+ if (nev <= 0) {
+ o_nev = EOF
+ return (EOF)
+ } else
+ goto done_
+ }
+
+ IO_V(io,2) = y
+ evidx = Memi[IO_YOFFVP(io)+y-1]
+
+ if (evidx > 0) {
+ if (IO_RMUSED(io) == YES) {
+ if (IO_LINEIO(io) == YES) {
+ if (!pl_linenotempty (pl,IO_V(io,1)))
+ next
+ } else {
+ v[1] = IO_VE(io,1); v[2] = y
+ if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM))
+ next
+ }
+ call pl_glri (pl, IO_V(io,1), Memi[rl],
+ IO_MDEPTH(io), IO_VN(io,1), PIX_SRC)
+ }
+ IO_RLI(io) = RL_FIRST
+ }
+ } until (IO_RLI(io) <= RLI_LEN(rl))
+
+ IO_EVI(io) = evidx
+ IO_EV1(io) = evidx
+ IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1
+ }
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Compute current range parameters and initialize event pointer.
+ rp = rl + (IO_RLI(io) - 1) * RL_LENELEM
+ x1 = Memi[rp+RL_XOFF]
+ x2 = x1 + Memi[rp+RL_NOFF] - 1
+ maskval = Memi[rp+RL_VOFF]
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ evtop = min (IO_EV2(io), IO_BKLASTEV(io))
+
+ # Extract events from bucket which lie within the current range
+ # of the current line. This is the inner loop of indexed event
+ # extraction, ignoring event attribute filtering.
+
+ do i = IO_EVI(io), evtop {
+ ev_p = (ev - 1) * SZ_SHORT / SZ_SHORT + 1
+ x = Mems[ev_p+ev_xoff]
+ if (x >= x1) {
+ if (x > x2) {
+ IO_RLI(io) = IO_RLI(io) + 1
+ break
+ } else if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ }
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = i
+ if (i > IO_EV2(io))
+ IO_RLI(io) = RLI_NEXTLINE
+ }
+done_
+ # Apply the event attribute filter if one is defined; repeat
+ # the whole process if we don't end up with any events.
+
+ if (nev > 0)
+ if (ex != NULL)
+ nev = qpex_evaluate (ex, o_ev, o_ev, nev)
+ if (nev <= 0)
+ goto again_
+exit_
+ o_nev = nev
+ if (o_nev <= 0)
+ o_nev = EOF
+
+ return (o_nev)
+end
+
+
+
+# QPX_GV -- Internal generic code for qpio_getevents. There is one copy
+# of this routine for each event coordinate datatype. The optimization
+# strategy used here assumes that executing qpio_gv is much more expensive
+# than building the call in qpio_getevents. This will normally be the case
+# for a large event list or a complex expression, otherwise the operation
+# is likely to be fast enough that it doesn't matter anyway.
+
+int procedure qpx_gvi (io, o_ev, maskval, maxev, o_nev)
+
+pointer io #I QPIO descriptor
+pointer o_ev[maxev] #O receives the event struct pointers
+int maskval #O receives the mask value of the events
+int maxev #I max events out
+int o_nev #O same as function value (nev_out|EOF)
+
+int x1, x2, y1, y2, xs, xe, ys, ye, x, y
+pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp
+bool useindex, lineio, bbused, rmused, nodata
+int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj
+int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i
+int ev_xoff, ev_yoff
+
+pointer plr_open()
+bool pl_linenotempty(), pl_sectnotempty()
+int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix()
+
+define swap {temp=$1;$1=$2;$2=temp}
+define putevent_ 91
+define again_ 92
+define done_ 93
+define exit_ 94
+
+begin
+ pl = IO_PL(io) # pixel list (region mask) descriptor
+ rl = IO_RL(io) # range list buffer
+ bp = IO_BP(io) # bucket buffer (type short)
+ ex = IO_EX(io) # QPEX (EAF) descriptor
+
+ # The following is executed when the first i/o is performed on a new
+ # region, to select the most efficient type of i/o to be performed,
+ # and initialize the i/o parameters for that case. The type of i/o
+ # to be performed depends upon whether or not an index can be used,
+ # and whether or not there is a region mask (RM) or bounding box (BB).
+ # The presence or absence of an event attribute filter (EAF) is not
+ # separated out as a special case, as it is quick and easy to test
+ # for the presence of an EAF and apply one it if it exists.
+
+ if (IO_ACTIVE(io) == NO) {
+ # Check for an index. We have an index if the event list is
+ # indexed, and the index is defined on the Y-coordinate we will
+ # be using for extraction.
+
+ useindex = (IO_INDEXLEN(io) == IO_NLINES(io) &&
+ IO_EVYOFF(io) == IO_IXYOFF(io) &&
+ IO_NOINDEX(io) == NO)
+
+ # Initialize the V and VN vectors.
+ do i = 1, NDIM {
+ IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1
+ if (IO_VN(io,i) < 0) {
+ swap (IO_VS(io,i), IO_VE(io,i))
+ IO_VN(io,i) = -IO_VN(io,i)
+ }
+ }
+ call amovi (IO_VS(io,1), IO_V(io,1), NDIM)
+
+ # Determine if full lines are to be accessed, and if a bounding
+ # box (subraster of the image) is defined.
+
+ lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io))
+ bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io))
+
+ # Determine if region mask data is to be used and if there is any
+ # data to be read.
+
+ nodata = (IO_NEVENTS(io) <= 0)
+ rmused = false
+
+ if (pl != NULL)
+ if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM))
+ rmused = true
+ else
+ nodata = true
+
+ # Select the optimal type of i/o to be used for extraction.
+ if (nodata) {
+ IO_IOTYPE(io) = NoDATA_NoAREA
+ useindex = false
+ bbused = false
+
+ } else if (bbused || rmused) {
+ if (useindex)
+ IO_IOTYPE(io) = INDEX_RMorBB
+ else
+ IO_IOTYPE(io) = NoINDEX_RMorBB
+
+ } else {
+ # If we are reading the entire image (no bounding box) and
+ # we are not using a mask, then there is no point in using
+ # indexed i/o.
+
+ IO_IOTYPE(io) = NoINDEX_NoRMorBB
+ useindex = false
+ }
+
+ # Initialize the range list data if it will be used.
+ if (useindex) {
+ # Dummy range specifying full line segment.
+ RLI_LEN(rl) = RL_FIRST
+ RLI_AXLEN(rl) = IO_NCOLS(io)
+
+ rp = rl + ((RL_FIRST - 1) * RL_LENELEM)
+ Memi[rp+RL_XOFF] = IO_VS(io,1)
+ Memi[rp+RL_NOFF] = IO_VN(io,1)
+ Memi[rp+RL_VOFF] = 1
+
+ IO_RLI(io) = RLI_INITIALIZE
+ }
+
+ # Open the mask for random access if i/o is not indexed and
+ # a region mask is used.
+
+ bbmask = IO_BBMASK(io)
+ if (bbmask != NULL)
+ call plr_close (bbmask)
+
+ if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) {
+ bbmask = plr_open (pl, v, 0) # (v is never referenced)
+ call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2),
+ IO_VE(io,1),IO_VE(io,2))
+ call plr_getlut (bbmask,
+ bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock)
+ }
+
+ # Update the QPIO descriptor.
+ IO_LINEIO(io) = btoi(lineio)
+ IO_RMUSED(io) = btoi(rmused)
+ IO_BBUSED(io) = btoi(bbused)
+ IO_BBMASK(io) = bbmask
+
+ IO_EVI(io) = 1
+ IO_BKNO(io) = 0
+ IO_BKLASTEV(io) = 0
+
+ IO_ACTIVE(io) = YES
+ }
+
+ # Initialize event extraction parameters.
+ szs_event = IO_EVENTLEN(io)
+ maskval = 0
+ nev = 0
+
+ ev_xoff = IO_EVXOFF(io)
+ ev_yoff = IO_EVYOFF(io)
+
+ # Extract events using the most efficient type of i/o for the given
+ # selection critera (index, mask, BB, EAF, etc.).
+again_
+ switch (IO_IOTYPE(io)) {
+ case NoDATA_NoAREA:
+ # We know in advance that there are no events to be returned,
+ # either because there is no data, or the area of the region
+ # mask within the bounding box is empty.
+
+ goto exit_
+
+ case NoINDEX_NoRMorBB:
+ # This is the simplest case; no index, region mask, or bounding
+ # box. Read and output all events in sequence.
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Copy out the event pointers.
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1)
+
+ do i = 1, nev {
+ o_ev[i] = ev
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = IO_EVI(io) + nev
+ maskval = 1
+
+ case NoINDEX_RMorBB:
+ # Fully general selection, including any combination of bounding
+ # box, region mask, or EAF, but no index, either because there is
+ # no index for this event list, or the index is for a different Y
+ # attribute than the one being used for extraction.
+
+ bbused = (IO_BBUSED(io) == YES)
+ x1 = IO_VS(io,1); x2 = IO_VE(io,1)
+ y1 = IO_VS(io,2); y2 = IO_VE(io,2)
+
+ # Refill the event bucket?
+ while (IO_EVI(io) > IO_BKLASTEV(io)) {
+ # Get the next bucket.
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Reject buckets that do not contain any events lying
+ # within the specified bounding box, if any.
+
+ if (bbused) {
+ ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_INT + 1
+ xs = Memi[ev_p+ev_xoff]
+ ys = Memi[ev_p+ev_yoff]
+
+ ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_INT + 1
+ xe = Memi[ev_p+ev_xoff]
+ ye = Memi[ev_p+ev_yoff]
+
+ if (xs > x2 || xe < x1 || ys > y2 || ye < y1)
+ IO_EVI(io) = IO_BKLASTEV(io) + 1
+ }
+ }
+
+ # Copy out any events which pass the region mask and which share
+ # the same mask value. Note that in this case, to speed mask
+ # value lookup at random mask coordinates, the region mask for
+ # the bounding box is stored as a populated array in the QPIO
+ # descriptor.
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event
+ bbmask = IO_BBMASK(io)
+ mval = 0
+
+ do i = IO_EVI(io), IO_BKLASTEV(io) {
+ # Get event x,y coordinates in whatever coord system.
+ ev = ev + szs_event
+ ev_p = (ev - 1) * SZ_SHORT / SZ_INT + 1
+
+ x = Memi[ev_p+ev_xoff]
+ y = Memi[ev_p+ev_yoff]
+
+ # Reject events lying outside the bounding box.
+ if (bbused)
+ if (x < x1 || x > x2 || y < y1 || y > y2)
+ next
+
+ # Take a shortcut if no region mask is in effect for this BB.
+ if (bbmask == NULL)
+ goto putevent_
+
+ # Get the mask pixel associated with this event.
+ ii = (x - 1) / bb_xblock
+ jj = (y - 1) / bb_yblock
+ mval = Memi[bb_bufp + jj*bb_xsize + ii]
+ if (mval < 0)
+ mval = plr_getpix (bbmask, x, y)
+
+ # Accumulate points lying in the first nonzero mask range
+ # encountered.
+
+ if (mval != 0) {
+ if (maskval == 0)
+ maskval = mval
+ if (mval == maskval) {
+putevent_ if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ } else
+ break
+ }
+ }
+
+ IO_EVI(io) = i
+
+ case INDEX_NoRMorBB, INDEX_RMorBB:
+ # General extraction for indexed data. Process successive ranges
+ # and range lists until we get at least one event which lies within
+ # the bounding box, within a range, and which passes the event
+ # attribute filter, if one is in use.
+
+ # If the current range list (mask line) has been exhausted, advance
+ # to the next line which contains both ranges and events. A range
+ # list is used to specify the bounding box even if we don't have
+ # a nonempty region mask within the BB.
+
+ if (IO_RLI(io) > RLI_LEN(rl)) {
+ repeat {
+ y = IO_V(io,2)
+ if (IO_RLI(io) == RLI_INITIALIZE)
+ IO_RLI(io) = RL_FIRST
+ else
+ y = y + 1
+
+ if (y > IO_VE(io,2)) {
+ if (nev <= 0) {
+ o_nev = EOF
+ return (EOF)
+ } else
+ goto done_
+ }
+
+ IO_V(io,2) = y
+ evidx = Memi[IO_YOFFVP(io)+y-1]
+
+ if (evidx > 0) {
+ if (IO_RMUSED(io) == YES) {
+ if (IO_LINEIO(io) == YES) {
+ if (!pl_linenotempty (pl,IO_V(io,1)))
+ next
+ } else {
+ v[1] = IO_VE(io,1); v[2] = y
+ if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM))
+ next
+ }
+ call pl_glri (pl, IO_V(io,1), Memi[rl],
+ IO_MDEPTH(io), IO_VN(io,1), PIX_SRC)
+ }
+ IO_RLI(io) = RL_FIRST
+ }
+ } until (IO_RLI(io) <= RLI_LEN(rl))
+
+ IO_EVI(io) = evidx
+ IO_EV1(io) = evidx
+ IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1
+ }
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Compute current range parameters and initialize event pointer.
+ rp = rl + (IO_RLI(io) - 1) * RL_LENELEM
+ x1 = Memi[rp+RL_XOFF]
+ x2 = x1 + Memi[rp+RL_NOFF] - 1
+ maskval = Memi[rp+RL_VOFF]
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ evtop = min (IO_EV2(io), IO_BKLASTEV(io))
+
+ # Extract events from bucket which lie within the current range
+ # of the current line. This is the inner loop of indexed event
+ # extraction, ignoring event attribute filtering.
+
+ do i = IO_EVI(io), evtop {
+ ev_p = (ev - 1) * SZ_SHORT / SZ_INT + 1
+ x = Memi[ev_p+ev_xoff]
+ if (x >= x1) {
+ if (x > x2) {
+ IO_RLI(io) = IO_RLI(io) + 1
+ break
+ } else if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ }
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = i
+ if (i > IO_EV2(io))
+ IO_RLI(io) = RLI_NEXTLINE
+ }
+done_
+ # Apply the event attribute filter if one is defined; repeat
+ # the whole process if we don't end up with any events.
+
+ if (nev > 0)
+ if (ex != NULL)
+ nev = qpex_evaluate (ex, o_ev, o_ev, nev)
+ if (nev <= 0)
+ goto again_
+exit_
+ o_nev = nev
+ if (o_nev <= 0)
+ o_nev = EOF
+
+ return (o_nev)
+end
+
+
+
+# QPX_GV -- Internal generic code for qpio_getevents. There is one copy
+# of this routine for each event coordinate datatype. The optimization
+# strategy used here assumes that executing qpio_gv is much more expensive
+# than building the call in qpio_getevents. This will normally be the case
+# for a large event list or a complex expression, otherwise the operation
+# is likely to be fast enough that it doesn't matter anyway.
+
+int procedure qpx_gvl (io, o_ev, maskval, maxev, o_nev)
+
+pointer io #I QPIO descriptor
+pointer o_ev[maxev] #O receives the event struct pointers
+int maskval #O receives the mask value of the events
+int maxev #I max events out
+int o_nev #O same as function value (nev_out|EOF)
+
+int x1, x2, y1, y2, xs, xe, ys, ye, x, y
+pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp
+bool useindex, lineio, bbused, rmused, nodata
+int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj
+int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i
+int ev_xoff, ev_yoff
+
+pointer plr_open()
+bool pl_linenotempty(), pl_sectnotempty()
+int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix()
+
+define swap {temp=$1;$1=$2;$2=temp}
+define putevent_ 91
+define again_ 92
+define done_ 93
+define exit_ 94
+
+begin
+ pl = IO_PL(io) # pixel list (region mask) descriptor
+ rl = IO_RL(io) # range list buffer
+ bp = IO_BP(io) # bucket buffer (type short)
+ ex = IO_EX(io) # QPEX (EAF) descriptor
+
+ # The following is executed when the first i/o is performed on a new
+ # region, to select the most efficient type of i/o to be performed,
+ # and initialize the i/o parameters for that case. The type of i/o
+ # to be performed depends upon whether or not an index can be used,
+ # and whether or not there is a region mask (RM) or bounding box (BB).
+ # The presence or absence of an event attribute filter (EAF) is not
+ # separated out as a special case, as it is quick and easy to test
+ # for the presence of an EAF and apply one it if it exists.
+
+ if (IO_ACTIVE(io) == NO) {
+ # Check for an index. We have an index if the event list is
+ # indexed, and the index is defined on the Y-coordinate we will
+ # be using for extraction.
+
+ useindex = (IO_INDEXLEN(io) == IO_NLINES(io) &&
+ IO_EVYOFF(io) == IO_IXYOFF(io) &&
+ IO_NOINDEX(io) == NO)
+
+ # Initialize the V and VN vectors.
+ do i = 1, NDIM {
+ IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1
+ if (IO_VN(io,i) < 0) {
+ swap (IO_VS(io,i), IO_VE(io,i))
+ IO_VN(io,i) = -IO_VN(io,i)
+ }
+ }
+ call amovi (IO_VS(io,1), IO_V(io,1), NDIM)
+
+ # Determine if full lines are to be accessed, and if a bounding
+ # box (subraster of the image) is defined.
+
+ lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io))
+ bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io))
+
+ # Determine if region mask data is to be used and if there is any
+ # data to be read.
+
+ nodata = (IO_NEVENTS(io) <= 0)
+ rmused = false
+
+ if (pl != NULL)
+ if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM))
+ rmused = true
+ else
+ nodata = true
+
+ # Select the optimal type of i/o to be used for extraction.
+ if (nodata) {
+ IO_IOTYPE(io) = NoDATA_NoAREA
+ useindex = false
+ bbused = false
+
+ } else if (bbused || rmused) {
+ if (useindex)
+ IO_IOTYPE(io) = INDEX_RMorBB
+ else
+ IO_IOTYPE(io) = NoINDEX_RMorBB
+
+ } else {
+ # If we are reading the entire image (no bounding box) and
+ # we are not using a mask, then there is no point in using
+ # indexed i/o.
+
+ IO_IOTYPE(io) = NoINDEX_NoRMorBB
+ useindex = false
+ }
+
+ # Initialize the range list data if it will be used.
+ if (useindex) {
+ # Dummy range specifying full line segment.
+ RLI_LEN(rl) = RL_FIRST
+ RLI_AXLEN(rl) = IO_NCOLS(io)
+
+ rp = rl + ((RL_FIRST - 1) * RL_LENELEM)
+ Memi[rp+RL_XOFF] = IO_VS(io,1)
+ Memi[rp+RL_NOFF] = IO_VN(io,1)
+ Memi[rp+RL_VOFF] = 1
+
+ IO_RLI(io) = RLI_INITIALIZE
+ }
+
+ # Open the mask for random access if i/o is not indexed and
+ # a region mask is used.
+
+ bbmask = IO_BBMASK(io)
+ if (bbmask != NULL)
+ call plr_close (bbmask)
+
+ if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) {
+ bbmask = plr_open (pl, v, 0) # (v is never referenced)
+ call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2),
+ IO_VE(io,1),IO_VE(io,2))
+ call plr_getlut (bbmask,
+ bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock)
+ }
+
+ # Update the QPIO descriptor.
+ IO_LINEIO(io) = btoi(lineio)
+ IO_RMUSED(io) = btoi(rmused)
+ IO_BBUSED(io) = btoi(bbused)
+ IO_BBMASK(io) = bbmask
+
+ IO_EVI(io) = 1
+ IO_BKNO(io) = 0
+ IO_BKLASTEV(io) = 0
+
+ IO_ACTIVE(io) = YES
+ }
+
+ # Initialize event extraction parameters.
+ szs_event = IO_EVENTLEN(io)
+ maskval = 0
+ nev = 0
+
+ ev_xoff = IO_EVXOFF(io)
+ ev_yoff = IO_EVYOFF(io)
+
+ # Extract events using the most efficient type of i/o for the given
+ # selection critera (index, mask, BB, EAF, etc.).
+again_
+ switch (IO_IOTYPE(io)) {
+ case NoDATA_NoAREA:
+ # We know in advance that there are no events to be returned,
+ # either because there is no data, or the area of the region
+ # mask within the bounding box is empty.
+
+ goto exit_
+
+ case NoINDEX_NoRMorBB:
+ # This is the simplest case; no index, region mask, or bounding
+ # box. Read and output all events in sequence.
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Copy out the event pointers.
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1)
+
+ do i = 1, nev {
+ o_ev[i] = ev
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = IO_EVI(io) + nev
+ maskval = 1
+
+ case NoINDEX_RMorBB:
+ # Fully general selection, including any combination of bounding
+ # box, region mask, or EAF, but no index, either because there is
+ # no index for this event list, or the index is for a different Y
+ # attribute than the one being used for extraction.
+
+ bbused = (IO_BBUSED(io) == YES)
+ x1 = IO_VS(io,1); x2 = IO_VE(io,1)
+ y1 = IO_VS(io,2); y2 = IO_VE(io,2)
+
+ # Refill the event bucket?
+ while (IO_EVI(io) > IO_BKLASTEV(io)) {
+ # Get the next bucket.
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Reject buckets that do not contain any events lying
+ # within the specified bounding box, if any.
+
+ if (bbused) {
+ ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_LONG + 1
+ xs = Meml[ev_p+ev_xoff]
+ ys = Meml[ev_p+ev_yoff]
+
+ ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_LONG + 1
+ xe = Meml[ev_p+ev_xoff]
+ ye = Meml[ev_p+ev_yoff]
+
+ if (xs > x2 || xe < x1 || ys > y2 || ye < y1)
+ IO_EVI(io) = IO_BKLASTEV(io) + 1
+ }
+ }
+
+ # Copy out any events which pass the region mask and which share
+ # the same mask value. Note that in this case, to speed mask
+ # value lookup at random mask coordinates, the region mask for
+ # the bounding box is stored as a populated array in the QPIO
+ # descriptor.
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event
+ bbmask = IO_BBMASK(io)
+ mval = 0
+
+ do i = IO_EVI(io), IO_BKLASTEV(io) {
+ # Get event x,y coordinates in whatever coord system.
+ ev = ev + szs_event
+ ev_p = (ev - 1) * SZ_SHORT / SZ_LONG + 1
+
+ x = Meml[ev_p+ev_xoff]
+ y = Meml[ev_p+ev_yoff]
+
+ # Reject events lying outside the bounding box.
+ if (bbused)
+ if (x < x1 || x > x2 || y < y1 || y > y2)
+ next
+
+ # Take a shortcut if no region mask is in effect for this BB.
+ if (bbmask == NULL)
+ goto putevent_
+
+ # Get the mask pixel associated with this event.
+ ii = (x - 1) / bb_xblock
+ jj = (y - 1) / bb_yblock
+ mval = Memi[bb_bufp + jj*bb_xsize + ii]
+ if (mval < 0)
+ mval = plr_getpix (bbmask, x, y)
+
+ # Accumulate points lying in the first nonzero mask range
+ # encountered.
+
+ if (mval != 0) {
+ if (maskval == 0)
+ maskval = mval
+ if (mval == maskval) {
+putevent_ if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ } else
+ break
+ }
+ }
+
+ IO_EVI(io) = i
+
+ case INDEX_NoRMorBB, INDEX_RMorBB:
+ # General extraction for indexed data. Process successive ranges
+ # and range lists until we get at least one event which lies within
+ # the bounding box, within a range, and which passes the event
+ # attribute filter, if one is in use.
+
+ # If the current range list (mask line) has been exhausted, advance
+ # to the next line which contains both ranges and events. A range
+ # list is used to specify the bounding box even if we don't have
+ # a nonempty region mask within the BB.
+
+ if (IO_RLI(io) > RLI_LEN(rl)) {
+ repeat {
+ y = IO_V(io,2)
+ if (IO_RLI(io) == RLI_INITIALIZE)
+ IO_RLI(io) = RL_FIRST
+ else
+ y = y + 1
+
+ if (y > IO_VE(io,2)) {
+ if (nev <= 0) {
+ o_nev = EOF
+ return (EOF)
+ } else
+ goto done_
+ }
+
+ IO_V(io,2) = y
+ evidx = Memi[IO_YOFFVP(io)+y-1]
+
+ if (evidx > 0) {
+ if (IO_RMUSED(io) == YES) {
+ if (IO_LINEIO(io) == YES) {
+ if (!pl_linenotempty (pl,IO_V(io,1)))
+ next
+ } else {
+ v[1] = IO_VE(io,1); v[2] = y
+ if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM))
+ next
+ }
+ call pl_glri (pl, IO_V(io,1), Memi[rl],
+ IO_MDEPTH(io), IO_VN(io,1), PIX_SRC)
+ }
+ IO_RLI(io) = RL_FIRST
+ }
+ } until (IO_RLI(io) <= RLI_LEN(rl))
+
+ IO_EVI(io) = evidx
+ IO_EV1(io) = evidx
+ IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1
+ }
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Compute current range parameters and initialize event pointer.
+ rp = rl + (IO_RLI(io) - 1) * RL_LENELEM
+ x1 = Memi[rp+RL_XOFF]
+ x2 = x1 + Memi[rp+RL_NOFF] - 1
+ maskval = Memi[rp+RL_VOFF]
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ evtop = min (IO_EV2(io), IO_BKLASTEV(io))
+
+ # Extract events from bucket which lie within the current range
+ # of the current line. This is the inner loop of indexed event
+ # extraction, ignoring event attribute filtering.
+
+ do i = IO_EVI(io), evtop {
+ ev_p = (ev - 1) * SZ_SHORT / SZ_LONG + 1
+ x = Meml[ev_p+ev_xoff]
+ if (x >= x1) {
+ if (x > x2) {
+ IO_RLI(io) = IO_RLI(io) + 1
+ break
+ } else if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ }
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = i
+ if (i > IO_EV2(io))
+ IO_RLI(io) = RLI_NEXTLINE
+ }
+done_
+ # Apply the event attribute filter if one is defined; repeat
+ # the whole process if we don't end up with any events.
+
+ if (nev > 0)
+ if (ex != NULL)
+ nev = qpex_evaluate (ex, o_ev, o_ev, nev)
+ if (nev <= 0)
+ goto again_
+exit_
+ o_nev = nev
+ if (o_nev <= 0)
+ o_nev = EOF
+
+ return (o_nev)
+end
+
+
+
+# QPX_GV -- Internal generic code for qpio_getevents. There is one copy
+# of this routine for each event coordinate datatype. The optimization
+# strategy used here assumes that executing qpio_gv is much more expensive
+# than building the call in qpio_getevents. This will normally be the case
+# for a large event list or a complex expression, otherwise the operation
+# is likely to be fast enough that it doesn't matter anyway.
+
+int procedure qpx_gvr (io, o_ev, maskval, maxev, o_nev)
+
+pointer io #I QPIO descriptor
+pointer o_ev[maxev] #O receives the event struct pointers
+int maskval #O receives the mask value of the events
+int maxev #I max events out
+int o_nev #O same as function value (nev_out|EOF)
+
+int x1, x2, y1, y2, xs, xe, ys, ye, x, y
+pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp
+bool useindex, lineio, bbused, rmused, nodata
+int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj
+int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i
+int ev_xoff, ev_yoff
+
+pointer plr_open()
+bool pl_linenotempty(), pl_sectnotempty()
+int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix()
+
+define swap {temp=$1;$1=$2;$2=temp}
+define putevent_ 91
+define again_ 92
+define done_ 93
+define exit_ 94
+
+begin
+ pl = IO_PL(io) # pixel list (region mask) descriptor
+ rl = IO_RL(io) # range list buffer
+ bp = IO_BP(io) # bucket buffer (type short)
+ ex = IO_EX(io) # QPEX (EAF) descriptor
+
+ # The following is executed when the first i/o is performed on a new
+ # region, to select the most efficient type of i/o to be performed,
+ # and initialize the i/o parameters for that case. The type of i/o
+ # to be performed depends upon whether or not an index can be used,
+ # and whether or not there is a region mask (RM) or bounding box (BB).
+ # The presence or absence of an event attribute filter (EAF) is not
+ # separated out as a special case, as it is quick and easy to test
+ # for the presence of an EAF and apply one it if it exists.
+
+ if (IO_ACTIVE(io) == NO) {
+ # Check for an index. We have an index if the event list is
+ # indexed, and the index is defined on the Y-coordinate we will
+ # be using for extraction.
+
+ useindex = (IO_INDEXLEN(io) == IO_NLINES(io) &&
+ IO_EVYOFF(io) == IO_IXYOFF(io) &&
+ IO_NOINDEX(io) == NO)
+
+ # Initialize the V and VN vectors.
+ do i = 1, NDIM {
+ IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1
+ if (IO_VN(io,i) < 0) {
+ swap (IO_VS(io,i), IO_VE(io,i))
+ IO_VN(io,i) = -IO_VN(io,i)
+ }
+ }
+ call amovi (IO_VS(io,1), IO_V(io,1), NDIM)
+
+ # Determine if full lines are to be accessed, and if a bounding
+ # box (subraster of the image) is defined.
+
+ lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io))
+ bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io))
+
+ # Determine if region mask data is to be used and if there is any
+ # data to be read.
+
+ nodata = (IO_NEVENTS(io) <= 0)
+ rmused = false
+
+ if (pl != NULL)
+ if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM))
+ rmused = true
+ else
+ nodata = true
+
+ # Select the optimal type of i/o to be used for extraction.
+ if (nodata) {
+ IO_IOTYPE(io) = NoDATA_NoAREA
+ useindex = false
+ bbused = false
+
+ } else if (bbused || rmused) {
+ if (useindex)
+ IO_IOTYPE(io) = INDEX_RMorBB
+ else
+ IO_IOTYPE(io) = NoINDEX_RMorBB
+
+ } else {
+ # If we are reading the entire image (no bounding box) and
+ # we are not using a mask, then there is no point in using
+ # indexed i/o.
+
+ IO_IOTYPE(io) = NoINDEX_NoRMorBB
+ useindex = false
+ }
+
+ # Initialize the range list data if it will be used.
+ if (useindex) {
+ # Dummy range specifying full line segment.
+ RLI_LEN(rl) = RL_FIRST
+ RLI_AXLEN(rl) = IO_NCOLS(io)
+
+ rp = rl + ((RL_FIRST - 1) * RL_LENELEM)
+ Memi[rp+RL_XOFF] = IO_VS(io,1)
+ Memi[rp+RL_NOFF] = IO_VN(io,1)
+ Memi[rp+RL_VOFF] = 1
+
+ IO_RLI(io) = RLI_INITIALIZE
+ }
+
+ # Open the mask for random access if i/o is not indexed and
+ # a region mask is used.
+
+ bbmask = IO_BBMASK(io)
+ if (bbmask != NULL)
+ call plr_close (bbmask)
+
+ if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) {
+ bbmask = plr_open (pl, v, 0) # (v is never referenced)
+ call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2),
+ IO_VE(io,1),IO_VE(io,2))
+ call plr_getlut (bbmask,
+ bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock)
+ }
+
+ # Update the QPIO descriptor.
+ IO_LINEIO(io) = btoi(lineio)
+ IO_RMUSED(io) = btoi(rmused)
+ IO_BBUSED(io) = btoi(bbused)
+ IO_BBMASK(io) = bbmask
+
+ IO_EVI(io) = 1
+ IO_BKNO(io) = 0
+ IO_BKLASTEV(io) = 0
+
+ IO_ACTIVE(io) = YES
+ }
+
+ # Initialize event extraction parameters.
+ szs_event = IO_EVENTLEN(io)
+ maskval = 0
+ nev = 0
+
+ ev_xoff = IO_EVXOFF(io)
+ ev_yoff = IO_EVYOFF(io)
+
+ # Extract events using the most efficient type of i/o for the given
+ # selection critera (index, mask, BB, EAF, etc.).
+again_
+ switch (IO_IOTYPE(io)) {
+ case NoDATA_NoAREA:
+ # We know in advance that there are no events to be returned,
+ # either because there is no data, or the area of the region
+ # mask within the bounding box is empty.
+
+ goto exit_
+
+ case NoINDEX_NoRMorBB:
+ # This is the simplest case; no index, region mask, or bounding
+ # box. Read and output all events in sequence.
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Copy out the event pointers.
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1)
+
+ do i = 1, nev {
+ o_ev[i] = ev
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = IO_EVI(io) + nev
+ maskval = 1
+
+ case NoINDEX_RMorBB:
+ # Fully general selection, including any combination of bounding
+ # box, region mask, or EAF, but no index, either because there is
+ # no index for this event list, or the index is for a different Y
+ # attribute than the one being used for extraction.
+
+ bbused = (IO_BBUSED(io) == YES)
+ x1 = IO_VS(io,1); x2 = IO_VE(io,1)
+ y1 = IO_VS(io,2); y2 = IO_VE(io,2)
+
+ # Refill the event bucket?
+ while (IO_EVI(io) > IO_BKLASTEV(io)) {
+ # Get the next bucket.
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Reject buckets that do not contain any events lying
+ # within the specified bounding box, if any.
+
+ if (bbused) {
+ ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_REAL + 1
+ xs = Memr[ev_p+ev_xoff] + 0.5
+ ys = Memr[ev_p+ev_yoff] + 0.5
+
+ ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_REAL + 1
+ xe = Memr[ev_p+ev_xoff] + 0.5
+ ye = Memr[ev_p+ev_yoff] + 0.5
+
+ if (xs > x2 || xe < x1 || ys > y2 || ye < y1)
+ IO_EVI(io) = IO_BKLASTEV(io) + 1
+ }
+ }
+
+ # Copy out any events which pass the region mask and which share
+ # the same mask value. Note that in this case, to speed mask
+ # value lookup at random mask coordinates, the region mask for
+ # the bounding box is stored as a populated array in the QPIO
+ # descriptor.
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event
+ bbmask = IO_BBMASK(io)
+ mval = 0
+
+ do i = IO_EVI(io), IO_BKLASTEV(io) {
+ # Get event x,y coordinates in whatever coord system.
+ ev = ev + szs_event
+ ev_p = (ev - 1) * SZ_SHORT / SZ_REAL + 1
+
+ x = Memr[ev_p+ev_xoff] + 0.5
+ y = Memr[ev_p+ev_yoff] + 0.5
+
+ # Reject events lying outside the bounding box.
+ if (bbused)
+ if (x < x1 || x > x2 || y < y1 || y > y2)
+ next
+
+ # Take a shortcut if no region mask is in effect for this BB.
+ if (bbmask == NULL)
+ goto putevent_
+
+ # Get the mask pixel associated with this event.
+ ii = (x - 1) / bb_xblock
+ jj = (y - 1) / bb_yblock
+ mval = Memi[bb_bufp + jj*bb_xsize + ii]
+ if (mval < 0)
+ mval = plr_getpix (bbmask, x, y)
+
+ # Accumulate points lying in the first nonzero mask range
+ # encountered.
+
+ if (mval != 0) {
+ if (maskval == 0)
+ maskval = mval
+ if (mval == maskval) {
+putevent_ if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ } else
+ break
+ }
+ }
+
+ IO_EVI(io) = i
+
+ case INDEX_NoRMorBB, INDEX_RMorBB:
+ # General extraction for indexed data. Process successive ranges
+ # and range lists until we get at least one event which lies within
+ # the bounding box, within a range, and which passes the event
+ # attribute filter, if one is in use.
+
+ # If the current range list (mask line) has been exhausted, advance
+ # to the next line which contains both ranges and events. A range
+ # list is used to specify the bounding box even if we don't have
+ # a nonempty region mask within the BB.
+
+ if (IO_RLI(io) > RLI_LEN(rl)) {
+ repeat {
+ y = IO_V(io,2)
+ if (IO_RLI(io) == RLI_INITIALIZE)
+ IO_RLI(io) = RL_FIRST
+ else
+ y = y + 1
+
+ if (y > IO_VE(io,2)) {
+ if (nev <= 0) {
+ o_nev = EOF
+ return (EOF)
+ } else
+ goto done_
+ }
+
+ IO_V(io,2) = y
+ evidx = Memi[IO_YOFFVP(io)+y-1]
+
+ if (evidx > 0) {
+ if (IO_RMUSED(io) == YES) {
+ if (IO_LINEIO(io) == YES) {
+ if (!pl_linenotempty (pl,IO_V(io,1)))
+ next
+ } else {
+ v[1] = IO_VE(io,1); v[2] = y
+ if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM))
+ next
+ }
+ call pl_glri (pl, IO_V(io,1), Memi[rl],
+ IO_MDEPTH(io), IO_VN(io,1), PIX_SRC)
+ }
+ IO_RLI(io) = RL_FIRST
+ }
+ } until (IO_RLI(io) <= RLI_LEN(rl))
+
+ IO_EVI(io) = evidx
+ IO_EV1(io) = evidx
+ IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1
+ }
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Compute current range parameters and initialize event pointer.
+ rp = rl + (IO_RLI(io) - 1) * RL_LENELEM
+ x1 = Memi[rp+RL_XOFF]
+ x2 = x1 + Memi[rp+RL_NOFF] - 1
+ maskval = Memi[rp+RL_VOFF]
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ evtop = min (IO_EV2(io), IO_BKLASTEV(io))
+
+ # Extract events from bucket which lie within the current range
+ # of the current line. This is the inner loop of indexed event
+ # extraction, ignoring event attribute filtering.
+
+ do i = IO_EVI(io), evtop {
+ ev_p = (ev - 1) * SZ_SHORT / SZ_REAL + 1
+ x = Memr[ev_p+ev_xoff] + 0.5
+ if (x >= x1) {
+ if (x > x2) {
+ IO_RLI(io) = IO_RLI(io) + 1
+ break
+ } else if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ }
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = i
+ if (i > IO_EV2(io))
+ IO_RLI(io) = RLI_NEXTLINE
+ }
+done_
+ # Apply the event attribute filter if one is defined; repeat
+ # the whole process if we don't end up with any events.
+
+ if (nev > 0)
+ if (ex != NULL)
+ nev = qpex_evaluate (ex, o_ev, o_ev, nev)
+ if (nev <= 0)
+ goto again_
+exit_
+ o_nev = nev
+ if (o_nev <= 0)
+ o_nev = EOF
+
+ return (o_nev)
+end
+
+
+
+# QPX_GV -- Internal generic code for qpio_getevents. There is one copy
+# of this routine for each event coordinate datatype. The optimization
+# strategy used here assumes that executing qpio_gv is much more expensive
+# than building the call in qpio_getevents. This will normally be the case
+# for a large event list or a complex expression, otherwise the operation
+# is likely to be fast enough that it doesn't matter anyway.
+
+int procedure qpx_gvd (io, o_ev, maskval, maxev, o_nev)
+
+pointer io #I QPIO descriptor
+pointer o_ev[maxev] #O receives the event struct pointers
+int maskval #O receives the mask value of the events
+int maxev #I max events out
+int o_nev #O same as function value (nev_out|EOF)
+
+int x1, x2, y1, y2, xs, xe, ys, ye, x, y
+pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp
+bool useindex, lineio, bbused, rmused, nodata
+int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj
+int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i
+int ev_xoff, ev_yoff
+
+pointer plr_open()
+bool pl_linenotempty(), pl_sectnotempty()
+int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix()
+
+define swap {temp=$1;$1=$2;$2=temp}
+define putevent_ 91
+define again_ 92
+define done_ 93
+define exit_ 94
+
+begin
+ pl = IO_PL(io) # pixel list (region mask) descriptor
+ rl = IO_RL(io) # range list buffer
+ bp = IO_BP(io) # bucket buffer (type short)
+ ex = IO_EX(io) # QPEX (EAF) descriptor
+
+ # The following is executed when the first i/o is performed on a new
+ # region, to select the most efficient type of i/o to be performed,
+ # and initialize the i/o parameters for that case. The type of i/o
+ # to be performed depends upon whether or not an index can be used,
+ # and whether or not there is a region mask (RM) or bounding box (BB).
+ # The presence or absence of an event attribute filter (EAF) is not
+ # separated out as a special case, as it is quick and easy to test
+ # for the presence of an EAF and apply one it if it exists.
+
+ if (IO_ACTIVE(io) == NO) {
+ # Check for an index. We have an index if the event list is
+ # indexed, and the index is defined on the Y-coordinate we will
+ # be using for extraction.
+
+ useindex = (IO_INDEXLEN(io) == IO_NLINES(io) &&
+ IO_EVYOFF(io) == IO_IXYOFF(io) &&
+ IO_NOINDEX(io) == NO)
+
+ # Initialize the V and VN vectors.
+ do i = 1, NDIM {
+ IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1
+ if (IO_VN(io,i) < 0) {
+ swap (IO_VS(io,i), IO_VE(io,i))
+ IO_VN(io,i) = -IO_VN(io,i)
+ }
+ }
+ call amovi (IO_VS(io,1), IO_V(io,1), NDIM)
+
+ # Determine if full lines are to be accessed, and if a bounding
+ # box (subraster of the image) is defined.
+
+ lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io))
+ bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io))
+
+ # Determine if region mask data is to be used and if there is any
+ # data to be read.
+
+ nodata = (IO_NEVENTS(io) <= 0)
+ rmused = false
+
+ if (pl != NULL)
+ if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM))
+ rmused = true
+ else
+ nodata = true
+
+ # Select the optimal type of i/o to be used for extraction.
+ if (nodata) {
+ IO_IOTYPE(io) = NoDATA_NoAREA
+ useindex = false
+ bbused = false
+
+ } else if (bbused || rmused) {
+ if (useindex)
+ IO_IOTYPE(io) = INDEX_RMorBB
+ else
+ IO_IOTYPE(io) = NoINDEX_RMorBB
+
+ } else {
+ # If we are reading the entire image (no bounding box) and
+ # we are not using a mask, then there is no point in using
+ # indexed i/o.
+
+ IO_IOTYPE(io) = NoINDEX_NoRMorBB
+ useindex = false
+ }
+
+ # Initialize the range list data if it will be used.
+ if (useindex) {
+ # Dummy range specifying full line segment.
+ RLI_LEN(rl) = RL_FIRST
+ RLI_AXLEN(rl) = IO_NCOLS(io)
+
+ rp = rl + ((RL_FIRST - 1) * RL_LENELEM)
+ Memi[rp+RL_XOFF] = IO_VS(io,1)
+ Memi[rp+RL_NOFF] = IO_VN(io,1)
+ Memi[rp+RL_VOFF] = 1
+
+ IO_RLI(io) = RLI_INITIALIZE
+ }
+
+ # Open the mask for random access if i/o is not indexed and
+ # a region mask is used.
+
+ bbmask = IO_BBMASK(io)
+ if (bbmask != NULL)
+ call plr_close (bbmask)
+
+ if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) {
+ bbmask = plr_open (pl, v, 0) # (v is never referenced)
+ call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2),
+ IO_VE(io,1),IO_VE(io,2))
+ call plr_getlut (bbmask,
+ bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock)
+ }
+
+ # Update the QPIO descriptor.
+ IO_LINEIO(io) = btoi(lineio)
+ IO_RMUSED(io) = btoi(rmused)
+ IO_BBUSED(io) = btoi(bbused)
+ IO_BBMASK(io) = bbmask
+
+ IO_EVI(io) = 1
+ IO_BKNO(io) = 0
+ IO_BKLASTEV(io) = 0
+
+ IO_ACTIVE(io) = YES
+ }
+
+ # Initialize event extraction parameters.
+ szs_event = IO_EVENTLEN(io)
+ maskval = 0
+ nev = 0
+
+ ev_xoff = IO_EVXOFF(io)
+ ev_yoff = IO_EVYOFF(io)
+
+ # Extract events using the most efficient type of i/o for the given
+ # selection critera (index, mask, BB, EAF, etc.).
+again_
+ switch (IO_IOTYPE(io)) {
+ case NoDATA_NoAREA:
+ # We know in advance that there are no events to be returned,
+ # either because there is no data, or the area of the region
+ # mask within the bounding box is empty.
+
+ goto exit_
+
+ case NoINDEX_NoRMorBB:
+ # This is the simplest case; no index, region mask, or bounding
+ # box. Read and output all events in sequence.
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Copy out the event pointers.
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1)
+
+ do i = 1, nev {
+ o_ev[i] = ev
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = IO_EVI(io) + nev
+ maskval = 1
+
+ case NoINDEX_RMorBB:
+ # Fully general selection, including any combination of bounding
+ # box, region mask, or EAF, but no index, either because there is
+ # no index for this event list, or the index is for a different Y
+ # attribute than the one being used for extraction.
+
+ bbused = (IO_BBUSED(io) == YES)
+ x1 = IO_VS(io,1); x2 = IO_VE(io,1)
+ y1 = IO_VS(io,2); y2 = IO_VE(io,2)
+
+ # Refill the event bucket?
+ while (IO_EVI(io) > IO_BKLASTEV(io)) {
+ # Get the next bucket.
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Reject buckets that do not contain any events lying
+ # within the specified bounding box, if any.
+
+ if (bbused) {
+ ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_DOUBLE + 1
+ xs = Memd[ev_p+ev_xoff] + 0.5
+ ys = Memd[ev_p+ev_yoff] + 0.5
+
+ ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_DOUBLE + 1
+ xe = Memd[ev_p+ev_xoff] + 0.5
+ ye = Memd[ev_p+ev_yoff] + 0.5
+
+ if (xs > x2 || xe < x1 || ys > y2 || ye < y1)
+ IO_EVI(io) = IO_BKLASTEV(io) + 1
+ }
+ }
+
+ # Copy out any events which pass the region mask and which share
+ # the same mask value. Note that in this case, to speed mask
+ # value lookup at random mask coordinates, the region mask for
+ # the bounding box is stored as a populated array in the QPIO
+ # descriptor.
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event
+ bbmask = IO_BBMASK(io)
+ mval = 0
+
+ do i = IO_EVI(io), IO_BKLASTEV(io) {
+ # Get event x,y coordinates in whatever coord system.
+ ev = ev + szs_event
+ ev_p = (ev - 1) * SZ_SHORT / SZ_DOUBLE + 1
+
+ x = Memd[ev_p+ev_xoff] + 0.5
+ y = Memd[ev_p+ev_yoff] + 0.5
+
+ # Reject events lying outside the bounding box.
+ if (bbused)
+ if (x < x1 || x > x2 || y < y1 || y > y2)
+ next
+
+ # Take a shortcut if no region mask is in effect for this BB.
+ if (bbmask == NULL)
+ goto putevent_
+
+ # Get the mask pixel associated with this event.
+ ii = (x - 1) / bb_xblock
+ jj = (y - 1) / bb_yblock
+ mval = Memi[bb_bufp + jj*bb_xsize + ii]
+ if (mval < 0)
+ mval = plr_getpix (bbmask, x, y)
+
+ # Accumulate points lying in the first nonzero mask range
+ # encountered.
+
+ if (mval != 0) {
+ if (maskval == 0)
+ maskval = mval
+ if (mval == maskval) {
+putevent_ if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ } else
+ break
+ }
+ }
+
+ IO_EVI(io) = i
+
+ case INDEX_NoRMorBB, INDEX_RMorBB:
+ # General extraction for indexed data. Process successive ranges
+ # and range lists until we get at least one event which lies within
+ # the bounding box, within a range, and which passes the event
+ # attribute filter, if one is in use.
+
+ # If the current range list (mask line) has been exhausted, advance
+ # to the next line which contains both ranges and events. A range
+ # list is used to specify the bounding box even if we don't have
+ # a nonempty region mask within the BB.
+
+ if (IO_RLI(io) > RLI_LEN(rl)) {
+ repeat {
+ y = IO_V(io,2)
+ if (IO_RLI(io) == RLI_INITIALIZE)
+ IO_RLI(io) = RL_FIRST
+ else
+ y = y + 1
+
+ if (y > IO_VE(io,2)) {
+ if (nev <= 0) {
+ o_nev = EOF
+ return (EOF)
+ } else
+ goto done_
+ }
+
+ IO_V(io,2) = y
+ evidx = Memi[IO_YOFFVP(io)+y-1]
+
+ if (evidx > 0) {
+ if (IO_RMUSED(io) == YES) {
+ if (IO_LINEIO(io) == YES) {
+ if (!pl_linenotempty (pl,IO_V(io,1)))
+ next
+ } else {
+ v[1] = IO_VE(io,1); v[2] = y
+ if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM))
+ next
+ }
+ call pl_glri (pl, IO_V(io,1), Memi[rl],
+ IO_MDEPTH(io), IO_VN(io,1), PIX_SRC)
+ }
+ IO_RLI(io) = RL_FIRST
+ }
+ } until (IO_RLI(io) <= RLI_LEN(rl))
+
+ IO_EVI(io) = evidx
+ IO_EV1(io) = evidx
+ IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1
+ }
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Compute current range parameters and initialize event pointer.
+ rp = rl + (IO_RLI(io) - 1) * RL_LENELEM
+ x1 = Memi[rp+RL_XOFF]
+ x2 = x1 + Memi[rp+RL_NOFF] - 1
+ maskval = Memi[rp+RL_VOFF]
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ evtop = min (IO_EV2(io), IO_BKLASTEV(io))
+
+ # Extract events from bucket which lie within the current range
+ # of the current line. This is the inner loop of indexed event
+ # extraction, ignoring event attribute filtering.
+
+ do i = IO_EVI(io), evtop {
+ ev_p = (ev - 1) * SZ_SHORT / SZ_DOUBLE + 1
+ x = Memd[ev_p+ev_xoff] + 0.5
+ if (x >= x1) {
+ if (x > x2) {
+ IO_RLI(io) = IO_RLI(io) + 1
+ break
+ } else if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ }
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = i
+ if (i > IO_EV2(io))
+ IO_RLI(io) = RLI_NEXTLINE
+ }
+done_
+ # Apply the event attribute filter if one is defined; repeat
+ # the whole process if we don't end up with any events.
+
+ if (nev > 0)
+ if (ex != NULL)
+ nev = qpex_evaluate (ex, o_ev, o_ev, nev)
+ if (nev <= 0)
+ goto again_
+exit_
+ o_nev = nev
+ if (o_nev <= 0)
+ o_nev = EOF
+
+ return (o_nev)
+end
+
+
diff --git a/sys/qpoe/gen/qpiorpixi.x b/sys/qpoe/gen/qpiorpixi.x
new file mode 100644
index 00000000..c64f0a8a
--- /dev/null
+++ b/sys/qpoe/gen/qpiorpixi.x
@@ -0,0 +1,150 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <syserr.h>
+include "../qpio.h"
+
+# QPIO_READPIX -- Sample the event list within the indicated rectangular
+# region, using the given blocking factor, to produce a rectangular array
+# of "pixels", where each pixel is a count of the number of events mapping
+# to that location which pass the event attribute filter and region mask.
+#
+# NOTE -- It is left up to the caller to zero the output buffer before
+# we are called. (We merely increment the counts of the affected pixels).
+
+int procedure qpio_readpixi (io, obuf, vs, ve, ndim, xblock, yblock)
+
+pointer io #I QPIO descriptor
+int obuf[ARB] #O output pixel buffer
+int vs[ndim], ve[ndim] #I vectors defining region to be extracted
+int ndim #I should be 2 for QPOE
+real xblock, yblock #I blocking factors
+
+double x, y
+pointer sp, evl, ev_p
+int evtype, maxpix, maskval, xoff, yoff, xw, yw, nev, totev, pix, i, j
+errchk qpio_getevents, qpio_setrange, syserr
+int qpio_getevents()
+
+begin
+ # Verify arguments.
+ if (xblock <= 0 || xblock > (ve[1] - vs[1] + 1))
+ call syserr (SYS_QPBLOCKOOR)
+ if (yblock <= 0 || yblock > (ve[2] - vs[2] + 1))
+ call syserr (SYS_QPBLOCKOOR)
+
+ # Compute the size of the output matrix in integer pixels. This
+ # truncates the last partially filled pixel in each axis.
+
+ xw = int ((ve[1] - vs[1] + 1) / xblock + (EPSILOND * 1000))
+ yw = int ((ve[2] - vs[2] + 1) / yblock + (EPSILOND * 1000))
+ if (xw <= 0 || yw <= 0)
+ return (0)
+
+ call smark (sp)
+ call salloc (evl, SZ_EVLIST, TY_POINTER)
+
+ xoff = IO_EVXOFF(io)
+ yoff = IO_EVYOFF(io)
+ maxpix = xw * yw
+ totev = 0
+
+ evtype = IO_EVXTYPE(io)
+ if (IO_EVXTYPE(io) != IO_EVYTYPE(io))
+ call syserr (SYS_QPINVEVT)
+
+ # Define the region from which we wish to read events.
+ call qpio_setrange (io, vs, ve, ndim)
+
+ # Read the events.
+ while (qpio_getevents (io, Memi[evl], maskval, SZ_EVLIST, nev) > 0) {
+ switch (evtype) {
+
+ case TY_SHORT:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_SHORT + 1
+
+ x = Mems[ev_p+xoff]
+ y = Mems[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ case TY_INT:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_INT + 1
+
+ x = Memi[ev_p+xoff]
+ y = Memi[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ case TY_LONG:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_LONG + 1
+
+ x = Meml[ev_p+xoff]
+ y = Meml[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ case TY_REAL:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_REAL + 1
+
+ x = Memr[ev_p+xoff]
+ y = Memr[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ case TY_DOUBLE:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_DOUBLE + 1
+
+ x = Memd[ev_p+xoff]
+ y = Memd[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ }
+
+ totev = totev + nev
+ }
+
+ call sfree (sp)
+ return (totev)
+end
diff --git a/sys/qpoe/gen/qpiorpixs.x b/sys/qpoe/gen/qpiorpixs.x
new file mode 100644
index 00000000..d97c7c42
--- /dev/null
+++ b/sys/qpoe/gen/qpiorpixs.x
@@ -0,0 +1,150 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <syserr.h>
+include "../qpio.h"
+
+# QPIO_READPIX -- Sample the event list within the indicated rectangular
+# region, using the given blocking factor, to produce a rectangular array
+# of "pixels", where each pixel is a count of the number of events mapping
+# to that location which pass the event attribute filter and region mask.
+#
+# NOTE -- It is left up to the caller to zero the output buffer before
+# we are called. (We merely increment the counts of the affected pixels).
+
+int procedure qpio_readpixs (io, obuf, vs, ve, ndim, xblock, yblock)
+
+pointer io #I QPIO descriptor
+short obuf[ARB] #O output pixel buffer
+int vs[ndim], ve[ndim] #I vectors defining region to be extracted
+int ndim #I should be 2 for QPOE
+real xblock, yblock #I blocking factors
+
+double x, y
+pointer sp, evl, ev_p
+int evtype, maxpix, maskval, xoff, yoff, xw, yw, nev, totev, pix, i, j
+errchk qpio_getevents, qpio_setrange, syserr
+int qpio_getevents()
+
+begin
+ # Verify arguments.
+ if (xblock <= 0 || xblock > (ve[1] - vs[1] + 1))
+ call syserr (SYS_QPBLOCKOOR)
+ if (yblock <= 0 || yblock > (ve[2] - vs[2] + 1))
+ call syserr (SYS_QPBLOCKOOR)
+
+ # Compute the size of the output matrix in integer pixels. This
+ # truncates the last partially filled pixel in each axis.
+
+ xw = int ((ve[1] - vs[1] + 1) / xblock + (EPSILOND * 1000))
+ yw = int ((ve[2] - vs[2] + 1) / yblock + (EPSILOND * 1000))
+ if (xw <= 0 || yw <= 0)
+ return (0)
+
+ call smark (sp)
+ call salloc (evl, SZ_EVLIST, TY_POINTER)
+
+ xoff = IO_EVXOFF(io)
+ yoff = IO_EVYOFF(io)
+ maxpix = xw * yw
+ totev = 0
+
+ evtype = IO_EVXTYPE(io)
+ if (IO_EVXTYPE(io) != IO_EVYTYPE(io))
+ call syserr (SYS_QPINVEVT)
+
+ # Define the region from which we wish to read events.
+ call qpio_setrange (io, vs, ve, ndim)
+
+ # Read the events.
+ while (qpio_getevents (io, Memi[evl], maskval, SZ_EVLIST, nev) > 0) {
+ switch (evtype) {
+
+ case TY_SHORT:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_SHORT + 1
+
+ x = Mems[ev_p+xoff]
+ y = Mems[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ case TY_INT:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_INT + 1
+
+ x = Memi[ev_p+xoff]
+ y = Memi[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ case TY_LONG:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_LONG + 1
+
+ x = Meml[ev_p+xoff]
+ y = Meml[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ case TY_REAL:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_REAL + 1
+
+ x = Memr[ev_p+xoff]
+ y = Memr[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ case TY_DOUBLE:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_DOUBLE + 1
+
+ x = Memd[ev_p+xoff]
+ y = Memd[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+
+ }
+
+ totev = totev + nev
+ }
+
+ call sfree (sp)
+ return (totev)
+end
diff --git a/sys/qpoe/gen/qpputc.x b/sys/qpoe/gen/qpputc.x
new file mode 100644
index 00000000..4415a177
--- /dev/null
+++ b/sys/qpoe/gen/qpputc.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_PUT -- Set the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# set by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+procedure qp_putc (qp, param, value)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char value #I scalar parameter value
+
+pointer pp
+bool indef
+int dtype
+int qp_putparam()
+errchk qp_putparam, syserrs
+
+begin
+ # Lookup the parameter and get a pointer to the value buffer.
+ dtype = qp_putparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n")
+ call pargstr (param)
+ call pargi (TY_CHAR)
+ call pargi (dtype)
+ call pargc (value)
+ }
+
+ indef = IS_INDEF(value)
+
+ # Set the parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ Memc[pp] = value
+ case TY_SHORT:
+ if (indef)
+ Mems[pp] = INDEFS
+ else
+ Mems[pp] = value
+ case TY_INT:
+ if (indef)
+ Memi[pp] = INDEFI
+ else
+ Memi[pp] = value
+ case TY_LONG:
+ if (indef)
+ Meml[pp] = INDEFL
+ else
+ Meml[pp] = value
+ case TY_REAL:
+ if (indef)
+ Memr[pp] = INDEFR
+ else
+ Memr[pp] = value
+ case TY_DOUBLE:
+ if (indef)
+ Memd[pp] = INDEFD
+ else
+ Memd[pp] = value
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ # Update the parameter in the datafile.
+ call qp_flushpar (qp)
+end
diff --git a/sys/qpoe/gen/qpputd.x b/sys/qpoe/gen/qpputd.x
new file mode 100644
index 00000000..2c9883e0
--- /dev/null
+++ b/sys/qpoe/gen/qpputd.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_PUT -- Set the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# set by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+procedure qp_putd (qp, param, value)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+double value #I scalar parameter value
+
+pointer pp
+bool indef
+int dtype
+int qp_putparam()
+errchk qp_putparam, syserrs
+
+begin
+ # Lookup the parameter and get a pointer to the value buffer.
+ dtype = qp_putparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n")
+ call pargstr (param)
+ call pargi (TY_DOUBLE)
+ call pargi (dtype)
+ call pargd (value)
+ }
+
+ indef = IS_INDEFD(value)
+
+ # Set the parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ Memc[pp] = value
+ case TY_SHORT:
+ if (indef)
+ Mems[pp] = INDEFS
+ else
+ Mems[pp] = value
+ case TY_INT:
+ if (indef)
+ Memi[pp] = INDEFI
+ else
+ Memi[pp] = value
+ case TY_LONG:
+ if (indef)
+ Meml[pp] = INDEFL
+ else
+ Meml[pp] = value
+ case TY_REAL:
+ if (indef)
+ Memr[pp] = INDEFR
+ else
+ Memr[pp] = value
+ case TY_DOUBLE:
+ if (indef)
+ Memd[pp] = INDEFD
+ else
+ Memd[pp] = value
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ # Update the parameter in the datafile.
+ call qp_flushpar (qp)
+end
diff --git a/sys/qpoe/gen/qpputi.x b/sys/qpoe/gen/qpputi.x
new file mode 100644
index 00000000..528e6bc7
--- /dev/null
+++ b/sys/qpoe/gen/qpputi.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_PUT -- Set the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# set by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+procedure qp_puti (qp, param, value)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+int value #I scalar parameter value
+
+pointer pp
+bool indef
+int dtype
+int qp_putparam()
+errchk qp_putparam, syserrs
+
+begin
+ # Lookup the parameter and get a pointer to the value buffer.
+ dtype = qp_putparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n")
+ call pargstr (param)
+ call pargi (TY_INT)
+ call pargi (dtype)
+ call pargi (value)
+ }
+
+ indef = IS_INDEFI(value)
+
+ # Set the parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ Memc[pp] = value
+ case TY_SHORT:
+ if (indef)
+ Mems[pp] = INDEFS
+ else
+ Mems[pp] = value
+ case TY_INT:
+ if (indef)
+ Memi[pp] = INDEFI
+ else
+ Memi[pp] = value
+ case TY_LONG:
+ if (indef)
+ Meml[pp] = INDEFL
+ else
+ Meml[pp] = value
+ case TY_REAL:
+ if (indef)
+ Memr[pp] = INDEFR
+ else
+ Memr[pp] = value
+ case TY_DOUBLE:
+ if (indef)
+ Memd[pp] = INDEFD
+ else
+ Memd[pp] = value
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ # Update the parameter in the datafile.
+ call qp_flushpar (qp)
+end
diff --git a/sys/qpoe/gen/qpputl.x b/sys/qpoe/gen/qpputl.x
new file mode 100644
index 00000000..50e6605a
--- /dev/null
+++ b/sys/qpoe/gen/qpputl.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_PUT -- Set the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# set by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+procedure qp_putl (qp, param, value)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+long value #I scalar parameter value
+
+pointer pp
+bool indef
+int dtype
+int qp_putparam()
+errchk qp_putparam, syserrs
+
+begin
+ # Lookup the parameter and get a pointer to the value buffer.
+ dtype = qp_putparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n")
+ call pargstr (param)
+ call pargi (TY_LONG)
+ call pargi (dtype)
+ call pargl (value)
+ }
+
+ indef = IS_INDEFL(value)
+
+ # Set the parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ Memc[pp] = value
+ case TY_SHORT:
+ if (indef)
+ Mems[pp] = INDEFS
+ else
+ Mems[pp] = value
+ case TY_INT:
+ if (indef)
+ Memi[pp] = INDEFI
+ else
+ Memi[pp] = value
+ case TY_LONG:
+ if (indef)
+ Meml[pp] = INDEFL
+ else
+ Meml[pp] = value
+ case TY_REAL:
+ if (indef)
+ Memr[pp] = INDEFR
+ else
+ Memr[pp] = value
+ case TY_DOUBLE:
+ if (indef)
+ Memd[pp] = INDEFD
+ else
+ Memd[pp] = value
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ # Update the parameter in the datafile.
+ call qp_flushpar (qp)
+end
diff --git a/sys/qpoe/gen/qpputr.x b/sys/qpoe/gen/qpputr.x
new file mode 100644
index 00000000..10af764b
--- /dev/null
+++ b/sys/qpoe/gen/qpputr.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_PUT -- Set the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# set by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+procedure qp_putr (qp, param, value)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+real value #I scalar parameter value
+
+pointer pp
+bool indef
+int dtype
+int qp_putparam()
+errchk qp_putparam, syserrs
+
+begin
+ # Lookup the parameter and get a pointer to the value buffer.
+ dtype = qp_putparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n")
+ call pargstr (param)
+ call pargi (TY_REAL)
+ call pargi (dtype)
+ call pargr (value)
+ }
+
+ indef = IS_INDEFR(value)
+
+ # Set the parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ Memc[pp] = value
+ case TY_SHORT:
+ if (indef)
+ Mems[pp] = INDEFS
+ else
+ Mems[pp] = value
+ case TY_INT:
+ if (indef)
+ Memi[pp] = INDEFI
+ else
+ Memi[pp] = value
+ case TY_LONG:
+ if (indef)
+ Meml[pp] = INDEFL
+ else
+ Meml[pp] = value
+ case TY_REAL:
+ if (indef)
+ Memr[pp] = INDEFR
+ else
+ Memr[pp] = value
+ case TY_DOUBLE:
+ if (indef)
+ Memd[pp] = INDEFD
+ else
+ Memd[pp] = value
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ # Update the parameter in the datafile.
+ call qp_flushpar (qp)
+end
diff --git a/sys/qpoe/gen/qpputs.x b/sys/qpoe/gen/qpputs.x
new file mode 100644
index 00000000..ec58607a
--- /dev/null
+++ b/sys/qpoe/gen/qpputs.x
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_PUT -- Set the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# set by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+procedure qp_puts (qp, param, value)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+short value #I scalar parameter value
+
+pointer pp
+bool indef
+int dtype
+int qp_putparam()
+errchk qp_putparam, syserrs
+
+begin
+ # Lookup the parameter and get a pointer to the value buffer.
+ dtype = qp_putparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n")
+ call pargstr (param)
+ call pargi (TY_SHORT)
+ call pargi (dtype)
+ call pargs (value)
+ }
+
+ indef = IS_INDEFS(value)
+
+ # Set the parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ Memc[pp] = value
+ case TY_SHORT:
+ if (indef)
+ Mems[pp] = INDEFS
+ else
+ Mems[pp] = value
+ case TY_INT:
+ if (indef)
+ Memi[pp] = INDEFI
+ else
+ Memi[pp] = value
+ case TY_LONG:
+ if (indef)
+ Meml[pp] = INDEFL
+ else
+ Meml[pp] = value
+ case TY_REAL:
+ if (indef)
+ Memr[pp] = INDEFR
+ else
+ Memr[pp] = value
+ case TY_DOUBLE:
+ if (indef)
+ Memd[pp] = INDEFD
+ else
+ Memd[pp] = value
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ # Update the parameter in the datafile.
+ call qp_flushpar (qp)
+end
diff --git a/sys/qpoe/gen/qprlmerged.x b/sys/qpoe/gen/qprlmerged.x
new file mode 100644
index 00000000..d08f4e5f
--- /dev/null
+++ b/sys/qpoe/gen/qprlmerged.x
@@ -0,0 +1,134 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QP_RLMERGE -- Merge (AND) two range lists. Only ranges which are
+# common to both range lists are output. The number of ranges in the
+# output range list is returned as the function value.
+
+int procedure qp_rlmerged (os,oe,olen, xs,xe,nx, ys,ye,ny)
+
+pointer os, oe #U output range list
+int olen #U allocated length of OS, OE arrays
+
+double xs[ARB], xe[ARB] #I range list to be merged with
+int nx #I number of ranges in X list
+double ys[ARB], ye[ARB] #I range list to be merged with X
+int ny #I number of ranges in Y list
+
+double o1, o2
+int nx_out, xi, yi, i
+double qp_minvald(), qp_maxvald()
+bool qp_lessthand()
+errchk realloc
+
+begin
+ nx_out = 0
+ if (nx <= 0 || ny <= 0)
+ return (0)
+
+ xi = 1
+ yi = 1
+
+ do i = 1, ARB {
+ # Find a pair of ranges which intersect.
+ repeat {
+ if (qp_lessthand (xe[xi], ys[yi])) {
+ if (xi >= nx)
+ return (nx_out)
+ else
+ xi = xi + 1
+ } else if (qp_lessthand (ye[yi], xs[xi])) {
+ if (yi >= ny)
+ return (nx_out)
+ else
+ yi = yi + 1
+ } else
+ break
+ }
+
+ # Compute the intersection.
+ o1 = qp_maxvald (xs[xi], ys[yi])
+ o2 = qp_minvald (xe[xi], ye[yi])
+
+ # Output the range.
+ if (nx_out + 1 > olen) {
+ olen = max (DEF_XLEN, olen * 2)
+ call realloc (os, olen, TY_DOUBLE)
+ call realloc (oe, olen, TY_DOUBLE)
+ }
+
+ Memd[os+nx_out] = o1
+ Memd[oe+nx_out] = o2
+ nx_out = nx_out + 1
+
+ # Advance to the next range.
+ if (xi < nx && qp_lessthand (xe[xi], ye[yi]))
+ xi = xi + 1
+ else if (yi < ny)
+ yi = yi + 1
+ else
+ break
+ }
+
+ return (nx_out)
+end
+
+
+# QP_MINVAL -- Return the lesser of two values, where either value can
+# be an open range.
+
+double procedure qp_minvald (x, y)
+
+double x #I first value
+double y #I second value
+
+bool qp_lessthand()
+
+begin
+ if (qp_lessthand (x, y))
+ return (x)
+ else
+ return (y)
+end
+
+
+# QP_MAXVAL -- Return the greater of two values, where either value can
+# be an open range.
+
+double procedure qp_maxvald (x, y)
+
+double x #I first value
+double y #I second value
+
+bool qp_lessthand()
+
+begin
+ if (qp_lessthand (x, y))
+ return (y)
+ else
+ return (x)
+end
+
+
+# QP_LESSTHAN -- Test if X is less than Y, where X and Y can be open
+# range values.
+
+bool procedure qp_lessthand (x, y)
+
+double x #I first value
+double y #I second value
+
+begin
+ if (IS_LEFTD(x))
+ return (!IS_LEFTD(y))
+ else if (IS_RIGHTD(x))
+ return (false)
+ else if (IS_LEFTD(y))
+ return (false)
+ else if (IS_RIGHTD(y))
+ return (true)
+ else
+ return (x < y)
+end
diff --git a/sys/qpoe/gen/qprlmergei.x b/sys/qpoe/gen/qprlmergei.x
new file mode 100644
index 00000000..f8476178
--- /dev/null
+++ b/sys/qpoe/gen/qprlmergei.x
@@ -0,0 +1,134 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QP_RLMERGE -- Merge (AND) two range lists. Only ranges which are
+# common to both range lists are output. The number of ranges in the
+# output range list is returned as the function value.
+
+int procedure qp_rlmergei (os,oe,olen, xs,xe,nx, ys,ye,ny)
+
+pointer os, oe #U output range list
+int olen #U allocated length of OS, OE arrays
+
+int xs[ARB], xe[ARB] #I range list to be merged with
+int nx #I number of ranges in X list
+int ys[ARB], ye[ARB] #I range list to be merged with X
+int ny #I number of ranges in Y list
+
+int o1, o2
+int nx_out, xi, yi, i
+int qp_minvali(), qp_maxvali()
+bool qp_lessthani()
+errchk realloc
+
+begin
+ nx_out = 0
+ if (nx <= 0 || ny <= 0)
+ return (0)
+
+ xi = 1
+ yi = 1
+
+ do i = 1, ARB {
+ # Find a pair of ranges which intersect.
+ repeat {
+ if (qp_lessthani (xe[xi], ys[yi])) {
+ if (xi >= nx)
+ return (nx_out)
+ else
+ xi = xi + 1
+ } else if (qp_lessthani (ye[yi], xs[xi])) {
+ if (yi >= ny)
+ return (nx_out)
+ else
+ yi = yi + 1
+ } else
+ break
+ }
+
+ # Compute the intersection.
+ o1 = qp_maxvali (xs[xi], ys[yi])
+ o2 = qp_minvali (xe[xi], ye[yi])
+
+ # Output the range.
+ if (nx_out + 1 > olen) {
+ olen = max (DEF_XLEN, olen * 2)
+ call realloc (os, olen, TY_INT)
+ call realloc (oe, olen, TY_INT)
+ }
+
+ Memi[os+nx_out] = o1
+ Memi[oe+nx_out] = o2
+ nx_out = nx_out + 1
+
+ # Advance to the next range.
+ if (xi < nx && qp_lessthani (xe[xi], ye[yi]))
+ xi = xi + 1
+ else if (yi < ny)
+ yi = yi + 1
+ else
+ break
+ }
+
+ return (nx_out)
+end
+
+
+# QP_MINVAL -- Return the lesser of two values, where either value can
+# be an open range.
+
+int procedure qp_minvali (x, y)
+
+int x #I first value
+int y #I second value
+
+bool qp_lessthani()
+
+begin
+ if (qp_lessthani (x, y))
+ return (x)
+ else
+ return (y)
+end
+
+
+# QP_MAXVAL -- Return the greater of two values, where either value can
+# be an open range.
+
+int procedure qp_maxvali (x, y)
+
+int x #I first value
+int y #I second value
+
+bool qp_lessthani()
+
+begin
+ if (qp_lessthani (x, y))
+ return (y)
+ else
+ return (x)
+end
+
+
+# QP_LESSTHAN -- Test if X is less than Y, where X and Y can be open
+# range values.
+
+bool procedure qp_lessthani (x, y)
+
+int x #I first value
+int y #I second value
+
+begin
+ if (IS_LEFTI(x))
+ return (!IS_LEFTI(y))
+ else if (IS_RIGHTI(x))
+ return (false)
+ else if (IS_LEFTI(y))
+ return (false)
+ else if (IS_RIGHTI(y))
+ return (true)
+ else
+ return (x < y)
+end
diff --git a/sys/qpoe/gen/qprlmerger.x b/sys/qpoe/gen/qprlmerger.x
new file mode 100644
index 00000000..a776a5db
--- /dev/null
+++ b/sys/qpoe/gen/qprlmerger.x
@@ -0,0 +1,134 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QP_RLMERGE -- Merge (AND) two range lists. Only ranges which are
+# common to both range lists are output. The number of ranges in the
+# output range list is returned as the function value.
+
+int procedure qp_rlmerger (os,oe,olen, xs,xe,nx, ys,ye,ny)
+
+pointer os, oe #U output range list
+int olen #U allocated length of OS, OE arrays
+
+real xs[ARB], xe[ARB] #I range list to be merged with
+int nx #I number of ranges in X list
+real ys[ARB], ye[ARB] #I range list to be merged with X
+int ny #I number of ranges in Y list
+
+real o1, o2
+int nx_out, xi, yi, i
+real qp_minvalr(), qp_maxvalr()
+bool qp_lessthanr()
+errchk realloc
+
+begin
+ nx_out = 0
+ if (nx <= 0 || ny <= 0)
+ return (0)
+
+ xi = 1
+ yi = 1
+
+ do i = 1, ARB {
+ # Find a pair of ranges which intersect.
+ repeat {
+ if (qp_lessthanr (xe[xi], ys[yi])) {
+ if (xi >= nx)
+ return (nx_out)
+ else
+ xi = xi + 1
+ } else if (qp_lessthanr (ye[yi], xs[xi])) {
+ if (yi >= ny)
+ return (nx_out)
+ else
+ yi = yi + 1
+ } else
+ break
+ }
+
+ # Compute the intersection.
+ o1 = qp_maxvalr (xs[xi], ys[yi])
+ o2 = qp_minvalr (xe[xi], ye[yi])
+
+ # Output the range.
+ if (nx_out + 1 > olen) {
+ olen = max (DEF_XLEN, olen * 2)
+ call realloc (os, olen, TY_REAL)
+ call realloc (oe, olen, TY_REAL)
+ }
+
+ Memr[os+nx_out] = o1
+ Memr[oe+nx_out] = o2
+ nx_out = nx_out + 1
+
+ # Advance to the next range.
+ if (xi < nx && qp_lessthanr (xe[xi], ye[yi]))
+ xi = xi + 1
+ else if (yi < ny)
+ yi = yi + 1
+ else
+ break
+ }
+
+ return (nx_out)
+end
+
+
+# QP_MINVAL -- Return the lesser of two values, where either value can
+# be an open range.
+
+real procedure qp_minvalr (x, y)
+
+real x #I first value
+real y #I second value
+
+bool qp_lessthanr()
+
+begin
+ if (qp_lessthanr (x, y))
+ return (x)
+ else
+ return (y)
+end
+
+
+# QP_MAXVAL -- Return the greater of two values, where either value can
+# be an open range.
+
+real procedure qp_maxvalr (x, y)
+
+real x #I first value
+real y #I second value
+
+bool qp_lessthanr()
+
+begin
+ if (qp_lessthanr (x, y))
+ return (y)
+ else
+ return (x)
+end
+
+
+# QP_LESSTHAN -- Test if X is less than Y, where X and Y can be open
+# range values.
+
+bool procedure qp_lessthanr (x, y)
+
+real x #I first value
+real y #I second value
+
+begin
+ if (IS_LEFTR(x))
+ return (!IS_LEFTR(y))
+ else if (IS_RIGHTR(x))
+ return (false)
+ else if (IS_LEFTR(y))
+ return (false)
+ else if (IS_RIGHTR(y))
+ return (true)
+ else
+ return (x < y)
+end
diff --git a/sys/qpoe/mkpkg b/sys/qpoe/mkpkg
new file mode 100644
index 00000000..b7553b0f
--- /dev/null
+++ b/sys/qpoe/mkpkg
@@ -0,0 +1,133 @@
+# Make the QPOE (position ordered event file) library.
+
+$checkout libex.a lib$
+$update libex.a
+$checkin libex.a lib$
+$exit
+
+zzdebug:
+zzdebug.e:
+ $checkout libex.a lib$
+ $update libex.a
+ $checkin libex.a lib$
+
+ $omake zzdebug.x <error.h> <ctype.h> <qpset.h> <qpexset.h> "qpoe.h"
+ $link -z zzdebug.o
+ ;
+
+generic:
+ $set GFLAGS = "-k -t csilrd -p gen/"
+ $ifolder (gen/qpgeti.x, qpget.gx) $generic $(GFLAGS) qpget.gx $endif
+ $ifolder (gen/qpputi.x, qpput.gx) $generic $(GFLAGS) qpput.gx $endif
+
+ $set GFLAGS = "-k -t bcsilrdx -p gen/"
+ $ifolder (gen/qpaddb.x, qpadd.gx) $generic $(GFLAGS) qpadd.gx $endif
+
+ $set GFLAGS = "-k -t ird -p gen/"
+ $ifolder (gen/qpexattrli.x, qpexattrl.gx)
+ $generic $(GFLAGS) qpexattrl.gx $endif
+ $ifolder (gen/qpexcodei.x, qpexcode.gx)
+ $generic $(GFLAGS) qpexcode.gx $endif
+ $ifolder (gen/qpexparsei.x, qpexparse.gx)
+ $generic $(GFLAGS) qpexparse.gx $endif
+ $ifolder (gen/qpexsubi.x, qpexsub.gx)
+ $generic $(GFLAGS) qpexsub.gx $endif
+
+ $ifolder (gen/qprlmergei.x, qprlmerge.gx)
+ $generic $(GFLAGS) qprlmerge.gx $endif
+
+ $set GFLAGS = "-k -t si -p gen/"
+ $ifolder (gen/qpiorpixi.x, qpiorpix.gx)
+ $generic $(GFLAGS) qpiorpix.gx $endif
+ $ifolder (gen/qpiogetev.x, qpiogetev.gx)
+ $generic -k -o gen/qpiogetev.x qpiogetev.gx $endif
+ ;
+
+libex.a:
+ # Retranslate any recently modified generic sources.
+ $ifeq (hostid, unix)
+ $call generic
+ $endif
+
+ @gen # Update datatype expanded files.
+
+ qpaccess.x qpoe.h
+ qpaccessf.x qpoe.h
+ qpaddf.x qpoe.h <error.h> <qpset.h>
+ qpastr.x qpoe.h
+ qpbind.x qpoe.h <fmset.h>
+ qpclose.x qpoe.h
+ qpcopy.x qpoe.h
+ qpcopyf.x qpoe.h <qpset.h>
+ qpctod.x
+ qpctoi.x <lexnum.h>
+ qpdelete.x qpoe.h
+ qpdeletef.x qpoe.h
+ qpdsym.x qpoe.h
+ qpdtype.x qpoe.h <ctype.h>
+ qpelsize.x
+ qpexclose.x qpex.h <mach.h>
+ qpexdata.x qpex.h <mach.h>
+ qpexdebug.x qpex.h qpoe.h <mach.h> <qpexset.h>
+ qpexdel.x qpex.h <mach.h>
+ qpexeval.x qpex.h <mach.h>
+ qpexgetat.x qpex.h <mach.h>
+ qpexgetfil.x qpex.h <mach.h>
+ qpexmodfil.x qpex.h qpoe.h <mach.h>
+ qpexopen.x qpex.h qpoe.h <mach.h>
+ qpexpand.x qpoe.h
+ qpgetb.x qpoe.h
+ qpgettok.x qpoe.h <ctype.h> <error.h> <fset.h>
+ qpgetx.x qpoe.h
+ qpgmsym.x qpoe.h
+ qpgnfn.x qpoe.h
+ qpgpar.x qpoe.h <ctype.h>
+ qpgpsym.x qpoe.h
+ qpgstr.x qpoe.h
+ qpinherit.x qpoe.h <error.h>
+ qpioclose.x qpio.h
+ qpiogetfil.x qpio.h qpoe.h <mach.h>
+ qpiogetrg.x qpio.h
+ qpiolmask.x <plset.h> qpio.h qpoe.h
+ qpiolwcs.x qpio.h
+ qpiomkidx.x qpio.h qpoe.h <error.h> <fset.h> <mach.h>
+ qpioopen.x qpex.h qpio.h qpoe.h <error.h> <fset.h> <mach.h>\
+ <plset.h>
+ qpioparse.x qpex.h qpio.h qpoe.h <ctype.h> <mach.h>
+ qpioputev.x qpio.h qpoe.h <mach.h>
+ qpiorb.x qpio.h
+ qpiosetfil.x qpex.h qpio.h
+ qpioseti.x qpio.h <plset.h> <qpioset.h>
+ qpiosetr.x qpio.h <qpioset.h>
+ qpiosetrg.x qpio.h
+ qpiostati.x qpio.h <qpioset.h>
+ qpiostatr.x qpio.h <qpioset.h>
+ qpiosync.x qpio.h qpoe.h <fset.h> <mach.h>
+ qpiowb.x qpio.h qpoe.h <fset.h> <mach.h>
+ qplenf.x qpoe.h
+ qploadwcs.x qpoe.h
+ qpmacro.x qpex.h qpoe.h <ctype.h> <error.h> <finfo.h>
+ qpmkfname.x qpoe.h
+ qpopen.x qpio.h qpoe.h <fmset.h>
+ qpparse.x <ctype.h>
+ qpparsefl.x qpex.h qpoe.h
+ qppclose.x <fset.h>
+ qppopen.x qpoe.h
+ qpppar.x qpoe.h <ctype.h>
+ qppstr.x qpoe.h
+ qpputb.x qpoe.h
+ qpputx.x qpoe.h
+ qpqueryf.x qpoe.h <qpset.h>
+ qpread.x qpoe.h
+ qprebuild.x qpoe.h
+ qprename.x qpoe.h
+ qprenamef.x qpoe.h
+ qpsavewcs.x qpoe.h
+ qpseti.x qpoe.h <qpset.h>
+ qpsetr.x qpoe.h <qpset.h>
+ qpsizeof.x qpoe.h
+ qpstati.x qpoe.h <qpset.h>
+ qpstatr.x qpoe.h <qpset.h>
+ qpsync.x qpoe.h
+ qpwrite.x qpoe.h
+ ;
diff --git a/sys/qpoe/qpaccess.x b/sys/qpoe/qpaccess.x
new file mode 100644
index 00000000..b8f5079f
--- /dev/null
+++ b/sys/qpoe/qpaccess.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_ACCESS -- Test if the named poefile exists and is accessible with the
+# given mode (mode=0 merely tests for the existence of the poefile).
+
+int procedure qp_access (poefile, mode)
+
+char poefile[ARB] #I poefile name
+int mode #I access mode
+
+int status
+pointer sp, fname
+int access()
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_PATHNAME, TY_CHAR)
+
+ call qp_mkfname (poefile, QPOE_EXTN, Memc[fname], SZ_PATHNAME)
+ status = access (Memc[fname], mode, 0)
+
+ call sfree (sp)
+ return (status)
+end
diff --git a/sys/qpoe/qpaccessf.x b/sys/qpoe/qpaccessf.x
new file mode 100644
index 00000000..4b11d570
--- /dev/null
+++ b/sys/qpoe/qpaccessf.x
@@ -0,0 +1,24 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_ACCESSF -- Test whether the named field (header parameter) exists.
+# Globally aliased parameters are recursively expanded and must resolve to
+# a normal parameter reference.
+
+int procedure qp_accessf (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer qp_gpsym()
+errchk qp_bind, qp_gpsym
+
+begin
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+ if (qp_gpsym (qp, param) != NULL)
+ return (YES)
+ else
+ return (NO)
+end
diff --git a/sys/qpoe/qpadd.gx b/sys/qpoe/qpadd.gx
new file mode 100644
index 00000000..83f240ae
--- /dev/null
+++ b/sys/qpoe/qpadd.gx
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../qpoe.h"
+
+# QP_ADD -- Set the value of a parameter, creating the parameter if it does
+# not already exist. This works for the most common case of simple scalar
+# valued header parameters, although any parameter may be written into it it
+# already exists. Additional control over the parameter attributes is possible
+# if the parameter is explicitly created with qp_addf before being written into.
+
+procedure qp_add$t (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+PIXEL value #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+char datatype[1]
+errchk qp_accessf, qp_addf
+string dtypes SPPTYPES
+int qp_accessf()
+
+begin
+ if (qp_accessf (qp, param) == NO) {
+ datatype[1] = dtypes[TY_PIXEL]
+ call qp_addf (qp, param, datatype, 1, comment, 0)
+ }
+ call qp_put$t (qp, param, value)
+end
diff --git a/sys/qpoe/qpaddf.x b/sys/qpoe/qpaddf.x
new file mode 100644
index 00000000..e72c2cc1
--- /dev/null
+++ b/sys/qpoe/qpaddf.x
@@ -0,0 +1,173 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <error.h>
+include <qpset.h>
+include "qpoe.h"
+
+# QP_ADDF -- Add a new field (header parameter) to the datafile. It is an
+# error if the parameter redefines an existing symbol. For variable array
+# parameters the initial size is zero, and a new lfile is allocated for the
+# parameter value. For static parameters storage is initialized to all zeros.
+
+procedure qp_addf (qp, param, datatype, maxelem, comment, flags)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char datatype[ARB] #I parameter data type
+int maxelem #I allocated length of parameter
+char comment[ARB] #I comment describing parameter
+int flags #I parameter flags
+
+bool newtype
+pointer sp, text, st, fm, sym, pval, dsym, dd
+int fd, sz_elem, type, nchars, dtype, nfields, i
+
+long note()
+pointer qp_gpsym(), stenter(), strefstab()
+int stpstr(), qp_dtype(), qp_parsefl(), gstrcpy
+int fm_nextlfile(), fm_getfd(), qp_elementsize(), fm_fopen()
+errchk qp_bind, qp_gpsym, stenter, stpstr, fm_nextlfile, fm_fopen
+errchk fm_getfd, note, write, syserrs
+define fixed_ 91
+
+begin
+ call smark (sp)
+ call salloc (text, SZ_TEXTBUF, TY_CHAR)
+
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ st = QP_ST(qp)
+ fm = QP_FM(qp)
+
+ # Resolve any macro references in the 'datatype' text.
+ # (Disabled - not sure this is a good idea here).
+
+ # nchars = qp_expandtext (qp, datatype, Memc[text], SZ_TEXTBUF)
+ nchars = gstrcpy (datatype, Memc[text], SZ_TEXTBUF)
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_addf: `%s' typ=`%s' nel=%d com=`%s' flg=%oB\n")
+ call pargstr (param)
+ call pargstr (Memc[text])
+ call pargi (maxelem)
+ call pargstr (comment)
+ call pargi (flags)
+ }
+
+ # Check for a redefinition.
+ sym = qp_gpsym (qp, param)
+ if (sym != NULL)
+ call syserrs (SYS_QPREDEF, param)
+
+ # Add the symbol.
+ sym = stenter (st, param, LEN_SYMBOL)
+
+ # Determine symbol type.
+ dtype = qp_dtype (qp, Memc[text], dsym)
+ newtype = (dtype == TY_USER && dsym == NULL)
+ sz_elem = qp_elementsize (qp, Memc[text], INSTANCEOF)
+
+ S_DTYPE(sym) = dtype
+ S_SZELEM(sym) = 0
+ if (dsym != NULL)
+ S_DSYM(sym) = dsym - strefstab(st,0)
+ else
+ S_DSYM(sym) = 0
+
+ # If defining a new user datatype (domain), SZELEM is the size of
+ # a structure element in chars, and MAXELEM is the length of the
+ # field list string, which becomes the value of the domain definition
+ # parameter.
+
+ if (newtype) {
+ S_MAXELEM(sym) = nchars
+ call salloc (dd, LEN_DDDES, TY_STRUCT)
+ iferr (nfields = qp_parsefl (qp, Memc[text], dd))
+ call erract (EA_WARN)
+ else
+ S_SZELEM(sym) = DD_STRUCTLEN(dd) * SZ_STRUCT
+ } else
+ S_MAXELEM(sym) = maxelem
+
+ # If no flags are specified, set SF_INHERIT for fixed length params.
+ if (flags == 0 && S_MAXELEM(sym) > 0)
+ S_FLAGS(sym) = SF_INHERIT
+ else if (flags == QPF_NONE)
+ S_FLAGS(sym) = 0
+ else
+ S_FLAGS(sym) = flags
+
+ # Comments are stored in the symbol table and cannot be modified.
+ if (comment[1] != EOS)
+ S_COMMENT(sym) = stpstr (st, comment, 0)
+ else
+ S_COMMENT(sym) = NULL
+
+ # Initialize data storage for the parameter.
+ if (S_MAXELEM(sym) == 0) {
+ # A variable length parameter; store in it's own lfile. The
+ # initial length is zero, hence initialization is not needed.
+
+ S_NELEM(sym) = 0
+ S_OFFSET(sym) = 1
+
+ # If we run out of lfiles, try to make do by allocating a fixed
+ # amount of static storage.
+
+ iferr (S_LFILE(sym) = fm_nextlfile(fm)) {
+ S_MAXELEM(sym) = (QP_FMPAGESIZE(qp) + sz_elem-1) / sz_elem
+ call erract (EA_WARN)
+ goto fixed_
+ }
+
+ if (dtype == TY_CHAR)
+ type = TEXT_FILE
+ else
+ type = BINARY_FILE
+
+ fd = fm_fopen (fm, S_LFILE(sym), NEW_FILE, type)
+ call close (fd)
+
+ } else {
+ # A fixed length parameter; allocate and initialize storage in
+ # LF_STATICPARS.
+fixed_
+ fd = fm_getfd (fm, LF_STATICPARS, APPEND, 0)
+
+ S_NELEM(sym) = 0
+ S_OFFSET(sym) = note (fd)
+ S_LFILE(sym) = LF_STATICPARS
+ nchars = S_MAXELEM(sym) * sz_elem
+
+ # The param value is the field list (datatype parameter) for a
+ # domain definition; otherwise we do not have a value yet, so we
+ # merely allocate the storage and initialize to zero.
+
+ if (newtype) {
+ call write (fd, Memc[text], nchars)
+ S_NELEM(sym) = S_MAXELEM(sym)
+ } else {
+ call salloc (pval, nchars, TY_CHAR)
+ call aclrc (Memc[pval], nchars)
+ call write (fd, Memc[pval], nchars)
+ }
+
+ call fm_retfd (fm, S_LFILE(sym))
+ }
+
+ if (QP_DEBUG(qp) > 2) {
+ # Dump symbol.
+ call eprintf ("%s: FLG=%oB TYP=%d DSY=%xX NEL=%d ")
+ call pargstr (param)
+ do i = 1, 4
+ call pargi (Memi[sym+i-1])
+ call eprintf ("MEL=%d SZE=%d COM=%xX LFN=%d OFF=%d\n")
+ do i = 5, 9
+ call pargi (Memi[sym+i-1])
+ }
+
+ QP_MODIFIED(qp) = YES
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpastr.x b/sys/qpoe/qpastr.x
new file mode 100644
index 00000000..4c856b10
--- /dev/null
+++ b/sys/qpoe/qpastr.x
@@ -0,0 +1,35 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_ASTR -- Set the value of a string parameter, creating the parameter if
+# it does not already exist. This works for the common case of string
+# parameters allocated a fixed amount of space at create time (any type of
+# string parameter can be written into if it already exists). Additional
+# control over the parameter attributes is possible if the parameter is
+# explicitly created with qp_addf before being written into.
+
+procedure qp_astr (qp, param, value, comment)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char value[ARB] #I parameter value
+char comment[ARB] #I comment field, if creating parameter
+
+int nchars
+int qp_accessf(), strlen()
+errchk qp_accessf, qp_addf
+
+begin
+ # By default we allocate a somewhat bigger storage area than needed
+ # to store the string, to permit updates of a similar length. If
+ # more control over the maximum string length is needed, QP_ADDF
+ # should be called explicitly.
+
+ if (qp_accessf (qp, param) == NO) {
+ nchars = (strlen(value) + INC_STRLEN-1) / INC_STRLEN * INC_STRLEN
+ call qp_addf (qp, param, "c", nchars, comment, 0)
+ }
+
+ call qp_pstr (qp, param, value)
+end
diff --git a/sys/qpoe/qpbind.x b/sys/qpoe/qpbind.x
new file mode 100644
index 00000000..cd9c9bb6
--- /dev/null
+++ b/sys/qpoe/qpbind.x
@@ -0,0 +1,48 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fmset.h>
+include "qpoe.h"
+
+# QP_BIND -- Fix the create-time QPOE file parameters. This is called
+# after the open, when the first datafile access occurs.
+
+procedure qp_bind (qp)
+
+pointer qp #I QPOE descriptor
+
+int fd
+pointer fm
+pointer stopen()
+int fm_fopen()
+errchk stopen, fm_fopen
+
+begin
+ if (QP_ACTIVE(qp) == NO) {
+ fm = QP_FM(qp)
+
+ # Create the initial symbol table.
+ QP_ST(qp) = stopen (QPOE_TITLE,
+ QP_STINDEXLEN(qp), QP_STSTABLEN(qp), QP_STSBUFSIZE(qp))
+
+ # Fix the datafile parameters.
+ call fm_seti (fm, FM_PAGESIZE, QP_FMPAGESIZE(qp))
+ call fm_seti (fm, FM_MAXLFILES, QP_FMMAXLFILES(qp))
+ call fm_seti (fm, FM_MAXPTPAGES, QP_FMMAXPTPAGES(qp))
+ call fm_seti (fm, FM_FCACHESIZE, QP_FMCACHESIZE(qp))
+
+ # Create the QPOE header and static storage lfiles.
+ fd = fm_fopen (fm, LF_QPOE, NEW_FILE, BINARY_FILE)
+ call close (fd)
+ fd = fm_fopen (fm, LF_STATICPARS, NEW_FILE, BINARY_FILE)
+ call close (fd)
+
+ # Must flag descriptor as active here to prevent reentrant
+ # calls via the procedures called by qp_inherit.
+
+ QP_ACTIVE(qp) = YES
+
+ # Inherit selected data objects from parent if NEW_COPY.
+ if (QP_MODE(qp) == NEW_COPY)
+ call qp_inherit (qp, QP_OQP(qp), STDERR)
+ }
+end
diff --git a/sys/qpoe/qpclose.x b/sys/qpoe/qpclose.x
new file mode 100644
index 00000000..eb537622
--- /dev/null
+++ b/sys/qpoe/qpclose.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_CLOSE -- Close an open QPOE descriptor and file.
+
+procedure qp_close (qp)
+
+pointer qp #I QPOE descriptor
+
+begin
+ # An open/close should produce an empty poefile.
+ if (QP_ACTIVE(qp) == NO) {
+ QP_MODIFIED(qp) = YES
+ call qp_bind (qp)
+ }
+
+ # Update the poefile on disk.
+ call qp_flushpar (qp)
+ call qp_sync (qp)
+
+ # Shut everything down.
+ call stclose (QP_ST(qp))
+ call fm_close (QP_FM(qp))
+ call mfree (qp, TY_STRUCT)
+end
diff --git a/sys/qpoe/qpcopy.x b/sys/qpoe/qpcopy.x
new file mode 100644
index 00000000..6d0a597d
--- /dev/null
+++ b/sys/qpoe/qpcopy.x
@@ -0,0 +1,28 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_COPY -- Copy a poefile. The output version is "rebuilt" in the process,
+# i.e., the datafile is not merely physically copied, but instead is rebuilt to
+# reclaim unused storage and render the file structures logically contiguous.
+
+procedure qp_copy (o_poefile, n_poefile)
+
+char o_poefile[ARB] #I old poefile name
+char n_poefile[ARB] #I new poefile name
+
+pointer sp
+pointer o_fname, n_fname
+string extn QPOE_EXTN
+
+begin
+ call smark (sp)
+ call salloc (o_fname, SZ_PATHNAME, TY_CHAR)
+ call salloc (n_fname, SZ_PATHNAME, TY_CHAR)
+
+ call qp_mkfname (o_poefile, extn, Memc[o_fname], SZ_PATHNAME)
+ call qp_mkfname (n_poefile, extn, Memc[n_fname], SZ_PATHNAME)
+ call fm_copy (Memc[o_fname], Memc[n_fname])
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpcopyf.x b/sys/qpoe/qpcopyf.x
new file mode 100644
index 00000000..ba47ad4f
--- /dev/null
+++ b/sys/qpoe/qpcopyf.x
@@ -0,0 +1,48 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpset.h>
+include "qpoe.h"
+
+define MAX_NELEM 8192 # copy unit (chunk) size
+
+# QP_COPYF -- Copy a field (parameter), either within a datafile, or from one
+# datafile to another.
+
+procedure qp_copyf (o_qp, o_param, n_qp, n_param)
+
+pointer o_qp #I QPOE descriptor of old (input) datafile
+char o_param[ARB] #I input parameter name
+pointer n_qp #I QPOE descriptor of new (output) datafile
+char n_param[ARB] #I output parameter name
+
+pointer sp, dp, cp, buf
+int nelem, elsize, chunk, nleft, first, maxelem, flags
+int qp_queryf(), qp_accessf(), qp_elementsize(), qp_read()
+errchk qp_queryf, qp_addf, qp_read, qp_write
+
+begin
+ call smark (sp)
+ call salloc (dp, SZ_DATATYPE, TY_CHAR)
+ call salloc (cp, SZ_COMMENT, TY_CHAR)
+
+ # Get parameter attributes and create new parameter if necessary.
+ nelem = qp_queryf (o_qp, o_param, Memc[dp], maxelem, Memc[cp], flags)
+ if (qp_accessf (n_qp, n_param) == NO)
+ call qp_addf (n_qp, n_param, Memc[dp], maxelem, Memc[cp], flags)
+
+ # Copy parameter value.
+ if (nelem > 0) {
+ elsize = qp_elementsize (o_qp, Memc[dp], INSTANCEOF)
+ chunk = min (MAX_NELEM, nelem)
+ call salloc (buf, chunk * elsize, TY_CHAR)
+
+ first = 1
+ for (nleft=nelem; nleft > 0; nleft=nleft-nelem) {
+ nelem = qp_read (o_qp,o_param, Memc[buf], chunk,first, Memc[dp])
+ call qp_write (n_qp,n_param, Memc[buf], nelem,first, Memc[dp])
+ first = first + nelem
+ }
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpctod.x b/sys/qpoe/qpctod.x
new file mode 100644
index 00000000..6487650c
--- /dev/null
+++ b/sys/qpoe/qpctod.x
@@ -0,0 +1,34 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define SZ_NUMBUF 32 # buffer for extracting numbers
+
+# QP_CTOD -- Return as a double the next numeric token from the input stream.
+# This differs from the standard FMTIO procedures only in that colon is not
+# considered a numeric character (as used in sexagesimal numbers).
+
+int procedure qp_ctod (str, ip, dval)
+
+char str[ARB] #I input string
+int ip #U pointer into input string
+double dval #O double value
+
+int nchars, op, i
+char numbuf[SZ_NUMBUF]
+int ctod()
+
+begin
+ i = ip
+ do op = 1, SZ_NUMBUF
+ if (str[i] != ':' && str[i] != EOS) {
+ numbuf[op] = str[i]
+ i = i + 1
+ } else
+ break
+
+ i = 1
+ numbuf[op] = EOS
+ nchars = ctod (numbuf, i, dval)
+ ip = ip + i - 1
+
+ return (nchars)
+end
diff --git a/sys/qpoe/qpctoi.x b/sys/qpoe/qpctoi.x
new file mode 100644
index 00000000..9ac70ddb
--- /dev/null
+++ b/sys/qpoe/qpctoi.x
@@ -0,0 +1,34 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <lexnum.h>
+
+# QP_CTOI -- Decode an integer token from the input string, advancing the
+# input pointer the the first character following the decoded number, and
+# returning the number of numeric characters decoded as the function value.
+# This is equivalent to the standard CTOI except that it calls LEXNUM first
+# to determine the radix of the input number, hence can deals with hex and
+# octal numbers as well as decimal.
+
+int procedure qp_ctoi (str, ip, ival)
+
+char str[ARB] #I input string
+int ip #U pointer into input string
+int ival #O integer value
+
+int ip_save, base, nchars
+int gctol(), lexnum()
+
+begin
+ ip_save = ip
+ switch (lexnum (str, ip, nchars)) {
+ case LEX_OCTAL:
+ base = 8
+ case LEX_HEX:
+ base = 16
+ default:
+ base = 10
+ }
+
+ ip = ip_save
+ return (gctol (str, ip, ival, base))
+end
diff --git a/sys/qpoe/qpdelete.x b/sys/qpoe/qpdelete.x
new file mode 100644
index 00000000..16b32161
--- /dev/null
+++ b/sys/qpoe/qpdelete.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_DELETE -- Delete a poefile.
+
+procedure qp_delete (poefile)
+
+char poefile[ARB] #I poefile name
+pointer sp, fname
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_PATHNAME, TY_CHAR)
+
+ call qp_mkfname (poefile, QPOE_EXTN, Memc[fname], SZ_PATHNAME)
+ call delete (Memc[fname])
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpdeletef.x b/sys/qpoe/qpdeletef.x
new file mode 100644
index 00000000..b7c41189
--- /dev/null
+++ b/sys/qpoe/qpdeletef.x
@@ -0,0 +1,35 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_DELETEF -- Delete a header parameter. It is an error if the named header
+# parameter does not exist. Deletions are permanent once the datafile is
+# updated.
+
+procedure qp_deletef (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer sym
+pointer qp_gpsym()
+errchk qp_gpsym, syserrs
+
+begin
+ # Access the named parameter.
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL)
+ call syserrs (SYS_QPUKNPAR, param)
+ else if (and (S_FLAGS(sym), SF_DELETED) != 0)
+ return
+
+ # If the parameter value is stored in its own lfile, delete it.
+ if (S_LFILE(sym) > LF_STATICPARS)
+ call fm_lfdelete (QP_FM(qp), S_LFILE(sym))
+
+ # Set the delete bit in the symbol descriptor.
+ S_FLAGS(sym) = or (S_FLAGS(sym), SF_DELETED)
+
+ QP_MODIFIED(qp) = YES
+end
diff --git a/sys/qpoe/qpdsym.x b/sys/qpoe/qpdsym.x
new file mode 100644
index 00000000..d94b54b4
--- /dev/null
+++ b/sys/qpoe/qpdsym.x
@@ -0,0 +1,56 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_DSYM -- Dump the symbol table of a QPOE file.
+
+procedure qp_dsym (qp, out)
+
+pointer qp #I QPOE descriptor
+int out #I output file
+
+int nsyms, i
+pointer sp, st, sym, op, pname, syms
+pointer sthead(), stnext(), stname()
+
+begin
+ call smark (sp)
+ st = QP_ST(qp)
+
+ # Count the symbols.
+ nsyms = 0
+ for (sym=sthead(st); sym != NULL; sym=stnext(st,sym))
+ nsyms = nsyms + 1
+
+ # Construct a reversed array of symbol pointers.
+ call salloc (syms, nsyms, TY_POINTER)
+ op = syms + nsyms - 1
+ for (sym=sthead(st); sym != NULL; sym=stnext(st,sym)) {
+ Memi[op] = sym
+ op = op - 1
+ }
+
+ # Output the symbols.
+ if (nsyms > 0)
+ call fprintf (out,
+" SYMBOL FLAGS DTYPE DSYM NELEM MAXELEM SZELEM COMMENT LFILE OFFSET\n")
+
+ do i = 1, nsyms {
+ sym = Memi[syms+i-1]
+ pname = stname (st, sym)
+
+ call fprintf (out, "%16s %5o %5d %4d %5d %7d %6d %7x %5d %6d\n")
+ call pargstr (Memc[pname])
+ call pargi (and (S_FLAGS(sym), 77777B))
+ call pargi (S_DTYPE(sym))
+ call pargi (S_DSYM(sym))
+ call pargi (S_NELEM(sym))
+ call pargi (S_MAXELEM(sym))
+ call pargi (S_SZELEM(sym))
+ call pargi (S_COMMENT(sym))
+ call pargi (S_LFILE(sym))
+ call pargi (S_OFFSET(sym))
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpdtype.x b/sys/qpoe/qpdtype.x
new file mode 100644
index 00000000..ed5b9e89
--- /dev/null
+++ b/sys/qpoe/qpdtype.x
@@ -0,0 +1,57 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+include "qpoe.h"
+
+# QP_DTYPE -- Translate the given symbolic datatype name into an integer
+# type code. The possible type codes are most of the standard SPP TY_type
+# codes, plus TY_MACRO, TY_OPAQUE, and TY_USER. If the symbol type is TY_USER
+# (a user defined data structure or domain)), then a pointer to the symbol
+# table entry for the named domain is returned as an output argument. The
+# integer type code is returned as the function value.
+
+int procedure qp_dtype (qp, datatype, dsym)
+
+pointer qp #I QPOE descriptor
+char datatype[ARB] #I symbolic datatype name
+pointer dsym #O pointer to domain symbol, if TY_USER
+
+char junk[1]
+int dtype, ip
+string types "|bool|char|short|int|long|real|double|complex|macro|opaque|"
+
+pointer stfind()
+int stridx(), strdic()
+
+begin
+ dtype = NULL
+ dsym = NULL
+ for (ip=1; IS_WHITE(datatype[ip]); ip=ip+1)
+ ;
+
+ # Single character standard dtype code (bcsilrdx)?
+ if (datatype[ip+1] == EOS)
+ dtype = stridx (datatype[ip], SPPTYPES)
+
+ # Spelled out dtype name. Check standard names first.
+ if (dtype == NULL) {
+ dtype = strdic (datatype[ip], junk, 1, types)
+ if (dtype == 9)
+ dtype = TY_MACRO
+ else if (dtype == 10)
+ dtype = TY_OPAQUE
+ }
+
+ # Lastly, check the special types.
+ if (dtype == 0) {
+ if (datatype[ip] == '{') # field list
+ dtype = TY_USER
+ else {
+ dsym = stfind (QP_ST(qp), datatype[ip])
+ if (dsym != NULL)
+ dtype = S_DTYPE(dsym)
+ }
+ }
+
+ return (dtype)
+end
diff --git a/sys/qpoe/qpelsize.x b/sys/qpoe/qpelsize.x
new file mode 100644
index 00000000..b84e0dfa
--- /dev/null
+++ b/sys/qpoe/qpelsize.x
@@ -0,0 +1,20 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+# QP_ELEMENTSIZE -- Determine the size in chars of a QPOE datatype. This may
+# be one of the special datatypes (user defined record types), or a primitive
+# type.
+
+int procedure qp_elementsize (qp, datatype, reftype)
+
+pointer qp #I QPOE descriptor
+char datatype[ARB] #I symbolic datatype name
+int reftype #I type of reference (immediate or instanceof)
+
+pointer dsym
+int dtype
+int qp_sizeof(), qp_dtype()
+
+begin
+ dtype = qp_dtype (qp, datatype, dsym)
+ return (qp_sizeof (qp, dtype, dsym, reftype))
+end
diff --git a/sys/qpoe/qpex.h b/sys/qpoe/qpex.h
new file mode 100644
index 00000000..45cc3db0
--- /dev/null
+++ b/sys/qpoe/qpex.h
@@ -0,0 +1,164 @@
+# QPEX.H -- QPOE expression evaluator definitions.
+
+# Size limiting definitions.
+define DEF_PROGBUFLEN 65536 # default program buffer length, ints
+define DEF_DATABUFLEN 65536 # default data buffer length, chars
+define DEF_SZEXPRBUF 2048 # default size expression buffer
+define INC_SZEXPRBUF 2048 # increment if overflow
+define DEF_XLEN 256 # default (initial) range buffer size
+define MAX_INSTRUCTIONS ARB # arbitrary do-loop index
+define MAX_LEVELS 32 # max levels of program nesting
+define DEF_MAXRRLUTLEN 1024 # max RRLUT (reduced resolution) length
+define DEF_MAXFRLUTLEN 8192 # max FRLUT (full resolution) length
+define DEF_LUTMINRANGES 5 # use RRLUT if more ranges than this
+define DEF_LUTSCALE 15 # multiplied by nranges to get rrlutlen
+
+# Magic values used to represent open ranges :N and N:.
+define LEFTI -MAX_INT
+define RIGHTI MAX_INT
+define LEFTR -MAX_REAL
+define RIGHTR MAX_REAL
+define LEFTD -MAX_DOUBLE
+define RIGHTD MAX_DOUBLE
+
+define IS_LEFTI (($1) == -MAX_INT)
+define IS_RIGHTI (($1) == MAX_INT)
+define IS_LEFTR (($1) <= -MAX_REAL)
+define IS_RIGHTR (($1) >= MAX_REAL)
+define IS_LEFTD (($1) <= -MAX_DOUBLE)
+define IS_RIGHTD (($1) >= MAX_DOUBLE)
+
+# The compiled expression descriptor. The program buffer holds the compiled
+# expression to be interpreted to test each data event structure. The data
+# buffer is used to store program data, e.g., lookup table descriptors,
+# TY_DOUBLE constants (these are too large to be stored directly in the
+# compiled program), and the textual expressions compiled to generate the
+# program; the latter are used by QPEX_GETFILTER to regenerate the current
+# expression. The expression terms (ET) and lookup table (LT) descriptors
+# are maintained on linked lists. New ET descriptors are linked onto the
+# tail of the ET list; LT descriptors are linked onto the head of the LT list.
+# The program and data buffers are *nonrelocatable* (hence fixed in size)
+# to allow use of absolute pointers to reference structures within the buffers.
+
+define LEN_EXDES 16
+define EX_QP Memi[$1] # back pointer to QPOE descriptor
+define EX_DEBUG Memi[$1+1] # debug level
+define EX_START Memi[$1+2] # pointer to first instruction
+define EX_PB Memi[$1+3] # pointer to program buffer (int)
+define EX_PBTOP Memi[$1+4] # pointer to top+1 of pb
+define EX_PBOP Memi[$1+5] # pointer to next avail. cell in pb
+define EX_DB Memi[$1+6] # data buffer pointer (char)
+define EX_DBTOP Memi[$1+7] # pointer to top+1 of db
+define EX_DBOP Memi[$1+8] # pointer to next avail. cell in db
+define EX_MAXFRLUTLEN Memi[$1+9] # max full-res lut length
+define EX_MAXRRLUTLEN Memi[$1+10] # max reduced-res lut length
+define EX_LUTMINRANGES Memi[$1+11] # min ranges required for a LUT
+define EX_LUTSCALE Memi[$1+12] # scale nranges to frlutlen
+define EX_ETHEAD Memi[$1+13] # offset of first expr term descriptor
+define EX_ETTAIL Memi[$1+14] # offset of last expr term descriptor
+define EX_LTHEAD Memi[$1+15] # offset of first LUT descriptor
+
+# Expression terms descriptor. Stored in the data buffer and maintained
+# as a linked list.
+
+define LEN_ETDES 9
+define ET_ATTTYPE Memi[$1] # datatype of attribute
+define ET_ATTOFF Memi[$1+1] # *typed* offset of attribute
+define ET_PROGPTR Memi[$1+2] # pointer to program segment
+define ET_NINSTR Memi[$1+3] # program segment size, instructions
+define ET_DELETED Memi[$1+4] # set if term is deleted
+define ET_ATNAME Memi[$1+5] # attribute name used in expr
+define ET_ASSIGNOP Memi[$1+6] # type of assignment ("=", "+=")
+define ET_EXPRTEXT Memi[$1+7] # saved expr text
+define ET_NEXT Memi[$1+8] # databuf offset of next ET struct
+
+# Lookup table descriptor. Stored in the data buffer and maintained as a
+# linked list. The table itself is separately allocated.
+
+define LEN_LTDES 10
+define LT_NEXT Memi[$1] # pointer to next stored LUT
+define LT_TYPE Memi[$1+1] # TY_SHORT pointer to stored LUT
+define LT_LUTP Memi[$1+2] # TY_SHORT pointer to stored LUT
+define LT_NBINS Memi[$1+3] # number of lookup table entries
+define LT_LEFT Memi[$1+4] # lut value if index off left end
+define LT_RIGHT Memi[$1+5] # lut value if index off right end
+define LT_I0 Memr[P2R($1+6)] # zero point for integer LUT
+define LT_IS Memr[P2R($1+8)] # scale factor for integer LUT
+define LT_R0 Memr[P2R($1+6)] # zero point for real LUT
+define LT_RS Memr[P2R($1+8)] # scale factor for real LUT
+define LT_D0 Memd[P2D($1+6)] # zero point for double LUT
+define LT_DS Memd[P2D($1+8)] # scale factor for double LUT
+
+define LT_LUT Mems[LT_LUTP($1)+$2-1] # LT_LUT(lt,i)
+
+# Macros for referencing the fields of an instruction. TY_DOUBLE arguments
+# are stored in the data buffer, storing an offset in the instruction field.
+
+define LEN_INSTRUCTION 4 # instruction length, ints
+
+define OPCODE Memi[$1] # instruction opcode.
+define IARG1 Memi[$1+1] # first integer argument
+define IARG2 Memi[$1+2] # second integer argument
+define IARG3 Memi[$1+3] # third integer argument
+define RARG1 Memr[P2R($1+1)] # first real argument
+define RARG2 Memr[P2R($1+2)] # second real argument
+define RARG3 Memr[P2R($1+3)] # third real argument
+define DARG1 Memd[IARG1($1)] # first double argument
+define DARG2 Memd[IARG2($1)] # second double argument
+define DARG3 Memd[IARG3($1)] # third double argument
+
+# Instruction opcodes.
+
+define PASS 00 # set pass=true and return
+define RET 01 # return from subprogram
+define NOP 02 # no-operation
+define GOTO 03 # goto offset
+define XIFT 04 # exit if expr-value true
+define XIFF 05 # exit if expr-value false
+define LDSI 06 # load short to int
+define LDII 07 # load int
+define LDRR 08 # load real
+define LDRD 09 # load real to double
+define LDDD 10 # load double
+
+define BTTI 11 # bit test, int
+define EQLI 12 # test if equal
+define EQLR 13
+define EQLD 14
+define LEQI 15 # test if less than or equal
+define LEQR 16
+define LEQD 17
+define GEQI 18 # test if greater than or equal
+define GEQR 19
+define GEQD 20
+define RNGI 21 # range test
+define RNGR 22
+define RNGD 23
+
+define BTTXS 24 # bit test direct and exit if false
+define BTTXI 25
+define NEQXS 26 # not equals test and exit
+define NEQXI 27
+define NEQXR 28
+define NEQXD 29
+define EQLXS 30 # equality test direct and exit if false
+define EQLXI 31
+define EQLXR 32
+define EQLXD 33
+define LEQXS 34 # LEQ test direct and exit if false
+define LEQXI 35
+define LEQXR 36
+define LEQXD 37
+define GEQXS 38 # GEQ test direct and exit if false
+define GEQXI 39
+define GEQXR 40
+define GEQXD 41
+define RNGXS 42 # range test direct and exit if false
+define RNGXI 43
+define RNGXR 44
+define RNGXD 45
+
+define LUTXS 46 # lookup table test
+define LUTXI 47
+define LUTXR 48
+define LUTXD 49
diff --git a/sys/qpoe/qpexattrl.gx b/sys/qpoe/qpexattrl.gx
new file mode 100644
index 00000000..7cd6cd0a
--- /dev/null
+++ b/sys/qpoe/qpexattrl.gx
@@ -0,0 +1,127 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+include "../qpex.h"
+
+# QPEX_ATTRL -- Get the good-value range list for the named attribute, as a
+# binary range list of the indicated type. This range list is a simplified
+# version of the original filter expression, which may have contained
+# multiple fields, some negated or overlapping, in any order, subsequently
+# modified or deleted with qpex_modfilter, etc. The final resultant range
+# list is ordered and consists of discreet nonoverlapping ranges.
+#
+# Upon input the variables XS and XE should either point to a pair of
+# preallocated buffers of length XLEN, or they should be set to NULL.
+# The routine will reallocate the buffers as necessary to allow for long
+# range lists, updating XLEN so that it always contains the actual length
+# of the arrays (which may not be completely full). Each list element
+# consists of a pair of values (xs[i],xe[i]) defining the start and end
+# points of the range. If xs[1] is INDEF the range is open to the left,
+# if xe[nranges] is INDEF the range is open to the right. The number of
+# ranges output is returned as the function value.
+
+int procedure qpex_attrl$t (ex, attribute, xs, xe, xlen)
+
+pointer ex #I QPEX descriptor
+char attribute[ARB] #I attribute name
+pointer xs #U pointer to array of start values
+pointer xe #U pointer to array of end values
+int xlen #U length of xs/xe arrays
+
+pointer ps, pe, qs, qe
+pointer sp, expr, ip, ep
+int plen, qlen, np, nq, nx
+int neterms, nchars, maxch
+int qpex_getattribute(), qpex_parse$t(), qp_rlmerge$t()
+
+begin
+ call smark (sp)
+
+ # Get attribute filter expression. In the unlikely event that the
+ # expression is too large to fit in our buffer, repeat with a buffer
+ # twice as large until it fits.
+
+ maxch = DEF_SZEXPRBUF
+ nchars = 0
+
+ repeat {
+ maxch = maxch * 2
+ call salloc (expr, maxch, TY_CHAR)
+ nchars = qpex_getattribute (ex, attribute, Memc[expr], maxch)
+ if (nchars <= 0)
+ break
+ } until (nchars < maxch)
+
+ # Parse expression to produce a range list. If the expression
+ # contains multiple eterms each is parsed separately and merged
+ # into the final output range list.
+
+ nx = 0
+ neterms = 0
+
+ if (nchars > 0) {
+ # Get range list storage.
+ plen = DEF_XLEN
+ call malloc (ps, plen, TY_PIXEL)
+ call malloc (pe, plen, TY_PIXEL)
+ qlen = DEF_XLEN
+ call malloc (qs, qlen, TY_PIXEL)
+ call malloc (qe, qlen, TY_PIXEL)
+
+ # Parse each subexpression and merge into output range list.
+ for (ip=expr; Memc[ip] != EOS; ) {
+ # Get next subexpression.
+ while (IS_WHITE (Memc[ip]))
+ ip = ip + 1
+ for (ep=ip; Memc[ip] != EOS; ip=ip+1)
+ if (Memc[ip] == ';') {
+ Memc[ip] = EOS
+ ip = ip + 1
+ break
+ }
+ if (Memc[ep] == EOS)
+ break
+
+ # Copy output range list to X list temporary.
+ if (max(nx,1) > plen) {
+ plen = max(xlen,1)
+ call realloc (ps, plen, TY_PIXEL)
+ call realloc (pe, plen, TY_PIXEL)
+ }
+ if (neterms <= 0) {
+ Mem$t[ps] = LEFT$T
+ Mem$t[pe] = RIGHT$T
+ np = 1
+ } else {
+ call amov$t (Mem$t[xs], Mem$t[ps], nx)
+ call amov$t (Mem$t[xe], Mem$t[pe], nx)
+ np = nx
+ }
+
+ # Parse next eterm into Y list temporary.
+ nq = qpex_parse$t (Memc[ep], qs, qe, qlen)
+
+ # Merge the X and Y lists, leaving result in output list.
+ nx = qp_rlmerge$t (xs,xe,xlen,
+ Mem$t[ps], Mem$t[pe], np, Mem$t[qs], Mem$t[qe], nq)
+
+ neterms = neterms + 1
+ }
+
+ # Free temporary range list storage.
+ call mfree (ps, TY_PIXEL); call mfree (pe, TY_PIXEL)
+ call mfree (qs, TY_PIXEL); call mfree (qe, TY_PIXEL)
+
+ # Convert LEFT/RIGHT magic values to INDEF.
+ if (nx > 0) {
+ if (IS_LEFT$T (Mem$t[xs]))
+ Mem$t[xs] = INDEF
+ if (IS_RIGHT$T (Mem$t[xe+nx-1]))
+ Mem$t[xe+nx-1] = INDEF
+ }
+ }
+
+ call sfree (sp)
+ return (nx)
+end
diff --git a/sys/qpoe/qpexclose.x b/sys/qpoe/qpexclose.x
new file mode 100644
index 00000000..137884e9
--- /dev/null
+++ b/sys/qpoe/qpexclose.x
@@ -0,0 +1,25 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "qpex.h"
+
+# QPEX_CLOSE -- Close the QPEX descriptor.
+
+procedure qpex_close (ex)
+
+pointer ex #I QPEX descriptor
+
+pointer lt
+
+begin
+ # Free any LUT buffers.
+ for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt))
+ call mfree (LT_LUTP(lt), TY_SHORT)
+
+ # Free the data and program buffers.
+ call mfree (EX_PB(ex), TY_STRUCT)
+ call mfree (EX_DB(ex), TY_CHAR)
+
+ # Free the main descriptor.
+ call mfree (ex, TY_STRUCT)
+end
diff --git a/sys/qpoe/qpexcode.gx b/sys/qpoe/qpexcode.gx
new file mode 100644
index 00000000..e148b499
--- /dev/null
+++ b/sys/qpoe/qpexcode.gx
@@ -0,0 +1,484 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QPEX_CODEGEN -- Generate interpreter metacode to evaluate the given
+# expression. The new code is appended to the current compiled program,
+# adding additional constraints which a data event will have to meet to
+# pass the filter.
+
+int procedure qpex_codegen$t (ex, atname, assignop, expr, offset, dtype)
+
+pointer ex #I qpex descriptor
+char atname[ARB] #I attribute name (for expr regeneration)
+char assignop[ARB] #I "=" or "+=" (for expr regeneration)
+char expr[ARB] #I expression to be compiled
+int offset #I typed offset of referenced attribute
+int dtype #I datatype of referenced attribute
+
+int nbins, bin, xp
+pointer lt, lut, lutx, pb
+PIXEL x1, x2, xmin, xmax
+int xlen, nranges, n_nranges, level, opcode, ip, i
+pointer pb_save, db_save, xs_buf, xe_buf, xs, xe, n_xs, n_xe, et, prev
+
+PIXEL sv_xs[MAX_LEVELS], sv_xe[MAX_LEVELS]
+pointer sv_lt[MAX_LEVELS], sv_lut[MAX_LEVELS], sv_lutx[MAX_LEVELS]
+int sv_xp[MAX_LEVELS], sv_nranges[MAX_LEVELS], sv_bin[MAX_LEVELS]
+int sv_nbins[MAX_LEVELS]
+
+$if (datatype == d)
+double xoffset, xscale
+double sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS]
+int d_x1, d_x2
+int qpex_refd()
+$else
+PIXEL d_x1, d_x2
+real xoffset, xscale
+real sv_xoffset[MAX_LEVELS], sv_xscale[MAX_LEVELS]
+$endif
+
+$if (datatype == rd)
+bool fp_equal$t()
+$else
+define fp_equal$t($1==$2)
+$endif
+
+$if (datatype == i)
+bool complement
+int maskval
+int qp_ctoi()
+$endif
+
+int qpex_parse$t()
+int stridxs(), btoi(), qpex_sublist$t()
+pointer qpex_dballoc(), qpex_dbpstr(), qpex_pbpos()
+errchk qpex_dballoc, qpex_pbpin, malloc, calloc, realloc, qpex_parse$t
+
+string qpexwarn "QPEX Warning"
+define error_ 91
+define next_ 92
+define null_ 93
+define resume_ 94
+define bbmask_ 95
+define continue_ 96
+define XS Mem$t[xs+($1)-1]
+define XE Mem$t[xe+($1)-1]
+
+begin
+ pb = EX_PB(ex)
+
+ # Save the program state in case we have to abort.
+ call qpex_mark (ex, pb_save, db_save)
+
+ # Allocate and initialize a new expression term descriptor, linking
+ # it onto the tail of the ETTERMs list.
+
+ et = qpex_dballoc (ex, LEN_ETDES, TY_STRUCT)
+
+ ET_ATTTYPE(et) = dtype
+ ET_ATTOFF(et) = offset
+ ET_ATNAME(et) = qpex_dbpstr (ex, atname)
+ ET_ASSIGNOP(et) = qpex_dbpstr (ex, assignop)
+ ET_EXPRTEXT(et) = qpex_dbpstr (ex, expr)
+ ET_PROGPTR(et) = qpex_pbpos (ex)
+ ET_DELETED(et) = NO
+
+ prev = EX_ETTAIL(ex)
+ if (prev != NULL)
+ ET_NEXT(prev) = et
+ ET_NEXT(et) = NULL
+ EX_ETTAIL(ex) = et
+ if (EX_ETHEAD(ex) == NULL)
+ EX_ETHEAD(ex) = et
+
+ ip = stridxs ("%", expr)
+ $if (datatype == i)
+ # Attempt to compile a bitmask test if `%' is found in the
+ # expression. Since bitmasks cannot be mixed with range list
+ # expressions, this case is handled separately.
+
+ if (ip > 0) {
+ complement = false
+ level = 0
+
+ # Parse expression (very limited for this case).
+ for (ip=1; expr[ip] != EOS; ip=ip+1) {
+ switch (expr[ip]) {
+ case '!':
+ complement = !complement
+ case '(', '[':
+ level = level + 1
+ case ')', ']':
+ level = level - 1
+ case '%':
+ ip = ip + 1
+ if (qp_ctoi (expr, ip, maskval) < 0)
+ goto bbmask_
+ else
+ ip = ip - 1
+ default:
+ goto bbmask_
+ }
+ }
+
+ # Verify paren level, handle errors.
+ if (level != 0) {
+bbmask_ call eprintf ("%s: bad bitmask expression `%s'\n")
+ call pargstr (qpexwarn)
+ call pargstr (expr)
+ goto error_
+ }
+
+ # Compile the bitmask test.
+ if (complement)
+ maskval = not(maskval)
+ if (dtype == TY_SHORT)
+ call qpex_pbpin (ex, BTTXS, offset, maskval, 0)
+ else
+ call qpex_pbpin (ex, BTTXI, offset, maskval, 0)
+
+ # Finish setting up the eterm descriptor.
+ ET_NINSTR(et) = 1
+ return (OK)
+ }
+ $else
+ # Bitmask tests are meaningless for floating point data.
+ if (ip > 0) {
+ call eprintf ("%s: bitmasks not permitted for floating data\n")
+ call pargstr (qpexwarn)
+ goto error_
+ }
+ $endif
+
+ # Compile a general range list expression. The basic procedure is
+ # to parse the expression to produce an optimized binary range list,
+ # then either compile the range list as an explicit series of
+ # instructions or as a lookup table, depending upon the number of
+ # ranges.
+
+ xlen = DEF_XLEN
+ call malloc (xs_buf, xlen, TY_PIXEL)
+ call malloc (xe_buf, xlen, TY_PIXEL)
+
+ # Convert expr to a binary range list and set up the initial context.
+ # Ensure that the range list buffers are large enough to hold any
+ # sublists extracted during compilation.
+
+ nranges = qpex_parse$t (expr, xs_buf, xe_buf, xlen)
+ if (xlen < nranges * 2) {
+ xlen = nranges * 2
+ call realloc (xs_buf, xlen, TY_PIXEL)
+ call realloc (xe_buf, xlen, TY_PIXEL)
+ }
+
+ xs = xs_buf
+ xe = xe_buf
+ level = 0
+
+ repeat {
+next_
+ # Compile a new range list (or sublist).
+ if (nranges <= 0) {
+ # This shouldn't happen.
+null_ call eprintf ("%s: null range list\n")
+ call pargstr (qpexwarn)
+ call qpex_pbpin (ex, PASS, 0, 0, 0)
+
+ } else if (nranges == 1) {
+ # Output an instruction to load the data, perform the range
+ # test, and conditionally exit all in a single instruction.
+
+ x1 = XS(1); x2 = XE(1)
+ $if (datatype == d)
+ d_x1 = qpex_refd (ex, x1)
+ d_x2 = qpex_refd (ex, x2)
+ $else
+ d_x1 = x1
+ d_x2 = x2
+ $endif
+
+ if (dtype == TY_SHORT) {
+ if (IS_LEFT$T(x1) && IS_RIGHT$T(x2))
+ ; # pass everything (no tests)
+ else if (IS_LEFT$T(x1))
+ call qpex_pbpin (ex, LEQXS, offset, d_x2, 0)
+ else if (IS_RIGHT$T(x2))
+ call qpex_pbpin (ex, GEQXS, offset, d_x1, 0)
+ else if (fp_equal$t (x1, x2))
+ call qpex_pbpin (ex, EQLXS, offset, d_x1, d_x2)
+ else
+ call qpex_pbpin (ex, RNGXS, offset, d_x1, d_x2)
+ } else {
+ if (IS_LEFT$T(x1) && IS_RIGHT$T(x2))
+ ; # pass everything (no tests)
+ else if (IS_LEFT$T(x1))
+ call qpex_pbpin (ex, LEQX$T, offset, d_x2, 0)
+ else if (IS_RIGHT$T(x2))
+ call qpex_pbpin (ex, GEQX$T, offset, d_x1, 0)
+ else if (fp_equal$t (x1, x2))
+ call qpex_pbpin (ex, EQLX$T, offset, d_x1, d_x2)
+ else
+ call qpex_pbpin (ex, RNGX$T, offset, d_x1, d_x2)
+ }
+
+ } else if (nranges < EX_LUTMINRANGES(ex)) {
+ # If the number of ranges to be tested for the data is small,
+ # compile explicit code to perform the range tests directly.
+ # Otherwise skip forward and compile a lookup table instead.
+ # In either case, the function of the instructions compiled
+ # is to test the data loaded into the register above, setting
+ # the value of PASS to true if the data lies in any of the
+ # indicated ranges.
+
+ # Check for !X, which is indicated in range list form by a
+ # two element list bracketing the X on each side.
+
+ if (nranges == 2)
+ if (IS_LEFT$T(XS(1)) && IS_RIGHT$T(XE(2)))
+ $if (datatype == si)
+ if (XE(1)+1 == XS(2)-1) {
+ if (dtype == TY_SHORT)
+ opcode = NEQXS
+ else
+ opcode = NEQXI
+ call qpex_pbpin (ex, opcode, offset, XE(1)+1, 0)
+ goto resume_
+ }
+ $else $if (datatype == r)
+ if (fp_equal$t (XE(1), XS(2))) {
+ call qpex_pbpin (ex, NEQX$T, offset, XE(1), 0)
+ goto resume_
+ }
+ $else
+ if (fp_equal$t (XE(1), XS(2))) {
+ call qpex_pbpin (ex, NEQX$T, offset,
+ qpex_refd(ex,XE(1)), 0)
+ goto resume_
+ }
+ $endif $endif
+
+ # If at level zero, output instruction to load data into
+ # register and initialize PASS to false. Don't bother if
+ # compiling a subprogram, as these operations will already
+ # have been performed by the caller.
+
+ if (level == 0) {
+ $if (datatype == i)
+ if (dtype == TY_SHORT)
+ opcode = LDSI
+ else
+ opcode = LDII
+ $else
+ opcode = LD$T$T
+ $endif
+ call qpex_pbpin (ex, opcode, offset, 0, 0)
+ }
+
+ # Compile a series of equality or range tests.
+ do i = 1, nranges {
+ x1 = XS(i); x2 = XE(i)
+ $if (datatype == d)
+ d_x1 = qpex_refd (ex, x1)
+ d_x2 = qpex_refd (ex, x2)
+ $else
+ d_x1 = x1
+ d_x2 = x2
+ $endif
+
+ if (IS_LEFT$T(x1))
+ call qpex_pbpin (ex, LEQ$T, d_x2, 0, 0)
+ else if (IS_RIGHT$T(x2))
+ call qpex_pbpin (ex, GEQ$T, d_x1, 0, 0)
+ else if (fp_equal$t (x1, x2))
+ call qpex_pbpin (ex, EQL$T, d_x1, d_x2, 0)
+ else
+ call qpex_pbpin (ex, RNG$T, d_x1, d_x2, 0)
+ }
+
+ # Compile a test and exit instruction.
+ call qpex_pbpin (ex, XIFF, 0, 0, 0)
+
+ } else {
+ # Compile a lookup table test. Lookup tables may be
+ # either compressed or fully resolved. If compressed
+ # (the resolution of the table is less than that of the
+ # range data, e.g., for floating point lookup tables) a
+ # LUT bin may have as its value, in addition to the
+ # usual 0 or 1, the address of an interpreter subprogram
+ # to be executed to test data values mapping to that bin.
+ # The subprogram pointed to may in turn be another lookup
+ # table, hence in the general case a tree of lookup tables
+ # and little code segments may be compiled to implement
+ # a complex range list test.
+
+ # Get the data range of the lookup table.
+ xmin = XS(1)
+ if (IS_LEFT$T(xmin))
+ xmin = XE(1)
+ xmax = XE(nranges)
+ if (IS_RIGHT$T(xmax))
+ xmax = XS(nranges)
+
+ # Get the lookup table size. Use a fully resolved table
+ # if the data is integer and the number of bins required
+ # is modest.
+
+ $if (datatype == i)
+ nbins = xmax - xmin + 1
+ if (nbins > EX_MAXFRLUTLEN(ex))
+ nbins = min (EX_MAXRRLUTLEN(ex),
+ nranges * EX_LUTSCALE(ex))
+ $else
+ nbins = min (EX_MAXRRLUTLEN(ex), nranges * EX_LUTSCALE(ex))
+ $endif
+
+ # Determine the mapping from data space to table space.
+ xoffset = xmin
+ $if (datatype == i)
+ xscale = nbins / (xmax - xmin + 1)
+ $else
+ xscale = nbins / (xmax - xmin)
+ $endif
+
+ # Allocate and initialize the lookup table descriptor.
+ lt = qpex_dballoc (ex, LEN_LTDES, TY_STRUCT)
+ call calloc (lut, nbins, TY_SHORT)
+
+ LT_NEXT(lt) = EX_LTHEAD(ex)
+ EX_LTHEAD(ex) = lt
+ LT_TYPE(lt) = TY_PIXEL
+ LT_LUTP(lt) = lut
+ LT_NBINS(lt) = nbins
+ LT_$T0(lt) = xoffset
+ LT_$TS(lt) = xscale
+ LT_LEFT(lt) = btoi (IS_LEFT$T(XS(1)))
+ LT_RIGHT(lt) = btoi (IS_RIGHT$T(XE(nranges)))
+
+ # Compile the LUTX test instruction. Save a back pointer
+ # to the instruction so that we can edit the jump field in
+ # case a subprogram is compiled after the LUTXt.
+
+ lutx = qpex_pbpos (ex)
+ if (dtype == TY_SHORT)
+ call qpex_pbpin (ex, LUTXS, offset, lt, 0)
+ else
+ call qpex_pbpin (ex, LUTX$T, offset, lt, 0)
+
+ xp = 1
+ bin = 1
+continue_
+ n_xs = xs + nranges
+ n_xe = xe + nranges
+
+ # Initialize the lookup table.
+ do i = bin, nbins {
+ x1 = (i-1) / xscale + xoffset
+ $if (datatype == i)
+ x2 = i / xscale + xoffset - 1
+ $else
+ x2 = i / xscale + xoffset
+ $endif
+
+ # Get sub-rangelist for range x1:x2.
+ n_nranges = qpex_sublist$t (x1, x2,
+ Mem$t[xs], Mem$t[xe], nranges, xp,
+ Mem$t[n_xs], Mem$t[n_xe])
+
+ if (n_nranges <= 0) {
+ Mems[lut+i-1] = 0
+
+ } else if (n_nranges == 1 && IS_LEFT$T(Mem$t[n_xs]) &&
+ IS_RIGHT$T(Mem$t[n_xe])) {
+
+ Mems[lut+i-1] = 1
+
+ } else {
+ # Compile the sub-rangelist as a subprogram.
+
+ # First set the LUT bin to point to the subprogram.
+ # We cannot use the IP directly here since the LUT
+ # bins are short integer, so store the offset into
+ # the pb instead (guaranteed to be >= 4).
+
+ Mems[lut+i-1] = qpex_pbpos(ex) - pb
+
+ # Push a new context.
+ level = level + 1
+ if (level > MAX_LEVELS) {
+ call eprintf ("%s: ")
+ call pargstr (qpexwarn)
+ call eprintf ("Excessive LUT nesting\n")
+ goto error_
+ }
+
+ # Save current LUT compilation context.
+ sv_xs[level] = xs
+ sv_xe[level] = xe
+ sv_xp[level] = xp
+ sv_xoffset[level] = xoffset
+ sv_xscale[level] = xscale
+ sv_nranges[level] = nranges
+ sv_lt[level] = lt
+ sv_bin[level] = i
+ sv_nbins[level] = nbins
+ sv_lut[level] = lut
+ sv_lutx[level] = lutx
+
+ # Set up context for the new rangelist.
+ xs = n_xs
+ xe = n_xe
+ nranges = n_nranges
+
+ goto next_
+ }
+ }
+
+ # Compile a test and exit instruction if the LUT calls any
+ # subprograms.
+
+ if (qpex_pbpos(ex) - lutx > LEN_INSTRUCTION)
+ call qpex_pbpin (ex, XIFF, 0, 0, 0)
+ }
+resume_
+ # Resume lookup table compilation if exiting due to LUT-bin
+ # subprogram compilation.
+
+ if (level > 0) {
+ # Pop saved context.
+ xs = sv_xs[level]
+ xe = sv_xe[level]
+ xp = sv_xp[level]
+ xoffset = sv_xoffset[level]
+ xscale = sv_xscale[level]
+ nranges = sv_nranges[level]
+ lt = sv_lt[level]
+ bin = sv_bin[level]
+ nbins = sv_nbins[level]
+ lut = sv_lut[level]
+ lutx = sv_lutx[level]
+
+ # Compile a return from subprogram.
+ call qpex_pbpin (ex, RET, 0, 0, 0)
+
+ # Patch up the original LUTX instruction to jump over the
+ # subprogram we have just finished compiling.
+
+ IARG3(lutx) = qpex_pbpos (ex)
+
+ # Resume compilation at the next LUT bin.
+ bin = bin + 1
+ level = level - 1
+ goto continue_
+ }
+ } until (level <= 0)
+
+ # Finish setting up the eterm descriptor.
+ ET_NINSTR(et) = (qpex_pbpos(ex) - ET_PROGPTR(et)) / LEN_INSTRUCTION
+
+ return (OK)
+error_
+ call qpex_free (ex, pb_save, db_save)
+ return (ERR)
+end
diff --git a/sys/qpoe/qpexdata.x b/sys/qpoe/qpexdata.x
new file mode 100644
index 00000000..1cfc7810
--- /dev/null
+++ b/sys/qpoe/qpexdata.x
@@ -0,0 +1,210 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <mach.h>
+include "qpex.h"
+
+.help qpexdata
+.nf --------------------------------------------------------------------------
+QPEXDATA -- Data management package for QPEX. The QPEX data structures
+consist of the QPEX descriptor and two main data buffers, the program buffer
+(pb), containing the instructions to be executed (interpreted) to evaluate
+an expression, and the data buffer (db), containing assorted data structures,
+e.g., the linked list of expression term descriptors, the lookup table
+descriptors, storage for DOUBLE data appearing in the compiled expression,
+and so on. The program and data buffers are dynamically allocated but are
+not relocatable, so to absolute pointers may be used to reference the objects
+therein (hence, runtime overflow is possible).
+
+During expression compilation the following routines are used to add data
+objects to the program and data buffers:
+
+ qpex_mark (ex, pb_save, db_save)
+ qpex_free (ex, pb_save, db_save)
+
+ ip = qpex_pbpos (ex)
+ qpex_pbpin (ex, opcode, arg1, arg2, arg3)
+
+ ptr = qpex_dbpstr (ex, strval)
+ intval = qpex_refd (ex, dval)
+ ptr = qpex_dballoc (ex, nelem, dtype)
+
+QPEX_MARK and QPEX_FREE are used to mark the current tops of the two buffers
+and subspequently free storage back to that point, e.g., for error recovery
+following detection of a compilation error. QPEX_PBPOS returns a pointer to
+the location in the program buffer where next instruction will be placed.
+QPEX_PBPIN compiles an instruction at that location.
+
+The main storage allocator for the data buffer is QPEX_DBALLOC, which allocates
+a properly aligned buffer of the indicated type in the data buffer, and returns
+a pointer of the same type as the function value. QPEX_DBPSTR stores a string
+constant in the data buffer and returns a pointer to the stored string.
+QPEX_REFD stores the given type double constant in the data buffer and returns
+(as an integer) a pointer to the stored value (this is necessary to permit
+only SZ_INT argument fields in instructions).
+.endhelp ---------------------------------------------------------------------
+
+
+# QPEX_MARK -- Mark the top of the program and data buffers.
+
+procedure qpex_mark (ex, pb_save, db_save)
+
+pointer ex #I QPEX descriptor
+pointer pb_save, db_save #O saved pointers
+
+begin
+ pb_save = EX_PBOP(ex)
+ db_save = EX_DBOP(ex)
+end
+
+
+# QPEX_FREE -- Free storage back to the marked points.
+
+procedure qpex_free (ex, pb_save, db_save)
+
+pointer ex #I QPEX descriptor
+pointer pb_save, db_save #I saved pointers
+
+pointer top, prev, lt, et
+pointer coerce()
+
+begin
+ # Free space in program buffer.
+ call aclri (Memi[pb_save], EX_PBTOP(ex) - pb_save)
+ EX_PBOP(ex) = pb_save
+
+ # Free space in the data buffer. Prune the LUT and ETERM lists
+ # and then reset the data buffer pointer.
+
+ # The LT list is backward linked from the most recent entry.
+ top = coerce (db_save, TY_CHAR, TY_STRUCT)
+ for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt))
+ if (lt >= top) {
+ call mfree (LT_LUTP(lt), TY_SHORT)
+ EX_LTHEAD(ex) = LT_NEXT(lt)
+ }
+
+ # The ET list is forward linked from the first entry.
+ prev = NULL
+ for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et))
+ if (et >= top) {
+ if (prev != NULL)
+ ET_NEXT(prev) = NULL
+ EX_ETTAIL(ex) = prev
+ break
+ }
+
+ EX_DBOP(ex) = db_save
+end
+
+
+# QPEX_PBPOS -- Return a pointer to the program buffer location where the
+# next instruction to be compiled will be located.
+
+pointer procedure qpex_pbpos (ex)
+
+pointer ex #I QPEX descriptor
+
+begin
+ return (EX_PBOP(ex))
+end
+
+
+# QPEX_PBPIN -- Add an insruction at the end of the program buffer.
+
+procedure qpex_pbpin (ex, opcode, arg1, arg2, arg3)
+
+pointer ex #I QPEX descriptor
+int opcode #I instruction opcode
+int arg1,arg2,arg3 #I instruction data fields (typeless)
+
+pointer op
+errchk syserr
+
+begin
+ op = EX_PBOP(ex)
+ if (op >= EX_PBTOP(ex))
+ call syserr (SYS_QPEXPBOVFL)
+
+ OPCODE(op) = opcode
+ IARG1(op) = arg1
+ IARG2(op) = arg2
+ IARG3(op) = arg3
+
+ EX_PBOP(ex) = op + LEN_INSTRUCTION
+end
+
+
+# QPEX_DBPSTR -- Store a string constant in the data buffer, returning a
+# pointer to the stored string as the function value.
+
+pointer procedure qpex_dbpstr (ex, strval)
+
+pointer ex #I QPEX descriptor
+char strval[ARB] #I string to be stored
+
+pointer op
+int nchars
+int strlen()
+errchk syserr
+
+begin
+ op = EX_DBOP(ex)
+ nchars = strlen (strval) + 1
+
+ if (op + nchars >= EX_DBTOP(ex))
+ call syserr (SYS_QPEXDBOVFL)
+
+ call strcpy (strval, Memc[op], nchars)
+ EX_DBOP(ex) = op + nchars
+
+ return (op)
+end
+
+
+# QPEX_REFD -- Reference a type DOUBLE datum, returning (as an integer) a
+# pointer to the double value, which is stored in the data buffer.
+
+int procedure qpex_refd (ex, value)
+
+pointer ex #I QPEX descriptor
+double value #I double value
+
+pointer dp
+pointer qpex_dballoc()
+errchk qpex_dballoc
+
+begin
+ dp = qpex_dballoc (ex, 1, TY_DOUBLE)
+ Memd[dp] = value
+ return (dp)
+end
+
+
+# QPEX_DBALLOC -- Allocate storage of the indicated type in the data
+# buffer, returning a typed pointer to the buffer. The buffer is fully
+# aligned.
+
+pointer procedure qpex_dballoc (ex, nelem, dtype)
+
+pointer ex #I QPEX descriptor
+int nelem #I amount of storage desired
+int dtype #I datatype of the storage element
+
+pointer op, top
+pointer coerce()
+int sizeof()
+errchk syserr
+
+begin
+ op = EX_DBOP(ex)
+ while (mod (op-1, SZ_DOUBLE) != 0)
+ op = op + 1
+
+ top = op + nelem * sizeof(dtype)
+ if (top >= EX_DBTOP(ex))
+ call syserr (SYS_QPEXDBOVFL)
+
+ EX_DBOP(ex) = top
+ return (coerce (op, TY_CHAR, dtype))
+end
diff --git a/sys/qpoe/qpexdebug.x b/sys/qpoe/qpexdebug.x
new file mode 100644
index 00000000..26f63e8b
--- /dev/null
+++ b/sys/qpoe/qpexdebug.x
@@ -0,0 +1,441 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpexset.h>
+include <mach.h>
+include "qpex.h"
+include "qpoe.h"
+
+define NLUTPERLINE 15
+define SZ_TEXT 4
+define SZ_FILTERBUF 32768
+
+# QPEX_DEBUG -- Output text describing the state and contents of the QPEX
+# descriptor (compiled event attribute filter).
+
+procedure qpex_debug (ex, out, what)
+
+pointer ex #I QPEX descriptor
+int out #I output stream
+int what #I bitflags defining what to print
+
+char binval[SZ_TEXT]
+pointer sp, text, label, lutp, et, lt, pb, ip
+int neterms, lutno, proglen, dest, nout, ch, i
+int qpex_getfilter()
+define lut_ 91
+
+begin
+ call smark (sp)
+
+ neterms = 0
+ for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et))
+ neterms = neterms + 1
+ proglen = (EX_PBOP(ex) - EX_PB(ex)) / LEN_INSTRUCTION
+
+ # Print summary information.
+ if (and (what, QPEXD_SUMMARY) != 0) {
+ call fprintf (out,
+ "QPEX_DEBUG: ex=%xX, neterms=%d, proglen=%d/%d\n")
+ call pargi (ex)
+ call pargi (neterms)
+ call pargi (proglen)
+ call pargi ((EX_PBTOP(ex) - EX_PB(ex)) / LEN_INSTRUCTION)
+
+ call fprintf (out, "pb=%xX, pbtop=%xX, pbop=%xX, start=%xX\n")
+ call pargi (EX_PB(ex))
+ call pargi (EX_PBTOP(ex))
+ call pargi (EX_PBOP(ex))
+ call pargi (EX_START(ex))
+
+ call fprintf (out, "db=%xX, dbtop=%xX, dbop=%xX, datalen=%d/%d\n")
+ call pargi (EX_DB(ex))
+ call pargi (EX_DBTOP(ex))
+ call pargi (EX_DBOP(ex))
+ call pargi (EX_DBOP(ex) - EX_DB(ex))
+ call pargi (EX_DBTOP(ex) - EX_DB(ex))
+
+ call fprintf (out, "max_frlutlen=%d, max_rrlutlen=%d, ")
+ call pargi (EX_MAXFRLUTLEN(ex))
+ call pargi (EX_MAXRRLUTLEN(ex))
+ call fprintf (out, "lut_scale=%d, lut_minranges=%d\n")
+ call pargi (EX_LUTSCALE(ex))
+ call pargi (EX_LUTMINRANGES(ex))
+
+ call fprintf (out, "ethead=%xX, ettail=%xX, lthead=%xX\n")
+ call pargi (EX_ETHEAD(ex))
+ call pargi (EX_ETTAIL(ex))
+ call pargi (EX_LTHEAD(ex))
+ }
+
+ # Regenerate and print the compiled expression.
+ if (and (what, QPEXD_SHOWEXPR) != 0) {
+ call salloc (text, SZ_FILTERBUF, TY_CHAR)
+ call fprintf (out,
+ "==================== expr ========================\n")
+ if (qpex_getfilter (ex, Memc[text], SZ_FILTERBUF) > 0) {
+ call putline (out, Memc[text])
+ call fprintf (out, "\n")
+ }
+ }
+
+ # Decode the compiled program (print assembled instructions).
+ if (and (what, QPEXD_PROGRAM) != 0) {
+ pb = EX_PB(ex)
+
+ call salloc (label, proglen+1, TY_INT)
+ call aclri (Memi[label], proglen+1)
+
+ # Flag those instructions which are the destinations of branches.
+ do i = 1, proglen {
+ ip = pb + (i - 1) * LEN_INSTRUCTION
+ switch (OPCODE(ip)) {
+ case GOTO:
+ dest = (IARG1(ip) - pb) / LEN_INSTRUCTION
+ Memi[label+dest] = YES
+ case LUTXS, LUTXI, LUTXR, LUTXD:
+ if (IARG3(ip) == NULL)
+ dest = i
+ else
+ dest = (IARG3(ip) - pb) / LEN_INSTRUCTION
+ Memi[label+dest] = YES
+ }
+ }
+
+ # Do the same for code segments pointed to by lookup tables.
+ for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt)) {
+ lutp = LT_LUTP(lt)
+ do i = 0, LT_NBINS(lt) - 1 {
+ dest = Mems[lutp+i]
+ if (dest > 1) {
+ dest = dest / LEN_INSTRUCTION
+ Memi[label+dest] = YES
+ }
+ }
+ }
+
+ # Output the program.
+ call fprintf (out,
+ "==================== program =====================\n")
+
+ do i = 1, proglen + 1 {
+ ip = pb + (i - 1) * LEN_INSTRUCTION
+
+ # Output instruction label if target of a branch.
+ if (Memi[label+i-1] == YES) {
+ call fprintf (out, "L%d:\t")
+ call pargi (i)
+ } else
+ call fprintf (out, "\t")
+
+ # Decode and output the instruction itself.
+ switch (OPCODE(ip)) {
+ case NOP:
+ call fprintf (out, "nop")
+ case GOTO:
+ dest = (IARG1(ip) - pb) / LEN_INSTRUCTION + 1
+ call fprintf (out, "goto L%d")
+ call pargi (dest)
+ case XIFT:
+ call fprintf (out, "xift")
+ case XIFF:
+ call fprintf (out, "xiff")
+ case PASS:
+ call fprintf (out, "pass")
+ case RET:
+ call fprintf (out, "ret")
+
+ case LDSI:
+ call fprintf (out, "ldsi\t(%d)")
+ call pargi (IARG1(ip))
+ case LDII:
+ call fprintf (out, "ldii\t(%d)")
+ call pargi (IARG1(ip))
+ case LDRR:
+ call fprintf (out, "ldrr\t(%d)")
+ call pargi (IARG1(ip))
+ case LDRD:
+ call fprintf (out, "ldrd\t(%d)")
+ call pargi (IARG1(ip))
+ case LDDD:
+ call fprintf (out, "lddd\t(%d)")
+ call pargi (IARG1(ip))
+
+ case BTTI:
+ call fprintf (out, "btti\t%oB")
+ call pargi (IARG1(ip))
+ case EQLI:
+ call fprintf (out, "eqli\t%d")
+ call pargi (IARG1(ip))
+ case EQLR:
+ call fprintf (out, "eqlr\t%g")
+ call pargr (RARG1(ip))
+ case EQLD:
+ call fprintf (out, "eqld\t%g")
+ call pargd (DARG1(ip))
+ case LEQI:
+ call fprintf (out, "leqi\t%d")
+ call pargi (IARG1(ip))
+ case LEQR:
+ call fprintf (out, "leqr\t%g")
+ call pargr (RARG1(ip))
+ case LEQD:
+ call fprintf (out, "leqd\t%g")
+ call pargd (DARG1(ip))
+ case GEQI:
+ call fprintf (out, "geqi\t%d")
+ call pargi (IARG1(ip))
+ case GEQR:
+ call fprintf (out, "geqr\t%g")
+ call pargr (RARG1(ip))
+ case GEQD:
+ call fprintf (out, "geqd\t%g")
+ call pargd (DARG1(ip))
+
+ case RNGI:
+ call fprintf (out, "rngi\t%d, %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case RNGR:
+ call fprintf (out, "rngr\t%g, %g")
+ call pargr (RARG1(ip))
+ call pargr (RARG2(ip))
+ case RNGD:
+ call fprintf (out, "rngd\t%g, %g")
+ call pargd (DARG1(ip))
+ call pargd (DARG2(ip))
+
+ case BTTXS:
+ call fprintf (out, "bttxs\t(%d), %oB")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case BTTXI:
+ call fprintf (out, "bttxi\t(%d), %oB")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+
+ case NEQXS:
+ call fprintf (out, "neqxs\t(%d), %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case NEQXI:
+ call fprintf (out, "neqxi\t(%d), %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case NEQXR:
+ call fprintf (out, "neqxr\t(%d), %g")
+ call pargi (IARG1(ip))
+ call pargr (RARG2(ip))
+ case NEQXD:
+ call fprintf (out, "neqxd\t(%d), %g")
+ call pargi (IARG1(ip))
+ call pargd (DARG2(ip))
+
+ case EQLXS:
+ call fprintf (out, "eqlxs\t(%d), %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case EQLXI:
+ call fprintf (out, "eqlxi\t(%d), %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case EQLXR:
+ call fprintf (out, "eqlxr\t(%d), %g")
+ call pargi (IARG1(ip))
+ call pargr (RARG2(ip))
+ case EQLXD:
+ call fprintf (out, "eqlxd\t(%d), %g")
+ call pargi (IARG1(ip))
+ call pargd (DARG2(ip))
+
+ case LEQXS:
+ call fprintf (out, "leqxs\t(%d), %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case LEQXI:
+ call fprintf (out, "leqxi\t(%d), %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case LEQXR:
+ call fprintf (out, "leqxr\t(%d), %g")
+ call pargi (IARG1(ip))
+ call pargr (RARG2(ip))
+ case LEQXD:
+ call fprintf (out, "leqxd\t(%d), %g")
+ call pargi (IARG1(ip))
+ call pargd (DARG2(ip))
+
+ case GEQXS:
+ call fprintf (out, "geqxs\t(%d), %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case GEQXI:
+ call fprintf (out, "geqxi\t(%d), %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ case GEQXR:
+ call fprintf (out, "geqxr\t(%d), %g")
+ call pargi (IARG1(ip))
+ call pargr (RARG2(ip))
+ case GEQXD:
+ call fprintf (out, "geqxd\t(%d), %g")
+ call pargi (IARG1(ip))
+ call pargd (DARG2(ip))
+
+ case RNGXS:
+ call fprintf (out, "rngxs\t(%d), %d, %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ call pargi (IARG3(ip))
+ case RNGXI:
+ call fprintf (out, "rngxi\t(%d), %d, %d")
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ call pargi (IARG3(ip))
+ case RNGXR:
+ call fprintf (out, "rngxr\t(%d), %g, %g")
+ call pargi (IARG1(ip))
+ call pargr (RARG2(ip))
+ call pargr (RARG3(ip))
+ case RNGXD:
+ call fprintf (out, "rngxd\t(%d), %g, %g")
+ call pargi (IARG1(ip))
+ call pargd (DARG2(ip))
+ call pargd (DARG3(ip))
+
+ case LUTXS:
+ ch = 's'
+ goto lut_
+ case LUTXI:
+ ch = 'i'
+ goto lut_
+ case LUTXR:
+ ch = 'r'
+ goto lut_
+ case LUTXD:
+ ch = 'd'
+lut_ call fprintf (out, "lutx%c\t(%d), %xX, L%d")
+ call pargi (ch)
+ call pargi (IARG1(ip))
+ call pargi (IARG2(ip))
+ if (IARG3(ip) != NULL)
+ call pargi ((IARG3(ip) - pb) / LEN_INSTRUCTION + 1)
+ else
+ call pargi (i + 1)
+ }
+
+ call fprintf (out, "\n")
+ }
+ }
+
+ # Output expression terms list.
+ if (and (what, QPEXD_ETLIST) != 0) {
+ call fprintf (out,
+ "==================== eterms ======================\n")
+ if (neterms > 0) {
+ call fprintf (out,
+ " N TYPE OFF IP LEN DEL ATTRIBUTE OP EXPR\n")
+ neterms = 0
+ for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) {
+ neterms = neterms + 1
+ call fprintf (out,
+ "%2d %4d %3d %3d %4d %3d %9.9s %2s ")
+ call pargi (neterms)
+ call pargi (ET_ATTTYPE(et))
+ call pargi (ET_ATTOFF(et))
+ call pargi ((ET_PROGPTR(et) - pb) / LEN_INSTRUCTION + 1)
+ call pargi (ET_NINSTR(et))
+ call pargi (ET_DELETED(et))
+ call pargstr (Memc[ET_ATNAME(et)])
+ call pargstr (Memc[ET_ASSIGNOP(et)])
+ call putline (out, Memc[ET_EXPRTEXT(et)])
+ call putline (out, "\n")
+ }
+ }
+ }
+
+ # Output lookup table list.
+ if (and (what, QPEXD_LTLIST+QPEXD_SHOWLUTS) != 0) {
+ if (EX_LTHEAD(ex) != NULL) {
+ call fprintf (out,
+ "==================== lutlist =====================\n")
+
+ # Output column labels.
+ call fprintf (out,
+ " N LT LUTP TYPE NBINS L R %*wZERO SCALE\n")
+ if (LT_TYPE(EX_LTHEAD(ex)) == TY_DOUBLE)
+ call pargi (NDIGITS_DP - 4)
+ else
+ call pargi (NDIGITS_RP - 4)
+
+ # Output lookup table descriptors.
+ lutno = 0
+ for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt)) {
+ lutno = lutno + 1
+ call fprintf (out, "%2d %6x %6x %4d %5d %d %d %*g %g\n")
+ call pargi (lutno)
+ call pargi (lt)
+ call pargi (LT_LUTP(lt))
+ call pargi (LT_TYPE(lt))
+ call pargi (LT_NBINS(lt))
+ call pargi (LT_LEFT(lt))
+ call pargi (LT_RIGHT(lt))
+
+ switch (LT_TYPE(lt)) {
+ case TY_INT:
+ call pargi (NDIGITS_RP)
+ call pargr (LT_I0(lt))
+ call pargr (LT_IS(lt))
+ case TY_REAL:
+ call pargi (NDIGITS_RP)
+ call pargr (LT_R0(lt))
+ call pargr (LT_RS(lt))
+ case TY_DOUBLE:
+ call pargi (NDIGITS_DP)
+ call pargd (LT_D0(lt))
+ call pargd (LT_DS(lt))
+ }
+ }
+ }
+
+ # Dump the lookup table data.
+ if (and (what, QPEXD_SHOWLUTS) != 0) {
+ lutno = 0
+ for (lt=EX_LTHEAD(ex); lt != NULL; lt=LT_NEXT(lt)) {
+ lutno = lutno + 1
+ lutp = LT_LUTP(lt)
+ call fprintf (out,
+ "================== LUT %d (%x) ==================\n")
+ call pargi (lutno)
+ call pargi (lutp)
+ nout = 0
+ do i = 0, LT_NBINS(lt) - 1 {
+ if (i == 0 || nout >= NLUTPERLINE) {
+ if (i > 0)
+ call fprintf (out, "\n")
+ call fprintf (out, "%04d")
+ call pargi (i)
+ nout = 0
+ }
+
+ # Print the bin value as 0, 1, or a statement label.
+ dest = Mems[lutp+i]
+ if (dest <= 1) {
+ call fprintf (out, " %4d")
+ call pargi (dest)
+ } else {
+ call sprintf (binval, SZ_TEXT, "L%d")
+ call pargi (dest / LEN_INSTRUCTION + 1)
+ call fprintf (out, " %4s")
+ call pargstr (binval)
+ }
+
+ nout = nout + 1
+ }
+ if (nout > 0)
+ call fprintf (out, "\n")
+ }
+ }
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpexdel.x b/sys/qpoe/qpexdel.x
new file mode 100644
index 00000000..ce4da432
--- /dev/null
+++ b/sys/qpoe/qpexdel.x
@@ -0,0 +1,58 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "qpex.h"
+
+# QPEX_DELETE -- Delete any previously compiled expression terms for the
+# event attribute with the given offset and datatype. Only terms up to and
+# including ET_LAST are affected (allowing deletion while compiling additional
+# terms).
+
+procedure qpex_delete (ex, et_last, offset, dtype)
+
+pointer ex #I QPEX descriptor
+pointer et_last #I last expression term to be edited
+int offset #I typed offset of attribute in event struct
+int dtype #I datatype of attribute
+
+pointer et, ip
+int ninstr, i
+
+begin
+ if (et_last == NULL)
+ return
+
+ for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) {
+ # Skip over already deleted terms or terms for other attributes.
+ if (ET_DELETED(et) == YES)
+ next
+ else if (ET_ATTOFF(et) != offset || ET_ATTTYPE(et) != dtype)
+ next
+
+ # Physically and logically delete the term. Edit the program
+ # buffer and replace the compiled sequence of instructions by
+ # a GOTO followed by a series of NO-OPs.
+
+ ip = ET_PROGPTR(et)
+ ninstr = ET_NINSTR(et)
+
+ OPCODE(ip) = GOTO
+ IARG1(ip) = ip + ninstr * LEN_INSTRUCTION
+ IARG2(ip) = NULL
+ IARG3(ip) = NULL
+
+ do i = 2, ninstr {
+ ip = ET_PROGPTR(et) + (i-1) * LEN_INSTRUCTION
+ OPCODE(ip) = NOP
+ IARG1(ip) = NULL
+ IARG2(ip) = NULL
+ IARG3(ip) = NULL
+ }
+
+ # Flag the eterm as deleted.
+ ET_DELETED(et) = YES
+
+ if (et == et_last)
+ break
+ }
+end
diff --git a/sys/qpoe/qpexeval.x b/sys/qpoe/qpexeval.x
new file mode 100644
index 00000000..5e120296
--- /dev/null
+++ b/sys/qpoe/qpexeval.x
@@ -0,0 +1,362 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "qpex.h"
+
+define RTOL (EPSILONR * 10.0) # (only useful for normalized numbers)
+define DTOL (EPSILOND * 10.0)
+
+# QPEX_EVALUATE -- Evaluate the compiled event-attribute expression for the
+# given seqeuence of event structs. Expression evaluation for each event
+# terminates as soon as an attribute test fails. If all attribute tests
+# succeed (i.e., the full expression is evaluated or the evaluate function
+# runs to completion) then the event pointer is put on the output list O_EV,
+# indicating that the event satisfies the given selection expression.
+# The function value is the number of events which passed the filter.
+# The time required to evaluate an expression depends upon the complexity of
+# the expression to be evaluated, and the fraction of events which fail the
+# test (a fail is determined quicker than a pass - attribute tests likely to
+# fail should appear first in the expression).
+
+int procedure qpex_evaluate (ex, i_ev, o_ev, nev)
+
+pointer ex #I QPEX descriptor (expression)
+pointer i_ev[nev] #I array of pointers to event structs
+pointer o_ev[nev] #O receives the pointers of the passed events
+int nev #I number of input events
+
+int i0 # integer data register
+real r0 # real data register
+double d0 # double data register
+bool pass # expression value
+int npass # number of events which pass expr
+
+real rbin
+bool pv_save[MAX_LEVELS]
+pointer ip_save[MAX_LEVELS]
+pointer lt, ev, ev_i, ev_r, ev_d, ip
+int level, bin, i, j, v
+
+define lut_ 91
+define ret_ 92
+define ev_s ev
+
+begin
+ npass = 0
+
+ do j = 1, nev {
+ pass = false
+ ev = i_ev[j]
+
+ # Get event struct pointers of various types.
+ ev_d = (ev - 1) * SZ_SHORT / SZ_DOUBLE + 1
+ ev_i = (ev - 1) * SZ_SHORT / SZ_INT + 1
+ ev_r = ev_i
+
+ # Execute each compiled instruction in sequence until the value
+ # of the compiled attribute-value expression is known. The call
+ # stack level is used to keep track of subroutine calls
+ # (subroutines are used to evaluate the indeterminate cells of
+ # compressed lookup tables).
+
+ # Notes on expression evaluation.
+ # ---------------------------------
+ # An expression consists of 1 or more expression terms, all of
+ # which must pass the event for the event to pass the filter.
+ #
+ # An expression term consists of a range list giving a list of
+ # acceptable values or ranges of values.
+ #
+ # The compiled expression consists of a sequence of instruction
+ # blocks, one for each expression term. If the event fails to
+ # pass any expression term (instruction block) then the event
+ # fails and we are done. Instruction blocks are of three types:
+ #
+ # 1) Multiple instructions consisting of a load register,
+ # any number of register tests, then a XIFF or XIFT
+ # test at the end of the block. PASS is set to false
+ # at the beginning of the block and can be set to true
+ # by any register test to pass the event to the next
+ # expression term.
+ #
+ # 2) In simple cases the above can all be expressed as a
+ # single test-and-exit-if-false instruction. These are
+ # the "X" instructions below.
+ #
+ # 3) The lookup table (LUTX) instruction. LUTX is like
+ # case 2) except that it may compile as a sequence of
+ # many instructions, using subprograms to evaluate the
+ # value of LUT bins. LUTs may nest. When lookup table
+ # evaluation is complete the instruction branches
+ # forward to a closing XIFF which is used to test the
+ # value of PASS returned by the executed LUT-bin
+ # subprograms.
+ #
+ # The blocks of instructions corresponding to successive expression
+ # terms are executed until the PASS instruction is encountered.
+ # Execution of PASS terminates evaluation and passes the event.
+
+ ip = EX_START(ex)
+ level = 0
+
+ do i = 1, MAX_INSTRUCTIONS {
+ pragma switch_no_range_check
+ switch (OPCODE(ip)) {
+ case NOP: # null operation
+ ;
+ case GOTO: # go-to prog offset
+ ip = IARG1(ip)
+ next
+ case XIFT: # exit if true
+ if (pass) {
+ pass = false
+ goto ret_
+ }
+ case XIFF: # exit if false
+ if (!pass)
+ goto ret_
+ case PASS:
+ pass = true
+ break
+ case RET: # return from subprog
+ret_ if (level > 0) {
+ pass = (pv_save[level] || pass)
+ ip = ip_save[level]
+ level = level - 1
+ next
+ } else
+ break
+
+ case LDSI: # load registers
+ i0 = Mems[ev_s+IARG1(ip)]
+ pass = false
+ case LDII:
+ i0 = Memi[ev_i+IARG1(ip)]
+ pass = false
+ case LDRR:
+ r0 = Memr[ev_r+IARG1(ip)]
+ pass = false
+ case LDRD:
+ d0 = Memr[ev_r+IARG1(ip)]
+ pass = false
+ case LDDD:
+ d0 = Memd[ev_d+IARG1(ip)]
+ pass = false
+
+ case BTTI: # register tests
+ pass = pass || (and (i0, IARG1(ip)) != 0)
+ case EQLI:
+ pass = pass || (i0 == IARG1(ip))
+ case EQLR:
+ pass = pass || (abs(r0 - RARG1(ip)) < RTOL)
+ case EQLD:
+ pass = pass || (abs(d0 - DARG1(ip)) < DTOL)
+ case LEQI:
+ pass = pass || (i0 <= IARG1(ip))
+ case LEQR:
+ pass = pass || (r0 <= RARG1(ip))
+ case LEQD:
+ pass = pass || (d0 <= DARG1(ip))
+ case GEQI:
+ pass = pass || (i0 >= IARG1(ip))
+ case GEQR:
+ pass = pass || (r0 >= RARG1(ip))
+ case GEQD:
+ pass = pass || (d0 >= DARG1(ip))
+ case RNGI:
+ pass = pass || (i0 >= IARG1(ip) && i0 <= IARG2(ip))
+ case RNGR:
+ pass = pass || (r0 >= RARG1(ip) && r0 <= RARG2(ip))
+ case RNGD:
+ pass = pass || (d0 >= DARG1(ip) && d0 <= DARG2(ip))
+
+ case BTTXS: # load, test, and
+ i0 = Mems[ev_s+IARG1(ip)] # exit if false
+ pass = (and (i0, IARG2(ip)) != 0)
+ if (!pass)
+ goto ret_
+ case BTTXI:
+ pass = (and (Memi[ev_i+IARG1(ip)], IARG2(ip)) != 0)
+ if (!pass)
+ goto ret_
+
+ case NEQXS:
+ pass = (Mems[ev_s+IARG1(ip)] != IARG2(ip))
+ if (!pass)
+ goto ret_
+ case NEQXI:
+ pass = (Memi[ev_i+IARG1(ip)] != IARG2(ip))
+ if (!pass)
+ goto ret_
+ case NEQXR:
+ pass = (abs(Memr[ev_r+IARG1(ip)] - RARG2(ip)) > RTOL)
+ if (!pass)
+ goto ret_
+ case NEQXD:
+ pass = (abs(Memd[ev_d+IARG1(ip)] - DARG2(ip)) > DTOL)
+ if (!pass)
+ goto ret_
+
+ case EQLXS:
+ pass = (Mems[ev_s+IARG1(ip)] == IARG2(ip))
+ if (!pass)
+ goto ret_
+ case EQLXI:
+ pass = (Memi[ev_i+IARG1(ip)] == IARG2(ip))
+ if (!pass)
+ goto ret_
+ case EQLXR:
+ pass = (abs(Memr[ev_r+IARG1(ip)] - RARG2(ip)) <= RTOL)
+ if (!pass)
+ goto ret_
+ case EQLXD:
+ pass = (abs(Memd[ev_d+IARG1(ip)] - DARG2(ip)) <= DTOL)
+ if (!pass)
+ goto ret_
+
+ case LEQXS:
+ pass = (Mems[ev_s+IARG1(ip)] <= IARG2(ip))
+ if (!pass)
+ goto ret_
+ case LEQXI:
+ pass = (Memi[ev_i+IARG1(ip)] <= IARG2(ip))
+ if (!pass)
+ goto ret_
+ case LEQXR:
+ pass = (Memr[ev_r+IARG1(ip)] <= RARG2(ip))
+ if (!pass)
+ goto ret_
+ case LEQXD:
+ pass = (Memd[ev_d+IARG1(ip)] <= DARG2(ip))
+ if (!pass)
+ goto ret_
+
+ case GEQXS:
+ pass = (Mems[ev_s+IARG1(ip)] >= IARG2(ip))
+ if (!pass)
+ goto ret_
+ case GEQXI:
+ pass = (Memi[ev_i+IARG1(ip)] >= IARG2(ip))
+ if (!pass)
+ goto ret_
+ case GEQXR:
+ pass = (Memr[ev_r+IARG1(ip)] >= RARG2(ip))
+ if (!pass)
+ goto ret_
+ case GEQXD:
+ pass = (Memd[ev_d+IARG1(ip)] >= DARG2(ip))
+ if (!pass)
+ goto ret_
+
+ case RNGXS:
+ i0 = Mems[ev_s+IARG1(ip)]
+ pass = (i0 >= IARG2(ip) && i0 <= IARG3(ip))
+ if (!pass)
+ goto ret_
+ case RNGXI:
+ i0 = Memi[ev_i+IARG1(ip)]
+ pass = (i0 >= IARG2(ip) && i0 <= IARG3(ip))
+ if (!pass)
+ goto ret_
+ case RNGXR:
+ r0 = Memr[ev_r+IARG1(ip)]
+ pass = (r0 >= RARG2(ip) && r0 <= RARG3(ip))
+ if (!pass)
+ goto ret_
+ case RNGXD:
+ d0 = Memd[ev_d+IARG1(ip)]
+ pass = (d0 >= DARG2(ip) && d0 <= DARG3(ip))
+ if (!pass)
+ goto ret_
+
+ case LUTXS: # lookup tables
+ i0 = Mems[ev_s+IARG1(ip)]
+ lt = IARG2(ip)
+ rbin = (i0 - int(LT_I0(lt))) * LT_IS(lt)
+ goto lut_
+ case LUTXI:
+ i0 = Memi[ev_i+IARG1(ip)]
+ lt = IARG2(ip)
+ rbin = (i0 - int(LT_I0(lt))) * LT_IS(lt)
+ goto lut_
+ case LUTXR:
+ r0 = Memr[ev_r+IARG1(ip)]
+ lt = IARG2(ip)
+ rbin = (r0 - LT_R0(lt)) * LT_RS(lt)
+ goto lut_
+ case LUTXD:
+ d0 = Memd[ev_d+IARG1(ip)]
+ lt = IARG2(ip)
+ rbin = (d0 - LT_D0(lt)) * LT_DS(lt)
+lut_
+ # Common code for any lookup table.
+ if (rbin <= 0)
+ v = LT_LEFT(lt)
+ else {
+ bin = int(rbin) + 1
+ if (bin > LT_NBINS(lt))
+ v = LT_RIGHT(lt)
+ else
+ v = LT_LUT(lt,bin)
+ }
+
+ # Table value may be 0, 1, or indeterminate, i.e., the
+ # offset of a subprogram to be called to evaluate the
+ # subrangelist for that bin.
+
+ if (v == 0) {
+ # Table value is zero, !pass, all done.
+ pass = false
+ goto ret_
+
+ } else if (v > 1) {
+ # Table value is indeterminate and depends on the
+ # data value. Call subroutine to evaluate subrange.
+ # At level=0 where we are starting to evaluate an
+ # independent expression term we must initialize pass
+ # to false before entering the subprogram instruction
+ # sequence.
+
+ if (level == 0)
+ pass = false
+
+ level = level + 1
+ pv_save[level] = pass
+
+ if (IARG3(ip) != NULL)
+ ip_save[level] = IARG3(ip)
+ else
+ ip_save[level] = ip + LEN_INSTRUCTION
+
+ pass = false
+ ip = EX_PB(ex) + v
+ next
+
+ } else if (v == 1) {
+ # Table value is one, value passes this test.
+ pass = true
+ }
+
+ # Go to the jump address if set. The jump is needed
+ # to skip over any subprograms that may have been compiled
+ # after the LUTX.
+
+ if (IARG3(ip) != NULL) {
+ ip = IARG3(ip)
+ next
+ }
+ }
+
+ # Advance to the next instruction.
+ ip = ip + LEN_INSTRUCTION
+ }
+
+ # Output event pointer if event passed the filter.
+ if (pass) {
+ npass = npass + 1
+ o_ev[npass] = ev
+ }
+ }
+
+ return (npass)
+end
diff --git a/sys/qpoe/qpexgetat.x b/sys/qpoe/qpexgetat.x
new file mode 100644
index 00000000..9f7b3af2
--- /dev/null
+++ b/sys/qpoe/qpexgetat.x
@@ -0,0 +1,61 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "qpex.h"
+
+# QPEX_GETATTRIBUTE -- Get the filter expression for the named attribute
+# as a text string. The length of the string is returned as the function
+# value. If the referenced QPEX descriptor does not contain any filter
+# terms for the named attribute, zero will be returned. If the expression
+# contains multiple eterms the successive terms will be delimited by
+# semicolons, e.g., "(a:b,c:d); (e:f,g)". The lists of ranges within an
+# eterm are OR-ed to produce a filter term; successive eterms are AND-ed
+# to produce the final filter (hence the example above is equivalent to
+# "(a to b OR c to d) AND (e to f OR g)").
+
+int procedure qpex_getattribute (ex, attribute, outstr, maxch)
+
+pointer ex #I QPEX descriptor
+char attribute[ARB] #I attribute name
+char outstr[maxch] #O receives the filter string
+int maxch #I max chars out
+
+pointer sp, atname, et
+int nchars, op, otop
+int gstrcpy(), qp_expandtext()
+bool strne()
+
+begin
+ call smark (sp)
+ call salloc (atname, SZ_FNAME, TY_CHAR)
+
+ # Translate attribute name, in case it is aliased.
+ nchars = qp_expandtext (EX_QP(ex), attribute, Memc[atname], SZ_FNAME)
+
+ # Construct filter expression for named attribute.
+ op = 1
+ otop = maxch + 1
+ for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) {
+ if (ET_DELETED(et) == YES)
+ next
+
+ # Skip entry if not for the named attribute.
+ if (strne (Memc[ET_ATNAME(et)], Memc[atname]))
+ next
+
+ # Add term delimiter if not first term.
+ if (op > 1) {
+ outstr[op] = ';'; op = min(otop, op + 1)
+ outstr[op] = ' '; op = min(otop, op + 1)
+ }
+
+ # The expression text (may be very large).
+ op = min (otop,
+ op + gstrcpy (Memc[ET_EXPRTEXT(et)], outstr[op], otop-op))
+ }
+ outstr[op] = EOS
+
+ # Return the string length, or zero if no filter for named attribute.
+ call sfree (sp)
+ return (op - 1)
+end
diff --git a/sys/qpoe/qpexgetfil.x b/sys/qpoe/qpexgetfil.x
new file mode 100644
index 00000000..3f1d2816
--- /dev/null
+++ b/sys/qpoe/qpexgetfil.x
@@ -0,0 +1,50 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "qpex.h"
+
+# QPEX_GETFILTER -- Return the currently active filter as a text string,
+# i.e., as a series of "attribute = expr" terms. The number of chars
+# output is returned as the function value.
+
+int procedure qpex_getfilter (ex, outstr, maxch)
+
+pointer ex #I QPEX descriptor
+char outstr[maxch] #O receives the filter string
+int maxch #I max chars out
+
+pointer et
+int op, otop
+int gstrcpy()
+
+begin
+ op = 1
+ otop = maxch + 1
+ for (et=EX_ETHEAD(ex); et != NULL; et=ET_NEXT(et)) {
+ if (ET_DELETED(et) == YES)
+ next
+
+ # Add term delimiter if not first term.
+ if (op > 1) {
+ outstr[op] = ','; op = min(otop, op + 1)
+ outstr[op] = ' '; op = min(otop, op + 1)
+ }
+
+ # Attribute name.
+ op = min (otop,
+ op + gstrcpy (Memc[ET_ATNAME(et)], outstr[op], otop-op))
+ outstr[op] = ' '; op = min(otop, op + 1)
+
+ # Assignment operator ("=" or "+=").
+ op = min (otop,
+ op + gstrcpy (Memc[ET_ASSIGNOP(et)], outstr[op], otop-op))
+ outstr[op] = ' '; op = min(otop, op + 1)
+
+ # The expression text (may be very large).
+ op = min (otop,
+ op + gstrcpy (Memc[ET_EXPRTEXT(et)], outstr[op], otop-op))
+ }
+ outstr[op] = EOS
+
+ return (op - 1)
+end
diff --git a/sys/qpoe/qpexmodfil.x b/sys/qpoe/qpexmodfil.x
new file mode 100644
index 00000000..7df4b926
--- /dev/null
+++ b/sys/qpoe/qpexmodfil.x
@@ -0,0 +1,247 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "qpoe.h"
+include "qpex.h"
+
+# QPEX_MODFILTER -- Compile an event attribute expression to be used for event
+# attribute filtering, modifying the the current EAF as directed by the expr.
+# An event attribute expression consists of a sequence of independent terms
+# of the form "attribute = expr", e.g.,
+#
+# pha=%104B, e=100:, t=(:9,11:29,33,42:65,67:99,!(82,87),103), ...
+#
+#
+# Variants on "attr = expr" are "attr := expr" and "attr += expr". In the
+# case of :=, any expression terms already entered for the named attribute
+# will be REPLACED by the new expression. In the case of +=, the given
+# expression denotes an additional condition which the attribute must satisfy
+# to pass the filter, i.e., a new term is added to the existing filter. The
+# case = is the same as +=, i.e., the default action is to modify rather than
+# replace any existing filter.
+#
+#
+# Our function is to extract each attribute=expr term and compile it into a
+# series of instructions to be repeatedly executed (interpreted) at runtime
+# to evaluate the expression for a particular event structure. Terms are
+# compiled and evaluated in the order in which they appear in the expression
+# list (except for replacement terms), allowing the user to manually optimize
+# the filter by giving terms which are most likely to fail first.
+#
+# The expression list may contain references to predefined global or local
+# (datafile) macros, external macro files, or back-quoted CL commands for
+# which the output is to be substituted as for a macro. In all cases, macro
+# substitution is handled at a lower level in the gettok routine. In
+# particular, the logical names of the fields of the event structure are
+# implemented as predefined datafile-local macros, hence we are concerned only
+# with physical field names here. The form of a physical field name is
+# a datatype code [SIRD] followed by the decimal zero-indexed byte offset
+# of the field in the event structure, e.g., S0, S2, R4, etc. (short integer
+# field at offset 0, same at offset 2, Real*4 field at offset 4, etc.).
+#
+# The function value is OK if the expression list compiles without any errors,
+# or ERR if some compilation error occurs. Compilation errors cause an error
+# message to be output to STDERR and the affected terms to be skipped, but are
+# otherwise ignored.
+
+int procedure qpex_modfilter (ex, exprlist)
+
+pointer ex #I qpex descriptor
+char exprlist[ARB] #I list of attribute=expr expressions
+
+bool replace
+int boffset, offset, max_offset, dtype
+int status, sz_expr, token, parenlevel, nchars, buflen
+pointer sp, atname, assignop, tokbuf, expr, qp, ip, op, in, et_tail
+
+pointer qp_opentext()
+int qpex_codegeni(), qpex_codegenr(), qpex_codegend()
+int qp_gettok(), strlen(), gstrcpy(), ctoi(), sizeof()
+errchk malloc, qp_opentext, qp_gettok, realloc, qpex_delete
+
+string qpexwarn "QPEX Warning"
+define eatup_ 91
+define badatt_ 92
+
+begin
+ call smark (sp)
+ call salloc (atname, SZ_TOKBUF, TY_CHAR)
+ call salloc (assignop, SZ_TOKBUF, TY_CHAR)
+ call salloc (tokbuf, SZ_TOKBUF, TY_CHAR)
+
+ status = OK
+ sz_expr = DEF_SZEXPRBUF
+ et_tail = EX_ETTAIL(ex)
+ qp = EX_QP(ex)
+
+ # Allocate a variable size expression buffer.
+ call malloc (expr, sz_expr, TY_CHAR)
+
+ # Open the expression list for token input with macro expansion.
+ in = qp_opentext (qp, exprlist)
+
+ # Accumulate and compile successive attribute=expr terms of the
+ # expression list.
+
+ repeat {
+ # Get attribute name.
+ switch (qp_gettok (in, Memc[atname], SZ_TOKBUF)) {
+ case EOF:
+ break # input exhausted
+ case ',', ';':
+ next # null statement
+ case TOK_IDENTIFIER:
+ ; # got one
+ default:
+ call eprintf ("%s: unexpected token `%s'\n")
+ call pargstr (qpexwarn)
+ call pargstr (Memc[atname])
+ goto eatup_
+ }
+
+ # Get operator.
+ switch (qp_gettok (in, Memc[assignop], SZ_TOKBUF)) {
+ case TOK_PLUSEQUALS, '=':
+ replace = false
+ case TOK_COLONEQUALS:
+ replace = true
+
+ default:
+ call eprintf ("%s: missing assignment token (`%s')\n")
+ call pargstr (qpexwarn)
+ call pargstr (Memc[atname])
+eatup_
+ # A half-hearted attempt to ignore the offending statement...
+ while (qp_gettok (in, Memc[expr], sz_expr) != EOF)
+ if (Memc[expr] == ',')
+ break
+
+ # The default is to add to any existing filter.
+ replace = false
+ }
+
+ parenlevel = 0
+ token = NULL
+
+ # Accumulate expression.
+ for (op=expr; token != EOF; ) {
+ # Get next token from input stream.
+ token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF)
+
+ # Process any special tokens.
+ switch (token) {
+ case EOF:
+ break
+ case '(':
+ parenlevel = parenlevel + 1
+ case ')':
+ parenlevel = parenlevel - 1
+ if (parenlevel < 0) {
+ call eprintf ("%s: missing left parenthesis\n")
+ call pargstr (qpexwarn)
+ parenlevel = 0
+ status = ERR
+ next
+ }
+ case ',', ';':
+ # An unparenthesized comma terminates the expression.
+ if (parenlevel <= 0)
+ break
+ }
+
+ # Allocate more storage if expr buf fills.
+ nchars = strlen (Memc[tokbuf])
+ buflen = op - expr
+ if (buflen + nchars > sz_expr) {
+ sz_expr = sz_expr + INC_SZEXPRBUF
+ call realloc (expr, sz_expr, TY_CHAR)
+ op = expr + buflen
+ }
+
+ # Concatenate token string to expr.
+ op = op + gstrcpy (Memc[tokbuf], Memc[op], SZ_TOKBUF)
+ }
+
+ Memc[op] = EOS
+ if (parenlevel > 0) {
+ call eprintf ("%s: missing right parenthesis in expression\n")
+ call pargstr (qpexwarn)
+ status = ERR
+ }
+
+ # Parse the attribute name to determine the datatype and offset.
+
+ # Get byte offset of field.
+ ip = atname + 1
+ if (ctoi (Memc, ip, boffset) <= 0)
+ goto badatt_
+
+ # Get datatype and scaled offset; check field alignment.
+ switch (Memc[atname]) {
+ case 'S', 's':
+ dtype = TY_SHORT
+ offset = boffset / (SZ_SHORT * SZB_CHAR)
+ if (offset * SZ_SHORT * SZB_CHAR != boffset)
+ goto badatt_
+ case 'I', 'i':
+ dtype = TY_INT
+ offset = boffset / (SZ_INT * SZB_CHAR)
+ if (offset * SZ_INT * SZB_CHAR != boffset)
+ goto badatt_
+ case 'R', 'r':
+ dtype = TY_REAL
+ offset = boffset / (SZ_REAL * SZB_CHAR)
+ if (offset * SZ_REAL * SZB_CHAR != boffset)
+ goto badatt_
+ case 'D', 'd':
+ dtype = TY_DOUBLE
+ offset = boffset / (SZ_DOUBLE * SZB_CHAR)
+ if (offset * SZ_DOUBLE * SZB_CHAR != boffset)
+ goto badatt_
+ default:
+ goto badatt_
+ }
+
+ # Verify that the field is in range in the event struct.
+ # (Actually, we don't know the event struct at compile time...)
+
+ max_offset = (boffset / SZB_CHAR) + sizeof(dtype) - 1
+ if (boffset < 0 || max_offset > ARB) {
+badatt_ call eprintf ("%s: bad attribute name `%s'\n")
+ call pargstr (qpexwarn)
+ call pargstr (Memc[atname])
+ status = ERR
+ next
+ }
+
+ # Clobber any old expression for the given attribute if replace
+ # mode is in effect. Only previous expression terms are affected,
+ # hence in single expressions like "pha=x,pha=y", the second entry
+ # does not clobber the first.
+
+ if (replace)
+ call qpex_delete (ex, et_tail, offset, dtype)
+
+ # Compile the expression.
+ switch (dtype) {
+ case TY_SHORT, TY_INT:
+ if (qpex_codegeni (ex, Memc[atname], Memc[assignop],
+ Memc[expr], offset, dtype) == ERR)
+ status = ERR
+ case TY_REAL:
+ if (qpex_codegenr (ex, Memc[atname], Memc[assignop],
+ Memc[expr], offset, dtype) == ERR)
+ status = ERR
+ case TY_DOUBLE:
+ if (qpex_codegend (ex, Memc[atname], Memc[assignop],
+ Memc[expr], offset, dtype) == ERR)
+ status = ERR
+ }
+ }
+
+ call qp_closetext (in)
+ call mfree (expr, TY_CHAR)
+ call sfree (sp)
+
+ return (status)
+end
diff --git a/sys/qpoe/qpexopen.x b/sys/qpoe/qpexopen.x
new file mode 100644
index 00000000..833a1461
--- /dev/null
+++ b/sys/qpoe/qpexopen.x
@@ -0,0 +1,67 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <mach.h>
+include "qpoe.h"
+include "qpex.h"
+
+# QPEX_OPEN -- Open the expression evaluator. If an expression is given it
+# is compiled into the descriptor, otherwise a null (pass all) expression
+# is compiled. The compiled expression may be modified or read out at any
+# time via calls to other routines in the QPEX package (e.g., qpex_modfilter,
+# qpex_getfilter).
+
+pointer procedure qpex_open (qp, expr)
+
+pointer qp #I QPOE descriptor
+char expr[ARB] #I selection expression (filter)
+
+pointer ex, pb, db
+int pb_len, db_len
+int qpex_modfilter()
+errchk calloc
+
+begin
+ # Allocate the main QPEX descriptor.
+ call calloc (ex, LEN_EXDES, TY_STRUCT)
+
+ # Allocate the program buffer.
+ pb_len = QP_EXPBLEN(qp)
+ call calloc (pb, pb_len, TY_INT)
+
+ # Allocate the data buffer.
+ db_len = QP_EXDBLEN(qp)
+ call calloc (db, db_len, TY_CHAR)
+
+ # Initialize the descriptor.
+
+ EX_QP(ex) = qp
+ EX_DEBUG(ex) = QP_DEBUG(qp)
+ EX_START(ex) = pb
+
+ EX_PB(ex) = pb
+ EX_PBTOP(ex) = pb + pb_len
+ EX_PBOP(ex) = pb
+
+ EX_DB(ex) = db
+ EX_DBTOP(ex) = db + db_len
+ EX_DBOP(ex) = db
+
+ EX_MAXFRLUTLEN(ex) = QP_EXMAXFRLLEN(qp)
+ EX_MAXRRLUTLEN(ex) = QP_EXMAXRRLLEN(qp)
+ EX_LUTMINRANGES(ex) = QP_EXLMINRANGES(qp)
+ EX_LUTSCALE(ex) = QP_EXLSCALE(qp)
+
+ if (EX_DEBUG(ex) > 1) {
+ call eprintf ("QPEX activated, expr = `%s'\n")
+ call pargstr (expr)
+ }
+
+ # If a selection expression was given, compile it into the descriptor.
+ if (qpex_modfilter (ex, expr) == ERR) {
+ call qpex_close (ex)
+ call syserrs (SYS_QPEXSYN, QP_DFNAME(qp))
+ }
+
+ return (ex)
+end
diff --git a/sys/qpoe/qpexpand.x b/sys/qpoe/qpexpand.x
new file mode 100644
index 00000000..9be19a36
--- /dev/null
+++ b/sys/qpoe/qpexpand.x
@@ -0,0 +1,60 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_EXPANDTEXT -- Copy a statement to the output, breaking it up into tokens
+# and expanding any macro references in the process. This is used to resolve
+# macro references which might otherwise be repeatedly expanded, or which it
+# might not be possible to expand if this is left to some future time when
+# the referenced macros are no longer defined.
+
+int procedure qp_expandtext (qp, s1, s2, maxch)
+
+pointer qp #I QPOE descriptor
+char s1[ARB] #I input string containing macros
+char s2[maxch] #O output string buffer
+int maxch #I max chars out
+
+pointer sp, tokbuf, in
+int token, op, otop
+int gstrcpy(), qp_gettok()
+pointer qp_opentext()
+
+begin
+ call smark (sp)
+ call salloc (tokbuf, SZ_TOKBUF, TY_CHAR)
+
+ # Open input text for macro expanded token input.
+ in = qp_opentext (qp, s1)
+ otop = maxch + 1
+ op = 1
+
+ # Copy tokens to the output, inserting a space after every token.
+ repeat {
+ token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF)
+ if (token != EOF) {
+ if (token == TOK_STRING) {
+ s2[op] = '"'
+ op = min (otop, op + 1)
+ }
+ op = op + gstrcpy (Memc[tokbuf], s2[op], otop-op)
+ if (token == TOK_STRING) {
+ s2[op] = '"'
+ op = min (otop, op + 1)
+ }
+ s2[op] = ' '; op = min (otop, op + 1)
+ if (op >= otop)
+ break
+ }
+ } until (token == EOF)
+
+ # Cancel the trailing blank and add the EOS.
+ if (op > 1 && op < otop)
+ op = op - 1
+ s2[op] = EOS
+
+ call qp_closetext (in)
+ call sfree (sp)
+
+ return (op - 1)
+end
diff --git a/sys/qpoe/qpexparse.gx b/sys/qpoe/qpexparse.gx
new file mode 100644
index 00000000..c6f40042
--- /dev/null
+++ b/sys/qpoe/qpexparse.gx
@@ -0,0 +1,410 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include <mach.h>
+include "../qpex.h"
+
+.help qpexparse
+.nf --------------------------------------------------------------------------
+QPEXPARSE -- Code to parse an event attribute expression, producing a binary
+range list as output.
+
+ nranges = qpex_parse[ird] (expr, xs, xe, xlen)
+
+The calling sequence for the parse routine is shown above. The arguments XS
+and XE are pointers to dynamically allocated arrays of length XLEN and type
+[IRD]. These arrays should be allocated in the calling program before calling
+the parser, and deallocated when no longer needed. Reallocation to increase
+the array length is automatic if the arrays fill during parsing. DTYPE should
+be the same datatype as the attribute with which the list is associated.
+
+The form of an event attribute expression may be a list of values,
+
+ attribute = n
+or
+ attribute = m, n, ...
+
+a list of inclusive or exclusive ranges,
+
+ attribute = m:n, !p:q
+
+including open ranges,
+
+ attribute = :n, p:q
+
+or any combination of the above (excluding combinations of bitmasks and values
+or ranges, which are mutually exclusive):
+
+ attribute = :n, a, b, p:q, !(m, e:f)
+
+Parenthesis may be used for grouping where desired, e.g.,
+
+ attribute = (:n, a, b, p:q, !(m, e:f))
+
+An additional form of the event attribute expression allows use of a bitmask
+to specify the acceptable values, e.g.,
+
+ attribute = %17B
+or
+ attribute = !%17B
+
+however, bitmasks are incompatible with range lists, and should be recognized
+and dealt with elsewhere (bitmasks may not be combined with range lists in
+the same expression term).
+
+We are concerned here only with the attribute value list itself, i.e.,
+everything to the right of the equals sign in the examples above. This list
+should be extracted and placed into a string containing a single line of
+text before we are called. Attribute value lists may be any length, but
+backslash continuation, file inclusion (or whatever means is used to form
+the attribute value list) is assumed to be handled at a higher level.
+
+The output of this package is an ordered boolean valued binary range list
+with type integer, real, or double breakpoints (i.e., the breakpoints are the
+same datatype as the attribute itself, but the range values are zero or one).
+The range list defines the initial value, final value, and any interior
+breakpoints where the attribute value changes state. Expression optimization
+is used to minimize the number of breakpoints (i.e., eliminate redundant
+breakpoints, such as a range within a range).
+
+Output range list format:
+
+ xs[1] xe[1]
+ xs[2] xe[2]
+ ...
+ xs[N] xe[N]
+
+Where each range is inclusive and only "true" ranges are shown. If XS[1] is
+LEFT a open-left range (:n) is indicated; if XE[N] is RIGHT an open-right
+range (n:) is indicated. In an integer range list, isolated points appear
+as a single range with (xe[i]=xs[i]). In a real or double range list,
+isolated points are represented as finite ranges with a width on the order of
+the machine epsilon.
+.endhelp ---------------------------------------------------------------------
+
+define DEF_XLEN 256 # default output range list length
+define INC_XLEN 256 # increment to above
+define DEF_VLEN 512 # default breakpoint list length
+define INC_VLEN 512 # increment to above
+define MAX_NEST 20 # parser stack depth
+
+define STEP 1 # step at boundary of closed range
+define ZERO 1000 # step at boundary of open range
+
+define XV Mem$t[xv+($1)-1] # reference x position values
+define UV Memi[uv+($1)-1] # unique flags for x value pairs
+define SV Memi[sv+($1)-1] # reference breakpoint step values
+
+
+# QPEX_PARSE -- Convert the given attribute value list into a binary
+# range list, returning the number of ranges as the function value.
+
+int procedure qpex_parse$t (expr, xs, xe, xlen)
+
+char expr[ARB] #I attribute value list to be parsed
+pointer xs #U pointer to array of start-range values
+pointer xe #U pointer to array of end-range values
+int xlen #U allocated length of XS, XE arrays
+
+bool range
+pointer xv, uv, sv
+PIXEL xstart, xend, xmin, temp, x, n_xs, n_xe
+int vlen, nrg, ip, op, ch, ip_start, i, j, jval, r1, r2, y, v, ov, dy
+int token[MAX_NEST], tokval[MAX_NEST], lev, itemp, umin
+errchk syserr, malloc, realloc
+define pop_ 91
+
+$if (datatype == si)
+int qp_ctoi()
+define fp_equal$t($1==$2)
+$else
+double dtemp
+bool bval, fp_equal$t()
+int qp_ctod()
+$endif
+
+begin
+ vlen = DEF_VLEN
+ call malloc (xv, vlen, TY_PIXEL)
+ call malloc (uv, vlen, TY_INT)
+ call malloc (sv, vlen, TY_INT)
+
+ lev = 0
+ nrg = 0
+
+ # Parse the expression string and compile the raw, unoptimized
+ # breakpoint list in the order in which the breakpoints occur in
+ # the string.
+
+ for (ip=1; expr[ip] != EOS; ) {
+ # Skip whitespace.
+ for (ch=expr[ip]; IS_WHITE(ch) || ch == '\n'; ch=expr[ip])
+ ip = ip + 1
+
+ # Extract and process token.
+ switch (ch) {
+ case EOS:
+ # At end of string.
+ if (lev > 0)
+ goto pop_
+ else
+ break
+
+ case ',':
+ # Comma list token delmiter.
+ ip = ip + 1
+ goto pop_
+
+ case '!', '(':
+ # Syntactical element - push on stack.
+ ip = ip + 1
+ lev = lev + 1
+ if (lev > MAX_NEST)
+ call syserr (SYS_QPEXLEVEL)
+ token[lev] = ch
+ tokval[lev] = nrg + 1
+
+ case ')':
+ # Close parenthesized group and pop parser stack.
+ ip = ip + 1
+ if (lev < 1)
+ call syserr (SYS_QPEXMLP)
+ else if (token[lev] != '(')
+ call syserr (SYS_QPEXRPAREN)
+ lev = lev - 1
+ goto pop_
+
+ default:
+ # Process a range term.
+ ip_start = ip
+
+ # Scan the M in M:N.
+ $if (datatype == si)
+ if (qp_ctoi (expr, ip, xstart) <= 0)
+ xstart = LEFT$T
+ $else
+ if (qp_ctod (expr, ip, dtemp) <= 0)
+ xstart = LEFT$T
+ else
+ xstart = dtemp
+ $endif
+
+ # Scan the : in M:N. The notation M-N is also accepted,
+ # provided the token - immediately follows the token M.
+
+ while (IS_WHITE(expr[ip]))
+ ip = ip + 1
+ range = (expr[ip] == ':')
+ if (range)
+ ip = ip + 1
+ else if (!IS_LEFT$T (xstart)) {
+ range = (expr[ip] == '-')
+ if (range)
+ ip = ip + 1
+ }
+
+ # Scan the N in M:N.
+ if (range) {
+ $if (datatype == si)
+ if (qp_ctoi (expr, ip, xend) <= 0)
+ xend = RIGHT$T
+ $else
+ if (qp_ctod (expr, ip, dtemp) <= 0)
+ xend = RIGHT$T
+ else
+ xend = dtemp
+ $endif
+ } else
+ xend = xstart
+
+ # Fix things if the user entered M:M explicitly.
+ if (range)
+ if (fp_equal$t (xstart, xend))
+ range = false
+
+ # Expand a single point into a range. For an integer list
+ # this produces M:M+1; for a floating list M-eps:M+eps.
+ # Verify ordering and that something recognizable was scanned.
+
+ if (!range) {
+ if (IS_LEFT$T(xstart))
+ call syserr (SYS_QPEXBADRNG)
+ $if (datatype == si)
+ xend = xstart + 1
+ $endif
+ } else {
+ if (xstart > xend) {
+ temp = xstart; xstart = xend; xend = temp
+ }
+ $if (datatype == si)
+ if (!IS_RIGHT$T(xend))
+ xend = xend + 1
+ $endif
+ }
+
+ # Make more space if vectors fill up.
+ if (nrg+4 > vlen) {
+ vlen = vlen + INC_VLEN
+ call realloc (xv, vlen, TY_PIXEL)
+ call realloc (uv, vlen, TY_INT)
+ call realloc (sv, vlen, TY_INT)
+ }
+
+ # Save range on intermediate breakpoint list.
+ nrg = nrg + 1
+ XV(nrg) = xstart
+ UV(nrg) = 0
+ SV(nrg) = STEP
+
+ nrg = nrg + 1
+ XV(nrg) = xend
+ UV(nrg) = 1
+ SV(nrg) = -STEP
+pop_
+ # Pop parser stack.
+ if (lev > 0)
+ if (token[lev] == '!') {
+ # Invert a series of breakpoints.
+ do i = tokval[lev], nrg {
+ if (SV(i) == STEP) # invert
+ SV(i) = -ZERO
+ else if (SV(i) == -STEP)
+ SV(i) = ZERO
+ else if (SV(i) == ZERO) # undo
+ SV(i) = -STEP
+ else if (SV(i) == -ZERO)
+ SV(i) = STEP
+ }
+ lev = lev - 1
+ }
+ }
+ }
+
+ # If the first range entered by the user is an exclude range,
+ # e.g., "(!N)" or "(!(expr))" this implies that all other values
+ # are acceptable. Add the open range ":" to the end of the range
+ # list to indicate this, i.e., convert "!N" to ":,!N".
+
+ if (SV(1) == -ZERO) {
+ nrg = nrg + 1
+ XV(nrg) = LEFT$T
+ UV(nrg) = 0
+ SV(nrg) = STEP
+
+ nrg = nrg + 1
+ XV(nrg) = RIGHT$T
+ UV(nrg) = 1
+ SV(nrg) = -STEP
+ }
+
+ # Sort the breakpoint list.
+ do j = 1, nrg {
+ xmin = XV(j); umin = UV(j)
+ jval = j
+ do i = j+1, nrg {
+ $if (datatype == rd)
+ bval = (XV(i) < xmin)
+ if (!bval)
+ if (abs (XV(i) - xmin) < 1.0E-5)
+ bval = (fp_equal$t(XV(i),xmin) && UV(i) < umin)
+ if (bval) {
+ $else
+ if (XV(i) < xmin || (XV(i) == xmin && UV(i) < umin)) {
+ $endif
+ xmin = XV(i); umin = UV(i)
+ jval = i
+ }
+ }
+ if (jval != j) {
+ temp = XV(j); XV(j) = XV(jval); XV(jval) = temp
+ itemp = UV(j); UV(j) = UV(jval); UV(jval) = itemp
+ itemp = SV(j); SV(j) = SV(jval); SV(jval) = itemp
+ }
+ }
+
+ # Initialize the output arrays if they were passed in as null.
+ if (xlen <= 0) {
+ xlen = DEF_XLEN
+ call malloc (xs, xlen, TY_PIXEL)
+ call malloc (xe, xlen, TY_PIXEL)
+ }
+
+ # Collapse sequences of redundant breakpoints into a single
+ # breakpoint, clipping the running sum value to the range 0-1.
+ # Accumulate and output successive nonzero ranges.
+
+ op = 1
+ ov = 0
+ y = 0
+
+ for (r1=1; r1 <= nrg; r1=r2+1) {
+ # Get a range of breakpoint entries for a single XV position.
+ for (r2=r1; r2 <= nrg; r2=r2+1) {
+ $if (datatype == si)
+ if (XV(r2) != XV(r1))
+ break
+ $else
+ bval = (UV(r2) != UV(r1))
+ if (!bval) {
+ bval = (abs (XV(r2) - XV(r1)) > 1.0E-5)
+ if (!bval)
+ bval = !fp_equal$t(XV(r2),XV(r1))
+ }
+ if (bval)
+ break
+ $endif
+ }
+ r2 = r2 - 1
+
+ # Collapse into a single breakpoint.
+ x = XV(r1)
+ dy = SV(r1)
+ do i = r1 + 1, r2
+ dy = dy + SV(i)
+ y = y + dy
+
+ # Clip value to the range 0-1.
+ v = max(0, min(1, y))
+
+ # Accumulate a range of nonzero value. This eliminates redundant
+ # points lying within a range which is already set high.
+
+ if (v == 1 && ov == 0) {
+ n_xs = x
+ ov = 1
+ } else if (v == 0 && ov == 1) {
+ $if (datatype == si)
+ if (IS_RIGHT$T(x))
+ n_xe = x
+ else
+ n_xe = x - 1
+ $else
+ n_xe = x
+ $endif
+ ov = 2
+ }
+
+ # Output a range.
+ if (ov == 2) {
+ if (op > xlen) {
+ xlen = xlen + INC_XLEN
+ call realloc (xs, xlen, TY_PIXEL)
+ call realloc (xe, xlen, TY_PIXEL)
+ }
+
+ Mem$t[xs+op-1] = n_xs
+ Mem$t[xe+op-1] = n_xe
+ op = op + 1
+
+ ov = 0
+ }
+ }
+
+ # All done; discard breakpoint buffers.
+ call mfree (xv, TY_PIXEL)
+ call mfree (uv, TY_INT)
+ call mfree (sv, TY_INT)
+
+ return (op - 1)
+end
diff --git a/sys/qpoe/qpexsub.gx b/sys/qpoe/qpexsub.gx
new file mode 100644
index 00000000..1fc51821
--- /dev/null
+++ b/sys/qpoe/qpexsub.gx
@@ -0,0 +1,67 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QPEX_SUBLIST -- Extract a sublist spanning the indicated range from a
+# larger range list. The number of ranges extracted is returned as the
+# function value.
+
+int procedure qpex_sublist$t (x1, x2, xs,xe,nranges,ip, o_xs,o_xe)
+
+PIXEL x1, x2 #I range to be extracted
+PIXEL xs[nranges],xe[nranges] #I input range list
+int nranges #I nranges in input list
+int ip #U start position in input list
+PIXEL o_xs[ARB],o_xe[ARB] #O output sublist
+
+PIXEL tol
+int op, i
+
+begin
+ $if (datatype == i)
+ tol = 0
+ $else
+ tol = (EPSILON$T * 10$f)
+ $endif
+
+ # Determine the range containing or immediately following the
+ # start point of the range of interest.
+
+ while (x1 < xs[ip] && ip > 1)
+ ip = ip - 1
+ while (x1 >= xs[ip])
+ if (x1 <= xe[ip] || ip >= nranges)
+ break
+ else
+ ip = ip + 1
+
+ # Check for an empty output range list.
+ if (xs[ip] > x2)
+ return (0)
+
+ # At least one input range contributes something to the output region.
+ # Copy a portion of the input range list to the ouput range list.
+
+ op = 1
+ do i = ip, nranges {
+ if (xs[i] <= x1)
+ o_xs[op] = LEFT$T - tol
+ else
+ o_xs[op] = xs[i]
+
+ if ((xe[i] - x2) >= tol) {
+ o_xe[op] = RIGHT$T + tol
+ op = op + 1
+ break
+ } else
+ o_xe[op] = xe[i]
+
+ op = op + 1
+ if (xs[i+1] > x2)
+ break
+ }
+
+ ip = i
+ return (op - 1)
+end
diff --git a/sys/qpoe/qpget.gx b/sys/qpoe/qpget.gx
new file mode 100644
index 00000000..dfdca1b0
--- /dev/null
+++ b/sys/qpoe/qpget.gx
@@ -0,0 +1,67 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_GET -- Return the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# returned by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+PIXEL procedure qp_get$t (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer pp
+int dtype
+PIXEL value
+int qp_getparam()
+errchk qp_getparam, syserrs
+
+begin
+ # Lookup the parameter and it's value.
+ dtype = qp_getparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ # Set default value of INDEF or NULL.
+ $if (datatype == c)
+ value = (NULL)
+ $else
+ value = (INDEF)
+ $endif
+
+ # Get a valid parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ value = (Memc[pp])
+ case TY_SHORT:
+ if (!IS_INDEFS(Mems[pp]))
+ value = (Mems[pp])
+ case TY_INT:
+ if (!IS_INDEFI(Memi[pp]))
+ value = (Memi[pp])
+ case TY_LONG:
+ if (!IS_INDEFL(Meml[pp]))
+ value = (Meml[pp])
+ case TY_REAL:
+ if (!IS_INDEFR(Memr[pp]))
+ value = (Memr[pp])
+ case TY_DOUBLE:
+ if (!IS_INDEFD(Memd[pp]))
+ value = (Memd[pp])
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_get: `%s', TYP=(%d->%d) returns %g\n")
+ call pargstr (param)
+ call pargi (dtype)
+ call pargi (TY_PIXEL)
+ call parg$t (value)
+ }
+
+ return (value)
+end
diff --git a/sys/qpoe/qpgetb.x b/sys/qpoe/qpgetb.x
new file mode 100644
index 00000000..cea5c5be
--- /dev/null
+++ b/sys/qpoe/qpgetb.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_GETB -- Return the boolean value of the named header parameter. Type
+# conversion is not permitted between boolean and the other datatypes.
+
+bool procedure qp_getb (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer pp
+int qp_getparam()
+errchk qp_getparam, syserrs
+
+begin
+ # Lookup the parameter and it's value.
+ if (qp_getparam (qp, param, pp) != TY_BOOL)
+ call syserrs (SYS_QPBADCONV, param)
+ else if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ return (Memb[pp])
+end
diff --git a/sys/qpoe/qpgettok.x b/sys/qpoe/qpgettok.x
new file mode 100644
index 00000000..feb8d780
--- /dev/null
+++ b/sys/qpoe/qpgettok.x
@@ -0,0 +1,687 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <error.h>
+include <ctype.h>
+include <fset.h>
+include "qpoe.h"
+
+.help gettok
+.nf --------------------------------------------------------------------------
+GETTOK -- Lexical input routines for QPOE. Used to return tokens from input
+text; this is where all macro expansion and file expansion takes place.
+
+ gt = qp_opentext (qp, text)
+ token = qp_gettok (gt, tokbuf, maxch)
+ qp_ungettok (gt, tokbuf)
+ token = qp_rawtok (gt, tokbuf, maxch)
+ token = qp_nexttok (gt)
+ qp_closetext (gt)
+
+Access to the package is gained by opening a text string with QP_OPENTEXT.
+This returns a descriptor which is passed to QP_GETTOK to read successive
+tokens, which may come from the input text string or from any macros,
+include files, etc., referenced in the text or in any substituted text.
+QP_UNGETTOK pushes a token back into the QP_GETTOK input stream, to be
+returned in the next QP_GETTOK call (following macro expansion).
+
+QP_RAWTOK returns the next physical token from an input stream (without
+macro expansion), and QP_NEXTTOK returns the type of the next *physical*
+token (no macro expansion) without actually fetching it (for look ahead
+decision making).
+
+The tokens that can be returned are as follows:
+
+ TOK_IDENTIFIER [a-zA-Z][a-zA-Z0-9_]*
+ TOK_NUMBER [0-9][0-9a-zA-Z.]*(e|E)?(+|-)?[0-9]*
+ TOK_STRING if "abc" or 'abc', the abc
+ 'c' other characters, e.g., =+-*/,;:()[] etc
+ EOF at end of input
+
+Macro replacement syntax:
+
+ macro push macro with null arglist
+ macro(arg,arg,...) push macro with argument substitution
+ @file push contents of file
+ @file(arg,arg,...) push file with argument substitution
+ `cmd` substitute output of CL command "cmd"
+
+where
+ macro is an identifier, the name of a global macro
+ or a datafile local macro (parameter)
+
+In all cases, occurences of $N in the replacement text are replaced by the
+macro arguments if any, and macros are recursively expanded. Whitespace,
+including newline, equates to a single space, as does EOF (hence always
+delimits tokens). Comments (# to end of line) are ignored. All identifiers
+in scanned text are checked to see if they are references to predefined global
+or local (datafile) macros.
+
+A global macro is a symbol defined globally for QPOE, in effect for all poefile
+accesses (see qpmacro.x). A local macro is a macro defined as a string
+parameter of type TY_MACRO in the poefile header (and hence affecting only
+that one datafile).
+.endhelp ---------------------------------------------------------------------
+
+# General definitions.
+define MAX_LEVELS 20 # max include file nesting
+define MAX_ARGS 9 # max arguments to a macro
+define SZ_CMD 80 # `cmd`
+define SZ_IBUF 8192 # buffer for macro replacement
+define SZ_OBUF 8192 # buffer for macro replacement
+define SZ_ARGBUF 256 # argument list to a macro
+
+# The gettok descriptor.
+define LEN_GTDES 45
+define GT_QP Memi[$1] # backpointer to QPOE descriptor
+define GT_FD Memi[$1+1] # current input stream
+define GT_NEXTCH Memi[$1+2] # lookahead character
+define GT_FTEMP Memi[$1+3] # file on stream is a temp file
+define GT_LEVEL Memi[$1+4] # current nesting level
+define GT_SVFD Memi[$1+5+$2-1] # stacked file descriptors
+define GT_SVFTEMP Memi[$1+25+$2-1]# stacked ftemp flags
+
+
+# QP_OPENTEXT -- Open the QP_GETTOK descriptor. The descriptor is initially
+# opened on the user supplied string buffer (which is opened as a file and
+# which must remain intact while token input is in progress), but include file
+# processing etc. may cause arbitrary nesting of file descriptors.
+
+pointer procedure qp_opentext (qp, text)
+
+pointer qp #I QPOE descriptor
+char text[ARB] #I input text to be scanned
+
+pointer gt
+int sz_pbbuf
+int stropen(), strlen()
+errchk stropen, calloc
+
+begin
+ call calloc (gt, LEN_GTDES, TY_STRUCT)
+
+ GT_QP(gt) = qp
+ GT_FD(gt) = stropen (text, strlen(text), READ_ONLY)
+
+ if (qp == NULL)
+ sz_pbbuf = DEF_MAXPUSHBACK
+ else
+ sz_pbbuf = QP_SZPBBUF(qp)
+ call fseti (GT_FD(gt), F_PBBSIZE, sz_pbbuf)
+
+ return (gt)
+end
+
+
+# QP_GETTOK -- Return the next token from the input stream. The token ID
+# (a predefined integer code or the character value) is returned as the
+# function value. The text of the token is returned as an output argument.
+# Any macro references, file includes, etc., are performed in the process
+# of scanning the input stream, hence only fully resolved tokens are output.
+
+int procedure qp_gettok (gt, tokbuf, maxch)
+
+pointer gt #I gettok descriptor
+char tokbuf[maxch] #O receives the text of the token
+int maxch #I max chars out
+
+pointer sp, bp, qp, cmd, ibuf, obuf, argbuf, fname, sym, textp
+int fd, token, level, nargs, nchars, i_fd, o_fd, ftemp
+
+bool streq()
+pointer qp_gmsym()
+int strmac(), open(), stropen()
+int qp_rawtok(), qp_nexttok(), qp_arglist()
+errchk qp_rawtok,close,ungetci,ungetline,qp_arglist,clcmdw,stropen,syserr
+define pushfile_ 91
+
+
+begin
+ call smark (sp)
+
+ # Allocate some buffer space.
+ nchars = SZ_CMD + SZ_IBUF + SZ_OBUF + SZ_ARGBUF + SZ_FNAME + 5
+ call salloc (bp, nchars, TY_CHAR)
+
+ cmd = bp
+ ibuf = cmd + SZ_CMD + 1
+ obuf = ibuf + SZ_IBUF + 1
+ argbuf = obuf + SZ_OBUF + 1
+ fname = argbuf + SZ_ARGBUF + 1
+
+ qp = GT_QP(gt)
+
+ # Read raw tokens and push back macro or include file text until we
+ # get a fully resolved token.
+
+ repeat {
+ fd = GT_FD(gt)
+
+ # Get a raw token.
+ token = qp_rawtok (gt, tokbuf, maxch)
+
+ # Process special tokens.
+ switch (token) {
+ case EOF:
+ # EOF has been reached on the current stream.
+ level = GT_LEVEL(gt)
+ if (GT_FTEMP(gt) == YES) {
+ call fstats (fd, F_FILENAME, Memc[fname], SZ_FNAME)
+ if (level > 0)
+ call close (fd)
+ iferr (call delete (Memc[fname]))
+ call erract (EA_WARN)
+ } else if (level > 0)
+ call close (fd)
+
+ if (level > 0) {
+ # Restore previous stream.
+ GT_FD(gt) = GT_SVFD(gt,level)
+ GT_FTEMP(gt) = GT_SVFTEMP(gt,level)
+ GT_LEVEL(gt) = level - 1
+ GT_NEXTCH(gt) = NULL
+ } else {
+ # Return EOF token to caller.
+ call strcpy ("EOF", tokbuf, maxch)
+ break
+ }
+
+ case TOK_IDENTIFIER:
+ # Lookup the identifier in the symbol table.
+ sym = NULL
+ if (qp != NULL)
+ sym = qp_gmsym (qp, tokbuf, textp)
+
+ # Process a defined macro.
+ if (sym != NULL) {
+ # If macro does not have any arguments, merely push back
+ # the replacement text.
+
+ if (and (S_FLAGS(sym), SF_MACARGS) == 0) {
+ if (GT_NEXTCH(gt) > 0) {
+ call ungetci (fd, GT_NEXTCH(gt))
+ GT_NEXTCH(gt) = 0
+ }
+ call ungetline (fd, Memc[textp])
+ next
+ }
+
+ # Extract argument list, if any, perform argument
+ # substitution on the macro, and push back the edited
+ # text to be rescanned.
+
+ if (qp_nexttok(gt) == '(') {
+ nargs = qp_arglist (gt, Memc[argbuf], SZ_ARGBUF)
+
+ # Pushback the text of a macro with arg substitution.
+ nchars = strmac (Memc[textp], Memc[argbuf],
+ Memc[obuf], SZ_OBUF)
+ if (GT_NEXTCH(gt) > 0) {
+ call ungetci (fd, GT_NEXTCH(gt))
+ GT_NEXTCH(gt) = 0
+ }
+ call ungetline (fd, Memc[obuf])
+ next
+
+ } else {
+ call eprintf ("macro `%s' called with no arguments\n")
+ call pargstr (tokbuf)
+ }
+ }
+
+ # Check for the builtin symbol $DFN, the datafile name.
+ if (tokbuf[1] == '$') {
+ if (streq (tokbuf, "$DFN")) {
+ call strcpy (QP_DFNAME(qp), tokbuf, maxch)
+ token = TOK_STRING
+ break
+ }
+ }
+
+ # Return a regular identifier.
+ break
+
+ case TOK_COMMAND:
+ # Send a command to the CL and push back the output.
+
+ # Execute the command, spooling the output in a temp file.
+ call mktemp ("tmp$co", Memc[fname], SZ_FNAME)
+ call sprintf (Memc[cmd], SZ_LINE, "%s > %s")
+ call pargstr (tokbuf)
+ call pargstr (Memc[fname])
+ call clcmdw (Memc[cmd])
+
+ # Open the output file as input text.
+ call strcpy (Memc[fname], tokbuf, maxch)
+ nargs = 0
+ ftemp = YES
+ goto pushfile_
+
+ case '@':
+ token = qp_rawtok (gt, tokbuf, maxch)
+ if (token != TOK_IDENTIFIER && token != TOK_STRING) {
+ call eprintf ("expected a filename after the `@'\n")
+ next
+ } else {
+ nargs = 0
+ if (qp_nexttok(gt) == '(') # )
+ nargs = qp_arglist (gt, Memc[argbuf], SZ_ARGBUF)
+ ftemp = NO
+ }
+pushfile_
+ # Attempt to open the file; first try the given name, then
+ # if that doesn't work, try adding the macro file extension.
+
+ iferr (i_fd = open (tokbuf, READ_ONLY, TEXT_FILE)) {
+ call qp_mkfname (tokbuf,
+ QPOE_MACROEXTN, Memc[fname], SZ_FNAME)
+ iferr (i_fd = open (Memc[fname],READ_ONLY,TEXT_FILE)) {
+ call eprintf ("cannot open `%s'\n")
+ call pargstr (tokbuf)
+ next
+ }
+ }
+
+ if (qp != NULL)
+ call fseti (i_fd, F_PBBSIZE, QP_SZPBBUF(qp))
+ else
+ call fseti (i_fd, F_PBBSIZE, DEF_MAXPUSHBACK)
+
+ # Cancel lookahead.
+ if (GT_NEXTCH(gt) > 0) {
+ call ungetci (fd, GT_NEXTCH(gt))
+ GT_NEXTCH(gt) = 0
+ }
+
+ # If the macro was called with a nonnull argument list,
+ # attempt to perform argument substitution on the file
+ # contents. Otherwise merely push the fd.
+
+ if (nargs > 0) {
+ # Pushback file contents with argument substitution.
+ o_fd = stropen (Memc[ibuf], SZ_IBUF, NEW_FILE)
+
+ call fcopyo (i_fd, o_fd)
+ nchars = strmac (Memc[ibuf],Memc[argbuf],Memc[obuf],SZ_OBUF)
+ call ungetline (fd, Memc[obuf])
+
+ call close (o_fd)
+ call close (i_fd)
+
+ } else {
+ # Push a new input stream.
+ level = GT_LEVEL(gt) + 1
+ if (level > MAX_LEVELS)
+ call syserr (SYS_QPMRECUR)
+
+ GT_SVFD(gt,level) = GT_FD(gt)
+ GT_SVFTEMP(gt,level) = GT_FTEMP(gt)
+ GT_LEVEL(gt) = level
+
+ fd = i_fd
+ GT_FD(gt) = fd
+ GT_FTEMP(gt) = ftemp
+ }
+
+ default:
+ break
+ }
+ }
+
+ if (qp != NULL)
+ if (QP_DEBUG(qp) > 4) {
+ call eprintf ("token=%d(%o), `%s'\n")
+ call pargi (token)
+ call pargi (max(0,token))
+ if (IS_PRINT(tokbuf[1]))
+ call pargstr (tokbuf)
+ else
+ call pargstr ("")
+ }
+
+ call sfree (sp)
+ return (token)
+end
+
+
+# QP_UNGETTOK -- Push a token back into the QP_GETTOK input stream, to be
+# returned as the next token by QP_GETTOK.
+
+procedure qp_ungettok (gt, tokbuf)
+
+pointer gt #I gettok descriptor
+char tokbuf[ARB] #I text of token
+
+int fd
+pointer qp
+errchk ungetci
+
+begin
+ fd = GT_FD(gt)
+ qp = GT_QP(gt)
+
+ if (qp != NULL)
+ if (QP_DEBUG(qp) > 4) {
+ call eprintf ("unget token `%s'\n")
+ call pargstr (tokbuf)
+ }
+
+ # Cancel lookahead.
+ if (GT_NEXTCH(gt) > 0) {
+ call ungetci (fd, GT_NEXTCH(gt))
+ GT_NEXTCH(gt) = 0
+ }
+
+ # First push back a space to ensure that the token is recognized
+ # when the input is rescanned.
+
+ call ungetci (fd, ' ')
+
+ # Now push the token text.
+ call ungetline (fd, tokbuf)
+end
+
+
+# QP_RAWTOK -- Get a raw token from the input stream, without performing any
+# macro expansion or file inclusion. The text of the token in returned in
+# tokbuf, and the token type is returened as the function value.
+
+int procedure qp_rawtok (gt, outstr, maxch)
+
+pointer gt #I gettok descriptor
+char outstr[maxch] #O receives text of token.
+int maxch #I max chars out
+
+int token, delim, fd, ch, op
+define again_ 91
+int getci()
+
+begin
+ fd = GT_FD(gt)
+again_
+ # Get lookahead char if we don't already have one.
+ ch = GT_NEXTCH(gt)
+ GT_NEXTCH(gt) = NULL
+ if (ch <= 0 || IS_WHITE(ch) || ch == '\n') {
+ while (getci (fd, ch) != EOF)
+ if (!(IS_WHITE(ch) || ch == '\n'))
+ break
+ }
+
+ # Output the first character.
+ op = 1
+ if (ch != EOF && ch != '"' && ch != '\'' && ch != '`') {
+ outstr[op] = ch
+ op = op + 1
+ }
+
+ # Accumulate token. Some of the token recognition logic used here
+ # (especially for numbers) is crude, but it is not clear that rigour
+ # is justified for this application.
+
+ if (ch == EOF) {
+ call strcpy ("EOF", outstr, maxch)
+ token = EOF
+
+ } else if (ch == '#') {
+ # Ignore a comment.
+ while (getci (fd, ch) != '\n')
+ if (ch == EOF)
+ break
+ goto again_
+
+ } else if (IS_ALPHA(ch) || ch == '_' || ch == '$' || ch == '.') {
+ # Identifier.
+ token = TOK_IDENTIFIER
+ while (getci (fd, ch) != EOF)
+ if (IS_ALNUM(ch) || ch == '_' || ch == '$' || ch == '.') {
+ outstr[op] = ch
+ op = min (maxch, op+1)
+ } else
+ break
+
+ } else if (IS_DIGIT(ch)) {
+ # Number.
+ token = TOK_NUMBER
+
+ # Get number.
+ while (getci (fd, ch) != EOF)
+ if (IS_ALNUM(ch) || ch == '.') {
+ outstr[op] = ch
+ op = min (maxch, op+1)
+ } else
+ break
+
+ # Get exponent if any.
+ if (ch == 'E' || ch == 'e') {
+ outstr[op] = ch
+ op = min (maxch, op+1)
+ while (getci (fd, ch) != EOF)
+ if (IS_DIGIT(ch) || ch == '+' || ch == '-') {
+ outstr[op] = ch
+ op = min (maxch, op+1)
+ } else
+ break
+ }
+
+ } else if (ch == '"' || ch == '\'' || ch == '`') {
+ # Quoted string or command.
+
+ if (ch == '`')
+ token = TOK_COMMAND
+ else
+ token = TOK_STRING
+
+ delim = ch
+ while (getci (fd, ch) != EOF)
+ if (ch==delim && (op>1 && outstr[op-1] != '\\') || ch == '\n')
+ break
+ else {
+ outstr[op] = ch
+ op = min (maxch, op+1)
+ }
+ ch = getci (fd, ch)
+
+ } else if (ch == '+') {
+ # May be the += operator.
+ if (getci (fd, ch) != EOF)
+ if (ch == '=') {
+ token = TOK_PLUSEQUALS
+ outstr[op] = ch
+ op = op + 1
+ ch = getci (fd, ch)
+ } else
+ token = '+'
+
+ } else if (ch == ':') {
+ # May be the := operator.
+ if (getci (fd, ch) != EOF)
+ if (ch == '=') {
+ token = TOK_COLONEQUALS
+ outstr[op] = ch
+ op = op + 1
+ ch = getci (fd, ch)
+ } else
+ token = ':'
+
+ } else {
+ # Other characters.
+ token = ch
+ ch = getci (fd, ch)
+ }
+
+ # Process the lookahead character.
+ if (IS_WHITE(ch) || ch == '\n') {
+ repeat {
+ ch = getci (fd, ch)
+ } until (!(IS_WHITE(ch) || ch == '\n'))
+ }
+
+ if (ch != EOF)
+ GT_NEXTCH(gt) = ch
+
+ outstr[op] = EOS
+ return (token)
+end
+
+
+# QP_NEXTTOK -- Determine the type of the next raw token in the input stream,
+# without actually fetching the token. TOK_PLUSEQUALS is not recognized at
+# this level. Note that this is at the same level as QP_RAWTOK, i.e., no
+# macro expansion is performed, and the lookahead token is that which would
+# be returned by the next qp_rawtok, which is not necessarily what qp_gettok
+# would return after macro replacement.
+
+int procedure qp_nexttok (gt)
+
+pointer gt #I gettok descriptor
+
+pointer qp
+int token, fd, ch
+int getci()
+
+begin
+ fd = GT_FD(gt)
+ qp = GT_QP(gt)
+
+ # Get lookahead char if we don't already have one.
+ ch = GT_NEXTCH(gt)
+ if (ch <= 0 || IS_WHITE(ch) || ch == '\n')
+ while (getci (fd, ch) != EOF)
+ if (!(IS_WHITE(ch) || ch == '\n'))
+ break
+
+ if (ch == EOF)
+ token = EOF
+ else if (IS_ALPHA(ch))
+ token = TOK_IDENTIFIER
+ else if (IS_DIGIT(ch))
+ token = TOK_NUMBER
+ else if (ch == '"' || ch == '\'')
+ token = TOK_STRING
+ else if (ch == '`')
+ token = TOK_COMMAND
+ else
+ token = ch
+
+ if (qp != NULL)
+ if (QP_DEBUG(qp) > 4) {
+ call eprintf ("nexttok=%d(%o) `%c'\n")
+ call pargi (token)
+ call pargi (max(0,token))
+ if (IS_PRINT(ch))
+ call pargi (ch)
+ else
+ call pargi (0)
+ }
+
+ return (token)
+end
+
+
+# QP_CLOSETEXT -- Close the gettok descriptor and any files opened thereon.
+
+procedure qp_closetext (gt)
+
+pointer gt #I gettok descriptor
+
+int level, fd
+pointer sp, fname
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+
+ for (level=GT_LEVEL(gt); level >= 0; level=level-1) {
+ fd = GT_FD(gt)
+ if (GT_FTEMP(gt) == YES) {
+ call fstats (fd, F_FILENAME, Memc[fname], SZ_FNAME)
+ call close (fd)
+ iferr (call delete (Memc[fname]))
+ call erract (EA_WARN)
+ } else
+ call close (fd)
+
+ if (level > 0) {
+ GT_FD(gt) = GT_SVFD(gt,level)
+ GT_FTEMP(gt) = GT_SVFTEMP(gt,level)
+ }
+ }
+
+ call mfree (gt, TY_STRUCT)
+ call sfree (sp)
+end
+
+
+# QP_ARGLIST -- Extract a paren and comma delimited argument list to be used
+# for substitution into a macro replacement string. Since the result will be
+# pushed back and rescanned, we do not have to perform macro substitution on
+# the argument list at this level.
+
+int procedure qp_arglist (gt, argbuf, maxch)
+
+pointer gt #I gettok descriptor
+char argbuf[maxch] #O receives parsed arguments
+int maxch #I max chars out
+
+int level, quote, nargs, op, ch, fd
+int getci()
+
+begin
+ fd = GT_FD(gt)
+
+ # Get lookahead char if we don't already have one.
+ ch = GT_NEXTCH(gt)
+ if (ch <= 0 || IS_WHITE(ch) || ch == '\n')
+ while (getci (fd, ch) != EOF)
+ if (!(IS_WHITE(ch) || ch == '\n'))
+ break
+
+ quote = 0
+ level = 1
+ nargs = 0
+ op = 1
+
+ if (ch == '(') {
+ while (getci (fd, ch) != EOF) {
+ if (ch == '"' || ch == '\'') {
+ if (quote == 0)
+ quote = ch
+ else if (quote == ch)
+ quote = 0
+
+ } else if (ch == '(' && quote == 0) {
+ level = level + 1
+ } else if (ch == ')' && quote == 0) {
+ level = level - 1
+ if (level <= 0) {
+ if (op > 1 && argbuf[op-1] != EOS)
+ nargs = nargs + 1
+ break
+ }
+
+ } else if (ch == ',' && level == 1 && quote == 0) {
+ ch = EOS
+ nargs = nargs + 1
+ } else if (ch == '\n') {
+ ch = ' '
+ } else if (ch == '\\' && quote == 0) {
+ ch = getci (fd, ch)
+ next
+ } else if (ch == '#' && quote == 0) {
+ while (getci (fd, ch) != EOF)
+ if (ch == '\n')
+ break
+ next
+ }
+
+ argbuf[op] = ch
+ op = min (maxch, op + 1)
+ }
+
+ GT_NEXTCH(gt) = NULL
+ }
+
+ argbuf[op] = EOS
+ return (nargs)
+end
diff --git a/sys/qpoe/qpgetx.x b/sys/qpoe/qpgetx.x
new file mode 100644
index 00000000..cf9468bb
--- /dev/null
+++ b/sys/qpoe/qpgetx.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_GETX -- Return the complex value of the named header parameter. Type
+# conversion is not permitted between complex and the other datatypes.
+
+complex procedure qp_getx (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer pp
+int qp_getparam()
+errchk qp_getparam, syserrs
+
+begin
+ # Lookup the parameter and it's value.
+ if (qp_getparam (qp, param, pp) != TY_COMPLEX)
+ call syserrs (SYS_QPBADCONV, param)
+ else if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ return (Memx[pp])
+end
diff --git a/sys/qpoe/qpgmsym.x b/sys/qpoe/qpgmsym.x
new file mode 100644
index 00000000..883f5c22
--- /dev/null
+++ b/sys/qpoe/qpgmsym.x
@@ -0,0 +1,76 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_GMSYM -- Lookup the named macro in the symbol table and return a pointer
+# to the symstruct describing the macro as the function value. NULL is
+# returned if the macro is not defined, or if the named symbol is not a macro.
+# Local macros take precedence over global macros. In the case of a local
+# macro whose value is stored in the datafile, we have to allocate an internal
+# buffer to hold the data after we exit; this data must be used promptly,
+# before the routine is again called.
+
+pointer procedure qp_gmsym (qp, macro, textp)
+
+pointer qp #I QPOE descriptor
+char macro[ARB] #I macro name
+pointer textp #O char pointer to macro text
+
+int sz_textbuf, nchars, fd
+pointer st, sm, sym, textbuf
+data textbuf /NULL/, sz_textbuf /NULL/
+
+int fm_getfd(), read()
+pointer qm_symtab(), strefsbuf(), stfind()
+errchk realloc, fm_getfd, seek, read
+
+begin
+ st = QP_ST(qp)
+ sm = qm_symtab (QP_QM(qp))
+
+ # First look in the datafile local symbol table. Macros are stored
+ # in the datafile symbol table as string macros of type TY_MACRO.
+
+ sym = stfind (st, macro)
+ if (sym != NULL)
+ if (S_DTYPE(sym) == TY_MACRO)
+ if (and (S_FLAGS(sym), SF_DELETED) == 0) {
+ if (S_LFILE(sym) > 0) {
+ # Macro value stored as data.
+
+ # Make sure the text buffer is large enough.
+ if (sz_textbuf < S_NELEM(sym)) {
+ sz_textbuf = S_NELEM(sym)
+ call realloc (textbuf, sz_textbuf, TY_CHAR)
+ }
+
+ # Read the data.
+ fd = fm_getfd (QP_FM(qp), S_LFILE(sym), READ_ONLY, 0)
+
+ call seek (fd, S_OFFSET(sym))
+ nchars = max (0, read (fd, Memc[textbuf], S_NELEM(sym)))
+ Memc[textbuf+nchars] = EOS
+ textp = textbuf
+
+ call fm_retfd (QP_FM(qp), S_LFILE(sym))
+
+ } else {
+ # Macro value stored in symbol table.
+ textp = strefsbuf (st, S_OFFSET(sym))
+ }
+
+ # Exit if a local symbol was found.
+ return (sym)
+ }
+
+ # Next look in the global macro symbol table.
+ sym = stfind (sm, macro)
+ if (sym != NULL)
+ if (and (S_FLAGS(sym), SF_DELETED) == 0)
+ textp = strefsbuf (sm, S_OFFSET(sym))
+ else
+ sym = NULL
+
+ return (sym)
+end
diff --git a/sys/qpoe/qpgnfn.x b/sys/qpoe/qpgnfn.x
new file mode 100644
index 00000000..c12adc76
--- /dev/null
+++ b/sys/qpoe/qpgnfn.x
@@ -0,0 +1,240 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+.help qp_gnfn
+.nf --------------------------------------------------------------------------
+QP_GNFN -- Access the file header as a parameter list.
+
+ list = qp_ofnl[su] (qp, template)
+ nchars|EOF = qp_gnfn (list, outstr, maxch)
+ len = qp_lenfnl (list)
+ qp_seekfnl (list, pos)
+ qp_cfnl (list)
+
+These routines are used to determine the names of the fields (parameters) in
+the QPOE file header, e.g., to list out the header. QP_OFNLS and QP_ONFLU open
+the file header (sorted or unsorted). QP_GNFN returns the next parameter name,
+returning as the function value the string length of the parameter name or EOF
+when the end of the list is reached. QP_SFNL seeks on or rewinds the list.
+QP_CFNL closes the list descriptor.
+.endhelp --------------------------------------------------------------------
+
+# Size limiting definitions.
+define DEF_LENOFFV 128 # initial length of keywd-offset vector
+define INC_LENOFFV 128 # increment to above
+define DEF_SZSBUF 1024 # initial size of string buffer
+define INC_SZSBUF 1024 # increment to above
+
+# List descriptor.
+define LEN_FL 3
+define FL_LEN Memi[$1] # number of names in list
+define FL_POS Memi[$1+1] # current position
+define FL_SBUF Memi[$1+2] # pointer to string buffer
+define FL_OFFV Memi[$1+3] # pointer to offset vector
+
+
+# QP_OFNLS -- Open a sorted field name list.
+
+pointer procedure qp_ofnls (qp, template)
+
+pointer qp #I QPOE descriptor
+char template[ARB] #I field name template
+
+pointer qp_ofnl()
+
+begin
+ return (qp_ofnl (qp, template, true))
+end
+
+
+# QP_OFNLU -- Open an unsorted field name list.
+
+pointer procedure qp_ofnlu (qp, template)
+
+pointer qp #I QPOE descriptor
+char template[ARB] #I field name template
+
+pointer qp_ofnl()
+
+begin
+ return (qp_ofnl (qp, template, false))
+end
+
+
+# QP_OFNL -- Open a sorted or unsorted field name list.
+
+pointer procedure qp_ofnl (qp, template, sort)
+
+pointer qp #I QPOE descriptor
+char template[ARB] #I field name template
+bool sort #I sort list of matched names?
+
+pointer sp, patbuf, pattern, sym, fl, st, offv, sbuf, ip, op
+int len_offv, sz_sbuf, nsyms, nc, junk, nchars, i, nmatch
+
+pointer sthead(), stnext(), stname()
+int patmake(), patmatch(), strlen()
+define swap {junk=$1;$1=$2;$2=junk}
+errchk calloc, malloc, realloc
+
+begin
+ call smark (sp)
+ call salloc (pattern, SZ_LINE, TY_CHAR)
+ call salloc (patbuf, SZ_LINE, TY_CHAR)
+
+ # Allocate the list descriptor.
+ call calloc (fl, LEN_FL, TY_STRUCT)
+ call malloc (offv, DEF_LENOFFV, TY_INT)
+ call malloc (sbuf, DEF_SZSBUF, TY_CHAR)
+
+ len_offv = DEF_LENOFFV
+ sz_sbuf = DEF_SZSBUF
+ st = QP_ST(qp)
+ nsyms = 0
+ nc = 0
+
+ # Default to match all; map '*' into '?*', which is probably what
+ # the user intends. Match only at the beginning of line as we want
+ # to match only entire field name strings.
+
+ if (template[1] == EOS)
+ call strcpy ("?*", Memc[pattern], SZ_LINE)
+ else {
+ op = pattern
+ Memc[op] = '^'
+ op = op + 1
+ for (ip=1; template[ip] != EOS && ip < SZ_LINE; ip=ip+1) {
+ if (template[ip] == '*')
+ if (ip == 1 || (ip > 1 && template[ip-1] != ']')) {
+ Memc[op] = '?'
+ op = op + 1
+ }
+ Memc[op] = template[ip]
+ op = op + 1
+ }
+ Memc[op] = EOS
+ }
+
+ # Compile the pattern matching template.
+ junk = patmake (Memc[pattern], Memc[patbuf], SZ_LINE)
+
+ # Scan the symbol table and generate the unsorted list.
+ for (sym=sthead(st); sym != NULL; sym=stnext(st,sym)) {
+ if (and (S_FLAGS(sym), SF_DELETED) != 0)
+ next
+
+ # Get the symbol name.
+ ip = stname (st, sym)
+ nchars = strlen (Memc[ip])
+
+ # Save in list if it matches.
+ nmatch = patmatch (Memc[ip], Memc[patbuf]) - 1
+ if (nmatch > 0 && nmatch == nchars) {
+ nsyms = nsyms + 1
+
+ # Make room in offset vector?
+ if (nsyms > len_offv) {
+ len_offv = len_offv + INC_LENOFFV
+ call realloc (offv, len_offv, TY_INT)
+ }
+
+ # Make room in string buffer?
+ if (nc + nchars + 1 > sz_sbuf) {
+ sz_sbuf = sz_sbuf + INC_SZSBUF
+ call realloc (sbuf, sz_sbuf, TY_CHAR)
+ }
+
+ # Add the symbol.
+ Memi[offv+nsyms-1] = nc + 1
+ call strcpy (Memc[ip], Memc[sbuf+nc], nchars)
+ nc = nc + nchars + 1
+ }
+ }
+
+ # Sort the list if indicated, else reverse the order of the list
+ # to get a time-ordered (FIFO) list.
+
+ if (sort)
+ call strsrt (Memi[offv], Memc[sbuf], nsyms)
+ else {
+ do i = 1, nsyms / 2
+ swap (Memi[offv+i-1], Memi[offv+nsyms-i])
+ }
+
+ # Finish setting up the descriptor.
+ FL_LEN(fl) = nsyms
+ FL_SBUF(fl) = sbuf
+ FL_OFFV(fl) = offv
+
+ call sfree (sp)
+ return (fl)
+end
+
+
+# QP_GNFN -- Return the next element from the field name list. The string
+# length is returned as the function value, or EOF at the end of the list.
+
+int procedure qp_gnfn (fl, outstr, maxch)
+
+pointer fl #I list descriptor
+char outstr[maxch] #O output string
+int maxch #I max chars out
+
+int pos, off, nchars
+int gstrcpy()
+
+begin
+ pos = FL_POS(fl)
+ if (pos >= FL_LEN(fl))
+ return (EOF)
+
+ off = Memi[FL_OFFV(fl) + pos]
+ nchars = gstrcpy (Memc[FL_SBUF(fl)+off-1], outstr, maxch)
+
+ FL_POS(fl) = pos + 1
+ return (nchars)
+end
+
+
+# QP_LENFNL -- Return the length of (number of names in) the field name list.
+
+int procedure qp_lenfnl (fl)
+
+pointer fl #I list descriptor
+
+begin
+ return (FL_LEN(fl))
+end
+
+
+# QP_SEEKFNL -- Seek on the field name list.
+
+procedure qp_seekfnl (fl, pos)
+
+pointer fl #I list descriptor
+int pos #I desired list element, BOF, EOF
+
+begin
+ switch (pos) {
+ case BOF:
+ FL_POS(fl) = 0
+ case EOF:
+ FL_POS(fl) = FL_LEN(fl)
+ default:
+ FL_POS(fl) = max(0, min(FL_LEN(fl), pos - 1))
+ }
+end
+
+
+# QP_CFNL -- Close a field name list.
+
+procedure qp_cfnl (fl)
+
+pointer fl #I list descriptor
+
+begin
+ call mfree (FL_SBUF(fl), TY_CHAR)
+ call mfree (FL_OFFV(fl), TY_INT)
+ call mfree (fl, TY_STRUCT)
+end
diff --git a/sys/qpoe/qpgpar.x b/sys/qpoe/qpgpar.x
new file mode 100644
index 00000000..f3307043
--- /dev/null
+++ b/sys/qpoe/qpgpar.x
@@ -0,0 +1,101 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include "qpoe.h"
+
+# QP_GETPARAM -- Lookup the named parameter in the symbol table and return
+# a pointer to the scalar parameter value. A NULL pointer is returned if
+# the parameter exists but does not currently have a value. The parameter
+# datatype code is returned as the function value. The pointed to parameter
+# value will be clobbered in the next call, hence should be used promptly.
+# The data element pointed to may be a structure as well as a primitive type.
+
+int procedure qp_getparam (qp, param, o_pp)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+pointer o_pp #O pointer to parameter value
+
+int loc_pval, loc_Mem, ip, ch, elem, sz_elem, fd
+pointer sp, key, fm, pp, op, sym
+double pval[LEN_PVAL+1]
+data pp /NULL/
+
+pointer qp_gpsym()
+int qp_sizeof(), fm_getfd(), qp_ctoi(), read()
+errchk qp_bind, qp_gpsym, syserrs, fm_getfd, seek, read
+
+begin
+ call smark (sp)
+ call salloc (key, SZ_FNAME, TY_CHAR)
+
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ fm = QP_FM(qp)
+
+ # Compute pointer (Memc index) to the static pval buffer.
+ # Make sure that the computed pointer is double aligned.
+
+ if (pp == NULL) {
+ call zlocva (pval, loc_pval)
+ call zlocva (Memc, loc_Mem)
+ pp = (loc_pval+SZ_DOUBLE - loc_Mem) / SZ_DOUBLE * SZ_DOUBLE + 1
+ }
+
+ # Extract the primary parameter name, minus any whitespace and
+ # subscript (e.g., "param[elem]").
+
+ op = key
+ do ip = 1, SZ_FNAME {
+ ch = param[ip]
+ if (IS_WHITE(ch))
+ next
+ else if (ch == '[' || ch == EOS)
+ break
+ Memc[op] = ch
+ op = op + 1
+ }
+ Memc[op] = EOS
+
+ # Determine the array element (default [1]).
+ elem = 1
+ if (param[ip] == '[') {
+ ip = ip + 1
+ if (qp_ctoi (param, ip, elem) <= 0)
+ elem = 1
+ }
+
+ # Lookup the symbol in the symbol table.
+ sym = qp_gpsym (qp, Memc[key])
+ if (sym == NULL)
+ call syserrs (SYS_QPUKNPAR, param)
+
+ # Check to make sure the parameter value exists, and fetch the
+ # value from the lfile where the parameter data is stored, setting
+ # the parameter value pointer to point to the stored value.
+
+ if (elem < 1 || elem > S_NELEM(sym))
+ o_pp = NULL
+ else {
+ sz_elem = qp_sizeof (qp, S_DTYPE(sym), sym, INSTANCEOF)
+ if (sz_elem > LEN_PVAL * SZ_DOUBLE)
+ call syserrs (SYS_QPPVALOVF, QP_DFNAME(qp))
+
+ fd = fm_getfd (fm, S_LFILE(sym), READ_ONLY, 0)
+
+ call seek (fd, S_OFFSET(sym) + (elem - 1) * sz_elem)
+ if (read (fd, Memc[pp], sz_elem) < sz_elem)
+ o_pp = NULL
+ else if (S_DTYPE(sym) == TY_USER)
+ o_pp = (pp - 1) / SZ_STRUCT + 1
+ else
+ o_pp = (pp - 1) / sz_elem + 1
+
+ call fm_retfd (fm, S_LFILE(sym))
+ }
+
+ call sfree (sp)
+ return (S_DTYPE(sym))
+end
diff --git a/sys/qpoe/qpgpsym.x b/sys/qpoe/qpgpsym.x
new file mode 100644
index 00000000..b18199e2
--- /dev/null
+++ b/sys/qpoe/qpgpsym.x
@@ -0,0 +1,90 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_GPSYM -- Lookup the named parameter in the symbol table and return
+# a pointer to the symstruct describing the parameter as the function value.
+# NULL is returned if the parameter is not defined, or if the named symbol is
+# not a parameter. Global parameter aliases are recursively expanded.
+# Local macros are not expanded at this level, since local macros are stored
+# as parameters themselves.
+
+pointer procedure qp_gpsym (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+int n
+pointer sp, pname, sym, st, sm
+pointer stfind(), strefsbuf(), qm_symtab()
+errchk syserrs
+
+#pointer ip
+#int fd, nchars
+#int fm_getfd(), read()
+#errchk fm_getfd, read, seek
+
+begin
+ call smark (sp)
+ call salloc (pname, SZ_FNAME, TY_CHAR)
+
+ st = QP_ST(qp)
+ sm = qm_symtab (QP_QM(qp))
+ call strcpy (param, Memc[pname], SZ_FNAME)
+
+ # First expand any aliases in the global macro symbol table.
+ sym = stfind (sm, param)
+ for (n=1; sym != NULL; n=n+1) {
+ if (and (S_FLAGS(sym), SF_DELETED) != 0)
+ break
+ call strcpy (strefsbuf(sm,S_OFFSET(sym)), Memc[pname], SZ_FNAME)
+ sym = stfind (sm, Memc[pname])
+ if (n > MAX_INDIR)
+ call syserrs (SYS_QPMRECUR, param)
+ }
+
+ # Lookup the symbol in the datafile local symbol table. Datafile
+ # local macros cannot be expanded in parameter references, since
+ # the macros are themselves stored as parameters (if macro parameters
+ # were expanded in parameter references, there would be no simple
+ # way to access the macro parameters themselves).
+
+ sym = stfind (st, Memc[pname])
+
+# Disable expansion of datafile-local macro defines.
+# if (sym != NULL) {
+# for (n=0; S_DTYPE(sym) == TY_MACRO; n=n+1) {
+# if (and (S_FLAGS(sym), SF_DELETED) != 0) {
+# break
+#
+# } else if (S_LFILE(sym) > 0) {
+# # Macro value stored as data.
+# fd = fm_getfd (QP_FM(qp), S_LFILE(sym), READ_ONLY, 0)
+#
+# call seek (fd, S_OFFSET(sym))
+# nchars = max (0, read (fd, Memc[pname], S_NELEM(sym)))
+# Memc[pname+nchars] = EOS
+# ip = pname
+#
+# call fm_retfd (QP_FM(qp), S_LFILE(sym))
+#
+# } else {
+# # Macro value stored in symbol table.
+# ip = strefsbuf (st, S_OFFSET(sym))
+# }
+#
+# # Macro recursion.
+# if (n > MAX_INDIR)
+# call syserrs (SYS_QPMRECUR, param)
+# }
+# }
+
+ # Don't "find" the symbol if it has been deleted.
+ if (sym != NULL)
+ if (and (S_FLAGS(sym), SF_DELETED) != 0)
+ sym = NULL
+
+ call sfree (sp)
+ return (sym)
+end
diff --git a/sys/qpoe/qpgstr.x b/sys/qpoe/qpgstr.x
new file mode 100644
index 00000000..91a03a0f
--- /dev/null
+++ b/sys/qpoe/qpgstr.x
@@ -0,0 +1,42 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_GSTR -- Return the string value of the named parameter.
+
+int procedure qp_gstr (qp, param, outstr, maxch)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char outstr[maxch] #O receives string value
+int maxch #I max chars out
+
+pointer sym, fm
+int nchars, fd
+pointer qp_gpsym()
+int fm_getfd(), read()
+errchk qp_bind, qp_gpsym, syserrs, fm_getfd, seek
+
+begin
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+ fm = QP_FM(qp)
+
+ # Lookup the symbol in the symbol table.
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL)
+ call syserrs (SYS_QPUKNPAR, param)
+ else if (!(S_DTYPE(sym) == TY_CHAR || S_DTYPE(sym) == TY_USER))
+ call syserrs (SYS_QPBADCONV, param)
+
+ # Fetch the string value from the lfile where the data is stored.
+ fd = fm_getfd (fm, S_LFILE(sym), READ_ONLY, 0)
+ call seek (fd, S_OFFSET(sym))
+
+ nchars = max (0, read (fd, outstr, min(S_NELEM(sym),maxch)))
+ outstr[nchars+1] = EOS
+
+ call fm_retfd (fm, S_LFILE(sym))
+ return (nchars)
+end
diff --git a/sys/qpoe/qpinherit.x b/sys/qpoe/qpinherit.x
new file mode 100644
index 00000000..495966f4
--- /dev/null
+++ b/sys/qpoe/qpinherit.x
@@ -0,0 +1,57 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include "qpoe.h"
+
+# QP_INHERIT -- Copy all the inheritable parameters from one datafile to
+# another.
+
+procedure qp_inherit (n_qp, o_qp, out)
+
+pointer n_qp #I QPOE descriptor of new datafile
+pointer o_qp #I QPOE descriptor of old datafile
+int out #I output stream for verbose messages, or NULL
+
+int nsyms, i
+pointer sp, n_st, o_st, sym, op, pname, syms
+pointer sthead(), stnext(), stname()
+int qp_accessf()
+
+begin
+ call smark (sp)
+
+ n_st = QP_ST(n_qp)
+ o_st = QP_ST(o_qp)
+
+ # Count the symbols to be copied.
+ nsyms = 0
+ for (sym=sthead(o_st); sym != NULL; sym=stnext(o_st,sym))
+ if (and (S_FLAGS(sym), SF_DELETED) == 0)
+ if (and (S_FLAGS(sym), SF_INHERIT) != 0)
+ nsyms = nsyms + 1
+
+ # Construct a reversed array of symbol pointers.
+ call salloc (syms, nsyms, TY_POINTER)
+ op = syms + nsyms - 1
+ for (sym=sthead(o_st); sym != NULL; sym=stnext(o_st,sym))
+ if (and (S_FLAGS(sym), SF_DELETED) == 0)
+ if (and (S_FLAGS(sym), SF_INHERIT) != 0) {
+ Memi[op] = sym
+ op = op - 1
+ }
+
+ # Copy each symbol.
+ do i = 1, nsyms {
+ pname = stname (o_st, Memi[syms+i-1])
+ if (qp_accessf (n_qp, Memc[pname]) == YES) {
+ if (out != NULL) {
+ call fprintf (out,
+ "parameter `%s' already exists, not copied\n")
+ call pargstr (Memc[pname])
+ }
+ } else iferr (call qp_copyf (o_qp, Memc[pname], n_qp, Memc[pname]))
+ call erract (EA_WARN)
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpio.h b/sys/qpoe/qpio.h
new file mode 100644
index 00000000..91b58c73
--- /dev/null
+++ b/sys/qpoe/qpio.h
@@ -0,0 +1,140 @@
+# QPIO.H -- Definitions for the QPOE event i/o subpackage.
+
+# Default parameter and domain names.
+define DEF_BLOCK "defblock" # header param - default block factor
+define DEF_XBLOCK "defxblock" # header param - default X block factor
+define DEF_YBLOCK "defyblock" # header param - default Y block factor
+define DEF_MASK "defmask" # header param - default region mask
+define DEF_FILTER "deffilt" # header param - default event filter
+define DEF_EVENTTYPE "event" # default name of user event datatype
+define DEF_EVENTPARAM "events" # default event-list parameter
+
+# QPIO keywords recognized in expressions (abbreviations permitted).
+define KEYWORDS "|block|xblock|yblock|debug|filter|key|noindex|param|mask|rect|"
+
+define KW_BLOCK 1 # blocking factor for image matrix
+define KW_XBLOCK 2 # X blocking factor for image matrix
+define KW_YBLOCK 3 # Y blocking factor for image matrix
+define KW_DEBUG 4 # debug level (integer, 0=nodebug)
+define KW_FILTER 5 # event attribute filter
+define KW_KEY 6 # event key (Y,X) fields (e.g.(s10,s8))
+define KW_NOINDEX 7 # don't use index even if present
+define KW_PARAM 8 # name of event list header parameter
+define KW_MASK 9 # region mask
+define KW_RECT 10 # rectangle (bounding box) for i/o
+
+# Size limiting definitions.
+define SZ_EVLIST 1024 # event list buffer size (arbitrary)
+define NDIM 2 # all QPOE images are 2-dim
+
+# The main QPIO descriptor.
+define LEN_IODES 82
+
+# general
+define IO_QP Memi[$1] # backpointer to QPOE descriptor
+define IO_MODE Memi[$1+1] # read_only or new_file
+define IO_DEBUG Memi[$1+2] # debug level
+define IO_NLINES Memi[$1+3] # number of image lines (physical)
+define IO_NCOLS Memi[$1+4] # number of image columns (physical)
+define IO_XBLOCK Memr[P2R($1+5)] # blocking factor for qpio_readpix
+define IO_YBLOCK Memr[P2R($1+6)] # blocking factor for qpio_readpix
+define IO_OPTBUFSIZE Memi[$1+7] # optbufsize for FIO (qpio_readpix)
+define IO_NOINDEX Memi[$1+8] # don't use indexed extraction
+define IO_NODEFFILT Memi[$1+9] # disable use of default filter
+define IO_NODEFMASK Memi[$1+10] # disable use of default mask
+define IO_PARAM Memi[$1+11] # pointer to buffer with param name
+define IO_PSYM Memi[$1+12] # symbol table entry for parameter
+define IO_MASK Memi[$1+13] # pointer to buffer with mask name
+define IO_MDEPTH Memi[$1+14] # mask depth, bits
+define IO_EXCLOSE Memi[$1+15] # qpex was opened by qpio
+define IO_PLCLOSE Memi[$1+16] # mask was opened by qpio
+define IO_PL Memi[$1+17] # PLIO (mask) pointer
+define IO_EX Memi[$1+18] # QPEX (event attribute filter) pointer
+define IO_FD Memi[$1+19] # file descriptor of open lfile
+define IO_LF Memi[$1+20] # lfile where event list is stored
+define IO_CHAN Memi[$1+21] # i/o channel of open lfile
+# events
+define IO_DD Memi[$1+22] # pointer to domain descriptor
+define IO_EVXOFF Memi[$1+23] # offset of X field used for extraction
+define IO_EVXTYPE Memi[$1+24] # datatype of X field
+define IO_EVYOFF Memi[$1+25] # offset of Y field used for extraction
+define IO_EVYTYPE Memi[$1+26] # datatype of Y field
+define IO_EVENTLEN Memi[$1+27] # length of event struct, shorts
+define IO_MINEVL Memi[$1+28] # pointer to min event for full list
+define IO_MAXEVL Memi[$1+29] # pointer to max event for full list
+# buckets
+define IO_SZBBUCKET Memi[$1+30] # event file bucket size, bytes
+define IO_BUCKETLEN Memi[$1+31] # nevents per bucket (excl. min/max)
+define IO_NEVENTS Memi[$1+32] # total data events in event list
+define IO_FBOFF Memi[$1+33] # lfile offset of first bucket
+define IO_EVMINOFF Memi[$1+34] # offset to the MIN event in a bucket
+define IO_EVMAXOFF Memi[$1+35] # offset to the MAX event in a bucket
+# index
+define IO_INDEXLEN Memi[$1+38] # length of index (same as nlines)
+define IO_IXXOFF Memi[$1+39] # offset of X field used in index
+define IO_IXXTYPE Memi[$1+40] # datatype of X field used in index
+define IO_IXYOFF Memi[$1+41] # offset of Y field used in index
+define IO_IXYTYPE Memi[$1+42] # datatype of Y field used in index
+define IO_YOFFVP Memi[$1+43] # pointer to Y-index array (len=nlines)
+define IO_YLENVP Memi[$1+44] # pointer to Y-line length array
+define IO_YOFFVOFF Memi[$1+45] # lfile offset of stored YOFFV
+define IO_YOFFVLEN Memi[$1+46] # length, words, of compressed YOFFV
+define IO_YLENVOFF Memi[$1+47] # lfile offset of stored YLENV
+define IO_YLENVLEN Memi[$1+48] # length, words, of compressed YLENV
+# i/o
+define IO_ACTIVE Memi[$1+50] # set once i/o begins
+define IO_IOTYPE Memi[$1+51] # type of i/o selected for BB
+define IO_LINEIO Memi[$1+52] # flag - BB width is full line width
+define IO_RMUSED Memi[$1+53] # flag - region mask used in this BB
+define IO_BBUSED Memi[$1+54] # flag - bounding box in use
+define IO_BBMASK Memi[$1+55] # BB region mask subras, nonindexed i/o
+define IO_RL Memi[$1+56] # range list pointer
+define IO_RLI Memi[$1+57] # range list index
+define IO_EVI Memi[$1+58] # event index into event list (for i/o)
+define IO_EV1 Memi[$1+59] # event index of first event on line
+define IO_EV2 Memi[$1+60] # event index of last event on line
+define IO_BP Memi[$1+61] # pointer to bucket buffer
+define IO_BKNO Memi[$1+62] # bucket number
+define IO_BKFIRSTEV Memi[$1+63] # event index of first event in bucket
+define IO_BKLASTEV Memi[$1+64] # event index of last event in bucket
+# (avail)
+define IO_V Meml[$1+70+$2-1]# current vector
+define IO_VS Meml[$1+72+$2-1]# start vector
+define IO_VE Meml[$1+74+$2-1]# end vector
+define IO_VN Meml[$1+76+$2-1]# size of section
+define IO_VSDEF Meml[$1+78+$2-1]# default start vector
+define IO_VEDEF Meml[$1+80+$2-1]# default end vector
+
+# Handy macros.
+define IO_MINEVB (IO_BP($1)+IO_EVMINOFF($1))
+define IO_MAXEVB (IO_BP($1)+IO_EVMAXOFF($1))
+define EVI_TO_BUCKET ((($2)-1)/IO_BUCKETLEN($1)+1)
+define BUCKET_TO_EVI ((($2)-1)*IO_BUCKETLEN($1)+1)
+
+# I/O types (specially optimized code for each case).
+define NoINDEX_NoRMorBB 0 # nonindexed, no RM no BB
+define NoINDEX_RMorBB 1 # nonindexed, maybe RM or BB
+define INDEX_NoRMorBB 2 # indexed, no RM or BB
+define INDEX_RMorBB 3 # indexed, maybe RM or BB
+define NoDATA_NoAREA 4 # no events can be returned
+
+# Stored Event List header (one per stored event list).
+define LEN_EHDES 18
+define EH_NEVENTS Memi[$1] # total data events in event list
+define EH_EVENTLEN Memi[$1+1] # event length, shorts
+define EH_SZBBUCKET Memi[$1+2] # event file bucket size, bytes
+define EH_BUCKETLEN Memi[$1+3] # nevents per bucket (excl. min/max)
+define EH_FBOFF Memi[$1+4] # lfile offset of first bucket
+define EH_EVMINOFF Memi[$1+5] # offset to the MIN event in a bucket
+define EH_EVMAXOFF Memi[$1+6] # offset to the MAX event in a bucket
+define EH_MINEVLOFF Memi[$1+7] # offset of stored MINEVL
+define EH_MAXEVLOFF Memi[$1+8] # offset of stored MAXEVL
+define EH_INDEXLEN Memi[$1+9] # length of index (same as nlines)
+define EH_YOFFVOFF Memi[$1+10] # lfile offset of stored YOFFV
+define EH_YOFFVLEN Memi[$1+11] # length, words, of compressed YOFFV
+define EH_YLENVOFF Memi[$1+12] # lfile offset of stored YLENV
+define EH_YLENVLEN Memi[$1+13] # length, words, of compressed YLENV
+define EH_IXXOFF Memi[$1+14] # event offset of indexed X field
+define EH_IXYOFF Memi[$1+15] # event offset of indexed Y field
+define EH_IXXTYPE Memi[$1+16] # datatype of indexed X field
+define EH_IXYTYPE Memi[$1+17] # datatype of indexed Y field
diff --git a/sys/qpoe/qpioclose.x b/sys/qpoe/qpioclose.x
new file mode 100644
index 00000000..78893487
--- /dev/null
+++ b/sys/qpoe/qpioclose.x
@@ -0,0 +1,49 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpio.h"
+
+# QPIO_CLOSE -- Close the QPIO descriptor. If writing to the event list,
+# the output bucket is automatically flushed and the event list header updated.
+
+procedure qpio_close (io)
+
+pointer io #I QPIO descriptor
+
+begin
+ if (IO_DEBUG(io) > 1) {
+ call eprintf ("qpio_close (%xX)\n")
+ call pargi (io)
+ }
+
+ call qpio_sync (io)
+
+ if (IO_EX(io) != NULL && IO_EXCLOSE(io) == YES)
+ call qpex_close (IO_EX(io))
+ if (IO_PL(io) != NULL && IO_PLCLOSE(io) == YES)
+ call pl_close (IO_PL(io))
+ if (IO_FD(io) != NULL)
+ call close (IO_FD(io))
+
+ if (IO_BP(io) != NULL)
+ call mfree (IO_BP(io), TY_SHORT)
+ if (IO_RL(io) != NULL)
+ call mfree (IO_RL(io), TY_INT)
+ if (IO_MINEVL(io) != NULL)
+ call mfree (IO_MINEVL(io), TY_SHORT)
+ if (IO_MAXEVL(io) != NULL)
+ call mfree (IO_MAXEVL(io), TY_SHORT)
+ if (IO_YLENVP(io) != NULL)
+ call mfree (IO_YLENVP(io), TY_INT)
+ if (IO_YOFFVP(io) != NULL)
+ call mfree (IO_YOFFVP(io), TY_INT)
+ if (IO_DD(io) != NULL)
+ call mfree (IO_DD(io), TY_STRUCT)
+ if (IO_BBMASK(io) != NULL)
+ call plr_close (IO_BBMASK(io))
+ if (IO_PARAM(io) != NULL)
+ call mfree (IO_PARAM(io), TY_CHAR)
+ if (IO_MASK(io) != NULL)
+ call mfree (IO_MASK(io), TY_CHAR)
+
+ call mfree (io, TY_STRUCT)
+end
diff --git a/sys/qpoe/qpiogetev.gx b/sys/qpoe/qpiogetev.gx
new file mode 100644
index 00000000..e6fbc612
--- /dev/null
+++ b/sys/qpoe/qpiogetev.gx
@@ -0,0 +1,467 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <pmset.h>
+include "../qpio.h"
+
+define RLI_NEXTLINE 9998
+define RLI_INITIALIZE 9999
+define SZ_CODE 7
+
+# QPIO_GETEVENTS -- Return a sequence of events sharing the same mask value
+# which satisfy the current event attribute filter. The returned events will
+# be only those in a rectangular subregion of the image (specified by a prior
+# call to qpio_setrange) which are also visible through the current mask.
+# Sequences of events are returned in storage order until the region is
+# exhausted, at which time EOF is returned.
+#
+# NOTE - If debug statements (printfs) are placed in this code they will cause
+# i/o problems at runtime due to reentrancy, since this routine is called in
+# a low level FIO pseudodevice driver (QPF). This is also true of any of the
+# routines called by this procedure, and of the related routine QPIO_READPIX.
+
+int procedure qpio_gvtevents (io, o_ev, maskval, maxev, o_nev)
+
+pointer io #I QPIO descriptor
+pointer o_ev[maxev] #O receives the event struct pointers
+int maskval #O receives the mask value of the events
+int maxev #I max events out
+int o_nev #O same as function value (nev_out|EOF)
+
+int status
+char code[SZ_CODE]
+int qpx_gvs(), qpx_gvi(), qpx_gvl(), qpx_gvr(), qpx_gvd()
+errchk syserrs
+define err_ 91
+
+begin
+ # The generic routines currently require that X,Y be the same type.
+ # It wouldn't be hard to remove this restriction if necessary, but
+ # it simplifies things and I doubt if a mixed types feature would
+ # be used very often.
+
+ if (IO_EVXTYPE(io) != IO_EVYTYPE(io))
+ goto err_
+
+ # Get the events.
+ switch (IO_EVXTYPE(io)) {
+ case TY_SHORT:
+ status = qpx_gvs (io, o_ev, maskval, maxev, o_nev)
+ case TY_INT:
+ status = qpx_gvi (io, o_ev, maskval, maxev, o_nev)
+ case TY_LONG:
+ status = qpx_gvl (io, o_ev, maskval, maxev, o_nev)
+ case TY_REAL:
+ status = qpx_gvr (io, o_ev, maskval, maxev, o_nev)
+ case TY_DOUBLE:
+ status = qpx_gvd (io, o_ev, maskval, maxev, o_nev)
+ default:
+err_ call sprintf (code, SZ_CODE, "%d")
+ call pargi (IO_EVXTYPE(io))
+ call syserrs (SYS_QPINVEVT, code)
+ }
+
+ return (status)
+end
+
+
+$for (silrd)
+
+# QPX_GV -- Internal generic code for qpio_getevents. There is one copy
+# of this routine for each event coordinate datatype. The optimization
+# strategy used here assumes that executing qpio_gv is much more expensive
+# than building the call in qpio_getevents. This will normally be the case
+# for a large event list or a complex expression, otherwise the operation
+# is likely to be fast enough that it doesn't matter anyway.
+
+int procedure qpx_gv$t (io, o_ev, maskval, maxev, o_nev)
+
+pointer io #I QPIO descriptor
+pointer o_ev[maxev] #O receives the event struct pointers
+int maskval #O receives the mask value of the events
+int maxev #I max events out
+int o_nev #O same as function value (nev_out|EOF)
+
+int x1, x2, y1, y2, xs, xe, ys, ye, x, y
+pointer pl, rl, rp, bp, ex, ev, ev_p, bbmask, bb_bufp
+bool useindex, lineio, bbused, rmused, nodata
+int bb_xsize, bb_ysize, bb_xblock, bb_yblock, ii, jj
+int v[NDIM], szs_event, mval, nev, evidx, evtop, temp, i
+int ev_xoff, ev_yoff
+
+pointer plr_open()
+bool pl_linenotempty(), pl_sectnotempty()
+int qpio_rbucket(), qpex_evaluate(), btoi(), plr_getpix()
+
+define swap {temp=$1;$1=$2;$2=temp}
+define putevent_ 91
+define again_ 92
+define done_ 93
+define exit_ 94
+
+begin
+ pl = IO_PL(io) # pixel list (region mask) descriptor
+ rl = IO_RL(io) # range list buffer
+ bp = IO_BP(io) # bucket buffer (type short)
+ ex = IO_EX(io) # QPEX (EAF) descriptor
+
+ # The following is executed when the first i/o is performed on a new
+ # region, to select the most efficient type of i/o to be performed,
+ # and initialize the i/o parameters for that case. The type of i/o
+ # to be performed depends upon whether or not an index can be used,
+ # and whether or not there is a region mask (RM) or bounding box (BB).
+ # The presence or absence of an event attribute filter (EAF) is not
+ # separated out as a special case, as it is quick and easy to test
+ # for the presence of an EAF and apply one it if it exists.
+
+ if (IO_ACTIVE(io) == NO) {
+ # Check for an index. We have an index if the event list is
+ # indexed, and the index is defined on the Y-coordinate we will
+ # be using for extraction.
+
+ useindex = (IO_INDEXLEN(io) == IO_NLINES(io) &&
+ IO_EVYOFF(io) == IO_IXYOFF(io) &&
+ IO_NOINDEX(io) == NO)
+
+ # Initialize the V and VN vectors.
+ do i = 1, NDIM {
+ IO_VN(io,i) = IO_VE(io,i) - IO_VS(io,i) + 1
+ if (IO_VN(io,i) < 0) {
+ swap (IO_VS(io,i), IO_VE(io,i))
+ IO_VN(io,i) = -IO_VN(io,i)
+ }
+ }
+ call amovi (IO_VS(io,1), IO_V(io,1), NDIM)
+
+ # Determine if full lines are to be accessed, and if a bounding
+ # box (subraster of the image) is defined.
+
+ lineio = (IO_VS(io,1) == 1 && IO_VE(io,1) == IO_NCOLS(io))
+ bbused = (!lineio || IO_VS(io,2) > 1 || IO_VE(io,2) < IO_NLINES(io))
+
+ # Determine if region mask data is to be used and if there is any
+ # data to be read.
+
+ nodata = (IO_NEVENTS(io) <= 0)
+ rmused = false
+
+ if (pl != NULL)
+ if (pl_sectnotempty (pl, IO_VS(io,1), IO_VE(io,1), NDIM))
+ rmused = true
+ else
+ nodata = true
+
+ # Select the optimal type of i/o to be used for extraction.
+ if (nodata) {
+ IO_IOTYPE(io) = NoDATA_NoAREA
+ useindex = false
+ bbused = false
+
+ } else if (bbused || rmused) {
+ if (useindex)
+ IO_IOTYPE(io) = INDEX_RMorBB
+ else
+ IO_IOTYPE(io) = NoINDEX_RMorBB
+
+ } else {
+ # If we are reading the entire image (no bounding box) and
+ # we are not using a mask, then there is no point in using
+ # indexed i/o.
+
+ IO_IOTYPE(io) = NoINDEX_NoRMorBB
+ useindex = false
+ }
+
+ # Initialize the range list data if it will be used.
+ if (useindex) {
+ # Dummy range specifying full line segment.
+ RLI_LEN(rl) = RL_FIRST
+ RLI_AXLEN(rl) = IO_NCOLS(io)
+
+ rp = rl + ((RL_FIRST - 1) * RL_LENELEM)
+ Memi[rp+RL_XOFF] = IO_VS(io,1)
+ Memi[rp+RL_NOFF] = IO_VN(io,1)
+ Memi[rp+RL_VOFF] = 1
+
+ IO_RLI(io) = RLI_INITIALIZE
+ }
+
+ # Open the mask for random access if i/o is not indexed and
+ # a region mask is used.
+
+ bbmask = IO_BBMASK(io)
+ if (bbmask != NULL)
+ call plr_close (bbmask)
+
+ if (IO_IOTYPE(io) == NoINDEX_RMorBB && rmused) {
+ bbmask = plr_open (pl, v, 0) # (v is never referenced)
+ call plr_setrect (bbmask, IO_VS(io,1),IO_VS(io,2),
+ IO_VE(io,1),IO_VE(io,2))
+ call plr_getlut (bbmask,
+ bb_bufp, bb_xsize, bb_ysize, bb_xblock, bb_yblock)
+ }
+
+ # Update the QPIO descriptor.
+ IO_LINEIO(io) = btoi(lineio)
+ IO_RMUSED(io) = btoi(rmused)
+ IO_BBUSED(io) = btoi(bbused)
+ IO_BBMASK(io) = bbmask
+
+ IO_EVI(io) = 1
+ IO_BKNO(io) = 0
+ IO_BKLASTEV(io) = 0
+
+ IO_ACTIVE(io) = YES
+ }
+
+ # Initialize event extraction parameters.
+ szs_event = IO_EVENTLEN(io)
+ maskval = 0
+ nev = 0
+
+ ev_xoff = IO_EVXOFF(io)
+ ev_yoff = IO_EVYOFF(io)
+
+ # Extract events using the most efficient type of i/o for the given
+ # selection critera (index, mask, BB, EAF, etc.).
+again_
+ switch (IO_IOTYPE(io)) {
+ case NoDATA_NoAREA:
+ # We know in advance that there are no events to be returned,
+ # either because there is no data, or the area of the region
+ # mask within the bounding box is empty.
+
+ goto exit_
+
+ case NoINDEX_NoRMorBB:
+ # This is the simplest case; no index, region mask, or bounding
+ # box. Read and output all events in sequence.
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Copy out the event pointers.
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ nev = min (maxev, IO_BKLASTEV(io) - IO_EVI(io) + 1)
+
+ do i = 1, nev {
+ o_ev[i] = ev
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = IO_EVI(io) + nev
+ maskval = 1
+
+ case NoINDEX_RMorBB:
+ # Fully general selection, including any combination of bounding
+ # box, region mask, or EAF, but no index, either because there is
+ # no index for this event list, or the index is for a different Y
+ # attribute than the one being used for extraction.
+
+ bbused = (IO_BBUSED(io) == YES)
+ x1 = IO_VS(io,1); x2 = IO_VE(io,1)
+ y1 = IO_VS(io,2); y2 = IO_VE(io,2)
+
+ # Refill the event bucket?
+ while (IO_EVI(io) > IO_BKLASTEV(io)) {
+ # Get the next bucket.
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Reject buckets that do not contain any events lying
+ # within the specified bounding box, if any.
+
+ if (bbused) {
+ ev_p = (IO_MINEVB(io) - 1) * SZ_SHORT / SZ_PIXEL + 1
+ $if (datatype == rd)
+ xs = Mem$t[ev_p+ev_xoff] + 0.5
+ ys = Mem$t[ev_p+ev_yoff] + 0.5
+ $else
+ xs = Mem$t[ev_p+ev_xoff]
+ ys = Mem$t[ev_p+ev_yoff]
+ $endif
+
+ ev_p = (IO_MAXEVB(io) - 1) * SZ_SHORT / SZ_PIXEL + 1
+ $if (datatype == rd)
+ xe = Mem$t[ev_p+ev_xoff] + 0.5
+ ye = Mem$t[ev_p+ev_yoff] + 0.5
+ $else
+ xe = Mem$t[ev_p+ev_xoff]
+ ye = Mem$t[ev_p+ev_yoff]
+ $endif
+
+ if (xs > x2 || xe < x1 || ys > y2 || ye < y1)
+ IO_EVI(io) = IO_BKLASTEV(io) + 1
+ }
+ }
+
+ # Copy out any events which pass the region mask and which share
+ # the same mask value. Note that in this case, to speed mask
+ # value lookup at random mask coordinates, the region mask for
+ # the bounding box is stored as a populated array in the QPIO
+ # descriptor.
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io) - 1) * szs_event
+ bbmask = IO_BBMASK(io)
+ mval = 0
+
+ do i = IO_EVI(io), IO_BKLASTEV(io) {
+ # Get event x,y coordinates in whatever coord system.
+ ev = ev + szs_event
+ ev_p = (ev - 1) * SZ_SHORT / SZ_PIXEL + 1
+
+ $if (datatype == rd)
+ x = Mem$t[ev_p+ev_xoff] + 0.5
+ y = Mem$t[ev_p+ev_yoff] + 0.5
+ $else
+ x = Mem$t[ev_p+ev_xoff]
+ y = Mem$t[ev_p+ev_yoff]
+ $endif
+
+ # Reject events lying outside the bounding box.
+ if (bbused)
+ if (x < x1 || x > x2 || y < y1 || y > y2)
+ next
+
+ # Take a shortcut if no region mask is in effect for this BB.
+ if (bbmask == NULL)
+ goto putevent_
+
+ # Get the mask pixel associated with this event.
+ ii = (x - 1) / bb_xblock
+ jj = (y - 1) / bb_yblock
+ mval = Memi[bb_bufp + jj*bb_xsize + ii]
+ if (mval < 0)
+ mval = plr_getpix (bbmask, x, y)
+
+ # Accumulate points lying in the first nonzero mask range
+ # encountered.
+
+ if (mval != 0) {
+ if (maskval == 0)
+ maskval = mval
+ if (mval == maskval) {
+putevent_ if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ } else
+ break
+ }
+ }
+
+ IO_EVI(io) = i
+
+ case INDEX_NoRMorBB, INDEX_RMorBB:
+ # General extraction for indexed data. Process successive ranges
+ # and range lists until we get at least one event which lies within
+ # the bounding box, within a range, and which passes the event
+ # attribute filter, if one is in use.
+
+ # If the current range list (mask line) has been exhausted, advance
+ # to the next line which contains both ranges and events. A range
+ # list is used to specify the bounding box even if we don't have
+ # a nonempty region mask within the BB.
+
+ if (IO_RLI(io) > RLI_LEN(rl)) {
+ repeat {
+ y = IO_V(io,2)
+ if (IO_RLI(io) == RLI_INITIALIZE)
+ IO_RLI(io) = RL_FIRST
+ else
+ y = y + 1
+
+ if (y > IO_VE(io,2)) {
+ if (nev <= 0) {
+ o_nev = EOF
+ return (EOF)
+ } else
+ goto done_
+ }
+
+ IO_V(io,2) = y
+ evidx = Memi[IO_YOFFVP(io)+y-1]
+
+ if (evidx > 0) {
+ if (IO_RMUSED(io) == YES) {
+ if (IO_LINEIO(io) == YES) {
+ if (!pl_linenotempty (pl,IO_V(io,1)))
+ next
+ } else {
+ v[1] = IO_VE(io,1); v[2] = y
+ if (!pl_sectnotempty (pl,IO_V(io,1),v,NDIM))
+ next
+ }
+ call pl_glri (pl, IO_V(io,1), Memi[rl],
+ IO_MDEPTH(io), IO_VN(io,1), PIX_SRC)
+ }
+ IO_RLI(io) = RL_FIRST
+ }
+ } until (IO_RLI(io) <= RLI_LEN(rl))
+
+ IO_EVI(io) = evidx
+ IO_EV1(io) = evidx
+ IO_EV2(io) = Memi[IO_YLENVP(io)+y-1] + evidx - 1
+ }
+
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ goto exit_
+
+ # Compute current range parameters and initialize event pointer.
+ rp = rl + (IO_RLI(io) - 1) * RL_LENELEM
+ x1 = Memi[rp+RL_XOFF]
+ x2 = x1 + Memi[rp+RL_NOFF] - 1
+ maskval = Memi[rp+RL_VOFF]
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ evtop = min (IO_EV2(io), IO_BKLASTEV(io))
+
+ # Extract events from bucket which lie within the current range
+ # of the current line. This is the inner loop of indexed event
+ # extraction, ignoring event attribute filtering.
+
+ do i = IO_EVI(io), evtop {
+ ev_p = (ev - 1) * SZ_SHORT / SZ_PIXEL + 1
+ $if (datatype == rd)
+ x = Mem$t[ev_p+ev_xoff] + 0.5
+ $else
+ x = Mem$t[ev_p+ev_xoff]
+ $endif
+ if (x >= x1) {
+ if (x > x2) {
+ IO_RLI(io) = IO_RLI(io) + 1
+ break
+ } else if (nev >= maxev)
+ break
+ nev = nev + 1
+ o_ev[nev] = ev
+ }
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = i
+ if (i > IO_EV2(io))
+ IO_RLI(io) = RLI_NEXTLINE
+ }
+done_
+ # Apply the event attribute filter if one is defined; repeat
+ # the whole process if we don't end up with any events.
+
+ if (nev > 0)
+ if (ex != NULL)
+ nev = qpex_evaluate (ex, o_ev, o_ev, nev)
+ if (nev <= 0)
+ goto again_
+exit_
+ o_nev = nev
+ if (o_nev <= 0)
+ o_nev = EOF
+
+ return (o_nev)
+end
+
+$endfor
diff --git a/sys/qpoe/qpiogetfil.x b/sys/qpoe/qpiogetfil.x
new file mode 100644
index 00000000..c653f6b5
--- /dev/null
+++ b/sys/qpoe/qpiogetfil.x
@@ -0,0 +1,123 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "qpoe.h"
+include "qpio.h"
+
+# QPIO_GETFILTER -- Get the current filtering parameters governing event
+# extraction via QPIO. These are the QPIO parameters (region mask, blocking
+# factor, coordinate system, etc.) plus the event attribute filter. We leave
+# it up to QPEX to reconstruct the EAF to allow for any dynamic edits which
+# may have taken place, e.g., via qpio_setfilter.
+
+int procedure qpio_getfilter (io, outstr, maxch)
+
+pointer io #I QPIO descriptor
+char outstr[maxch] #O where to put the filter text
+int maxch #I max chars out
+
+pointer sp, buf, bp
+int op, dtype[2], offset[2], i
+int sizeof(), gstrcpy(), qpex_getfilter()
+define ovfl_ 91
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_TEXTBUF, TY_CHAR)
+
+ op = 1
+
+ # Report on QPIO parameters first.
+
+ # Parameter name.
+ call sprintf (Memc[buf], SZ_TEXTBUF, "param=%s,")
+ call pargstr (Memc[IO_PARAM(io)])
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ if (op > maxch)
+ goto ovfl_
+
+ # Coordinate system.
+ dtype[1] = IO_EVXTYPE(io); dtype[2] = IO_EVYTYPE(io)
+ offset[1] = IO_EVXOFF(io); offset[2] = IO_EVYOFF(io)
+
+ call sprintf (Memc[buf], SZ_TEXTBUF, "key=(%c%d,%c%d),")
+ do i = 1, 2 {
+ switch (dtype[i]) {
+ case TY_SHORT:
+ call pargi ('s')
+ case TY_INT:
+ call pargi ('i')
+ case TY_LONG:
+ call pargi ('l')
+ case TY_REAL:
+ call pargi ('r')
+ case TY_DOUBLE:
+ call pargi ('d')
+ default:
+ call pargi ('?')
+ }
+
+ call pargi (offset[i] * sizeof(dtype[i]) * SZB_CHAR)
+ }
+
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ if (op > maxch)
+ goto ovfl_
+
+ # Blocking factor for generating pixels.
+ call sprintf (Memc[buf], SZ_TEXTBUF, "block=%0.4gx%0.4g, ")
+ call pargr (IO_XBLOCK(io))
+ call pargr (IO_YBLOCK(io))
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ if (op > maxch)
+ goto ovfl_
+
+ # Region mask, if any.
+ if (Memc[IO_MASK(io)] != EOS) {
+ call sprintf (Memc[buf], SZ_TEXTBUF, "mask=%s,")
+ call pargstr (Memc[IO_MASK(io)])
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ if (op > maxch)
+ goto ovfl_
+ }
+
+ # Debug level, if debug messages enabled.
+ if (IO_DEBUG(io) > 0) {
+ call sprintf (Memc[buf], SZ_TEXTBUF, "debug=%d,")
+ call pargi (IO_DEBUG(io))
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ if (op > maxch)
+ goto ovfl_
+ }
+
+ # Noindex flag, if enabled.
+ if (IO_NOINDEX(io) > 0) {
+ call sprintf (Memc[buf], SZ_TEXTBUF, "noindex=%b,")
+ call pargi (IO_NOINDEX(io))
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ if (op > maxch)
+ goto ovfl_
+ }
+
+ # Event attribute filter.
+ if (IO_EX(io) != NULL) {
+ bp = buf + gstrcpy ("filter=(", Memc[buf], SZ_TEXTBUF)
+ bp = bp + qpex_getfilter (IO_EX(io), Memc[bp], SZ_TEXTBUF-8)
+ Memc[bp] = ')'; bp = bp + 1
+ Memc[bp] = EOS
+ op = op + gstrcpy (Memc[buf], outstr[op], maxch-op+1)
+ if (op > maxch)
+ goto ovfl_
+ } else if (op > 1) {
+ # Clobber trailing comma.
+ op = op - 1
+ outstr[op] = EOS
+ }
+
+ call sfree (sp)
+ return (op - 1)
+ovfl_
+ call sfree (sp)
+ outstr[maxch+1] = EOS
+ return (maxch)
+end
diff --git a/sys/qpoe/qpiogetrg.x b/sys/qpoe/qpiogetrg.x
new file mode 100644
index 00000000..bc2b0272
--- /dev/null
+++ b/sys/qpoe/qpiogetrg.x
@@ -0,0 +1,19 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpio.h"
+
+# QPIO_GETRANGE -- Get the current range in X and Y within which events will
+# be extracted by qpio_getevents.
+
+int procedure qpio_getrange (io, vs, ve, maxdim)
+
+pointer io #I QPIO descriptor
+int vs[ARB] #O start vector (lower left corner)
+int ve[ARB] #O end vector (upper right corner)
+int maxdim #I vector length (ndim=2 at present)
+
+begin
+ call amovi (IO_VS(io,1), vs, maxdim)
+ call amovi (IO_VE(io,1), ve, maxdim)
+ return (NDIM)
+end
diff --git a/sys/qpoe/qpiolmask.x b/sys/qpoe/qpiolmask.x
new file mode 100644
index 00000000..979de478
--- /dev/null
+++ b/sys/qpoe/qpiolmask.x
@@ -0,0 +1,119 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <plset.h>
+include "qpoe.h"
+include "qpio.h"
+
+# QPIO_LOADMASK -- Load the named region mask into the QPIO descriptor.
+# The mask name may be the name of a header parameter containing the mask
+# as the stored array value (TY_OPAQUE parameter), the name of a header
+# parameter containing the name of the mask (TY_CHAR), or the name of a
+# mask storage file (.pl extension).
+
+procedure qpio_loadmask (io, mask, merge)
+
+pointer io #I QPIO descriptor
+char mask[ARB] #I mask to be loaded
+int merge #I merge with old mask?
+
+int niter
+int naxes, axlen[PL_MAXDIM], v[PL_MAXDIM]
+pointer sp, title, mp, sym, plbuf, qp, o_pl, n_pl, b_pl
+
+pointer pl_open(), qp_gpsym()
+int strmatch(), qp_accessf(), qp_read(), qp_gstr()
+errchk pl_open, pl_close, pl_loadf, qp_read, syserrs, qp_gstr, malloc
+define tryfile_ 91
+
+begin
+ call smark (sp)
+ call salloc (title, SZ_FNAME, TY_CHAR)
+ call salloc (mp, SZ_FNAME, TY_CHAR)
+
+ if (IO_DEBUG(io) > 0) {
+ call eprintf ("load mask `%s'\n")
+ call pargstr (mask)
+ }
+
+ qp = IO_QP(io)
+ o_pl = IO_PL(io)
+ call strcpy (mask, Memc[mp], SZ_FNAME)
+
+ # Open new mask.
+ for (niter=0; Memc[mp] != EOS; niter=niter+1) {
+ if (strmatch (Memc[mp], ".pl$") > 0) {
+ # Mask is stored in a file.
+tryfile_
+ n_pl = pl_open (NULL)
+ call pl_loadf (n_pl, Memc[mp], Memc[title], SZ_FNAME)
+
+ } else if (qp_accessf (qp, Memc[mp]) == YES) {
+ # Named parameter contains or points to mask.
+
+ sym = qp_gpsym (qp, Memc[mp])
+ if (S_DTYPE(sym) == TY_OPAQUE) {
+ # Parameter value is stored mask.
+ call salloc (plbuf, S_NELEM(sym) / SZ_SHORT, TY_SHORT)
+ if (qp_read (qp, Memc[mp], Mems[plbuf], S_NELEM(sym), 1,
+ "opaque") < S_NELEM(sym)) {
+ call syserrs (SYS_QPBADVAL, Memc[mp])
+ } else {
+ n_pl = pl_open (plbuf) # no deref
+ }
+
+ } else if (S_DTYPE(sym) == TY_CHAR) {
+ # Parameter value is pointer to mask.
+ if (qp_gstr (qp, Memc[mp], Memc[mp], SZ_FNAME) > 0) {
+ if (niter < MAX_INDIR)
+ next
+ else
+ call syserrs (SYS_QPMRECUR, Memc[mp])
+ }
+ } else
+ goto tryfile_
+ } else
+ goto tryfile_
+
+ break
+ }
+
+ # Check that mask and image are the same size, and get mask depth.
+ call pl_gsize (n_pl, naxes, axlen, IO_MDEPTH(io))
+ if (axlen[1] != IO_NCOLS(io) || axlen[2] != IO_NLINES(io))
+ call syserrs (SYS_QPPLSIZE, Memc[mp])
+
+ # Merge the old and new mask if so indicated. The result mask is the
+ # same as the new mask, but only those pixels also present (nonzero)
+ # in the old mask are preserved.
+
+ if (merge == YES && o_pl != NULL) {
+ b_pl = pl_open (NULL)
+ call amovkl (1, v, PL_MAXDIM)
+ call pl_ssize (b_pl, naxes, axlen, 1)
+ call pl_rop (o_pl, v, b_pl, v, axlen, PIX_SRC)
+ call pl_rop (b_pl, v, n_pl, v, axlen, and(PIX_SRC,PIX_DST))
+ call pl_close (b_pl)
+ }
+
+ # Close old mask, if any.
+ if (IO_PL(io) != NULL && IO_PLCLOSE(io) == YES)
+ call pl_close (IO_PL(io))
+
+ # Install new mask.
+ IO_PL(io) = n_pl
+ IO_PLCLOSE(io) = YES
+ call strcpy (Memc[mp], Memc[IO_MASK(io)], SZ_FNAME)
+
+ # Allocate a range list buffer if i/o is indexed.
+ if (IO_INDEXLEN(io) > 0) {
+ if (IO_RL(io) != NULL)
+ call mfree (IO_RL(io), TY_INT)
+ if (IO_PL(io) != NULL)
+ call malloc (IO_RL(io), RL_MAXLEN(IO_PL(io)), TY_INT)
+ else
+ call malloc (IO_RL(io), RL_LENELEM*2, TY_INT)
+ }
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpiolwcs.x b/sys/qpoe/qpiolwcs.x
new file mode 100644
index 00000000..76c1650f
--- /dev/null
+++ b/sys/qpoe/qpiolwcs.x
@@ -0,0 +1,50 @@
+include "qpio.h"
+
+# QPIO_LOADWCS -- Load the WCS, if any, from the QPOE file associated with the
+# given QPIO descriptor, into an open MWCS descriptor. This is equivalent to
+# QP_LOADWCS except that the Lterm is updated to reflect the current blocking
+# factor and rect (if any) used for rasterization. In the resultant WCS, the
+# logical coordinate system gives the pixel coordinates of the sampled rect.
+
+pointer procedure qpio_loadwcs (io)
+
+pointer io #I QPIO descriptor
+
+pointer qp, mw
+int ndim, i, j
+double ltv_1[NDIM], ltv_2[NDIM], ltm[NDIM,NDIM]
+pointer qp_loadwcs()
+errchk qp_loadwcs
+
+begin
+ qp = IO_QP(io)
+ mw = qp_loadwcs (qp)
+ ndim = NDIM
+
+ # Formalize the transformation.
+ ltv_1[1] = IO_VSDEF(io,1) - 1
+ ltv_1[2] = IO_VSDEF(io,2) - 1
+
+ # L(i) :== LTM=(1 / block) * P(i) + Vx
+ # At pixel {P(i) :== (block + 1) / 2} L(i) is 1.0.
+ # Solve for Vx :== 1.0 - (1 / block) * ((block + 1) / 2)
+ # --> 0.5 - 1 / (2 * block)
+
+ ltv_2[1] = 0.5d0 - 1.0d0 / double (max (1.0, IO_XBLOCK(io))) / 2.0d0
+ ltv_2[2] = 0.5d0 - 1.0d0 / double (max (1.0, IO_YBLOCK(io))) / 2.0d0
+
+ do j = 1, ndim
+ do i = 1, ndim
+ if (i == j) {
+ if (i == 1)
+ ltm[i,j] = 1.0D0 / max (1.0, IO_XBLOCK(io))
+ else
+ ltm[i,j] = 1.0D0 / max (1.0, IO_YBLOCK(io))
+ } else
+ ltm[i,j] = 0
+
+ # Apply the transformation.
+ call mw_translated (mw, ltv_1, ltm, ltv_2, ndim)
+
+ return (mw)
+end
diff --git a/sys/qpoe/qpiomkidx.x b/sys/qpoe/qpiomkidx.x
new file mode 100644
index 00000000..979b1142
--- /dev/null
+++ b/sys/qpoe/qpiomkidx.x
@@ -0,0 +1,299 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <error.h>
+include <mach.h>
+include <fset.h>
+include "qpoe.h"
+include "qpio.h"
+
+define SZ_CODE 7
+
+
+# QPIO_MKINDEX -- Make an index for the event list associated with the QPIO
+# descriptor. The event list must have been already written out, in sorted
+# order according to the given key. Once an event list is indexed it cannot
+# be further extended or otherwise modified. The key fields are specified
+# as, e.g., "s10,s8" or "(s10,s8)" (Y // X), where the field name is the
+# datatype code (silrd for short, int, long, real, or double) followed by the
+# decimal byte offset of the field in the event struct.
+
+procedure qpio_mkindex (io, key)
+
+pointer io #I QPIO descriptor
+char key[ARB] #I list of key fields
+
+pointer sp, tokbuf, ip, in, ev, ev_p, ov, lv, oo, bp
+int ox, line, nevents, szs_event, ncols, nlines, nout, x, y, i, ch
+int token, offset, xoff, yoff, len_index, nev, fd, sv_evi, firstev
+int dtype, ntype
+
+long note()
+pointer qp_opentext()
+int qp_gettok(), ctoi(), qpio_rbucket(), pl_p2li(), sizeof()
+errchk qp_opentext, qpio_rbucket, qpio_sync, write, calloc, syserrs
+define nosort_ 91
+
+begin
+ call smark (sp)
+ call salloc (tokbuf, SZ_TOKBUF, TY_CHAR)
+ call malloc (oo, IO_NLINES(io) * 3 + 32, TY_SHORT)
+
+ ncols = IO_NCOLS(io)
+ nlines = IO_NLINES(io)
+ sv_evi = IO_EVI(io)
+
+ # Key defaults to sort x/y.
+ xoff = IO_EVXOFF(io)
+ yoff = IO_EVYOFF(io)
+ dtype = IO_EVXTYPE(io)
+
+ # Parse key list (macro references are permitted) to get offsets of
+ # the X and Y coordinate fields to be used as the index key.
+
+ in = qp_opentext (IO_QP(io), key)
+
+ do i = 1, 2 {
+ # Get next field token.
+ repeat {
+ token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF)
+ } until (token == EOF || token == TOK_IDENTIFIER)
+ if (token == EOF)
+ break
+
+ # Determine field type.
+ call strlwr (Memc[tokbuf])
+ ch = Memc[tokbuf]
+
+ switch (ch) {
+ case 's':
+ ntype = TY_SHORT
+ case 'i':
+ ntype = TY_INT
+ case 'l':
+ ntype = TY_LONG
+ case 'r':
+ ntype = TY_REAL
+ case 'd':
+ ntype = TY_DOUBLE
+ default:
+ call syserrs (SYS_QPXYFNS, key)
+ }
+
+ # Both X and Y must be the same type.
+ if (i == 1)
+ dtype = ntype
+ else if (ntype != dtype)
+ call syserrs (SYS_QPINVEVT, key)
+
+ ip = tokbuf + 1
+ if (ctoi (Memc, ip, offset) <= 0)
+ call syserrs (SYS_QPBADKEY, key)
+ else
+ offset = offset / (sizeof(dtype) * SZB_CHAR)
+
+ if (i == 1)
+ yoff = offset
+ else
+ xoff = offset
+
+ while (qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) != EOF)
+ if (Memc[tokbuf] == ',')
+ break
+ }
+
+ call qp_closetext (in)
+
+ # Sync the event list to ensure that the bucket is flushed.
+ call qpio_sync (io)
+
+ fd = IO_FD(io)
+ bp = IO_BP(io)
+ len_index = nlines
+ szs_event = IO_EVENTLEN(io)
+
+ if (IO_DEBUG(io) > 1) {
+ call eprintf ("qpio_mkindex (%xX, `%s')\n")
+ call pargi (io)
+ call pargstr (key)
+ call eprintf ("nevents=%d, evsize=%d, xkey=%c%d, ykey=%c%d\n")
+ call pargi (IO_NEVENTS(io))
+ call pargi (szs_event)
+ call pargi (ch)
+ call pargi (xoff)
+ call pargi (ch)
+ call pargi (yoff)
+ }
+
+ # Allocate the offset and length vectors (comprising the index).
+ # These are deallocated at qpio_close time.
+
+ call calloc (ov, len_index, TY_INT)
+ call calloc (lv, len_index, TY_INT)
+
+ ox = -1
+ line = 1
+ firstev = 1
+ nevents = 0
+
+ # Rewind the list.
+ i = qpio_rbucket (io, 1)
+
+ # For each event in the event list...
+ for (IO_EVI(io)=1; IO_EVI(io) <= IO_NEVENTS(io); ) {
+ # Refill the event bucket?
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ if (qpio_rbucket (io, IO_EVI(io)) == EOF)
+ break
+
+ # Process all events in the bucket.
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ nev = min (IO_NEVENTS(io), IO_BKLASTEV(io)) - IO_EVI(io) + 1
+ nout = 0
+
+ do i = 1, nev {
+ ev_p = (ev - 1) * SZ_SHORT / sizeof(dtype) + 1
+
+ switch (dtype) {
+ case TY_SHORT:
+ x = Mems[ev_p+xoff]
+ y = Mems[ev_p+yoff]
+ case TY_INT:
+ x = Memi[ev_p+xoff]
+ y = Memi[ev_p+yoff]
+ case TY_LONG:
+ x = Meml[ev_p+xoff]
+ y = Meml[ev_p+yoff]
+ case TY_REAL:
+ x = Memr[ev_p+xoff] + 0.5
+ y = Memr[ev_p+yoff] + 0.5
+ case TY_DOUBLE:
+ x = Memd[ev_p+xoff] + 0.5
+ y = Memd[ev_p+yoff] + 0.5
+ }
+
+ x = max(1, min(ncols, x))
+ y = max(1, min(nlines, y))
+
+ if (IO_DEBUG(io) > 4) {
+ # Egads! Dump every photon.
+ call eprintf ("(%04d,%04d) ")
+ call pargi (x)
+ call pargi (y)
+ nout = nout + 1
+ if (nout >= 6) {
+ call eprintf ("\n")
+ nout = 0
+ }
+ }
+
+ if (y > line) {
+ # Add index entry.
+ if (nevents > 0) {
+ Memi[ov+line-1] = firstev
+ Memi[lv+line-1] = nevents
+
+ if (IO_DEBUG(io) > 3 && nevents > 0) {
+ if (nout > 0) {
+ call eprintf ("\n")
+ nout = 0
+ }
+ call eprintf ("%4d: ev=%d, nev=%d\n")
+ call pargi (line)
+ call pargi (firstev)
+ call pargi (nevents)
+ }
+ }
+
+ # Set up the new line.
+ firstev = IO_EVI(io) + i - 1
+ nevents = 1
+ line = y
+ ox = x
+
+ } else if (y == line) {
+ # Add another event to the current line.
+ nevents = nevents + 1
+ if (x < ox)
+ goto nosort_
+ else
+ ox = x
+ } else
+ goto nosort_
+
+ ev = ev + szs_event
+ }
+
+ IO_EVI(io) = IO_EVI(io) + nev
+ if (nout > 0) {
+ call eprintf ("\n")
+ nout = 0
+ }
+ }
+
+ # Output final index entry.
+ if (nevents > 0) {
+ Memi[ov+line-1] = firstev
+ Memi[lv+line-1] = nevents
+ }
+
+ # Apply data compression to the index arrays and append to the event
+ # list lfile.
+
+ call fseti (fd, F_BUFSIZE, len_index * SZ_INT)
+ call seek (fd, EOF)
+
+ IO_YOFFVOFF(io) = note (fd)
+ IO_YOFFVLEN(io) = pl_p2li (Memi[ov], 1, Mems[oo], len_index)
+ call write (fd, Mems[oo], IO_YOFFVLEN(io) * SZ_SHORT)
+
+ IO_YLENVOFF(io) = note (fd)
+ IO_YLENVLEN(io) = pl_p2li (Memi[lv], 1, Mems[oo], len_index)
+ call write (fd, Mems[oo], IO_YLENVLEN(io) * SZ_SHORT)
+
+ call flush (fd)
+ call fseti (fd, F_BUFSIZE, 0)
+
+ # Update the remaining index related fields of the QPIO descriptor.
+ IO_INDEXLEN(io) = len_index
+ IO_YOFFVP(io) = ov
+ IO_YLENVP(io) = lv
+
+ IO_IXXOFF(io) = xoff
+ IO_IXYOFF(io) = yoff
+ IO_IXXTYPE(io) = dtype
+ IO_IXYTYPE(io) = dtype
+
+ if (IO_DEBUG(io) > 1) {
+ call eprintf ("index.offv %d words at offset %d\n")
+ call pargi (IO_YOFFVLEN(io))
+ call pargi (IO_YOFFVOFF(io))
+ call eprintf ("index.lenv %d words at offset %d\n")
+ call pargi (IO_YLENVLEN(io))
+ call pargi (IO_YLENVOFF(io))
+ }
+
+ # Update the event list header.
+ call qpio_sync (io)
+
+ IO_EVI(io) = sv_evi
+ call sfree (sp)
+ return
+
+nosort_
+ # A nonsorted event list has been detected, hence we cannot build
+ # an index, but we need not abort since nonindexed event lists are
+ # still usable.
+
+ if (nout > 0)
+ call eprintf ("\n")
+ iferr (call syserrs (SYS_QPEVNSORT, Memc[IO_PARAM(io)]))
+ call erract (EA_WARN)
+
+ IO_INDEXLEN(io) = 0
+ call mfree (ov, TY_INT)
+ call mfree (lv, TY_INT)
+
+ IO_EVI(io) = sv_evi
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpioopen.x b/sys/qpoe/qpioopen.x
new file mode 100644
index 00000000..4ed8a711
--- /dev/null
+++ b/sys/qpoe/qpioopen.x
@@ -0,0 +1,392 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <error.h>
+include <mach.h>
+include <fset.h>
+include <plset.h>
+include "qpoe.h"
+include "qpex.h"
+include "qpio.h"
+
+# QPIO_OPEN -- Open the named event list parameter for event i/o. Since event
+# lists can only be read and written sequentially, there are only two useful
+# i/o modes, namely, READ_ONLY and NEW_FILE. Filtering is permitted only
+# when reading an event list; when writing to a new event list, the events
+# are merely copied out as they are received.
+
+pointer procedure qpio_open (qp, paramex, mode)
+
+pointer qp #I QPOE descriptor
+char paramex[ARB] #I event-list parameter plus expression list
+int mode #I access mode
+
+bool newlist
+pointer sp, io, dd, eh, op, oo, flist, deffilt, defmask, maskname
+pointer param, expr, filter, filter_text, mask, umask, psym, dsym, name
+int sz_filter, szb_page, nwords, nchars, junk, fd, ip, i, j
+
+pointer qp_gpsym(), qpex_open(), stname(), strefstab()
+int qp_popen(), qp_lenf(), read(), pl_l2pi(), fstati()
+int qp_geti(), qp_gstr(), qp_parsefl(), qpio_parse(), qpex_modfilter()
+
+errchk qp_bind, qp_geti, qpio_parse, qp_gpsym, qp_addf, qp_gstr
+errchk qp_parsefl, qp_popen, qpex_open, qpio_loadmask, qpex_modfilter
+errchk stname, calloc, malloc, realloc, read, syserrs
+string s_deffilt DEF_FILTER
+string s_defmask DEF_MASK
+define done_ 91
+
+begin
+ call smark (sp)
+ call salloc (deffilt, SZ_FNAME, TY_CHAR)
+ call salloc (defmask, SZ_FNAME, TY_CHAR)
+ call salloc (maskname, SZ_FNAME, TY_CHAR)
+ call salloc (umask, SZ_FNAME, TY_CHAR)
+
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ newlist = (mode == NEW_FILE || mode == APPEND)
+
+ if (QP_DEBUG(qp) > 0) {
+ call eprintf ("qpio_open (%xX, `%s', %d)\n")
+ call pargi (qp)
+ call pargstr (paramex)
+ call pargi (mode)
+ }
+
+ # Allocate and initialize the QPIO descriptor.
+ call calloc (io, LEN_IODES, TY_STRUCT)
+
+ call calloc (IO_DD(io), LEN_DDDES, TY_STRUCT)
+ call calloc (IO_PARAM(io), SZ_FNAME, TY_CHAR)
+ call calloc (IO_MASK(io), SZ_FNAME, TY_CHAR)
+
+ IO_QP(io) = qp
+ IO_MODE(io) = mode
+ IO_DEBUG(io) = QP_DEBUG(qp)
+ IO_XBLOCK(io) = QP_XBLOCK(qp)
+ IO_YBLOCK(io) = QP_YBLOCK(qp)
+ IO_NODEFFILT(io) = QP_NODEFFILT(qp)
+ IO_NODEFMASK(io) = QP_NODEFMASK(qp)
+ IO_OPTBUFSIZE(io) = QP_OPTBUFSIZE(qp)
+ IO_ACTIVE(io) = NO
+
+ dd = IO_DD(io)
+ param = IO_PARAM(io)
+ mask = IO_MASK(io)
+ filter = NULL
+
+iferr {
+ # Get the image dimensions.
+ IO_NCOLS(io) = qp_geti (qp, "axlen[1]")
+ IO_NLINES(io) = qp_geti (qp, "axlen[2]")
+
+ # Parse the parameter expression into the parameter name and
+ # expression qualifier fields. Possible variations on the input
+ # syntax are "" (null string, default everything), "param" (parameter
+ # name only), "param[expr]" (parameter name plus expression), and
+ # "[expr]" or "expr" (expression only), where the parameter name may
+ # be specified as in "[param=value,...]", i.e., as a term in the
+ # expression (allowing it to be input by the user to override the
+ # default).
+
+ op = param
+ for (ip=1; paramex[ip] != EOS && paramex[ip] != '['; ip=ip+1) {
+ Memc[op] = paramex[ip]
+ op = op + 1
+ }
+ expr = ip
+
+ # Parse the expression qualifier field to set the i/o parameters,
+ # e.g., region mask, event attribute filter, blocking factor,
+ # coordinate system, etc. All QPIO parameters are removed, returning
+ # the filter expression (if any) to be passed on to QPEX for event
+ # attribute filtering. The `filter' buffer is passed by pointer so
+ # that it may be reallocated if more space is needed.
+
+ sz_filter = DEF_SZEXPRBUF
+ call malloc (filter, sz_filter, TY_CHAR)
+ if (qpio_parse (io, paramex[expr],
+ filter, sz_filter, Memc[mask], SZ_FNAME) == ERR)
+ call eprintf ("QPIO warning: error parsing options expression\n")
+
+ # If no event list parameter was named, use the default.
+ if (Memc[param] == EOS)
+ call strcpy (DEF_EVENTPARAM, Memc[param], SZ_FNAME)
+
+ # Verify the parameter's type if it already exists, or create a new
+ # parameter of the default type if the mode is newfile or append.
+
+ psym = qp_gpsym (qp, Memc[param])
+ if (psym != NULL) {
+ if (S_DTYPE(psym) != TY_USER || S_DSYM(psym) == NULL)
+ call syserrs (SYS_QPNEVPAR, Memc[param])
+ else if (newlist && S_NELEM(psym) > 0)
+ call syserrs (SYS_QPCLOBBER, Memc[param])
+ } else if (mode == READ_ONLY) {
+ call syserrs (SYS_QPUKNPAR, Memc[param])
+ } else {
+ call qp_addf (qp, Memc[param], DEF_EVENTTYPE, 0, "", 0)
+ psym = qp_gpsym (qp, Memc[param])
+ }
+
+ # Get the field list for the user defined event structure. This
+ # defines the size of an event struct, lists the offset and type
+ # of each field, and indicates which fields are to be used for X
+ # and Y in positional accesses (unless already set in the paramex).
+
+ dsym = strefstab (QP_ST(qp), S_DSYM(psym))
+ nchars = S_NELEM(dsym)
+ name = stname (QP_ST(qp), dsym)
+
+ call salloc (flist, nchars, TY_CHAR)
+ if (qp_gstr (qp, Memc[name], Memc[flist], nchars) < nchars)
+ call syserrs (SYS_QPBADVAL, Memc[name])
+
+ if (qp_parsefl (qp, Memc[flist], IO_DD(io)) <= 0)
+ call syserrs (SYS_QPINVDD, Memc[name])
+ else if (IO_EVXOFF(io) == NULL && IO_EVYOFF(io) == NULL) {
+ i = DD_XFIELD(dd)
+ j = DD_YFIELD(dd)
+ if (i == 0 || j == 0)
+ call syserrs (SYS_QPNOXYF, Memc[name])
+
+ switch (DD_FTYPE(dd,i)) {
+ case TY_SHORT, TY_INT, TY_LONG, TY_REAL, TY_DOUBLE:
+ IO_EVXTYPE(io) = DD_FTYPE(dd,i)
+ default:
+ call syserrs (SYS_QPXYFNS, Memc[name])
+ }
+
+ switch (DD_FTYPE(dd,j)) {
+ case TY_SHORT, TY_INT, TY_LONG, TY_REAL, TY_DOUBLE:
+ IO_EVYTYPE(io) = DD_FTYPE(dd,j)
+ default:
+ call syserrs (SYS_QPXYFNS, Memc[name])
+ }
+
+ IO_EVXOFF(io) = DD_FOFFSET(dd,i)
+ IO_EVYOFF(io) = DD_FOFFSET(dd,j)
+ }
+
+ IO_EVENTLEN(io) = DD_STRUCTLEN(dd) * SZ_STRUCT / SZ_SHORT
+
+ # Open the lfile used to store the event list.
+ IO_FD(io) = qp_popen (qp, Memc[param], mode, BINARY_FILE)
+ IO_LF(io) = S_LFILE(psym)
+ IO_CHAN(io) = fstati (IO_FD(io), F_CHANNEL)
+ IO_PSYM(io) = psym
+
+ # The rest of the initialization is performed in the first call to
+ # qpio_putev if we are writing a new event list.
+
+ if (newlist) # EXIT if new event list
+ goto done_ # -----------------------
+
+ fd = IO_FD(io)
+ szb_page = QP_FMPAGESIZE(qp)
+ nchars = szb_page / SZB_CHAR
+ call salloc (eh, szb_page / (SZ_STRUCT*SZB_CHAR), TY_STRUCT)
+ call aclri (Memi[eh], szb_page / (SZ_STRUCT*SZB_CHAR))
+
+ # Read event list header.
+ if (read (fd, Memi[eh], nchars) < nchars)
+ call syserrs (SYS_QPNOEH, Memc[param])
+
+ IO_NEVENTS(io) = EH_NEVENTS(eh)
+ IO_EVENTLEN(io) = EH_EVENTLEN(eh)
+ IO_SZBBUCKET(io)= EH_SZBBUCKET(eh)
+ IO_BUCKETLEN(io)= EH_BUCKETLEN(eh)
+ IO_FBOFF(io) = EH_FBOFF(eh)
+ IO_EVMINOFF(io) = EH_EVMINOFF(eh)
+ IO_EVMAXOFF(io) = EH_EVMAXOFF(eh)
+ IO_INDEXLEN(io) = EH_INDEXLEN(eh)
+ IO_YOFFVOFF(io) = EH_YOFFVOFF(eh)
+ IO_YOFFVLEN(io) = EH_YOFFVLEN(eh)
+ IO_YLENVOFF(io) = EH_YLENVOFF(eh)
+ IO_YLENVLEN(io) = EH_YLENVLEN(eh)
+ IO_IXXOFF(io) = EH_IXXOFF(eh)
+ IO_IXYOFF(io) = EH_IXYOFF(eh)
+ IO_IXXTYPE(io) = EH_IXXTYPE(eh)
+ IO_IXYTYPE(io) = EH_IXYTYPE(eh)
+
+ # Copy the MINEVL event struct into the QPIO descriptor.
+ nwords = IO_EVENTLEN(io)
+ call malloc (IO_MINEVL(io), nwords, TY_SHORT)
+ call amovs (Memi[eh+EH_MINEVLOFF(eh)], Mems[IO_MINEVL(io)],
+ IO_EVENTLEN(io))
+
+ # Copy the MAXEVL event struct into the QPIO descriptor.
+ call malloc (IO_MAXEVL(io), nwords, TY_SHORT)
+ call amovs (Memi[eh+EH_MAXEVLOFF(eh)], Mems[IO_MAXEVL(io)],
+ IO_EVENTLEN(io))
+
+ if (IO_DEBUG(io) > 0) {
+ call eprintf ("%s: nev=%d, szbk=%d, bklen=%d+2, ixlen=%d\n")
+ call pargstr (Memc[param])
+ call pargi (IO_NEVENTS(io))
+ call pargi (IO_SZBBUCKET(io))
+ call pargi (IO_BUCKETLEN(io))
+ call pargi (IO_INDEXLEN(io))
+ }
+
+ # Get compressed event list index, if any.
+ if (IO_INDEXLEN(io) > 0) {
+ call salloc (oo, IO_INDEXLEN(io) * 2, TY_SHORT)
+ call malloc (IO_YOFFVP(io), IO_INDEXLEN(io), TY_INT)
+ call malloc (IO_YLENVP(io), IO_INDEXLEN(io), TY_INT)
+
+ nchars = IO_YOFFVLEN(io) * SZ_SHORT
+ call seek (fd, IO_YOFFVOFF(io))
+ if (read (fd, Mems[oo], nchars) < nchars)
+ call syserrs (SYS_QPBADIX, Memc[param])
+ junk = pl_l2pi (Mems[oo], 1, Memi[IO_YOFFVP(io)], IO_INDEXLEN(io))
+
+ nchars = IO_YLENVLEN(io) * SZ_SHORT
+ call seek (fd, IO_YLENVOFF(io))
+ if (read (fd, Mems[oo], nchars) < nchars)
+ call syserrs (SYS_QPBADIX, Memc[param])
+ junk = pl_l2pi (Mems[oo], 1, Memi[IO_YLENVP(io)], IO_INDEXLEN(io))
+ }
+
+ # We won't need the file buffer any more, so free it.
+ call fseti (fd, F_BUFSIZE, 0)
+
+ # Compile the event attribute filter (EAF). Always open the default
+ # filter if one is provided with the datafile. If the user has also
+ # specified a filter, this will modify the default filter.
+
+ if (IO_NODEFFILT(io) != YES) {
+ # Check for "deffilt.<evl>" first, then "deffilt".
+ call sprintf (Memc[deffilt], SZ_FNAME, "%s.%s")
+ call pargstr (s_deffilt)
+ call pargstr (Memc[param])
+ nchars = qp_lenf (qp, Memc[deffilt])
+ if (nchars <= 0) {
+ call strcpy (s_deffilt, Memc[deffilt], SZ_FNAME)
+ nchars = qp_lenf (qp, Memc[deffilt])
+ }
+
+ # Open the default filter if one was found.
+ if (nchars > 0) {
+ call salloc (filter_text, nchars, TY_CHAR)
+ if (qp_gstr(qp,Memc[deffilt],Memc[filter_text],nchars) < nchars)
+ call syserrs (SYS_QPBADVAL, Memc[deffilt])
+ IO_EX(io) = qpex_open (qp, Memc[filter_text])
+ IO_EXCLOSE(io) = YES
+ }
+ }
+
+ # Fold in the user specified filter if one was given.
+ if (Memc[filter] != EOS) {
+ if (IO_EX(io) != NULL) {
+ if (qpex_modfilter (IO_EX(io), Memc[filter]) == ERR)
+ call fprintf (STDERR,
+ "Warning: error compiling QPIO filter\n")
+ } else {
+ IO_EX(io) = qpex_open (qp, Memc[filter])
+ IO_EXCLOSE(io) = YES
+ }
+
+ if (IO_DEBUG(io) > 0) {
+ call eprintf ("event attribute filter: %s\n")
+ call pargstr (Memc[filter])
+ }
+ }
+
+ # Open the region mask. This may be specified (named) in the parameter
+ # expression, else we try to open a default mask. If a mask is named,
+ # the name may be the name of a header parameter containing the mask
+ # as the stored array value (TY_OPAQUE parameter), the name of a header
+ # parameter containing the name of the mask (TY_CHAR), or the name of
+ # a mask storage file (.pl extension).
+
+ # Make a copy of the user mask name, as qpio_loadmask will clobber it.
+ call strcpy (Memc[mask], Memc[umask], SZ_FNAME)
+
+ if (IO_NODEFMASK(io) != YES) {
+ # Check for "defmask.<evl>" first, then "defmask".
+ call sprintf (Memc[defmask], SZ_FNAME, "%s.%s")
+ call pargstr (s_defmask)
+ call pargstr (Memc[param])
+ nchars = qp_lenf (qp, Memc[defmask])
+ if (nchars <= 0) {
+ call strcpy (s_defmask, Memc[defmask], SZ_FNAME)
+ nchars = qp_lenf (qp, Memc[defmask])
+ }
+
+ if (nchars > 0)
+ if (qp_gstr (qp, Memc[defmask], Memc[maskname], SZ_FNAME) > 0)
+ call qpio_loadmask (io, Memc[maskname], NO)
+ }
+
+ # Load user specified mask.
+ if (Memc[umask] != EOS)
+ call qpio_loadmask (io, Memc[umask], YES)
+ else if (IO_INDEXLEN(io) > 0)
+ call malloc (IO_RL(io), RL_LENELEM*2, TY_INT)
+
+ # Allocate the bucket buffer.
+ call malloc (IO_BP(io), IO_SZBBUCKET(io)/SZB_CHAR/SZ_SHORT, TY_SHORT)
+done_
+ # If no default rect was specified, set default bounding box for
+ # reading to be the entire image.
+
+ if (IO_BBUSED(io) == NO) {
+ IO_VSDEF(io,1) = 1; IO_VSDEF(io,2) = 1
+ IO_VEDEF(io,1) = IO_NCOLS(io); IO_VEDEF(io,2) = IO_NLINES(io)
+ }
+
+ # Initialize the active BB to the default.
+ call amovi (IO_VSDEF(io,1), IO_VS(io,1), NDIM)
+ call amovi (IO_VEDEF(io,1), IO_VE(io,1), NDIM)
+
+} then {
+ # We branch here if any nasty errors occur above. Cleanup and free
+ # the partially opened descriptor and pass the error on to whoever
+ # called us.
+
+ if (IO_BP(io) != NULL)
+ call mfree (IO_BP(io), TY_SHORT)
+ if (IO_RL(io) != NULL)
+ call mfree (IO_RL(io), TY_INT)
+ if (IO_PL(io) != NULL && IO_PLCLOSE(io) == YES)
+ call pl_close (IO_PL(io))
+ if (IO_EX(io) != NULL)
+ call qpex_close (IO_EX(io))
+
+ if (IO_YLENVP(io) != NULL)
+ call mfree (IO_YLENVP(io), TY_INT)
+ if (IO_YOFFVP(io) != NULL)
+ call mfree (IO_YOFFVP(io), TY_INT)
+ if (IO_MINEVL(io) != NULL)
+ call mfree (IO_MINEVL(io), TY_SHORT)
+ if (IO_MAXEVL(io) != NULL)
+ call mfree (IO_MAXEVL(io), TY_SHORT)
+ if (IO_FD(io) != NULL)
+ call close (IO_FD(io))
+
+ if (IO_MASK(io) != NULL)
+ call mfree (IO_MASK(io), TY_CHAR)
+ if (IO_PARAM(io) != NULL)
+ call mfree (IO_PARAM(io), TY_CHAR)
+ if (IO_DD(io) != NULL)
+ call mfree (IO_DD(io), TY_STRUCT)
+
+ if (filter != NULL)
+ call mfree (filter, TY_CHAR)
+ if (io != NULL)
+ call mfree (io, TY_STRUCT)
+
+ call erract (EA_ERROR)
+}
+
+ # The filter can be regenerated, so don't keep the input expr around.
+ call mfree (filter, TY_CHAR)
+
+ # Normal exit for read-only access.
+ call sfree (sp)
+ return (io)
+end
diff --git a/sys/qpoe/qpioparse.x b/sys/qpoe/qpioparse.x
new file mode 100644
index 00000000..40f858ab
--- /dev/null
+++ b/sys/qpoe/qpioparse.x
@@ -0,0 +1,374 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include <mach.h>
+include "qpoe.h"
+include "qpex.h"
+include "qpio.h"
+
+# QPIO_PARSE -- Parse the QPIO expression operand input to qpio_open or
+# qpio_setfilter. This consists of a comma delimited list of keyword=value
+# terms. We factor out those which are QPIO related and deal with these
+# directly, concatenating the remaining terms to be passed on to QPEX.
+# The output filter buffer is resized as needed to hold the filter expr.
+# ERR is returned as the function value if an error occurs while compiling
+# the expression.
+
+int procedure qpio_parse (io, expr, filter, sz_filter, mask, sz_mask)
+
+pointer io #I QPIO descriptor
+char expr[ARB] #I expression to be parsed
+pointer filter #U filter buffer
+int sz_filter #U allocated buffer size
+char mask[sz_mask] #O new mask name (not reallocatable)
+int sz_mask #I max chars out
+
+real rval
+pointer qp, sp, keyword, vp, in
+int assignop, byte_offset, sz_field
+int level, zlevel, status, start, value, token, op, kw, tokno
+
+pointer qp_opentext()
+int qp_gettok(), gstrcpy(), strlen(), strdic(), ctoi(), ctor()
+errchk qp_opentext, malloc, realloc, qp_gettok, qp_ungettok, syserrs
+
+define F Memc[filter+($1)-1]
+define noval_ 91
+define badval_ 92
+define badkey_ 93
+
+begin
+ call smark (sp)
+ call salloc (keyword, SZ_FNAME, TY_CHAR)
+
+ qp = IO_QP(io)
+
+ # Open the input expression for macro expanded token input.
+ in = qp_opentext (qp, expr)
+
+ # Extract and process a series of "param[=expr]" terms, where
+ # the expr may be any series of tokens, delimited by an
+ # unparenthesized comma.
+
+ op = 1
+ tokno = 0
+ F(op) = EOS
+ mask[1] = EOS
+ status = OK
+ level = 0
+
+ repeat {
+ start = op
+
+ # Advance to the next keyword.
+ token = qp_gettok (in, F(op), SZ_TOKBUF)
+ tokno = tokno + 1
+
+ switch (token) {
+ case EOF:
+ break
+ case '(', '[', '{':
+ level = level + 1
+ next
+ case ')', ']', '}':
+ level = level - 1
+ next
+ case '!':
+ if (tokno <= 2) {
+ IO_NODEFFILT(io) = YES
+ IO_NODEFMASK(io) = YES
+ tokno = 1
+ }
+ next
+ case TOK_IDENTIFIER:
+ op = op + strlen (F(op))
+ if (op + SZ_TOKBUF > sz_filter) {
+ sz_filter = sz_filter + INC_SZEXPRBUF
+ call realloc (filter, sz_filter, TY_CHAR)
+ }
+ call strcpy (F(start), Memc[keyword], SZ_FNAME)
+ call strlwr (Memc[keyword])
+ default:
+ if (token != ',') {
+ call eprintf ("QPIO: unexpected token `%s'\n")
+ call pargstr (F(op))
+ status = ERR
+ }
+ next
+ }
+
+ value = NULL
+ token = qp_gettok (in, F(op), SZ_TOKBUF)
+
+ if (token == '=' ||
+ token == TOK_PLUSEQUALS || token == TOK_COLONEQUALS) {
+
+ # Accumulate the expression.
+ zlevel = level
+ assignop = token
+ op = op + strlen (F(op))
+ value = op
+
+ repeat {
+ # Peek at the next token to see if it terminates the
+ # expression. An unparenthesized comma or unmatched
+ # right brace, bracket, or parenthesis is part of the
+ # next statement and terminates the expression.
+
+ token = qp_gettok (in, F(op), SZ_TOKBUF)
+ switch (token) {
+ case EOF:
+ break
+ case '(', '[', '{':
+ level = level + 1
+ case ')', ']', '}':
+ if (level <= zlevel) {
+ call qp_ungettok (in, F(op))
+ F(op) = EOS
+ break
+ } else
+ level = level - 1
+ case ',':
+ if (level <= zlevel) {
+ call qp_ungettok (in, F(op))
+ F(op) = EOS
+ break
+ }
+ }
+
+ # Accept token as data.
+ op = op + strlen (F(op))
+ if (op + SZ_TOKBUF + 1 > sz_filter) {
+ sz_filter = sz_filter + INC_SZEXPRBUF
+ call realloc (filter, sz_filter, TY_CHAR)
+ }
+
+ F(op) = ' '; op = op + 1
+ F(op) = EOS
+ }
+ }
+
+ # Process the keywords known to QPIO and pass anything else on
+ # to the output filter buffer.
+
+ kw = strdic (Memc[keyword], Memc[keyword], SZ_FNAME, KEYWORDS)
+ vp = filter + value - 1
+
+ switch (kw) {
+ case KW_BLOCK:
+ # Set the XY blocking factor for pixelation.
+ if (value == NULL)
+ goto noval_
+ else if (ctor (Memc, vp, rval) <= 0)
+ goto badval_
+ IO_XBLOCK(io) = rval
+ IO_YBLOCK(io) = rval
+ op = start
+
+ case KW_XBLOCK:
+ # Set the X blocking factor for pixelation.
+ if (value == NULL)
+ goto noval_
+ else if (ctor (Memc, vp, rval) <= 0)
+ goto badval_
+ IO_XBLOCK(io) = rval
+ op = start
+
+ case KW_YBLOCK:
+ # Set the Y blocking factor for pixelation.
+ if (value == NULL)
+ goto noval_
+ else if (ctor (Memc, vp, rval) <= 0)
+ goto badval_
+ IO_YBLOCK(io) = rval
+ op = start
+
+ case KW_DEBUG:
+ # Set the debug level, default 1 if no argument.
+ if (value == NULL)
+ IO_DEBUG(io) = 1
+ else if (ctoi (Memc, vp, IO_DEBUG(io)) <= 0) {
+ IO_DEBUG(io) = QP_DEBUG(qp)
+badval_ call eprintf ("QPIO: cannot convert `%s' to integer\n")
+ call pargstr (Memc[vp])
+ }
+ op = start
+
+ case KW_FILTER:
+ # A term such as "filter=(...)". Keep the (...).
+ if (value == NULL)
+ goto noval_
+ else {
+ # Accumulate expression term.
+ op = start + gstrcpy (Memc[vp], F(start), ARB)
+ F(op) = ','; op = op + 1
+ F(op) = EOS
+ }
+
+ case KW_KEY:
+ # Set the offsets of the event attribute fields to be used
+ # for the event coordinates during extraction. The typical
+ # syntax of the key value is, e.g., key=(s10,s8). Fields
+ # used for event coordinate keys must be a numeric type.
+
+ call strlwr (Memc[vp])
+ while (Memc[vp] == ' ' || Memc[vp] == '(')
+ vp = vp + 1
+
+ # Get the X field offset and type.
+ switch (Memc[vp]) {
+ case 's':
+ IO_EVXTYPE(io) = TY_SHORT
+ sz_field = SZ_SHORT
+ case 'i':
+ IO_EVXTYPE(io) = TY_INT
+ sz_field = SZ_INT
+ case 'l':
+ IO_EVXTYPE(io) = TY_LONG
+ sz_field = SZ_LONG
+ case 'r':
+ IO_EVXTYPE(io) = TY_REAL
+ sz_field = SZ_REAL
+ case 'd':
+ IO_EVXTYPE(io) = TY_DOUBLE
+ sz_field = SZ_DOUBLE
+ default:
+ goto badkey_
+ }
+
+ vp = vp + 1
+ if (ctoi (Memc, vp, byte_offset) <= 0)
+ goto badkey_
+ else
+ IO_EVXOFF(io) = byte_offset / (sz_field * SZB_CHAR)
+
+ while (Memc[vp] == ' ' || Memc[vp] == ',')
+ vp = vp + 1
+
+ # Get the Y field offset.
+ switch (Memc[vp]) {
+ case 's':
+ IO_EVYTYPE(io) = TY_SHORT
+ sz_field = SZ_SHORT
+ case 'i':
+ IO_EVYTYPE(io) = TY_INT
+ sz_field = SZ_INT
+ case 'l':
+ IO_EVYTYPE(io) = TY_LONG
+ sz_field = SZ_LONG
+ case 'r':
+ IO_EVYTYPE(io) = TY_REAL
+ sz_field = SZ_REAL
+ case 'd':
+ IO_EVYTYPE(io) = TY_DOUBLE
+ sz_field = SZ_DOUBLE
+ default:
+ goto badkey_
+ }
+
+ vp = vp + 1
+ if (ctoi (Memc, vp, byte_offset) <= 0) {
+badkey_ call eprintf ("QPIO: bad key value `%s'\n")
+ call pargstr (F(value))
+ status = ERR
+ } else
+ IO_EVYOFF(io) = byte_offset / (sz_field * SZB_CHAR)
+
+ op = start
+
+ case KW_NOINDEX:
+ # Disable use of the index for extraction (for testing).
+ IO_NOINDEX(io) = YES
+ op = start
+
+ case KW_PARAM, KW_MASK:
+ # Set a string valued option.
+
+ if (value == NULL) {
+noval_ call eprintf ("QPIO: kewyord `%s' requires an argument\n")
+ call pargstr (Memc[keyword])
+ status = ERR
+
+ } else {
+ # Kill space added at end of token.
+ op = op - 1
+ F(op) = EOS
+
+ # Output the string.
+ if (kw == KW_PARAM) {
+ # Set the name of the event list parameter.
+ call strcpy (Memc[vp], Memc[IO_PARAM(io)], SZ_FNAME)
+ } else {
+ # Set the name of the region mask.
+ call strcpy (Memc[vp], mask, sz_mask)
+ if (assignop == TOK_COLONEQUALS)
+ IO_NODEFMASK(io) = YES
+ }
+ }
+ op = start
+
+ case KW_RECT:
+ # Set the source rect or "bounding box" for i/o. The syntax
+ # is somewhat flexible, i.e., "*", ":N", "N:", "M:N" are
+ # all accepted ways of expressing the range for an axis.
+
+ IO_VSDEF(io,1) = 1; IO_VSDEF(io,2) = 1
+ IO_VEDEF(io,1) = IO_NCOLS(io); IO_VEDEF(io,2) = IO_NLINES(io)
+
+ if (Memc[vp] == '[' || Memc[vp] == '(') # ])
+ vp = vp + 1
+ while (Memc[vp] == ' ')
+ vp = vp + 1
+
+ # Get range in X.
+ if (Memc[vp] == '*')
+ vp = vp + 1
+ else {
+ if (ctoi (Memc, vp, IO_VSDEF(io,1)) <= 0)
+ IO_VSDEF(io,1) = 1
+ while (IS_WHITE(Memc[vp]) || Memc[vp] == ':')
+ vp = vp + 1
+ if (ctoi (Memc, vp, IO_VEDEF(io,1)) <= 0)
+ IO_VEDEF(io,1) = IO_NCOLS(io)
+ }
+
+ while (IS_WHITE(Memc[vp]) || Memc[vp] == ',')
+ vp = vp + 1
+
+ # Get range in Y.
+ if (Memc[vp] == '*')
+ vp = vp + 1
+ else {
+ if (ctoi (Memc, vp, IO_VSDEF(io,2)) <= 0)
+ IO_VSDEF(io,2) = 1
+ while (IS_WHITE(Memc[vp]) || Memc[vp] == ':')
+ vp = vp + 1
+ if (ctoi (Memc, vp, IO_VEDEF(io,2)) <= 0)
+ IO_VEDEF(io,2) = IO_NLINES(io)
+ }
+
+ IO_BBUSED(io) = YES
+ op = start
+
+ default:
+ # Accumulate EAF expression term.
+ F(op) = ','; op = op + 1
+ F(op) = ' '; op = op + 1
+ F(op) = EOS
+ }
+ }
+
+ # Verify that the parens etc. match.
+ if (level != 0)
+ call syserrs (SYS_QPIOSYN, QP_DFNAME(qp))
+
+ F(op) = EOS
+ sz_filter = op
+ call realloc (filter, sz_filter, TY_CHAR)
+
+ call qp_closetext (in)
+ call sfree (sp)
+
+ return (status)
+end
diff --git a/sys/qpoe/qpioputev.x b/sys/qpoe/qpioputev.x
new file mode 100644
index 00000000..69c6d26d
--- /dev/null
+++ b/sys/qpoe/qpioputev.x
@@ -0,0 +1,104 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "qpoe.h"
+include "qpio.h"
+
+# QPIO_PUTEVENTS -- Append events to a new event list. No filtering is
+# performed. As events are received they are merely copied into the bucket
+# currently being filled, writing each bucket to the output lfile as it fills.
+# No sorting is performed, hence if an indexed list is desired, the caller
+# must output the events in sort order (normally sorted by Y and then by X
+# within each image line).
+
+procedure qpio_putevents (io, i_ev, nevents)
+
+pointer io #I QPIO descriptor
+pointer i_ev[ARB] #I array of event pointers
+int nevents #I number of events
+
+pointer qp, bp, ev
+int szs_event, szb_page, nwords, bklen, bksiz, nev, i, j
+errchk qpio_wbucket, qpio_sync, malloc, calloc
+
+begin
+ szs_event = IO_EVENTLEN(io)
+ bp = IO_BP(io)
+
+ # Fix the event list parameters and write out the event list header
+ # when the first write to a new event list occurs.
+
+ if (IO_ACTIVE(io) == NO) {
+ qp = IO_QP(io)
+ szb_page = QP_FMPAGESIZE(qp)
+
+ IO_FBOFF(io) = szb_page + 1
+ IO_EVENTLEN(io) = DD_STRUCTLEN(IO_DD(io))*SZ_STRUCT/SZ_SHORT
+ IO_NEVENTS(io) = 0
+
+ # Force the bucket size to an integral number of datafile pages,
+ # and adjust the number of events to fill the bucket, allowing
+ # 2 extra slots at the end for the min/max event structs.
+
+ bklen = QP_BUCKETLEN(qp) + 2
+ bksiz = bklen * (IO_EVENTLEN(io) * SZ_SHORT * SZB_CHAR)
+ bksiz = (bksiz - 1) / szb_page * szb_page
+ bklen = bksiz / (IO_EVENTLEN(io) * SZ_SHORT * SZB_CHAR)
+
+ IO_BUCKETLEN(io) = bklen - 2
+ IO_SZBBUCKET(io) = bksiz
+ IO_EVMINOFF(io) = szs_event * (bklen - 2)
+ IO_EVMAXOFF(io) = szs_event * (bklen - 1)
+ IO_EVI(io) = 1
+ IO_BKNO(io) = 1
+ IO_BKFIRSTEV(io) = 1
+ IO_BKLASTEV(io) = bklen - 2
+
+ if (IO_DEBUG(io) > 1) {
+ call eprintf ("%s: assign szbk=%d, bklen=%d+2\n")
+ call pargstr (Memc[IO_PARAM(io)])
+ call pargi (bksiz)
+ call pargi (bklen - 2)
+ }
+
+ # Allocate the bucket buffer.
+ call malloc (IO_BP(io), bksiz / SZB_CHAR / SZ_SHORT, TY_SHORT)
+ bp = IO_BP(io)
+
+ # Allocate the MINEVL and MAXEVL event structs, used to keep
+ # track of the min and max event field values for the entire
+ # event list.
+
+ nwords = IO_EVENTLEN(io)
+ call calloc (IO_MINEVL(io), nwords, TY_SHORT)
+ call calloc (IO_MAXEVL(io), nwords, TY_SHORT)
+
+ # Write the event list header.
+ call qpio_sync (io)
+
+ IO_ACTIVE(io) = YES
+ }
+
+ # Make sure there is room in the bucket.
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ call qpio_wbucket (io, IO_EVI(io))
+
+ # Output the current batch of events.
+ for (j=0; j < nevents; j=j+nev) {
+ # Copy out as many events as will fit in the bucket.
+ nev = min (nevents-j, (IO_BKLASTEV(io) - IO_EVI(io) + 1))
+ if (nev <= 0)
+ break
+
+ ev = bp + (IO_EVI(io) - IO_BKFIRSTEV(io)) * szs_event
+ do i = 1, nev {
+ call amovs (Mems[i_ev[i+j]], Mems[ev], szs_event)
+ ev = ev + szs_event
+ }
+
+ # Write out the bucket if it fills.
+ IO_EVI(io) = IO_EVI(io) + nev
+ if (IO_EVI(io) > IO_BKLASTEV(io))
+ call qpio_wbucket (io, IO_EVI(io))
+ }
+end
diff --git a/sys/qpoe/qpiorb.x b/sys/qpoe/qpiorb.x
new file mode 100644
index 00000000..9d46cc59
--- /dev/null
+++ b/sys/qpoe/qpiorb.x
@@ -0,0 +1,44 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpio.h"
+
+# QPIO_RBUCKET -- Load the bucket containing the specified event into the
+# QPIO descriptor, returning EOF if the numbered event does not exist.
+
+int procedure qpio_rbucket (io, evi)
+
+pointer io #I QPIO descriptor
+int evi #I bucket number desired
+
+int ev1, ev2, nb
+int offset, bkno, status
+
+begin
+ # Event does not exist?
+ if (evi < 1 || evi > IO_NEVENTS(io))
+ return (EOF)
+
+ # Bucket already loaded?
+ bkno = EVI_TO_BUCKET(io,evi)
+ if (bkno == IO_BKNO(io))
+ return (bkno)
+
+ # Determine range of events in bucket.
+ ev1 = BUCKET_TO_EVI(io,bkno)
+ ev2 = min (IO_NEVENTS(io), ev1 + IO_BUCKETLEN(io) - 1)
+
+ # Physically read the bucket.
+ nb = IO_SZBBUCKET(io)
+ offset = (bkno - 1) * nb + IO_FBOFF(io)
+ call fm_lfaread (IO_CHAN(io), Mems[IO_BP(io)], nb, offset)
+ call fm_lfawait (IO_CHAN(io), status)
+ if (status < nb)
+ return (EOF)
+
+ # Update the bucket descriptor.
+ IO_BKNO(io) = bkno
+ IO_BKFIRSTEV(io) = ev1
+ IO_BKLASTEV(io) = ev2
+
+ return (bkno)
+end
diff --git a/sys/qpoe/qpiorpix.gx b/sys/qpoe/qpiorpix.gx
new file mode 100644
index 00000000..e766749c
--- /dev/null
+++ b/sys/qpoe/qpiorpix.gx
@@ -0,0 +1,86 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <syserr.h>
+include "../qpio.h"
+
+# QPIO_READPIX -- Sample the event list within the indicated rectangular
+# region, using the given blocking factor, to produce a rectangular array
+# of "pixels", where each pixel is a count of the number of events mapping
+# to that location which pass the event attribute filter and region mask.
+#
+# NOTE -- It is left up to the caller to zero the output buffer before
+# we are called. (We merely increment the counts of the affected pixels).
+
+int procedure qpio_readpix$t (io, obuf, vs, ve, ndim, xblock, yblock)
+
+pointer io #I QPIO descriptor
+PIXEL obuf[ARB] #O output pixel buffer
+int vs[ndim], ve[ndim] #I vectors defining region to be extracted
+int ndim #I should be 2 for QPOE
+real xblock, yblock #I blocking factors
+
+double x, y
+pointer sp, evl, ev_p
+int evtype, maxpix, maskval, xoff, yoff, xw, yw, nev, totev, pix, i, j
+errchk qpio_getevents, qpio_setrange, syserr
+int qpio_getevents()
+
+begin
+ # Verify arguments.
+ if (xblock <= 0 || xblock > (ve[1] - vs[1] + 1))
+ call syserr (SYS_QPBLOCKOOR)
+ if (yblock <= 0 || yblock > (ve[2] - vs[2] + 1))
+ call syserr (SYS_QPBLOCKOOR)
+
+ # Compute the size of the output matrix in integer pixels. This
+ # truncates the last partially filled pixel in each axis.
+
+ xw = int ((ve[1] - vs[1] + 1) / xblock + (EPSILOND * 1000))
+ yw = int ((ve[2] - vs[2] + 1) / yblock + (EPSILOND * 1000))
+ if (xw <= 0 || yw <= 0)
+ return (0)
+
+ call smark (sp)
+ call salloc (evl, SZ_EVLIST, TY_POINTER)
+
+ xoff = IO_EVXOFF(io)
+ yoff = IO_EVYOFF(io)
+ maxpix = xw * yw
+ totev = 0
+
+ evtype = IO_EVXTYPE(io)
+ if (IO_EVXTYPE(io) != IO_EVYTYPE(io))
+ call syserr (SYS_QPINVEVT)
+
+ # Define the region from which we wish to read events.
+ call qpio_setrange (io, vs, ve, ndim)
+
+ # Read the events.
+ while (qpio_getevents (io, Memi[evl], maskval, SZ_EVLIST, nev) > 0) {
+ switch (evtype) {
+ $for (silrd)
+ case TY_PIXEL:
+ # Process a sequence of neighbor events.
+ do i = 1, nev {
+ ev_p = (Memi[evl+i-1] - 1) * SZ_SHORT / SZ_PIXEL + 1
+
+ x = Mem$t[ev_p+xoff]
+ y = Mem$t[ev_p+yoff]
+
+ j = int ((y - vs[2]) / yblock + (EPSILOND * 1000))
+ if (j >= 0 && j < yw) {
+ pix = j * xw + (x - vs[1]) / xblock + 1
+ if (pix > 0 && pix <= maxpix)
+ obuf[pix] = obuf[pix] + 1
+ }
+ }
+ $endfor
+ }
+
+ totev = totev + nev
+ }
+
+ call sfree (sp)
+ return (totev)
+end
diff --git a/sys/qpoe/qpiosetfil.x b/sys/qpoe/qpiosetfil.x
new file mode 100644
index 00000000..4f5f9833
--- /dev/null
+++ b/sys/qpoe/qpiosetfil.x
@@ -0,0 +1,59 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpex.h"
+include "qpio.h"
+
+# QPIO_SETFILTER -- Modify the filter used to reject events during event
+# extraction with qpio_getevents or qpio_readpix. Possible items to be set
+# here include the event attribute filter, region mask, and various QPIO
+# parameters. The input expression should be a comma delimited list of
+# param=value terms, where PARAM is `filter', `mask', or the name of a QPIO
+# or QPEX parameter, and where `value' is an expression, e.g., a comma
+# delimited list of range terms enclosed in parenthesis.
+
+procedure qpio_setfilter (io, expr)
+
+pointer io #I QPIO descriptor
+char expr[ARB] #I option setting expression
+
+int sz_filter
+pointer sp, filter, mask
+errchk qpio_parse, qpex_open, qpex_modfilter
+int qpex_modfilter(), qpio_parse()
+pointer qpex_open()
+
+begin
+ call smark (sp)
+ call salloc (mask, SZ_FNAME, TY_CHAR)
+
+ if (IO_DEBUG(io) > 0) {
+ call eprintf ("qpio_setfilter (%xX, `%s')\n")
+ call pargi (io)
+ call pargstr (expr)
+ }
+
+ # Parse full QPIO oriented filter expression.
+ sz_filter = DEF_SZEXPRBUF
+ call malloc (filter, sz_filter, TY_CHAR)
+ if (qpio_parse (io,expr,filter,sz_filter,Memc[mask],SZ_FNAME) == ERR) {
+ call eprintf ("QPIO warning: error parsing `%s'\n")
+ call pargstr (expr)
+ }
+
+ # Set event attribute filter.
+ if (IO_EX(io) == NULL)
+ IO_EX(io) = qpex_open (IO_QP(io), Memc[filter])
+ else if (qpex_modfilter (IO_EX(io), Memc[filter]) == ERR) {
+ call eprintf ("Warning: errors compiling `%s'\n")
+ call pargstr (expr)
+ }
+
+ # Set region mask.
+ if (Memc[mask] != EOS)
+ call qpio_loadmask (io, Memc[mask], NO)
+
+ IO_ACTIVE(io) = NO
+
+ call mfree (filter, TY_CHAR)
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpioseti.x b/sys/qpoe/qpioseti.x
new file mode 100644
index 00000000..d5dadceb
--- /dev/null
+++ b/sys/qpoe/qpioseti.x
@@ -0,0 +1,90 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpioset.h>
+include <syserr.h>
+include <plset.h>
+include "qpio.h"
+
+# QPIO_SETI -- Set a QPIO interface integer valued parameter. This procedure
+# represents the lowest level interface by which an applications program can
+# control QPIO.
+
+procedure qpio_seti (io, param, value)
+
+pointer io #I QPIO descriptor
+int param #I parameter code
+int value #I new parameter value
+
+int naxes, axlen[PL_MAXDIM], sv_active
+errchk pl_close, syserr, realloc
+
+begin
+ # Almost everything here cancels any active i/o.
+ sv_active = IO_ACTIVE(io)
+ IO_ACTIVE(io) = NO
+
+ # Set the named parameter.
+ switch (param) {
+ case QPIO_BLOCKFACTOR:
+ IO_XBLOCK(io) = value
+ IO_YBLOCK(io) = value
+ case QPIO_XBLOCKFACTOR:
+ IO_XBLOCK(io) = value
+ case QPIO_YBLOCKFACTOR:
+ IO_YBLOCK(io) = value
+ case QPIO_EVXOFF:
+ IO_EVXOFF(io) = value
+ case QPIO_EVYOFF:
+ IO_EVYOFF(io) = value
+ case QPIO_EVXTYPE:
+ IO_EVXTYPE(io) = value
+ case QPIO_EVYTYPE:
+ IO_EVYTYPE(io) = value
+ case QPIO_NOINDEX:
+ IO_NOINDEX(io) = value
+ case QPIO_NODEFFILT:
+ IO_NODEFFILT(io) = value
+ case QPIO_NODEFMASK:
+ IO_NODEFMASK(io) = value
+ case QPIO_OPTBUFSIZE:
+ IO_OPTBUFSIZE(io) = value
+
+ case QPIO_BUCKETLEN:
+ # Set the bucket length (new event lists only).
+ if (IO_MODE(io) != READ_ONLY)
+ IO_BUCKETLEN(io) = value
+
+ case QPIO_DEBUG:
+ # Set the debug level; don't modify IO_ACTIVE.
+ IO_ACTIVE(io) = sv_active
+ IO_DEBUG(io) = value
+
+ case QPIO_EX:
+ # Set the event attribute filter.
+ if (IO_EX(io) != NULL && IO_EXCLOSE(io) == YES)
+ call qpex_close (IO_EX(io))
+ IO_EX(io) = value
+ IO_EXCLOSE(io) = NO
+
+ case QPIO_PL:
+ # Set the PLIO region mask.
+ if (IO_PL(io) != NULL && IO_PLCLOSE(io) == YES)
+ call pl_close (IO_PL(io))
+
+ IO_PL(io) = value
+ IO_PLCLOSE(io) = NO
+ call pl_gsize (IO_PL(io), naxes, axlen, IO_MDEPTH(io))
+ if (axlen[1] != IO_NCOLS(io) || axlen[2] != IO_NLINES(io))
+ call syserr (SYS_QPPLSIZE)
+
+ # Allocate a range list buffer if i/o is indexed.
+ if (IO_INDEXLEN(io) > 0)
+ call realloc (IO_RL(io), RL_MAXLEN(IO_PL(io)), TY_INT)
+
+ # Update the mask name, such as it is...
+ if (IO_MASK(io) != NULL) {
+ call sprintf (Memc[IO_MASK(io)], SZ_FNAME, "%xX")
+ call pargi (value)
+ }
+ }
+end
diff --git a/sys/qpoe/qpiosetr.x b/sys/qpoe/qpiosetr.x
new file mode 100644
index 00000000..768e1b82
--- /dev/null
+++ b/sys/qpoe/qpiosetr.x
@@ -0,0 +1,30 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpioset.h>
+include "qpio.h"
+
+# QPIO_SETR -- Set a QPIO interface real valued parameter. This procedure
+# represents the lowest level interface by which an applications program can
+# control QPIO.
+
+procedure qpio_setr (io, param, value)
+
+pointer io #I QPIO descriptor
+int param #I parameter code
+real value #I new parameter value
+
+begin
+ # Almost everything here cancels any active i/o.
+ IO_ACTIVE(io) = NO
+
+ # Set the named parameter.
+ switch (param) {
+ case QPIO_BLOCKFACTOR:
+ IO_XBLOCK(io) = value
+ IO_YBLOCK(io) = value
+ case QPIO_XBLOCKFACTOR:
+ IO_XBLOCK(io) = value
+ case QPIO_YBLOCKFACTOR:
+ IO_YBLOCK(io) = value
+ }
+end
diff --git a/sys/qpoe/qpiosetrg.x b/sys/qpoe/qpiosetrg.x
new file mode 100644
index 00000000..6a93f11e
--- /dev/null
+++ b/sys/qpoe/qpiosetrg.x
@@ -0,0 +1,34 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpio.h"
+
+# QPIO_SETRANGE -- Set the range in X and Y within which events will be
+# extracted by qpio_getevents. This defines the "bounding box" for i/o
+# and "rewinds" the getevent i/o pointer.
+
+procedure qpio_setrange (io, vs, ve, ndim)
+
+pointer io #I QPIO descriptor
+int vs[ARB] #I start vector (lower left corner)
+int ve[ARB] #I end vector (upper right corner)
+int ndim #I vector length (ndim=2 at present)
+
+int i
+int vlim[NDIM]
+
+begin
+ vlim[1] = IO_NCOLS(io)
+ vlim[2] = IO_NLINES(io)
+
+ if (ndim <= 0) {
+ call amovi (IO_VSDEF(io,1), IO_VS(io,1), NDIM)
+ call amovi (IO_VEDEF(io,1), IO_VE(io,1), NDIM)
+ } else {
+ do i = 1, ndim {
+ IO_VS(io,i) = max(1, min(vlim[i], vs[i]))
+ IO_VE(io,i) = max(1, min(vlim[i], ve[i]))
+ }
+ }
+
+ IO_ACTIVE(io) = NO
+end
diff --git a/sys/qpoe/qpiostati.x b/sys/qpoe/qpiostati.x
new file mode 100644
index 00000000..678ee0bb
--- /dev/null
+++ b/sys/qpoe/qpiostati.x
@@ -0,0 +1,84 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpioset.h>
+include "qpio.h"
+
+# QPIO_STATI -- Stat a QPIO interface integer valued parameter.
+
+int procedure qpio_stati (io, param)
+
+pointer io #I QPIO descriptor
+int param #I parameter code
+
+bool fp_equalr()
+
+begin
+ switch (param) {
+ case QPIO_BLOCKFACTOR:
+ if (fp_equalr (IO_XBLOCK(io), IO_YBLOCK(io)))
+ return (IO_XBLOCK(io))
+ else
+ return (ERR)
+ case QPIO_XBLOCKFACTOR:
+ return (IO_XBLOCK(io))
+ case QPIO_YBLOCKFACTOR:
+ return (IO_YBLOCK(io))
+ case QPIO_BUCKETLEN:
+ return (IO_BUCKETLEN(io))
+ case QPIO_DEBUG:
+ return (IO_DEBUG(io))
+ case QPIO_EVXOFF:
+ return (IO_EVXOFF(io))
+ case QPIO_EVYOFF:
+ return (IO_EVYOFF(io))
+ case QPIO_EVXTYPE:
+ return (IO_EVXTYPE(io))
+ case QPIO_EVYTYPE:
+ return (IO_EVYTYPE(io))
+ case QPIO_EX:
+ return (IO_EX(io))
+ case QPIO_NODEFFILT:
+ return (IO_NODEFFILT(io))
+ case QPIO_NODEFMASK:
+ return (IO_NODEFMASK(io))
+ case QPIO_NOINDEX:
+ return (IO_NOINDEX(io))
+ case QPIO_OPTBUFSIZE:
+ return (IO_OPTBUFSIZE(io))
+ case QPIO_PL:
+ return (IO_PL(io))
+
+ case QPIO_EVENTLEN: # length of event struct, shorts
+ return (IO_EVENTLEN(io))
+ case QPIO_FD: # FIO fd of event list lfile
+ return (IO_FD(io))
+ case QPIO_INDEXLEN: # index length (0=noindex)
+ return (IO_INDEXLEN(io))
+ case QPIO_IXXOFF: # offset of X in index
+ return (IO_IXXOFF(io))
+ case QPIO_IXYOFF: # offset of Y in index
+ return (IO_IXYOFF(io))
+ case QPIO_IXXTYPE: # datatype of X in index
+ return (IO_IXXTYPE(io))
+ case QPIO_IXYTYPE: # datatype of Y in index
+ return (IO_IXYTYPE(io))
+ case QPIO_LF: # FMIO lfile number
+ return (IO_LF(io))
+ case QPIO_MASKP: # PLIO descriptor
+ return (IO_MASK(io))
+ case QPIO_MAXEVP: # pointer to short
+ return (IO_MAXEVL(io))
+ case QPIO_MINEVP: # pointer to short
+ return (IO_MINEVL(io))
+ case QPIO_NCOLS:
+ return (IO_NCOLS(io))
+ case QPIO_NLINES:
+ return (IO_NLINES(io))
+ case QPIO_PARAMP: # pointer to char
+ return (IO_PARAM(io))
+ case QPIO_QP:
+ return (IO_QP(io)) # QPOE descriptor
+ }
+
+ return (ERR)
+end
diff --git a/sys/qpoe/qpiostatr.x b/sys/qpoe/qpiostatr.x
new file mode 100644
index 00000000..429ea2e9
--- /dev/null
+++ b/sys/qpoe/qpiostatr.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpioset.h>
+include "qpio.h"
+
+# QPIO_STATR -- Stat a QPIO interface real valued parameter.
+
+real procedure qpio_statr (io, param)
+
+pointer io #I QPIO descriptor
+int param #I parameter code
+
+bool fp_equalr()
+
+begin
+ switch (param) {
+ case QPIO_BLOCKFACTOR:
+ if (fp_equalr (IO_XBLOCK(io), IO_YBLOCK(io)))
+ return (IO_XBLOCK(io))
+ else
+ return (ERR)
+ case QPIO_XBLOCKFACTOR:
+ return (IO_XBLOCK(io))
+ case QPIO_YBLOCKFACTOR:
+ return (IO_YBLOCK(io))
+ }
+
+ return (ERR)
+end
diff --git a/sys/qpoe/qpiosync.x b/sys/qpoe/qpiosync.x
new file mode 100644
index 00000000..35492d7d
--- /dev/null
+++ b/sys/qpoe/qpiosync.x
@@ -0,0 +1,78 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <fset.h>
+include "qpoe.h"
+include "qpio.h"
+
+# QPIO_SYNC -- Update an event list on disk, i.e., flush the bucket buffer if
+# it has been written into, and update the event list header. No QPIO state
+# parameters are modified, e.g., the i/o pointer IO_EVI is not affected,
+# nor are the contents of the bucket currently being filled; partially filled
+# buckets can be synced if desired.
+
+procedure qpio_sync (io)
+
+pointer io #I QPIO descriptor
+
+pointer sp, eh
+int szb_page, off, flen
+int fstati()
+errchk qpio_wbucket
+
+begin
+ if (IO_MODE(io) == READ_ONLY)
+ return
+
+ # Flush the bucket buffer.
+ if (IO_EVI(io) > 1)
+ call qpio_wbucket (io, IO_EVI(io))
+
+ call smark (sp)
+ szb_page = QP_FMPAGESIZE(IO_QP(io))
+
+ # Update the event list header (stored in a full datafile page).
+ call salloc (eh, szb_page / (SZ_STRUCT*SZB_CHAR), TY_STRUCT)
+ call aclri (Memi[eh], szb_page / (SZ_STRUCT*SZB_CHAR))
+
+ EH_FBOFF(eh) = szb_page + 1
+ EH_NEVENTS(eh) = IO_NEVENTS(io)
+ EH_EVENTLEN(eh) = IO_EVENTLEN(io)
+ EH_SZBBUCKET(eh) = IO_SZBBUCKET(io)
+ EH_BUCKETLEN(eh) = IO_BUCKETLEN(io)
+ EH_EVMINOFF(eh) = IO_EVMINOFF(io)
+ EH_EVMAXOFF(eh) = IO_EVMAXOFF(io)
+ EH_INDEXLEN(eh) = IO_INDEXLEN(io)
+ EH_YOFFVOFF(eh) = IO_YOFFVOFF(io)
+ EH_YOFFVLEN(eh) = IO_YOFFVLEN(io)
+ EH_YLENVOFF(eh) = IO_YLENVOFF(io)
+ EH_YLENVLEN(eh) = IO_YLENVLEN(io)
+ EH_IXXOFF(eh) = IO_IXXOFF(io)
+ EH_IXYOFF(eh) = IO_IXYOFF(io)
+ EH_IXXTYPE(eh) = IO_IXXTYPE(io)
+ EH_IXYTYPE(eh) = IO_IXYTYPE(io)
+
+ # Output MINEV and MAXEV event structs following the header struct,
+ # but in the header page.
+
+ if (IO_MINEVL(io) != NULL) {
+ off = LEN_EHDES
+ call amovs (Mems[IO_MINEVL(io)], Memi[eh+off], IO_EVENTLEN(io))
+ EH_MINEVLOFF(eh) = off
+ }
+
+ if (IO_MAXEVL(io) != NULL) {
+ off = LEN_EHDES + (IO_EVENTLEN(io) * SZ_SHORT / SZ_STRUCT)
+ call amovs (Mems[IO_MAXEVL(io)], Memi[eh+off], IO_EVENTLEN(io))
+ EH_MAXEVLOFF(eh) = off
+ }
+
+ # Write the header page to the lfile.
+ call fm_lfawrite (IO_CHAN(io), Memi[eh], szb_page, 1)
+ call fm_lfawait (IO_CHAN(io), szb_page)
+ flen = fstati (IO_FD(io), F_FILESIZE)
+ if (szb_page / SZB_CHAR > flen)
+ call fseti (IO_FD(io), F_FILESIZE, szb_page / SZB_CHAR)
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qpiowb.x b/sys/qpoe/qpiowb.x
new file mode 100644
index 00000000..42f6ccc2
--- /dev/null
+++ b/sys/qpoe/qpiowb.x
@@ -0,0 +1,131 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <fset.h>
+include "qpoe.h"
+include "qpio.h"
+
+# QPIO_WBUCKET -- Flush any data currently in the bucket to the datafile,
+# and set up the buffer to receive data for the bucket BKNO. The min/max
+# event structs are updated whenever a bucket is written to disk. IO_EVI
+# is assumed to point to the event following the last event written into
+# in the buffer. Data should always be written sequentially.
+
+procedure qpio_wbucket (io, evi)
+
+pointer io #I QPIO descriptor
+int evi #I evi of next bucket on exit
+
+pointer min_ev[2], max_ev[2], ev, fp, mp, dd
+int sz_event, offset, dtype, nb, flen, nchars, i, j, k
+int fstati()
+
+begin
+ dd = IO_DD(io)
+
+ # Write the current bucket to the datafile if nonempty.
+ if (dd != NULL && IO_EVI(io) > IO_BKFIRSTEV(io)) {
+ # Scan through the events in the bucket and update the min/max
+ # event structs for the event list ([1] below) and for the
+ # bucket ([2] below, stored at the end of the bucket).
+ # Use CHAR pointers to facilitate pointer conversions.
+
+ min_ev[1] = (IO_MINEVL(io) - 1) * SZ_SHORT + 1
+ min_ev[2] = (IO_MINEVB(io) - 1) * SZ_SHORT + 1
+ max_ev[1] = (IO_MAXEVL(io) - 1) * SZ_SHORT + 1
+ max_ev[2] = (IO_MAXEVB(io) - 1) * SZ_SHORT + 1
+ sz_event = DD_STRUCTLEN(dd) * SZ_STRUCT
+
+ do k = 1, 2 {
+ ev = (IO_BP(io) - 1) * SZ_SHORT + 1
+ # If min/max of bucket or first bucket of event list...
+ if (k == 2 || IO_BKNO(io) == 1) {
+ call amovc (Memc[ev], Memc[min_ev[k]], sz_event)
+ call amovc (Memc[ev], Memc[max_ev[k]], sz_event)
+ }
+
+ do j = 1, IO_EVI(io) - IO_BKFIRSTEV(io) {
+ do i = 1, DD_NFIELDS(dd) {
+ # Get the typed offset and datatype of the field.
+ offset = DD_FOFFSET(dd,i)
+ dtype = DD_FTYPE(dd,i)
+
+ # Update the min/max entries for the field.
+ switch (dtype) {
+ case TY_SHORT:
+ fp = (ev - 1) / SZ_SHORT + 1 + offset
+ mp = (min_ev[k] - 1) / SZ_SHORT + 1 + offset
+ if (Mems[fp] < Mems[mp])
+ Mems[mp] = Mems[fp]
+ mp = (max_ev[k] - 1) / SZ_SHORT + 1 + offset
+ if (Mems[fp] > Mems[mp])
+ Mems[mp] = Mems[fp]
+
+ case TY_INT, TY_LONG:
+ fp = (ev - 1) / SZ_INT + 1 + offset
+ mp = (min_ev[k] - 1) / SZ_INT + 1 + offset
+ if (Memi[fp] < Memi[mp])
+ Memi[mp] = Memi[fp]
+ mp = (max_ev[k] - 1) / SZ_INT + 1 + offset
+ if (Memi[fp] > Memi[mp])
+ Memi[mp] = Memi[fp]
+
+ case TY_REAL:
+ fp = (ev - 1) / SZ_REAL + 1 + offset
+ mp = (min_ev[k] - 1) / SZ_REAL + 1 + offset
+ if (Memr[fp] < Memr[mp])
+ Memr[mp] = Memr[fp]
+ mp = (max_ev[k] - 1) / SZ_REAL + 1 + offset
+ if (Memr[fp] > Memr[mp])
+ Memr[mp] = Memr[fp]
+
+ case TY_DOUBLE:
+ fp = (ev - 1) / SZ_DOUBLE + 1 + offset
+ mp = (min_ev[k] - 1) / SZ_DOUBLE + 1 + offset
+ if (Memd[fp] < Memd[mp])
+ Memd[mp] = Memd[fp]
+ mp = (max_ev[k] - 1) / SZ_DOUBLE + 1 + offset
+ if (Memd[fp] > Memd[mp])
+ Memd[mp] = Memd[fp]
+ }
+ }
+
+ ev = ev + sz_event
+ }
+ }
+
+ # Zero out any remaining events.
+ while (ev < min_ev[2]) {
+ call aclrc (Memc[ev], sz_event)
+ ev = ev + sz_event
+ }
+
+ # Write the bucket.
+ nb = IO_SZBBUCKET(io)
+ offset = (IO_BKNO(io) - 1) * nb + IO_FBOFF(io)
+ call fm_lfawrite (IO_CHAN(io), Mems[IO_BP(io)], nb, offset)
+ call fm_lfawait (IO_CHAN(io), nb)
+
+ # Update the file size.
+ flen = fstati (IO_FD(io), F_FILESIZE)
+ nchars = (offset + nb) / SZB_CHAR
+ if (nchars > flen)
+ call fseti (IO_FD(io), F_FILESIZE, nchars)
+
+ # Increment the total event count.
+ IO_NEVENTS(io) = max (IO_NEVENTS(io), IO_EVI(io) - 1)
+ S_NELEM(IO_PSYM(io)) = IO_NEVENTS(io)
+ QP_MODIFIED(IO_QP(io)) = YES
+ }
+
+ # Set up the buffer for the new bucket.
+ IO_BKNO(io) = EVI_TO_BUCKET(io,evi)
+ IO_BKFIRSTEV(io) = BUCKET_TO_EVI(io,IO_BKNO(io))
+ IO_BKLASTEV(io) = IO_BKFIRSTEV(io) + IO_BUCKETLEN(io) - 1
+
+ if (IO_DEBUG(io) > 2) {
+ call eprintf ("wbucket: evi=%d, bkno=%d\n")
+ call pargi(evi)
+ call pargi(IO_BKNO(io))
+ }
+end
diff --git a/sys/qpoe/qplenf.x b/sys/qpoe/qplenf.x
new file mode 100644
index 00000000..c8e4539e
--- /dev/null
+++ b/sys/qpoe/qplenf.x
@@ -0,0 +1,26 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_LENF -- Return the length of the named parameter, i.e., the number of
+# stored elements in the parameter value. NULL is returned if there is no
+# value, or ERR if the parameter does not exist.
+
+int procedure qp_lenf (qp, param)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+
+pointer sym
+pointer qp_gpsym()
+
+begin
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL)
+ return (ERR)
+ else
+ return (S_NELEM(sym))
+end
diff --git a/sys/qpoe/qploadwcs.x b/sys/qpoe/qploadwcs.x
new file mode 100644
index 00000000..b4609ce7
--- /dev/null
+++ b/sys/qpoe/qploadwcs.x
@@ -0,0 +1,38 @@
+include <syserr.h>
+include "qpoe.h"
+
+# QP_LOADWCS -- Load the default WCS, if there is one, from the QPOE image
+# header. A QPOE file can contain any number of WCS, but the default WCS
+# should relate the physical coordinate system, e.g., sky coordinates in
+# the range 1024sq, 8192sq, etc., to world coordinates, e.g., the TAN
+# projection. Probably we should provide for multiple physical coordinate
+# systems (sky, detector, etc.) each with its own WCS, but at present we
+# assume a single WCS.
+
+pointer procedure qp_loadwcs (qp)
+
+pointer qp #I QPOE descriptor
+
+int wcslen
+pointer sp, svwcs, mw
+errchk qp_lenf, syserrs, qp_read, mw_open
+int qp_lenf(), qp_read()
+pointer mw_open()
+string s_qpwcs QPWCS
+
+begin
+ # Determine if there is a WCS, and if so, how big the saved version is.
+ wcslen = qp_lenf (qp, s_qpwcs)
+ if (wcslen <= 0)
+ call syserrs (SYS_QPNOWCS, QP_DFNAME(qp))
+
+ call smark (sp)
+ call salloc (svwcs, wcslen, TY_CHAR)
+
+ # Retrieved the saved wcs, and load it into an MWCS descriptor.
+ wcslen = qp_read (qp, s_qpwcs, Memc[svwcs], wcslen, 1, "opaque")
+ mw = mw_open (svwcs, 0)
+
+ call sfree (sp)
+ return (mw)
+end
diff --git a/sys/qpoe/qpmacro.x b/sys/qpoe/qpmacro.x
new file mode 100644
index 00000000..984742d2
--- /dev/null
+++ b/sys/qpoe/qpmacro.x
@@ -0,0 +1,832 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <error.h>
+include <finfo.h>
+include <ctype.h>
+include "qpoe.h"
+include "qpex.h"
+
+.help qpmacro
+.nf ---------------------------------------------------------------------------
+QPMACRO -- Macro facility for QPOE. QPOE permits macro replacement in various
+places, e.g., to alias parameter names, or enter predefined selection
+expressions (selection functions). While macros may be defined permanently
+in the datafile, they are more commonly defined by the user at runtime, and
+used as a global facility to access any number of datafiles. Since we do not
+want to store runtime macros in the datafile headers, the compiled definitions
+cannot be entered into the datafile symbol table, but must be entered into a
+separate global symbol table, maintained by QPOE and used to store runtime
+macros to be used in all datafile accesses.
+
+The purpose of the package is to maintain an up to date global macro symbol
+table. The symbol table itself is directly accessed by the client program,
+rather than via the package interface, so that the standard SYMTAB package
+routines may be used to access the symbol table.
+
+ qm = qm_access ()
+ st = qm_symtab (qm)
+ qm_setdefaults (qm, qp)
+ qm_upddefaults (qm, qp)
+
+ qm_scan (qm, fname, flags)
+ qm_scano (qm, fd, flags)
+
+The macro symbol table is accessed with QM_ACCESS, which will compile or
+update the in-core version of the symbol table if necessary. A call to
+QM_SYMTAB is required to obtain the symbol table descriptor, a pointer,
+which may change any time the symbol table is modified. QM_ACCESS should
+be called only occasionally (e.g., at datafile open time) since it makes a
+number of system calls to check file dates. QM_SYMTAB should be called
+once upon entry to every routine which accesses the macro database.
+
+QM_SETDEFAULTS is called when a datafile is opened to set the default values
+of all interface and datafile parameters; the user can control these defaults
+by including SET statements in the macro definition file. QM_UPDDEFAULTS is
+similar, except that it sets ONLY the values of those parameters that have
+been explicitly set in SET statements in the macro files.
+
+When QM_ACCESS is called it looks for two variables in the user environment.
+
+ QMSAVE The name of the file (default home$qpoe.msv) in
+ which the compiled macro database is to be saved,
+ or from which it is to be loaded.
+
+ QMFILES A comma delimited list of macro definition (MD)
+ files to be scanned to compile the macro database.
+ (No default unless one is supplied by the local
+ system administrator).
+
+If the binary symbol table file QMSAVE exists and is newer than any of the MD
+files then the symbol table is reloaded from the binary save file, else the MD
+files are scanned and we attempt to write a new save file when done. If the
+symbol table is already open and is newer than either the save file or the
+MD files, then the routine exits immediately, returning a pointer to the global
+QPOE macro database (symbol table). QM_SCAN and QM_SCANO are low level
+routines for reading the contents of a MD file or stream into the symbol table.
+
+Note that at runtime, a completely different facility exists for macro
+replacement; macros may be drawn from either source. The second mechanism
+takes the name of the macro to be the *name* of a file in the current
+directory containing the value string for the macro. This is less efficient,
+but allows macros to be independently created and used dynamically at runtime.
+The latter type of macros may be referenced only in QPOE selection expressions
+(rather than as, for example, parameter name aliases).
+.endhelp ----------------------------------------------------------------------
+
+# Size limiting definitions.
+define QM_MAXFILES 32 # maximum files in QMFILES list
+define QM_SZCBUF 1024 # char storage for file list
+define DEF_LENINDEX 50 # symbol table (init hash index)
+define DEF_LENSTAB 256 # symbol table (init table len)
+define DEF_SZSBUF 1024 # symbol table (init string buf len)
+define SZ_MNAME 32 # max size macro name
+define SZ_MVBUF 8192 # max size macro value
+
+# Defined parameters.
+define QMFILES "qmfiles" # macro define file list
+define QMSAVE "qmsave" # symtab save file
+define DEF_QMSAVE "uparm$qpoe.msv" # default macro save file
+define STTIME "$STTIME" # time of last st compile
+define PSETKW "$PSETKW" # param used to store SET values
+define QMSTNAME "QPOEMACROS" # symbol table name
+
+# Flags for QM_SCAN.
+define QM_FLAGREDEFS 1B # complain about redefined macros
+
+# The QM descriptor (fixed pointer, while QM_ST is allowed to change).
+define LEN_QM 1
+define QM_ST Memi[$1] # pointer to macro symbol table
+
+# The parameter set descriptor (for SET statements).
+define LEN_PSET 32 # allow some extra space
+define PS_EXPBLEN Memi[$1] # QPEX program buffer length
+define PS_EXDBLEN Memi[$1+1] # QPEX data buffer length
+define PS_EXMAXFRLLEN Memi[$1+2] # QPEX max FRLUT length
+define PS_EXMAXRRLLEN Memi[$1+3] # QPEX max RRLUT length
+define PS_EXLMINRANGES Memi[$1+4] # QPEX max ranges before using LUT
+define PS_EXLSCALE Memi[$1+5] # QPEX scale nranges to LUT bins
+define PS_SZPBBUF Memi[$1+6] # size of pushback buffer for macros
+define PS_BUCKETLEN Memi[$1+7] # QPIO event file bucket size
+define PS_FMMAXLFILES Memi[$1+8] # FMIO maxlfiles
+define PS_FMMAXPTPAGES Memi[$1+9] # FMIO maxptpages (page table pages)
+define PS_FMPAGESIZE Memi[$1+10] # FMIO pagesize
+define PS_FMCACHESIZE Memi[$1+11] # FMIO buffer cache size
+define PS_STINDEXLEN Memi[$1+12] # SYMTAB hash index length
+define PS_STSTABLEN Memi[$1+13] # SYMTAB stab len (start)
+define PS_STSBUFSIZE Memi[$1+14] # SYMTAB sbuf size (start)
+define PS_NODEFFILT Memi[$1+15] # Disable use of default filter
+define PS_NODEFMASK Memi[$1+16] # Disable use of default mask
+define PS_XBLOCK Memr[P2R($1+17)]# QPIO blocking factor in X
+define PS_YBLOCK Memr[P2R($1+18)]# QPIO blocking factor in Y
+define PS_DEBUG Memi[$1+19] # debug level
+define PS_OPTBUFSIZE Memi[$1+20] # QPIO/QPF FIO optimum buffer size
+
+# Handy macros.
+define IS_PUNCT (IS_WHITE($1)||($1)==','||($1)=='\n')
+
+
+# QM_ACCESS -- Access the QPOE macro descriptor. Once opened, this should
+# remain open for the lifetime of the process. Since these macros are global,
+# the single descriptor is shared by all open datafiles and all tasks in the
+# process.
+
+pointer procedure qm_access()
+
+int nfiles, fd, i
+bool save_file_exists
+pointer file[QM_MAXFILES]
+long fi[LEN_FINFO], date[QM_MAXFILES], stdate
+pointer sp, qmfiles, qmsave, cbuf, qm, st, st_start, start, sym, ps, ip, op
+
+long clktime()
+int envfind(), finfo(), open()
+pointer stopen(), stenter(), stfind(), strestore()
+errchk stopen, malloc, syserrs
+string sttimekw STTIME
+define uptodate_ 91
+data qm /NULL/
+
+begin
+ call smark (sp)
+ call salloc (qmfiles, SZ_LINE, TY_CHAR)
+ call salloc (qmsave, SZ_PATHNAME, TY_CHAR)
+ call salloc (cbuf, QM_SZCBUF, TY_CHAR)
+
+ # Open the QM descriptor only once (per process).
+ if (qm == NULL) {
+ # Allocate descriptor.
+ call malloc (qm, LEN_QM, TY_STRUCT)
+
+ # Initialize symbol table.
+ st = stopen (QMSTNAME, DEF_LENINDEX, DEF_LENSTAB, DEF_SZSBUF)
+ sym = stenter (st, sttimekw, SZ_LONG); Meml[sym] = 0
+
+ # Initialize settable interface/datafile parameters.
+ ps = stenter (st, PSETKW, LEN_PSET)
+ call aclri (Memi[ps], LEN_PSET)
+
+ # Free back to here when rebuilding symbol table.
+ call stmark (st, st_start)
+ }
+
+ # Get the QMSAVE symtab save file filename.
+ if (envfind (QMSAVE, Memc[qmsave], SZ_PATHNAME) <= 0)
+ call strcpy (DEF_QMSAVE, Memc[qmsave], SZ_PATHNAME)
+
+ # Get the QMFILES macro define file list.
+ if (envfind (QMFILES, Memc[qmfiles], SZ_LINE) <= 0)
+ Memc[qmfiles] = EOS
+
+ # Process the QMFILES string into a list of filenames, and get the
+ # modify date of each file.
+
+ nfiles = 0
+ op = cbuf
+ ip = qmfiles
+
+ repeat {
+ # Get the next comma delimited argument from QMFILES.
+ while (IS_PUNCT(Memc[ip]))
+ ip = ip + 1
+
+ start = op
+ while (Memc[ip] != EOS && !IS_PUNCT(Memc[ip])) {
+ Memc[op] = Memc[ip]
+ op = op + 1
+ ip = ip + 1
+ }
+
+ Memc[op] = EOS
+ op = op + 1
+ if (Memc[start] == EOS)
+ break
+
+ # Add the file and its modify date to the file list.
+ if (finfo (Memc[start], fi) == ERR) {
+ call eprintf ("Warning: QPOE macro file %s not found\n")
+ call pargstr (Memc[start])
+ } else {
+ nfiles = nfiles + 1
+ if (nfiles > QM_MAXFILES)
+ call syserrs (SYS_QMNFILES, Memc[qmsave])
+ file[nfiles] = start
+ date[nfiles] = fi[FI_MTIME]
+ }
+ }
+
+ # Check the dates of the MD files against the in-core symbol table
+ # and exit if the symbol table is up to date. The date of the symbol
+ # table is stored in the table itself.
+
+ sym = stfind (st, sttimekw)
+ if (nfiles > 0 && sym != NULL) {
+ stdate = Meml[sym]
+ for (i=1; i <= nfiles; i=i+1)
+ if (date[i] > stdate)
+ break
+ if (i > nfiles)
+ goto uptodate_
+ }
+
+ # If a binary symtab save file exists and is up to date, load it
+ # into the descriptor.
+
+ save_file_exists = false
+ if (nfiles > 0)
+ save_file_exists = (finfo (Memc[qmsave], fi) == OK)
+
+ if (save_file_exists) {
+ stdate = fi[FI_MTIME]
+ for (i=1; i <= nfiles; i=i+1)
+ if (date[i] > stdate)
+ break
+ if (i > nfiles || nfiles == 0) {
+ iferr (fd = open (Memc[qmsave], READ_ONLY, BINARY_FILE)) {
+ call eprintf ("Warning: cannot open ")
+ call eprintf ("QPOE macro save file %s\n")
+ call pargstr (Memc[qmsave])
+ } else {
+ call stclose (st)
+ st = strestore (fd)
+ call close (fd)
+ goto uptodate_
+ }
+ }
+ }
+
+ # If we get here then we need to scan the MD files and build a new
+ # symbol table.
+
+ # Rebuild the symbol table.
+ call stfree (st, st_start)
+ call stmark (st, st_start)
+ QM_ST(qm) = st
+
+ for (i=1; i <= nfiles; i=i+1)
+ iferr (call qm_scan (qm, Memc[file[i]], 0))
+ call erract (EA_WARN)
+
+ # Set the time of last update.
+ Meml[sym] = clktime (0)
+
+ # Update the save file if we have any defined macros.
+ if (nfiles > 0) {
+ call intr_disable()
+ if (save_file_exists)
+ iferr (call delete (Memc[qmsave]))
+ call erract (EA_WARN)
+ iferr (fd = open (Memc[qmsave], NEW_FILE, BINARY_FILE))
+ call erract (EA_WARN)
+ else {
+ iferr (call stsave (st, fd))
+ call erract (EA_WARN)
+ call close (fd)
+ }
+ call intr_enable()
+ }
+
+uptodate_
+ call sfree (sp)
+ QM_ST(qm) = st
+ return (qm)
+end
+
+
+# QM_SYMTAB -- Get a pointer to the symbol table used to store the defined
+# macros for QPOE. The level of indirection is needed so that the QM pointer
+# can be fixed while the symtab pointer is allowed to change as the symbol
+# table is modified or rebuilt.
+
+pointer procedure qm_symtab (qm)
+
+pointer qm #I QM descriptor
+
+begin
+ return (QM_ST(qm))
+end
+
+
+# QM_SCAN -- Scan a macro definition (MD) file and add any macros defined
+# therein into the symbol table.
+
+procedure qm_scan (qm, fname, flags)
+
+pointer qm #I QM descriptor
+char fname[ARB] #I MD file name
+int flags #I scan flags
+
+int fd
+int open()
+errchk open
+
+begin
+ fd = open (fname, READ_ONLY, TEXT_FILE)
+ call qm_scano (qm, fd, flags)
+ call close (fd)
+end
+
+
+# QM_SCANO -- Scan a stream and parse any macro defines therein, adding the
+# defined macros to the given symbol table, and setting the values of the
+# specified interface or datafile parameters.
+#
+# The syntax of a SET statement, used to set the default values of interface
+# and datafile parameters, is as follows:
+#
+# set parameter value
+#
+# where the parameter names are as given in <qpset.h> (case not significant).
+# Parameter values set in this way may be overridden by QP_SETI calls after
+# opening a datafile.
+#
+# The syntax of a macro define is as follows:
+#
+# define name value
+#
+# where NAME is a simple alphanumeric identifier, and the string VALUE may
+# contain references of the form $N, N=0:9, $0 being the macro name, $1:9
+# being replaced by the arguments to the macro when it is called. Newline
+# may be escaped to enter multiline macro definition statements. Comments
+# and blank lines are ignored. During macro expansion, any parenthesized
+# arguments following the macro name will be consumed only if the macro as
+# defined has symbolic arguments. The value string will be inserted without
+# adding any whitespace at either end, and whitespace within the value string
+# is significant.
+
+procedure qm_scano (qm, fd, flags)
+
+pointer qm #I QM descriptor
+int fd #I input stream
+int flags #I scan flags
+
+int ch
+bool is_define, is_set
+int symarg, junk, buflen, i
+pointer sp, mname, mvbuf, sym, st, op, otop
+
+bool streq()
+int qm_getc(), stpstr()
+pointer stfind(), stenter()
+errchk qm_getc, qm_setparam, stenter, stpstr, malloc, realloc
+define next_ 91
+
+begin
+ call smark (sp)
+ call salloc (mname, SZ_MNAME, TY_CHAR)
+ call malloc (mvbuf, SZ_MVBUF, TY_CHAR)
+
+ st = QM_ST(qm)
+ junk = qm_getc (NULL, ch)
+
+ # The following can only be set true in set statements, so we must
+ # initialize the values before processing the file.
+
+ sym = stfind (st, PSETKW)
+ if (sym != NULL) {
+ PS_NODEFFILT(sym) = NO
+ PS_NODEFMASK(sym) = NO
+ }
+
+ # Each loop processes one newline delimited statement from the
+ # input stream. The qm_getc function deals with continuation,
+ # blank lines and comments, etc.
+
+ repeat {
+ # Get `define' and macro name (or `set' and parameter name).
+next_
+ do i = 1, 2 {
+ # Get identifier token.
+ op = mname
+ otop = mname + SZ_MNAME - 1
+ while (qm_getc (fd, ch) != EOF) {
+ if (IS_ALNUM(ch) || ch == '_') {
+ Memc[op] = ch
+ op = min (otop, op + 1)
+ } else if (ch == '\n') {
+ if (op == mname)
+ goto next_
+ else {
+ call ungetci (fd, ch)
+ break
+ }
+ } else if (IS_WHITE(ch) && op == mname) {
+ next
+ } else
+ break
+ }
+ Memc[op] = EOS
+
+ # Process statement type keyword.
+ if (i == 1) {
+ is_define = (streq (Memc[mname], "define"))
+ is_set = (streq (Memc[mname], "set"))
+
+ # Ignore statements other than SET or DEFINE.
+ if (!(is_define || is_set)) {
+ while (qm_getc (fd, ch) != EOF)
+ if (ch == '\n')
+ goto next_
+ }
+ }
+ }
+
+ # Check for EOF.
+ if (Memc[mname] == EOS)
+ break
+
+ # Skip optional "=" if SET statement.
+ if (is_set)
+ while (IS_WHITE(ch)) {
+ if (qm_getc (fd, ch) == EOF)
+ break
+ else if (ch == '\n')
+ break
+ else if (ch == '=')
+ ch = ' '
+ }
+
+ # Skip to value string; leave first char in ch.
+ while (IS_WHITE(ch)) {
+ if (qm_getc (fd, ch) == EOF)
+ break
+ else if (ch == '\n')
+ break
+ }
+
+ # Get value string. Check for the presence of any symbolic
+ # arguments of the form $N in the process.
+
+ symarg = 0
+ buflen = SZ_MVBUF
+ op = mvbuf
+
+ Memc[op] = ch
+ op = op + 1
+
+ while (qm_getc (fd, ch) != EOF) {
+ if (ch == '\n')
+ break
+ else {
+ Memc[op] = ch
+ if (IS_DIGIT(ch))
+ if (op > mvbuf)
+ if (Memc[op-1] == '$')
+ symarg = max (symarg, TO_INTEG(ch))
+ op = op + 1
+ if (op - mvbuf == buflen) {
+ call realloc (mvbuf, buflen + SZ_MVBUF, TY_CHAR)
+ op = mvbuf + buflen
+ buflen = buflen + SZ_MVBUF
+ }
+ }
+ }
+ Memc[op] = EOS
+
+ # Process SET statements.
+ if (is_set) {
+ call strlwr (Memc[mname])
+ call qm_setparam (qm, Memc[mname], Memc[mvbuf])
+ next
+ }
+
+ # Check for a redef.
+ if (and (flags, QM_FLAGREDEFS) != 0) {
+ sym = stfind (st, Memc[mname])
+ if (sym != NULL) {
+ call eprintf ("Warning: QPOE macro `%s' redefined\n")
+ call pargstr (Memc[mname])
+ }
+ }
+
+ # Enter the macro into the symbol table.
+ sym = stenter (st, Memc[mname], LEN_SYMBOL)
+ S_OFFSET(sym) = stpstr (st, Memc[mvbuf], 0)
+ S_DTYPE(sym) = TY_MACRO
+ S_FLAGS(sym) = 0
+ if (symarg > 0)
+ S_FLAGS(sym) = SF_MACARGS
+ else
+ S_FLAGS(sym) = 0
+ }
+
+ call mfree (mvbuf, TY_CHAR)
+ call sfree (sp)
+end
+
+
+# QM_SETPARAM -- Set the default value of an interface or datafile parameter.
+
+procedure qm_setparam (qm, param, valstr)
+
+pointer qm #I QM descriptor
+char param[ARB] #I parameter to be set
+char valstr[ARB] #I parameter value
+
+pointer ps
+double dval
+int value, ip, pp
+int qp_ctoi(), qp_ctod(), strncmp()
+pointer stfind()
+bool streq()
+errchk stfind
+define err_ 91
+
+begin
+ ps = stfind (QM_ST(qm), PSETKW)
+ if (ps == NULL)
+ return
+
+ # Accept either QP_PARAM or just PARAM.
+ pp = 1
+ if (strncmp (param, "qp_", 3) == 0)
+ pp = 4
+
+ # Decode the parameter value - mostly integer parameters at present,
+ # except for "nodeffilt" and "nodefmask" which do not have a value,
+ # and the blocking factors, which are floating point.
+
+ ip = 1
+ if (strncmp (param[pp], "nodef", 5) == 0) {
+ return
+ } else if (strncmp (param[pp], "block", 5) == 0) {
+ if (qp_ctod (valstr, ip, dval) <= 0)
+ goto err_
+ PS_XBLOCK(ps) = dval
+ PS_YBLOCK(ps) = dval
+ return
+ } else if (strncmp (param[pp], "xblock", 6) == 0) {
+ if (qp_ctod (valstr, ip, dval) <= 0)
+ goto err_
+ PS_XBLOCK(ps) = dval
+ return
+ } else if (strncmp (param[pp], "yblock", 6) == 0) {
+ if (qp_ctod (valstr, ip, dval) <= 0)
+ goto err_
+ PS_YBLOCK(ps) = dval
+ return
+ } else {
+ if (qp_ctoi (valstr, ip, value) <= 0) {
+err_ call eprintf ("bad value `%s' for QPOE parameter `%s'\n")
+ call pargstr (valstr)
+ call pargstr (param)
+ return
+ }
+ }
+
+ # Set the parameter value in the global QM descriptor.
+ if ( streq (param[pp], "bucketlen"))
+ PS_BUCKETLEN(ps) = value
+ else if (streq (param[pp], "cachesize"))
+ PS_FMCACHESIZE(ps) = value
+ else if (streq (param[pp], "indexlen"))
+ PS_STINDEXLEN(ps) = value
+ else if (streq (param[pp], "maxlfiles"))
+ PS_FMMAXLFILES(ps) = value
+ else if (streq (param[pp], "maxptpages"))
+ PS_FMMAXPTPAGES(ps) = value
+ else if (streq (param[pp], "pagesize"))
+ PS_FMPAGESIZE(ps) = value
+ else if (streq (param[pp], "sbufsize"))
+ PS_STSBUFSIZE(ps) = value
+ else if (streq (param[pp], "stablen"))
+ PS_STSTABLEN(ps) = value
+ else if (streq (param[pp], "progbuflen"))
+ PS_EXPBLEN(ps) = value
+ else if (streq (param[pp], "databuflen"))
+ PS_EXDBLEN(ps) = value
+ else if (streq (param[pp], "maxfrlutlen"))
+ PS_EXMAXFRLLEN(ps) = value
+ else if (streq (param[pp], "maxrrlutlen"))
+ PS_EXMAXRRLLEN(ps) = value
+ else if (streq (param[pp], "lutminranges"))
+ PS_EXLMINRANGES(ps) = value
+ else if (streq (param[pp], "lutscale"))
+ PS_EXLSCALE(ps) = value
+ else if (streq (param[pp], "maxpushback"))
+ PS_SZPBBUF(ps) = value
+ else if (streq (param[pp], "nodeffilt"))
+ PS_NODEFFILT(ps) = YES
+ else if (streq (param[pp], "nodefmask"))
+ PS_NODEFMASK(ps) = YES
+ else if (streq (param[pp], "blockfactor"))
+ { PS_XBLOCK(ps) = value; PS_YBLOCK(ps) = value }
+ else if (streq (param[pp], "xblockfactor"))
+ PS_XBLOCK(ps) = value
+ else if (streq (param[pp], "yblockfactor"))
+ PS_YBLOCK(ps) = value
+ else if (streq (param[pp], "debuglevel"))
+ PS_DEBUG(ps) = value
+ else if (streq (param[pp], "optbufsize"))
+ PS_OPTBUFSIZE(ps) = value
+ else {
+ call eprintf ("unknown QPOE parameter `%s' in SET statement\n")
+ call pargstr (param)
+ }
+end
+
+
+# QM_SETDEFAULTS -- Set the current default values of all interface and
+# datafile parameters in a QPOE descriptor. Called at datafile open time
+# to get the defaults.
+
+procedure qm_setdefaults (qm, qp)
+
+pointer qm #I QM descriptor
+pointer qp #I QPOE descriptor
+
+pointer ps
+pointer stfind()
+int qm_spari()
+real qm_sparr()
+errchk stfind
+
+begin
+ ps = stfind (QM_ST(qm), PSETKW)
+ if (ps == NULL)
+ return
+
+ # Interface parameters.
+ QP_EXPBLEN(qp) = qm_spari (PS_EXPBLEN(ps), DEF_PROGBUFLEN)
+ QP_EXDBLEN(qp) = qm_spari (PS_EXDBLEN(ps), DEF_DATABUFLEN)
+ QP_EXMAXFRLLEN(qp) = qm_spari (PS_EXMAXFRLLEN(ps), DEF_MAXFRLUTLEN)
+ QP_EXMAXRRLLEN(qp) = qm_spari (PS_EXMAXRRLLEN(ps), DEF_MAXRRLUTLEN)
+ QP_EXLMINRANGES(qp) = qm_spari (PS_EXLMINRANGES(ps), DEF_LUTMINRANGES)
+ QP_EXLSCALE(qp) = qm_spari (PS_EXLSCALE(ps), DEF_LUTSCALE)
+ QP_SZPBBUF(qp) = qm_spari (PS_SZPBBUF(ps), DEF_MAXPUSHBACK)
+ QP_FMCACHESIZE(qp) = qm_spari (PS_FMCACHESIZE(ps), DEF_FMCACHESIZE)
+
+ # Datafile parameters.
+ QP_BUCKETLEN(qp) = qm_spari (PS_BUCKETLEN(ps), DEF_BUCKETLEN)
+ QP_FMMAXLFILES(qp) = qm_spari (PS_FMMAXLFILES(ps), DEF_FMMAXLFILES)
+ QP_FMMAXPTPAGES(qp) = qm_spari (PS_FMMAXPTPAGES(ps), DEF_FMMAXPTPAGES)
+ QP_FMPAGESIZE(qp) = qm_spari (PS_FMPAGESIZE(ps), DEF_FMPAGESIZE)
+ QP_STINDEXLEN(qp) = qm_spari (PS_STINDEXLEN(ps), DEF_STINDEXLEN)
+ QP_STSTABLEN(qp) = qm_spari (PS_STSTABLEN(ps), DEF_STSTABLEN)
+ QP_STSBUFSIZE(qp) = qm_spari (PS_STSBUFSIZE(ps), DEF_STSBUFSIZE)
+
+ # Other parameters.
+ QP_NODEFFILT(qp) = qm_spari (PS_NODEFFILT(ps), NO)
+ QP_NODEFMASK(qp) = qm_spari (PS_NODEFMASK(ps), NO)
+ QP_XBLOCK(qp) = qm_sparr (PS_XBLOCK(ps), DEF_BLOCKFACTOR)
+ QP_YBLOCK(qp) = qm_sparr (PS_YBLOCK(ps), DEF_BLOCKFACTOR)
+ QP_OPTBUFSIZE(qp) = qm_spari (PS_OPTBUFSIZE(ps), DEF_OPTBUFSIZE)
+ QP_DEBUG(qp) = qm_spari (PS_DEBUG(ps), 0)
+end
+
+
+# QM_SETPAR -- Return the given parameter value, if set in the user's macro
+# files, otherwise return the interface default.
+
+int procedure qm_setpar (userval, defval)
+
+int userval #I user specified value, or zero
+int defval #I interface default
+int qm_spari()
+
+begin
+ return (qm_spari (userval, defval))
+end
+
+
+# QM_SPARI -- Return the given int parameter value, if set in the user's macro
+# files, otherwise return the interface default.
+
+int procedure qm_spari (userval, defval)
+
+int userval #I user specified value, or zero
+int defval #I interface default
+
+begin
+ if (userval != 0)
+ return (userval)
+ else
+ return (defval)
+end
+
+
+# QM_SPARR -- Return the given real parameter value, if set in the user's macro
+# files, otherwise return the interface default.
+
+real procedure qm_sparr (userval, defval)
+
+real userval #I user specified value, or zero
+real defval #I interface default
+
+begin
+ if (userval != 0)
+ return (userval)
+ else
+ return (defval)
+end
+
+
+# QM_UPDDEFAULTS -- Update the values in the QPOE descriptor of all interface
+# and datafile parameters set explicitly by a user macro or SET statement.
+# Only those parameters for which values were explicitly specified in the
+# use macro files are affected, allowing the use of global macros or set
+# statements to override the interface or datafile defaults.
+
+procedure qm_upddefaults (qm, qp)
+
+pointer qm #I QM descriptor
+pointer qp #I QPOE descriptor
+
+pointer ps
+pointer stfind()
+errchk stfind
+
+begin
+ ps = stfind (QM_ST(qm), PSETKW)
+ if (ps == NULL)
+ return
+
+ # Interface parameters.
+ if (PS_EXPBLEN(ps) != 0) QP_EXPBLEN(qp) = PS_EXPBLEN(ps)
+ if (PS_EXDBLEN(ps) != 0) QP_EXDBLEN(qp) = PS_EXDBLEN(ps)
+ if (PS_EXMAXFRLLEN(ps) != 0) QP_EXMAXFRLLEN(qp) = PS_EXMAXFRLLEN(ps)
+ if (PS_EXMAXRRLLEN(ps) != 0) QP_EXMAXRRLLEN(qp) = PS_EXMAXRRLLEN(ps)
+ if (PS_EXLMINRANGES(ps) != 0) QP_EXLMINRANGES(qp)= PS_EXLMINRANGES(ps)
+ if (PS_EXLSCALE(ps) != 0) QP_EXLSCALE(qp) = PS_EXLSCALE(ps)
+ if (PS_SZPBBUF(ps) != 0) QP_SZPBBUF(qp) = PS_SZPBBUF(ps)
+ if (PS_FMCACHESIZE(ps) != 0) QP_FMCACHESIZE(qp) = PS_FMCACHESIZE(ps)
+
+ # Datafile parameters.
+ if (PS_BUCKETLEN(ps) != 0) QP_BUCKETLEN(qp) = PS_BUCKETLEN(ps)
+ if (PS_FMMAXLFILES(ps) != 0) QP_FMMAXLFILES(qp) = PS_FMMAXLFILES(ps)
+ if (PS_FMMAXPTPAGES(ps) != 0) QP_FMMAXPTPAGES(qp)= PS_FMMAXPTPAGES(ps)
+ if (PS_FMPAGESIZE(ps) != 0) QP_FMPAGESIZE(qp) = PS_FMPAGESIZE(ps)
+ if (PS_STINDEXLEN(ps) != 0) QP_STINDEXLEN(qp) = PS_STINDEXLEN(ps)
+ if (PS_STSTABLEN(ps) != 0) QP_STSTABLEN(qp) = PS_STSTABLEN(ps)
+ if (PS_STSBUFSIZE(ps) != 0) QP_STSBUFSIZE(qp) = PS_STSBUFSIZE(ps)
+
+ # Other parameters.
+ if (PS_NODEFFILT(ps) != 0) QP_NODEFFILT(qp) = PS_NODEFFILT(ps)
+ if (PS_NODEFMASK(ps) != 0) QP_NODEFMASK(qp) = PS_NODEFMASK(ps)
+ if (PS_XBLOCK(ps) != 0) QP_XBLOCK(qp) = PS_XBLOCK(ps)
+ if (PS_YBLOCK(ps) != 0) QP_YBLOCK(qp) = PS_YBLOCK(ps)
+ if (PS_OPTBUFSIZE(ps) != 0) QP_OPTBUFSIZE(qp) = PS_OPTBUFSIZE(ps)
+ if (PS_DEBUG(ps) != 0) QP_DEBUG(qp) = PS_DEBUG(ps)
+end
+
+
+# QM_GETC -- Return the next character from the input stream, ignoring
+# comments and joining continued lines. The character value or EOF is
+# returned as the function value. A call with FD=0 will initialize i/o
+# for a new file.
+
+int procedure qm_getc (fd, ch)
+
+int fd #I input file
+int ch #O returned character
+
+int quote
+int getci()
+errchk getci
+define again_ 91
+
+begin
+ # Initialization.
+ if (fd <= 0) {
+ quote = 0
+ return (0)
+ }
+
+ # Handle the most common cases first.
+again_
+ if (getci (fd, ch) == EOF) {
+ quote = 0
+ return (EOF)
+ } else if (IS_ALNUM(ch))
+ return (ch)
+
+ # Handle the special cases - comments, escapes, quoted strings.
+ if (ch == '#' && quote == 0) {
+ # Skip a comment.
+ while (getci (fd, ch) != EOF)
+ if (ch == '\n')
+ goto again_
+ } else if (ch == '\'' || ch == '"') {
+ # Toggle quoted string flag.
+ if (quote == 0)
+ quote = ch
+ else if (quote == ch)
+ quote = 0
+ } else if (ch == '\\') {
+ # Process escapes.
+ if (getci (fd, ch) == '\n')
+ goto again_
+ else if (quote == 0 && (ch == '\'' || ch == '"' || ch == '#'))
+ ;
+ else {
+ call ungetci (fd, ch)
+ ch = '\\'
+ }
+ }
+
+ # Init context at end of every logical line.
+ if (ch == '\n')
+ quote = 0
+
+ return (ch)
+end
diff --git a/sys/qpoe/qpmkfname.x b/sys/qpoe/qpmkfname.x
new file mode 100644
index 00000000..3721f6f4
--- /dev/null
+++ b/sys/qpoe/qpmkfname.x
@@ -0,0 +1,23 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_MKFNAME -- Construct the poefile filename, i.e., eliminate any whitespace
+# and add the given extension if omitted.
+
+procedure qp_mkfname (poefile, extn, fname, maxch)
+
+char poefile[ARB] #I raw poefile name
+char extn[ARB] #I extension to be added if absent
+char fname[maxch] #O output filename
+int maxch #I max chars out
+
+int n
+bool strne()
+int nowhite()
+
+begin
+ n = nowhite (poefile, fname, maxch)
+ if (n <= 3 || strne (fname[n-2], extn))
+ call strcpy (extn, fname[n+1], maxch-n)
+end
diff --git a/sys/qpoe/qpoe.h b/sys/qpoe/qpoe.h
new file mode 100644
index 00000000..41d07059
--- /dev/null
+++ b/sys/qpoe/qpoe.h
@@ -0,0 +1,115 @@
+# QPOE.H -- QPOE data definitions (private to the package).
+
+# Size limiting definitions.
+define DEF_BLOCKFACTOR 1.0 # default block factor for image matrix
+define DEF_BUCKETLEN 1024 # def nevents per bucket
+define DEF_FMMAXLFILES 128 # def maxlfile per datafile
+define DEF_FMMAXPTPAGES 256 # def maxptpages per datafile
+define DEF_FMPAGESIZE 512 # def datafile page size
+define DEF_FMCACHESIZE 8 # def buffer cache size
+define DEF_STINDEXLEN 100 # def symtab hash index len
+define DEF_STSTABLEN 2048 # initial symbol table len
+define DEF_STSBUFSIZE 2048 # initial string buf size
+define DEF_MAXPUSHBACK 8192 # max pushed back chars (macros)
+define DEF_OPTBUFSIZE (512*512*2) # default buffer size for IMIO/QPF/FIO
+define MAX_INDIR 20 # max macro indirection
+define MAX_REDEF 20 # max entries for a symbol
+define MAX_FIELDS 50 # max fields in a user structure
+define INC_STRLEN 32 # unit of storage for strings
+define LEN_PVAL 64 # max TY_USER struct size (in doubles)
+define SZ_QPDFNAME 255 # max size QPOE filename
+define SZ_TEXTBUF 2048 # handy text buffer for macro expansion
+define SZ_TOKBUF 256 # token buffer size
+
+# Magic numbers.
+define LF_QPOE 1 # QPOE file header and symbol table
+define LF_STATICPARS 2 # static (fixed size) params
+define QPOE_MAGIC 121120B # QPOE magic code (descriptor type)
+define QPOE_VERSION 101 # QPOE interface version number
+define QPOE_TITLE "QPOE-V1.2" # title string, for symbol table
+define QPOE_EXTN ".qp" # QPOE file extension
+define QPOE_MACROEXTN ".qpm" # QPOE macro definitions file extension
+define QPWCS "qpwcs" # header parameter for default WCS
+define IMMEDIATE 0 # for qp_sizeof
+define INSTANCEOF 1 # for qp_sizeof
+
+# The main QPOE descriptor.
+define LEN_QPDES 160
+define QP_MAGIC Memi[$1] # descriptor type code
+define QP_VERSION Memi[$1+1] # QPOE version number
+define QP_ACTIVE Memi[$1+2] # descriptor fully activated
+define QP_FM Memi[$1+3] # datafile handle
+define QP_ST Memi[$1+4] # datafile symbol table handle
+define QP_QM Memi[$1+5] # global QPOE symbol table handle
+define QP_MODE Memi[$1+6] # datafile access mode
+define QP_OQP Memi[$1+7] # o_qp, if new copy file
+define QP_EXPBLEN Memi[$1+8] # QPEX program buffer length
+define QP_EXDBLEN Memi[$1+9] # QPEX data buffer length
+define QP_EXMAXFRLLEN Memi[$1+10] # QPEX max FRLUT length
+define QP_EXMAXRRLLEN Memi[$1+11] # QPEX max RRLUT length
+define QP_EXLMINRANGES Memi[$1+12] # QPEX min ranges before using LUT
+define QP_EXLSCALE Memi[$1+13] # QPEX scale nranges to LUT bins
+define QP_SZPBBUF Memi[$1+14] # size of pushback buffer for macros
+define QP_BUCKETLEN Memi[$1+15] # QPIO event file bucket size
+define QP_FMMAXLFILES Memi[$1+16] # FMIO maxlfiles
+define QP_FMMAXPTPAGES Memi[$1+17] # FMIO maxptpages
+define QP_FMPAGESIZE Memi[$1+18] # FMIO pagesize
+define QP_FMCACHESIZE Memi[$1+19] # FMIO buffer cache size
+define QP_STINDEXLEN Memi[$1+20] # SYMTAB hash index length
+define QP_STSTABLEN Memi[$1+21] # SYMTAB stab len (start)
+define QP_STSBUFSIZE Memi[$1+22] # SYMTAB sbuf size (start)
+define QP_STOFFSET Memi[$1+23] # lfile offset of stored symbol table
+define QP_MODIFIED Memi[$1+24] # QPOE descriptor has been modified
+define QP_DEBUG Memi[$1+25] # global debug level (debug messages)
+define QP_XBLOCK Memr[P2R($1+26)]# default X blocking factor for QPIO
+define QP_YBLOCK Memr[P2R($1+27)]# default Y blocking factor for QPIO
+define QP_OPTBUFSIZE Memi[$1+28] # optimum buffer size for IMIO/QPF/FIO
+define QP_NODEFFILT Memi[$1+29] # disable use of default filter
+define QP_NODEFMASK Memi[$1+30] # disable use of default mask
+define QP_DFNAME Memc[P2C($1+31)] # QPOE filename (for messages)
+
+# Symbol descriptor.
+define LEN_SYMBOL 9
+define S_FLAGS Memi[$1] # integer flag word
+define S_DTYPE Memi[$1+1] # datatype code
+define S_DSYM Memi[$1+2] # offset of domain symbol if TY_USER
+define S_NELEM Memi[$1+3] # number of elements of dtype
+define S_MAXELEM Memi[$1+4] # allocated length
+define S_SZELEM Memi[$1+5] # elsize, chars (primary domains only)
+define S_COMMENT Memi[$1+6] # pointer to comment string in sbuf
+define S_LFILE Memi[$1+7] # lfile where value is stored
+define S_OFFSET Memi[$1+8] # char offset of value in lfile
+
+# Symbol flags.
+define SF_DELETED 0001B # symbol has been deleted
+define SF_INHERIT 0002B # inherit in NEW_COPY mode
+define SF_MACARGS 0004B # macro symbol has symbolic arguments
+
+# QPOE special datatypes.
+define SPPTYPES "bcsilrdx" # index is SPP TY_xxx type code
+define TY_MACRO 15 # datafile local macro define
+define TY_OPAQUE 16 # opaque (typeless) binary type
+define TY_USER 17 # some user defined type
+
+# Lexical tokens.
+define TOK_IDENTIFIER (-99)
+define TOK_NUMBER (-98)
+define TOK_STRING (-97)
+define TOK_COMMAND (-96)
+define TOK_PLUSEQUALS (-95)
+define TOK_COLONEQUALS (-94)
+
+# QPOE header as stored in datafile.
+define LEN_QPH 32
+define QPH_MAGIC Memi[$1]
+define QPH_VERSION Memi[$1+1]
+define QPH_STOFFSET Memi[$1+2]
+
+# Domain descriptor structure.
+define LEN_DDDES 110
+define DD_STRUCTLEN Memi[$1] # structure length, su
+define DD_NFIELDS Memi[$1+1] # number of fields in user structure
+define DD_XFIELD Memi[$1+2] # field assigned to coordinate "x"
+define DD_YFIELD Memi[$1+3] # field assigned to coordinate "y"
+define DD_FOFFSET Memi[$1+10+$2-1]# array of field offsets
+define DD_FTYPE Memi[$1+60+$2-1]# array of field datatypes
diff --git a/sys/qpoe/qpopen.x b/sys/qpoe/qpopen.x
new file mode 100644
index 00000000..d2b7e0b4
--- /dev/null
+++ b/sys/qpoe/qpopen.x
@@ -0,0 +1,132 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <fmset.h>
+include "qpoe.h"
+include "qpio.h"
+
+# QP_OPEN -- Open or create a QPOE datafile. This routine must be called
+# before the poefile can be accessed. In the case of a create, the file
+# parameters are not fixed until the first i/o or header access occurs,
+# allowing one to use QP_SET calls to modify the file parameters after the
+# open.
+
+pointer procedure qp_open (poefile, mode, o_qp)
+
+char poefile[ARB] #I QPOE file to be opened
+int mode #I file access mode
+pointer o_qp #I reference file, if NEW_COPY
+
+int fmmode, fd, n
+pointer sp, qph, qp, fname, fm
+
+real qp_getr()
+pointer fm_open(), strestore(), qm_access()
+int fm_fopen(), read(), fm_stati(), qp_accessf()
+errchk fm_open, strestore, fm_fopen, seek, read
+errchk calloc, syserrs, qm_access
+
+string s_defblock DEF_BLOCK
+string s_defxblock DEF_XBLOCK
+string s_defyblock DEF_YBLOCK
+
+begin
+ call smark (sp)
+ call salloc (qph, LEN_QPH, TY_STRUCT)
+ call salloc (fname, SZ_PATHNAME, TY_CHAR)
+
+ # Construct the filename (with extension .qp) of the poefile.
+ call qp_mkfname (poefile, QPOE_EXTN, Memc[fname], SZ_PATHNAME)
+
+ # Open or create the poefile via the file manager.
+ fmmode = mode
+ if (mode == NEW_COPY)
+ fmmode = NEW_FILE
+ fm = fm_open (Memc[fname], fmmode)
+
+ # Allocate the QPOE descriptor.
+ call calloc (qp, LEN_QPDES, TY_STRUCT)
+ call strcpy (Memc[fname], QP_DFNAME(qp), SZ_QPDFNAME)
+
+ # Access the global macro database, and set the default values of
+ # all interface and datafile parameters.
+
+ QP_QM(qp) = qm_access()
+ call qm_setdefaults (QP_QM(qp), qp)
+
+ QP_MODE(qp) = mode
+ QP_OQP(qp) = o_qp
+ QP_FM(qp) = fm
+
+ if (mode == NEW_FILE || mode == NEW_COPY) {
+ # Initialize the descriptor for a new poefile. The file
+ # attributes are not fixed until the file is accessed, to
+ # allow time to change the defaults with qp_seti.
+
+ QP_MAGIC(qp) = QPOE_MAGIC
+ QP_VERSION(qp) = QPOE_VERSION
+
+ if (mode == NEW_COPY) {
+ # Inherit datafile defaults from parent file.
+ QP_BUCKETLEN(qp) = QP_BUCKETLEN(o_qp)
+ QP_FMMAXLFILES(qp) = QP_FMMAXLFILES(o_qp)
+ QP_FMMAXPTPAGES(qp) = QP_FMMAXPTPAGES(o_qp)
+ QP_FMPAGESIZE(qp) = QP_FMPAGESIZE(o_qp)
+ QP_FMCACHESIZE(qp) = QP_FMCACHESIZE(o_qp)
+ QP_STINDEXLEN(qp) = QP_STINDEXLEN(o_qp)
+ QP_STSTABLEN(qp) = QP_STSTABLEN(o_qp)
+ QP_STSBUFSIZE(qp) = QP_STSBUFSIZE(o_qp)
+ }
+
+ QP_ACTIVE(qp) = NO
+
+ } else {
+ # Open an existing poefile. The encoded QPOE header and
+ # symbol table are stored in a binary lfile in the datafile.
+
+ fd = fm_fopen (fm, LF_QPOE, READ_ONLY, BINARY_FILE)
+
+ # Read the QPOE file header.
+ n = LEN_QPH * SZ_STRUCT
+ call aclri (Memi[qph], LEN_QPH)
+ if (read (fd, Memi[qph], n) < n)
+ call syserrs (SYS_QPBADFILE, QP_DFNAME(qp))
+ call miiupk32 (Memi[qph], Memi[qph], LEN_QPH, TY_STRUCT)
+
+ QP_MAGIC(qp) = QPH_MAGIC(qph)
+ QP_VERSION(qp) = QPH_VERSION(qph)
+ QP_STOFFSET(qp) = QPH_STOFFSET(qph)
+
+ if (QP_MAGIC(qp) != QPOE_MAGIC)
+ call syserrs (SYS_QPBADFILE, QP_DFNAME(qp))
+
+ # Read the stored symbol table.
+ call seek (fd, QP_STOFFSET(qp))
+ QP_ST(qp) = strestore (fd)
+
+ # Initialize any remaining QP descriptor parameters.
+ QP_FMPAGESIZE(qp) = fm_stati (fm, FM_PAGESIZE)
+ call fm_seti (fm, FM_FCACHESIZE, DEF_FMCACHESIZE)
+ QP_ACTIVE(qp) = YES
+
+ # See if the default block factor is set in the datafile header.
+ if (qp_accessf (qp, s_defblock) == YES) {
+ QP_XBLOCK(qp) = qp_getr (qp, s_defblock)
+ QP_YBLOCK(qp) = QP_XBLOCK(qp)
+ }
+ if (qp_accessf (qp, s_defxblock) == YES)
+ QP_XBLOCK(qp) = qp_getr (qp, s_defxblock)
+ if (qp_accessf (qp, s_defyblock) == YES)
+ QP_YBLOCK(qp) = qp_getr (qp, s_defyblock)
+
+ call close (fd)
+ }
+
+ # Allow any interface parameters set explicitly in global macro SET
+ # statements to override the inherited or datafile values set above.
+
+ call qm_upddefaults (QP_QM(qp), qp)
+
+ call sfree (sp)
+ return (qp)
+end
diff --git a/sys/qpoe/qpparse.x b/sys/qpoe/qpparse.x
new file mode 100644
index 00000000..2bbd5cea
--- /dev/null
+++ b/sys/qpoe/qpparse.x
@@ -0,0 +1,70 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# QP_PARSE -- Parse a QPOE/QPIO specification into the root (poefile) name
+# and event list filter expression fields.
+#
+# Syntax: root[filter]
+#
+# where the filter spec is optional.
+
+procedure qp_parse (qpspec, root, sz_root, filter, sz_filter)
+
+char qpspec[ARB] #I full event list specification
+char root[sz_root] #O receives root name
+int sz_root #I max chars in root name
+char filter[sz_filter] #O receives filter
+int sz_filter #I max chars in filter name
+
+int level, ip, op, ch
+
+begin
+ ip = 1
+ op = 1
+
+ # Extract root name. The first (unescaped) [ marks the start of
+ # the filter field.
+
+ for (ch=qpspec[ip]; ch != EOS && ch != '['; ch=qpspec[ip]) {
+ if (ch == '\\' && qpspec[ip+1] == '[') {
+ root[op] = '\\'
+ op = op + 1
+ root[op] = '['
+ ip = ip + 1
+ } else
+ root[op] = ch
+
+ op = min (sz_root, op + 1)
+ ip = ip + 1
+ }
+
+ root[op] = EOS
+ level = 0
+ op = 1
+
+ # Extract the [] bracketed filter expression, allowing for nested
+ # brackets.
+
+ for (ch=qpspec[ip]; ch != EOS; ch=qpspec[ip]) {
+ if (ch == '[')
+ level = level + 1
+ else if (ch == ']')
+ level = level - 1
+
+ filter[op] = ch
+ op = min (sz_filter, op + 1)
+
+ ip = ip + 1
+ if (level <= 0)
+ break
+ }
+
+ # Add closing brace if the user left it off.
+ if (op > 1 && ch != ']') {
+ filter[op] = ']'
+ op = min (sz_filter, op + 1)
+ }
+
+ filter[op] = EOS
+end
diff --git a/sys/qpoe/qpparsefl.x b/sys/qpoe/qpparsefl.x
new file mode 100644
index 00000000..566274fb
--- /dev/null
+++ b/sys/qpoe/qpparsefl.x
@@ -0,0 +1,149 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+include "qpex.h"
+
+# QP_PARSEFL -- Parse the field list, or declarations string for a user
+# defined datatype (structure or domain).
+#
+# Syntax: { type1, type2, ..., typeN }
+#
+# e.g., {d,s:x,s:y,s,s,s,s} (Rosat/PROS event structure)
+#
+# where the TYPEi are primitive types, e.g., "r" or "real", "i" or "int",
+# etc. Selected fields may have ":x" or ":y" appended to indicate that these
+# are the default coordinate fields to be used for position based extraction.
+# Fields will be automatically aligned as necessary, and the computed structure
+# size will be forced to be an integral multiple of the largest datatype
+# within the structure, to ensure proper alignment in arrays of the structures.
+
+int procedure qp_parsefl (qp, fieldlist, dd)
+
+pointer qp #I QPOE descriptor
+char fieldlist[ARB] #I field list defining new datatype (domain)
+pointer dd #U pointer to domain descriptor
+
+pointer sp, tokbuf, dsym, in
+int nfields, offset, maxsize, xfield, yfield, token, dtype, fsize
+
+pointer qp_opentext()
+int qp_gettok(), qp_nexttok(), sizeof(), qp_dtype()
+errchk qp_gettok, qp_opentext, qp_nexttok
+string qperr "QPOE structdef"
+define nextfield_ 91
+
+begin
+ call smark (sp)
+ call salloc (tokbuf, SZ_TOKBUF, TY_CHAR)
+
+ # Open declarations string for non macro expanded token input.
+ in = qp_opentext (NULL, fieldlist)
+
+ # Advance to structure terms list.
+ while (qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) != EOF)
+ if (Memc[tokbuf] == '{')
+ break
+
+ nfields = 0
+ offset = 0
+ maxsize = 0
+ xfield = 0
+ yfield = 0
+
+ # Process the structure terms list.
+ repeat {
+ token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF)
+
+ switch (token) { # {
+ case EOF, '}':
+ break
+
+ case TOK_IDENTIFIER:
+ # Get field datatype and size.
+ dtype = qp_dtype (qp, Memc[tokbuf], dsym)
+ if (dtype < TY_BOOL || dtype > TY_COMPLEX) {
+ call eprintf ("%s: bad field type `%s'\n")
+ call pargstr (qperr)
+ call pargstr (Memc[tokbuf])
+ goto nextfield_
+ } else
+ fsize = sizeof (dtype)
+
+ # Output field descriptor.
+ nfields = nfields + 1
+ if (nfields > MAX_FIELDS) {
+ call eprintf ("%s: too many fields `%s'\n")
+ call pargstr (qperr)
+ call pargstr (Memc[tokbuf])
+ break
+ }
+ DD_FOFFSET(dd,nfields) = (offset + fsize-1) / fsize
+ DD_FTYPE(dd,nfields) = dtype
+
+ # Update structure size parameters.
+ offset = (DD_FOFFSET(dd,nfields) * fsize) + fsize
+ maxsize = max (maxsize, fsize)
+
+ # Process any :[XY] field modifiers.
+ if (qp_nexttok(in) == ':') {
+ repeat {
+ token = qp_gettok (in, Memc[tokbuf], SZ_TOKBUF)
+ switch (Memc[tokbuf]) {
+ case ':':
+ next
+ case 'x':
+ if (xfield != 0) {
+ call eprintf ("%s: duplicate X field `%s'\n")
+ call pargstr (qperr)
+ call pargstr (Memc[tokbuf])
+ }
+ xfield = nfields
+ break
+ case 'y':
+ if (yfield != 0) {
+ call eprintf ("%s: duplicate Y field `%s'\n")
+ call pargstr (qperr)
+ call pargstr (Memc[tokbuf])
+ }
+ yfield = nfields
+ break
+ default:
+ call eprintf ("%s: unknown : field modifier `%s'\n")
+ call pargstr (qperr)
+ call pargstr (Memc[tokbuf])
+ }
+ }
+ goto nextfield_
+ }
+ case ',':
+ next
+ default:
+ call eprintf ("%s: unexpected token `%s'\n")
+ call pargstr (qperr)
+ call pargstr (Memc[tokbuf])
+ }
+
+nextfield_
+ # Read and discard tokens until we get to the next field.
+ while (qp_gettok (in, Memc[tokbuf], SZ_TOKBUF) != EOF)
+ if (Memc[tokbuf] == ',')
+ break
+ }
+
+ # Complete the domain descriptor initialization.
+ DD_NFIELDS(dd) = nfields
+ DD_XFIELD(dd) = xfield
+ DD_YFIELD(dd) = yfield
+
+ # Pad the struct size to an integral multiple of the max field size.
+ if (nfields > 0) {
+ maxsize = max (SZ_STRUCT, maxsize)
+ DD_STRUCTLEN(dd) = (offset+maxsize-1)/maxsize*maxsize / SZ_STRUCT
+ } else
+ DD_STRUCTLEN(dd) = 0
+
+ call qp_closetext (in)
+ call sfree (sp)
+
+ return (nfields)
+end
diff --git a/sys/qpoe/qppclose.x b/sys/qpoe/qppclose.x
new file mode 100644
index 00000000..7a0bc9ab
--- /dev/null
+++ b/sys/qpoe/qppclose.x
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fset.h>
+
+# QP_PCLOSE -- Close a parameter opened as a file with QP_POPEN. This
+# differs from a simple call to fio.close in that the lfile used to store
+# the parameter data is unlocked, as well as closing the file under FIO.
+
+procedure qp_pclose (fd)
+
+int fd #I file descriptor of QP_POPEN-ed parameter
+
+int lfile, type
+pointer sp, lfname, fm
+int fm_lfparse()
+
+begin
+ call smark (sp)
+ call salloc (lfname, SZ_FNAME, TY_CHAR)
+
+ call fstats (fd, F_FILENAME, Memc[lfname], SZ_FNAME)
+ if (fm_lfparse (Memc[lfname], fm, lfile, type) != ERR)
+ call fm_unlock (fm, lfile)
+
+ call close (fd)
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qppopen.x b/sys/qpoe/qppopen.x
new file mode 100644
index 00000000..4b0e2191
--- /dev/null
+++ b/sys/qpoe/qppopen.x
@@ -0,0 +1,62 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_POPEN -- Open a variable-array type parameter as a file. A call to
+# fio.close is used to close the file. Note that the varlen parameter, which
+# is stored in its own lfile in the datafile, is opened directly as a file
+# independently of the FMIO file buffer cache. Most QPOE parameter i/o is
+# via the cache, hence mixing QP_POPEN calls with ordinary QPOE i/o on the
+# same parameter at the same time could lead to loss of data integrity due
+# to the same lfile being opened simultaneously on two different file
+# descriptors. We ensure that the lfile is not in the file cache at qp_popen
+# time, but no checks are made once the file has been opened. A FIO file
+# descriptor is returned as the function value; CLOSE should be called to
+# close the file descriptor when it is no longer needed.
+
+int procedure qp_popen (qp, param, mode, type)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+int mode #I file(param) access mode
+int type #I file type, text or binary
+
+pointer sym
+int fm_fopen()
+pointer qp_gpsym()
+errchk qp_gpsym, qp_addf(), fm_lockout, syserrs
+
+begin
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ # Lookup the parameter; make sure it is a varlen parameter.
+ # Create a new parameter if none exists and the mode is NEW_FILE.
+
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL) {
+ if (mode != NEW_FILE)
+ call syserrs (SYS_QPUKNPAR, param)
+ else {
+ # Create a new parameter.
+ if (type == TEXT_FILE)
+ call qp_addf (qp, param, "c", 0, "", 0)
+ else
+ call qp_addf (qp, param, "opaque", 0, "", 0)
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL)
+ call syserrs (SYS_QPUKNPAR, param)
+ }
+ } else if (S_MAXELEM(sym) != 0)
+ call syserrs (SYS_QPPOPEN, param)
+
+ # Place a lock on the file and then remove it, to cause an error
+ # if the lfile is already active in the file cache.
+
+ call fm_lockout (QP_FM(qp), S_LFILE(sym))
+ call fm_unlock (QP_FM(qp), S_LFILE(sym))
+
+ # Open the assigned lfile and return the file descriptor.
+ return (fm_fopen (QP_FM(qp), S_LFILE(sym), mode, type))
+end
diff --git a/sys/qpoe/qpppar.x b/sys/qpoe/qpppar.x
new file mode 100644
index 00000000..17d9496d
--- /dev/null
+++ b/sys/qpoe/qpppar.x
@@ -0,0 +1,136 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <ctype.h>
+include "qpoe.h"
+
+# QP_PUTPARAM -- Lookup the named parameter in the symbol table and return
+# a pointer to a buffer into which the scalar parameter value is to be
+# placed. A subsequent call to QPOE_FLUSHPAR updates the parameter value
+# in the datafile. A NULL pointer is returned if the parameter exists but
+# does not currently have a value. The parameter datatype code is returned
+# as the function value.
+
+int procedure qp_putparam (qp, param, o_pp)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+pointer o_pp #O pointer to parameter value
+
+bool first_time
+pointer sp, key, fm, op
+int loc_pval, loc_Mem, ip, ch, sz_elem
+data first_time /true/
+
+int elem
+pointer pp, sym
+bool put_value
+double pval[LEN_PVAL+1]
+common /qppval/ pval, sym, elem, pp, put_value
+
+pointer qp_gpsym()
+int ctoi(), qp_sizeof()
+errchk qp_bind, syserrs
+
+begin
+ call smark (sp)
+ call salloc (key, SZ_FNAME, TY_CHAR)
+
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ fm = QP_FM(qp)
+
+ # Compute pointer (Memc index) to the static pval buffer.
+ # Make sure that the computed pointer is double aligned.
+
+ if (first_time) {
+ call zlocva (pval, loc_pval)
+ call zlocva (Memc, loc_Mem)
+ pp = (loc_pval+SZ_DOUBLE - loc_Mem) / SZ_DOUBLE * SZ_DOUBLE + 1
+ put_value = false
+ first_time = false
+ } else if (put_value)
+ call qp_flushpar (qp)
+
+ # Extract the primary parameter name, minus any whitespace and
+ # subscript (e.g., "param[elem]").
+
+ op = key
+ do ip = 1, SZ_FNAME {
+ ch = param[ip]
+ if (IS_WHITE(ch))
+ next
+ else if (ch == '[')
+ break
+ Memc[op] = ch
+ op = op + 1
+ }
+ Memc[op] = EOS
+
+ # Determine the array element (default [1]).
+ elem = 1
+ if (param[ip] == '[') {
+ ip = ip + 1
+ if (ctoi (param, ip, elem) <= 0)
+ elem = 1
+ }
+
+ # Lookup the symbol in the symbol table.
+ sym = qp_gpsym (qp, Memc[key])
+ if (sym == NULL)
+ call syserrs (SYS_QPUKNPAR, param)
+
+ # Check to make sure storage for the parameter value exists, and
+ # set the parameter buffer pointer for the indicated datatype.
+
+ sz_elem = qp_sizeof (qp, S_DTYPE(sym), sym, INSTANCEOF)
+ if (sz_elem > LEN_PVAL * SZ_DOUBLE)
+ call syserrs (SYS_QPPVALOVF, QP_DFNAME(qp))
+
+ if (elem < 1 || elem > S_MAXELEM(sym))
+ o_pp = NULL
+ else if (S_DTYPE(sym) == TY_USER)
+ o_pp = (pp - 1) / SZ_STRUCT + 1
+ else
+ o_pp = (pp - 1) / sz_elem + 1
+
+ # Set a flag to flush the value after the user has entered it.
+ put_value = true
+
+ call sfree (sp)
+ return (S_DTYPE(sym))
+end
+
+
+# QP_FLUSHPAR -- Update the saved parameter value in the indicated lfile.
+# Repeated calls are harmless.
+
+procedure qp_flushpar (qp)
+
+pointer qp #I QPOE descriptor
+
+int sz_elem, fd
+int qp_sizeof(), fm_getfd()
+errchk fm_getfd, seek, write
+
+int elem
+pointer pp, sym
+bool put_value
+double pval[LEN_PVAL+1]
+common /qppval/ pval, sym, elem, pp, put_value
+
+begin
+ if (put_value) {
+ sz_elem = qp_sizeof (qp, S_DTYPE(sym), S_DSYM(sym), INSTANCEOF)
+ fd = fm_getfd (QP_FM(qp), S_LFILE(sym), READ_WRITE, 0)
+
+ call seek (fd, S_OFFSET(sym) + (elem - 1) * sz_elem)
+ call write (fd, Memc[pp], sz_elem)
+ S_NELEM(sym) = max (S_NELEM(sym), elem)
+ QP_MODIFIED(qp) = YES
+
+ call fm_retfd (QP_FM(qp), S_LFILE(sym))
+ put_value = false
+ }
+end
diff --git a/sys/qpoe/qppstr.x b/sys/qpoe/qppstr.x
new file mode 100644
index 00000000..9ab2b402
--- /dev/null
+++ b/sys/qpoe/qppstr.x
@@ -0,0 +1,47 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_PSTR -- Update the string value of the named parameter.
+
+procedure qp_pstr (qp, param, strval)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char strval[ARB] #I new string value
+
+pointer fm, sym
+int fd, nchars
+
+pointer qp_gpsym()
+int fm_getfd(), strlen()
+errchk qp_bind, qp_gpsym, syserrs, fm_getfd, seek
+
+begin
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ fm = QP_FM(qp)
+
+ # Lookup the symbol in the symbol table.
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL)
+ call syserrs (SYS_QPUKNPAR, param)
+ else if (S_DTYPE(sym) != TY_CHAR)
+ call syserrs (SYS_QPBADCONV, param)
+
+ # Update the value of the parameter in the datafile.
+ fd = fm_getfd (fm, S_LFILE(sym), READ_WRITE, 0)
+ call seek (fd, S_OFFSET(sym))
+
+ nchars = strlen (strval)
+ if (S_MAXELEM(sym) > 0)
+ nchars = min (S_MAXELEM(sym), nchars)
+
+ call write (fd, strval, nchars)
+ S_NELEM(sym) = nchars
+ QP_MODIFIED(qp) = YES
+
+ call fm_retfd (fm, S_LFILE(sym))
+end
diff --git a/sys/qpoe/qpput.gx b/sys/qpoe/qpput.gx
new file mode 100644
index 00000000..871b2943
--- /dev/null
+++ b/sys/qpoe/qpput.gx
@@ -0,0 +1,74 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "../qpoe.h"
+
+# QP_PUT -- Set the value of the named header parameter. Automatic type
+# conversion is performed where possible. While only scalar values can be
+# set by this function, the scalar may be an element of a one-dimensional
+# array, e.g., "param[N]".
+
+procedure qp_put$t (qp, param, value)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+PIXEL value #I scalar parameter value
+
+pointer pp
+bool indef
+int dtype
+int qp_putparam()
+errchk qp_putparam, syserrs
+
+begin
+ # Lookup the parameter and get a pointer to the value buffer.
+ dtype = qp_putparam (qp, param, pp)
+ if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ if (QP_DEBUG(qp) > 1) {
+ call eprintf ("qp_put: `%s', TYP=(%d->%d), new value %g\n")
+ call pargstr (param)
+ call pargi (TY_PIXEL)
+ call pargi (dtype)
+ call parg$t (value)
+ }
+
+ indef = IS_INDEF(value)
+
+ # Set the parameter value.
+ switch (dtype) {
+ case TY_CHAR:
+ Memc[pp] = value
+ case TY_SHORT:
+ if (indef)
+ Mems[pp] = INDEFS
+ else
+ Mems[pp] = value
+ case TY_INT:
+ if (indef)
+ Memi[pp] = INDEFI
+ else
+ Memi[pp] = value
+ case TY_LONG:
+ if (indef)
+ Meml[pp] = INDEFL
+ else
+ Meml[pp] = value
+ case TY_REAL:
+ if (indef)
+ Memr[pp] = INDEFR
+ else
+ Memr[pp] = value
+ case TY_DOUBLE:
+ if (indef)
+ Memd[pp] = INDEFD
+ else
+ Memd[pp] = value
+ default:
+ call syserrs (SYS_QPBADCONV, param)
+ }
+
+ # Update the parameter in the datafile.
+ call qp_flushpar (qp)
+end
diff --git a/sys/qpoe/qpputb.x b/sys/qpoe/qpputb.x
new file mode 100644
index 00000000..da7f7ba8
--- /dev/null
+++ b/sys/qpoe/qpputb.x
@@ -0,0 +1,31 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_PUTB -- Set the boolean value of the named header parameter. Type
+# conversion is not permitted between boolean and the other data types.
+
+procedure qp_putb (qp, param, value)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+bool value #I scalar parameter value
+
+pointer pp
+int qp_putparam()
+errchk qp_putparam, syserrs
+
+begin
+ # Lookup the parameter and get a pointer to the value buffer.
+ if (qp_putparam (qp, param, pp) != TY_BOOL)
+ call syserrs (SYS_QPBADCONV, param)
+ else if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ # Pass the new value.
+ Memb[pp] = value
+
+ # Update the parameter in the datafile.
+ call qp_flushpar (qp)
+end
diff --git a/sys/qpoe/qpputx.x b/sys/qpoe/qpputx.x
new file mode 100644
index 00000000..0ed73b35
--- /dev/null
+++ b/sys/qpoe/qpputx.x
@@ -0,0 +1,31 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_PUTX -- Set the complex value of the named header parameter. Type
+# conversion is not permitted between complex and the other data types.
+
+procedure qp_putx (qp, param, value)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+complex value #I scalar parameter value
+
+pointer pp
+int qp_putparam()
+errchk qp_putparam, syserrs
+
+begin
+ # Lookup the parameter and get a pointer to the value buffer.
+ if (qp_putparam (qp, param, pp) != TY_COMPLEX)
+ call syserrs (SYS_QPBADCONV, param)
+ else if (pp == NULL)
+ call syserrs (SYS_QPNOVAL, param)
+
+ # Pass the new value.
+ Memx[pp] = value
+
+ # Update the parameter in the datafile.
+ call qp_flushpar (qp)
+end
diff --git a/sys/qpoe/qpqueryf.x b/sys/qpoe/qpqueryf.x
new file mode 100644
index 00000000..c8de988a
--- /dev/null
+++ b/sys/qpoe/qpqueryf.x
@@ -0,0 +1,91 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include <qpset.h>
+include "qpoe.h"
+
+# QP_QUERYF -- Get information describing the named parameter. The current
+# vector length of the parameter is returned as the function value, or ERR
+# if the parameter does not exist.
+
+int procedure qp_queryf (qp, param, datatype, maxelem, comment, flags)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char datatype[SZ_DATATYPE] #O parameter data type
+int maxelem #O allocated length of parameter
+char comment[SZ_COMMENT] #O comment describing parameter
+int flags #O parameter flag word
+
+int junk
+pointer sym, dsym, ip, st
+
+int qp_gstr()
+pointer qp_gpsym(), stname(), strefstab(), strefsbuf()
+errchk qp_gpsym, stname, syserrs
+
+begin
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ st = QP_ST(qp)
+
+ # Locate the symbol.
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL)
+ return (ERR)
+
+ flags = S_FLAGS(sym)
+ maxelem = S_MAXELEM(sym)
+
+ # Output the symbolic datatype.
+ datatype[2] = EOS
+ switch (S_DTYPE(sym)) {
+ case TY_BOOL:
+ datatype[1] = 'b'
+ case TY_CHAR:
+ datatype[1] = 'c'
+ case TY_SHORT:
+ datatype[1] = 's'
+ case TY_INT:
+ datatype[1] = 'i'
+ case TY_LONG:
+ datatype[1] = 'l'
+ case TY_REAL:
+ datatype[1] = 'r'
+ case TY_DOUBLE:
+ datatype[1] = 'd'
+ case TY_COMPLEX:
+ datatype[1] = 'x'
+
+ case TY_MACRO:
+ call strcpy ("macro", datatype, SZ_DATATYPE)
+ case TY_OPAQUE:
+ call strcpy ("opaque", datatype, SZ_DATATYPE)
+
+ case TY_USER:
+ # User defined type: if S_DSYM is NULL, this is the domain entry
+ # itself, else the domain name is the datatype of the parameter.
+ # If this is a primary domain entry, the field list defining the
+ # structure is stored as the string value of the parameter.
+
+ if (S_DSYM(sym) == NULL)
+ junk = qp_gstr (qp, param, datatype, SZ_DATATYPE)
+ else {
+ dsym = strefstab (st, S_DSYM(sym))
+ call strcpy (Memc[stname(st,dsym)], datatype, SZ_DATATYPE)
+ }
+
+ default:
+ call strcpy ("<unknown>", datatype, SZ_DATATYPE)
+ }
+
+ # Output the comment field.
+ if (S_COMMENT(sym) != NULL) {
+ ip = strefsbuf (st, S_COMMENT(sym))
+ call strcpy (Memc[ip], comment, SZ_COMMENT)
+ } else
+ comment[1] = EOS
+
+ return (S_NELEM(sym))
+end
diff --git a/sys/qpoe/qpread.x b/sys/qpoe/qpread.x
new file mode 100644
index 00000000..c25fe506
--- /dev/null
+++ b/sys/qpoe/qpread.x
@@ -0,0 +1,80 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_READ -- Read a range of elements from a parameter. Works for any
+# parameter, including scalar parameters and both static and variable
+# length array valued parameters. Automatic datatype conversion is
+# performed for the primitive types.
+
+int procedure qp_read (qp, param, buf, maxelem, first, datatype)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char buf[ARB] #O user data buffer to receive data
+int maxelem #I max number of data elements to read
+int first #I first data element to read
+char datatype[ARB] #I datatype to be returned
+
+pointer sp, fm, sym, tbuf, isym, osym
+int fd, sz_itype, sz_otype, nelem, itype, otype
+
+pointer qp_gpsym()
+int fm_getfd(), qp_sizeof(), read(), qp_dtype()
+errchk qp_bind, qp_gpsym, fm_getfd, seek, read, syserrs
+
+begin
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ fm = QP_FM(qp)
+ otype = qp_dtype (qp, datatype, osym)
+
+ # Lookup the symbol in the symbol table.
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL)
+ call syserrs (SYS_QPUKNPAR, param)
+ else {
+ itype = S_DTYPE(sym)
+ isym = sym
+ }
+
+ # Determine the number of inbounds elements.
+ nelem = max(0, min(maxelem, S_NELEM(sym) - first + 1))
+ if (first <= 0)
+ call syserrs (SYS_QPINDXOOR, param)
+
+ # Verify that any type conversion is legal.
+ if (otype != itype)
+ if (min(otype,itype) < TY_CHAR || max(otype,itype) > TY_DOUBLE)
+ call syserrs (SYS_QPBADCONV, param)
+
+ # Open the lfile and read the data segment.
+ fd = fm_getfd (fm, S_LFILE(sym), READ_ONLY, 0)
+
+ if (nelem > 0) {
+ sz_itype = qp_sizeof (qp, itype, isym, IMMEDIATE)
+ sz_otype = qp_sizeof (qp, otype, osym, INSTANCEOF)
+
+ # Read and output the data.
+ call seek (fd, S_OFFSET(sym) + (first - 1) * sz_itype)
+ if (sz_itype <= sz_otype) {
+ # Read the data directly into the user's buffer.
+ nelem = read (fd, buf, nelem * sz_itype) / sz_itype
+ if (nelem > 0 && otype != itype)
+ call acht (buf, buf, nelem, itype, otype)
+ } else {
+ # Read the data into a temporary buffer.
+ call smark (sp)
+ call salloc (tbuf, nelem * sz_itype, TY_CHAR)
+ nelem = read (fd, Memc[tbuf], nelem * sz_itype) / sz_itype
+ if (nelem > 0)
+ call acht (Memc[tbuf], buf, nelem, itype, otype)
+ call sfree (sp)
+ }
+ }
+
+ call fm_retfd (fm, S_LFILE(sym))
+ return (nelem)
+end
diff --git a/sys/qpoe/qprebuild.x b/sys/qpoe/qprebuild.x
new file mode 100644
index 00000000..369eba1e
--- /dev/null
+++ b/sys/qpoe/qprebuild.x
@@ -0,0 +1,21 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_REBUILD -- Rebuild a poefile to reclaim unused space, and render storage
+# elements logically contiguous to improve file access efficiency.
+
+procedure qp_rebuild (poefile)
+
+char poefile[ARB] #I poefile name
+pointer sp, fname
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_PATHNAME, TY_CHAR)
+
+ call qp_mkfname (poefile, QPOE_EXTN, Memc[fname], SZ_PATHNAME)
+ call fm_rebuild (Memc[fname])
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qprename.x b/sys/qpoe/qprename.x
new file mode 100644
index 00000000..2287b0fd
--- /dev/null
+++ b/sys/qpoe/qprename.x
@@ -0,0 +1,25 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_RENAME -- Rename a poefile.
+
+procedure qp_rename (o_poefile, n_poefile)
+
+char o_poefile[ARB] #I old poefile name
+char n_poefile[ARB] #I new poefile name
+
+pointer sp, o_fname, n_fname
+string extn QPOE_EXTN
+
+begin
+ call smark (sp)
+ call salloc (o_fname, SZ_PATHNAME, TY_CHAR)
+ call salloc (n_fname, SZ_PATHNAME, TY_CHAR)
+
+ call qp_mkfname (o_poefile, extn, Memc[o_fname], SZ_PATHNAME)
+ call qp_mkfname (n_poefile, extn, Memc[n_fname], SZ_PATHNAME)
+ call fm_rename (Memc[o_fname], Memc[n_fname])
+
+ call sfree (sp)
+end
diff --git a/sys/qpoe/qprenamef.x b/sys/qpoe/qprenamef.x
new file mode 100644
index 00000000..39abb915
--- /dev/null
+++ b/sys/qpoe/qprenamef.x
@@ -0,0 +1,48 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_RENAMEF -- Rename a header parameter. It is an error if the named header
+# parameter does not exist, or if the new name would redefine another symbol.
+
+procedure qp_renamef (qp, param, newname)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char newname[ARB] #I new parameter name
+
+pointer sym, nsym, st
+pointer qp_gpsym(), stenter()
+errchk qp_gpsym, syserrs, stenter
+
+begin
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ st = QP_ST(qp)
+
+ # Access the named parameter.
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL)
+ call syserrs (SYS_QPUKNPAR, param)
+
+ # Check for a parameter redefinition.
+ nsym = qp_gpsym (qp, newname)
+ if (nsym != NULL)
+ call syserrs (SYS_QPREDEF, newname)
+
+ # Rename the symbol. We cannot just edit the name, as the hash
+ # function would not be able to find it. We must create a new
+ # symstruct and replace the old one by it. The stenter can cause
+ # reallocation of the symbol table, so we need to recompute the
+ # symbol pointer.
+
+ nsym = stenter (st, newname, LEN_SYMBOL)
+ sym = qp_gpsym (qp, param)
+
+ call amovi (Memi[sym], Memi[nsym], LEN_SYMBOL)
+ S_FLAGS(sym) = or (S_FLAGS(sym), SF_DELETED)
+
+ QP_MODIFIED(qp) = YES
+end
diff --git a/sys/qpoe/qprlmerge.gx b/sys/qpoe/qprlmerge.gx
new file mode 100644
index 00000000..410bb952
--- /dev/null
+++ b/sys/qpoe/qprlmerge.gx
@@ -0,0 +1,134 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include "../qpex.h"
+
+# QP_RLMERGE -- Merge (AND) two range lists. Only ranges which are
+# common to both range lists are output. The number of ranges in the
+# output range list is returned as the function value.
+
+int procedure qp_rlmerge$t (os,oe,olen, xs,xe,nx, ys,ye,ny)
+
+pointer os, oe #U output range list
+int olen #U allocated length of OS, OE arrays
+
+PIXEL xs[ARB], xe[ARB] #I range list to be merged with
+int nx #I number of ranges in X list
+PIXEL ys[ARB], ye[ARB] #I range list to be merged with X
+int ny #I number of ranges in Y list
+
+PIXEL o1, o2
+int nx_out, xi, yi, i
+PIXEL qp_minval$t(), qp_maxval$t()
+bool qp_lessthan$t()
+errchk realloc
+
+begin
+ nx_out = 0
+ if (nx <= 0 || ny <= 0)
+ return (0)
+
+ xi = 1
+ yi = 1
+
+ do i = 1, ARB {
+ # Find a pair of ranges which intersect.
+ repeat {
+ if (qp_lessthan$t (xe[xi], ys[yi])) {
+ if (xi >= nx)
+ return (nx_out)
+ else
+ xi = xi + 1
+ } else if (qp_lessthan$t (ye[yi], xs[xi])) {
+ if (yi >= ny)
+ return (nx_out)
+ else
+ yi = yi + 1
+ } else
+ break
+ }
+
+ # Compute the intersection.
+ o1 = qp_maxval$t (xs[xi], ys[yi])
+ o2 = qp_minval$t (xe[xi], ye[yi])
+
+ # Output the range.
+ if (nx_out + 1 > olen) {
+ olen = max (DEF_XLEN, olen * 2)
+ call realloc (os, olen, TY_PIXEL)
+ call realloc (oe, olen, TY_PIXEL)
+ }
+
+ Mem$t[os+nx_out] = o1
+ Mem$t[oe+nx_out] = o2
+ nx_out = nx_out + 1
+
+ # Advance to the next range.
+ if (xi < nx && qp_lessthan$t (xe[xi], ye[yi]))
+ xi = xi + 1
+ else if (yi < ny)
+ yi = yi + 1
+ else
+ break
+ }
+
+ return (nx_out)
+end
+
+
+# QP_MINVAL -- Return the lesser of two values, where either value can
+# be an open range.
+
+PIXEL procedure qp_minval$t (x, y)
+
+PIXEL x #I first value
+PIXEL y #I second value
+
+bool qp_lessthan$t()
+
+begin
+ if (qp_lessthan$t (x, y))
+ return (x)
+ else
+ return (y)
+end
+
+
+# QP_MAXVAL -- Return the greater of two values, where either value can
+# be an open range.
+
+PIXEL procedure qp_maxval$t (x, y)
+
+PIXEL x #I first value
+PIXEL y #I second value
+
+bool qp_lessthan$t()
+
+begin
+ if (qp_lessthan$t (x, y))
+ return (y)
+ else
+ return (x)
+end
+
+
+# QP_LESSTHAN -- Test if X is less than Y, where X and Y can be open
+# range values.
+
+bool procedure qp_lessthan$t (x, y)
+
+PIXEL x #I first value
+PIXEL y #I second value
+
+begin
+ if (IS_LEFT$T(x))
+ return (!IS_LEFT$T(y))
+ else if (IS_RIGHT$T(x))
+ return (false)
+ else if (IS_LEFT$T(y))
+ return (false)
+ else if (IS_RIGHT$T(y))
+ return (true)
+ else
+ return (x < y)
+end
diff --git a/sys/qpoe/qpsavewcs.x b/sys/qpoe/qpsavewcs.x
new file mode 100644
index 00000000..35293d62
--- /dev/null
+++ b/sys/qpoe/qpsavewcs.x
@@ -0,0 +1,35 @@
+include "qpoe.h"
+
+# QP_SAVEWCS -- Save the given WCS in the QPOE header, as a variable length
+# binary array (machine independent) in the default WCS parameter QPWCS.
+
+procedure qp_savewcs (qp, mw)
+
+pointer qp #I QPOE descriptor
+pointer mw #I MWCS descriptor
+
+pointer bp
+int buflen, nchars
+int mw_save(), qp_accessf()
+errchk mw_save, qp_accessf, qp_addf, qp_write
+string s_opaque "opaque"
+string s_qpwcs QPWCS
+
+begin
+ bp = NULL
+ buflen = 0
+
+ # Encode the WCS as a machine independent binary array.
+ nchars = mw_save (mw, bp, buflen)
+
+ # Save it in the QPOE header.
+ if (nchars > 0) {
+ if (qp_accessf (qp, s_qpwcs) == NO)
+ call qp_addf (qp, s_qpwcs,
+ s_opaque, 0, "World coordinate system", SF_INHERIT)
+ call qp_write (qp, s_qpwcs, Memc[bp], nchars, 1, s_opaque)
+ }
+
+ if (bp != NULL)
+ call mfree (bp, TY_CHAR)
+end
diff --git a/sys/qpoe/qpseti.x b/sys/qpoe/qpseti.x
new file mode 100644
index 00000000..3a18c35e
--- /dev/null
+++ b/sys/qpoe/qpseti.x
@@ -0,0 +1,62 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpset.h>
+include "qpoe.h"
+
+# QP_SETI -- Set an QPOE integer parameter.
+
+procedure qp_seti (qp, param, value)
+
+pointer qp #I QPOE descriptor
+int param #I parameter to be set
+int value #I new value for parameter
+
+begin
+ switch (param) {
+ case QPOE_BLOCKFACTOR:
+ QP_XBLOCK(qp) = value
+ QP_YBLOCK(qp) = value
+ case QPOE_XBLOCKFACTOR:
+ QP_XBLOCK(qp) = value
+ case QPOE_YBLOCKFACTOR:
+ QP_YBLOCK(qp) = value
+ case QPOE_BUCKETLEN:
+ QP_BUCKETLEN(qp) = value
+ case QPOE_CACHESIZE:
+ QP_FMCACHESIZE(qp) = value
+ case QPOE_DATABUFLEN:
+ QP_EXDBLEN(qp) = value
+ case QPOE_DEBUGLEVEL:
+ QP_DEBUG(qp) = value
+ case QPOE_INDEXLEN:
+ QP_STINDEXLEN(qp) = value
+ case QPOE_LUTMINRANGES:
+ QP_EXLMINRANGES(qp) = value
+ case QPOE_LUTSCALE:
+ QP_EXLSCALE(qp) = value
+ case QPOE_MAXFRLUTLEN:
+ QP_EXMAXFRLLEN(qp) = value
+ case QPOE_MAXLFILES:
+ QP_FMMAXLFILES(qp) = value
+ case QPOE_MAXPTPAGES:
+ QP_FMMAXPTPAGES(qp) = value
+ case QPOE_MAXRRLUTLEN:
+ QP_EXMAXRRLLEN(qp) = value
+ case QPOE_MAXPUSHBACK:
+ QP_SZPBBUF(qp) = value
+ case QPOE_NODEFFILT:
+ QP_NODEFFILT(qp) = value
+ case QPOE_NODEFMASK:
+ QP_NODEFMASK(qp) = value
+ case QPOE_OPTBUFSIZE:
+ QP_OPTBUFSIZE(qp) = value
+ case QPOE_PAGESIZE:
+ QP_FMPAGESIZE(qp) = value
+ case QPOE_PROGBUFLEN:
+ QP_EXPBLEN(qp) = value
+ case QPOE_SBUFSIZE:
+ QP_STSBUFSIZE(qp) = value
+ case QPOE_STABLEN:
+ QP_STSTABLEN(qp) = value
+ }
+end
diff --git a/sys/qpoe/qpsetr.x b/sys/qpoe/qpsetr.x
new file mode 100644
index 00000000..9378912f
--- /dev/null
+++ b/sys/qpoe/qpsetr.x
@@ -0,0 +1,24 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpset.h>
+include "qpoe.h"
+
+# QP_SETR -- Set an QPOE real valued parameter.
+
+procedure qp_setr (qp, param, value)
+
+pointer qp #I QPOE descriptor
+int param #I parameter to be set
+real value #I new value for parameter
+
+begin
+ switch (param) {
+ case QPOE_BLOCKFACTOR:
+ QP_XBLOCK(qp) = value
+ QP_YBLOCK(qp) = value
+ case QPOE_XBLOCKFACTOR:
+ QP_XBLOCK(qp) = value
+ case QPOE_YBLOCKFACTOR:
+ QP_YBLOCK(qp) = value
+ }
+end
diff --git a/sys/qpoe/qpsizeof.x b/sys/qpoe/qpsizeof.x
new file mode 100644
index 00000000..bfa1698f
--- /dev/null
+++ b/sys/qpoe/qpsizeof.x
@@ -0,0 +1,46 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_SIZEOF -- Determine the size in chars of a QPOE datatype. This may
+# be one of the special datatypes (user defined record types), or a primitive
+# type. In the case of a special type, the REFTYPE flag specifies whether
+# the size of the value of the type variable itself (always SZ_CHAR) is to be
+# returned, or the size of an *instance* of the special type.
+
+int procedure qp_sizeof (qp, dtype, dsym, reftype)
+
+pointer qp #I QPOE descriptor
+int dtype #I datatype code
+pointer dsym #I domain descriptor, if type TY_USER
+int reftype #I IMMEDIATE (domain itself) or INSTANCEOF
+
+pointer sym
+int sizeof()
+pointer strefstab()
+
+begin
+ switch (dtype) {
+ case TY_MACRO, TY_OPAQUE:
+ return (SZ_CHAR)
+
+ case TY_USER:
+ # Size of a user defined structure (or the element size of the
+ # struct definition entry itself).
+
+ if (dsym == NULL) { # {...}
+ return (SZ_CHAR)
+ } else if (S_DSYM(dsym) == NULL) { # reference is to
+ if (reftype == IMMEDIATE) # primary domain entry
+ return (SZ_CHAR)
+ else
+ return (S_SZELEM(dsym))
+ } else { # instance of domain
+ sym = strefstab (QP_ST(qp), S_DSYM(dsym))
+ return (S_SZELEM(sym))
+ }
+
+ default:
+ return (sizeof (dtype))
+ }
+end
diff --git a/sys/qpoe/qpstati.x b/sys/qpoe/qpstati.x
new file mode 100644
index 00000000..df8d176c
--- /dev/null
+++ b/sys/qpoe/qpstati.x
@@ -0,0 +1,76 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpset.h>
+include "qpoe.h"
+
+# QP_STATI -- Get the value of an QPOE integer parameter.
+
+int procedure qp_stati (qp, param)
+
+pointer qp #I QPOE descriptor
+int param #I parameter to be queried
+
+bool fp_equalr()
+
+begin
+ switch (param) {
+ case QPOE_BLOCKFACTOR: # standard params
+ if (fp_equalr (QP_XBLOCK(qp), QP_YBLOCK(qp)))
+ return (QP_XBLOCK(qp))
+ else
+ return (ERR)
+ case QPOE_XBLOCKFACTOR:
+ return (QP_XBLOCK(qp))
+ case QPOE_YBLOCKFACTOR:
+ return (QP_YBLOCK(qp))
+ case QPOE_BUCKETLEN:
+ return (QP_BUCKETLEN(qp))
+ case QPOE_CACHESIZE:
+ return (QP_FMCACHESIZE(qp))
+ case QPOE_DATABUFLEN:
+ return (QP_EXDBLEN(qp))
+ case QPOE_DEBUGLEVEL:
+ return (QP_DEBUG(qp))
+ case QPOE_INDEXLEN:
+ return (QP_STINDEXLEN(qp))
+ case QPOE_MAXLFILES:
+ return (QP_FMMAXLFILES(qp))
+ case QPOE_MAXPTPAGES:
+ return (QP_FMMAXPTPAGES(qp))
+ case QPOE_MAXFRLUTLEN:
+ return (QP_EXMAXFRLLEN(qp))
+ case QPOE_MAXRRLUTLEN:
+ return (QP_EXMAXRRLLEN(qp))
+ case QPOE_LUTMINRANGES:
+ return (QP_EXLMINRANGES(qp))
+ case QPOE_LUTSCALE:
+ return (QP_EXLSCALE(qp))
+ case QPOE_MAXPUSHBACK:
+ return (QP_SZPBBUF(qp))
+ case QPOE_NODEFFILT:
+ return (QP_NODEFFILT(qp))
+ case QPOE_NODEFMASK:
+ return (QP_NODEFMASK(qp))
+ case QPOE_OPTBUFSIZE:
+ return (QP_OPTBUFSIZE(qp))
+ case QPOE_PAGESIZE:
+ return (QP_FMPAGESIZE(qp))
+ case QPOE_PROGBUFLEN:
+ return (QP_EXPBLEN(qp))
+ case QPOE_SBUFSIZE:
+ return (QP_STSBUFSIZE(qp))
+ case QPOE_STABLEN:
+ return (QP_STSTABLEN(qp))
+
+ case QPOE_FM: # read-only params
+ return (QP_FM(qp))
+ case QPOE_MODE:
+ return (QP_MODE(qp))
+ case QPOE_ST:
+ return (QP_ST(qp))
+ case QPOE_VERSION:
+ return (QP_VERSION(qp))
+ }
+
+ return (ERR)
+end
diff --git a/sys/qpoe/qpstatr.x b/sys/qpoe/qpstatr.x
new file mode 100644
index 00000000..0b3aec99
--- /dev/null
+++ b/sys/qpoe/qpstatr.x
@@ -0,0 +1,29 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <qpset.h>
+include "qpoe.h"
+
+# QP_STATR -- Get the value of an QPOE real parameter.
+
+real procedure qp_statr (qp, param)
+
+pointer qp #I QPOE descriptor
+int param #I parameter to be queried
+
+bool fp_equalr()
+
+begin
+ switch (param) {
+ case QPOE_BLOCKFACTOR: # standard params
+ if (fp_equalr (QP_XBLOCK(qp), QP_YBLOCK(qp)))
+ return (QP_XBLOCK(qp))
+ else
+ return (ERR)
+ case QPOE_XBLOCKFACTOR:
+ return (QP_XBLOCK(qp))
+ case QPOE_YBLOCKFACTOR:
+ return (QP_YBLOCK(qp))
+ }
+
+ return (ERR)
+end
diff --git a/sys/qpoe/qpsync.x b/sys/qpoe/qpsync.x
new file mode 100644
index 00000000..692136d6
--- /dev/null
+++ b/sys/qpoe/qpsync.x
@@ -0,0 +1,51 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "qpoe.h"
+
+# QP_SYNC -- Update the poefile on disk.
+
+procedure qp_sync (qp)
+
+pointer qp #I QPOE descriptor
+
+int n, fd
+pointer sp, qph
+int fm_fopen()
+errchk qp_flushpar, fm_fopen, write, stsave
+
+begin
+ # Flush the put-parameter buffer.
+ call qp_flushpar (qp)
+
+ # Update the QPOE descriptor and symbol table in the datafile.
+ if (QP_MODIFIED(qp) != NO) {
+ call smark (sp)
+ call salloc (qph, LEN_QPH, TY_STRUCT)
+ call aclri (Memi[qph], LEN_QPH)
+
+ QPH_MAGIC(qph) = QP_MAGIC(qp)
+ QPH_VERSION(qph) = QPOE_VERSION
+ QPH_STOFFSET(qph) = LEN_QPH * SZ_STRUCT + 1
+
+ # The encoded QPOE header and symbol table are stored in a
+ # binary lfile in the datafile.
+
+ fd = fm_fopen (QP_FM(qp), LF_QPOE, NEW_FILE, BINARY_FILE)
+
+ # Update the QPOE file header.
+ n = LEN_QPH * SZ_STRUCT
+ call miipak32 (Memi[qph], Memi[qph], LEN_QPH, TY_STRUCT)
+ call write (fd, Memi[qph], n)
+
+ # Update the symbol table.
+ call stsqueeze (QP_ST(qp))
+ call stsave (QP_ST(qp), fd)
+
+ QP_MODIFIED(qp) = NO
+ call close (fd)
+ call sfree (sp)
+ }
+
+ # Update the datafile itself.
+ call fm_fcsync (QP_FM(qp))
+end
diff --git a/sys/qpoe/qpwrite.x b/sys/qpoe/qpwrite.x
new file mode 100644
index 00000000..dc5d4dab
--- /dev/null
+++ b/sys/qpoe/qpwrite.x
@@ -0,0 +1,79 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <syserr.h>
+include "qpoe.h"
+
+# QP_WRITE -- Write to a range of elements in a parameter. Works for any
+# parameter, including scalar parameters and both static and variable
+# length array valued parameters. Automatic datatype conversion is
+# performed for the primitive types.
+
+procedure qp_write (qp, param, buf, nelem, first, datatype)
+
+pointer qp #I QPOE descriptor
+char param[ARB] #I parameter name
+char buf[ARB] #I user data buffer containing data
+int nelem #I number of data elements to write
+int first #I first data element to write to
+char datatype[ARB] #I datatype of input data
+
+pointer sp, fm, sym, tbuf, isym, osym
+int fd, sz_itype, sz_otype, last, otype, itype
+errchk qp_bind, qp_gpsym, fm_getfd, seek, syserrs
+int fm_getfd(), qp_sizeof(), qp_dtype()
+pointer qp_gpsym()
+
+begin
+ if (nelem <= 0)
+ return
+
+ if (QP_ACTIVE(qp) == NO)
+ call qp_bind (qp)
+
+ itype = qp_dtype (qp, datatype, isym)
+ fm = QP_FM(qp)
+
+ # Lookup the symbol in the symbol table.
+ sym = qp_gpsym (qp, param)
+ if (sym == NULL)
+ call syserrs (SYS_QPUKNPAR, param)
+ else {
+ otype = S_DTYPE(sym)
+ osym = sym
+ }
+
+ sz_itype = qp_sizeof (qp, itype, isym, INSTANCEOF)
+ sz_otype = qp_sizeof (qp, otype, osym, IMMEDIATE)
+ last = first + nelem - 1
+
+ # Check that the write is inbounds.
+ if (first <= 0 || (S_MAXELEM(sym) > 0 && last > S_MAXELEM(sym)))
+ call syserrs (SYS_QPINDXOOR, param)
+
+ # Verify that any type conversion is legal.
+ if (otype != itype)
+ if (min(otype,itype) < TY_CHAR || max(otype,itype) > TY_DOUBLE)
+ call syserrs (SYS_QPBADCONV, param)
+
+ # Open the lfile and update the data segment.
+ fd = fm_getfd (fm, S_LFILE(sym), READ_WRITE, 0)
+ call seek (fd, S_OFFSET(sym) + (first - 1) * sz_otype)
+
+ # Output the data.
+ if (otype != itype) {
+ call smark (sp)
+ call salloc (tbuf, nelem * sz_otype, TY_CHAR)
+ call acht (buf, Memc[tbuf], nelem, itype, otype)
+ call write (fd, Memc[tbuf], nelem * sz_otype)
+ call sfree (sp)
+ } else
+ call write (fd, buf, nelem * sz_otype)
+
+ # Update the array size if it got bigger.
+ if (last > S_NELEM(sym)) {
+ S_NELEM(sym) = last
+ QP_MODIFIED(qp) = YES
+ }
+
+ call fm_retfd (fm, S_LFILE(sym))
+end
diff --git a/sys/qpoe/zzdebug.x b/sys/qpoe/zzdebug.x
new file mode 100644
index 00000000..5ca2de42
--- /dev/null
+++ b/sys/qpoe/zzdebug.x
@@ -0,0 +1,1696 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <ctype.h>
+include <imhdr.h>
+include <qpexset.h>
+include <qpioset.h>
+include <qpset.h>
+include <gset.h>
+include "qpoe.h"
+include "qpex.h"
+include "qpio.h"
+
+# ZZDEBUG -- Debug routines for the QPOE package.
+
+task parsei = t_parsei, # parse integer range list
+ parser = t_parser, # parse floating range list
+ qpparse = t_qpparse, # test qp_parse
+ tokens = t_tokens, # test get token, macro replacement
+ comp = t_comp, # test QPEX compile
+ expand = t_expand, # perform macro expansion on text
+ recio = t_recio, # test general record i/o
+ newcopy = t_newcopy, # test inheritance
+ syms = t_syms, # dump symbol table
+ hlist = t_hlist, # list file header
+ dumpevl = t_dumpevl, # dump event list descriptor
+ mkpoe = t_mkpoe, # convert CFA poefile to QPOE poefile
+ testpoe = t_testpoe, # make a test QPOE file
+ countpoe = t_countpoe, # count photons in regions
+ tfilter = t_tfilter, # verify time filtering
+ plotpoe = t_plotpoe, # read and plot photons
+ sum = t_sum, # sum counts in an image section
+ setwcs = t_setwcs, # store a wcs in a qpoe file
+ setmask = t_setmask, # set the default mask
+ mergei = t_mergei, # test merging of range lists
+ clear = t_clear # clear the screen
+
+define SZ_EXPR 256
+define SZ_OBUF 128000
+define SZ_TBUF 8
+define MAX_EVENTS 8192
+define SZ_RLBUF 512
+
+
+# PARSEI -- Test integer range list decoding and expression optimization.
+
+procedure t_parsei()
+
+pointer xs, xe
+char lbuf[SZ_LINE], left[SZ_TBUF], right[SZ_TBUF]
+int nranges, xlen, i1, i2, i
+
+int getline(), qpex_parsei()
+bool streq()
+
+begin
+ xlen = 0
+
+ repeat {
+ # Get next expression.
+ call printf ("parse> ")
+ call flush (STDOUT)
+ if (getline (STDIN, lbuf) == EOF)
+ break
+ else if (streq (lbuf, "bye\n"))
+ break
+
+ # Parse the expression.
+ nranges = qpex_parsei (lbuf, xs, xe, xlen)
+
+ # List the ranges.
+ do i = 1, nranges {
+ i1 = Memi[xs+i-1]
+ i2 = Memi[xe+i-1]
+
+ if (IS_LEFTI(i1))
+ call strcpy ("LEFT", left, SZ_TBUF)
+ else {
+ call sprintf (left, SZ_TBUF, "%d")
+ call pargi (i1)
+ }
+
+ if (IS_RIGHTI(i2))
+ call strcpy ("RIGHT", right, SZ_TBUF)
+ else {
+ call sprintf (right, SZ_TBUF, "%d")
+ call pargi (i2)
+ }
+
+ call printf ("%2d: %8s %8s\n")
+ call pargi (i)
+ call pargstr (left)
+ call pargstr (right)
+ }
+
+ call flush (STDOUT)
+ }
+
+ call mfree (xs, TY_INT)
+ call mfree (xe, TY_INT)
+end
+
+
+# PARSER -- Test real range list decoding and expression optimization.
+
+procedure t_parser()
+
+pointer xs, xe
+char lbuf[SZ_LINE]
+int nranges, xlen, i
+
+int getline(), qpex_parser()
+bool streq()
+
+begin
+ xlen = 0
+
+ repeat {
+ # Get next expression.
+ call printf ("parse> ")
+ call flush (STDOUT)
+ if (getline (STDIN, lbuf) == EOF)
+ break
+ else if (streq (lbuf, "bye\n"))
+ break
+
+ # Parse the expression.
+ nranges = qpex_parser (lbuf, xs, xe, xlen)
+ do i = 1, nranges {
+ call printf ("%2d: %7g %7g\n")
+ call pargi (i)
+ call pargr (Memr[xs+i-1])
+ call pargr (Memr[xe+i-1])
+ }
+ call flush (STDOUT)
+ }
+
+ call mfree (xs, TY_REAL)
+ call mfree (xe, TY_REAL)
+end
+
+
+# QPPARSE -- Test qp_parse.
+
+procedure t_qpparse()
+
+char expr[SZ_LINE]
+char root[SZ_FNAME]
+char filter[SZ_FNAME]
+
+begin
+ call clgstr ("expr", expr, SZ_LINE)
+ call qp_parse (expr, root, SZ_FNAME, filter, SZ_FNAME)
+
+ call printf ("root=`%s', filter=`%s'\n")
+ call pargstr (root)
+ call pargstr (filter)
+end
+
+
+# TOKENS -- Translate an input character stream into a stream of QPOE tokens.
+# Macro replacement is performed on the input text.
+
+procedure t_tokens()
+
+char input[SZ_FNAME], refpoe[SZ_FNAME]
+char tokbuf[SZ_FNAME], num[SZ_FNAME]
+
+pointer qp, in
+int token, junk
+int qp_gettok(), gltoc()
+pointer qp_open, qp_opentext()
+
+begin
+ input[1] = '@'
+ call clgstr ("input", input[2], SZ_FNAME-1)
+ call clgstr ("refpoe", refpoe, SZ_FNAME)
+
+ if (refpoe[1] != EOS)
+ qp = qp_open (refpoe, READ_ONLY, NULL)
+ else
+ qp = NULL
+ in = qp_opentext (qp, input)
+
+ repeat {
+ token = qp_gettok (in, tokbuf, SZ_FNAME)
+ if (token != EOF) {
+ call printf ("%10s: %s\n")
+ switch (token) {
+ case TOK_IDENTIFIER:
+ call pargstr ("IDENT")
+ case TOK_NUMBER:
+ call pargstr ("NUMBER")
+ case TOK_STRING:
+ call pargstr ("STRING")
+ case TOK_COMMAND:
+ call pargstr ("COMMAND")
+ case TOK_PLUSEQUALS:
+ call pargstr ("PLUSEQ")
+ case TOK_COLONEQUALS:
+ call pargstr ("COLONEQ")
+ default:
+ junk = gltoc (token, num, SZ_FNAME, 8)
+ call pargstr (num)
+ }
+ if (IS_PRINT(tokbuf[1]))
+ call pargstr (tokbuf)
+ else
+ call pargstr ("")
+ }
+ } until (token == EOF)
+
+ call printf ("EOF\n")
+ call flush (STDOUT)
+
+ call qp_closetext (in)
+ if (qp != NULL)
+ call qp_close (qp)
+end
+
+
+# COMP -- Compile an expression with QPEX and print out the contents of
+# the resultant descriptor, including the assembler translation of the
+# expression.
+
+procedure t_comp()
+
+int out
+pointer qp, ex
+char text[SZ_LINE]
+char output[SZ_FNAME]
+
+int open()
+bool streq()
+pointer qp_open(), qpex_open()
+
+begin
+ call clgstr ("poefile", text, SZ_LINE)
+ qp = qp_open (text, READ_ONLY, 0)
+
+ call clgstr ("output", output, SZ_FNAME)
+ if (output[1] != EOS)
+ out = open (output, APPEND, TEXT_FILE)
+ else
+ out = NULL
+
+ repeat {
+ call clgstr ("expr", text, SZ_LINE)
+ if (streq (text, "bye"))
+ break
+ else if (text[1] != EOS) {
+ ex = qpex_open (qp, text)
+ call qpex_debug (ex, STDOUT, QPEXD_SHOWALL)
+ if (out != NULL) {
+ call fprintf (out, "\f")
+ call qpex_debug (ex, out, QPEXD_SHOWALL)
+ }
+ call qpex_close (ex)
+ call flush (STDOUT)
+ }
+ }
+
+ call close (out)
+ call qp_close (qp)
+end
+
+
+# EXPAND -- Perform macro expansion on text input by the user.
+
+procedure t_expand()
+
+pointer qp, sp, ip, text, obuf
+int getline(), qp_expandtext(), strncmp(), clgeti()
+pointer qp_open()
+
+begin
+ call smark (sp)
+ call salloc (text, SZ_LINE, TY_CHAR)
+ call salloc (obuf, SZ_OBUF, TY_CHAR)
+
+ call clgstr ("poefile", Memc[text], SZ_LINE)
+ qp = qp_open (Memc[text], READ_ONLY, 0)
+ call qp_seti (qp, QPOE_DEBUGLEVEL, clgeti("debug"))
+
+ call printf ("Q> ")
+ call flush (STDOUT)
+
+ while (getline (STDIN, Memc[text]) != EOF) {
+ for (ip=text; IS_WHITE(Memc[ip]); ip=ip+1)
+ ;
+ if (strncmp (Memc[ip], "bye", 3) == 0)
+ break
+ else if (Memc[ip] != '\n') {
+ call write (STDOUT, Memc[obuf],
+ qp_expandtext (qp, Memc[text], Memc[obuf], SZ_OBUF))
+ call printf ("\n")
+ }
+
+ call printf ("Q> ")
+ call flush (STDOUT)
+ }
+
+ call qp_close (qp)
+ call sfree (sp)
+end
+
+
+# RECIO -- Test general record i/o.
+
+procedure t_recio()
+
+int i, n, nrec
+pointer sp, qp, rp, poefile, data
+int qp_read(), qp_accessf(), clgeti()
+pointer qp_open()
+
+begin
+ call smark (sp)
+ call salloc (poefile, SZ_FNAME, TY_CHAR)
+ call salloc (data, 4096 * 3, TY_STRUCT)
+
+ call clgstr ("poefile", Memc[poefile], SZ_FNAME)
+ qp = qp_open (Memc[poefile], READ_WRITE, 0)
+ call qp_seti (qp, QPOE_DEBUGLEVEL, clgeti("debug"))
+ nrec = clgeti ("nrec")
+
+ # Initialize the data array.
+ do i = 1, nrec {
+ rp = data + (i-1) * 3
+ Memr[rp] = i
+ Memi[rp+1] = i
+ Mems[P2S(rp)+4] = i*10+1
+ Mems[P2S(rp)+5] = i*10+2
+ }
+
+ if (qp_accessf (qp, "urec") == NO)
+ call qp_addf (qp, "urec", "{r,i,s,s}", 0, "User record type", 0)
+ if (qp_accessf (qp, "data") == NO)
+ call qp_addf (qp, "data", "urec", nrec, "User records", 0)
+
+ # Initialize the parameter.
+ call qp_write (qp, "data", Memi[data], nrec, 1, "urec")
+
+ call eprintf ("---------------- Full array read test:")
+ call aclri (Memi[data], nrec * 3)
+ n = qp_read (qp, "data", Memi[data], nrec, 1, "urec")
+ call eprintf (" n=%d\n"); call pargi(n)
+ do i = 1, nrec {
+ rp = data + (i-1) * 3
+ call eprintf ("%8.1f %4d %4d %4d\n")
+ call pargr (Memr[rp])
+ call pargi (Memi[rp+1])
+ call pargs (Mems[P2S(rp)+4])
+ call pargs (Mems[P2S(rp)+5])
+ }
+
+ call eprintf ("---------------- Array element read test:\n")
+ call aclri (Memi[data], nrec * 3)
+ do i = 1, nrec {
+ rp = data + (i-1) * 3
+ n = qp_read (qp, "data", Memi[rp], 1, i, "urec")
+ call eprintf ("%4d %8.1f %4d %4d %4d\n")
+ call pargi (i)
+ call pargr (Memr[rp])
+ call pargi (Memi[rp+1])
+ call pargs (Mems[P2S(rp)+4])
+ call pargs (Mems[P2S(rp)+5])
+ }
+
+ call eprintf ("---------------- Array element read test (reversed):\n")
+ call aclri (Memi[data], nrec * 3)
+ do i = nrec, 1, -1 {
+ rp = data + (i-1) * 3
+ n = qp_read (qp, "data", Memi[rp], 1, i, "urec")
+ call eprintf ("%4d %8.1f %4d %4d %4d\n")
+ call pargi (i)
+ call pargr (Memr[rp])
+ call pargi (Memi[rp+1])
+ call pargs (Mems[P2S(rp)+4])
+ call pargs (Mems[P2S(rp)+5])
+ }
+
+ call qp_close (qp)
+ call sfree (sp)
+end
+
+
+# NEWCOPY -- Test inheritance occurring during a new-copy open.
+
+procedure t_newcopy()
+
+char iname[SZ_FNAME] # input name
+char oname[SZ_FNAME] # output name
+
+pointer iqp, oqp
+pointer qp_open()
+
+begin
+ call clgstr ("input", iname, SZ_FNAME)
+ call clgstr ("output", oname, SZ_FNAME)
+
+ iqp = qp_open (iname, READ_ONLY, NULL)
+ oqp = qp_open (oname, NEW_COPY, iqp)
+
+ call printf ("iqp=%x; oqp=%x\n")
+ call pargi (iqp)
+ call pargi (oqp)
+
+ call qp_close (oqp)
+ call qp_close (iqp)
+end
+
+
+# SYMS -- Dump the symbol table of a QPOE datafile.
+
+procedure t_syms()
+
+char fname[SZ_FNAME]
+pointer qp, qp_open()
+
+begin
+ call clgstr ("fname", fname, SZ_FNAME)
+ qp = qp_open (fname, READ_ONLY, 0)
+ call qp_dsym (qp, STDOUT)
+ call qp_close (qp)
+end
+
+
+# HLIST -- List selected header parameters.
+
+procedure t_hlist()
+
+pointer qp, list, sym
+int nelem, maxelem, flags
+char datatype[SZ_DATATYPE], comment[SZ_COMMENT]
+char fname[SZ_FNAME], param[SZ_FNAME], pattern[SZ_FNAME]
+pointer qp_open(), qp_ofnlu(), qp_ofnls(), qp_gpsym()
+int qp_queryf(), qp_gnfn()
+bool clgetb()
+
+begin
+ call clgstr ("fname", fname, SZ_FNAME)
+ call clgstr ("pattern", pattern, SZ_FNAME)
+
+ qp = qp_open (fname, READ_ONLY, 0)
+ if (clgetb ("sort"))
+ list = qp_ofnls (qp, pattern)
+ else
+ list = qp_ofnlu (qp, pattern)
+
+ call printf (" PARAM DTYPE NELEM MAXEL LF OFF FLG COMMENT\n")
+ while (qp_gnfn (list, param, SZ_FNAME) != EOF) {
+ nelem = qp_queryf (qp, param, datatype, maxelem, comment, flags)
+ sym = qp_gpsym (qp, param)
+
+ call printf ("%15s %6s %5d %5d %2d%4d %3o %s\n")
+ call pargstr (param)
+ call pargstr (datatype)
+ call pargi (nelem)
+ call pargi (maxelem)
+ if (sym != NULL) {
+ call pargi (S_LFILE(sym))
+ call pargi (S_OFFSET(sym))
+ call pargi (and (flags, 777B))
+ call pargstr (comment)
+ } else {
+ call pargi (0)
+ call pargi (0)
+ call pargi (0)
+ call pargstr ("[could not find symbol (macro?)]")
+ }
+ }
+
+ call qp_cfnl (list)
+ call qp_close (qp)
+end
+
+
+# DUMPEVL -- Dump an event list descriptor.
+
+procedure t_dumpevl()
+
+pointer qp, io, dd, ev
+char poefile[SZ_FNAME], param[SZ_FNAME]
+char datatype[SZ_DATATYPE], comment[SZ_COMMENT]
+int offset, dtype, size, nelem, maxelem, flags, i, j
+pointer qp_open(), qpio_open(), qpio_stati(), coerce()
+int qp_queryf(), sizeof()
+
+begin
+ call clgstr ("poefile", poefile, SZ_FNAME)
+ qp = qp_open (poefile, READ_ONLY, NULL)
+
+ call clgstr ("eventlist", param, SZ_FNAME)
+ if (param[1] == EOS)
+ call strcpy ("events", param, SZ_FNAME)
+ io = qpio_open (qp, param, READ_ONLY)
+
+ call printf ("%s.%s:\n")
+ call pargstr (poefile)
+ call pargstr (param)
+
+ nelem = qp_queryf (qp, param, datatype, maxelem, comment, flags)
+ call printf ("dtype=%s nelem=%d maxel=%d flg=%o comment=%s\n")
+ call pargstr (datatype)
+ call pargi (nelem)
+ call pargi (maxelem)
+ call pargi (and (flags, 777B))
+ call pargstr (comment)
+
+ call printf ("%s=%dx%d ")
+ call pargstr ("blockfactor")
+ call pargi (qpio_stati(io, QPIO_XBLOCKFACTOR))
+ call pargi (qpio_stati(io, QPIO_YBLOCKFACTOR))
+ call printf ("%s=%d ")
+ call pargstr ("bucketlen")
+ call pargi (qpio_stati(io, QPIO_BUCKETLEN))
+ call printf ("%s=%d ")
+ call pargstr ("debug")
+ call pargi (qpio_stati(io, QPIO_DEBUG))
+ call printf ("%s=%d ")
+ call pargstr ("evxoff")
+ call pargi (qpio_stati(io, QPIO_EVXOFF))
+ call printf ("%s=%d ")
+ call pargstr ("evxtype")
+ call pargi (qpio_stati(io, QPIO_EVXTYPE))
+ call printf ("%s=%d ")
+ call pargstr ("evyoff")
+ call pargi (qpio_stati(io, QPIO_EVYOFF))
+ call printf ("%s=%d ")
+ call pargstr ("evytype")
+ call pargi (qpio_stati(io, QPIO_EVYTYPE))
+ call printf ("\n")
+
+ call printf ("%s=%xX ")
+ call pargstr ("ex")
+ call pargi (qpio_stati(io, QPIO_EX))
+ call printf ("%s=%d ")
+ call pargstr ("noindex")
+ call pargi (qpio_stati(io, QPIO_NOINDEX))
+ call printf ("%s=%d ")
+ call pargstr ("optbufsize")
+ call pargi (qpio_stati(io, QPIO_OPTBUFSIZE))
+ call printf ("%s=%xX ")
+ call pargstr ("pl")
+ call pargi (qpio_stati(io, QPIO_PL))
+ call printf ("%s=%d ")
+ call pargstr ("eventlen")
+ call pargi (qpio_stati(io, QPIO_EVENTLEN))
+ call printf ("%s=%d ")
+ call pargstr ("fd")
+ call pargi (qpio_stati(io, QPIO_FD))
+ call printf ("\n")
+
+ call printf ("%s=%d ")
+ call pargstr ("indexlen")
+ call pargi (qpio_stati(io, QPIO_INDEXLEN))
+ call printf ("%s=%d ")
+ call pargstr ("ixxoff")
+ call pargi (qpio_stati(io, QPIO_IXXOFF))
+ call printf ("%s=%d ")
+ call pargstr ("ixxtype")
+ call pargi (qpio_stati(io, QPIO_IXXTYPE))
+ call printf ("%s=%d ")
+ call pargstr ("ixyoff")
+ call pargi (qpio_stati(io, QPIO_IXYOFF))
+ call printf ("%s=%d ")
+ call pargstr ("ixytype")
+ call pargi (qpio_stati(io, QPIO_IXYTYPE))
+ call printf ("%s=%d ")
+ call pargstr ("lf")
+ call pargi (qpio_stati(io, QPIO_LF))
+ call printf ("%s=%xX ")
+ call pargstr ("maskp")
+ call pargi (qpio_stati(io, QPIO_MASKP))
+ call printf ("\n")
+
+ call printf ("%s=%xX ")
+ call pargstr ("maxevp")
+ call pargi (qpio_stati(io, QPIO_MAXEVP))
+ call printf ("%s=%xX ")
+ call pargstr ("minevp")
+ call pargi (qpio_stati(io, QPIO_MINEVP))
+ call printf ("%s=%d ")
+ call pargstr ("ncols")
+ call pargi (qpio_stati(io, QPIO_NCOLS))
+ call printf ("%s=%d ")
+ call pargstr ("nlines")
+ call pargi (qpio_stati(io, QPIO_NLINES))
+ call printf ("%s=%xX ")
+ call pargstr ("paramp")
+ call pargi (qpio_stati(io, QPIO_PARAMP))
+ call printf ("%s=%xX ")
+ call pargstr ("qp")
+ call pargi (qpio_stati(io, QPIO_QP))
+ call printf ("\n")
+
+ # Print domain attributes.
+ dd = IO_DD(io)
+ call printf ("Domain `%s': len=%d nfields=%d xfield=%d yfield=%d\n")
+ call pargstr (datatype)
+ call pargi (DD_STRUCTLEN(dd))
+ call pargi (DD_NFIELDS(dd))
+ call pargi (DD_XFIELD(dd))
+ call pargi (DD_YFIELD(dd))
+
+ # Print min/max evl records.
+ do j = 1, 2 {
+ if (j == 1) {
+ call printf ("minevl: ")
+ ev = qpio_stati (io, QPIO_MINEVP)
+ } else {
+ call printf ("maxevl: ")
+ ev = qpio_stati (io, QPIO_MAXEVP)
+ }
+
+ do i = 1, DD_NFIELDS(dd) {
+ offset = DD_FOFFSET(dd,i)
+ dtype = DD_FTYPE(dd,i)
+ size = sizeof(dtype)
+
+ switch (dtype) {
+ case TY_SHORT:
+ call printf (" s%d=%d")
+ call pargi (offset * size * SZB_CHAR)
+ call pargs (Mems[coerce(ev,TY_SHORT,dtype) + offset])
+ case TY_INT:
+ call printf (" i%d=%d")
+ call pargi (offset * size * SZB_CHAR)
+ call pargi (Memi[coerce(ev,TY_SHORT,dtype) + offset])
+ case TY_LONG:
+ call printf (" l%d=%d")
+ call pargi (offset * size * SZB_CHAR)
+ call pargl (Meml[coerce(ev,TY_SHORT,dtype) + offset])
+ case TY_REAL:
+ call printf (" r%d=%0.5g")
+ call pargi (offset * size * SZB_CHAR)
+ call pargr (Memr[coerce(ev,TY_SHORT,dtype) + offset])
+ case TY_DOUBLE:
+ call printf (" d%d=%0.8g")
+ call pargi (offset * size * SZB_CHAR)
+ call pargd (Memd[coerce(ev,TY_SHORT,dtype) + offset])
+ default:
+ call printf (" type=%d")
+ call pargi (dtype)
+ }
+ }
+
+ call printf ("\n")
+ }
+end
+
+
+# MKPOE -- Convert CFA poefile to QPOE poefile.
+# -------------------------
+
+# Size limiting defintions.
+define LEN_EVBUF 512 # size of output event buffer
+define LEN_CVBUF 1000 # max number of mask regions
+define SZ_KEY 20
+
+# CFA Poefile definitions.
+define SZ_EVENT SZ_OEVENT
+define SZ_IEVENT 10 # size of input event struct, chars
+define SZ_OEVENT 12 # size of output event struct, chars
+define SZ_FILEHEADER 256 # size of file header, chars
+
+# File header fields of interest.
+define O_MISSION 1 # byte offset of "mission" field
+define T_MISSION TY_SHORT # datatype of mission field
+define O_INSTRUMENT 1 # byte offset of "instrument" field
+define T_INSTRUMENT TY_SHORT # datatype of instrument field
+define O_XDIM 129 # byte offset of Xdim field
+define T_XDIM TY_SHORT # datatype of Xdim field
+define O_YDIM 131 # byte offset of Ydim field
+define T_YDIM TY_SHORT # datatype of Ydim field
+define O_PSTART 505 # byte offset of PhotonStart field
+define T_PSTART TY_LONG # datatype of PhotonStart field
+define O_PSTOP 509 # byte offset of PhotonStop field
+define T_PSTOP TY_LONG # datatype of PhotonStop field
+
+# Input event struct fields of interest.
+define O_X 1 # sky coordinates
+define T_X TY_SHORT
+define O_Y 3
+define T_Y TY_SHORT
+define O_TIME 5 # arrival time
+define T_TIME TY_DOUBLE
+define O_PHA 13 # pulse height
+define T_PHA TY_SHORT
+define O_PI 15 # energy
+define T_PI TY_SHORT
+define O_DX 17 # detector coordinates
+define T_DX TY_SHORT
+define O_DY 19
+define T_DY TY_SHORT
+
+# The event struct to be stored in the QPOE file.
+define EVTYPE "event"
+define FIELDLIST "{s:x,s:y,s,s,d,s,s}"
+
+define EV_X Mems[$1]
+define EV_Y Mems[$1+1]
+define EV_PHA Mems[$1+2]
+define EV_PI Mems[$1+3]
+define EV_TIME Memd[($1+4-1)/SZ_DOUBLE+1]
+define EV_DX Mems[$1+8]
+define EV_DY Mems[$1+9]
+
+# Define an event structure with short coordinates
+define S_FIELDLIST "{s:x,s:y,s,s,d,s,s}"
+define S_SZ_EVENT 10
+define S_EV_X Mems[$1]
+define S_EV_Y Mems[$1+1]
+define S_EV_PHA Mems[$1+2]
+define S_EV_PI Mems[$1+3]
+define S_EV_TIME Memd[($1+4-1)/SZ_DOUBLE+1]
+define S_EV_DX Mems[$1+8]
+define S_EV_DY Mems[$1+9]
+
+# Define an event structure with integer coordinates
+define I_FIELDLIST "{d,i:x,i:y,i,i,s,s}"
+define I_SZ_EVENT 14
+define I_EV_TIME Memd[($1-1)/SZ_DOUBLE+1]
+define I_EV_X Memi[($1+4-1)/SZ_INT+1]
+define I_EV_Y Memi[($1+6-1)/SZ_INT+1]
+define I_EV_DX Memi[($1+8-1)/SZ_INT+1]
+define I_EV_DY Memi[($1+10-1)/SZ_INT+1]
+define I_EV_PHA Mems[$1+12]
+define I_EV_PI Mems[$1+13]
+
+# Define an event structure with real coordinates
+define R_FIELDLIST "{d,r:x,r:y,r,r,s,s}"
+define R_SZ_EVENT 14
+define R_EV_TIME Memd[($1-1)/SZ_DOUBLE+1]
+define R_EV_X Memr[($1+4-1)/SZ_REAL+1]
+define R_EV_Y Memr[($1+6-1)/SZ_REAL+1]
+define R_EV_DX Memr[($1+8-1)/SZ_REAL+1]
+define R_EV_DY Memr[($1+10-1)/SZ_REAL+1]
+define R_EV_PHA Mems[$1+12]
+define R_EV_PI Mems[$1+13]
+
+# Define an event structure with double coordinates
+define D_FIELDLIST "{d,d:x,d:y,d,d,s,s}"
+define D_SZ_EVENT 22
+define D_EV_TIME Memd[($1-1)/SZ_DOUBLE+1]
+define D_EV_X Memd[($1+4-1)/SZ_DOUBLE+1]
+define D_EV_Y Memd[($1+8-1)/SZ_DOUBLE+1]
+define D_EV_DX Memd[($1+12-1)/SZ_DOUBLE+1]
+define D_EV_DY Memd[($1+16-1)/SZ_DOUBLE+1]
+define D_EV_PHA Mems[$1+20]
+define D_EV_PI Mems[$1+21]
+
+
+# MKPOE -- Write out a new POEFILE, taking a CFA POE file as input.
+# The input file uses big-endian format for integers, and IEEE for floats.
+# The input file must be sorted in order for the output file to be indexed.
+
+procedure t_mkpoe()
+
+char infile[SZ_FNAME] # input CFA-format poefile
+char outfile[SZ_FNAME] # output QPOE-format poefile
+
+char key[SZ_KEY]
+pointer sp, hdr, obuf, optr, ph, ev, qp, io
+int datastart, dataend, mission, instrument, now
+int debug, nphotons, naxes, axlen[2], dmin[8], dmax[8], in, op, i
+
+bool clgetb()
+double mp_getd()
+pointer qp_open(), qpio_open()
+int open(), read(), mp_geti(), clgeti(), clktime()
+
+begin
+ call smark (sp)
+ call salloc (hdr, SZ_FILEHEADER, TY_CHAR)
+ call salloc (obuf, LEN_EVBUF * SZ_OEVENT / SZ_SHORT, TY_SHORT)
+ call salloc (optr, LEN_EVBUF, TY_POINTER)
+ call salloc (ph, SZ_IEVENT, TY_CHAR)
+
+ call clgstr ("infile", infile, SZ_FNAME)
+ call clgstr ("outfile", outfile, SZ_FNAME)
+
+ # Open the input and output files. Clobber the output file if
+ # it already exists.
+
+ in = open (infile, READ_ONLY, BINARY_FILE)
+ iferr (call qp_delete (outfile))
+ ;
+ qp = qp_open (outfile, NEW_FILE, NULL)
+
+ # Set the datafile page size.
+ call qp_seti (qp, QPOE_PAGESIZE, clgeti("pagesize"))
+
+ # Set the bucket length in units of number of events.
+ call qp_seti (qp, QPOE_BUCKETLEN, clgeti("bucketlen"))
+
+ # Set the debug level.
+ debug = clgeti ("debug")
+ call qp_seti (qp, QPOE_DEBUGLEVEL, debug)
+
+ # Read and decode the input file header.
+ if (read (in, Memc[hdr], SZ_FILEHEADER) < SZ_FILEHEADER)
+ call error (1, "cannot read input file header")
+
+ naxes = 2
+ axlen[1] = mp_geti (Memc[hdr], O_XDIM, T_XDIM)
+ axlen[2] = mp_geti (Memc[hdr], O_YDIM, T_YDIM)
+ datastart = mp_geti (Memc[hdr], O_PSTART, T_PSTART)
+ dataend = mp_geti (Memc[hdr], O_PSTOP, T_PSTOP)
+ nphotons = (dataend - datastart + 1) / (SZ_IEVENT * SZB_CHAR)
+
+ mission = mp_geti (Memc[hdr], O_MISSION, T_MISSION)
+ instrument = mp_geti (Memc[hdr], O_INSTRUMENT, T_INSTRUMENT)
+
+ call eprintf ("xdim=%d, ydim=%d, datastart=%d, nphotons=%d\n")
+ call pargi (axlen[1])
+ call pargi (axlen[2])
+ call pargi (datastart)
+ call pargi (nphotons)
+
+ # Setup the QPOE file header.
+ call qp_addf (qp, "naxes", "i", 1, "number of image axes", 0)
+ call qp_puti (qp, "naxes", naxes)
+ call qp_addf (qp, "axlen", "i", 2, "length of each axis", 0)
+ call qp_write (qp, "axlen", axlen, 2, 1, "i")
+
+ now = clktime(0)
+ call qp_addf (qp, "cretime", "i", 1, "image creation time", 0)
+ call qp_puti (qp, "cretime", now)
+ call qp_addf (qp, "modtime", "i", 1, "image modify time", 0)
+ call qp_puti (qp, "modtime", now)
+ call qp_addf (qp, "limtime", "i", 1, "data min/max update time", 0)
+ call qp_puti (qp, "limtime", now)
+
+ # Invent some data min/max values for now.
+ do i = 1, 8 {
+ dmin[i] = 0
+ dmax[i] = 64
+ }
+ call qp_addf (qp, "datamin", "i", 8, "minimum pixel value", 0)
+ call qp_write (qp, "datamin", dmin, 8, 1, "i")
+ call qp_addf (qp, "datamax", "i", 8, "maximum pixel value", 0)
+ call qp_write (qp, "datamax", dmax, 8, 1, "i")
+
+ # Throw in a few miscellaneous params for testing purposes.
+ call qp_addf (qp, "mission", "s", 1, "mission type code", 0)
+ call qp_puti (qp, "mission", mission)
+ call qp_addf (qp, "instrument", "s", 1, "instrument type code", 0)
+ call qp_puti (qp, "instrument", instrument)
+
+ # Define the event structure for the QPOE output file.
+ call qp_addf (qp, EVTYPE, FIELDLIST, 1, "event record type", 0)
+
+ # Copy the event (photon) list.
+ call qp_addf (qp, "events", "event", 0, "main event list", 0)
+ io = qpio_open (qp, "events", NEW_FILE)
+ call seek (in, datastart / SZB_CHAR + 1)
+ op = 0
+
+ do i = 1, nphotons {
+ # Read next event.
+ if (read (in, Memc[ph], SZ_IEVENT) < SZ_IEVENT)
+ call error (2, "photon event list truncated")
+
+ # Copy/transform event struct (not very efficient, but this
+ # is only debug code).
+
+ ev = obuf + (op * SZ_OEVENT / SZ_SHORT)
+ Memi[optr+op] = ev
+
+ EV_TIME(ev) = mp_getd (Memc[ph], O_TIME, T_TIME)
+ EV_X(ev) = mp_geti (Memc[ph], O_X, T_X)
+ EV_Y(ev) = mp_geti (Memc[ph], O_Y, T_Y)
+ EV_PHA(ev) = mp_geti (Memc[ph], O_PHA, T_PHA)
+ EV_PI(ev) = mp_geti (Memc[ph], O_PI, T_PI)
+ EV_DX(ev) = mp_geti (Memc[ph], O_DX, T_DX)
+ EV_DY(ev) = mp_geti (Memc[ph], O_DY, T_DY)
+
+ if (debug > 4) {
+ call eprintf ("%4d %4d %4d %4d %7d %7d %g\n")
+ call pargs (EV_X(ev))
+ call pargs (EV_Y(ev))
+ call pargs (EV_DX(ev))
+ call pargs (EV_DY(ev))
+ call pargs (EV_PHA(ev))
+ call pargs (EV_PI(ev))
+ call pargd (EV_TIME(ev))
+ }
+
+ # Bump output pointer and flush output buffer when it fills.
+ op = op + 1
+ if (op >= LEN_EVBUF) {
+ call qpio_putevents (io, Memi[optr], op)
+ op = 0
+ }
+ }
+
+ # Flush any remaining buffered data.
+ if (op > 0)
+ call qpio_putevents (io, Memi[optr], op)
+
+ # Construct index.
+ if (clgetb ("mkindex")) {
+ call clgstr ("key", key, SZ_KEY)
+ call qpio_mkindex (io, key)
+ }
+
+ # Clean up.
+ call qpio_close (io)
+ call qp_close (qp)
+ call close (in)
+
+ call sfree (sp)
+end
+
+
+# MP_GETI -- Get an integer field from the raw input data.
+
+int procedure mp_geti (buf, boffset, dtype)
+
+char buf[ARB] # byte-stream data buffer
+int boffset # byte offset of desired field
+int dtype # datatype of stored field
+
+short sval
+int nbytes, ival
+int sizeof()
+
+begin
+ nbytes = sizeof(dtype) * SZB_CHAR
+
+ switch (dtype) {
+ case TY_SHORT:
+ if (BYTE_SWAP2 == YES)
+ call bswap2 (buf, boffset, sval, 1, nbytes)
+ else
+ call bytmov (buf, boffset, sval, 1, nbytes)
+ return (sval)
+ case TY_INT, TY_LONG:
+ if (BYTE_SWAP4 == YES)
+ call bswap4 (buf, boffset, ival, 1, nbytes)
+ else
+ call bytmov (buf, boffset, ival, 1, nbytes)
+ return (ival)
+ default:
+ call error (3, "bad dtype switch in mp_geti")
+ }
+end
+
+
+# MP_GETD -- Get a double field from the raw input data. We assume that both
+# the input and output are IEEE floating, hence all we are really doing here
+# is providing for arbitrarily aligned fields, and providing type conversion.
+
+double procedure mp_getd (buf, boffset, dtype)
+
+char buf[ARB] # byte-stream data buffer
+int boffset # byte offset of desired field
+int dtype # datatype of stored field
+
+double dval
+real rval
+int nbytes, half
+int sizeof()
+
+begin
+ nbytes = sizeof(dtype) * SZB_CHAR
+ half = nbytes / 2
+
+ switch (dtype) {
+ case TY_REAL:
+ if (BYTE_SWAP4 == YES)
+ call bswap4 (buf, boffset, rval, 1, nbytes)
+ else
+ call bytmov (buf, boffset, rval, 1, nbytes)
+ return (rval)
+ case TY_DOUBLE:
+ if (BYTE_SWAP4 == YES) {
+ call bswap4 (buf, boffset, dval, 1+half, half)
+ call bswap4 (buf, boffset+half, dval, 1, half)
+ } else
+ call bytmov (buf, boffset, dval, 1, nbytes)
+ return (dval)
+ default:
+ call error (3, "bad dtype switch in mp_getd")
+ }
+end
+
+
+# TESTPOE -- Make a test QPOE file, generating an artificial sequence of
+# events (for testing special cases). The event list is not sorted or
+# indexed.
+
+procedure t_testpoe()
+
+char outfile[SZ_FNAME] # output QPOE-format poefile
+
+pointer sp, ev, qp, io, evl[1]
+int nevents, naxes, axlen[2], i, datatype
+pointer qp_open(), qpio_open()
+int clgeti()
+char clgetc()
+
+begin
+ # Open the output file.
+ call clgstr ("outfile", outfile, SZ_FNAME)
+ iferr (call qp_delete (outfile))
+ ;
+ qp = qp_open (outfile, NEW_FILE, NULL)
+
+ naxes = 2
+ axlen[1] = clgeti ("ncols")
+ axlen[2] = clgeti ("nlines")
+ datatype = clgetc ("datatype")
+
+ # Setup the QPOE file header.
+ call qp_addf (qp, "naxes", "i", 1, "number of image axes", 0)
+ call qp_puti (qp, "naxes", naxes)
+ call qp_addf (qp, "axlen", "i", 2, "length of each axis", 0)
+ call qp_write (qp, "axlen", axlen, 2, 1, "i")
+
+ # Define the event structure for the QPOE output file.
+ call smark (sp)
+ switch (datatype) {
+ case 's':
+ call salloc (ev, S_SZ_EVENT / SZ_SHORT, TY_SHORT)
+ call qp_addf (qp, EVTYPE, S_FIELDLIST, 1, "event record type", 0)
+ case 'i':
+ call salloc (ev, I_SZ_EVENT / SZ_SHORT, TY_SHORT)
+ call qp_addf (qp, EVTYPE, I_FIELDLIST, 1, "event record type", 0)
+ case 'r':
+ call salloc (ev, R_SZ_EVENT / SZ_SHORT, TY_SHORT)
+ call qp_addf (qp, EVTYPE, R_FIELDLIST, 1, "event record type", 0)
+ case 'd':
+ call salloc (ev, D_SZ_EVENT / SZ_SHORT, TY_SHORT)
+ call qp_addf (qp, EVTYPE, D_FIELDLIST, 1, "event record type", 0)
+ }
+
+
+ # Copy the event (photon) list.
+ call qp_addf (qp, "events", "event", 0, "main event list", 0)
+ io = qpio_open (qp, "events", NEW_FILE)
+
+ # Generate some dummy events.
+ nevents = clgeti ("nevents")
+ evl[1] = ev
+
+ # Hack this to generate different types of test files.
+ do i = 1, nevents {
+ switch (datatype) {
+
+ case 's':
+ S_EV_TIME(ev) = 1.0D0 + double(i) / 10.0D0
+ if (mod(i,2) == 0) {
+ S_EV_X(ev) = (i - 1) * 10 + 1
+ S_EV_Y(ev) = (i - 1) * 10 + 1
+ } else {
+ S_EV_X(ev) = axlen[1] - ((i - 1) * 10 + 1)
+ S_EV_Y(ev) = axlen[2] - ((i - 1) * 10 + 1)
+ }
+ S_EV_PHA(ev) = mod (nint(i * 11.1111), 20)
+ S_EV_PI(ev) = i / 2
+ S_EV_DX(ev) = nevents - i + 1
+ S_EV_DY(ev) = nevents - i + 1
+
+ case 'i':
+ I_EV_TIME(ev) = 1.0D0 + double(i) / 10.0D0
+ if (mod(i,2) == 0) {
+ I_EV_X(ev) = (i - 1) * 10 + 1
+ I_EV_Y(ev) = (i - 1) * 10 + 1
+ } else {
+ I_EV_X(ev) = axlen[1] - ((i - 1) * 10 + 1)
+ I_EV_Y(ev) = axlen[2] - ((i - 1) * 10 + 1)
+ }
+ I_EV_PHA(ev) = mod (nint(i * 11.1111), 20)
+ I_EV_PI(ev) = i / 2
+ I_EV_DX(ev) = nevents - i + 1
+ I_EV_DY(ev) = nevents - i + 1
+
+ case 'r':
+ R_EV_TIME(ev) = 1.0D0 + double(i) / 10.0D0
+ if (mod(i,2) == 0) {
+ R_EV_X(ev) = (i - 1) * 10 + 1
+ R_EV_Y(ev) = (i - 1) * 10 + 1
+ } else {
+ R_EV_X(ev) = axlen[1] - ((i - 1) * 10 + 1)
+ R_EV_Y(ev) = axlen[2] - ((i - 1) * 10 + 1)
+ }
+ R_EV_PHA(ev) = mod (nint(i * 11.1111), 20)
+ R_EV_PI(ev) = i / 2
+ R_EV_DX(ev) = nevents - i + 1
+ R_EV_DY(ev) = nevents - i + 1
+
+ case 'd':
+ D_EV_TIME(ev) = 1.0D0 + double(i) / 10.0D0
+ if (mod(i,2) == 0) {
+ D_EV_X(ev) = (i - 1) * 10 + 1
+ D_EV_Y(ev) = (i - 1) * 10 + 1
+ } else {
+ D_EV_X(ev) = axlen[1] - ((i - 1) * 10 + 1)
+ D_EV_Y(ev) = axlen[2] - ((i - 1) * 10 + 1)
+ }
+ D_EV_PHA(ev) = mod (nint(i * 11.1111), 20)
+ D_EV_PI(ev) = i / 2
+ D_EV_DX(ev) = nevents - i + 1
+ D_EV_DY(ev) = nevents - i + 1
+
+ }
+
+ call qpio_putevents (io, evl, 1)
+ }
+
+ # Clean up.
+ call qpio_close (io)
+ call qp_close (qp)
+ call sfree (sp)
+end
+
+
+# COUNTPOE -- Count photons in regions. Whether or not there are any regions
+# depends upon whether the user specifies a region mask, or upon whether the
+# image has a default mask. If there is no mask the entire image is counted.
+# If the user specifies a filter then event attribute filtering will be
+# performed as well. Mask region values should be restricted to the range
+# 0-999.
+
+procedure t_countpoe()
+
+bool list_events
+int debug, nev, mval, m, i
+pointer sp, qp, poefile, evlist, evl, cv, ev, io
+
+bool clgetb()
+pointer qp_open(), qpio_open()
+int qpio_getevents(), clgeti()
+
+begin
+ call smark (sp)
+ call salloc (poefile, SZ_FNAME, TY_CHAR)
+ call salloc (evlist, SZ_EXPR, TY_CHAR)
+ call salloc (evl, LEN_EVBUF, TY_POINTER)
+ call salloc (cv, LEN_CVBUF, TY_INT)
+
+ call clgstr ("poefile", Memc[poefile], SZ_FNAME)
+ qp = qp_open (Memc[poefile], READ_ONLY, NULL)
+
+ debug = clgeti ("debug")
+ call qp_seti (qp, QPOE_DEBUGLEVEL, debug)
+
+ call clgstr ("eventlist", Memc[evlist], SZ_EXPR)
+ io = qpio_open (qp, Memc[evlist], READ_ONLY)
+
+ list_events = clgetb ("list_events")
+ if (list_events)
+ call printf (" EV X Y DX DY PHA PI TIME\n")
+
+ call aclri (Memi[cv], LEN_CVBUF)
+
+ # Scan the event list.
+ while (qpio_getevents (io, Memi[evl], mval, LEN_EVBUF, nev) != EOF) {
+ if (list_events) {
+ do i = 1, nev {
+ ev = Memi[evl+i-1]
+ call printf ("%4d %4d %4d %4d %4d %7d %7d %g\n")
+ call pargi (i)
+ call pargs (EV_X(ev))
+ call pargs (EV_Y(ev))
+ call pargs (EV_DX(ev))
+ call pargs (EV_DY(ev))
+ call pargs (EV_PHA(ev))
+ call pargs (EV_PI(ev))
+ call pargd (EV_TIME(ev))
+ }
+ }
+
+ m = min (LEN_CVBUF, mval)
+ Memi[cv+m] = Memi[cv+m] + nev
+ }
+
+ call qpio_close (io)
+
+ # Print the count of the number of photons in each region.
+ if (list_events)
+ call printf ("\n")
+
+ call printf ("REGION: ")
+ do i = 0, LEN_CVBUF-1
+ if (Memi[cv+i] > 0) {
+ call printf (" %6oB")
+ call pargi (i)
+ }
+ call printf ("\n")
+
+ call printf ("COUNTS: ")
+ do i = 0, LEN_CVBUF-1
+ if (Memi[cv+i] > 0) {
+ call printf (" %7d")
+ call pargi (Memi[cv+i])
+ }
+ call printf ("\n")
+
+ call qp_close (qp)
+ call sfree (sp)
+end
+
+
+# TFILTER -- Perform a brute force time filtering operation upon an event
+# list, and compare against the results of a standard QPEX time filter.
+# This is used to verify the operation of the optimized QPEX time filter.
+
+procedure t_tfilter()
+
+bool open_left, open_right, pass
+int nev1, nev2, totev, mval, nev, xlen, nranges, fd1, fd2, i, j
+pointer sp, poefile, filter, output, fname, evl, x1, y1, t1, x2, y2, t2
+pointer qp, io, ex, ev, xs, xe
+double t
+
+bool clgetb()
+int qpio_getevents(), qpex_attrld(), open()
+pointer qp_open(), qpio_open(), qpex_open()
+
+begin
+ call smark (sp)
+ call salloc (poefile, SZ_FNAME, TY_CHAR)
+ call salloc (output, SZ_FNAME, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (filter, SZ_EXPR, TY_CHAR)
+ call salloc (evl, LEN_EVBUF, TY_POINTER)
+
+ call salloc (x1, MAX_EVENTS, TY_SHORT)
+ call salloc (y1, MAX_EVENTS, TY_SHORT)
+ call salloc (t1, MAX_EVENTS, TY_DOUBLE)
+ call salloc (x2, MAX_EVENTS, TY_SHORT)
+ call salloc (y2, MAX_EVENTS, TY_SHORT)
+ call salloc (t2, MAX_EVENTS, TY_DOUBLE)
+
+ nev1 = 0
+ nev2 = 0
+ totev = 0
+
+ call clgstr ("poefile", Memc[poefile], SZ_FNAME)
+ qp = qp_open (Memc[poefile], READ_ONLY, NULL)
+ io = qpio_open (qp, "", READ_ONLY)
+
+ call clgstr ("filter", Memc[filter], SZ_EXPR)
+ ex = qpex_open (qp, Memc[filter])
+
+ call clgstr ("output", Memc[output], SZ_FNAME)
+
+ if (clgetb ("showfilter")) {
+ call qpex_debug (ex, STDOUT, QPEXD_SHOWALL)
+ call flush (STDOUT)
+ }
+
+ # Scan the event list using the given filter.
+ call printf ("scan event list using optimized filter: ")
+ call flush (STDOUT)
+
+ call qpio_seti (io, QPIO_EX, ex)
+ while (qpio_getevents (io, Memi[evl], mval, LEN_EVBUF, nev) != EOF) {
+ do i = 1, nev {
+ ev = Memi[evl+i-1]
+ Mems[x1+nev1] = EV_X(ev)
+ Mems[y1+nev1] = EV_Y(ev)
+ Memd[t1+nev1] = EV_TIME(ev)
+ nev1 = min (MAX_EVENTS, nev1 + 1)
+ }
+ }
+
+ call printf ("%d events\n")
+ call pargi (nev1)
+ xlen = 128
+ call malloc (xs, TY_DOUBLE, xlen)
+ call malloc (xe, TY_DOUBLE, xlen)
+
+ # Get the time filter as a list of ranges.
+ xs = NULL; xe = NULL; xlen = 0
+ nranges = qpex_attrld (ex, "time", xs, xe, xlen)
+ if (nranges > 0) {
+ open_left = IS_INDEF(Memd[xs])
+ open_right = IS_INDEF(Memd[xe+nranges-1])
+ } else {
+ open_left = false
+ open_right = false
+ }
+
+ # Scan the event list, applying a brute force time filter.
+ call qpio_seti (io, QPIO_EX, NULL)
+ call printf ("scan event list using brute force filter: ")
+ call flush (STDOUT)
+
+ while (qpio_getevents (io, Memi[evl], mval, LEN_EVBUF, nev) != EOF) {
+ do i = 1, nev {
+ ev = Memi[evl+i-1]
+ t = EV_TIME(ev)
+
+ # Apply the time filter.
+ if (open_left && open_right && nranges == 1)
+ pass = true
+ else {
+ pass = false
+ do j = 1, nranges {
+ if (j == 1 && open_left) {
+ if (t <= Memd[xe]) {
+ pass = true
+ break
+ }
+ } else if (j == nranges && open_right) {
+ if (t >= Memd[xs+nranges-1]) {
+ pass = true
+ break
+ }
+ } else if (t >= Memd[xs+j-1] && t <= Memd[xe+j-1]) {
+ pass = true
+ break
+ }
+ }
+ }
+
+ if (pass) {
+ Mems[x2+nev2] = EV_X(ev)
+ Mems[y2+nev2] = EV_Y(ev)
+ Memd[t2+nev2] = EV_TIME(ev)
+ nev2 = min (MAX_EVENTS, nev2 + 1)
+ }
+ }
+
+ totev = totev + nev
+ }
+
+ call printf ("%d events\n")
+ call pargi (nev2)
+ call printf ("out of a total of %d events\n")
+ call pargi (totev)
+ call flush (STDOUT)
+
+ # Dump the two event lists if an output root filename was given.
+ if (Memc[output] != EOS) {
+ call sprintf (Memc[fname], SZ_FNAME, "%s.1")
+ call pargstr (Memc[output])
+ iferr (call delete (Memc[fname]))
+ ;
+ fd1 = open (Memc[fname], NEW_FILE, TEXT_FILE)
+
+ call sprintf (Memc[fname], SZ_FNAME, "%s.2")
+ call pargstr (Memc[output])
+ iferr (call delete (Memc[fname]))
+ ;
+ fd2 = open (Memc[fname], NEW_FILE, TEXT_FILE)
+
+ do i = 1, max (nev1, nev2) {
+ if (i <= nev1) {
+ call fprintf (fd1, "%d %d %g\n")
+ call pargs (Mems[x1+i-1])
+ call pargs (Mems[y1+i-1])
+ call pargd (Memd[t1+i-1])
+ }
+ if (i <= nev2) {
+ call fprintf (fd2, "%d %d %g\n")
+ call pargs (Mems[x2+i-1])
+ call pargs (Mems[y2+i-1])
+ call pargd (Memd[t2+i-1])
+ }
+ }
+
+ call close (fd1)
+ call close (fd2)
+ }
+
+ # Compare the results of the two filters for equality.
+ pass = true
+ do i = 1, min (nev1, nev2) {
+ if (Mems[x1+i-1] != Mems[x2+i-1] || Mems[y1+i-1] != Mems[y2+i-1]) {
+ call printf ("bad compare at event %d: ")
+ call pargi (i)
+ call printf ("[%d,%d,%0.4f] != [%d,%d,%0.4f]\n")
+ call pargs (Mems[x1+i-1])
+ call pargs (Mems[y1+i-1])
+ call pargd (Memd[t1+i-1])
+ call pargs (Mems[x2+i-1])
+ call pargs (Mems[y2+i-1])
+ call pargd (Memd[t2+i-1])
+ pass = false
+ break
+ }
+ }
+
+ if (pass) {
+ call printf ("first %d events are identical\n")
+ call pargi (min (nev1, nev2))
+ }
+
+ call mfree (xs, TY_DOUBLE)
+ call mfree (xe, TY_DOUBLE)
+ call qpex_close (ex)
+ call qpio_close (io)
+ call qp_close (qp)
+
+ call sfree (sp)
+end
+
+
+# PLOTPOE -- Read and plot photons, showing the position of each photon
+# in the image matrix, according to the current coordinate system.
+
+procedure t_plotpoe()
+
+int ncols, nlines, xblock, yblock, mval, nev, i
+pointer sp, poefile, evlist, evl, xv, yv, qp, io, ev, gp
+pointer qp_open(), gopen(), qpio_open
+int clgeti(), qp_stati(), qp_geti(), qpio_getevents()
+
+begin
+ call smark (sp)
+ call salloc (poefile, SZ_FNAME, TY_CHAR)
+ call salloc (evlist, SZ_EXPR, TY_CHAR)
+ call salloc (evl, LEN_EVBUF, TY_POINTER)
+ call salloc (xv, LEN_EVBUF, TY_REAL)
+ call salloc (yv, LEN_EVBUF, TY_REAL)
+
+ call clgstr ("poefile", Memc[poefile], SZ_FNAME)
+ qp = qp_open (Memc[poefile], READ_ONLY, NULL)
+
+ call qp_seti (qp, QPOE_DEBUGLEVEL, clgeti ("debug"))
+ call qp_seti (qp, QPOE_XBLOCKFACTOR, clgeti ("xblock"))
+ call qp_seti (qp, QPOE_YBLOCKFACTOR, clgeti ("yblock"))
+
+ xblock = qp_stati (qp, QPOE_XBLOCKFACTOR)
+ yblock = qp_stati (qp, QPOE_YBLOCKFACTOR)
+ ncols = qp_geti (qp, "axlen[1]") / xblock
+ nlines = qp_geti (qp, "axlen[2]") / yblock
+
+ gp = gopen ("stdgraph", NEW_FILE, STDGRAPH)
+ call gswind (gp, 1., real(ncols), 1., real(nlines))
+ call gsetr (gp, G_ASPECT, 1.0)
+
+ call clgstr ("eventlist", Memc[evlist], SZ_EXPR)
+ io = qpio_open (qp, Memc[evlist], READ_ONLY)
+
+ if (Memc[evlist] == EOS)
+ call glabax (gp, "events", "X", "Y")
+ else
+ call glabax (gp, Memc[evlist], "X", "Y")
+
+ # Scan the event list.
+ while (qpio_getevents (io, Memi[evl], mval, LEN_EVBUF, nev) != EOF) {
+ do i = 1, nev {
+ ev = Memi[evl+i-1]
+ Memr[xv+i-1] = EV_X(ev) / xblock + 1.0
+ Memr[yv+i-1] = EV_Y(ev) / yblock + 1.0
+ }
+ call gpmark (gp, Memr[xv], Memr[yv], nev, GM_POINT, 0.0, 0.0)
+ call gflush (gp)
+ }
+
+ call qpio_close (io)
+ call gclose (gp)
+
+ call qp_close (qp)
+ call sfree (sp)
+end
+
+
+# SUM -- Sum the counts in an image section.
+
+procedure t_sum()
+
+double sum
+char image[SZ_LINE]
+int ncols, nlines, i
+pointer im, immap(), imgl2i()
+real asumi()
+
+begin
+ call clgstr ("image", image, SZ_LINE)
+ im = immap (image, READ_ONLY, 0)
+
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+
+ call printf ("ncols=%d, nlines=%d, pixtype=%d\n")
+ call pargi (ncols)
+ call pargi (nlines)
+ call pargi (IM_PIXTYPE(im))
+ call flush (STDOUT)
+
+ sum = 0
+ do i = 1, nlines
+ sum = sum + asumi (Memi[imgl2i(im,i)], ncols)
+
+ call printf ("total pixels = %d, counts = %14.0f\n")
+ call pargi (ncols * nlines)
+ call pargd (sum)
+
+ call imunmap (im)
+end
+
+
+# SETWCS -- Store a wcs in a QPOE file.
+
+procedure t_setwcs()
+
+pointer qp, mw
+char text[SZ_LINE]
+pointer qp_open, mw_open
+
+begin
+ call clgstr ("poefile", text, SZ_LINE)
+ qp = qp_open (text, READ_WRITE, 0)
+
+ mw = mw_open (NULL, 2)
+ call qp_savewcs (qp, mw)
+
+ call mw_close (mw)
+ call qp_close (qp)
+end
+
+
+# SETFILT -- Set the default filter in a QPOE file.
+
+procedure t_setfilt()
+
+pointer qp
+char poefile[SZ_FNAME]
+char filter[SZ_LINE]
+pointer qp_open()
+
+begin
+ call clgstr ("poefile", poefile, SZ_FNAME)
+ qp = qp_open (poefile, READ_WRITE, 0)
+
+ call clgstr ("deffilt", filter, SZ_LINE)
+ call qp_astr (qp, "deffilt", filter, "default filter")
+
+ call qp_close (qp)
+end
+
+
+# SETMASK -- Set the default mask in a QPOE file.
+
+procedure t_setmask()
+
+pointer qp
+char poefile[SZ_FNAME]
+char mask[SZ_LINE]
+pointer qp_open()
+
+begin
+ call clgstr ("poefile", poefile, SZ_FNAME)
+ qp = qp_open (poefile, READ_WRITE, 0)
+
+ call clgstr ("defmask", mask, SZ_LINE)
+ call qp_astr (qp, "defmask", mask, "default mask")
+
+ call qp_close (qp)
+end
+
+
+# MERGEI -- Test the merge range list routine (integer version).
+# The lists may be specified either as strings, or as @file-name.
+
+procedure t_mergei()
+
+int p1, p2
+char list1[SZ_LINE], list2[SZ_LINE]
+pointer sp, rl1, rl2, op, xs, xe, ys, ye, os, oe
+int fd, ch, xlen, ylen, olen, nx, ny, nout, i
+int open(), getci(), qpex_parsei(), qp_rlmergei()
+
+begin
+ call smark (sp)
+ call salloc (rl1, SZ_RLBUF, TY_CHAR)
+ call salloc (rl2, SZ_RLBUF, TY_CHAR)
+
+ # Get the first range list.
+ call clgstr ("list1", list1, SZ_LINE)
+ if (list1[1] == '@') {
+ fd = open (list1[2], READ_ONLY, TEXT_FILE)
+ op = rl1
+ while (getci (fd, ch) != EOF) {
+ if (ch == '\n')
+ ch = ' '
+ Memc[op] = ch
+ op = op + 1
+ }
+ Memc[op] = EOS
+ } else
+ call strcpy (list1, Memc[rl1], SZ_RLBUF)
+
+ # Get the second range list.
+ call clgstr ("list2", list2, SZ_LINE)
+ if (list2[1] == '@') {
+ fd = open (list2[2], READ_ONLY, TEXT_FILE)
+ op = rl2
+ while (getci (fd, ch) != EOF) {
+ if (ch == '\n')
+ ch = ' '
+ Memc[op] = ch
+ op = op + 1
+ }
+ Memc[op] = EOS
+ } else
+ call strcpy (list2, Memc[rl2], SZ_RLBUF)
+
+ # Parse the lists.
+ xlen = 100
+ call malloc (xs, xlen, TY_INT)
+ call malloc (xe, xlen, TY_INT)
+ nx = qpex_parsei (Memc[rl1], xs, xe, xlen)
+
+ ylen = 100
+ call malloc (ys, ylen, TY_INT)
+ call malloc (ye, ylen, TY_INT)
+ ny = qpex_parsei (Memc[rl2], ys, ye, ylen)
+
+ # Merge the lists.
+ olen = 100
+ call malloc (os, olen, TY_INT)
+ call malloc (oe, olen, TY_INT)
+ nout = qp_rlmergei (os,oe,olen,
+ Memi[xs],Memi[xe],nx, Memi[ys],Memi[ye],ny)
+
+ # Print results:
+ call printf ("---- list 1 -----\n")
+ do i = 1, nx {
+ p1 = Memi[xs+i-1]
+ p2 = Memi[xe+i-1]
+ call printf ("%8d %8s : %8s\n")
+ call pargi (i)
+ if (IS_LEFTI(p1))
+ call pargstr ("left")
+ else
+ call pargi (p1)
+ if (IS_RIGHTI(p2))
+ call pargstr ("right")
+ else
+ call pargi (p2)
+ }
+
+ call printf ("---- list 2 -----\n")
+ do i = 1, ny {
+ p1 = Memi[ys+i-1]
+ p2 = Memi[ye+i-1]
+ call printf ("%8d %8s : %8s\n")
+ call pargi (i)
+ if (IS_LEFTI(p1))
+ call pargstr ("left")
+ else
+ call pargi (p1)
+ if (IS_RIGHTI(p2))
+ call pargstr ("right")
+ else
+ call pargi (p2)
+ }
+
+ call printf ("---- merged -----\n")
+ do i = 1, nout {
+ p1 = Memi[os+i-1]
+ p2 = Memi[oe+i-1]
+ call printf ("%8d %8s : %8s\n")
+ call pargi (i)
+ if (IS_LEFTI(p1))
+ call pargstr ("left")
+ else
+ call pargi (p1)
+ if (IS_RIGHTI(p2))
+ call pargstr ("right")
+ else
+ call pargi (p2)
+ }
+
+ # Free list storage.
+ call mfree (xs, TY_INT); call mfree (xe, TY_INT)
+ call mfree (ys, TY_INT); call mfree (ye, TY_INT)
+ call mfree (os, TY_INT); call mfree (oe, TY_INT)
+
+ call sfree (sp)
+end
+
+
+# CLEAR -- Clear the terminal screen.
+
+procedure t_clear()
+
+pointer tty
+pointer ttyodes()
+errchk ttyodes
+
+begin
+ # Clear the screen.
+ tty = ttyodes ("terminal")
+ call ttyclear (STDOUT, tty)
+ call ttycdes (tty)
+end