PRO FXBREADM, UNIT, COL, $
              D0,  D1,  D2,  D3,  D4,  D5,  D6,  D7,  D8,  D9, $
              D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, $
              D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, $
              D30, D31, D32, D33, D34, D35, D36, D37, D38, D39, $
              D40, D41, D42, D43, D44, D45, D46, D47, D48, D49, $
              ROW=ROW, VIRTUAL=VIR, DIMENSIONS=DIM, $
              NOSCALE=NOSCALE, NOIEEE=NOIEEE, $
              NANVALUE=NANVALUE, BUFFERSIZE=BUFFERSIZE, $
              ERRMSG=ERRMSG, WARNMSG=WARNMSG, STATUS=OUTSTATUS

;+
; Project     : RXTE/PCA
;
; Name        : 
;	FXBREADM
; Purpose     : 
;	Read multiple columns/rows from a disk FITS binary table file.
; Explanation : 
;       A call to FXBREADM will read data from multiple rows and
;       multiple columns in a single procedure call.  Up to fifty
;       columns may be read in a single pass; the number of rows is
;       limited essentially by available memory.  The file should have
;       already been opened with FXBOPEN.  FXBREADM optimizes reading
;       multiple columns by first reading a large chunk of data from
;       the FITS file directly, and then slicing the data into columns
;       within memory.  FXBREADM cannot read variable-length arrays;
;       use FXBREAD instead.
; Use         : 
;	FXBREADM, UNIT, COL, DATA1, DATA2, ... [, ROW=ROW ]
; Inputs      : 
;	UNIT	= Logical unit number corresponding to the file containing the
;		  binary table.
;       COL     = An array of columns in the binary table to read data
;                 from, either as character strings containing column
;                 labels (TTYPE), or as numerical column indices
;                 starting from column one.
; Opt. Inputs : 
;       None.
; Outputs     : 
;	D0, ... = A named variable to accept the data values, one for
;                 each column.  The columns are stored in order of the
;                 list in COL.  If the read operation fails for a
;                 particular column, then the corresponding output Dn
;                 variable is not altered.  See the STATUS keyword.
;
; Opt. Outputs: 
;	None.
; Keywords    : 
;	ROW	= Either row number in the binary table to read data from,
;		  starting from row one, or a two element array containing a
;		  range of row numbers to read.  If not passed, then the entire
;		  column is read in.
;	NOSCALE	= If set, then the ouput data will not be scaled using the
;		  optional TSCAL and TZERO keywords in the FITS header.
;		  Default is to scale.
;	VIRTUAL	= If set, and COL is passed as a name rather than a number,
;		  then if the program can't find a column with that name, it
;		  will then look for a keyword with that name in the header.
;		  Such a keyword would then act as a "virtual column", with the
;		  same value for every row.
;	DIMENSIONS = FXBREADM ignores this keyword.  It is here for
;	          compatibility only.
;	NANVALUE= Value signalling data dropout.  All points corresponding to
;		  IEEE NaN (not-a-number) are converted to this number.
;		  Ignored unless DATA is of type float, double-precision or
;		  complex.
;	ERRMSG	= If defined and passed, then any error messages will be
;		  returned to the user in this parameter rather than
;		  depending on the MESSAGE routine in IDL.  If no errors are
;		  encountered, then a null string is returned.  In order to
;		  use this feature, ERRMSG must be defined first, e.g.
;
;			ERRMSG = ''
;			FXBREAD, ERRMSG=ERRMSG, ...
;			IF ERRMSG NE '' THEN ...
;       WARNMSG = Messages which are considered to be non-fatal
;                 "warnings" are returned in this  output string.
;       BUFFERSIZE = Raw data are transferred from the file in chunks
;                 to conserve memory.  This is the size in bytes of
;                 each chunk.  If a value of zero is given, then all
;                 of the data are transferred in one pass.  Default is
;                 32768 (32 kB).
;       STATUS  = An output array containing the status for each
;                 column read, 1 meaning success and 0 meaning failure.
;
; Calls       : 
;	IEEE_TO_HOST, FXPAR, WHERENAN
; Common      : 
;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
;	information.
; Restrictions: 
;	The binary table file must have been opened with FXBOPEN.
;
;	The data must be consistent with the column definition in the binary
;	table header.
;
;	The row number must be consistent with the number of rows stored in the
;	binary table header.
;
;       No variable-length columns may be read with FXBREADM.
;
;       Generaly speaking, FXBREADM will be faster than iterative
;       calls to FXBREAD when (a) a large number of columns is to be
;       read or (b) the size in bytes of each cell is small, so that
;       the overhead of the FOR loop in FXBREAD becomes significant.
;
; Side effects: 
;	If there are no elements to read in (the number of elements is zero),
;	then the program sets !ERR to -1, and DATA is unmodified.
;
; Category    : 
;	Data Handling, I/O, FITS, Generic.
; Prev. Hist. : 
;	W. Thompson, Jan 1992.
;	W. Thompson, Feb 1992, modified to support variable length arrays.
;	W. Thompson, Jun 1992, modified way that row ranges are read in.  No
;			       longer works reiteratively.
;	W. Thompson, Jun 1992, fixed bug where NANVALUE would be modified by
;			       TSCAL and TZERO keywords.
;	W. Thompson, Jun 1992, fixed bug when reading character strings.
;			       Treats dimensions better when reading multiple
;			       rows.
;       C. Markwardt, based in concept on FXBREAD version 12 from
;                              IDLASTRO, but with significant and
;                              major changes to accomodate the
;                              multiple row/column technique.  Mostly
;                              the parameter checking and general data
;                              flow remain.
;
; Written     : 
;       Craig Markwardt, GSFC, January 1999.
; Modified    :
;       Version 1, Craig Markwardt, GSFC 18 January 1999.
;               Documented this routine, 18 January 1999. 
; Version     :
;       Version 1, 18 January 1999.
;-
;
@fxbintable
	ON_ERROR, 2
