/* ******************************************************************* * * * Copyright (c) L-DGO/MIT/JGOFS * * * * * * File : defmet.c * * * * Purpose : * * This program to read met data. met data is an * * instantiation of def data, described as follows: * * From: rpayne@cliff.whoi.edu * Date: Wed, 24 May 95 08:14:02 est * R.V.Endeavor meteorological files (one file per cruise). Data has been * edited to remove obvious spikes. Each file has 3 header records. The * column descriptors are in the third record and are as follows (with the * column format): * * YD F10.5 Decimal year day * HR I3 Hour * MN I3 Minute * LAT F8.4 Latitude (deg N), Magellan 5000DX GPS * LONG F8.4 Longitude (deg W), Magellan * SMG F5.1 Speed made good (m/s), Trimble Navtrac GPS * CMG F5.0 Course made good (deg), Trimble * TWS F5.1 True wind speed (m/s), vector sum of anemometer and ship * Uses larger of port or starboard wind speed * TWD F5.0 True wind direction (deg), vector sum of anemometer and ship * Uses same anemometer as wind speed. Very noisy. * AT F5.1 Air temperature (C) * RH F5.0 Relative humidity (%RH) * BP F7.1 Barometric pressure (mb) * SST F6.1 Sea surface temperature (C), hull contact sensor, depth = 1 m * EDO_Z F6.1 Bottom depth from Edo speed log transducer (m), corrected for * depth of transducer * LW F6.1 Downward long wave irradiance (watts/m^2) * The LW sensor was not added to the Endeavor suite until cruise 263. * (Payne to Groman, 20 Jun 95) * * * Differences from defw: (<= 13 of potential general use; * * > 13 met data only) * * 1) Optional fixed format input. Assumes required decimal * * pts are in fields. Will generate "nd" for blank fields * * as well as fields beyond end of input record * * 2) In fixed format cases, consider input field width when * * making output field width attribute * * 3) Convert all variable names to lower case * * 4) Optionally translate variables according to file spec'd * * in argument list passed to outer by operating system * * See transvar documentation for details * * 5) Return -9999.0 if any char of numeric field bad (not * * just first) * * 6) Optional removal of variables * * This allows replacement of variables with computed * * versions of others. Technique: create a level 0 * * variable list with the computed variable on or below * * the level of all its component variables. Then code * * the computation and ask for removal of the components* * Use the ok_to_calc and ok_to_use functions to check * * that components are in correct position (as well as * * if there is data for them) * * 7) More detailed diagnostic code * * a) Tokens in variable list that start with " must * * end with " * * b) Attributes must end with ] * * c) Various buffer overflow tests * * 8) Modified diagnostic messages for a bit more clarity * * 9) Diagnostics all print with ID lines * * 10) Various calculations * * Generality depends on variable names * * a) W longitude (from input's E longitude) * * Done w/strings-if input begins with +, replace * * w/-. If input begins with -, remove it from * * string. Otherwise, insert - at string start * * Assumes that longitude, after translation, is * * named "lon" * * b) time_gmt (from input's hour and minute) * * Done w/strings-input assumed to be 1 or 2 digits * * each. GMT string is 2 characters of hour, * * leading zero filled if necessary, followed by 2 * * characters of minute, leading zero filled if * * necessary. "nd" is used if either input is * * incorrect. * * Assumes that variable names for hour and minute, * * BEFORE translation, are hr and mn * * 11) Ignore embedded comments anywhere * * 12) No assumption that width attribute is only one ending * * in width * * 13) Ability to specify most buffer sizes from the * * compilation line, defaulting to preset values * * 14) If the first line after the comment lines begins with * * the string FILE:, treat that line as a comment. * * Following line must be " All speeds in m/s", and it, * * too, is treated as a comment. If line following THAT is * * the emet259.dat variable list, that's a comment, too. * * Then check for yet more normally formatted comments. * * 15) If a variable list corresponding to the one in * * emet259.dat is found, embedded, after an embedded * * met comment pair, ignore it, too. * * 16) Various variable name translations (see trans_list) * * 17) Ignore variable Z if it immediately follows variable * * depth_w (which is translated EDO_Z input variable) * * 18) Effectively delete input variables HR & MN. HR is * * removed via removal_list. MN is translated into * * time_gmt, and computed (see above) * * * * Version Number : 1.7 * * * * Revision History : * * * * Date Developer * * ---- --------- */ #define PROGRAM_VERSION "v 1.7 17 Jan 1996" /* 17 Jan 96 v 1.7 WJS * * Do not abort on embedded comments-just ignore * * User-spec'd [width=] attr for free format input, too * * 15 Jan 96 v 1.6 WJS * * Reset transvar file to arg 1000. * * Cannot use ANSI __DATE__ built-in on gb1 (or I don't know how)* * 12 Jan 96 v 1.6 WJS * * Expect setting of major switches from compile line * * Allow setting of most buffer sizes same way * * Tighten definition of met comment-FILE: must be first on * * on line; following line must be exact. * * Diagnose misplaced comments (except embedded met comments) * * Ignore embedded met259 variable list, too, if found after * * a met comment pair * * 10 Jan 96 v 1.6 WJS * * Check for duplicate variables. * * Rework comment processing. * * Reset transvar file to arg 1. Print msg if it's being used * * 10 Jan 96 v 1.5 WJS * * Remove m/s -> kts conversion. * * Remove code that made HR missing * * More transvar work. * * 9 Jan 96 v 1.5 WJS * * Make arg # for transvar file unreasonably large until imple- * * mented. Otherwise, I think that it interferes with html * * interface. * * 6 Jan 96 v 1.5 WJS * * Variable removal code-remove HR * * Have err print out an id line, too * * Break up scanheader into scanheader and scanheader0 * * Err check HR & MN characters * * Begin insertion of transvar hooks * * 30 Dec 95 v 1.4 WJS * * New translation table. * * Prefix mn (translated to time_gmt) with hr, leading zero * * filling if necessary * * Wind speed conversion calculation. * * Modify output width algorithm in fixed field case to be * * the max of input field width and variable width * * Extract some code into functions * * Since makewidth creates width= attrib from integer and * * getwidth extracts and saves integer width from width= * * strings, cut out the middleman. Still need getwidth * * (now getwidth_from_attr) if width= attrib was spec'd * * by user. Appropriate mods to ioattrout_ not to call * * getwidth. * * Bug fix. Fixed-format input NG if variables not in same * * order at different levels * * 28 Dec 95 v 1.4 WJS * * iovalreal_ mod. -9999.0 now returned if any character * * "improper"; not just first * * Test for various buffer overflows * * Test a couple of syntax situations in variable list * * 28 Dec 95 v 1.3 WJS * * Test for #var/#data mismatch in free-field input * * 26 Dec 95 v 1.3 WJS * * Change special defmet comment algorithm. Now looks for * * FILE: in first non-comment line. If found, skips * * that line and next (and looks for more comments) * * Bug fix relating to missing data at lower levels * * 21 Dec 95 v 1.2 WJS * * Incorporate defw 1.2 mods as of this date (except * * those dealing with debug statements). * * Mod to width= test to allow other attribs to end with width * * 21 Dec 95 v 1.1 WJS * * Lower-case all variables * * Change proclabel to makewidth. Set output field width * * to input fixed field width if appropriate * * 8 Dec 95 WJS * * From defw.c vers 1.2; itself from def.c. Comments from * * original source file below. Comments in quotation marks in * * the body of defmet come from outerw's descriptions of inner * * routines. Date of source outerw approx 13 Sep 95 * ******** * * Sat Oct 17 1992 10.00 Glenn Flierl * * Oct 1993 tabattributes * * July 1995 Christine Hammond * * change to add field width determination based on width of * * the label in the data. Preparer must determine max width * * of column from inspection of data column width. * * Ex: event___ , sta__ ,bot * * will give lengths of 8, 5, and 3 for the respective cols * * July 1995 Christine Hammond * * change to allow subfiles to be located in different * * directories from header file * * * ******************************************************************* */ #include #include #include double strtod(); #define TRUE 1 #define FALSE 0 #define PROGRAM_NAME "defmet" #define LEVEL_CONTINUATION_CHAR '>' #define MISSING_VALUE_STRING "nd" #define MISSING_VALUE_REAL -9999. #define DIRSEP '/' /* #define DEBUG */ #define SEPARATOR " \t,\n" #define W_EXTEND '_' #define TRANS_FILE_ARG 1000 /* Which command line arg is translation*/ /* file spec */ #define TRANS_COMMENT_PREFIX "trans>" /* String to prefix comments */ /* from translation file */ #define TRANS_SEPARATORS "= \t" /* Characters which separate variables */ /* from synonyms in translation file */ #ifndef MAXLEVELS #define MAXLEVELS 5 #endif #ifndef TOKEN #define TOKEN 40 /* Approx max len of input tokens */ #endif #define TOKEN_BUF_LEN TOKEN+1 #ifndef NVAR #define NVAR 250 #endif #ifndef COMMENTSIZE #define COMMENTSIZE 2430 #endif #define COMMENTSIZE_BUF_LEN COMMENTSIZE+1 #ifndef DIRSTRING #define DIRSTRING 80 #endif #define DIRSTRING_BUF_LEN DIRSTRING+1 #ifndef SUBFILENAMESIZE #define SUBFILENAMESIZE 80 #endif #define SUBFILENAME_BUF_LEN SUBFILENAMESIZE+1 #ifndef MAXREC #define MAXREC 1257 /* "Magic" constant to me, I fear */ #endif #define MAXREC_BUF_LEN MAXREC+1 /* Params below define differences from defw. DEFMET may be */ /* set to 0 and appropriate others set to 1. Allow these to be */ /* set on compile line */ /* To enable fixed-format input, define string var_data_widths */ /* in the code */ #ifndef DEFMET /* Controls features below, plus 2 non-# comment */ #define DEFMET FALSE /* lines plus conversion of variable edo z to edo_z*/ #endif #ifndef LONG_TO_LON #define LONG_TO_LON DEFMET /* Change long variable name to lon */ #endif #ifndef NEGATE_LON #define NEGATE_LON DEFMET /* Negate lon data */ #endif #ifndef CREATE_TIMEGMT /* Create time_gmt variable from hr */ #define CREATE_TIMEGMT DEFMET /* & mn variable. Set trans_hr & */ #endif /* trans_mn, below */ /* *var are indices of "special" variables. Init to "not found" */ #if CREATE_TIMEGMT int hrvar = -1; /* Location of hour variable */ int mnvar = -1; /* Location of minute variable */ char trans_hr[TOKEN_BUF_LEN]; /* Name of hour variable after trans */ char trans_mn[TOKEN_BUF_LEN]; /* Name of minute variable after trans */ #endif #if NEGATE_LON int lonvar = -1; #endif #if DEFMET char last_tok[TOKEN_BUF_LEN]; /* Part of edo z stuff */ char trans_edo[TOKEN_BUF_LEN];/* Part of edo z stuff */ #endif char fixed_format[MAXLEVELS]; /* Indicates if a level is fixed format */ char first_time_through = TRUE; /* Initialization indicator */ /* Tables below are NOT parallel. Names, widths & attributes have */ /* entries for all possible variables in this object, determined */ /* by the variable list present in level 0. Values table has */ /* holes if variables are missing in a particular free-field */ /* sublevel. The holes are NOT at the index corresponding to */ /* the missing variable. Instead, the holes are at the end of */ /* the list of values at that sublevel. The pointers array maps */ /* between the names, etc arrays and the values array */ int pointers[NVAR]; int inpwidths[NVAR]; /* Input field width for each fixed-format variable */ int inpstarts[NVAR]; /* Input field starts for each fixed-format variable */ int tabwidths[NVAR]; char tabnames[NVAR][TOKEN_BUF_LEN]; char tabattributes[NVAR][TOKEN_BUF_LEN]; char tabvalues[NVAR][TOKEN_BUF_LEN]; /* Add one more level of indirection to allow removal of variables */ /* data_wo_removals is list of pointers w/entries beyond removed */ /* variables "shoved up" one. That is, if we read in 3 variables */ /* and want to remove #2, data_wo_removals[0]=0, */ /* data_wo_removals[1]=2, and data_wo_removals[2] doesn't exist */ /* Because of the indirection, a set of io*__ routines replaced */ /* the io*_ routines. The io*_ routines now call the io*__ */ /* routines with data_wo_removals[*variable] as an arg instead of */ /* *variable. The io*__ are used directly by defmet, since all */ /* variables "in here" "exist" */ int data_wo_removals[NVAR]; int removal_list[1]; /* Big enough to contain indices of all */ /* removed variables, but must exist even if */ /* no removals are done */ char tabcomments[COMMENTSIZE_BUF_LEN]; char *tabcomments_ptr; /* Points to end of comments buffer */ int maxlev; int ncnt; /* Number of variables */ int nvarlevel[MAXLEVELS+1]; /* Not sure why +1 */ int fileopen[MAXLEVELS]; FILE *fl[MAXLEVELS]; char dirstring[DIRSTRING_BUF_LEN]; static char subfilename[SUBFILENAME_BUF_LEN]; /* For fixed format input, create string var_data_widths */ /* w/ format SVSFwW per variable, where */ /* S = any non-whitespace character not in a */ /* variable name (or width!) */ /* V = variable name (after any translation) */ /* Fw= Field width for variable V */ /* W = white space character */ /* Variables can appear in any order; they are permuted */ /* (into array inpwidths) into the order in which */ /* they appear in the data */ /* The total of all widths specified in var_data_widths must */ /* add up to the input record width before any subfile */ /* name. */ #if DEFMET static char var_data_widths[] = "/yrday_gmt/10 /hr/3 /time_gmt/3 /lat/8 /lon/8 \ /speed_trim/5 /course_trim/5 /wind_speed_c/5 /wind_dir_c/5 /temp_air/5 \ /humidity/5 /press_bar/7 /temp_ss1/6 /depth_w/6 /ed_lw/6 "; #else char var_data_widths[] = '\0'; #endif /* */ /************************************************************************/ /* */ err(s,t) char *s,*t; { int i; printf("&x %s%s\n",s,t); for (i=MAXLEVELS;i>=0;i--) if (fileopen[i] == 1) break; if (i < 0) printf ("&x No data files open\n"); else if (i == 0) printf ("&x Lowest level data file open is level 0\n"); else printf ("&x Lowest level data file open is %s at level %i\n", subfilename,i); printf("&x This msg from: %s %s\n",PROGRAM_NAME,PROGRAM_VERSION); exit(1); } char ok_to_calc (var,this_level) int var,this_level; /* Returns true if it's OK to calculate variable var. */ /* It's OK if variable exists and is on this level (hence we need */ /* present level as an input argument) */ { char ok; ok = (var != -1); if (ok) ok = (iovarlevel__(var) == this_level); if (ok) ok = (pointers[var] != -1); return ok; } char ok_to_use (var,this_level) int var,this_level; /* Returns true if it's OK to use variable var. */ /* It's OK if variable exists and is on or above this level (hence */ /* we need present level as an input argument), and is not missing */ { char ok; ok = (var != -1); if (ok) ok = (iovarlevel__(var) <= this_level); if (ok) ok = (pointers[var] != -1); if (ok) ok = ( strcmp(MISSING_VALUE_STRING,tabvalues[pointers[var]]) !=0 ); return ok; } char *lookup (s,wjstbl) char s[],wjstbl[]; /* Looks up string s in a "wjs table". If found, return a pointer */ /* to the "value" corresponding to the key; else return NULL */ /* A "wjs table" is a string consisting of a number of objects. */ /* Each object is of the form */ /* separator_character, */ /* key_string, */ /* separator_character, */ /* value_string, */ /* white_space_characer(s) */ /* The 2 separator chars must be the same, and must not be white */ /* space characters or characters found in any key or value. */ /* There may be multiple blanks */ { char key_search_string[TOKEN+2],*found; int key_len; /* Build string consisting of input string surrounded by */ /* separator characters */ key_search_string[0]=wjstbl[0]; strcpy(key_search_string+1,s); key_len=strlen(s); key_search_string[key_len+1]=wjstbl[0]; key_search_string[key_len+2]='\0'; if ( (found=strstr(wjstbl,key_search_string)) == NULL) return NULL; else return found+key_len+2; } add_to_comment_string (prefix,comment) char *prefix,*comment; /* Copies prefix and comment string to end of tabcomments array, */ /* checking for buffer overflow. */ /* comment normally does not begin with #. Upper level routines */ /* supply a leading # before each comment line. prefix is used */ /* to allow differentiation between comments, if desired */ /* tabcomments_ptr is implicit input; points to present end of */ /* comments string; initialized in ioopen_; altered in iocommout_ */ { char *end_tabcomments_ptr; end_tabcomments_ptr=tabcomments_ptr+strlen(prefix)+strlen(comment); if (end_tabcomments_ptr > tabcomments+COMMENTSIZE) err ("Comment buffer overflow attempting to add comment ",comment); strcpy(tabcomments_ptr,prefix); strcat(tabcomments_ptr,comment); tabcomments_ptr=end_tabcomments_ptr++; return; } int process_comments (buf,stream,prefix) char buf[],prefix[]; FILE *stream; /* Does all reading for defmet (! maybe should have better name!) */ /* Called in 2 modes-null or non-null prefix string */ /* Non-null prefix corresponds to expected comments. Called at */ /* start of a level, with nothing in buf. Read all comments into */ /* tabcomments. Result is single string with embedded newline */ /* characters. Prefix each comment line with prefix argument */ /* Null prefix corresponds to unexpected comments. buf contains */ /* potential comment. Skip all comments */ /* If process_comments returns, its value is 0 if EOF/error; 1 not */ /* If no EOF/error, buf contains the first non-comment line following */ /* the comments */ { #define COMMENT_CHAR '#' static char met_comment_key[] = "FILE:"; static char met_comment_rec2[] = " All speeds in m/s\n"; static char met259_var_list[] = " YD HR MN LAT LONG SMG \ CMG TWS TWD AT RH BP SST EDO_Z\n"; /* Read until there's a record without a leading # */ /* If appropriate, save contents of records */ while (TRUE) { if (fgets(buf,MAXREC,stream) == NULL) return 0; if (buf[0] != COMMENT_CHAR) break; if (prefix != NULL) add_to_comment_string(prefix,buf+1); } #if DEFMET /* defmet- there may be 2 consecutive comment lines, neither of */ /* which begins with #. If so, the first begins with FILE:. Use */ /* that as a key for detection of this anomaly. The second, if */ /* present, is " All speeds in m/s" */ if (strncmp(buf,met_comment_key,strlen(met_comment_key)) == 0) { /* Copy 1st met comment; process 2nd if necessary */ if (prefix != NULL) add_to_comment_string (prefix,buf); if (fgets(buf,MAXREC,stream) == NULL) return 0; if (strcmp(buf,met_comment_rec2) == 0) { if (prefix != NULL) add_to_comment_string (prefix,buf); if (fgets(buf,MAXREC,stream) == NULL) return 0; /* EMET259.DAT also has embedded variable lists (actually, it */ /* has complete embedded headers, which is why the whole */ /* embedded comments thing got written). Ignore them, too */ /* (Make sure what's being ignored is embedded!) */ if ( (strcmp(buf,met259_var_list) == 0) && (prefix == NULL) ) if (fgets(buf,MAXREC,stream) == NULL) return 0; } /* Read any normal (#-preceded) comments after defmet's */ while (buf[0] == COMMENT_CHAR) { if (prefix != NULL) add_to_comment_string(prefix,buf+1); if (fgets(buf,MAXREC,stream) == NULL) return 0; } } #endif return 1; } char *unquote (ptr) char *ptr; /* Logically remove one set of quotation marks, if found */ { if (ptr[0] == '"') { if (ptr[strlen(ptr)-1]=='"') { ptr[strlen(ptr)-1]='\0'; /* Take " off end */ return ++ptr; /* Advance beyond front " */ } else err("Unpaired or improperly paired quotation marks. \nToken = ", ptr); } return ptr; } /***************** Begin scanheader ********************************/ void scanheader(lev) int lev; /* */ /* scanheader reads the portion of a JGOFS-formatted data */ /* file that occurs before the actual data. This portion includes */ /* any comment lines, and the variable list for this data file */ /* and any that occur below it. */ /* Data sets can consist of many data files. The top-most */ /* data file is treated differently from the rest and is processed */ /* by routine scanheader0. */ /* For subfiles of the data set, scanheader records any */ /* comments. It verifies the variable list for the subfile against*/ /* the master variable list. It notes expected variables missing */ /* as well as noting where each expected variable is in this */ /* subfile (variables need not occur in the same order as in the */ /* master list. It then skips over the lists of variables for */ /* levels below the one being processed. There must be as */ /* many lines of variable names as there are subfiles from this */ /* level and below. */ { int i,k; int next_non_missing_var, inp_index; char bad_var_name; char tmp[MAXREC_BUF_LEN],*attr; char *trans_loc; char *tok_ptr,tok[TOKEN_BUF_LEN]; bad_var_name=FALSE; /* Lower levels may be missing variables defined at higher levels */ /* Use pointers array to map between list of all variables and */ /* list found at this level. Start by marking all variables as */ /* missing; then set pointers appropriately as variables are found */ for (i=nvarlevel[lev-1]; i TOKEN) err ("Variable name too long./nName =",tok_ptr); strcpy (tok,tok_ptr); for (i=0; i */ while (TRUE) { inp_index = 0; /* For fixed-field; position within data record */ tok_ptr=strtok(tmp,SEPARATOR); while (tok_ptr != NULL) { tok_ptr = unquote (tok_ptr); save_then_remove_attr(tok_ptr,attrsav); /* What's left is variable name. Copy it into its own buffer. */ /* Need this in case name translation produces longer name. */ /* Didn't do it sooner because variable + attribute might be */ /* too long while neither is too long by itself */ if (strlen(tok_ptr)>TOKEN) err ("Variable name too long.\nName =",tok_ptr); strcpy (tok,tok_ptr); /* Lowercase & translate variable name, checking for duplicates */ for (i=0; i=nvarlevel[i]) i++; this_var_fixed_format = (inpwidths[j] != 0); if (fixed_format[i] == -1) fixed_format[i]=this_var_fixed_format; else if (( fixed_format[i] && !this_var_fixed_format) || (!fixed_format[i] && this_var_fixed_format) ) err ("Mixed fixed/free format input.\nVariable= ",tabnames[j]); } *total_number_variables=ncnt; return maxlev; } /* */ /***************** End scanheader0 *******************************/ void remove_vars (ntotal,data_wo_removals,removals,nremovals) int *ntotal,data_wo_removals[],removals[],nremovals; /* To remove a variable, find its position in the master variable */ /* list. Put this position in removals array. nremovals is the */ /* number of elements in removals-be sure removals is big */ /* enough. Order of entries in removals is irrelevant */ /* pointers array & its size, ncnt, are base data for this function. */ /* At this point, however, pointers[i]=i, so that simplifies things*/ /* Idea is to prepare data_wo_removals, a version of pointers w/o the*/ /* indices in removals */ /* Below the index of a removed variable, the data_wo_removals list */ /* is the same as the pointers list. At and above, data_wo_removals */ /* points to the next higher pointers element */ /* *ntotal is the size of data_wo_removals and (presumably) number */ /* variables returned to outer */ { int i; for (i=0; i= ncnt) err ("Requested removal of variable that doesn't exist. ", "Its index exceeds that of last variable in dataset"); if (removals[i] > 0) data_wo_removals[removals[i]] = -1; } *ntotal=0; for (i=0; i DIRSTRING) err ("Directory string too long.\nString = ",s[0]); strcpy(dirstring,s[0]); tok=strrchr(dirstring,DIRSEP); if (tok==NULL) dirstring[0]='\0'; else *(tok+1)='\0'; fl[0]=fopen(s[0],"r"); if (fl[0] == NULL) err("Bad header file name ",s[0]); s[0][0] = '\0'; fileopen[0]=1; tabcomments_ptr = tabcomments; if (*nparams > TRANS_FILE_ARG) { printf (" *** Warning-using translation file\n"); if (transvaropen(s[TRANS_FILE_ARG]) != 0) err ("Error opening translation file ",s[TRANS_FILE_ARG]); add_to_comment_string("","Using translation file "); add_to_comment_string ("",s[TRANS_FILE_ARG]); s[TRANS_FILE_ARG][0] = '\0'; transvarcomment(tabcomments,TRANS_COMMENT_PREFIX); transvarinit (TRANS_SEPARATORS); } else transvaropen(NULL); /* Get synonyms of special variables (after lower casing) */ #if DEFMET /* Get synonym for EDO variable (after lower casing) and set */ /* last_tok to something not equal to that */ transvar(trans_edo,"edo"); strcpy(last_tok,"not "); strcat(last_tok,trans_edo); #endif #if CREATE_TIMEGMT transvar(trans_hr,"hr"); transvar(trans_mn,"mn"); #endif maxlev=scanheader0(ntotal); #if DEFMET /* Set up to remove HR variable so outer doesn't see it */ removal_list[0]=hrvar; nremovals=1; #else nremovals=0; #endif remove_vars (ntotal,data_wo_removals,removal_list,nremovals,pointers); return maxlev; } /********* Functions called with index of variable. See **************/ /********* data_wo_removals comments up top for reasons **************/ /********* for _ & __ forms of functions **************/ void ioname__(n,s) int n; char *s; { strcpy(s,tabnames[n]); return; } void ioname_(vn,s) int *vn; char *s; /* "Return name (s) corresponding to variable number vn" */ { ioname__(data_wo_removals[*vn],s); return; } int iovarlevel__(n) int n; { int i; for (i=maxlev-1;i>=0;i--) if (n >= nvarlevel[i]) return i+1; return 0; } int iovarlevel_(vn) int *vn; /* "Return level corresponding to variable indexed by vn" */ /* (returns maxlev for indices illegally big; 0 for indices */ /* illegally small) */ { return iovarlevel__(data_wo_removals[*vn]); } int ioattrout__(n,str) int n; char *str; { char *at; /* if there IS an attribute, find one and move next one to */ /* front of string */ if (tabattributes[n][0]){ at=strchr(tabattributes[n],';'); if(at == NULL){ /* this is the only attribute for variable *vn */ strcpy(str,tabattributes[n]); if (strncmp(str,"width=",6) == 0) str = '\0'; /* don't show width attr */ tabattributes[n][0]='\0'; } else { /* there is more than 1 attribute */ *at = '\0'; strcpy(str,tabattributes[n]); if (strncmp(str,"width=",6) == 0) str = '\0'; /* don't show width attr */ strcpy(tabattributes[n],at+1); } return 1; } else return 0; } int ioattrout_(vn,str) int *vn; char *str; /* "Output next attribute for variable indexed by vn. 0=none left"*/ /* Width attribute special - don't return it to the caller */ { return ioattrout__(data_wo_removals[*vn],str); } iovalreal__(n,f) int n; float *f; { int i; char *end_char_ptr; i=pointers[n]; if (i<0) {*f= MISSING_VALUE_REAL; return;} *f=strtod(tabvalues[i],&end_char_ptr); if (*end_char_ptr != '\0') *f= MISSING_VALUE_REAL; #ifdef DEBUG printf("iovalreal %d %f\n",*vn,f); #endif } iovalreal_(vn,f) int *vn; float *f; /* "Return real value (f) for variable indexed by vn. -9999" */ /* "for strings" */ { return iovalreal__(data_wo_removals[*vn],f); } void iovalstr__(n,tmp) int n; char *tmp; { static char *s; int i; i=pointers[n]; if (i<0) {strcpy(tmp,MISSING_VALUE_STRING); return;} s=tabvalues[i]; s=s+strspn(s," "); strcpy(tmp,s); #ifdef DEBUG printf("iovalstr %d %s\n",*vn,tmp); #endif return; } void iovalstr_(vn,tmp) int *vn; char *tmp; /* "Return string value (tmp) for variable indexed by vn." */ { iovalstr__(data_wo_removals[*vn],tmp); return; } int iowidth__(n) int n; { return tabwidths[n]; } int iowidth_(vn) int *vn; /* "Return length of variable field indexed by vn" */ { return iowidth__(data_wo_removals[*vn]); } /* */ /********* End functions called with index of variable. ***************/ void calc_gmt(timegmt_ptr,hr_ptr,mn_ptr) char *timegmt_ptr,*hr_ptr,*mn_ptr; /* Make 4 digit, left-zero filled 24 hr time from up to 2 digs */ /* each of hour and minute. Buffer chars locally to, among other */ /* things, allow output field to overlap input field if desired. */ { char hr_char0,hr_char1,mn_char0,mn_char1; switch (strlen(mn_ptr)) { case 1: mn_char0 = '0'; mn_char1 = *mn_ptr; break; case 2: mn_char0 = *mn_ptr; mn_char1 = *(mn_ptr+1); break; default: err ("Minute null or too long.\nMinute = ",mn_ptr); break; } if ( ! (isdigit(mn_char0) && isdigit(mn_char1) ) ) err ("Non-numeric minute.\nMinute=",mn_ptr); if ( mn_char0 > '5' ) err ("Minute > 59.\nMinute=",mn_ptr); switch (strlen(hr_ptr)) { case 1: hr_char0 = '0'; hr_char1 = *hr_ptr; break; case 2: hr_char0 = *hr_ptr; hr_char1 = *(hr_ptr+1); break; default: err ("Hour null or too long.\nHour = ",hr_ptr); break; } if ( ! (isdigit(hr_char0) && isdigit(hr_char1) ) ) err ("Non-numeric hour.\nHour =",hr_ptr); if ( hr_char0 > '2' || (hr_char0 == '2' && hr_char1 > '3') ) err ("Hour > 23.\nHour =",hr_ptr); *timegmt_ptr =hr_char0; *(timegmt_ptr+1)=hr_char1; *(timegmt_ptr+2)=mn_char0; *(timegmt_ptr+3)=mn_char1; *(timegmt_ptr+4) ='\0'; } /****************** Begin ioreadrec_ *********************************/ /* */ /* ioreadrec is the routine most responsible for reading */ /* and manipulating the data corresponding to the variables set */ /* up in scanheader. */ /* ioreadrec also opens subfiles and calls scanheader to check */ /* them and prepare the variable list for this particular level */ /* (Subfiles need not contain all the variables specified at the */ /* top level) */ /* "Read record at appropriate level. Return 0 if end at that" */ /* "level. Return 1 if ok." */ int ioreadrec_(level) int *level; { int field_term_pos, missing_data_discrepancy, len_tmp; char dummy[2], save_char; /* fmt big enough to contain "%-Ws %1s" string, */ /* where W is width of a variable */ char fmt[12]; char tmp[MAXREC_BUF_LEN],*tok; char *lon_ptr; int *nparams,*ntotal; int i,j,k; int nitem; #ifdef DEBUG printf("ioreadrec %d\n",*level); #endif /* fprintf(stderr,"ioreadrec %d\n",*level); */ k= *level; if (! fileopen[k]) { #ifdef DEBUG printf("open %s\n",subfilename); #endif if((fl[k]=fopen(subfilename,"r")) == NULL)err("Missing file ",subfilename); fileopen[k]=1; scanheader(k); } /* Read records until a non-blank one (actually, one that doesn't */ /* consist exclusively of SEPARATOR characters). If EOF occurs */ /* first, close subfile and return appropriately */ while (TRUE) { if (process_comments(tmp,fl[k],NULL) == 0) { #ifdef DEBUG printf("eof at level %d\n",k); #endif fclose(fl[k]); fileopen[k]=0; return 0; } len_tmp=strlen(tmp); i=strspn(tmp,SEPARATOR); if (i=2 */ /* indicates embedded white space, in turn indicating this */ /* field shorter than expected. Note this could be */ /* due to preceding field longer than expected. */ if (nitem==EOF) strcpy(tabvalues[pointers[i]],MISSING_VALUE_STRING); else if (nitem>=2) err("More than one datum in single fixed field.\nField = ", &tmp[inpstarts[i]]); } } /* Get next token as free format. This should ONLY be a subfile */ /* specification */ if (field_term_pos < len_tmp) tok=strtok(tmp+field_term_pos,SEPARATOR); else tok=NULL; } else { /* Free format input on this level */ tok=strtok(tmp,SEPARATOR); /* In case of missing data, tabvalues will end up with */ /* null strings at its end at this level, I believe */ /* Number of null strings should match number of -1 pointers */ missing_data_discrepancy=0; for(i=j;i TOKEN) err ("Token too long.\nBad token = ",tok); strcpy(tabvalues[i],tok); tok=strtok(NULL,SEPARATOR); } if (missing_data_discrepancy < 0) err("Too few variables in data record.\nRecord = ",tmp); if (missing_data_discrepancy > 0) err("Too many variables in data record.\nRecord = ",tmp); } /* At this point, all variables on this level have been read in */ /* Data processing for "special" variables */ #if NEGATE_LON if (ok_to_calc(lonvar,k)) { i=pointers[lonvar]; if (*tabvalues[i] == '+') *tabvalues[i]='-'; else if (*tabvalues[i] == '-') strcpy(tabvalues[i],tabvalues[i]+1); else { /* Must insert -. If tabvalues already full, drop a digit */ lon_ptr = tabvalues[i] + strlen(tabvalues[i]); if (lon_ptr==tabvalues[i]+TOKEN) *--lon_ptr = '\0'; /* May need to copy string backwards since source & dest */ /* overlap. Leery if strcpy on Sun works this way (weird */ /* intermittent problems) */ while (lon_ptr>=tabvalues[i]) *(lon_ptr+1) = *lon_ptr--; *tabvalues[i] = '-'; } } #endif #if CREATE_TIMEGMT /* Replace old MN field with time_gmt value if possible */ if (ok_to_calc(mnvar,k)) { if (ok_to_use(hrvar,k) && ok_to_use(mnvar,k)) calc_gmt (tabvalues[pointers[mnvar]],tabvalues[pointers[hrvar]], tabvalues[pointers[mnvar]]); else strcpy (tabvalues[pointers[mnvar]],MISSING_VALUE_STRING); } #endif if(k SUBFILENAMESIZE) err ("Subfile name too big. Bad name = ",tok); strcpy(subfilename,tok); } else { if (strlen(tok) + strlen(dirstring) > SUBFILENAMESIZE) err ("Directory + subfile name too big.\nBad name = ",tok); strcpy(subfilename,dirstring); strcat(subfilename,tok); } tok=strtok(NULL,SEPARATOR); } if (tok != NULL) err("Too many variables in data record.\nRecord = ",tmp); return 1; } /* */ /************** End ioreadrec_ **********************************/ ioclose_() /* "Close files" */ { int i; for(i=0;i<=maxlev;i++)if (fileopen[i])fclose(fl[i]); } int iocommout_(str) char *str; /* "Return next comment string. 0=none left." */ { char *at; if (tabcomments[0]){ at=strchr(tabcomments,'\n'); if(at){ *at = '\0'; strcpy(str,tabcomments); strcpy(tabcomments,at+1); tabcomments_ptr=tabcomments+strlen(tabcomments); } else { strcpy(str,tabcomments); tabcomments_ptr=tabcomments; } return 1; } else return 0; } /* */ /************************ End defmet **********************************/ /************************ Start transvar ******************************/ /* */ #define TRANS_LIST_WHITE_SPACE ' ' #define TRANS_LIST_SEPARATOR '/' #define TRANS_LIST_SIZE 10000 char *trans_list_ptr; FILE *trans_stream; /* For translations, create string trans_list */ /* w/ format SVbSVtB per variable, where */ /* S = is any non-whitespace character not in a */ /* variable name (or width!) */ /* Vb= variable name before translation */ /* (will be lowercased by code) */ /* Vt= name to which variable is to be translated */ /* (will be lowercased by code) */ /* B = at least one blank */ /* Variables can appear in any order */ #if DEFMET static char trans_list[TRANS_LIST_SIZE] = "/yd/yrday_gmt /long/lon /smg/speed_trim /cmg/course_trim \ /tws/wind_speed_c /twd/wind_dir_c /at/temp_air /rh/humidity \ /bp/press_bar /sst/temp_ss1 /lw/ed_lw /mn/time_gmt \ /edo/depth_w /edo_z/depth_w \0"; #else char trans_list[TRANS_LIST_SIZE] = '\0'; #endif int transvaropen(translation_file) char *translation_file; { trans_list_ptr=trans_list; if (translation_file==NULL) { trans_stream==NULL; return 0; } else { trans_stream=fopen(translation_file,"r"); if (trans_stream == NULL) return 1; else return 0; } } int transvarcomment(buf,prefix) char *buf,*prefix; { if (process_comments(buf,trans_stream,prefix) == 0) err ("EOF/error before translation file data records",""); return; } int transvarinit(separators) char *separators; { char tmp[MAXREC]; char *tok; if (trans_stream != NULL) { while (TRUE) { if (process_comments(tmp,trans_stream,NULL) == 0) break; else { /* Get name to be translated */ tok=strtok(tmp,separators); *trans_list_ptr++ = TRANS_LIST_SEPARATOR; while (*tok != '\0') *trans_list_ptr++ = *tok++; *trans_list_ptr++ = TRANS_LIST_SEPARATOR; /* Get its synonym */ tok=strtok(NULL,separators); while (*tok != '\0') *trans_list_ptr++ = *tok++; *trans_list_ptr++ = TRANS_LIST_WHITE_SPACE; } } *trans_list_ptr = '\0'; } return; } int transvar(synonym,variable) char *variable,*synonym; { char *trans_loc,*synonym_ptr; int len_synonym; trans_loc=lookup(variable,trans_list); if (trans_loc == NULL) { if (variable != synonym) strcpy (synonym,variable); return 0; } else { synonym_ptr = synonym; while (*trans_loc != ' ') *synonym_ptr++ = *trans_loc++; *synonym_ptr = '\0'; return 1; } }