#! /bin/sh

# This is a shell archive.  Type 'sh <file>' to unpack.

echo x - README.1st
cat >README.1st <<'MKSHAR_EOF'
Date: January 6, 2003

Utility: on_to_unload.c

Synopsis: This utility, was written to deblock and extract records from the

output of arcunload.  It will also read onunload output for a single
table and the arcunload feature of archecker.  Currently on_to_unload is
formatted to read an onunload format file and write out a dbload
compatible delimited data file.  Data decoding is based on a list of data
types, with length for char fields, as generated by sql2fmt.awk from a
dbschema or myschema listing of the single table.

This utility was written as a companion for the arcunload utility available
from IIUG Software Repository (Special Programs section) which extracts a
table's data from a level 0 ontape archive and writes the data pages to a
file suitable for reloading using onload.  However, since it is not always
convenient to restore that way and since such emergency extractions are
normally done to recover a subset of the data lost due to fumble fingers, I
wrote this utility to read an onunload file, such as that produced by
arcunload, and output a delimited file suitable for editing and manual
extraction and for loading using dbload or the dbaccess load command.

The purpose of this is to be able to edit and extract selected rows from
the arcunload output without having to load the data into a table first.
It also makes it possible to arcunload records from one server's archive
and restore them to another server.

Datatype support is not complete and may never be as arcunload is no
longer supported though the arcunload feature of archecker still exists. 
        
Author(s): Art S. Kagel, kagelfamily@netscape.net
Revision: 1.4
Version(s) supported: 7.2x+
File(s): on_to_unload.c, sql2fmt.awk
Comments: requires arcunload, though it can be used with a true onunload
    output file or tape containing a single table (testing was performed
    using both arcunload and onunload output).
 
Release Notes: 

Ver 1.2-1.4 implement additional datatypes including DATE, DATETIME,
DECIMAL, INTERVAL, and VARCHAR.

Ver 1.01 fixed bug outputting rows with misaligned data (ie binary row image 
contains columns that are not properly aligned for the hardware).

This initial release only contains rudimentary data type and table support.
Tables containing BLOBS, UDTs, etc and rows larger than a single page are
not supported.  Supported data types so far: serial, integer, smallint, float,
smallfloat, char (fixed length).  Also GLS support is not included.  It should
not be difficult to add support for the other fixed length data types.  Support
for variable length data types requires that the slot table parsing understand
about slot forwarding when a row outgrows its home page.  Also support for rows
that do not fit on a single page will require complex record caching techniques
as will BLOB support.

Usage instructions:

Pipe the output of dbschema or myschema to awk -f sql2fmt.awk and save
the output, ie:

myschema -d mydatabase -t table_to_unload | awk -f sql2fmt.awk >typefile

Edit the output file to remove any lines that DO NOT begin with a data type.

Now run the on_to_unload executable as:

on_to_unload typefile arcunload_output_file on_to_unload_output_file.unl

If you get any messages about unknown types you will have to update
on_to_unload.c to handle them.  Please send any update back to me for
distribution. 
MKSHAR_EOF


echo x - sql2fmt.awk
cat >sql2fmt.awk <<'MKSHAR_EOF'
BEGIN{ start=0; intable = 0; }
/create table/ { intable = 1; next; }
/CREATE TABLE/ { intable = 1; start = 1; next; }
intable == 1 && $1 == "(" { start = 1; next; }
start == 1 && $1 == ");" { start = 0; intable = 0; next; }
start == 1 && $1 == ")" && $2 == "IN" {start = 0; intable = 0; next; }
start == 1 {
#  print $0;
  split ($2, b, "," );
  n=split( b[1], a, "[,()]" );
  if (n>2) {
    printf "%s %d %d\n", a[1], a[2], a[3];
  }else if (n>1) {
    printf "%s %d\n", a[1], a[2];
  } else {
    printf "%s\n", a[1];
  }
  n=split($0, c );
  if (c[n] == ");") { start = 0; intable = 0; }
}
{ next; }
MKSHAR_EOF