;
;  Check the number of parameters.
;
	IF N_PARAMS() LT 2 THEN BEGIN
		MESSAGE = 'Syntax:  FXBREADM, UNIT, COL, D0, D1, ... [, ROW= ]'
		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
			ERRMSG = MESSAGE
			RETURN
		END ELSE MESSAGE, MESSAGE
	ENDIF
        IF N_ELEMENTS(BUFFERSIZE) EQ 0 THEN BUFFERSIZE = 32768L

;
;  COL may be one of several descriptors:
;     * a list of column numbers, beginning with 1
;     * a list of column names
;
        MYCOL = [ COL ]    ; Make sure it is an array

        SC = SIZE(MYCOL)
        NUMCOLS = N_ELEMENTS(MYCOL)
        OUTSTATUS = LONARR(NUMCOLS)

;
;  Find the logical unit number in the FXBINTABLE common block.
;
	ILUN = WHERE(LUN EQ UNIT,NLUN)
	ILUN = ILUN(0)
	IF NLUN EQ 0 THEN BEGIN
		MESSAGE = 'Unit ' + STRTRIM(UNIT,2) +	$
			' not opened properly'
		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
			ERRMSG = MESSAGE
			RETURN
		END ELSE MESSAGE, MESSAGE
	ENDIF

;
;  Check the number of columns.  It should be fewer than 50
;
        IF NUMCOLS GT 50 THEN BEGIN
            MESSAGE = 'Maximum of 50 columns exceeded'
            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
                ERRMSG = MESSAGE
                RETURN
            END ELSE MESSAGE, MESSAGE
        ENDIF
        IF NUMCOLS LT N_PARAMS()-2 AND N_ELEMENTS(ERRMSG) EQ 0 THEN BEGIN
            MESSAGE, 'WARNING: number of data parameters less than columns', $
              /INFO
        ENDIF
            
        ICOL    = LONARR(NUMCOLS)
        VIRTUAL = BYTARR(NUMCOLS)
        VIRTYPE = LONARR(NUMCOLS)
        FOUND   = BYTARR(NUMCOLS)
        NOTFOUND = ''
        NNOTFOUND = 0L
        IF N_ELEMENTS(WARNMSG) NE 0 THEN WARNMSG = ''

;
;  If COL is of type string, then search for a column with that label.
;
        IF SC(SC(0)+1) EQ 7 THEN BEGIN
            MYCOL = STRUPCASE(STRTRIM(MYCOL,2))
            FOR I = 0, NUMCOLS-1 DO BEGIN
		XCOL = WHERE(TTYPE(*,ILUN) EQ MYCOL(I), NCOL)
		ICOL(I) = XCOL(0)
