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 - < -> <
191 - > -> >
192 - " -> "
193 - ' -> '
194 - & -> &
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 && /* < */
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 && /* > */
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 && /* " */
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 && /* ' */
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 && /* & */
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   (decimal) and ý (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 "&" */
313
314 case '<':
315 case '>': lout += 4 ; break ; /* replace '<' with "<" */
316
317 case '"' :
318 case '\'': lout += 6 ; break ; /* replace '"' with """ */
319
320 case CR:
321 case LF: lout += 6 ; break ; /* replace CR with "
"
322 LF with "
" */
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,"&",5) ; jj+=5 ; break ;
334
335 case '<': memcpy(out+jj,"<",4) ; jj+=4 ; break ;
336 case '>': memcpy(out+jj,">",4) ; jj+=4 ; break ;
337
338 case '"' : memcpy(out+jj,""",6) ; jj+=6 ; break ;
339
340 case '\'': memcpy(out+jj,"'",6) ; jj+=6 ; break ;
341
342 case CR: memcpy(out+jj,"
",6) ; jj+=6 ; break ; /* 15 Oct 2002 */
343 case LF: memcpy(out+jj,"
",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