echo x - on_to_unload.c
cat >on_to_unload.c <<'MKSHAR_EOF'
/* on_to_unload.c - Program to deblock and extract records from Informix
   data pages.  Currently formatted to read an onunload format file.
   Formatting based on a list of data types, with length for char fields,
   and variable length fields, as generated by sql2fmt.awk.  

   This utility was written as a companion for the arcunload utility available
   from IIUG Software Repository (Special Programs section) which extracts a 
   table's data from a level 0 ontape archive and writes the data pages to a 
   file suitable for reloading using onload.  However, since it is not always 
   convenient to restore that way and since such emergency extractions are 
   normally done to recover a subset of the data lost due to fumble fingers,
   I wrote this utility to read an onunload file, such as that produced by 
   arcunload, and output a delimited file suitable for editing and manual 
   extraction and for loading using dbload or the dbaccess load command.

   Output records use quoted HEX codes for non-printable characters including 
   newline characters embedded in character columns.  This means that files 
   containing such data are best loaded using dbload -X.

   Author: Art S. Kagel
   Created: October 20, 1998

   This source is released for public use.  All rights of commercial
   exploitation retained.  Copyright 1998, Art S. Kagel.

*/
/*
   RCS Header:
   -----------
   $Header: /home/kagel/utils/RCS/on_to_unload.c,v 1.4 2001/01/17 16:22:29 kagel Exp kagel $

   RCS Log:
   --------
   $Log: on_to_unload.c,v $
   Revision 1.4  2001/01/17 16:22:29  kagel
   Updated to handle DECIMALS.

   Revision 1.3  1999/03/15 17:36:33  kagel
   *** empty log message ***

 * Revision 1.2  1998/11/05  20:30:22  kagel
 * Added RCS headers, added support for new datatypes.  Completed:
 *    varchar(for full rows on a page only), date, datetime.  Working on:
 *    interval.  Also fixed bugs affected misaligned data, can now recognize
 *    format files in upper or lower case (support for dbschema & myschema).
 *

   Vers 1.1:
   Updated through Nov. 3, 1998 before initial checkin.

*/

static char RCSHeader[] = "$Header: /home/kagel/utils/RCS/on_to_unload.c,v 1.4 2001/01/17 16:22:29 kagel Exp kagel $";
static char RCSWhat[] = "$What: <@(#) on_to_unload.c,v	 1.4 > $ $Date: 2001/01/17 16:22:29 $";
static char RCSCompile[] = "$cc: " __FILE__ " " __DATE__ " " __TIME__ " $";

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <strings.h>
#include <ctype.h>
#include <limits.h>

#include <decimal.h>
#include <datetime.h>

typedef struct s_types {
    union {
	struct {
	    short length, scale;
	} dec;
	struct {
	    short max, incr;
	} vchar;
	struct {
	    short start, end;
	} dtim;
	struct {
	    char len, start, end;
	} intv;
    } optn;
    long length;
    short type;
    char type_str[19];
} t_types;

typedef struct s_packdec {
    char exp_sgn:1, exponent:7;
    char digits[16];
} t_packdec;

typedef struct pagehdr_s {
    long pagenum;
    long timestmp;
    short numslts;
    short flagbits;
    short freeoffs;
    short freebytes;
    long nextnode;
    long prevnode;
} pagehdr_t;

typedef struct partn_s {
    pagehdr_t hdr;
    long partnum;             /* partition number */
    short flags;              /* flags                        */
    short pctfree;            /* percentage free space        */
    short rowsize;            /* uncompressed row size        */
    short ncols;              /* number columns in table      */
    short nkeys;              /* number keys for table        */
    short nextns;             /* number extents in partition  */
    long created;            /* date/time created            */
    long serial;             /* current serial value         */
    long fextsize;           /* first extent size            */
    long nxtsize;            /* subsequent extents size      */
    long nptotal;            /* number pages total           */
    long npused;             /* number pages ever used       */
    long npdata;             /* number pages of data         */
    long octptnm;            /* partnum of the optical cluster table */
    long lockid;             /* lock id to use               */
    long nrows;             /* number rows in table         */
} partn_t;

long ncols, counter = 0, pages = 0, dpages = 0, totslots = 0;
t_types ttypes[1000];

enum types {CHAR, SHORT, LONG32, LONG64, FLOAT, DOUBLE, DECIMAL,
	    DTIME, INTVL, DATE, VCHAR };

void printrec( FILE *out, char *rec );
void gettypes( FILE *file );
char *nextrec( char *page, short *slot );
void strlower( char *str );
void date_to_ymd( long date, short *year, short *month, short *day );
void rdec_to_str( char *str, t_types *t, char *p );
void rdt_to_str( char *str, t_types *t, char *p );
void rint_to_str( char *str, t_types *t, char *p );

