#include "slalib.h" #include "slamac.h" #include static int idchf ( int, char*, int*, int*, double* ); void slaDfltin ( char *string, int *nstrt, double *dreslt, int *jflag ) /* ** - - - - - - - - - - ** s l a D f l t i n ** - - - - - - - - - - ** ** Convert free-format input into double precision floating point. ** ** Given: ** *string char string containing field to be decoded ** *nstrt int where to start decode (1st = 1) ** ** Returned: ** *nstrt int advanced to next field ** *dreslt double result ** *jflag int -1 = -OK, 0 = +OK, 1 = null field, 2 = error ** ** Called: idchf ** ** Notes: ** ** 1 A tab character is interpreted as a space, and lower ** case d,e are interpreted as upper case. ** ** 2 The basic format is #^.^@#^ where # means + or -, ** ^ means a decimal subfield and @ means D or E. ** ** 3 Spaces: ** Leading spaces are ignored. ** Embedded spaces are allowed only after # and D or E, ** and after . where the first ^ is absent. ** Trailing spaces are ignored; the first signifies ** end of decoding and subsequent ones are skipped. ** ** 4 Field separators: ** Any character other than +,-,0-9,.,D,E or space may be ** used to end a field. Comma is recognized by slaDfltin ** as a special case; it is skipped, leaving the ** pointer on the next character. See 12, below. ** ** 5 Both signs are optional. The default is +. ** ** 6 The mantissa defaults to 1. ** ** 7 The exponent defaults to e0. ** ** 8 The decimal subfields may be of any length. ** ** 9 The decimal point is optional for whole numbers. ** ** 10 A null field is one that does not begin with ** +,-,0-9,.,D or E, or consists entirely of spaces. ** If the field is null, jflag is set to 1 and dreslt ** is left untouched. ** ** 11 nstrt = 1 for the first character in the string. ** ** 12 On return from slaDfltin, nstrt is set ready for the next ** decode - following trailing blanks and (if used) the ** comma separator. If a separator other than comma is ** being used, nstrt must be incremented before the next ** call to slaDfltin. ** ** 13 Errors (jflag=2) occur when: ** a) A +, -, D or E is left unsatisfied. ** b) The decimal point is present without at least ** one decimal subfield. ** c) An exponent more than 100 has been presented. ** ** 14 When an error has been detected, nstrt is left ** pointing to the character following the last ** one used before the error came to light. This ** may be after the point at which a more sophisticated ** program could have detected the error. For example, ** slaDfltin does not detect that '1e999' is unacceptable ** until the whole field has been read. ** ** 15 Certain highly unlikely combinations of mantissa & ** exponent can cause arithmetic faults during the ** decode, in some cases despite the fact that they ** together could be construed as a valid number. ** ** 16 Decoding is left to right, one pass. ** ** 17 End of field may occur in either of two ways: ** a) As dictated by the string length. ** b) Detected during the decode. ** (b overrides a.) ** ** 18 See also slaFlotin and slaIntin. ** ** Last revision: 6 November 1999 ** ** Copyright P.T.Wallace. All rights reserved. */ /* Definitions shared between slaDfltin and idchf */ #define NUMBER 0 #define SPACE 1 #define EXPSYM 2 #define PERIOD 3 #define PLUS 4 #define MINUS 5 #define COMMA 6 #define OTHER 7 #define END 8 { int l_string, nptr, ndigit; double digit; /* Current state of the decode and the values it can take */ int state; #define seek_sign 100 #define neg_mant 200 #define seek_1st_leading_digit 300 #define accept_leading_digit 400 #define seek_digit_when_none_before_pt 500 #define seek_trailing_digit 600 #define accept_trailing_digit 700 #define accept_uns_exp_no_mant 800 #define seek_sign_exp 900 #define neg_exp 1000 #define seek_1st_exp_digit 1100 #define accept_exp_digit 1200 #define end_of_field 1300 #define build_result 1310 #define seeking_end_of_field 1620 #define next_field_OK 1720 #define next_field_default 9100 #define null_field 9110 #define next_field_error 9200 #define error 9210 #define done 9900 int msign, nexp, ndp, isignx, j; double dmant; /* Find string length */ l_string = strlen ( string ); /* Current character index */ nptr = *nstrt - 1; /* Set defaults: mantissa & sign, exponent & sign, decimal place count */ dmant = 0.0; msign = 1; nexp = 0; isignx = 1; ndp = 0; /* Initialize state to "looking for sign" */ state = seek_sign; /* Loop until decode is complete */ while ( state != done ) { switch ( state ) { case seek_sign : /* Look for sign */ switch ( idchf ( l_string, string, &nptr, &ndigit, &digit ) ) { case NUMBER : state = accept_leading_digit; break; case SPACE : state = seek_sign; break; case EXPSYM : state = accept_uns_exp_no_mant; break; case PERIOD : state = seek_digit_when_none_before_pt; break; case PLUS : state = seek_1st_leading_digit; break; case MINUS : state = neg_mant; break; case OTHER : state = next_field_default; break; case COMMA : case END : state = null_field; break; default : state = error; } break; case neg_mant : /* Negative mantissa */ msign = -1; case seek_1st_leading_digit : /* Look for first leading decimal */ switch ( idchf ( l_string, string, &nptr, &ndigit, &digit ) ) { case NUMBER : state = accept_leading_digit; break; case SPACE : state = seek_1st_leading_digit; break; case EXPSYM : state = accept_uns_exp_no_mant; break; case PERIOD : state = seek_digit_when_none_before_pt; break; case PLUS : case MINUS : case COMMA : case OTHER : state = next_field_error; break; case END : default : state = error; } break; case accept_leading_digit : /* Accept leading decimals */ dmant = dmant * 1e1 + digit; switch ( idchf ( l_string, string, &nptr, &ndigit, &digit ) ) { case NUMBER : state = accept_leading_digit; break; case SPACE : state = build_result; break; case EXPSYM : state = seek_sign_exp; break; case PERIOD : state = seek_trailing_digit; break; case PLUS : case MINUS : case COMMA : case OTHER : state = end_of_field; break; case END : state = build_result; break; default : state = error; } break; case seek_digit_when_none_before_pt : /* Look for decimal when none preceded the point */ switch ( idchf ( l_string, string, &nptr, &ndigit, &digit ) ) { case NUMBER : state = accept_trailing_digit; break; case SPACE : state = seek_digit_when_none_before_pt; break; case EXPSYM : case PERIOD : case PLUS : case MINUS : case COMMA : case OTHER : state = next_field_error; break; case END : default : state = error; } break; case seek_trailing_digit : /* Look for trailing decimals */ switch ( idchf ( l_string, string, &nptr, &ndigit, &digit ) ) { case NUMBER : state = accept_trailing_digit; break; case EXPSYM : state = seek_sign_exp; break; case PERIOD : case PLUS : case MINUS : case COMMA : case OTHER : state = end_of_field; break; case SPACE : case END : state = build_result; break; default : state = error; } break; case accept_trailing_digit : /* Accept trailing decimals */ ndp++; dmant = dmant * 1e1 + digit; state = seek_trailing_digit; break; case accept_uns_exp_no_mant : /* Exponent symbol first in field: default mantissa to 1 */ dmant = 1.0; case seek_sign_exp : /* Look for sign of exponent */ switch ( idchf ( l_string, string, &nptr, &ndigit, &digit ) ) { case NUMBER : state = accept_exp_digit; break; case SPACE : state = seek_sign_exp; break; case PLUS : state = seek_1st_exp_digit; break; case MINUS : state = neg_exp; break; case EXPSYM : case PERIOD : case COMMA : case OTHER : state = next_field_error; break; case END : default : state = error; } break; case neg_exp : /* Exponent negative */ isignx = -1; case seek_1st_exp_digit : /* Look for first digit of exponent */ switch ( idchf ( l_string, string, &nptr, &ndigit, &digit ) ) { case NUMBER : state = accept_exp_digit; break; case SPACE : state = seek_1st_exp_digit; break; case EXPSYM : case PERIOD : case PLUS : case MINUS : case COMMA : case OTHER : state = next_field_error; break; case END : default : state = error; } break; case accept_exp_digit : /* Use exponent digit */ nexp = nexp * 10 + ndigit; if ( nexp > 100 ) { state = next_field_error; } else { /* Look for subsequent digits of exponent */ switch ( idchf ( l_string, string, &nptr, &ndigit, &digit ) ) { case NUMBER : state = accept_exp_digit; break; case SPACE : state = build_result; break; case EXPSYM : case PERIOD : case PLUS : case MINUS : case COMMA : case OTHER : state = end_of_field; break; case END : state = build_result; break; default : state = error; } } break; case end_of_field : /* Off the end of the field: move pointer back */ nptr--; case build_result : /* Combine exponent and decimal place count */ nexp = nexp * isignx - ndp; /* Sign of exponent? */ if ( nexp >= 0 ) { /* Positive exponent: scale up */ while ( nexp >= 10 ) { dmant *= 1e10; nexp -= 10; } while ( nexp >= 1 ) { dmant *= 1e1; nexp--; } } else { /* Negative exponent: scale down */ while ( nexp <= -10 ) { dmant /= 1e10; nexp += 10; } while ( nexp <= -1 ) { dmant /= 1e1; nexp++; } } /* Get result & status */ if ( msign == 1 ) { *dreslt = dmant; j = 0; } else { *dreslt = -dmant; j = -1; } case seeking_end_of_field : /* Skip to end of field */ switch ( idchf ( l_string, string, &nptr, &ndigit, &digit ) ) { case SPACE : state = seeking_end_of_field; break; case NUMBER : case EXPSYM : case PERIOD : case PLUS : case MINUS : case OTHER : state = next_field_OK; break; case COMMA : case END : state = done; break; default : state = error; } break; case next_field_OK : /* Next field terminates successful decode */ nptr--; state = done; break; case next_field_default : /* Next field terminates null decode */ nptr--; case null_field : /* Null decode */ j = 1; state = done; break; case next_field_error : /* Next field detected prematurely */ nptr--; case error : /* Decode has failed: set bad status */ j = 2; state = done; break; default : state = error; } } /* Finished: return updated pointer and the status */ *nstrt = nptr + 1; *jflag = j; } static int idchf ( int l_string, char *string, int *nptr, int *ndigit, double *digit ) /* ** - - - - - ** i d c h f ** - - - - - ** ** Internal routine used by slaDfltin: ** ** identify next character in string. ** ** Given: ** l_string int length of string ** string char* string ** nptr int* character to be identified (1st = 0) ** ** Returned: ** nptr int* incremented unless end of field ** ndigit int* 0-9 if character was a numeral ** digit double* (double) ndigit ** ** Returned (function value): ** idchf int vector for identified character: ** ** value meaning ** ** NUMBER 0-9 ** SPACE space or tab ** EXPSYM D, d, E or e ** PERIOD . ** PLUS + ** MINUS - ** COMMA , ** OTHER else ** END outside field ** ** Last revision: 24 June 1996 ** ** Copyright P.T.Wallace. All rights reserved. */ { int ivec, ictab; char c; /* Character/vector tables */ #define NCREC (20) static char kctab[NCREC] = { '0','1','2','3','4','5', '6','7','8','9', ' ','\t', 'D','d','E','e', '.', '+', '-', ',' }; static int kvtab[NCREC] = { NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, SPACE, SPACE, EXPSYM, EXPSYM, EXPSYM, EXPSYM, PERIOD, PLUS, MINUS, COMMA }; /* Initialize returned value */ ivec = OTHER; /* Pointer outside field? */ if ( *nptr < 0 || *nptr >= l_string ) { /* Yes: prepare returned value */ ivec = END; } else { /* Not end of field: identify character */ c = string [ *nptr ]; for ( ictab = 0; ictab < NCREC; ictab++ ) { if ( kctab [ ictab ] == c ) { /* Recognized */ ivec = kvtab [ ictab ]; /* Allow for numerals */ *ndigit = ictab; *digit = (double) *ndigit; /* Quit the loop */ break; } } /* Increment pointer */ ( *nptr )++; } /* Return the value identifying the character */ return ivec; }