1 #include "niml_private.h"
2 
3 /****************************************************************************/
4 /************* Debugging function *******************************************/
5 /****************************************************************************/
6 
7 FILE *dfp = NULL ;                  /* debug file pointer */
8 
NI_dpr(char * fmt,...)9 void NI_dpr( char *fmt , ... )      /* print debug stuff */
10 {
11   va_list vararg_ptr ;
12   char *nms ;
13   if( dfp == NULL ) return ;        /* printing turned off? */
14   va_start( vararg_ptr , fmt ) ;
15   vfprintf(dfp,fmt,vararg_ptr) ;    /* do printing */
16 
17   nms = NI_malloc_status() ;        /* 18 Nov 2002 */
18   if( nms != NULL ) fprintf(dfp,"     NI_malloc status: %s\n",nms) ;
19 
20   fflush(dfp); va_end(vararg_ptr);  /* cleanup */
21 }
22 
23 /****************************************************************************/
24 /*********************** NIML Utility functions *****************************/
25 /****************************************************************************/
26 
27 /*--------------------------------------------------------------------------*/
28 /*! Return the file length (-1 if file not found).
29 ----------------------------------------------------------------------------*/
30 
NI_filesize(char * pathname)31 int64_t NI_filesize( char *pathname )
32 {
33    static struct stat buf ; int ii ;
34    if( pathname == NULL ) return -1 ;
35    ii = stat( pathname , &buf ) ; if( ii != 0 ) return -1 ;
36    return (int64_t)buf.st_size ;
37 }
38 
39 /*--------------------------------------------------------------------------*/
40 /*! Is this a FIFO or not? [27 Aug 2019]
41 ----------------------------------------------------------------------------*/
42 
NI_is_fifo(char * pathname)43 int NI_is_fifo( char *pathname )
44 {
45    static struct stat buf ; int ii ;
46    if( pathname == NULL ) return 0 ;
47    ii = stat( pathname , &buf ) ; if( ii != 0 ) return 0 ;
48    return (buf.st_mode & S_IFIFO) ;
49 }
50 
51 /*-------------------------------------------------------------------*/
52 /*!  Sleep a given # of milliseconds (uses the Unix select routine).
53 ---------------------------------------------------------------------*/
54 
NI_sleep(int msec)55 void NI_sleep( int msec )
56 {
57    struct timeval tv ;
58    if( msec <= 0 ) return ;             /* can't wait into the past */
59    tv.tv_sec  = msec/1000 ;
60    tv.tv_usec = (msec%1000)*1000 ;
61    select( 1 , NULL,NULL,NULL , &tv ) ;
62    return ;
63 }
64 
65 /*---------------------------------------------------------------*/
66 /*! Return time elapsed since first call to this routine (msec).
67 
68     Note this will overflow an int after 24+ days.  You probably
69     don't want to use this if the program will be running
70     continuously for such a long time.
71 -----------------------------------------------------------------*/
72 
NI_clock_time(void)73 int NI_clock_time(void)
74 {
75    struct timeval  new_tval ;
76    struct timezone tzone ;
77    static struct timeval old_tval ;
78    static int first = 1 ;
79 
80    gettimeofday( &new_tval , &tzone ) ;
81 
82    if( first ){
83       old_tval = new_tval ;
84       first    = 0 ;
85       return 0 ;
86    }
87 
88    if( old_tval.tv_usec > new_tval.tv_usec ){
89       new_tval.tv_usec += 1000000 ;
90       new_tval.tv_sec -- ;
91    }
92 
93    return (int)( (new_tval.tv_sec  - old_tval.tv_sec )*1000.0
94                 +(new_tval.tv_usec - old_tval.tv_usec)*0.001 + 0.5 ) ;
95 }
96 
97 /*---------------------------------------------------------------------------*/
98 /*! Replacement for mktemp(). */
99 
NI_mktemp(char * templ)100 char * NI_mktemp( char *templ )
101 {
102    int nt ; char *xx,*uu ; struct stat buf ;
103 
104    if( templ == NULL || templ[0] == '\0' ) return NULL ;
105 
106    nt = strlen(templ) ;
107    if( nt < 6 ){ templ[0] = '\0'; return NULL; }
108    xx = templ+(nt-6) ;
109    if( strcmp(xx,"XXXXXX") != 0 ){ templ[0] = '\0'; return NULL; }
110 
111    while(1){
112      uu = UUID_idcode() ;
113      memcpy( xx , uu , 6 ) ;
114      nt = stat( templ , &buf ) ;
115      if( nt != 0 ) return templ ;
116    }
117 }
118 
119 /*************************************************************************/
120 /****************** NIML string utilities ********************************/
121 /*************************************************************************/
122 
123 /*--------------------------------------------------------------------------*/
124 /*! Like strncpy, but better (result always ends in NUL char).
125 
126     If dest is NULL, does nothing.  If src is NULL, put a NUL char
127     in dest[0].
128 ----------------------------------------------------------------------------*/
129 
NI_strncpy(char * dest,const char * src,size_t n)130 char * NI_strncpy( char *dest , const char *src , size_t n )
131 {
132    if( dest == NULL || n == 0 ) return NULL ;
133    if( src  == NULL || n == 1 ){ dest[0] = '\0' ; return dest ; }
134    strncpy( dest , src , n-1 ) ;
135    dest[n-1] = '\0' ; return dest ;
136 }
137 
138 /*------------------------------------------------------------------------*/
139 /*! Like strlen, but better (input=NULL ==> output=0).
140 --------------------------------------------------------------------------*/
141 
NI_strlen(char * str)142 int NI_strlen( char *str )
143 {
144    if( str == NULL ) return 0 ;
145    return strlen(str) ;
146 }
147 
148 /*------------------------------------------------------------------------*/
149 /*! Like strdup, but better (input=NULL ==> output=NULL).
150 --------------------------------------------------------------------------*/
151 
NI_strdup(char * str)152 char * NI_strdup( char *str )
153 {
154    int nn ; char *dup ;
155    if( str == NULL ) return NULL ;
156    nn = NI_strlen(str); dup = NI_malloc(char, nn+1); strcpy(dup,str); return dup;
157 }
158 
159 /*------------------------------------------------------------------------*/
160 /*! Like NI_strdup, but relies on length, not nul      21 Jun 2006 [rickr]
161 --------------------------------------------------------------------------*/
162 
NI_strdup_len(char * str,int len)163 char * NI_strdup_len( char *str, int len )
164 {
165    char *dup ;
166    if( str == NULL || len < 0 ) return NULL ;
167    dup = NI_malloc(char, len+1) ; strncpy(dup,str,len) ; dup[len] = '\0' ;
168    return dup;
169 }
170 
171 /*------------------------------------------------------------------------*/
172 /*! Find a string in an array of strings; return index (-1 if not found).
173 --------------------------------------------------------------------------*/
174 
string_index(char * targ,int nstr,char * str[])175 int string_index( char *targ, int nstr, char *str[] )
176 {
177    int ii ;
178 
179    if( nstr < 1 || str == NULL || targ == NULL ) return -1 ;
180 
181    for( ii=0 ; ii < nstr ; ii++ )
182      if( str[ii] != NULL && strcmp(str[ii],targ) == 0 ) return ii ;
183 
184    return -1 ;
185 }
186 
187 /*------------------------------------------------------------------------*/
188 /*! Un-escape a C string inplace.  (This can be done since the replacement
189     is always smaller than the input.)  Escapes recognized are:
190       -  &lt;   ->  <
191       -  &gt;   ->  >
192       -  &quot; ->  "
193       -  &apos; ->  '
194       -  &amp;  ->  &
195     Also replace CR LF pair (Microsoft), or CR alone (Macintosh) with
196     LF (Unix), per the XML standard.
197     Return value is number of replacements made.
198 --------------------------------------------------------------------------*/
199 
200 #undef  CR
201 #undef  LF
202 #define CR 0x0D
203 #define LF 0x0A
204 
unescape_inplace(char * str)205 int unescape_inplace( char *str )
206 {
207    int ii,jj , nn,ll ;
208 
209    if( str == NULL ) return 0 ;                /* no string? */
210    ll = NI_strlen(str) ;
211 
212    /* scan for escapes: &something; */
213 
214    for( ii=jj=nn=0 ; ii<ll ; ii++,jj++ ){ /* scan at ii; put results in at jj */
215 
216       if( str[ii] == '&' ){  /* start of escape? */
217 
218               if( ii+3 < ll        &&   /* &lt; */
219                   str[ii+1] == 'l' &&
220                   str[ii+2] == 't' &&
221                   str[ii+3] == ';'   ){ str[jj] = '<' ; ii += 3 ; nn++ ; }
222 
223          else if( ii+3 < ll        &&   /* &gt; */
224                   str[ii+1] == 'g' &&
225                   str[ii+2] == 't' &&
226                   str[ii+3] == ';'   ){ str[jj] = '>' ; ii += 3 ; nn++ ; }
227 
228          else if( ii+5 < ll        &&   /* &quot; */
229                   str[ii+1] == 'q' &&
230                   str[ii+2] == 'u' &&
231                   str[ii+3] == 'o' &&
232                   str[ii+4] == 't' &&
233                   str[ii+5] == ';'   ){ str[jj] = '"' ; ii += 5 ; nn++ ; }
234 
235          else if( ii+5 < ll        &&   /* &apos; */
236                   str[ii+1] == 'a' &&
237                   str[ii+2] == 'p' &&
238                   str[ii+3] == 'o' &&
239                   str[ii+4] == 's' &&
240                   str[ii+5] == ';'   ){ str[jj] = '\'' ; ii += 5 ; nn++ ; }
241 
242          else if( ii+4 < ll        &&  /* &amp; */
243                   str[ii+1] == 'a' &&
244                   str[ii+2] == 'm' &&
245                   str[ii+3] == 'p' &&
246                   str[ii+4] == ';'   ){ str[jj] = '&' ; ii += 4 ; nn++ ; }
247 
248          /* although the comments above don't mention it,
249             we also look for XML style numeric escapes
250             of the forms &#32; (decimal) and &#xfd; (hex) */
251 
252          else if( ii+3 < ll        &&
253                   str[ii+1] == '#' &&
254                   isdigit(str[ii+2]) ){   /* &#dec; */
255 
256             unsigned int val='?' ; int kk=ii+3 ;
257             while( kk < ll && str[kk] != ';' ) kk++ ;
258             sscanf( str+ii+2 , "%u" , &val ) ;
259             str[jj] = (char) val ; ii = kk ; nn++ ;
260          }
261 
262          else if( ii+4 < ll        &&
263                   str[ii+1] == '#' &&
264                   str[ii+2] == 'x' &&
265                   isxdigit(str[ii+3]) ){   /* &#hex; */
266 
267             unsigned int val='?' ; int kk=ii+4 ;
268             while( kk < ll && str[kk] != ';' ) kk++ ;
269             sscanf( str+ii+3 , "%x" , &val ) ;
270             str[jj] = (char) val ; ii = kk ; nn++ ;
271          }
272 
273          /* didn't start a recognized escape, so just copy as normal */
274 
275          else if( jj < ii ){ str[jj] = str[ii] ; }
276 
277       } else if( str[ii] == CR ) {  /* is a carriage return */
278 
279          if( str[ii+1] == LF ){ str[jj] = LF ; ii++ ; nn++ ; }  /* CR LF */
280          else                 { str[jj] = LF ;      ; nn++ ; }  /* CR only */
281 
282       } else { /* is a normal character, just copy to output */
283 
284               if( jj < ii ){ str[jj] = str[ii] ; }
285       }
286 
287       /* at this point, ii=index of last character used up in scan
288                         jj=index of last character written to (jj <= ii) */
289 
290    } /* end of loop scanning over input/output string */
291 
292    if( jj < ll ) str[jj] = '\0' ; /* end string properly */
293 
294    return nn ;
295 }
296 
297 /*------------------------------------------------------------------------*/
298 /*! Quotize (and escapize) one string, returning a new string.
299     Approximately speaking, this is the inverse of unescape_inplace().
300 --------------------------------------------------------------------------*/
301 
quotize_string(char * str)302 char * quotize_string( char *str )
303 {
304    int ii,jj , lstr,lout ;
305    char *out ;
306 
307    lstr = NI_strlen(str) ;
308    if( lstr == 0 ){ out = NI_malloc(char, 4); strcpy(out,"\"\""); return out; }
309    lout = 8 ;                      /* length of output */
310    for( ii=0 ; ii < lstr ; ii++ ){ /* count characters for output */
311       switch( str[ii] ){
312          case '&':  lout += 5 ; break ;  /* replace '&' with "&amp;" */
313 
314          case '<':
315          case '>':  lout += 4 ; break ;  /* replace '<' with "&lt;" */
316 
317          case '"' :
318          case '\'': lout += 6 ; break ;  /* replace '"' with "&quot;" */
319 
320          case CR:
321          case LF:   lout += 6 ; break ;  /* replace CR with "&#x0d;"
322                                                     LF with "&#x0a;" */
323 
324          default: lout++ ; break ;      /* copy all other chars */
325       }
326    }
327    out = NI_malloc(char, lout) ;              /* allocate output string */
328    out[0] = '"' ;                       /* opening quote mark */
329    for( ii=0,jj=1 ; ii < lstr ; ii++ ){
330       switch( str[ii] ){
331          default: out[jj++] = str[ii] ; break ;  /* normal characters */
332 
333          case '&':  memcpy(out+jj,"&amp;",5)  ; jj+=5 ; break ;
334 
335          case '<':  memcpy(out+jj,"&lt;",4)   ; jj+=4 ; break ;
336          case '>':  memcpy(out+jj,"&gt;",4)   ; jj+=4 ; break ;
337 
338          case '"' : memcpy(out+jj,"&quot;",6) ; jj+=6 ; break ;
339 
340          case '\'': memcpy(out+jj,"&apos;",6) ; jj+=6 ; break ;
341 
342          case CR:   memcpy(out+jj,"&#x0d;",6) ; jj+=6 ; break ;  /* 15 Oct 2002 */
343          case LF:   memcpy(out+jj,"&#x0a;",6) ; jj+=6 ; break ;
344       }
345    }
346    out[jj++] = '"'  ;  /* closing quote mark */
347    out[jj]   = '\0' ;  /* terminate the string */
348    return out ;
349 }
350 
351 /*------------------------------------------------------------------------*/
352 /*! Quotize an array of strings into one string,
353     separating substrings with sep (setting sep=0 means use commas).
354 --------------------------------------------------------------------------*/
355 
quotize_string_vector(int num,char ** str,char sep)356 char * quotize_string_vector( int num , char **str , char sep )
357 {
358    char *out , **qstr ;
359    int ii , ntot , ll ;
360 
361    /* handle special cases */
362 
363    if( num <= 0 || str == NULL )
364       return quotize_string(NULL) ;      /* will be string of 2 quotes */
365 
366    if( num == 1 )
367       return quotize_string( str[0] ) ;  /* just quotize the only string */
368 
369    /* default separator */
370 
371    if( sep == '\0' ) sep = ',' ;
372 
373    /* temp array for quotized individual sub-strings */
374 
375    qstr = NI_malloc(char*, sizeof(char *)*num) ;
376 
377    for( ntot=ii=0 ; ii < num ; ii++ ){       /* quotize each input string */
378       qstr[ii] = quotize_string( str[ii] ) ;
379       ntot += NI_strlen( qstr[ii] ) ;      /* length of all quotized strings */
380    }
381 
382    /* make output, put 1st sub-string into it */
383 
384    out = NI_malloc(char, ntot) ;
385    strcpy( out , qstr[0] ) ; NI_free(qstr[0]) ;
386    for( ii=1 ; ii < num ; ii++ ){
387       ll = strlen(out) ;  /* put separator at end of output string, */
388       out[ll-1] = sep ;   /* in place of the closing " mark.       */
389 
390       strcat(out,qstr[ii]+1) ;  /* catenate with next sub-string, */
391                                 /* but skip the opening " mark.  */
392       NI_free(qstr[ii]) ;       /* toss the quotized trash */
393    }
394 
395    NI_free(qstr) ; return out ;
396 }
397 
398 /*------------------------------------------------------------------------*/
399 /*! Quotize a bunch of ints int a string like "1,32,-12".
400 --------------------------------------------------------------------------*/
401 
quotize_int_vector(int num,int * vec,char sep)402 char * quotize_int_vector( int num , int *vec , char sep )
403 {
404    int ii , jj ;
405    char *out , **qstr ;
406 
407    if( num <= 0 || vec == NULL )
408       return quotize_string(NULL) ;
409 
410    qstr = NI_malloc(char*, sizeof(char *)*num) ;  /* temp array of strings */
411    for( ii=0 ; ii < num ; ii++ ){
412       qstr[ii] = NI_malloc(char, 16) ;           /* max size of printed int */
413       sprintf(qstr[ii],"%d",vec[ii]) ;               /* print int */
414       for( jj=strlen(qstr[ii])-1 ;                   /* clip */
415            jj > 0 && isspace(qstr[ii][jj]) ; jj-- )  /* trailing */
416         qstr[ii][jj] = '\0' ;                        /* blanks */
417    }
418 
419    out = quotize_string_vector( num , qstr , sep ) ;
420 
421    for( ii=0 ; ii < num ; ii++ ) NI_free(qstr[ii]) ;
422 
423    NI_free(qstr) ; return out ;
424 }
425 
426 /*------------------------------------------------------------------------*/
427 /*! Quotize a bunch of floats into a string like "-2.71828,3.1416,1.111".
428 --------------------------------------------------------------------------*/
429 
quotize_float_vector(int num,float * vec,char sep)430 char * quotize_float_vector( int num , float *vec , char sep )
431 {
432    int ii , ff ;
433    char *out , **qstr , fbuf[32] ;
434 
435    if( num <= 0 || vec == NULL )
436       return quotize_string(NULL) ;
437 
438    qstr = NI_malloc(char*, sizeof(char *)*num) ;
439    for( ii=0 ; ii < num ; ii++ ){
440       sprintf(fbuf," %14.7g",vec[ii]) ;
441       for( ff=strlen(fbuf) ; fbuf[ff]==' ' ; ff-- ) /* skip trailing blanks */
442         fbuf[ff] = '\0' ;
443       for( ff=0 ; fbuf[ff] == ' ' ; ff++ ) ;         /* skip leading blanks */
444       qstr[ii] = NI_strdup(fbuf+ff) ;              /* array of temp strings */
445    }
446 
447    out = quotize_string_vector( num , qstr , sep ) ;
448 
449    for( ii=0 ; ii < num ; ii++ ) NI_free(qstr[ii]) ;
450 
451    NI_free(qstr) ; return out ;
452 }
453 
454 /*------------------------------------------------------------------------*/
455 /*! Check a string for 'nameness' - that is, consists only of legal
456     characters for a NIML 'Name' and also starts with an alphabetic
457     character.  Returns 1 if it is a Name and 0 if is not.
458 --------------------------------------------------------------------------*/
459 
NI_is_name(char * str)460 int NI_is_name( char *str )
461 {
462    int ii ;
463 
464    if( str == NULL || str[0] == '\0' || !isalpha(str[0]) ) return 0 ;
465 
466    for( ii=1 ; str[ii] != '\0' ; ii++ )
467      if( !IS_NAME_CHAR(str[ii]) ) return 0 ; /* this one is bad */
468 
469    return 1 ;                      /* all were good ==> success */
470 }
471 
472 /*------------------------------------------------------------------------*/
473 /*! Find a trailing name in a pathname.
474 
475    For example, for fname = "/bob/cox/is/the/author/of/NIML",
476      - the lev=0 trailing name is "NIML",
477      - the lev=1 trailing name is "of/NIML",
478      - the lev=2 trailing name is "author/of/NIML", and so on.
479    That is, "lev" is the number of directory names above the last name
480    to keep.  The pointer returned is to some place in the middle of fname;
481    that is, this is not a malloc()-ed string, so don't try to free() it!.
482 --------------------------------------------------------------------------*/
483 
trailname(char * fname,int lev)484 char * trailname( char *fname , int lev )
485 {
486    int fpos , flen , flev ;
487 
488    if( fname == NULL || (flen=strlen(fname)) <= 1 ) return fname ;
489 
490    if( lev < 0 ) lev = 0 ;
491 
492    flev = 0 ;
493    fpos = flen ;
494    if( fname[fpos-1] == '/' ) fpos-- ;  /* skip trailing slash */
495 
496    /* fpos   = index of latest character I've accepted,
497       fpos-1 = index of next character to examine,
498       flev   = number of directory levels found so far */
499 
500    while( fpos > 0 ){
501 
502       if( fname[fpos-1] == '/' ){
503          flev++ ; if( flev >  lev ) break ;  /* reached the lev we like */
504       }
505       fpos-- ;  /* scan backwards */
506    }
507 
508    return (fname+fpos) ;
509 }
510 
511 /*--------------------------------------------------------------------------*/
512 /*! Given an array of strings, determine how many are numbers [11 Sep 2018] */
513 /*  (After conversion to floats, thd_floatscan() can be used to for fixups) */
514 /*--------------------------------------------------------------------------*/
515 
516 #undef  FBAD
517 #define FBAD(sss)                       \
518  ( strcasecmp ((sss),"N/A"  ) == 0 ||   \
519    strncasecmp((sss),"NAN",3) == 0 ||   \
520    strncasecmp((sss),"INF",3) == 0   )
521 
NI_count_numbers(int nstr,char ** str)522 int NI_count_numbers( int nstr , char **str )
523 {
524    int nnum=0 , ii ; double val ; char *cpt ;
525 
526    if( nstr < 1 || str == NULL ) return nnum ;
527 
528    for( ii=0 ; ii < nstr ; ii++ ){
529      if( str[ii] != NULL ){
530        if( FBAD(str[ii]) ) nnum++ ;  /* 17 Oct 2018 */
531        else {
532          val = strtod(str[ii],&cpt) ;
533          if( *cpt == '\0' || isspace(*cpt) ) nnum++ ;
534        }
535      }
536    }
537    return nnum ;
538 }
539 
540 /*---------------------------------------------------------------------------*/
541 /*! Read an entire text file into a string [27 Aug 2019] */
542 /*---------------------------------------------------------------------------*/
543 
544 #undef  DBUF
545 #define DBUF 8192       /* how many bytes per fifo read */
546 
547 #undef  MAXBUF
548 #define MAXBUF 8388608  /* 8 Mbytes */
549 
NI_suck_file(char * fname)550 char * NI_suck_file( char *fname )
551 {
552    int fd , ii , nbuf ;
553    char *buf ;
554 
555    if( fname == NULL || fname[0] == '\0' ) return NULL ;
556 
557    fd = open( fname , O_RDONLY ) ; /* blocking reads */
558    if( fd < 0 ) return NULL ;
559 
560    nbuf = 0 ;
561    buf  = (char *)malloc( sizeof(char) * (DBUF+4) ) ;
562 
563    while( nbuf < MAXBUF ){
564      ii = read( fd , buf+nbuf , DBUF ) ;      /* try to read up to DBUF bytes */
565      if( ii <= 0 ) break ;                                     /* read failed */
566      nbuf += ii ;                        /* add in how many bytes we now have */
567      buf = (char *)realloc( buf, sizeof(char)*(nbuf+DBUF+4) ) ; /* resize buf */
568    }
569 
570    close(fd) ;
571    buf = (char *)realloc( buf , sizeof(char)*(nbuf+4) ) ;
572    buf[nbuf] = '\0' ; /* terminate string */
573    return buf ;
574 }
575