int main( int argc, char **argv )
{
    FILE *typefile, *inpfile, *outpfile;
    char page[2048], *rec;
    short slot;
    pagehdr_t *hdr = (pagehdr_t *)page;
    partn_t *phdr = (partn_t *)page;
    long datapages = 0;

    if (argc < 4) {
	fprintf( stderr,
		 "Usage: %s typefile  inputfile  outputfile\n\n",
		 argv[0] );
	fprintf( stderr,
		 "\ttypefile   - file containing one datatype per line, "
		 "second column \n\t\t    is length for character columns.\n" );
	fprintf( stderr,
		 "\tinputfile  - the file containing output from arcunload.\n" );
	fprintf( stderr,
		 "\toutputfile - the file into which to write delimited output.\n\n" );
	exit( 1 );
    }

    typefile = fopen( argv[1], "r" );
    inpfile  = fopen( argv[2], "r" );
    outpfile = fopen( argv[3], "w" );

    gettypes( typefile );
    fclose( typefile );
    for (;;) {
	if (fread( page, 2048, 1, inpfile ) == 0)
	    break;
	if (hdr->pagenum == 0xfffffffd) {
	    pages++;
	    continue;
	} else if (hdr->pagenum == -1) {
	    pages++;
	    fprintf( stderr,
		     "\nHeader page(%d):\n\tBlocksize = %d\n\ttapelen = %d.\n",
		     pages,
		     hdr->freeoffs,
		     hdr->nextnode );
	    continue;
	} else if (hdr->pagenum == -2) {
	    pages++;
	    fprintf( stderr, "Separator page(%d).\n", pages );
	    continue;
	} else if (hdr->pagenum == -3) {
	    pages++;
	    fprintf( stderr, "Trailer page (%d)!\n\n", pages );
	    break;
	} else if (hdr->flagbits == 2) {
	    /* Partition page. */
	    pages++;
	    fprintf( stderr,
		     "Partition page(%d) reports %d data pages.\n",
		     pages,
		     phdr->npdata );
	    datapages = phdr->npdata;
	    continue;
	} else if (hdr->flagbits == 1) {
	    /* Data/index page. */
	    pages++;
	    slot = 0;
	    while ((rec = nextrec( page, &slot ))) {
		printrec( outpfile, rec );
		counter++;
		if (counter % 1000 == 0) {
		    printf( "Wrote: %d\r", counter );
		    fflush( stdout );
		}
	    }
	} else
	    pages++;
	if (dpages >= datapages) {
	    fprintf( stderr, "Found all data pages.  Exiting.\n" );
	    break;
	}
    }

    fflush( outpfile );
    printf( "\nRead %d pages, %d data pages.\nDecoded %d slots.\nWrote %d records.\n",
	    pages,
	    dpages,
	    totslots,
	    counter );
    fflush( stdout );
    fclose( outpfile );
    fclose( inpfile );
}

/* Copy a string converting all to printables, quoting special chars.
   Returns a pointer to the end of the output string. */
char *intellicpy( char *to, char *from, short l )
{
    short i = 0;
    int h;
    unsigned char *t = (unsigned char *)to, *f = (unsigned char *)from;
    char *e;

    while (i < l && *f) {
	if (*f < ' ' || *f > '~') {
	    h = *f;
	    t += sprintf( t, "\\%02.2x", h ) - 1;
	} else if (*f == '\\' || *f == '|') {
	    *t = '\\';
	    t++;
	    *t = *f;
	} else
	    *t = *f;
	t++;
	f++;
	i++;
    }
    e = (char *)(t - 1);
    for (;i <= l; i++, t++)
	*t = (char)0;
    return e;
}

void strip( char *to, char *from, short l )
{
    char *p;

    p = intellicpy( to, from, l );
    while (p > to && *(p) == ' ') {
	*p = (char)0;
	p--;
    };
    if (to[0] == (char)0)
	to[0] = ' ';
}

