    Title: RECIO DESIGN AND DEVELOPMENT NOTES
Copyright: (C) 1994 William Pierpoint
  Version: 2.04
     Date: October 10, 1994



1.0 DATA STRUCTURES

1.1 REC structure for each record stream

    * defined in recio.h.
    * 3 (!dos) or 4 (dos) static RECs for recin, recout, etc (included in 
      ROPEN_MAX count).
    * allocate dynamic array of RECs dimensioned to ROPEN_MAX-NREC in ropen().
    * Each REC has two associated buffers:
      1) record string buffer containing current record;
         allocate when first record read;
         reallocate if record becomes larger.
      2) field string buffer containing current field;
         allocate when first field read;
         reallocate if field becomes larger.
    * deallocate dynamic RECs and associated buffers in rclose() and
      rcloseall() if all record streams closed; deallocate associated
      buffers for recin with an exit function registered with atexit().


1.2 REC r_flags assignments

    Bit	    Description
   -----    -------------------------------------------------------------
     0	    If clear, colno start at 0; if set, colno start at 1
     1      if clear, read mode; if set, write/append mode
     2      if clear, current field not quoted by text char;
            if set, current field quoted by text char
    3-6     Reserved
     7	    If clear, EOF not reached; if set, EOF reached
    8-11    If clear, no error; else rerror number
   12-15    if clear, no warning; else rwarning number


1.3 Accessing REC Members and Associated Buffers

    How do I
    * access the name of the record stream?           rnames()
    * access the current context number?              rcxtno()
    * access the current record number?               rrecno()
    * access the current field number?                rfldno()
    * access the current column number?               rcolno()
    * access the record string buffer?                rrecs()
    * access the field string buffer?                 rflds()
    * determine the number of fields in a record?     rnumfld()
    * determine if column numbers start at 0 or 1?    rbegcolno()
    * determine if the current field was quoted?      ristxtfld()
    * determine if there are more records left?       reof()
    * determine if there is an error on the stream?   rerror()
    * determine if there is a warning on the stream?  rwarning()
    * access the error message for the stream?        rerrstr()
    * force an error on a record stream?              rseterr()
    * clear an error on a record stream?              rclearerr()
    * scan the record buffer more than once?          rresetrec()
    * increase the size of the record string buffer?  rsetrecsiz()
    * increase the size of the field string buffer?   rsetfldsiz()
    * replace the data in the field string buffer?    rsetfldstr()
    * replace the data in the record string buffer?   rsetrecstr()
    * get/set position of a field in a record?        rgetfldpos()/rsetfldpos()
    * set the field delimiter character?              rsetfldch()
    * set the text delimiter character?               rsettxtch()
    * set the context number?                         rsetcxtno()
    * set column numbering to start at 0 or 1?        rsetbegcolno()
    * dynamically copy a string?                      scpys()
    * dynamically concatenate a string?               scats()
    * trim white space from the ends of a string?     strims()



2.0 CODE STRUCTURES

2.1 RECIO Structure Chart

    ͻ                  ͻ
     rstr.c                     recio.c 
    ͼ                  ͼ
                                      
             ͻ     input      output   ͻ
         Ķ rget.c     Ķ rput.c 
       char   ͼ column             char   ͼ column
     delimited        delimited         delimited        delimited
    ͻ      ͻ      ͻ      ͻ
     rgets.c Ĵ    Ķ rcgets.c        rputs.c Ĵ    Ķ rcputs.c 
    ͼ      ͼ      ͼ      ͼ
                                                       
    ͻ      ͻ      ͻ      ͻ
     rgetf.c Ĵ    Ķ rcgetf.c        rputf.c Ĵ    Ķ rcputf.c 
    ͼ      ͼ      ͼ      ͼ
                                                       
    ͻ      ͻ      ͻ      ͻ
     rbget.c     Ķ rcbget.c        rbput.c     Ķ rcbput.c 
    ͼ        ͼ      ͼ        ͼ
                                      
                       ͻ      ͻ        ͻ
                        rwarn.c  Ķ rerr.c  Ķ  rfix.c  
                       ͼ       ͼ        ͼ
                                              
                       ͻ       ͻ
                        rwmsg.c          remsg.c 
                       ͼ       ͼ


