/* dct ("dictionary") routines for JGOFS data system */ /* G Flierl pre 1997 */ /* ==================== search sequence: ============= //machine or /machine (see is_a_remote_object in utils.c) path/tail - tail in path/.objects [local] /jgpath/tail - tail in OBJECT_ROOT/jgpath/.objects /jgpath/tail - tail in OBJECT_ROOT/jgpath/.remoteobjects path/exec - executeable program METHOD_ROOT/path/exec [local || dctcount<8 (not first pass)] path/datafile - use def method on local file [local] Also see dctsearch_operation_and_errs.doc ===================================================== */ /* DETAILS ABOUT dct v 2.0 ROUTINE CHANGES (Aug 2012) AT END OF SOURCE */ #define DCT_VERSION "dct version 2.0a 8 May 2013" /* 8 May 2013. v 2.0a WJS */ /* Bug fix: Parenthesis removal code assumed that if there is an */ /* initial paren, there is a trailing paren and they match. As */ /* the song says, it ain't necessarily so */ /* By the way, v 2.0 was "let out" without completing status */ /* handling. Don't know how much we'll do this time around */ /* [begin v 2.0a] */ /* 28 Jan 2013. v 2.0 WJS */ /* Fix bad strdupl calls. Amazingly, most worked! */ /* Begin work to implement USE_MERGEPARAMS */ /* Fiddle w/final default (to def ). Probably won't test it any */ /* more than replaced code was tested */ /* 29 Aug 2012. v 2.0 WJS */ /* More changing over to dynamic memory allocation scheme, while */ /* removing code that assumes it can write into input args */ /* Eliminate some fixed-size temp buffers */ /* Write new dctsearch which returns pointers to correctly sized */ /* buffers and a shell which behaves the old way. Eventually */ /* need to change dctsearch callers (jdb & serv, at least) to */ /* directly call the new routine; else problem continues */ /* Write new scandct and shell for same reasons as new dctsearch */ /* Nothing "in JGOFS system" outside of dct.c calls scandct, so */ /* this should just be a precaution. Another reason is to re- */ /* port the status of scandct's putenv call (see v 1.2b comment) */ /* New mergeparams, getting rid of mysterious [] stuff, along w/its */ /* other problems. Needs to be controlled w/compile-time switch */ /* in case dctsearch users need the [] stuff (neither Chris nor */ /* Bob remembers it at all) */ /* [begin v 2.0] */ /* 24 Jun 2010. v 1.5a WJS */ /* First-pass status for DCTSEARCH_METHOD_HISTORY should be */ /* _WILLNOTBETESTED rather than _UNTESTED */ /* 3 Jul 2009. v 1.5 WJS */ /* Ignore lines w/# in col 1 */ /* Ignore 0-length object name specs and all-whitespace object name */ /* specs. Should probably error, and should probably check more */ /* but not now */ /* [needs core.h 2.0b] */ /* 29 Feb 2008. v 1.4 WJS */ /* Change way we determine remote object */ /* 18 Aug 2007. v 1.3 WJS */ /* Take a shot at diagnosing dctsearch failure */ /* Not backward compatible - keep old behavior by compiling with */ /* DCTSEARCH_EXTENDED_ERROR_INFO=0 */ /* dctsearch returns 0/1 for failed/succeeded. Failure info is */ /* also copied into the string in which the caller expects to get */ /* the filespec of the method, etc. That info up until now has */ /* been 1 of 2 strings. In the vast majority of situations, that */ /* string is "Cannot be found in dictionary:" */ /* We cannot expand the status space since programs explicitly */ /* test for status 0. Instead, we will hope that programs do not */ /* explicitly test the return strings, and we will put info there. */ /* We are further constrained since there can be no length check */ /* on the return buffer. Therefore, we will attempt to keep the */ /* length short, resulting in some cryptic stuff */ /* Add dctsearch_trace to return a full, readable trace */ /* Considerable "housecleaning" (loop that reads .objects files, */ /* switching from "notfound" variable to "found" variable, etc) */ /* [Begin 1.3] */ /* 10 Dec 2004. v 1.2c WJS */ /* Bug fix: incorrect strdupl call */ /* [Begin 1.2c] */ /* 25 Aug 2004. v 1.2b WJS */ /* serv had its own putenv. Recode dct to use the system one */ /* Not checking return value yet - that's a job for dct v 2.0 */ /* Put MAX_SERV_DCT_BUF (formerly MAX_BUF) in core.h so serv & */ /* dct can be coordinated */ /* Standardize version stuff */ /* [Begin 1.2b] */ /* 11 Feb 2004. v 1.2a WJS */ /* Declare putenv to avoid diagnostic. #include stdlib since man */ /* page says it "goes with" putenv - don't know why. */ /* Remove declaration for getenv since it's unused */ /* [Begin 1.2a] */ /* 17 Jan 2004. v 1.2 WJS */ /* Divider between obj defn and env vars was 1st blank in obj defn */ /* Arrange things not to count blanks between parens */ /* Put a void type on mergeparams (this mod made on synthesis' */ /* version 6 Nov 2000 by Chris Hammond) */ /* [Begin 1.2] */ #include "core.h" /* Next value limits number of times search code is executed */ /* Typically, there is one pass to find the object defn, and another */ /* to find the method. However, each defn is allowed to point to */ /* another, so we need a limit. Has "always" been 8 */ #define MAX_DCTCOUNT 8 /* Next value controls whether dctsearch uses the old mergeparams */ /* code. User would need to spec parameter as 1 in the compilation */ /* to effect the regression */ #ifndef USE_MERGEPARAMS #define USE_MERGEPARAMS 0 #endif /* #define OBJECT_ROOT "/d3/glenn/jg/objects" #define METHOD_ROOT "/d3/glenn/jg/methods/" #define DEBUG */ int putenv(); /* from utils.c (jgofs.a) */ char *buildstring(); void errn(); int is_a_remote_object(); char *strdupl(); char *strip_space(); /* scandct -> search status communication globals (sigh) */ int scandct_errno; Logical scandct_dict_exists; Logical scandct_dict_accessible; /* status history pointers. static so that we can dynamically */ /* allocate space once no matter how many times we're called */ /* (Actually, too lazy to free every time if I left things dynamic; */ /* this way I "leak" this much memory exactly once) */ static char *status_history[DCTSEARCH_NUMBER_HISTORIES]; static char *userobj_status = NULL; static char *obj_status,*remobj_status,*method_status; static char *datfile_status,*default_method_status; /************************************************************************/ char *dct_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[] = DCT_VERSION"/"COREH_VERSION; return version; } Logical remove_encasing_parens(outstring,instring,quote_char) /* Returns FALSE if there were unmatched quote_chars or unmatched */ /* parens */ char **outstring; char *instring; char quote_char; { char *first,*last,*ptr; Logical init_paren_matches_close_paren,open_quote_in_effect; int net_open_parens; *outstring = instring; if (instring == NULL) return TRUE; if (*instring != '(') return TRUE; first = instring; init_paren_matches_close_paren = TRUE; net_open_parens = 1; open_quote_in_effect = FALSE; last = first + strlen(first) - 1; for (ptr = first+1; ptr <= last; ptr++) { if (*ptr == quote_char) open_quote_in_effect = (! open_quote_in_effect); if ( ! open_quote_in_effect) { if (*ptr == '(') net_open_parens++; if (*ptr == ')') net_open_parens--; /* Idea: if net_open_parens reaches 0 at any place before the end */ /* then the initial paren that got us into this loop is not */ /* matched with any final paren. We could quit, but we continue */ /* for syntax-checking purposes */ if ( (net_open_parens == 0) && init_paren_matches_close_paren && (ptr < last) ) init_paren_matches_close_paren = FALSE; } } if (open_quote_in_effect || (net_open_parens != 0)) return FALSE; if (init_paren_matches_close_paren) { *last = '\0'; first++; first = strip_space(first); *outstring = first; } return TRUE; } int mergeparams1(result,user_sel_proj,dict_params) /* Concatenates dict_params and user_sel_proj, in that order (NOT */ /* arbitrary - consider obj defn of rs(defgb) - defgb MUST be */ /* first in resulting param string) */ /* Each input string has whitespace stripped. */ /* If either input string enclosed in parens, remove them */ /* Strip white space again */ /* If both strings are present, separate with a comma */ /* If there are no characters in the result (input null, input empty, */ /* etc), or if there is an error, result is NULL */ /* Function return is an error status */ /* [to be defined] */ /* Differences from mergeparams */ /* mergeparams assumes dict_params has parens and user_sel_proj */ /* does not, and does not strip whitespace is not an issue */ /* mergeparams truncated dict_params to an empty string */ /* mergeparams treated []-enclosed strings in dict_params in a */ /* a special fashion; moving them to the end of the result string */ /* under certain circumstances (selection in user_sel_proj? */ char **result; char *user_sel_proj,*dict_params; { int len_user_sel_proj,len_dict_params; char *start_user_sel_proj,*start_dict_params; char *result_ptr; char *free1,*free2,*ptr; /* We are forced into considering parens embedded in selections. */ /* In turn, this leads to the question of whether such parens are */ /* part of the syntax or part of the selection. At this stage, I */ /* just have no idea what the "allowed" selection syntax is. */ /* Accordingly, I'm arbitrarily defining and using a quote char- */ /* acter. Of course, this belongs in a .h file, etc. Oh well ... */ #define QUOTE_CHAR '"' *result = NULL; if (user_sel_proj == NULL) { if (dict_params == NULL) return MP1_NO_INPUT; if (*dict_params == '\0') return MP1_NO_INPUT; } if ((dict_params == NULL) && (*user_sel_proj == '\0')) return MP1_NO_INPUT; free1 = strdupl (&start_user_sel_proj,user_sel_proj,"mergeparams1 temp copy 1"); free2 = strdupl (&start_dict_params,dict_params,"mergeparams1 temp copy 2"); start_user_sel_proj = strip_space(start_user_sel_proj); start_dict_params = strip_space(start_dict_params); if ((start_user_sel_proj == NULL) && (start_dict_params == NULL)) { free (free1); free (free2); return MP1_NO_SIGNIF_INPUT; } if ( ! remove_encasing_parens (&start_user_sel_proj,start_user_sel_proj,QUOTE_CHAR) ) return MP1_SYNTAX_ERROR_ARG1; if ( ! remove_encasing_parens (&start_dict_params,start_dict_params,QUOTE_CHAR) ) return MP1_SYNTAX_ERROR_ARG2; if ((start_user_sel_proj == NULL) && (start_dict_params == NULL)) { free (free1); free (free2); return MP1_NO_SIGNIF_INPUT; } if (start_user_sel_proj == NULL) { strdupl(result,start_dict_params,"mergeparams1 returning arg 2"); } else if (start_dict_params == NULL) { strdupl(result,start_user_sel_proj,"mergeparams1 returning arg 1"); } else { len_user_sel_proj = strlen(start_user_sel_proj); len_dict_params = strlen(start_dict_params); *result = (char *)malloc(len_user_sel_proj + 1 + len_dict_params + 1); if (*result == NULL) { free (free1); free (free2); return MP1_MALLOC_FAILURE; } strcpy(*result,start_dict_params); * (*result + len_dict_params) = ','; strcpy ( (*result + len_dict_params + 1) , start_user_sel_proj ); } free (free1); free (free2); return MP1_OK; } int handle_dict_params(params_to_update,method_or_recursive_objspec,dict_results) /* Essentially splits dict results on (, if any, and merges what's */ /* after the ( with the params_to_update (presumably the user-spec'ed */ /* params), replacing the latter. params_to_update must be dynamic- */ /* ally allocated - pre-updated string is freed if there is an update */ /* Returns mergeparams1 status or -1 if using old mergeparams */ /* Returns what precedes any ( in a dynamically allocated string */ /* pointed to by method_or_recursive_objspec */ char **params_to_update,**method_or_recursive_objspec; char *dict_results; { char *paren_ptr; char *tmp; int status; strdupl(method_or_recursive_objspec,dict_results,"in handle_dict_params"); paren_ptr = strchr(*method_or_recursive_objspec,'('); if (paren_ptr == NULL) return MP1_OK; #if USE_MERGEPARAMS mergeparams(*params_to_update,paren_ptr); return -1; #else tmp = *params_to_update; status = mergeparams1(params_to_update,tmp,paren_ptr); free (tmp); *paren_ptr = '\0'; return status; #endif } int scandct1(result,which_objects_file,object_spec) /* which_objects_file is either .objects or .remoteobjects */ /* (as of dct 2.0 and within JGOFS system */ /* object_spec, if local, includes directory for which_objects_file */ /* (explicitly or implicitly (eg, off server's OBJECT_ROOT). */ /* There may be another wrinkle or 2 */ char *which_objects_file,*object_spec; char **result; { /* Next buffer now (dct v 2.0) used only for holding .objects file */ /* records. Not sure how that needs coordination w/serv any more */ char tdic[MAX_SERV_DCT_BUF]; FILE *fdic; char *s,*t; char *dctname,*dict_search_string; char *sp; int nb,l,putenv_status; Logical found; /* Next comment based on the idea that putenv does NOT make a copy */ /* of its argument [from man putenv on talia.whoi.edu, a linux box */ /* "Thus, it is an error is to call putenv() with an auto- */ /* matic variable as the argument, then return from the call- */ /* ing function while string is still part of the environment."] */ /* Next is a volatile pointer that is set to dynamically allocated */ /* memory. Thus, the pointer will be lost upon function return, */ /* and the memory cannot be freed. This is OK, since idea is to */ /* have this memory survive exit from scandct so env vars will be */ /* "present" when/if calling program needs them. Cannot use a */ /* static buffer here in case scandct is called more than once */ char *list_of_env_vars; /* Make copies since original code assumes it can write into input */ /* args */ strdupl(&s,which_objects_file,"Copying scandct1 arg 1"); strdupl(&t,object_spec,"Copying scandct1 arg 2"); #ifdef DEBUG printf("Searching >%s< for >%s<\n",s,t); #endif /* Routine is expected to return the putenv status, but we might not */ /* end up calling putenv. Return "putenv normal", without bothering */ /* to try to figure out why 0 is normal. It's documented that way, */ /* but I don't think the c library has it parametrized */ putenv_status = 0; /* Default return value to NULL, which takes care of the various error */ /* conditions, as well as the actual "not found" */ *result = NULL; sp = strrchr(t,'/'); if (sp) { *(sp++)='\0'; if (strip_space(sp) == NULL) return putenv_status; dctname = buildstring(t,"/",s,"building dictionary name string"); } else { if (strip_space(t) == NULL) return putenv_status; sp = t; dctname = s; } dict_search_string = buildstring(sp,"=",NULL,"adding = to object name"); free (t); #ifdef DEBUG printf("Open >%s< and look for >%s<\n",dctname,dict_search_string); #endif scandct_errno = 0; scandct_dict_exists = TRUE; scandct_dict_accessible = TRUE; fdic = fopen(dctname,"r"); if (fdic == NULL) { scandct_errno = errno; scandct_dict_exists = (access(dctname,F_OK) == 0); scandct_dict_accessible = (access(dctname,R_OK) == 0); free (s); if (dctname != s) free (dctname); return putenv_status; } free (s); if (dctname != s) free (dctname); found = FALSE; while (fgets(tdic,MAX_SERV_DCT_BUF-1,fdic) != NULL) { /* If/when more restrictions get added here, note that */ /* we guarantee to ignore empty lines and lines consisting */ /* entirely of white space. Not an issue in the forseeable */ /* future because there is always an = in the search string. */ if (tdic[0] != DCTSEARCH_COMMENT_CHAR) { if (strstr(tdic,dict_search_string) == tdic) { found = TRUE; break; } } } free (dict_search_string); if ( ! found) { if ( ! feof(fdic) ) scandct_errno = errno; fclose(fdic); /* Prefer read failure status to any close status */ return putenv_status; } fclose(fdic); /* ... and don't care a lot about close status anyway */ t = strchr(tdic,'=') + 1; l = strlen(t); if (t[l-1] == '\n') t[--l] = '\0'; nb=0; for(sp=t; sptmp) if(*(k-1) == ')') *(k-1)=','; j=req-1; reqproj=0; nb=0; i=1; while(*(++j)){ *(k++)= *j; switch(*j){ case '(':nb++;break; case ')':nb--;break; case '<': case '>': case '=':i=0;break; case ',':if(nb==0 && i==1)reqproj=1; i=1; break; }; }; *k=0; if(reqproj || dctopt) { while(j=strstr(tmp,",,"))strcpy(j,j+1); strcpy(req,tmp); *dct=0; return; }; j=dct; nb=0; while(*(++j)){ switch(*j){ case '[':nb++;break; case ']':nb--;break; default:if(nb) *(k++) = *j;break; }; }; if(k>tmp) { if(*(k-1) == ',') *(k-1)=0; else *k=0; while(j=strstr(tmp,",,"))strcpy(j,j+1); strcpy(req,tmp); } else *req=0; *dct=0; return; } void set_status_history_buffers() { int i; i = (MAX_DCTCOUNT+1) * sizeof(char); userobj_status = (char *) malloc (i); obj_status = (char *) malloc (i); remobj_status = (char *) malloc (i); method_status = (char *) malloc (i); datfile_status = (char *) malloc (i); default_method_status = (char *) malloc (i); if ( (userobj_status == NULL) || (obj_status == NULL) || (remobj_status == NULL) || (method_status == NULL) || (datfile_status == NULL) || (default_method_status == NULL) ) errn ("Could not get memory for status buffers. Nbytes=",i); status_history[DCTSEARCH_USEROBJ_HISTORY] = userobj_status; status_history[DCTSEARCH_OBJ_HISTORY] = obj_status; status_history[DCTSEARCH_REMOBJ_HISTORY] = remobj_status; status_history[DCTSEARCH_METHOD_HISTORY] = method_status; status_history[DCTSEARCH_DATFILE_HISTORY] = datfile_status; status_history[DCTSEARCH_DEFMETHOD_HISTORY] = default_method_status; return; } char *pack_status_history(history) char *history[]; { int i; char *ptr,*start_untesteds,*out_ptr; char end_delimiter; /* +1 is for separator characters; 3 = delimiters & trailing \0 */ /* Note that old string was 30 chars long... */ static char out_buffer[DCTSEARCH_NUMBER_HISTORIES * (MAX_DCTCOUNT + 1) + 3]; /* Reduce length of status strings by removing trailing _UNTESTED */ /* and _WILLNOTBETESTED */ for (i=0; i < DCTSEARCH_NUMBER_HISTORIES; i++) { ptr = history[i]; if (*ptr == DCTSEARCH_STATUS_WILLNOTBETESTED) { /* If "will not be tested" in first pass, will never be tested */ /* Leave 1 "placeholder" and remove the rest */ start_untesteds = ptr + 1; } else { start_untesteds = NULL; /* Trim trailing .s. Start from 2nd char so if whole string */ /* is .s , we still have 1 left */ while ( *(++ptr) != '\0') { if (*ptr == DCTSEARCH_STATUS_UNTESTED) { if (start_untesteds == NULL) start_untesteds = ptr; } else { start_untesteds = NULL; } } } if (start_untesteds != NULL) *start_untesteds = '\0'; } ptr = DCTSEARCH_HISTORY_DELIMITERS; *out_buffer = *(ptr++); end_delimiter = *ptr; out_ptr = out_buffer + 1; for (i=0; i < DCTSEARCH_NUMBER_HISTORIES; i++) { ptr = history[i]; while (*ptr != '\0') *(out_ptr++) = *(ptr++); *(out_ptr++) = DCTSEARCH_HISTORY_SEPARATOR; } *(--out_ptr) = end_delimiter; *(++out_ptr) = '\0'; return out_buffer; } char set_status(exists,accessible,io_error) Logical exists,accessible,io_error; /* DCTSEARCH_STATUS_OK File exists & is accessible */ /* DCTSEARCH_STATUS_NOSUCHFILE Does not exist (or dirs inaccessible) */ /* DCTSEARCH_STATUS_NOACCESS Exists; not accessible */ /* DCTSEARCH_STATUS_IOERR DCTSEARCH_STATUS_OK but read problem */ { char status; if (exists) { if (accessible) { status = (io_error) ? DCTSEARCH_STATUS_IOERR : DCTSEARCH_STATUS_OK; } else { status = DCTSEARCH_STATUS_NOACCESS; } } else { status = DCTSEARCH_STATUS_NOSUCHFILE; } return status; } int dctsearch1(obj,method,params,localflag) char *obj,**method,**params; int localflag; /* Do not free the string returned into method unless function */ /* returns 1 . If the function returns 0 , method may point to */ /* an error message string. */ { int dctcount; char *pathreq; char *parreq; char *method_candidate; char *tmp; char *ptr; Logical dctfound,free_tmp; int i,status; /* Next are returns from scandct1 */ int putenv_status; char *dict_search_results; if (userobj_status == NULL) set_status_history_buffers(); strdupl(&pathreq,obj,"dct: copying object string"); strdupl(&parreq,*params,"dct: copying parameter string"); dctcount=0; while (dctcount < MAX_DCTCOUNT) { dctfound = FALSE; for (i=0; i < DCTSEARCH_NUMBER_HISTORIES; i++) { if (localflag) status_history[i][dctcount] = DCTSEARCH_STATUS_UNTESTED; else status_history[i][dctcount] = ( (i == DCTSEARCH_USEROBJ_HISTORY) || (i == DCTSEARCH_DATFILE_HISTORY) || (i == DCTSEARCH_METHOD_HISTORY) || (i == DCTSEARCH_DEFMETHOD_HISTORY) ) ? DCTSEARCH_STATUS_WILLNOTBETESTED : DCTSEARCH_STATUS_UNTESTED; } /* //machine ... */ #ifdef DEBUG printf("test // ... %s**%s\n",pathreq,parreq); #endif if (is_a_remote_object(pathreq) != 0) { *method = pathreq; *params = parreq; return 1; } /* check for object dictionary */ if (localflag) { #ifdef DEBUG printf("test local obj... %s**%s\n",pathreq,parreq); #endif putenv_status = scandct1(&dict_search_results,".objects",pathreq); if (putenv_status != 0) { *method = strerror(putenv_status); return 0; } if (dict_search_results != NULL) { status = handle_dict_params(&parreq,&method_candidate,dict_search_results); /* do something with status */ if (is_a_remote_object(method_candidate) != 0) { *method = method_candidate; *params = parreq; return 1; } pathreq = method_candidate; dctfound = TRUE; } userobj_status[dctcount] = set_status(scandct_dict_exists, scandct_dict_accessible, (scandct_errno != 0) ); } /* check system .objects dictionary */ if ( (! dctfound) && (*pathreq == '/') ) { if (*pathreq) { tmp = buildstring(OBJECT_ROOT,"/",pathreq,"Building objroot subdir"); free_tmp = TRUE; } else { tmp = OBJECT_ROOT; free_tmp = FALSE; } #ifdef DEBUG printf("test sys local ... %s**%s\n",tmp,parreq); #endif putenv_status = scandct1(&dict_search_results,".objects",tmp); if (free_tmp) free (tmp); if (putenv_status != 0) { *method = strerror(putenv_status); return 0; } if (dict_search_results != NULL) { status = handle_dict_params(&parreq,&method_candidate,dict_search_results); /* do something with status */ if (is_a_remote_object(method_candidate) != 0) { *method = method_candidate; *params = parreq; return 1; } pathreq = method_candidate; dctfound = TRUE; } obj_status[dctcount] = set_status(scandct_dict_exists, scandct_dict_accessible, (scandct_errno != 0) ); } /* check system .remoteobjects dictionary */ if ( (! dctfound) && (*pathreq == '/') ) { if (*pathreq) { tmp = buildstring(OBJECT_ROOT,"/",pathreq,"Building objroot subdir2"); free_tmp = TRUE; } else { tmp = OBJECT_ROOT; free_tmp = FALSE; } #ifdef DEBUG printf("test sys global ... %s**%s\n",tmp,parreq); #endif putenv_status = scandct1(&dict_search_results,".remoteobjects",tmp); if (free_tmp) free (tmp); if (putenv_status != 0) { *method = strerror(putenv_status); return 0; } if (dict_search_results != NULL) { status = handle_dict_params(&parreq,&method_candidate,dict_search_results); /* do something with status */ if (is_a_remote_object(method_candidate) != 0) { *method = method_candidate; *params = parreq; return 1; } pathreq = method_candidate; dctfound = TRUE; } remobj_status[dctcount] = set_status(scandct_dict_exists, scandct_dict_accessible, (scandct_errno != 0) ); } /* check for system method (not first time through, though) */ if( (! dctfound) && (localflag || dctcount > 0) ) { tmp = buildstring (METHOD_ROOT,pathreq,NULL,"Building system method string"); #ifdef DEBUG printf("test sys meth ... %s**%s\n",tmp,parreq); #endif method_status[dctcount] = set_status( (access(tmp,F_OK) == 0), (access(tmp,X_OK) == 0), FALSE ); if (method_status[dctcount] == DCTSEARCH_STATUS_OK) { *method = tmp; *params = parreq; return 1; } } /* local file - use default method */ if( (! dctfound) && localflag ) { #ifdef DEBUG printf("test def file ... %s**\n",pathreq); #endif datfile_status[dctcount] = set_status( (access(pathreq,F_OK) == 0), (access(pathreq,R_OK) == 0), FALSE ); if (datfile_status[dctcount] == DCTSEARCH_STATUS_OK) { tmp = buildstring ( "(", pathreq, ")", "parenthesizing pathreq" ); status = handle_dict_params(&parreq,&method_candidate,tmp); /* do something with status */ default_method_status[dctcount] = set_status( (access(DCTSEARCH_DEFAULT_METHOD,F_OK) == 0), (access(DCTSEARCH_DEFAULT_METHOD,X_OK) == 0), FALSE ); if (default_method_status[dctcount] == DCTSEARCH_STATUS_OK) { strdupl(&pathreq,DCTSEARCH_DEFAULT_METHOD, "Copying default method name"); dctfound = TRUE; } } } /* error - return info in buffer where method name was expected */ if (! dctfound) { if (DCTSEARCH_EXTENDED_ERROR_INFO) { /* Turn char arrays into strings */ for (i=0; i < DCTSEARCH_NUMBER_HISTORIES; i++) status_history[i][dctcount+1] = '\0'; *method = pack_status_history(status_history); } else { *method = "Cannot be found in dictionary:"; } return 0; } dctcount++; } *method = "Too many iterations in dictionary - check for loops:"; return 0; } char *dctsearch_trace() { /* Easiest option is to mod dctsearch to use lengthen_str to build */ /* diagnostics "on the fly", one line per file access. Make this */ /* static - when caller comes here, give them the pointer */ /* Another approach is to save the _HISTORIES*_MAX_DCTCOUNT file specs */ /* along w/the status buffer. Easier to do options */ /* Options: by default, do NOT want to include OBJECT_ROOT, */ /* METHOD_ROOT, ".objects", etc for security reasons. Control this */ /* via ?? can't use a per-object env var!!. Caller might want */ /* successes, might not. Might want host name, might not. Might */ /* want chronological trace or an by-file-type trace. Might want */ /* reverse chronological trace! */ /* Practically speaking, calling code is going to select one format, */ /* option set, etc. Human will see output and not be happy, so con- */ /* trol has to be from outside the program. Further, consider that */ /* if search takes place on remote box, can't be locally controlled */ /* Might just want "easiest"... */ return NULL; } int dctsearch(obj,method,params,localflag) char *obj,*method,*params; int localflag; { int dctsearch1_status; char **dynamic_method,**dynamic_params; char *copy_method,*copy_params; strdupl(©_method,method,"dctsearch: copying method"); strdupl(©_params,params,"dctsearch: copying params"); dynamic_method = ©_method; dynamic_params = ©_params; dctsearch1_status = dctsearch1(obj,dynamic_method,dynamic_params,localflag); free (copy_method); free (copy_params); if (*dynamic_method != NULL) { /* Note potential for buffer overflow in following statement. */ /* That's the main reason to convert to dctsearch1 */ strcpy(method,*dynamic_method); free (*dynamic_method); } else { *method = '\0'; } if (*dynamic_params != NULL) { /* Note potential for buffer overflow in following statement. */ /* That's the main reason to convert to dctsearch1 */ strcpy(params,*dynamic_params); free (*dynamic_params); } else { *params = '\0'; } return dctsearch1_status; } int scandct(s,t) char *s,*t; { char *result_of_dict_lookup; /* Note that scandct1 returns status of putenv, but scandct never */ /* dealt w/that before, so for consistency, ignore the status. Note */ /* that if things are/were able to proceed after a putenv failure, */ /* logical consequences are/were extreme - env vars upon which the */ /* object depends would NOT be in the environment as expected */ scandct1(&result_of_dict_lookup,s,t); if (result_of_dict_lookup ==NULL) return 0; /* Note potential for buffer overflow in following statement. That's */ /* the main reason to convert to scandct1 */ strcpy(t,result_of_dict_lookup); return 1; } /* main(argc,argv) int argc; char *argv[]; { char met[1024],par[1024]; int i; if(dctsearch(argv[1],met,par,1)) printf("dctsearch = %s**%s\n",met,par); else printf("Not found\n"); } */ /* Routine changes dct 2.0 (Aug 2012) */ /* scandct -> scandct1 */ /* int scandct(s,t) */ /* Returns 1 or 0; overwrites t with result of scan */ /* int scandct1(scandct_result,s,t) */ /* scandct_result is char ** into which is placed a */ /* pointer to the result of the scan of the dictionary */ /* In the case where scandct returned 0 as the function */ /* value, scandct_result points to NULL. The pointer */ /* (if non-NULL) is to a dynamically allocated string, */ /* which can be freed when no longer needed */ /* The return value of scandct1 is the result of the */ /* attempt by scandct1 to put strings into the environ- */ /* ment should the object definition require that. 0 */ /* is OK; otherwise the value is an errno . */ /* mergeparams -> mergeparams1 */ /* dctsearch -> dctsearch1 */ /* int dctsearch(obj,method,params,localflag) */ /* Writes into both method and params, which are char * */ /* int dctsearch1(obj,method,params,localflag) */ /* Returns pointers to result strings. method and params */ /* are both char ** . */ /* If dctsearch1 was OK (return value != 0), pointers are */ /* to dynamically allocated strings, which can be freed */ /* If dctsearch1 was NOT OK, pointers are to string */ /* constants describing the error, so do NOT free */