void printrec( FILE *out, char *rec )
{
    char *p = rec;
    static char holder[32768];
    int field;
    short yr, mo, dy;
    static union u_data {
	double a_double;
	long a_long;
	short a_short;
	float a_float;
	long  nullchk[2];
    } data;

    for (field=0; field < ncols; field++) {
	switch (ttypes[field].type) {
	case LONG32:
	    memcpy( &data.a_long, p, sizeof (long) );
	    if (data.a_long == LONG_MIN)
		fprintf( out, "|" );
	    else {
		fprintf( out, "%ld|", data.a_long );
	    }
	    break;
	case SHORT:
	    memcpy(&data.a_short, p, sizeof (short) );
	    if (data.a_short == SHRT_MIN)
		fprintf( out, "|" );
	    else {
		fprintf( out, "%hd|", data.a_short );
	    }
	    break;
	case FLOAT:
	    memcpy( &data.a_float, p, sizeof (float) );
	    if (data.nullchk[0] == 0xffffffff)
		fprintf( out, "|" );
	    else {
		fprintf( out, "%hf|", data.a_float );
	    }
	    break;
	case DOUBLE:
	    memcpy( &data.a_double, p, sizeof (double) );
	    if (data.nullchk[0] == 0xffffffff && data.nullchk[1] == 0xffffffff)
		fprintf( out, "|" );
	    else {
		fprintf( out, "%f|", data.a_double );
	    }
	    break;
	case CHAR:
	    if (*p == (char)0)
		fprintf( out, "|" );
	    else {
		strip( holder, p, ttypes[field].length );
		fprintf( out,
			 "%0.*s|",
			 ttypes[field].length * -1,
			 holder );
	    }
	    break;
	case VCHAR:
		/* Column length is encoded in first byte, text follows. */
	    if (*p < 0)
		fprintf( out, "|" );
	    else if (*p == 0)
		fprintf( out, " |" );
	    else {
		strip( holder, (char *)(p+1), *p );
		fprintf( out,
			 "%s|",
			 holder );
	    }
	    break;
	case DATE:
	    memcpy( &data.a_long, p, sizeof (long) );
	    date_to_ymd( data.a_long, &yr, &mo, &dy );
	    fprintf( out, "%d/%d/%d|", mo, dy, yr );
	    break;
	case DECIMAL:
	    holder[0] = (char)0;
	    rdec_to_str( holder, &ttypes[field], p );
	    fprintf( out, "%s|", holder );
	    break;
	case DTIME:
	    holder[0] = (char)0;
	    rdt_to_str( holder, &ttypes[field], p );
	    fprintf( out, "%s|", holder );
	    break;
	case INTVL:
	    holder[0] = (char)0;
	    rint_to_str( holder, &ttypes[field], p );
	    fprintf( out, "%s|", holder );
	    break;
	}
	p += ttypes[field].length;
    }
    fprintf( out, "\n" );
    fflush( out );
}

char *nextrec( char *page, short *slot )
{
    short nslots, offs;
    char *rec;
    pagehdr_t *hdr = (pagehdr_t *)page;

    if (hdr->flagbits != (short)1) /* Not a data page, skip it. */
	return (char *)NULL;

    if (*slot == 0) {
	totslots += hdr->numslts;
	dpages++;
    }
    nslots = hdr->numslts;
rehash:
    (*slot)++;
    if (*slot > nslots)
	return (char *)NULL;
    offs = (*(short *)(page + 2044 - (*slot * 4)));
    if (!offs)
	goto rehash;
    rec = (page + offs);

    return rec;
}

