/* ******************************************************************* * * * Copyright (c) MIT/JGOFS * * * * * * File : nm.c * * * * Purpose : * * * * Version Number : 1.7a * * * * Revision History : * * * * Date Developer * * ---- --------- */ #define NM_VERSION "nm version 1.7a 4 Jun 2005" /* 4 Jun 04 v 1.7a WJS * * Need some "void"s & some "putting functions at the top" * * [Begin 1.7a] * * 13 Jul 04 v 1.7 WJS * * Account for defgb_utils->utils * * defgb.h->utils.h & path_info_routines.h * * startchild_defgb->startchild * * Recode err to use add_id_to_err; expect errn from library * * Add "version function" * * Add iovaldouble_ entry * * [Needs utils 1.9; utils.h 1.1] * * 16 Jan 04 v 1.7 WJS * * Add "exec" capability (if "input file" is enclosed in * * parens, assume input file is command w/params; open pipe * * to it, etc). Steal most code from defgb, hence need for * * many new routines in make file... * * Emit "big" error messages * * Parametrize a few things like "nd" * * Put comments in inverse time order. If dates same, invert * * order. * * Change version to 1.7. Version that was released with * * "JGOFS 1.5 release" was numbered 1.5. So was version * * extant BEFORE "JGOFS 1.5 release". ? Allow one of them to * * be considered 1.6? Anyway... * * [Needs defgb_utils 1.5] * * [Needs defgb.h 5.0 (which needs core.h & path_info.h)] * * [Begin 1.7 - in use on globec as testnm] * * * * June 1997 grf * * changed sub names to in_xxxx, fixes for v 1.5 * * * * May 1997 Glenn Flierl * * changed error calls to use error_ in outer.c * * * * November 1996 Christine Hammond * * proclabel change - place separating ';' AFTER first attrib * * * * November 1996 Warren Sass, Christine Hammond * * ioattrout change - remove code after getwidth() call whose * * comment stated: don't show the width attribute * * code was: str = '\0'; * * we want outer to determine what gets shown. * * * * March 1996 Warren Sass, Christine Hammond * * use a .h include file to set sizes of buffers used * * NOTE: 'minbufsize.h' also included in outer.c, to achieve * * some correspondence. User-specified include file allowed * * (see minbufsize.h) as a compile switch IOBUF_INCLUDE * * * * March 1996 Christine Hammond * * fix in_proclabel (width sensing) to ignore attributes with a* * substring 'width=', ex: 'inpwidth=N', and to properly deal * * with a 'width=N' attr when it occurs after a ';', as in * * a series of attributes, ex: 'meters;color=pink;width=12' * * * * January 1996 Christine Hammond * * 'type' the functions that outer method calls to avoid * * warnings on ANSI compilers - void() used for functions * * returning no value (e.g., return;) * * * * 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 * * * * Oct 1993 attr[][] * * Sat Oct 17 1992 10.00 Glenn Flierl * ******************************************************************* */ /* #define DEBUG */ #include INNEROPTIONS #include "utils.h" /* There are a zillion default separator strings defined */ /* None of them are this one. Sigh. No real problem. Using defgb */ /* as an example, since defgb ultimately allows user to spec the */ /* string, so we can pretend this is the string user spec'd */ #define SEPARATOR " ,=\t\n" /* outer routine */ void error_(); /* utils routines */ void analy_source(); char *buildstring(); char *strdupl(); Logical add_id_to_err(); #if READ_COMMANDS char *startchild(); #endif char names[NVAR][VARNAMESIZE]; char values[NVAR][DATUMSIZE]; char attr[NVAR][ATTRSIZE]; struct fileinfo file; int fldwidths[NVAR]; int map[NVAR]; int nheader, ndata; FILE *fl; int maxlev,newlev; int eofflag=0; char str[INBUFSIZE],*strp; char tabcomments[COMMENTSIZE]; /* * * * * * end of declarations * * * * * */ char *nm_return_vers() /* Dummy routine. Exists only to force .h file version string into */ /* this module. Note string must not be global or we'll have con- */ /* flicts if another routine similarly includes the version string */ { static char version[] = NM_VERSION"/"FULL_UTILSH_VERSION; return version; } void err(s,t) char *s,*t; { char *ss,*tt; add_id_to_err(&ss,&tt,s,t,NM_VERSION); error_(ss,tt); return; /* Not that it should ever get here... */ } void in_getwidth(vn, ptr) int *vn; char *ptr; { char *p; int i,len; i = *vn; p = strchr(ptr,'='); sscanf(p+1,"%d", &len); fldwidths[i] = len; return; } void in_striptoken(p) char *p; { char *tp; int i; /* if there are extending characters at end of string, strip them */ i = strlen(p); while ( (tp = strrchr(p,W_EXTEND)) == p+i-1) { *tp = '\0'; /* shorten string by that one extend char */ i = strlen(p); }; return; } void in_proclabel(n, ptr) int n; char *ptr; /* attr[] is the global array. routine to determine the length of the title string indexed at 'n' and pointed to by 'ptr', and based on the idea that the user preparing the data has placed W_EXTEND characters to pad the title to the desired width. An attribute is constructed and saved in attr[], either by appending to existing attr's or saving as the sole attribute, beginning with 'width='. Then, the W_EXTEND characters must be stripped from the names. Same name separators are used as before (space, tab, comma, newline). If an explicit 'width=' attribute is found, it is used. */ { int len, docalc, others; char attrib[ATTRSIZE], *attrptr; /* default value for flag indicating attributes other than 'width=' */ others = FALSE; /* calculate a field width, if no attributes are present or, if none of the attributes are exactly == 'width=N' */ docalc = TRUE; attrptr = attr[n]; while (*attrptr) { if (strncmp(attrptr,"width=",6) == 0) docalc = FALSE; if ((attrptr = strchr(attrptr,';')) != NULL){ attrptr++; others = TRUE; } } if (docalc == TRUE) { len = strlen(ptr); /* length of (possibly) extended label */ sprintf(attrib, "width=%d", len); if (others == TRUE) { /* add to end of other attributes? */ strcat(attr[n],";"); strcat(attr[n],attrib); } else strcpy(attr[n],attrib); }; /* end docalc loop */ /* strip off the column extenders from the label */ in_striptoken(ptr); /* and save label name without the extra W_EXTEND characters */ strcpy(names[n], ptr); return; } int in_fg(str) char *str; { *str = 0; while(*str == 0){ if(fgets(str,INBUFSIZE-1,fl) == NULL) return 1; if(strspn(str,SEPARATOR)==strlen(str))*str = 0; }; return 0; } int in_readdata(flag) int flag; { int state,nn,i; char *pstr; state=1; nn=0; if(*str == 0) if(in_fg(str))return -1; if (str[0] == '#'){ *tabcomments=0; while (str[0] == '#') { strcat(tabcomments,str+1); if (in_fg(str)) return -1; }; }; if(flag<=1) while(strchr(str,'=')==NULL) if(in_fg(str))return -1; while(strchr(str,'=')){ state=2; strp=strtok(str,SEPARATOR); while(strp){ if(flag==0){ if(pstr=strchr(strp,'[')){ *(pstr+strlen(pstr)-1)=0; strcpy(attr[nheader],pstr+1); *pstr=0; } else attr[nheader][0]=0; /* routine to determine field width and assign field name */ in_proclabel(nheader, strp); nn=nheader++; } else { if(pstr=strchr(strp,'['))*pstr=0; for(nn=0;nn= TRACE_PERFILE_ROUTINES) { tmp = buildstring("open ",file->source,NULL, " building trace msg"); do_diag_trace (TRACE_PERFILE_ROUTINES,tmp); free (tmp); } */ errno = 0; /* Be sure that errno errors are ours! */ switch (file->source_type) { case COMMAND_FILE: #if READ_COMMANDS /* If a method is run by the command file, it will look */ /* at the PATH_INFO string. Ideally, the command file it- */ /* self would set PATH_INFO appropriately. However, */ /* we sometimes have a command itself, not a command file. */ /* Tough choice, since presumably command file might want */ /* to know PATH_INFO, but replace proto string with "none" */ /* (and drop options - see ioopen_ where none_putenv string */ /* is made). My decision at this point... none has no */ /* meaning to system. */ /* For nm 1.7, short circuit this ... */ if (PATH_INFO_none_putenv != NULL) if ( (i = putenv(PATH_INFO_none_putenv)) != 0 ) err ("putenv failure for PATH_INFO: ",strerror(i)); tmp = startchild (file->source, NULL, &file->stream); /* Not sure if bad startchild result would always give */ /* bad return status */ if ( (file->stream == NULL) && (tmp == NULL) ) tmp = "Reason unknown"; if (tmp != NULL) err ( buildstring("Error opening data script", file->source, " : \n ", " open_datafile-script msg"), tmp ); file->open=TRUE; /* Restore original PATH_INFO if we changed it */ if (PATH_INFO_none_putenv != NULL) if ( (i = putenv(PATH_INFO_orig_putenv)) != 0 ) err ("putenv failure restoring PATH_INFO: ",strerror(i)); #else err("Method not compiled with command-reading capability,\n\ needed to read data from script ",file->source); #endif break; case DATA_FILE: if ( (file->stream = fopen(file->source,"r")) == NULL ) err ( buildstring("Error opening data file ", file->source, " : \n ", " open datafile msg"), strerror(errno) ); file->open=TRUE; break; default: tmp = buildstring( "Attempt to open illegal file type as data file\n File type = ", file->descrip, "\n File source type = ", " building open_datafile err string"); *one_char_buf = file->source_type; err (tmp, one_char_buf); free (tmp); break; } file->eod=FALSE; file->eof=FALSE; file->nrecs=0; /* A bit deceptive. Counts between EODs. If */ /* >1 dataset comes from a single file, this is */ /* NOT the count on the whole file */ return; } int ioopen_(s,nparams,ntotal) char *s[]; int *nparams,*ntotal; { char tmp[INBUFSIZE],*tok; int ncnt,i,j; file.stream=NULL; file.source=NULL; file.source_type=ILLEGAL_SOURCE; file.descrip="datafile"; file.open=FALSE; file.eof=FALSE; file.eod=FALSE; file.nrecs=0; file.entry_separators=NULL; file.item_separators="\n\t"; /* Now used only to parse input command */ file.item_alt_separators[0] = '\0'; /* at some point could allocate file.buf,.buf_size; */ /* at same time switch to using file.eof */ strdupl(&file.source,s[0]," saving level 0 source info"); analy_source(&file,SEPARATOR,NULL); open_datafile(&file); fl = file.stream; s[0][0] = 0; str[0]=0; strp=str; nheader=0; ndata=0; *tabcomments=0; if(in_readdata(0)<=0)err("file structure",""); *ntotal=nheader+ndata; if (nheader>0){ newlev=3; return 1; } else { newlev=2; return 0; } } void ioname_(vn,s) int *vn; char *s; { strcpy(s,names[*vn]); } int iovarlevel_(vn) int *vn; { if(*vn