/* 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 ===================================================== */ #define DCT_VERSION "dct version 1.5a 24 Jun 2010" /* 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 /* #define OBJECT_ROOT "/d3/glenn/jg/objects" #define METHOD_ROOT "/d3/glenn/jg/methods/" #define DEBUG */ int putenv(); /* from utils.c (jgofs.a) */ 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; } int scandct(s,t) char *s,*t; { FILE *fdic; char tdic[MAX_SERV_DCT_BUF],dctname[MAX_SERV_DCT_BUF],*sp; int nb,l; 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; #ifdef DEBUG printf("Searching >%s< for >%s<\n",s,t); #endif sp=strrchr(t,'/'); if(sp){ *(sp++)=0; strcpy(dctname,t); strcat(dctname,"/"); strcat(dctname,s); } else { strcpy(dctname,s); sp=t; }; if (strip_space(sp) == NULL) return 0; strcat(sp,"="); #ifdef DEBUG printf("Open >%s< and look for >%s<\n",dctname,sp); #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); return 0; } 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,sp) == tdic) { found = TRUE; break; } } } if ( ! found) { if ( ! feof(fdic) ) scandct_errno = errno; fclose(fdic); /* Prefer read failure status to any close status */ return 0; } fclose(fdic); /* ... and don't care a lot about close status anyway */ strcpy(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 dctsearch(obj,method,params,localflag) char *obj,*method,*params; int localflag; { int dctcount; char pathreq[MAX_SERV_DCT_BUF]; char metreq[MAX_SERV_DCT_BUF]; char parreq[MAX_SERV_DCT_BUF]; char tmp[MAX_SERV_DCT_BUF]; char *ptr; Logical dctfound; int i; if (userobj_status == NULL) set_status_history_buffers(); COPY_INTO_FIXED_LEN_BUFFER(pathreq,obj,"dct: copying object string"); COPY_INTO_FIXED_LEN_BUFFER(parreq,params,"dct: copying parameter string"); *method = '\0'; *params = '\0'; 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) { strcpy(method,pathreq); strcpy(params,parreq); return 1; } /* check for object dictionary */ if (localflag) { strcpy(tmp,pathreq); #ifdef DEBUG printf("test local obj... %s**%s\n",tmp,parreq); #endif if (scandct(".objects",tmp)) { ptr = strchr(tmp,'('); if (ptr) mergeparams(parreq,ptr); if (is_a_remote_object(tmp) != 0) { strcpy(method,tmp); strcpy(params,parreq); return 1; } strcpy(pathreq,tmp); dctfound = TRUE; } userobj_status[dctcount] = set_status(scandct_dict_exists, scandct_dict_accessible, (scandct_errno != 0) ); } /* check system .objects dictionary */ if ( (! dctfound) && (*pathreq == '/') ) { strcpy(tmp,OBJECT_ROOT); if (*pathreq) { strcat(tmp,"/"); strcat(tmp,pathreq); } #ifdef DEBUG printf("test sys local ... %s**%s\n",tmp,parreq); #endif if (scandct(".objects",tmp)) { ptr = strchr(tmp,'('); if (ptr) mergeparams(parreq,ptr); if (is_a_remote_object(tmp) != 0) { strcpy(method,tmp); strcpy(params,parreq); return 1; } strcpy(pathreq,tmp); dctfound = TRUE; } obj_status[dctcount] = set_status(scandct_dict_exists, scandct_dict_accessible, (scandct_errno != 0) ); } /* check system .remoteobjects dictionary */ if ( (! dctfound) && (*pathreq == '/') ) { strcpy(tmp,OBJECT_ROOT); if (*pathreq) { strcat(tmp,"/"); strcat(tmp,pathreq); } #ifdef DEBUG printf("test sys global ... %s**%s\n",tmp,parreq); #endif if (scandct(".remoteobjects",tmp)) { ptr = strchr(tmp,'('); if (ptr) mergeparams(parreq,ptr); if (is_a_remote_object(method) != 0) { strcpy(method,tmp); strcpy(params,parreq); return 1; }; strcpy(pathreq,tmp); 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) ) { strcpy(tmp,METHOD_ROOT); strcat(tmp,pathreq); #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) { strcpy(method,tmp); strcpy(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(tmp,F_OK) == 0), (access(tmp,R_OK) == 0), FALSE ); if (datfile_status[dctcount] == DCTSEARCH_STATUS_OK) { strcpy(tmp,"("); strcat(tmp,pathreq); strcat(tmp,")"); mergeparams(parreq,tmp); default_method_status[dctcount] = set_status( (access(tmp,F_OK) == 0), (access(tmp,X_OK) == 0), FALSE ); if (default_method_status[dctcount] == DCTSEARCH_STATUS_OK) { strcpy(pathreq,DCTSEARCH_DEFAULT_METHOD); dctfound = TRUE; } } } /* error - return info in buffer where method name was expected */ /* Potential overflow problem, of course... */ 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'; strcpy(method,pack_status_history(status_history)); } else { strcpy(method,"Cannot be found in dictionary:"); } return 0; } dctcount++; } strcpy(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; } /* 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"); } */