void gettypes( FILE *file )
{
    char buff[1025], *p;
    int digits, scale;

    ncols = 0;
    for (;;) {
	if (fgets( buff, 1025, file ) == (char *)NULL)
	    break;
	p = strtok( buff, " \n" );
	strlower( p );
	strcpy( ttypes[ncols].type_str, p );
	if (strcmp( p, "integer" ) == 0) {
	    ttypes[ncols].length = 4;
	    ttypes[ncols].type = LONG32;
	} else if (strcmp( p, "serial" ) == 0) {
	    ttypes[ncols].length = 4;
	    ttypes[ncols].type = LONG32;
	} else if (strcmp( p, "smallint" ) == 0) {
	    ttypes[ncols].length = 2;
	    ttypes[ncols].type = SHORT;
	} else if (strcmp( p, "float" ) == 0) {
	    ttypes[ncols].length = 8;
	    ttypes[ncols].type = DOUBLE;
	} else if (strcmp( p, "smallfloat" ) == 0) {
	    ttypes[ncols].length = 4;
	    ttypes[ncols].type = FLOAT;
	} else if (strcmp( p, "char" ) == 0) {
	    p = strtok( NULL, " " );
	    ttypes[ncols].length = atol( p );
	    ttypes[ncols].type = CHAR;
	} else if (strcmp( p, "date" ) == 0) {
	    ttypes[ncols].length = 4;
	    ttypes[ncols].type = DATE;
	} else if (strcmp( p, "varchar" ) == 0) {
	    p = strtok( NULL, " " );
	    ttypes[ncols].optn.vchar.max = atoi( p );
	    p = strtok( NULL, " " );
	    if (p)
		ttypes[ncols].optn.vchar.incr = atoi( p );
	    else
		ttypes[ncols].optn.vchar.incr = 1;
	    ttypes[ncols].type = DATE;
	} else if (strcmp( p, "decimal" ) == 0 || strcmp( p, "money" ) == 0) {
	    p = strtok( NULL, " " );
	    if (p) {
		ttypes[ncols].optn.dec.length = atoi( p );
		p = strtok( NULL, " " );
		if (p)
		    ttypes[ncols].optn.dec.scale = atoi( p );
		else
		    ttypes[ncols].optn.dec.scale = -1;  /* Float pt. */
	    } else {
		ttypes[ncols].optn.dec.length = 16;
		ttypes[ncols].optn.dec.scale = -1;  /* Float pt. */
	    }
	    ttypes[ncols].type = DECIMAL;
	    ttypes[ncols].length = ((ttypes[ncols].optn.dec.length + 1) / 2) + 1;
	} else if (strcmp( p, "datetime" ) == 0) {
	    p = strtok( NULL, " " );
	    strlower( p );
	    if (strcmp( p, "year" ) == 0)
		ttypes[ncols].optn.dtim.start = TU_YEAR;
	    else if (strcmp( p, "month" ) == 0)
		ttypes[ncols].optn.dtim.start = TU_MONTH;
	    else if (strcmp( p, "day" ) == 0)
		ttypes[ncols].optn.dtim.start = TU_DAY;	    
	    else if (strcmp( p, "hour" ) == 0)
		ttypes[ncols].optn.dtim.start = TU_HOUR;
	    else if (strcmp( p, "minute" ) == 0)
		ttypes[ncols].optn.dtim.start = TU_MINUTE;
	    else if (strcmp( p, "second" ) == 0)
		ttypes[ncols].optn.dtim.start = TU_SECOND;
	    else if (strcmp( p, "fraction" ) == 0)
		ttypes[ncols].optn.dtim.start = TU_FRAC;
	    p = strtok( NULL, " " );
	    strlower( p );
	    if (strcmp( p, "year" ) == 0)
		ttypes[ncols].optn.dtim.end = TU_YEAR;
	    else if (strcmp( p, "month" ) == 0)
		ttypes[ncols].optn.dtim.end = TU_MONTH;
	    else if (strcmp( p, "day" ) == 0)
		ttypes[ncols].optn.dtim.end = TU_DAY;	    
	    else if (strcmp( p, "hour" ) == 0)
		ttypes[ncols].optn.dtim.end = TU_HOUR;
	    else if (strcmp( p, "minute" ) == 0)
		ttypes[ncols].optn.dtim.end = TU_MINUTE;
	    else if (strcmp( p, "second" ) == 0)
		ttypes[ncols].optn.dtim.end = TU_SECOND;
	    else if (strcmp( p, "fraction" ) == 0) {
		p = strtok( NULL, " " );
		ttypes[ncols].optn.dtim.end = 10 + atoi( p );
	    }
	    ttypes[ncols].type = DTIME;
	    ttypes[ncols].length = 1 +
		(((ttypes[ncols].optn.dtim.end -
		   ttypes[ncols].optn.dtim.start) +
		  (ttypes[ncols].optn.dtim.start == TU_YEAR ? 4 : 2) + 1) / 2);
	} else if (strcmp( p, "interval" ) == 0) {
	    p = strtok( NULL, " " );
	    strlower( p );
	    if (strcmp( p, "year" ) == 0)
		ttypes[ncols].optn.intv.start = TU_YEAR;
	    else if (strcmp( p, "month" ) == 0)
		ttypes[ncols].optn.intv.start = TU_MONTH;
	    else if (strcmp( p, "day" ) == 0)
		ttypes[ncols].optn.intv.start = TU_DAY;	    
	    else if (strcmp( p, "hour" ) == 0)
		ttypes[ncols].optn.intv.start = TU_HOUR;
	    else if (strcmp( p, "minute" ) == 0)
		ttypes[ncols].optn.intv.start = TU_MINUTE;
	    else if (strcmp( p, "second" ) == 0)
		ttypes[ncols].optn.intv.start = TU_SECOND;
	    else if (strcmp( p, "fraction" ) == 0)
		ttypes[ncols].optn.intv.start = TU_FRAC;
	    p = strtok( NULL, " " );
	    strlower( p );
	    if ((ttypes[ncols].optn.intv.len = atoi( p )) == 0)
		if (ttypes[ncols].optn.intv.start == TU_YEAR)
		    ttypes[ncols].optn.intv.len = 4;
		else
		    ttypes[ncols].optn.intv.len = 2;
	    else {
		p = strtok( NULL, " " );
		strlower( p );
	    }
	    if (strcmp( p, "year" ) == 0)
		ttypes[ncols].optn.intv.end = TU_YEAR;
	    else if (strcmp( p, "month" ) == 0)
		ttypes[ncols].optn.intv.end = TU_MONTH;
	    else if (strcmp( p, "day" ) == 0)
		ttypes[ncols].optn.intv.end = TU_DAY;	    
	    else if (strcmp( p, "hour" ) == 0)
		ttypes[ncols].optn.intv.end = TU_HOUR;
	    else if (strcmp( p, "minute" ) == 0)
		ttypes[ncols].optn.intv.end = TU_MINUTE;
	    else if (strcmp( p, "second" ) == 0)
		ttypes[ncols].optn.intv.end = TU_SECOND;
	    else if (strcmp( p, "fraction" ) == 0) {
		p = strtok( NULL, " " );
		ttypes[ncols].optn.intv.end = 10 + atoi( p );
	    }
	    ttypes[ncols].type = INTVL;
	    ttypes[ncols].length = 1 + ((ttypes[ncols].optn.intv.end -
					 ttypes[ncols].optn.intv.start +
					 ttypes[ncols].optn.intv.len + 1) / 2);
	} else {
	    fprintf( stderr, "Unknown type: %s on line %d\n", p, ncols+1 );
	    continue;
	}
	ncols++;
    }
}

