Listing 5

/* soffset and related routines.  C/C++.  Author: Stephen D. Williams */
/* Structure packing and unpacking routines, with alignment compensation. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>     /* used to set ints to max/min on over/under flow */
#define yr_of_date(d)   ((int)((d)/10000L))
#define mo_of_date(d)   ((int)(((d)%10000L)/100L))
#define dy_of_date(d)   ((int)(((d)%10000L)%100L))
#define date_of_yrmody(y,m,d)   ((y)*10000L+(m)*100L+(d))
extern long us_date_to_long(char *dates, char **endptr);//in date.cxx
int *chkalign(void)
{
 /* char, short, int, long, float, double, not used: enum, long double */
static int alignarry[8]= {0,0,0,0,0,0,0,0};
   /* if you really have a long double, doubles below should change */
struct { double a; char b; char c; } c;
struct { double a; char b; short s; } s;
struct { double a; char b; int i; } i;
struct { double a; char b; long l; } l;
struct { double a; char b; float f; } f;
struct { double a; char b; double d; } d;

   /* sets number of pad chars to get to boundary */
alignarry[0]=((char *)&c.c-(char *)&c.b)-1;
alignarry[1]=((char *)&s.s-(char *)&s.b)-1;
alignarry[2]=((char *)&i.i-(char *)&i.b)-1;
alignarry[3]=((char *)&l.l-(char *)&l.b)-1;
alignarry[4]=((char *)&f.f-(char *)&f.b)-1;
alignarry[5]=((char *)&d.d-(char *)&d.b)-1;

return(alignarry);
}
int num_fields(const char *str)    /* count non digits/spaces */
{
 int nf = 0;
 while(*str)
   {
   if(isdigit(*str++))
      continue;
//      if(!isspace(*(str-1)))  //  assume any non digit is valid now
                       // watch for 2 digit lengths to use this
      nf++;
   }
 return(nf);
 }
/* gets the offset of the nth item from 0 */
int soffset(const char *str, int n, char *t, int *numitems, int dat_align)
{    // type/size is "c20"
char *endptr;
int num, size = 0;
int ind;
static int *al = NULL;                // only call once

if(!al)
  al = chkalign();
while(n--)
 {
 char cc;
 cc = *str++;
 num = (int)strtol(str, &endptr, 10);
 str = endptr;
 num = num ? num : 1;
 switch(cc)
   {         /* skip char */
   case 'c':
   case 's':
      break;
   case 'i':
   case 'u':
      num *= sizeof(int);
      break;
   case 'D':    /* long date */
   case 'l':
   case 'm':    /* unsigned long */
      num *= sizeof(long);
      break;
   case 'f':
      num* = sizeof(float);
      break;
   case 'd':
      num* = sizeof(double);
      break;
   default:
      return(0);          /* bad format code */
    }
 switch(*str)
   {  /*  look at next char */
   case 's':
   case 'c':
      ind = 0;
      break;
   case 'i':
   case 'u':
      ind = 2;
      break;
   case 'D':   /*  long date */
   case 'l':
   case 'm':
      ind = 3;
      break;  /*  unsigned long */
   case 'f':
      ind = 4;
      break;
   case 'd':
      ind = 5;
      break;
   default:
      ind = 0;     /* either bad format code, or end of string */
   }
 size += num;      /* add size of current element */
  /* this should align to next boundary, if on */
 if (dat_align)
   size += ((al[ind] + 1 - (al[ind] & size)) & al[ind]);
  }
if(t)
  *t = *str;    /*  return type */
str++;
num = (int)strtol(str, &endptr, 10);
str = endptr;
num = num ? num : 1;
if(numitems)
  *numitems = num; /* if passed a pointer, return len */
return(size);
}
 /* converts the data to str */
// fmt is null or "" for not used
void ttos(char *dest, void *src, int offs, char t, int numitems, char *fmt)
 {       /* doesn't check for sprintf overflow */
 int fmtp = (fmt && *fmt);
 switch(t & 0xff)
   {
   case 'c':       /* this adds a null to end of all strings */
     if(fmtp)
        {
        sprintf(dest, fmt, ((char *)src+offs));
        }
     else
       {
        strncpy(dest,((char *)src+offs),numitems);
//            dest[numitems] = 0;    /* delimit string */
        }
    break;
 case 'i':
    sprintf(dest,fmtp ? fmt : "%d",*((int *)((char *)src+offs)));
    break;
 case 'u':
    sprintf(dest,fmtp ? fmt : "%u",*((unsigned *)((char *)src+offs)));
    break;
 case 'l':
    sprintf(dest,fmtp ? fmt : "%ld",*((long *)((char *)src+offs)));
    break;
 case 'm':        /* unsigned long */
    sprintf(dest,fmtp ? fmt : "%lu",*((unsigned long *)((char *)src+offs)));
    break;
 case 'D':        /* long date */
    {
    long t;
    t = *((long *)((char *)src+offs));
    if((t == 0) || (t == -1))
       strcpy(dest, " / / ");
    else
      {
      sprintf(dest,fmtp ? fmt : "%02.2d/%02.2d/%02.2d",
      mo_of_date(t), dy_of_date(t), yr_of_date(t));
      }
    }
    break;
 case 'f':       /* note: prints ieee max significant */
    sprintf(dest,fmtp ? fmt : "%.7g", *((float *) ((char *)src+offs)));
    break;
 case 'd':
    sprintf(dest,fmtp ? fmt : "%.15g", *((double*) ((char *)src+offs)));
    break;
 }
}
 /* converts the str to data, returns success: only for special data types */
int stot(void *dest, char *src, int offs, char t, int numitems)
{       /*  doesn't check for sprintf overflow */
long ttmp;
char *p;

p = (char *)dest + offs;
switch(t & 0xff)
 {
 case 'c':
    strncpy(p,src,numitems);
    break;
 case 'i':
    *((int *)(p)) = (int)(ttmp = strtol(src, (char **)NULL, 10));
    if(ttmp > INT_MAX)
       *((int *)p) = INT_MAX;
    else
      if(ttmp < INT_MIN)
          *((int *)p) = INT_MIN;
    break;
 case 'u':
    *((unsigned *)(p)) = (int)(ttmp = strtoul(src, (char **)NULL, 10));
    if(ttmp > UINT_MAX)
       *((unsigned*)p) = UINT_MAX;
    break;
 case 'l':
    *((long *)(p)) = strtol(src, (char **)NULL, 10);
    break;
 case 'm':       /* unsigned long */
    *((unsigned long *)(p)) = strtoul(src, (char **)NULL, 10);
    break;
 case 'D':       /* long date */
    *((long *)(p)) = us_date_to_long(src, (char **) 0);
    if (*(long *)p == -1l)
       return(-1);  /*  bad date */
    break;
 case 'f':
    *((float *)(p)) = (float)strtod(src, (char **)NULL);
    break;
 case 'd':
    *((double *)(p)) = strtod(src, (char **)NULL);
    break;
 }
return(0);           /* good conversion */
}
int num_chars_display(char type, int numitems)
{
 int num = 1;

 switch(type & 0xff)
   {        /* skip char */
   case 'c':
   case 's':
      num = numitems;
      break;
   case 'i':
      num = 6;
      break;
   case 'u':
      num = 5;
      break;
   case 'D':   /* long date */
      num = 8;
      break;
   case 'l':
      num = 11;
      break;
   case 'm':   /* unsigned long */
      num = 10;
      break;
   case 'f':
      num = 16;
      break;
   case 'd':
      num = 24;
      break;
   default:
       num = 8;        /* bad format code */
   }
return(num);
}