;
;  If the column was not found, and VIRTUAL was set, then search for a keyword
;  by that name.
;
                IF NCOL GT 0 THEN FOUND(I) = 1
                IF NOT FOUND(I) AND KEYWORD_SET(VIR) THEN BEGIN
                    HEADER = HEAD(*,ILUN)
                    VALUE = FXPAR(HEADER,MYCOL(I))
                    IF !ERR GE 0 THEN BEGIN
                        RESULT = EXECUTE('D'+STRTRIM(I,2)+$
                                         ' = VALUE')
                        SV = SIZE(VALUE)
                        VIRTYPE(I) = SV(SV(0)+1)
                        VIRTUAL(I) = 1
                        FOUND(I) = 1
                    ENDIF
                ENDIF ELSE IF NOT FOUND(I) THEN BEGIN
                    IF NOTFOUND EQ '' THEN NOTFOUND = MYCOL(I) $
                    ELSE NOTFOUND = NOTFOUND +', ' + MYCOL(I)
                    NNOTFOUND = NNOTFOUND + 1
                ENDIF

            ENDFOR

            IF NNOTFOUND EQ NUMCOLS THEN BEGIN
                MESSAGE = 'ERROR: None of the requested columns were found'
                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
                    ERRMSG = MESSAGE
                    RETURN
                END ELSE MESSAGE, MESSAGE
            ENDIF ELSE IF NNOTFOUND GT 0 THEN BEGIN
                MESSAGE = 'WARNING: Columns ' + NOTFOUND + ' were not found'
                IF N_ELEMENTS(WARNMSG) NE 0 THEN WARNMSG = MESSAGE $
                ELSE MESSAGE, MESSAGE, /INFO
            ENDIF
                
;
;  Otherwise, a numerical column was passed.  Check its value.
;
	ENDIF ELSE BEGIN
            ICOL(*) = LONG(MYCOL) - 1
        ENDELSE

;  Step through each column index
        MESSAGE = ''
        FOR I = 0, NUMCOLS-1 DO BEGIN
            IF NOT FOUND(I) THEN GOTO, LOOP_END_COLCHECK
            IF VIRTUAL(I) THEN GOTO, LOOP_END_COLCHECK

            IF (ICOL(I) LT 0) OR (ICOL(I) GE TFIELDS(ILUN)) THEN BEGIN
                MESSAGE = MESSAGE + '; COL "'+STRTRIM(MYCOL(I),2)+$
                  '" must be between 1 and ' +	$
                  STRTRIM(TFIELDS(ILUN),2)
                FOUND(I) = 0
;                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
;                    ERRMSG = MESSAGE
;                    RETURN
;                END ELSE MESSAGE, MESSAGE
            ENDIF
;
;  If there are no elements in the array, then set !ERR to -1.
;
            IF FOUND(I) AND N_ELEM(ICOL(I),ILUN) EQ 0 THEN BEGIN
                FOUND(I) = 0
                MESSAGE = MESSAGE + '; Number of elements to read in "'+$
                  STRTRIM(MYCOL(I),2)+'" is zero'
;                !ERR = -1
;                RETURN
            ENDIF

;
;  Do not permit variable-length columns
;
            IF MAXVAL(ICOL(I),ILUN) GT 0 THEN BEGIN
                MESSAGE = MESSAGE + '; FXBREADM cannot read ' + $
                  'variable-length column "'+STRTRIM(MYCOL(I),2)+'"'
                FOUND(I) = 0
;                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
;                    ERRMSG = MESSAGE
;                    RETURN
;                END ELSE MESSAGE, MESSAGE
            ENDIF

            LOOP_END_COLCHECK:

        ENDFOR

;
;  Check to be sure that there are columns to be read
;
        W = WHERE(FOUND EQ 1, COUNT)
        IF COUNT EQ 0 THEN BEGIN
            STRPUT, MESSAGE, ':', 0
            MESSAGE = 'ERROR: No requested columns could be read'+MESSAGE
            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
                ERRMSG = MESSAGE
                RETURN
            END ELSE MESSAGE, MESSAGE
        ENDIF ELSE IF MESSAGE NE '' THEN BEGIN
            STRPUT, MESSAGE, ':', 0
            MESSAGE = 'WARNING: Some columns could not be read'+MESSAGE
            IF N_ELEMENTS(WARNMSG) NE 0 THEN WARNMSG = MESSAGE $
            ELSE MESSAGE, MESSAGE, /INFO
        ENDIF
            