void strlower( char *str )
{
    while (*str) {
	if (isupper( *str ))
	    *str = _tolower( *str );
	str++;
    }
}

void date_to_ymd( long date, short *year, short *month, short *day )
{
    int idx;
    long ydays, yr;
 
    long days_in_mo[12]
	= {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
    long days_in_lp[12]
	= {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
 
 
    /* # Days between leap years.  365 * 3 + 366 == 1461 */
    yr = (date - (date / 1461)) / 365;
    ydays = date - ((yr - 1) / 4 + yr * 365);
    if (((date - (date / 1461)) % 365) == 0 && (ydays == 0 || ydays > 364)) {
        *year = 1900 + yr - 1;
        *month = 12;
        *day = 31;
    } else {
        *year = 1900 + yr;
 
        if (((*year % 4) == 0 && (*year % 100) != 0) || ((*year % 400) == 0)) {
            /* In a leap year use adjusted table. */
            for (idx = 11; idx > 0; idx--)
                if (ydays > days_in_lp[idx])
                    break;
            *day = ydays - days_in_lp[idx];
        } else {
            for (idx = 11; idx > 0; idx--)
                if (ydays > days_in_mo[idx])
                    break;
            *day = ydays - days_in_mo[idx];
        }
        *month = idx + 1;
    }
}

void rdec_to_str( char *str, t_types *t, char *p )
{
    int placeholder = 1;
    t_packdec *d = (t_packdec *)p;
    int d1, d2, ix, ndigs = 0;
    double value, factor;

    value = 0.0d;
    factor = 1.0;
    for (ix=15; ix >= 0; ix--) {
	d1 = d->digits[ix] % 256;
	if (d1) {
	    if (lastnull) {
		ndigs += lastnull;
		lastnull = 0;
	    }
	    value = value + ((double)d1 * factor);
	    factor *= 100.0;
	    ndigs++;
	} else {
	    if (d2)
		factor *= 100.0;
	    lastnull++;
	}
	if (d2) {
	    if (lastnull) {
		ndigs += lastnull;
		lastnull = 0;
	    }
	    d2 = d->digits[ix] / 256;
	    value = value + ((double)d2 * factor);
	    factor *= 100.0;
	    ndigs++;
	    lastnull = 0;
	} else
	    lastnull++;
    }
    if (d->exp_sgn == (char)-1)
	value = ((value / (d->exponent * 100.0)) / ((double)ndigs * 100.0));
    else
	value = ((value * (d->exponent * 100.0)) / ((double)ndigs * 100.0));

    return;
}

void rdt_to_str( char *str, t_types *t, char *p )
{
    int strt, end, ndigs, len, ddigs;
    int year, month, day, hour, minute, second, fraction, multi;
    int index = 1 /* 0 is exponent */, tmp, i, parts[9], part = 0;
    time_t tim;
    char fmt[100];
 
    strt = t->optn.dtim.start;
    end  = t->optn.dtim.end;
 
    ndigs = end - strt + (strt == TU_YEAR?4:2);
    fmt[0] = (char)0;

    if (strt == TU_YEAR) {
        year = 0;
        for (i=0; i<2; i++)
            year = year * 100 + (long)p[index++];
	if (end > TU_YEAR)
	    strcat( fmt, "%0.4d-" );
	else
	    strcat( fmt, "%0.4d" );
	parts[part++] = year;
    } else {
        year = 0;
    }
    if (strt <= TU_MONTH && end >= TU_MONTH) {
        month = ((long)p[index++]);
	if (end > TU_MONTH)
	    strcat( fmt, "%0.2d-" );
	else
	    strcat( fmt, "%0.2d" );
	parts[part++] = month;
    } else {
        month = 0;
    }
    if (strt <= TU_DAY && end >= TU_DAY) {
        day = ((long)p[index++]);
	if (end > TU_DAY)
	    strcat( fmt, "%0.2d " );
	else
	    strcat( fmt, "%0.2d" );
	parts[part++] = day;
    } else {
        day = 0;
    }
    if (strt <= TU_HOUR && end >= TU_HOUR) {
        hour = ((long)p[index++]);
	if (end > TU_HOUR)
	    strcat( fmt, "%0.2d:" );
	else
	    strcat( fmt, "%0.2d" );
	parts[part++] = hour;
    } else {
        hour = 0;
    }
    if (strt <= TU_MINUTE && end >= TU_MINUTE) {
        minute = ((long)p[index++]);
	if (end > TU_MINUTE)
	    strcat( fmt, "%0.2d:" );
	else
	    strcat( fmt, "%0.2d" );
	parts[part++] = minute;
    } else {
        minute = 0;
    }
    if (strt <= TU_SECOND && end >= TU_SECOND) {
        second = ((long)p[index++]);
	if (end > TU_SECOND)
	    strcat( fmt, "%0.2d." );
	else
	    strcat( fmt, "%0.2d" );
	parts[part++] = second;
    } else {
        second = 0;
    }
    ddigs = 0;
    if (end >= TU_F1) {
        switch (end) {
          case TU_F5:
            fraction = (long)p[index++];
            fraction = fraction * 100 + (long)p[index++];
            fraction = fraction * 100 + (long)p[index++];
            multi = 1;
	    ddigs = 5;
            break;
          case TU_F4:
	      ddigs = 4;
          case TU_F3:
            fraction = (long)p[index++];
            fraction = fraction * 100 + (long)p[index++];
            multi = 100;
	    if (!ddigs)
		ddigs = 3;
            break;
          case TU_F2:
	      ddigs = 2;
          case TU_F1:
            fraction = (long)p[index++];
            multi = 10000;
	    if (!ddigs)
		ddigs = 1;
            break;
        }
	parts[part++] = ddigs;
	parts[part++] = fraction;
	strcat( fmt, "%0.*d" );
    } else
        fraction = 0;
    sprintf( str,
	     fmt,
	     parts[0],
	     parts[1],
	     parts[2],
	     parts[3],
	     parts[4],
	     parts[5],
	     parts[6],
	     parts[7],
	     parts[8] );
}

void rint_to_str( char *str, t_types *t, char *p )
{
    int strt, end, ndigs, len, ddigs, scale;
    int year, month, day, hour, minute, second, fraction, multi;
    int index = 1, tmp, i, parts[10], part = 0;
    time_t tim;
    char fmt[100];
 
    strt = t->optn.intv.start;
    end  = t->optn.intv.end;
    scale = ((long)p[0] & 0x7F) - 64;
 
    ndigs = (t->optn.intv.len + 1) / 2;
    fmt[0] = (char)0;

    if (strt == TU_YEAR) {
        year = 0;
        while (ndigs-- && scale--)
            year = year * 100 + (long)p[index++];
	if (end > TU_YEAR)
	    strcat( fmt, "%1.*d-" );
	else
	    strcat( fmt, "%1.*d" );
	parts[part++] = ndigs;
	parts[part++] = year;
    } else {
        year = 0;
    }
    if (strt == TU_MONTH) {
        month = 0;
        while (ndigs-- && scale--)
            month = month * 100 + (long)p[index++];
	parts[part++] = month;
	if (end > TU_MONTH)
	    strcat( fmt, "%0.*d-" );
	else
	    strcat( fmt, "%0.*d" );
    } else if (strt < TU_MONTH && end >= TU_MONTH) {
        month = ((long)p[index++]);
	if (end > TU_MONTH)
	    strcat( fmt, "%0.*d-" );
	else
	    strcat( fmt, "%0.*d" );
	parts[part++] = month;
    } else {
        month = 0;
    }
    if (strt == TU_DAY) {
        day = 0;
        while (ndigs-- && scale--)
            day = day * 100 + (long)p[index++];
	if (end > TU_DAY)
	    strcat( fmt, "%0.*d " );
	else
	    strcat( fmt, "%0.*d" );
	parts[part++] = day;
    } else if (strt < TU_DAY && end >= TU_DAY) {
        day = ((long)p[index++]);
	if (end > TU_DAY)
	    strcat( fmt, "%0.*d " );
	else
	    strcat( fmt, "%0.*d" );
	parts[part++] = day;
    } else {
        day = 0;
    }
    if (strt == TU_HOUR) {
        hour = 0;
        while (ndigs-- && scale--)
            hour = hour * 100 + (long)p[index++];
	if (end > TU_HOUR)
	    strcat( fmt, "%0.*d:" );
	else
	    strcat( fmt, "%0.*d" );
	parts[part++] = hour;
    } else if (strt < TU_HOUR && end >= TU_HOUR) {
        hour = ((long)p[index++]);
	if (end > TU_HOUR)
	    strcat( fmt, "%0.*d:" );
	else
	    strcat( fmt, "%0.*d" );
	parts[part++] = hour;
    } else {
        hour = 0;
    }
    if (strt == TU_MINUTE) {
        minute = 0;
        while (ndigs-- && scale--)
            minute = minute * 100 + (long)p[index++];
	if (end > TU_MINUTE)
	    strcat( fmt, "%0.*d:" );
	else
	    strcat( fmt, "%0.*d" );
	parts[part++] = minute;
    } else if (strt < TU_MINUTE && end >= TU_MINUTE) {
        minute = ((long)p[index++]);
	if (end > TU_MINUTE)
	    strcat( fmt, "%0.*d:" );
	else
	    strcat( fmt, "%0.*d" );
	parts[part++] = minute;
    } else {
        minute = 0;
    }
    if (strt == TU_SECOND) {
        second = 0;
        while (ndigs-- && scale--)
            second = second * 100 + (long)p[index++];
	if (end > TU_SECOND)
	    strcat( fmt, "%0.*d." );
	else
	    strcat( fmt, "%0.*d" );
	parts[part++] = second;
    } else if (strt < TU_SECOND && end >= TU_SECOND) {
        second = ((long)p[index++]);
	if (end > TU_SECOND)
	    strcat( fmt, "%0.*d." );
	else
	    strcat( fmt, "%0.*d" );
	parts[part++] = second;
    } else {
        second = 0;
    }
    ddigs = 0;
    if (end >= TU_F1) {
        switch (end) {
          case TU_F5:
            fraction = (long)p[index++];
            fraction = fraction * 100 + (long)p[index++];
            fraction = fraction * 100 + (long)p[index++];
            multi = 1;
	    ddigs = 5;
            break;
          case TU_F4:
	      ddigs = 4;
          case TU_F3:
            fraction = (long)p[index++];
            fraction = fraction * 100 + (long)p[index++];
            multi = 100;
	    if (!ddigs)
		ddigs = 3;
            break;
          case TU_F2:
	      ddigs = 2;
          case TU_F1:
            fraction = (long)p[index++];
            multi = 10000;
	    if (!ddigs)
		ddigs = 1;
            break;
        }
	parts[part++] = fraction;
	parts[part++] = ddigs;
	strcat( fmt, "%0.*d" );
    } else
        fraction = 0;
    sprintf( str,
	     fmt,
	     parts[0],
	     parts[1],
	     parts[2],
	     parts[3],
	     parts[4],
	     parts[5],
	     parts[6],
	     parts[7],
	     parts[8],
	     parts[9] );
}

MKSHAR_EOF