/* utils. Utility routines for various other routines */ /* Formerly defgb_utils. Should probably end up including */ /* outer_utils... */ /* NB: These routines report their errors to an externally */ /* defined function err(s,t). This function should be coded */ /* in anticipation that there is trouble getting dynamic */ /* memory, since many error messages are about that */ /* These routines are used by more than one other defgb module */ /* or are more-or-less generic. Also, these routines have no */ /* "knowledge" of the JGOFS "system" (but do have knowledge */ /* about defgb conventions) */ /* Logical add_id_to_err(new_s,new_t,s,t,id_string) */ /* char *add_to_buffer(dest,sizeofdest,source,errmsg) */ /* void analy_source(f,separators,dirstring) */ /* char *buildstring(str1,str2,str3,errstring) */ /* Logical char_sets_disjoint(str_ptrs,n_str_ptrs) */ /* char *copy_into_buffer(dest,sizeofdest,source,errmsg) */ /* void errn(s,i) */ /* Logical extract_wjstbl */ /* (synonym,max_len_synonym,variable,wjstbl) */ /* Logical get_logical_from_string (string,errmsg1,errmsg2) */ /* void free_lengthened_str(in_str_buf) */ /* void free_lengthened_str_special() */ /* Logical get_logical_value (string) */ /* Logical is_a_leap_year(year) */ /* int is_a_remote_object(s) */ /* int is_a_remote_object_old_way(s) */ /* int is_a_remote_object_new_way(s) */ /* Logical is_a_varname(s) */ /* int get_integer_attribute(ptr) */ /* int keyword_pairs_to_wjstbl */ /* (wjstbl_ptr_ptr,keyword_pair_str,sep_array,err_msg_id_str) */ /* char *lengthen_str(in_str_buf, */ /* append_str1,append_str2,extend_len,errstr) */ /* char *lengthen_str_nl(in_str_buf, */ /* append_str1,append_str2,extend_len,errstr) */ /* char *lengthen_str_and_free(in_str_buf, */ /* append_str1,append_str2,extend_len,errstr) */ /* int *level_splits(handle,maxlev,nvars) */ /* char *lookup_wjstbl (s,wjstbl) */ /* char *nxttok(start,separators,start_next,quotes, */ /* signif_consec_seps,nested_quotes) */ /* char *rem_delims (ptr,delims) */ /* char *set_option_string */ /* (option_name,option_default,wjstbl,n_wjstbl_args_left) */ /* char *strcasecmp_wjs(inptr1,inptr2) */ /* char *strdupl(outptr_ptr,inptr,errstring) */ /* char *strip_space(inptr) */ /* char *strip_space_n(inptr,&len) */ /* Logical string_in_list(list,string,sep) */ /* Logical string_is_numeric(datum,missing_is_numeric) */ /* char **string_sets_intersection(s1,num_s1,s2,num_s2) */ /* char **string_sets_union(s1,num_s1,s2,num_s2) */ #define UTILS_VERSION "utils version 2.4a 29 Aug 2014" /* 29 Aug 14. WJS */ /* lengthen_str has been allocating 1 too few bytes all these */ /* years - classic string-length/buffer-size confusion */ /* [Begin 2.4a] */ /* 28 Apr 13. WJS */ /* Typo fix in comment. */ /* 18 Oct 08. WJS */ /* Bug fix to is_a_remote_object. Dot analysis needed to be */ /* confined to portion of object defn relating to node:port */ /* 5 Aug 08. WJS */ /* Allow strings w/trailing blanks to be considered numeric */ /* [needs core.h 2.0] */ /* 13 Jun 08. WJS */ /* Up version. 2.3 is in use in a "frozen" jgofs tree */ /* 29 Feb 08. WJS */ /* is_a_remote_object stuff */ /* Change analy_source to use is_a_remote_object */ /* [Begin 2.4] */ /* 20 Sep 07. WJS */ /* string_sets_intersection, string_sets_union */ /* 2 Aug 07. WJS */ /* is_a_varname */ /* Another bug found in the realloc mod. Regress to 2.1 */ /* lengthen_str and, accordingly, up the version so we can declare */ /* 2.2 no good (not that 2.1 or 2.2 were ever "released". Regarding */ /* that, note that despite 2.0 comment, the first "JGOFS 2.0" tar */ /* file appears to contain 2.0 (contents of fleetlink testjg root)) */ /* [Begin 2.3] */ /* 26 Apr 07. WJS */ /* Bug fix to realloc mod. I should fiddle more w/working code.. */ /* 11 Jan 07. WJS */ /* Bug fix: add_id_to_len did not handle NULL input */ /* 30 Dec 06. WJS */ /* recode lengthen_str to use realloc */ /* set_option_string */ /* 20 Dec 06. WJS */ /* Check for unsucessful malloc in lookup_wjstbl */ /* Check for null input, too */ /* 16 Dec 06. WJS */ /* strip_space; strip_space_n */ /* char_sets_disjoint; keyword_pairs_to_wjstbl */ /* [Begin 2.2] */ /* 14 Jul 06. WJS */ /* add_to_buffer */ /* Change copy_into_fixed_length_buffer to copy_into_buffer */ /* [Begin 2.1 - don't think 2.0 was ever "released"] */ /* 21 Aug 05. WJS */ /* Another bug fix to level_splits! */ /* 17 Aug 05. WJS */ /* string_in_list & string_is_numeric */ /* 12 Aug 05. WJS */ /* is_a_leap_year */ /* 5 Aug 05. WJS */ /* Bug fix; arg list change (nlevs -> maxlev) to level_splits */ /* 5 Jul 05. WJS */ /* get_integer_attribute */ /* [Begin 2.0 (might be called 1.9a (5 Aug)] */ /* [ or 1.10 (12 Aug) elsewhere) ] */ /* 10 Jun 05. WJS */ /* Comment change */ /* 3 Jun 05. WJS */ /* level_splits */ /* [Needs utils.h 1.1] */ /* [Begin 1.9] */ /* 23 Apr 04. WJS */ /* Mods to "version-returning function". */ /* a) change from local to global to ensure it can't be "opti- */ /* mized out" */ /* b) don't want "version" in any function name since such */ /* names appear when grep'ping for "version" */ /* 18 Feb 04. WJS */ /* Rename from defgb_utils to utils. */ /* #include utils.h instead of defgb.h */ /* [Begin 1.8] */ /* 4 Feb 04. WJS */ /* Get .h file versions into this module */ /* 30 Jan 04. WJS */ /* add_id_to_err; errn */ /* [Begin 1.7] */ /* [1.0 -> 1.6 comments in utils_revision.doc. 5 Jul 05] */ #include "utils.h" /* Uses no global variables external to this routine */ Logical free_flag = FALSE; /* Communicates between lengthen_str & */ /* lengthen_str_and_free. Couldn't */ /* think of better way to do this */ /* from a usage point of view */ Logical add_nl = FALSE; /* Communicates between lengthen_str & */ /* lengthen_str_nl. */ /* Requires following routine : */ void err(); /************************************************************************/ char *utils_return_vers() /* Routine exists mostly to force .h file version string into this */ /* module, but we could call it if we want. Note string must not be */ /* global or we'll have conflicts if another routine similarly */ /* includes the version string */ { static char version[] = UTILS_VERSION"/"FULL_UTILSH_VERSION; return version; } char *strip_space_n(inptr,lenptr) char *inptr; int *lenptr; /* Return pointer to first non-whitespace char in input; NULL if none */ /* Return length of non-whitespace string in lenptr; 0 if none */ { char *ptr,*ptr2; *lenptr = 0; if ( (ptr = inptr) == NULL ) return NULL; while (isspace(*ptr)) ptr++; if (*ptr == '\0') return NULL; ptr2 = ptr + strlen(ptr); while (ptr2 != ptr) if ( ! isspace(*(--ptr2)) ) break; *lenptr = ptr2 - ptr + 1; return ptr; } char *strip_space(inptr) char *inptr; /* Strip leading and trailing whitespace from input string. Return */ /* pointer to string. Requires write access to input string */ /* If string is all whitespace, return NULL */ { int len; char *ptr; if ( (ptr = strip_space_n(inptr,&len)) == NULL ) return NULL; *(ptr+len) = '\0'; return ptr; } Logical string_is_numeric(datum,missing_is_numeric) char *datum; Logical missing_is_numeric; { Logical status; double dummy; /* MISSING_VALUE_STRING set in core.h */ /* (so is GET_NUMBER_FROM_STRING) */ if ( strcmp(MISSING_VALUE_STRING,datum) == 0 ) return missing_is_numeric; GET_NUMBER_FROM_STRING(dummy,datum,status); return status; } Logical string_in_list(list,string,sep) char *list,*string,sep; /* sep is list separator character */ { char *ptr; int string_len; Logical start_test,end_test; if (strchr(string,sep) != NULL) return FALSE; string_len = strlen(string); ptr = list; while ( (ptr = strstr(ptr,string)) != NULL ) { start_test = (ptr == list) ? TRUE : (*(ptr-1) == sep); ptr += string_len; end_test = (*ptr == sep) || (*ptr == '\0'); if (start_test && end_test) return TRUE; } return FALSE; } Logical add_id_to_err(new_s,new_t,s,t,id_string) char **new_s,**new_t; char *s,*t,*id_string; /* Takes error message that is presumably in 2 parts in strings s */ /* and t. Concatenates s & t with colon and returns pointer to */ /* this string in new_s. Makes a 3-section new string from present */ /* time, effective username running the process, and id_string. */ /* Returns pointer to this string in new_t. */ /* Function return values is FALSE if we couldn't get dynamic */ /* memory needed for string fooling. new_s and new_t try hard to */ /* contain the best content possible under that circumstance... */ /* Function also returns FALSE if id_string is "too long" and had to */ /* be truncated. Calling programs can test their id_string lengths */ /* against MAXLEN_PROGRAM_ID_STRING to avoid this (there is some */ /* extra space available in this routine, so strings longer than */ /* MAXLEN_PROGRAM_ID_STRING may end up whole anyway) */ /* Memory pointed to by new_s and new_t is malloc'ed and can be */ /* freed. */ /* NB: If there were no malloc problems, the string pointed to by */ /* new_t begins with \n because of the way error_ (in outer; */ /* eventual consumer of many error messages) interacts with this */ /* routine. Start from new_t+1 if this is undesirable */ /* */ /* This routine cannot call err to report trouble since this */ /* routine is usually called by err */ { #define TIME_PREFIX "This message issued at " #define USERNAME_PREFIX "Effective username of process: " #define NO_USERNAME "cannot be determined" #define LEN_NO_USERNAME 20/* Don't know how to sizeof() in preprocessor */ /* Assume L_cuserid value is represents max len of username returned */ /* via getpwuid (it really should...) */ #if LEN_NO_USERNAME > L_cuserid #define SIZE_USERNAME LEN_NO_USERNAME #else #define SIZE_USERNAME L_cuserid #endif /* Allow for some room if for some reason we can't concatenate s & t */ #define SLOP 256 /* 1 = size of \n */ /* 25 = size of ctime return less its \0 (but including its \n) */ /* 1 = size of \n */ /* 1 = size of \n */ /* 1 = size of \0 */ /* static since we could be using it in times of problem getting memory */ static char buf[1 + \ sizeof(TIME_PREFIX) + 25 + \ sizeof(USERNAME_PREFIX) + SIZE_USERNAME + 1 + \ MAXLEN_PROGRAM_ID_STRING + 1 + 1 + SLOP]; char *buf1,*buf2,*uname; int len_s,len_t; time_t timbuf; struct passwd *passwd; Logical retval = TRUE; len_s = (s == NULL) ? 0 : strlen(s); len_t = (t == NULL) ? 0 : strlen(t); if (len_s == 0) if (len_t == 0) /* Don't need to reset len_s */ s = "Unknown error (add_id_to_err called w/ null or zero len args)"; else { buf1 = t; t = s; s = buf1; len_s = len_t; len_t = 0; } buf2 = buf; if (len_t == 0) buf1 = s; else { /* +1 for space between s & t; + 1 for \0 */ buf1 = (char *)malloc(len_s + len_t + 2); if (buf1 == NULL) { retval = FALSE; buf1 = s; /* < takes care of trailing \0; + 2 for ": " */ if (len_t < SLOP + 2) { /* Emulate a bit of error_ formatting */ buf[0] = ':'; buf[1] = ' '; strcpy(&buf[2],t); } else buf2 = t; } else { strcpy (buf1,s); if ( ! isspace(*(s + len_s - 1)) ) *(buf1 + len_s++) = ' '; strcpy (buf1 + len_s,t); } } if (buf2 == buf) { buf2[0] = '\n'; buf2[1] = '\0'; #if VMS uname = cuserid(NULL); #else if ( (passwd = getpwuid(geteuid())) == NULL ) uname = NO_USERNAME; else uname = passwd->pw_name; #endif strcat(buf2,TIME_PREFIX); time(&timbuf); strcat(buf2,ctime(&timbuf)); strcat(buf2,USERNAME_PREFIX); strcat(buf2,uname); strcat(buf2,"\n"); strncat(buf2,id_string,MAXLEN_PROGRAM_ID_STRING+SLOP); strcat(buf2,"\n"); retval = (strlen(id_string) <= MAXLEN_PROGRAM_ID_STRING+SLOP); } *new_s = buf1; *new_t = buf2; return retval; } void errn(s,i) char *s; int i; { static char intbuf[11]; sprintf(intbuf,"%d",i); err(s,intbuf); return; } int get_integer_attribute(ptr) /* Input is string supposedly of form "attribute=integer_string" */ /* Verify format (in particular, that integer_string is legal). If */ /* so, return value of integer_string */ char *ptr; { char *p; int len,ierr; if ( (p = strchr(ptr,'=')) == NULL ) err ("No \"=\" in attribute string ",ptr); switch (ierr = sscanf(p+1,"%d", &len)) { case EOF: err ("No value after \"=\" in attribute string ",ptr); case 0: err("Illegal integer after \"=\" in attribute string ",ptr); case 1: break; default: errn("Unexpected return from sscanf in get_integer_attribute. Return=", ierr); } return len; } /* Because of troubles w/ Apache 2 web server and double slashes, */ /* consider an object remote not only if it begins w/double slash */ /* (old way), but also if it begins with /a.b.c, where a, b, & c */ /* are non-empty, non-period-containing strings (new way). */ /* This means that subdirs of objects directories may not have the */ /* a.b.c form. Agreed to by RCG in email 31 Dec 07 */ int is_a_remote_object_old_way(s) char *s; { if (strstr(s,REMOTE_OBJECT_PREFIX_OLD) == s) return strlen(REMOTE_OBJECT_PREFIX_OLD); return 0; } int is_a_remote_object_new_way(s) char *s; { /* Interesting issue of REMOTE_OBJECT_HOST_TERMINATOR_CHAR = '.' */ /* (or ':', for that matter) not dealt with either here or in utils.h */ /* (as of Oct 08) */ char *dot1,*dot2,*end; int len_remote_prefix; if (strstr(s,REMOTE_OBJECT_PREFIX_NEW) != s) return 0; /* doesn't start w/ slash */ len_remote_prefix = strlen(REMOTE_OBJECT_PREFIX_NEW); end = strchr(s+len_remote_prefix,REMOTE_OBJECT_HOST_TERMINATOR_CHAR); if (end == NULL) end = s + strlen(s); dot1 = strchr(s+len_remote_prefix,'.'); if (dot1 == NULL) return 0; /* no .s */ if (dot1 >= end) return 0; /* not in host portion */ if (dot1 == s+1) return 0; /* a empty */ dot2 = strchr(dot1+1,'.'); if (dot2 == NULL) return 0; /* only 1 . */ if (dot2 >= end) return 0; /* not in host portion */ if (dot2 == dot1+1) return 0; /* b empty */ if (*(dot2+1) == '\0') return 0; /* c empty */ if (*(dot2+1) == '.') return 0; /* c empty */ return len_remote_prefix; } int is_a_remote_object(s) char *s; { /* Return 0 if NOT a remote object */ /* Return length of prefix to be skipped to get at node name if */ /* it IS a remote object */ /* This means the following 2 less-than-ideal things work: */ /* if (is_a_remote_object) ... */ /* and */ /* if (is_a_remote_object == 1) remote new way */ /* if (is_a_remote_object == 2) remote old way */ int i; i = is_a_remote_object_old_way(s); return ( (i == 0) ? is_a_remote_object_new_way(s) : i ); } Logical is_a_varname(s) char *s; { /* Ancient agreement of original "band of 4" said that varnames would */ /* henceforth have only characters that would be legal in a c variable */ /* names. This code taken from Harbison & Steele, sec 12.1 */ char ch; if ((ch = *s++) == '\0') return FALSE; if ( ! (isalpha(ch) || ch == '_') ) return FALSE; while ((ch = *s++) != '\0') { if ( ! (isalnum(ch) || ch == '_') ) return FALSE; } return TRUE; } Logical is_a_leap_year(year) int year; { /* Algorithm from http://www.nist.gov/y2k/documents.htm#leapyear */ return (year%4 == 0) && ((year%100 != 0) || (year%400 == 0)); } int strcasecmp_wjs(str1,str2) char *str1,*str2; { char *ptr1,*ptr2; ptr1 = str1; ptr2 = str2; while ((*ptr1 != '\0') && (*ptr2 != '\0')) { if (toupper(*ptr1) != toupper(*ptr2)) break; ptr1++; ptr2++; } return toupper(*ptr1) - toupper(*ptr2); } char *buildstring(str1,str2,str3,errstring) char *str1,*str2,*str3,*errstring; /* Allocate memory for, and return pointer to, concatenation of the */ /* 3 input strings. Returns NULL if all strings are NULL */ /* On alloc failure, call err with errstring & the number of bytes we */ /* tried to get, unless errstring NULL, in which case return NULL. */ /* See lengthen_str for more general string concatenator ... */ { char errbuf[80]; /* Too lazy to count */ char *out; int len; if ( (str1 == NULL) && (str2 == NULL) && (str3 == NULL) ) return NULL; len=0; if (str1 != NULL) len += strlen(str1); if (str2 != NULL) len += strlen(str2); if (str3 != NULL) len += strlen(str3); out = (char *)malloc(len+1); if (out == NULL) { /* We couldn't get memory. */ if (errstring == NULL) return NULL; sprintf (errbuf, "Failed to get %d (0x%X) bytes of memory\n Purpose = ",len,len); err (errbuf,errstring); } else { *out = '\0'; if (str1 != NULL) strcat (out,str1); if (str2 != NULL) strcat (out,str2); if (str3 != NULL) strcat (out,str3); return out; } return NULL; /* Never get here, but compiler wants return val */ } char *strdupl(out,in,errstring) char **out,*in,*errstring; /* Finds length of in, grabs that much memory, copies in to the */ /* scratch area, and puts the addr of the scratch area in *out (w/pass */ /* by value, cannot directly affect out). Returns addr of out */ /* (like strcpy does) */ /* Some C libs have a strdup, which should probably be used, but some */ /* don't */ { *out=buildstring(in,NULL,NULL,errstring); return *out; } char *add_to_buffer(dest,sizeofdest,source,errmsg) char *source,*dest,*errmsg; int sizeofdest; /* act buf size; = (useful size) + (1 for \0) */ { int len; len = strlen(dest); return copy_into_buffer(dest+len,sizeofdest-len,source,errmsg); } char *copy_into_buffer(dest,sizeofdest,source,errmsg) char *source,*dest,*errmsg; int sizeofdest; /* act buf size; = (useful size) + (1 for \0) */ { int sourcelen,sourcefragsize; char *init_char; char msg1[] = " Tried to fit "; char msg2[] = " chars (including trailing \\0) into a "; char msg3[] = " char buffer\n Next line is "; /* Each of next 2 need to be big enough to hold biggest int */ /* Too lazy to think about exact # */ char nsource_chars[15],ndest_chars[15]; /* Initial 1 is max size of init_char. +1 is \0 concluding */ /* errbuf1. -3 is the \0s concluding msgNs */ char err1_part1[1 + sizeof(msg1) + sizeof(msg2) + sizeof(msg3) + sizeof(nsource_chars) + sizeof(ndest_chars) + 1 - 3]; char *err2,*err1_part2; /* = is a problem because of need for trailing \0 */ if ( (sourcelen = strlen(source)) < sizeofdest ) return strcpy(dest,source); init_char = ( errmsg[strlen(errmsg) - 1] == '\n' ) ? "" : "\n"; /* -1 accounts for trailing \0 in source */ sprintf (err1_part1,"%s%s%d%s%d%s", init_char,msg1,sourcelen+1,msg2,sizeofdest,msg3); /* Try to print bad string, but obviously we don't want to print */ /* a runaway. Decided to try to print what would have fit plus */ /* next 10 chars, but must be able to copy truncated portion. */ /* Sigh (Can't just store a \0 into proper spot because offending */ /* string might be a literal or some such...gee, it'd be nice if */ /* one could try the store and then, if it failed, do the copy) */ if ( sourcelen > (sourcefragsize = sizeofdest + 10) ) { err2 = (char *)malloc(sourcefragsize + 1); if (err2 == NULL) { err2 = source; } else { strncpy(err2,source,sourcefragsize); err2[sourcefragsize+1] = '\0'; } } else { err2 = source; } err1_part2 = (err2 == source) ? "offending string\n" : "beginning of offending string\n"; /* 2nd line to err is analyzed for bad chars and funky whitespace */ /* so try to confine it to problem string (else our formating gets */ /* "analyzed") */ err (buildstring(errmsg,err1_part1,err1_part2,"fixed_str_ovf_msg1"),err2); return NULL; /* Should not get to this statement */ } char *lengthen_str(in_str_buf,append_str1,append_str2,extend_len,errstring) char *in_str_buf,*append_str1,*append_str2,*errstring; int extend_len; /* [Looks like this was written w/o knowledge of realloc. WJS Jul 03] */ /* [Changed malloc to realloc per above comment. WJS Dec 06] */ /* [Did it so well that I regressed to malloc code. WJS Aug 07] */ /* lengthen_str appends append_str1 & append_str2 (and a newline, */ /* if entry was made via lengthen_str_nl) to the string within */ /* in_str_buf while checking that the result will fit in in_str_buf. */ /* If the result will not fit, memory is allocated for a new */ /* in_str_buf, and the result is put there. Memory of the original */ /* buffer is freed if lengthen_str allocated it. (To cause the */ /* freeing of the original, user-supplied buffer, use function */ /* lengthen_str_and_free instead of lengthen_str on at least the */ /* first call) */ /* extend_len is provided to allow the caller to try to minimize */ /* the number of memory allocations/deallocations. It is optional, */ /* and may always be specified as 0. It specifies the amount to */ /* extend in_str_buf if extension is required. It is ignored if it */ /* is insufficient to hold the result. An exception is a negative */ /* extend_len on the first call to lengthen_str for a particular */ /* in_str_buf. In this case, the absolute value of extend_len is */ /* used if extension is required. This absolute value also re- */ /* presents the initial size of in_str_buf (not the length of the */ /* string in it). Negative extend_len on other calls is ignored. */ /* errstring is used for ID purposes when a memory allocation */ /* failure occurs. If NULL (or special key-see below), lengthen_str */ /* returns NULL. Otherwise, function err is called with errstring */ /* and other information. Exception: if errstring is a special */ /* string (currently "Use special entries"), it is not used in error */ /* messages. It indicates that this particular string extension */ /* cannot fail because too many other strings are being lengthened. */ /* The return value of lengthen_str is a pointer to the */ /* first character of the buffer containing the complete string */ /* */ /* lengthen_str keeps an internal pointer to the next free char- */ /* acter in the buffer, so be very careful of adding or removing */ /* characters from the buffer between lengthen_str calls. The */ /* number of pointers is limited (to 100 at the moment). */ /* */ /* When finished using a string, free its space w/a call to */ /* free_lengthened_str rather than using free. This serves 2 pur- */ /* poses. First, it won't free a string that hasn't been extended */ /* (unless entry was made via lengthen_str_and_free). Second, it */ /* will free the internal lengthen_str table entries */ /* To free memory allocated to the special buffer (see "errstring" */ /* doc above) call free_lengthened_str_special() */ /* */ /* Details: */ /* in_str_buf is looked up in an internal table. If not found, */ /* a new entry is made. Exception: if errstring is a special string, */ /* a special, extra table entry is used. There is only one such */ /* extra entry. Idea is to be able to use lengthen_str to diagnose */ /* its own errors from previous calls */ /* The absolute value of extend_len (or the */ /* length of in_str_buf) is assumed to be the size of the buffer. */ /* If append_str1 is NULL, memory allocated to in_str_buf is freed */ /* if appropriate, and in_str_buf is removed from the table */ /* extend_len is added to the original length of in_str_buf if */ /* this particular call to lengthen_str requires a buffer extension. */ /* As a length, extend_len is ignored if using it does not make */ /* enough space for the resulting string. Thus, 0 extend_len */ /* produces an extension each call, exactly big enough to hold the */ /* concatenated string. */ /* As extensions are made, the internal table is updated. There- */ /* fore, the most usual in_str_buf value is the return of a previous */ /* lengthen_str call */ { #define NLENGTHEN_STR_TBL 100 static char *addr[NLENGTHEN_STR_TBL+1]; static char *bufptr[NLENGTHEN_STR_TBL+1]; static int size[NLENGTHEN_STR_TBL+1]; /* The zeros to which the c standard will init all these static */ /* arrays will be FALSEs in the case of the next array, which will */ /* protect us in case somebody tries a free before an allocate. */ /* However, it's not nice for the caller to depend on that... */ static Logical free_addr[NLENGTHEN_STR_TBL+1]; static Logical spare_buffer_initialized = FALSE; static int ntbl = 0; int itbl,len,len1,len2; char *tmp; char errbuf[80]; /* Too lazy to count */ Logical use_spare_buffer; /* USE_SPECIALS defined in utils.h */ if (errstring == NULL) use_spare_buffer = FALSE; else use_spare_buffer = (strcmp(USE_SPECIALS,errstring) == 0); if (use_spare_buffer) itbl = NLENGTHEN_STR_TBL; else for ( itbl = 0; itbl < ntbl; itbl++ ) if (in_str_buf == addr[itbl]) break; if ((itbl == ntbl) || (use_spare_buffer && ! spare_buffer_initialized)) { /* Need to add a table entry */ if ( (ntbl == NLENGTHEN_STR_TBL) && ! use_spare_buffer ) errn("Too many string lengthenings in progress. Max is ",ntbl); addr[itbl] = in_str_buf; len = (in_str_buf == NULL) ? 0 : strlen(in_str_buf); bufptr[itbl] = in_str_buf + len; /* free_flag is FALSE unless got here via lengthen_str_and_free */ free_addr[itbl] = free_flag; /* Initial size from extend_len if extend_len negative */ if (extend_len < 0) { extend_len = -extend_len; /* -1 is to account for terminating null */ size[itbl] = (extend_len-1 > len) ? extend_len-1 : len; } else size[itbl] = len; if (use_spare_buffer) spare_buffer_initialized = TRUE; else ntbl++; } /* Note: can make new entry in block above and remove it here */ /* during same call to lengthen_str... */ if (append_str1 == NULL) { if (free_addr[itbl]) free(addr[itbl]); if (use_spare_buffer) spare_buffer_initialized = FALSE; else { /* Remove this table entry by copying last one over it & */ /* shortening list */ addr[itbl] = addr[ntbl]; bufptr[itbl] = bufptr[ntbl]; free_addr[itbl] = free_addr[ntbl]; size[itbl] = size[ntbl]; ntbl--; } return NULL; } len1 = strlen(append_str1); len2 = (append_str2 == NULL) ? 0 : strlen(append_str2); len = len1 + len2; if (add_nl) len++; if ( (addr[itbl] + size[itbl]) < (bufptr[itbl] + len) ) { /* Need a bigger string. */ /* Figure out its length */ size[itbl] += (extend_len > len) ? extend_len : len; /* Get it, and move old string to new location */ tmp = addr[itbl]; /* +1 for terminating null. "size"s are str lens, not buf sizes*/ addr[itbl] = (char *)malloc(size[itbl]+1); if (addr[itbl] == NULL) { if ( (errstring == NULL) || use_spare_buffer ) return NULL; sprintf (errbuf, "Error trying to get %d (0x%X) bytes of memory\n Purpose = ", size[itbl], size[itbl]); err (errbuf,errstring); } if (tmp != NULL) { strcpy(addr[itbl],tmp); /* Free old string if appropriate. */ if (free_addr[itbl]) free(tmp); } /* Adjust pointer to first free character in string */ bufptr[itbl] += (addr[itbl] - tmp); /* Mark this string to be freed if another extension is needed */ free_addr[itbl] = TRUE; } strcpy(bufptr[itbl],append_str1); bufptr[itbl] += len1; if (append_str2 != NULL) { strcpy(bufptr[itbl],append_str2); bufptr[itbl] += len2; } if (add_nl) { *(bufptr[itbl]++) = '\n'; *bufptr[itbl] = '\0'; } return addr[itbl]; } void free_lengthened_str_special() { lengthen_str(NULL,NULL,NULL,0,USE_SPECIALS); return; } void free_lengthened_str(in_str_buf) char *in_str_buf; { lengthen_str(in_str_buf,NULL,NULL,0,"... free_lengthened_str"); return; } char *lengthen_str_nl(in_str_buf,ap_str1,ap_str2,extend_len,errstring) char *in_str_buf,*ap_str1,*ap_str2,*errstring; int extend_len; /* Asks lengthen_str to add a newline character after ap_strs */ { char *tmp; add_nl = TRUE; tmp = lengthen_str(in_str_buf,ap_str1,ap_str2,extend_len,errstring); add_nl = FALSE; return tmp; } char *lengthen_str_and_free(in_str_buf,ap_str1,ap_str2,extend_len,errstring) char *in_str_buf,*ap_str1,*ap_str2,*errstring; int extend_len; /* Needed only if first call to lengthen_str wants to spec an */ /* input buffer which should be freed if it needs lengthening */ /* Except for first call for a particular input buffer, can be used */ /* interchangeably w/lengthen_str */ { char *tmp; free_flag = TRUE; tmp = lengthen_str(in_str_buf,ap_str1,ap_str2,extend_len,errstring); free_flag = FALSE; return tmp; } char *nxttok(start,separators,nxt,quotes,signif_consec_seps,nested_quotes) char *start,*separators,*quotes,**nxt; Logical signif_consec_seps,nested_quotes; /* strtok w/ quoting & significant consecutive separator capability */ /* nxt is pointer to place where we return pointer to next string */ /* start. Returns pointer to token start; NULL if no more tokens. */ /* Does not return nxt if no more tokens */ /* if quote is first non-separator character of token, it and its */ /* trailing quote are removed. Otherwise, quotes are left in. */ /* nxttok can also be used to replace strtok when "nested" strtok's */ /* are desired. In the sequence */ /* tok = strtok (start,separators); */ /* while (tok != NULL) { */ /* do_whatever(tok); */ /* tok = strtok (NULL,separators); */ /* } */ /* do_whatever may not call strtok on a different string. The */ /* sequence can be replaced with */ /* char *nxt; */ /* tok = nxttok(start,separators,&nxt,NULL,FALSE,FALSE); */ /* while (tok != NULL) { */ /* do_whatever(tok); */ /* tok = nxttok(nxt,separators,&nxt,NULL,FALSE,FALSE); */ /* } */ { char *rtn,*next_start,*next_quote; char open_quote,close_quote; if (start == NULL) return NULL; /* Determine string start based on whether or not 2 consecutive */ /* separators means an empty field */ if (separators == NULL || signif_consec_seps) rtn = start; else rtn = start + strspn(start,separators); if (*rtn == '\0') return NULL; /* Field was all separators */ if (quotes == NULL) open_quote='\0'; else { open_quote = *quotes; close_quote = *(quotes+1); } if (*rtn == open_quote) { /* Field is between quote characters */ /* Advance beyond open quote */ rtn++; /* Find terminating quote-either last one or next one */ /* depending on how field is being parsed */ if (nested_quotes) next_start=strrchr(rtn,close_quote); else next_start=strchr(rtn,close_quote); /* Chop string on top of close quote and advance beyond it */ if (next_start == NULL) err ("Unclosed delimited field. Field = ",rtn-1); else *(next_start++) = '\0'; } else { /* Field is between separator characters */ /* No separators means next token is whole string */ if (separators == NULL) next_start = NULL; else { next_start = rtn; while (next_start != NULL) { /* Get next separator & next open quote. */ if ((next_quote = quotes) != NULL) next_quote = strchr(next_start,open_quote); next_start = strpbrk(next_start,separators); /* If no open quote or open quote after next separator, done */ if (next_quote == NULL || next_quote > next_start) break; /* Advance beyond open quote & find appropriate close quote */ if (nested_quotes) next_start=strrchr(++next_quote,close_quote); else next_start=strchr(++next_quote,close_quote); /* If appropriate, advance beyond close quote & try again */ if (next_start == NULL) err ("Unclosed delimited field. Field = ",rtn); else if (*(++next_start) == '\0') next_start = NULL; } } /* If more separators, chop string on top of next one and */ /* advance beyond it */ if (next_start != NULL) *(next_start++) = '\0'; } if (nxt != NULL) *nxt = next_start; return rtn; } Logical get_logical_value (string) char *string; /* Return 1 if string is synonym for TRUE, 0 for FALSE, -1 if not a synonym */ { if (string != NULL) switch (*string) { case 't': case 'T': case 'y': case 'Y': case '1': if (*(string+1) == '\0') return TRUE; if ( (strcmp("YES",string) == 0) || (strcmp("yes",string) == 0) || (strcmp("TRUE",string) == 0) || (strcmp("true",string) == 0) ) return TRUE; break; case 'f': case 'F': case 'n': case 'N': case '0': if (*(string+1) == '\0') return FALSE; if ( (strcmp("NO",string) == 0) || (strcmp("no",string) == 0) || (strcmp("FALSE",string) == 0) || (strcmp("false",string) == 0) ) return FALSE; break; default: /* For completeness */ ; break; } return NOT_VALID; } Logical get_logical_from_string (string,errmsg1,errmsg2) char *string,*errmsg1,*errmsg2; /* get_logical_value + error msgs. Still returns -1 if NG (if err */ /* does not terminate program...) */ { char *errtmp1,*errtmp2; switch (get_logical_value(string)) { case 1: return TRUE; case 0: return FALSE; default: ; } errtmp1 = buildstring("Illegal logical value ",string,"\n ", " get_logical_from_string"); errtmp2 = buildstring(errmsg1,errmsg2,NULL," get_logical_from_string"); err (errtmp1,errtmp2); /* Lines below for completeness (err does not return) */ free (errtmp1); free (errtmp2); return NOT_VALID; } char *rem_delims (ptr,delims) char *ptr,*delims; /* Logically remove one set of nested delimiters, if found, from */ /* *ptr. Does this by returning a pointer to a string w/o the */ /* delimiters */ /* */ /* delims is a 2 character string consisting of the open and close */ /* delimiter characters. */ /* */ /* Error if first character of input string */ /* is opening delimiter and last character is not closing delim */ { char *nxt,*retval; retval = nxttok(ptr,NULL,&nxt,delims,FALSE,TRUE); if (nxt != NULL) /* No delim string should end up NULL */ if (*nxt != '\0') /* Delim string put '\0' over close delim, */ /* hence "next string" is '\0' that closed */ /* original string */ err ("Closing delimiter not last character of field. Field = ",ptr); return retval; } void analy_source(f,separators,dirstring) struct fileinfo *f; char *separators,*dirstring; /* Alters f->source (maybe) and f->source_type */ /* All f->source values point to dynamically allocated fields on */ /* both input and output. Therefore, they must point to the first */ /* characters of their fields so they can be subsequently freed if */ /* necessary. */ /* Examine .source field of structure f to determine what kind of */ /* input it represents. Set the .source_type field accordingly. */ /* Along the way, remove the special characters that identified the */ /* field as whatever type it was, and do some validity checks. */ /* Field represents a command or script if it's wrapped in one set */ /* of delimiters; or a JGOFS object if wrapped in another. It's also */ /* a JGOFS object if it leads off with http node delimiters. If, */ /* instead, it leads off with a special character, it represents the */ /* data itself. If none of the above, it represents a file. If that */ /* file is /dev/null, it's a special type (phony "missing data" record */ /* can be supplied if necessary) */ { char badscriptchars[4] = {PIPECHAR, INP_REDIRECT_CHAR, OUTP_REDIRECT_CHAR, '\0'}; char *msg,*nxt,*source,*savsrc; Logical might_need_dir; /* Make copy of string for error message purposes. As long as we */ /* have it, and since copying is necessary, muck w/copy, even- */ /* tually overwriting original */ /* Must retain pointer to string start in order to free it... */ strdupl(&savsrc,f->source," analy_source temp buffer"); source = savsrc; source += strspn(source,separators); /* Skip leading separators */ if (*source == '\0') { msg=buildstring("Null input specifier for ", f->descrip, " file.", " analy_source err msg1"); err (msg,""); free (msg); } else if (*source == *EXEC_DELIM) { f->source_type = COMMAND_FILE; source = nxttok(source,separators,&nxt,EXEC_DELIM,FALSE,TRUE); if (strpbrk(source,badscriptchars) != NULL) err ("May not do piping or i/o redirection in command\n Command was ",source); } else if (*source == SOURCE_IS_DATA) { f->source_type = INDIRECT_FILE_LINE; ++source; /* Advance past \ */ nxt = NULL; } else if (*source == *OBJECT_DELIM) { f->source_type = JGOFS_OBJECT; source = nxttok(source,separators,&nxt,OBJECT_DELIM,FALSE,TRUE); } else if (strcmp(source,NULL_DEVICE) == 0) { f->source_type = CREATE_NULL_DATA; nxt = NULL; } else { /* All characters are part of source string */ f->source_type = is_a_remote_object(source) ? JGOFS_OBJECT : DATA_FILE; /* Next line will not reset source because of strspn before */ /* loop. However, it will truncate trailing delims & set nxt, */ /* both of which are necessary. Left in source = for symmetry */ source = nxttok(source,separators,&nxt,NULL,FALSE,FALSE); if (strpbrk(source," \t") != NULL) err ("Embedded blank or tab in file name. Name = ",source); } /* Check for extra entry */ if (nxttok(nxt,separators,NULL,NULL,FALSE,FALSE) != NULL) { msg=buildstring("Extra parameter in input specifier for ", f->descrip, " file. Specifier = ", " analy_source err msg"); err (msg,f->source); free (msg); } /* Final string is generally a substring of original. Because */ /* of dynamic memory issues, f->source pointer cannot be moved, so */ /* copy substring to beginning of string. If original string was */ /* "perfect" (just file spec, no leading/trailing whitespace, etc) */ /* copy is unnecessary. However, although this is probably most */ /* common situation, too tough to keep track of whether or not */ /* copy is needed. */ /* Exception to substring occurs when we need to prefix file */ /* spec w/directory. In this case, chuck out old string and point */ /* to new one. Note that this logic depends on source pointing to */ /* a copy of the original string (as opposed to original string) */ might_need_dir = ( (f->source_type == DATA_FILE) || (f->source_type == COMMAND_FILE) ); if ( might_need_dir && (*source != DIRSEP) && (dirstring != NULL) ) { free (f->source); f->source = buildstring(dirstring,source,NULL," putting filespec together"); } else strcpy (f->source,source); free (savsrc); return; } char *lookup_wjstbl (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_character(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 *found,*ptr; int key_len; if (wjstbl == NULL) err("lookup_wjstbl received null wjstbl",""); if (wjstbl[0] == '\0') return NULL; key_len = strlen(s); ptr = (char *)malloc(key_len + 3); if (ptr == NULL) errn ("Could not allocate mem for copy of key in lookup_wjstbl. Nbytes=", key_len+3); /* Build string consisting of input string surrounded by */ /* separator characters */ ptr[0] = wjstbl[0]; strcpy(ptr+1,s); ptr[key_len+1] = wjstbl[0]; ptr[key_len+2] = '\0'; found=strstr(wjstbl,ptr); free(ptr); if (found == NULL) return NULL; else return found+key_len+2; } Logical extract_wjstbl(synonym,max_len_synonym,variable,wjstbl) char *variable,*synonym,*wjstbl; int max_len_synonym; /* Returns NOT_VALID if synonym string is truncated */ /* else */ /* TRUE if translation found. *synonym contains translation */ /* FALSE if no translation found. *synonym contains copy of */ /* *variable */ { char *trans_loc,*synonym_ptr,white_space; int i; trans_loc=lookup_wjstbl(variable,wjstbl); if (trans_loc == NULL) { if (variable == synonym) return FALSE; else { strncpy (synonym,variable,max_len_synonym); if (strlen(variable) <= max_len_synonym) return FALSE; else return NOT_VALID; } } else { synonym_ptr = synonym; /* See description of a wjstbl for location of white space char */ white_space = *(wjstbl+strlen(wjstbl)-1); for (i=0;i= num_intersection) intersection[num_intersection++] = s1[i]; } } intersection[num_intersection] = NULL; return intersection; } char **string_sets_union(s1,num_s1,s2,num_s2) char *s1[],*s2[]; int num_s1,num_s2; /* Given 2 sets of pointers to strings (and the number of pointers */ /* in each), return set of pointers to strings such that the */ /* strings pointed to in the return set consist of strings in either */ /* input sets. */ /* Returned set of pointers is in dynamically allocated memory */ /* NULL is returned if memory could not be allocated */ /* Returned set of pointers is terminated by a NULL pointer. */ /* No 2 returned pointers point to identical strings */ { char **union_; int i,k,num_union; i = num_s1 + num_s2 + 1; i *= sizeof (char *); union_ = (char **)malloc(i); if (union_ == NULL) return NULL; num_union = 0; for (i=0; i= num_union) union_[num_union++] = s1[i]; } for (i=0; i= num_union) union_[num_union++] = s2[i]; } union_[num_union] = NULL; return union_; } Logical char_sets_disjoint(str_ptrs,n_str_ptrs) char *str_ptrs[]; int n_str_ptrs; { int i,j; for (i = 0; i < n_str_ptrs; i++) for (j = i+1; j < n_str_ptrs; j++) if (strpbrk(str_ptrs[i],str_ptrs[j])) return FALSE; return TRUE; } int keyword_pairs_to_wjstbl (wjstbl_ptr_ptr,keyword_pair_str,sep_array,err_msg_id_str) char **wjstbl_ptr_ptr; char *keyword_pair_str; char *sep_array[PARSE_SEPARATOR_ARRAY_SIZE]; char *err_msg_id_str; /* Makes a wjstbl from parsed keyword/value string */ /* Returns # pairs in wjstbl as function return, and pointer to */ /* (dynamically allocated) wjstbl in provided wjstbl_ptr_ptr arg */ /* NOTE: returns - (# pairs) if some keywords began w/special chars */ /* and some did not */ /* See lookup_wjstbl routines in utils.c for info about wjstbls */ /* Parse things like */ /* a=b; *foo* = bar; ENABLE_FEATURE_X */ /* [x1]KEYWORD1[x1] [y1 VALUE1z1] [x2]KEYWORD2[x2] [y2 VALUE2z2] */ /* x, y, and z are all characters, but come from strings of perm- */ /* issible characters. Also allow for a pair of quotation characters */ /* to permit literal representation of x, y, & z chars in VALUEs */ /* (quotation characters may be nested to allow for THEIR literal */ /* representation) */ /* The sets of quotation, x, y, and z characters must be disjoint */ /* (all but y for logical reasons; y by design choice). The x */ /* characters before and after keywords must match (parsing design */ /* choice). x characters must not be legal JGOFS variable name */ /* characters (parsing design choice - presence/absence of x */ /* characters can then distinguish selections/projections from other */ /* a=b arguments. */ /* Unquoted leading and trailing white space is removed from */ /* KEYWORDs and VALUEs. If a VALUE begins and ends with a quotation */ /* character, that pair of characters is removed */ /* x characters are removed from KEYWORDs */ /* If a KEYWORD is supplied without a VALUE, the string TRUE is */ /* assigned as the value */ /* Coded from create_wjstbl (in ioopen_routines.c, in defgb release) */ { int paircnt,outsize; char *key,*val; char *pair_ptr,*next_pair_ptr,*copy_keyword_pair_ptr; char *next_item_ptr; char *wjstbl_ptr,*wjstbl_start; char *ptr; char *msg = NULL; char wjstbl_separators[2] = {WJSTBL_SEPARATOR,WJSTBL_WHITE_SPACE}; Logical any_specials = FALSE; Logical any_non_specials = FALSE; if (sep_array == NULL) err("No separator array provided for keyword/value parsing",""); if (sep_array[BETWEEN_PAIRS] == NULL) err("No between_pairs separators provided for keyword/value parsing",""); if (sep_array[WITHIN_PAIRS] == NULL) err("No within_pairs separators provided for keyword/value parsing",""); if ( ! char_sets_disjoint(sep_array,PARSE_SEPARATOR_ARRAY_SIZE) ) err ("Separator sets for keyword/value parsing not disjoint", err_msg_id_str); if (keyword_pair_str == NULL) err("Null keyword/values provided for keyword/value parsing",""); if (*keyword_pair_str == '\0') err("No keyword/values provided for keyword/value parsing",""); /* Pair count as determined below can be high since it counts */ /* pair separators that might be "quoted out". Hopefully somebody */ /* is not quoting out thousands of separators */ paircnt=0; next_pair_ptr = keyword_pair_str; while ( ( next_pair_ptr = strpbrk(next_pair_ptr,sep_array[BETWEEN_PAIRS]) ) != NULL ) { next_pair_ptr++; paircnt++; } paircnt++; /* Output buffer */ /* Sizing on the high side because of paircnt (above) and because */ /* there is stuff in input string (white space, special chars, */ /* etc) that is not moved to output buffer */ /* +3 for 2 WJSTBL_SEPARATORs and 1 WJSTBL_WHITE_SPACE */ /* +1 for '\0' */ outsize = strlen(keyword_pair_str) + 3*paircnt + 1; wjstbl_start = (char *) malloc (outsize); if (wjstbl_start == NULL) { msg = buildstring ("Could not allocate memory for keyword parsing\n", err_msg_id_str, "Number of bytes to be allocated = ", " creating err msg 1"); errn (msg,outsize); } wjstbl_ptr = wjstbl_start; /* Need a copy of input string because we are going to slice it up */ /* and need to be able to write into it (as well as provide un- */ /* sliced version in error messages). */ strdupl (©_keyword_pair_ptr,keyword_pair_str,err_msg_id_str); next_pair_ptr = copy_keyword_pair_ptr; paircnt=0; /* Loop per entry (pair) in input string */ while ( (pair_ptr = nxttok (next_pair_ptr, sep_array[BETWEEN_PAIRS], &next_pair_ptr, sep_array[QUOTES], TRUE, TRUE) ) != NULL ) { key = nxttok(pair_ptr, sep_array[WITHIN_PAIRS], &next_item_ptr, NULL, TRUE, TRUE); if (key == NULL) { msg = buildstring ("No keyword in pair somewhere in string ", keyword_pair_str,NULL, " creating err msg 2"); err (msg,err_msg_id_str); } if ( (key = strip_space(key)) == NULL ) { msg = buildstring ("No keyword in pair somewhere in string ", keyword_pair_str,NULL, " creating err msg 2a"); err (msg,err_msg_id_str); } if (strpbrk(key,wjstbl_separators) != NULL) { msg = buildstring("Table separator appears in keyword ", key, "\n NB: separator probably an unprintable or space char", " creating err msg 5"); err (msg,err_msg_id_str); } /* If keyword begins w/'special' character find its mate and */ /* remove both */ if (strchr(sep_array[SPECIALS],*key) == NULL) { any_non_specials = TRUE; } else { any_specials = TRUE; ptr = strchr(key+1,*key); if (ptr == NULL) { msg =buildstring ("Unpaired special characters enclosing keyword ", key,NULL, " creating err msg 6"); err (msg,err_msg_id_str); } if (*(ptr+1) != '\0') { msg =buildstring ("Paired special characters do not enclose keyword ", key,NULL, " creating err msg 7"); err (msg,err_msg_id_str); } key++; *ptr = '\0'; } if (lookup_wjstbl(key,wjstbl_ptr) != NULL) { msg =buildstring ("keyword appears twice in list\n keyword = ",key,NULL, " creating err msg 8"); err (msg,err_msg_id_str); } val = nxttok(next_item_ptr, sep_array[WITHIN_PAIRS], &next_item_ptr, sep_array[QUOTES], TRUE, TRUE); if (val != NULL) val = strip_space(val); if (val == NULL) val = "TRUE"; if (strpbrk(val,wjstbl_separators) != NULL) { msg = buildstring("Table separator appears in string ", val, "\n NB: separator probably an unprintable or space char", " creating err msg 15"); err (msg,err_msg_id_str); } ptr = nxttok(next_item_ptr, sep_array[WITHIN_PAIRS], &next_item_ptr, sep_array[QUOTES], TRUE, TRUE); if (ptr != NULL) { msg = buildstring("Pair appears to be a triple somewhere in ", keyword_pair_str,NULL, " creating err msg 16"); err (msg,err_msg_id_str); } *wjstbl_ptr++ = WJSTBL_SEPARATOR; while (*key != '\0') *wjstbl_ptr++ = *key++; *wjstbl_ptr++ = WJSTBL_SEPARATOR; while (*val != '\0') *wjstbl_ptr++ = *val++; *wjstbl_ptr++ = WJSTBL_WHITE_SPACE; paircnt++; } *wjstbl_ptr = '\0'; *wjstbl_ptr_ptr = wjstbl_start; free(copy_keyword_pair_ptr); return (any_specials && any_non_specials) ? -paircnt : paircnt; } char *set_option_string(option_name,option_default,wjstbl,n_wjstbl_args_left) int *n_wjstbl_args_left; char *wjstbl,*option_name,*option_default; /* Return "something" corresponding to option_name as follows: */ /* If found in wjstbl, return value from there (& adjust # wjstbl */ /* args still to be processed) */ /* If not found there, look for env var "option_name" */ /* If not found there, return option_default passed to us. */ /* Appropriate setting of default (eg, NULL) allows determination */ /* of whether anything was explicitly provided */ { char *ptr,*term_char; /* extract_wjstbl wants buffer from us. malloc one per call so */ /* multiple calls to this routine do not result in a single buffer */ /* being overwritten. This introduces a memory leak, since a */ /* caller of this routine does not know when the pointer returned */ /* should be freed */ char *option_val; int size_wjstbl_val = 100; if (*n_wjstbl_args_left > 0) { option_val = (char *)malloc(size_wjstbl_val); if (option_val == NULL) errn ("Could not get space for wjstbl val buffer. Nbytes = ", size_wjstbl_val); switch (extract_wjstbl(option_val,size_wjstbl_val,option_name,wjstbl)) { case NOT_VALID: ptr = buildstring("Size of value string for keyword ", option_name, " exceeds buffer size. Buffer size = ", "err msg in set_option_string"); errn (ptr,size_wjstbl_val); case TRUE: (*n_wjstbl_args_left)--; return option_val; default: free (option_val); break; } } if ( (option_val = getenv(option_name)) == NULL ) option_val = option_default; return option_val; }