2.2 Callback Error Functions

    The callback error function technique used by the RECIO library can
    be applied within your programs to other abstract data types.  For
    abstract data types within the standard C library, you will need to
    write wrapper functions.

                                 ͻ
                                  errnofn 
                                 ͼ
                                      
                        etc
                                                   
                  ͻ    ͻ    ͻ
                   rerrfn       ferrfn       merrfn  
                  ͼ    ͼ    ͼ
		     REC            FILE          MEMORY


2.2.1 Errno Callback Error Function Skeleton [errnofn()]

    switch on error number [errno]
    case out of memory [ENOMEM]
    case out of record or file pointers [EMFILE]
    case permission denied [EACCES]
    case invalid argument [EINVAL]
    default [anything else]
    endcase


2.2.2 RECIO Callback Error Function Skeleton [rerrfn(rp)]

    if valid record pointer [risvalid(rp)]
        if past end of file [reof(rp)]  (if reof test removed, past EOF will
        else [error number set]           become R_EMISDAT or R_WEMPSTR)
            switch on error number [rerror(rp)]
            case read data errors [R_ERANGE || R_EINVDAT || R_EMISDAT]
            case write data errors [R_ENOPUT]
                switch on context number [rcxtno(rp)]
                case RECIN
                    switch field number [rfldno(rp)]
                    case 1 (first field read)
                    case 2 (second field read)
                    ...
                    endcase
                ...
                default [missing or unknown context number]
                endcase
            case out of memory [R_ENOMEM]
            case fatal errors [R_EINVAL || R_EINVMOD]
            default [anything else]
            endcase
        endif
    else [invalid record pointer]
        call error function that handles errno errors [errnofn()]
    endif


2.3 RECIO Callback Warning Function Skeleton [rwarnfn(rp)]

    if valid record pointer [risvalid(rp)]
        switch on warning number [rwarning(rp)]
        case read data warnings [R_WEMPSTR]
        case write data warnings [R_WWIDTH]
        case code runtime warnings [R_WNOREG]
        default [anything else]
        endcase
    endif



3.0 DEVELOPMENT NOTES

3.1 fgets (Microsoft C 5.1)

Previous notes of mine indicate that Microsoft's fgets function does not
work correctly when it reads a line of text that consists of only a newline.
However this can be worked around by first setting the string buffer to an
empty string.  If you plan on retaining the newline, you will need to test
this further.  The fgets function is used twice in the rgetrec function.
If porting to Microsoft C, you may need to implement this fix in rget.c:

     *rrecs(rp) = '\0';  /* just prior to the first fgets (added v1.20) */
     *str = '\0';        /* just prior to the second fgets */


3.2 fopen (Borland C 3.1)

fopen() calls __openfp() calls open().  Borland's "Library Reference"
documents error numbers for open(), but not for fopen().  These error
numbers are ENOENT, EMFILE, EACCES, and EINVACC.  Because ropen() screens
the access code, the EINVACC error should not occur from the recio library.


3.3 strtol & strtoul (Borland C 3.1)

These functions stop consuming input once they overflow, setting ERANGE.
Hence endptr can point into the middle of a sequence of valid characters
having the expected form as given in ANSI X3.159-1989, Sections 4.10.1.5
and 4.10.1.6.  IMHO this characteristic is not in conformance with the
ANSI standard as endptr should only point to the first unrecognized
character or to the terminating null character.  Borland's strtod does
not have this problem.

Note that ANSI X3.159-1989 Section 4.10.1.6 allows strtoul (unsigned
long) to have an optional negative sign.  A negative unsigned long?!
Borland 3.1 strtoul converts a negative long to an unsigned number
without error.  But I prefer to trap any negative numbers input to
unsigned fields.  So str2ul is a wrapper function for strtoul that
first tests for a negative number and if one is found, flags the data
as invalid and returns zero.

The test suite includes -0 as a data value.  The strtol function traps
this as an ERANGE error and returns the overflow limit.  The rfixi and
rfixl functions substitute zero.

3.4 strtod (Borland C 3.1)

Unlike the strtol and strtoul functions, strtod does not clear errno first.
Starting with recio vers. 2.02, errno is cleared before any conversion
where recio code checks the value of errno after the conversion.  It is also
cleared when a record stream is successfully opened.