;
;  If ROW was not passed, then set it equal to the entire range.  Otherwise,
;  extract the range.
;
        IF N_ELEMENTS(ROW) EQ 0 THEN ROW = [1L, NAXIS2(ILUN)]
	CASE N_ELEMENTS(ROW) OF
		1:  ROW2 = LONG(ROW(0))
		2:  ROW2 = LONG(ROW(1))
		ELSE:  BEGIN
			MESSAGE = 'ROW must have one or two elements'
			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
				ERRMSG = MESSAGE
				RETURN
			END ELSE MESSAGE, MESSAGE
			END
	ENDCASE
	ROW1 = LONG(ROW(0))
;
;  If ROW represents a range, then make sure that the row range is legal, and
;  that reading row ranges is allowed (i.e., the column is not variable length.
;
	IF ROW1 NE ROW2 THEN BEGIN
		MAXROW = NAXIS2(ILUN)
		IF (ROW1 LT 1) OR (ROW1 GT MAXROW) THEN BEGIN
			MESSAGE = 'ROW(0) must be between 1 and ' +	$
				STRTRIM(MAXROW,2)
			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
				ERRMSG = MESSAGE
				RETURN
			END ELSE MESSAGE, MESSAGE
		END ELSE IF (ROW2 LT ROW1) OR (ROW2 GT MAXROW) THEN BEGIN
			MESSAGE = 'ROW(1) must be between ' +	$
				STRTRIM(ROW1,2) + ' and ' + STRTRIM(MAXROW,2)
			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
				ERRMSG = MESSAGE
				RETURN
			END ELSE MESSAGE, MESSAGE
		ENDIF
;
;  Otherwise, if ROW is a single number, then just make sure it's valid.
;
	END ELSE BEGIN
		IF (ROW1 LT 1) OR (ROW1 GT NAXIS2(ILUN)) THEN BEGIN
			MESSAGE = 'ROW must be between 1 and ' +	$
				STRTRIM(NAXIS2(ILUN),2)
			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
				ERRMSG = MESSAGE
				RETURN
			END ELSE MESSAGE, MESSAGE
		ENDIF
	ENDELSE

;
;  Compose information about the output
;
        COLNDIM = LONARR(NUMCOLS)
        COLDIM  = LONARR(NUMCOLS, 20) ;; Maximum of 20 dimensions in output
        COLTYPE = LONARR(NUMCOLS)
        BOFF1   = LONARR(NUMCOLS)
        BOFF2   = LONARR(NUMCOLS)
        NROWS = ROW2-ROW1+1
        DTYPENAMES = [ 'BAD TYPE', 'BYTE', 'FIX', 'LONG', $
                       'FLOAT', 'DOUBLE', 'COMPLEX', 'STRING', $
                       'BAD TYPE', 'DCOMPLEX' ]
        FOR I = 0L, NUMCOLS-1 DO BEGIN

            IF NOT FOUND(I) THEN GOTO, LOOP_END_DIMS
            ;;  Data type of the input.
            IF VIRTUAL(I) THEN BEGIN
                COLTYPE(I) = VIRTYPE(I) 
                GOTO, LOOP_END_DIMS
            ENDIF ELSE $
              COLTYPE(I) = IDLTYPE(ICOL(I),ILUN)

            DIMS = N_DIMS(*,ICOL(I),ILUN)
            NDIMS = DIMS(0)
            DIMS  = DIMS(1:NDIMS)

            IF NDIMS EQ 1 AND DIMS(0) EQ 1 THEN BEGIN

                ;; Case of only one output element, try to return a
                ;; scalar.  Otherwise, it is a vector equal to the
                ;; number of rows to be read

                COLNDIM(I) = 1L
                COLDIM(I,0) = NROWS
            ENDIF ELSE BEGIN

                COLNDIM(I) = NDIMS
                COLDIM(I,0:(NDIMS-1)) = DIMS
                IF NROWS GT 1 THEN BEGIN
                    COLDIM(I,NDIMS) = NROWS
                    COLNDIM(I) = COLNDIM(I)+1
                ENDIF

            ENDELSE
            
            ;; For strings, the number of characters is the first
            ;; dimension.  This information is useless to us now,
            ;; since the STRING() type cast which will appear below
            ;; handles the array conversion automatically.
            IF COLTYPE(I) EQ 7 THEN BEGIN
                IF COLNDIM(I) GT 1 THEN BEGIN
                    COLDIM(I,0:COLNDIM(I)-2) = COLDIM(I,1:COLNDIM(I)-1)
                    COLDIM(I,COLNDIM(I)-1)   = 0
                    COLNDIM(I) = COLNDIM(I) - 1
                ENDIF ELSE BEGIN  ;; Case of a single row
                    COLNDIM(I) = 1L
                    COLDIM(I,0) = NROWS
                ENDELSE
            ENDIF

            ;; Byte offsets
            BOFF1(I) = BYTOFF(ICOL(I),ILUN)
            IF ICOL(I) EQ TFIELDS(ILUN)-1 THEN BOFF2(I) = NAXIS1(ILUN)-1 $
            ELSE BOFF2(I) = BYTOFF(ICOL(I)+1,ILUN)-1

            LOOP_END_DIMS:

        ENDFOR

;
;  Construct any virtual columns first
;
        WC = WHERE(FOUND EQ 1 AND VIRTUAL EQ 1, WCCOUNT)
        IF WCCOUNT GT 0 THEN BEGIN
            FOR I = 0, WCCOUNT-1 DO BEGIN
                ;; If it's virtual, then the value only needs to be
                ;; replicated
                EXTCMD = 'D'+STRTRIM(WC(I),2)+$
                  ' = REPLICATE(D'+STRTRIM(WC(I),2)+',NROWS)'
                ;; Run the command that selects the data
                RESULT = EXECUTE(EXTCMD)
                IF RESULT EQ 0 THEN BEGIN
                    MESSAGE = 'ERROR: Could not extract data (column '+$
                      STRTRIM(MYCOL(WC(I)),2)+')'
                    IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
                        ERRMSG = MESSAGE
                        RETURN
                    ENDIF ELSE MESSAGE, MESSAGE
                ENDIF
                OUTSTATUS(I) = 1
            ENDFOR
        ENDIF

;  Skip to the end if all columns are virtual
        IF WCCOUNT EQ NUMCOLS THEN GOTO, PROC_CLEANUP

        IF N_ELEMENTS(NANVALUE) GE NUMCOLS THEN BEGIN
            NANVALUES = NANVALUE(0:NUMCOLS-1)
        ENDIF ELSE IF N_ELEMENTS(NANVALUE) GT 0 THEN BEGIN
            NANVALUES = REPLICATE(NANVALUE(0), NUMCOLS)
            NANVALUES(0) = NANVALUE
            I = N_ELEMENTS(NANVALUE)
            IF I LT NUMCOLS THEN $
              NANVALUES(I:*) = NANVALUE(0)
        ENDIF

;
;  Find the position of the first byte of the data array in the file.
;
	OFFSET0 = NHEADER(ILUN) + NAXIS1(ILUN)*(ROW1-1)
        POS = 0L
        NROWS0 = NROWS
        J = 0L
        FIRST = 1
        ;; Here, we constrain the buffer to be at least 16 rows long.
        ;; If we fill up 32 kB with fewer than 16 rows, then there
        ;; must be a lot of (big) columns in this table.  It's
        ;; probably a candidate for using FXBREAD instead.
        BUFFROWS = LONG((BUFFERSIZE/NAXIS1(ILUN)) > 16L)
        IF BUFFERSIZE LE 0 THEN BUFFROWS = NROWS0

;
;  Loop through the data in chunks
;
        WHILE NROWS GT 0 DO BEGIN
        J = J + 1
        NR  = NROWS < BUFFROWS
        OFFSET1 = NAXIS1(ILUN)*POS

;
;  Proceed by reading a byte array from the input data file
;  FXBREADM reads all columns from the specified rows, and
;  sorts out the details of which bytes belong to which columns
;  in the next FOR loop.
;
        BB = BYTARR(NAXIS1(ILUN), NR)
        FXGSEEK, UNIT, OFFSET0+OFFSET1
        FXGREAD, UNIT, BB

;
;  Now select out the desired columns
;
        FOR I = 0, NUMCOLS-1 DO BEGIN
           
            ;; Extract the proper rows and columns
            IF NOT FOUND(I) THEN GOTO, LOOP_END_STORE
            IF VIRTUAL(I) THEN GOTO, LOOP_END_STORE

            ;; Extract the data from the byte array and convert it
            ;; The inner CALL_FUNCTION is to one of the coercion
            ;; functions, such as FIX(), DOUBLE(), STRING(), etc.,
            ;; which is called with an offset to force a conversion
            ;; from bytes to the data type.
            ;; The outer CALL_FUNCTION is to REFORM(), which makes
            ;; sure that the data structure is correct.
            ;;
            DIMS = COLDIM(I,0:COLNDIM(I)-1)
            PERROW = ROUND(PRODUCT(DIMS)/NROWS0)

            IF COLTYPE(I) EQ 7 THEN BEGIN
                DD = STRING(BB(BOFF1(I):BOFF2(I), *))
            ENDIF ELSE BEGIN
                DD = CALL_FUNCTION(DTYPENAMES(COLTYPE(I)), $
                                   BB(BOFF1(I):BOFF2(I),*),0, PERROW*NR)
            ENDELSE
            IF N_ELEMENTS(DD) EQ 1 THEN DD = [DD]
            DD = REFORM(DD, PERROW, NR, /OVERWRITE)

            ;; Now perform any type-specific conversions, etc.
            COUNT = 0L
            CT = COLTYPE(I)
            CASE 1 OF
                ;; Integer types
                (CT EQ 2 OR CT EQ 3): BEGIN
                    IF NOT KEYWORD_SET(NOIEEE) THEN $
                      IEEE_TO_HOST, DD 
                END

                ;; Floating and complex types
                (CT GE 4 OR CT LE 6 OR CT EQ 9): BEGIN
                    IF NOT KEYWORD_SET(NOIEEE) THEN BEGIN
                        IF N_ELEMENTS(NANVALUES) GT 0 THEN W=WHERENAN(DD,COUNT)
                        IEEE_TO_HOST, DD
                    ENDIF
                END

                ;; String types (CT EQ 7) have already been converted
                ;; in the above CALL_FUNCTION.  No further conversion
                ;; is necessary here.
            ENDCASE

;
;  If the parameters TZERO and TSCAL are non-trivial, then adjust the array by
;  these values.
;
            IF NOT KEYWORD_SET(NOIEEE) AND NOT KEYWORD_SET(NOSCALE) THEN BEGIN
                BZERO  = TZERO(ICOL(I),ILUN)
                BSCALE = TSCAL(ICOL(I),ILUN)
                IF (BSCALE NE 0) AND (BSCALE NE 1) THEN DD = BSCALE*DD
		IF BZERO NE 0 THEN DD = DD + BZERO
            ENDIF

;
;  Store NANVALUE everywhere where the data corresponded to IEEE NaN.
;
            IF COUNT GT 0 THEN DD(W) = NANVALUES(I)

            ;; Initialize the output variable on the first chunk
            IF FIRST THEN BEGIN
                RESULT = EXECUTE('D'+STRTRIM(I,2)+' = 0')
                DA = MAKE_ARRAY(PERROW, NROWS0, TYPE=COLTYPE(I))
                RESULT = EXECUTE('D'+STRTRIM(I,2)+' = '+$
                                 'REFORM(DA,PERROW, NROWS0,/OVERWRITE)')
            ENDIF

            ;; Finally, store this in the output variable
            RESULT = EXECUTE('D'+STRTRIM(I,2)+'(0,POS) = DD')
            DD = 0
            IF RESULT EQ 0 THEN BEGIN
                MESSAGE = 'ERROR: Could not compose output data D'+$
                  STRTRIM(I,2)
                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
                    ERRMSG = MESSAGE
                    RETURN
                ENDIF ELSE MESSAGE, MESSAGE
            ENDIF

            OUTSTATUS(I) = 1

            LOOP_END_STORE:
        ENDFOR

        FIRST = 0
        NROWS = NROWS - NR
        POS   = POS + NR
        ENDWHILE

        FOR I = 0, NUMCOLS-1 DO BEGIN
            IF OUTSTATUS(I) NE 1 THEN GOTO, LOOP_END_FINAL
            DIMS = COLDIM(I,0:COLNDIM(I)-1)
            NEL  = PRODUCT(DIMS)
            IF NEL GT 1 THEN $
              RESULT = EXECUTE('D'+STRTRIM(I,2)+' = '+$
                               'REFORM(D'+STRTRIM(I,2)+',DIMS,/OVERWRITE)') $
            ELSE $
              RESULT = EXECUTE('D'+STRTRIM(I,2)+' = D'+STRTRIM(I,2)+'(0)')
            LOOP_END_FINAL:
        ENDFOR

        PROC_CLEANUP:
;
        IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
	RETURN
	END