/* PATH_INFO_routines. WJS Jul 97 */ /* Purpose of these routines is to extract portions of information */ /* from the "PATH_INFO" environment variable as formatted by the JGOFS */ /* system, and to create a properly formatted string that can be used */ /* to created that environment variable. */ /* */ /* To get information, use the functions */ /* int get_level() */ /* char *get_object() */ /* char *get_options() */ /* char *get_protocol() */ /* Each takes an argument, which is usually NULL (details in the */ /* per-function comments, below) */ /* The package is missing get_dir, get_info, etc functions at */ /* present. See some comments at bottom */ /* */ /* To create a string that can be used by putenv, use the function */ /* char *make_PATH_INFO_putenv_string() */ /* See its documentation, below */ /* */ /* */ /* This implementation derived from code in outer from JGOFS */ /* release 1.5, but does not match it exactly. It will correctly */ /* deal with strings that JFOFS 1.5 will read, and hopefully will */ /* be flexible enough to deal with future strings. The get_protocol */ /* and get_level routines will validate things to more closely match */ /* the outer code (& therefore will probably need more modification */ /* in the future) */ /* */ /* At present, the environment variable PATH_INFO contains various */ /* information. Approximately, its layout is */ /* object_location,object_name,protocol,level,options */ /* options, if any, are contained in curly brackets. The rest of the */ /* string is formatted as a unix file spec, with the object name in */ /* the position of the file name, and the protocol/level string in */ /* the position of the file type. */ /* (Note that the URL precedes this string with the cgi-bin program */ /* that will process the string. Generally, it is not possible to */ /* tell where the object_location begins, but because the program is */ /* usually of the form "/jg/name" (eg, /jg/serv, /jg/dir), one can */ /* guess.) */ /* */ /* If PATH_INFO contains the strings .html or .flat (anywhere), */ /* those strings are considered "protocol" strings by outer. They */ /* must be followed by at least a 1 digit level. Other parts of the */ /* JGOFS system limit the level to 1 digit, but outer does not. In */ /* the absence of a protocol string, outer produces a default protocol */ /* */ #define PATH_INFO_ROUTINES_VERSION "path_info_routines version 1.1 9 Aug 1997" /* 9 Aug 97. WJS */ /* get_object & get_options entries */ /* [Begin version 1.1] */ /* 25 Jul 97. WJS */ /* [Begin version 1.0] */ /* Force version string into object files */ char *path_info_routines_version = PATH_INFO_ROUTINES_VERSION; #include "path_info_routines.h" #include #include #include #include /* All these functions are local; defined here so that they can */ /* be arbitrarily ordered in the source file */ char *parse_PATH_INFO_string(); char *make_PATH_INFO_putenv_string(); char *get_object(); char *get_params(); char *get_options(); int get_level(); char *copy_substring(); /************************************************************************/ /* get_info routines. */ /************************************************************************/ /* All depend on order of fields in PATH_INFO */ /* All take 1 input argument, an "info_source" string */ /* If info_source is NULL, default source of info is used. If */ /* non-NULL, it is a pointer to a PATH_INFO string. Note that */ /* logically, the info we want has nothing to do with PATH_INFO, */ /* allowing us to change the mechanism of specifying the various */ /* pieces of information */ /* Routines that return a string pointer return a pointer to a */ /* dynamically allocated, null-terminated string. They return */ /* a pointer to an empty string if no info is found. They */ /* returns a NULL pointer if the string is bad somehow (see each */ /* routine) or if there's a memory allocation failure */ char *copy_substring(start_ptr,end_ptr) char *start_ptr,*end_ptr; /* Copy substring defined by start_ptr & end_ptr into dynamically */ /* allocated string. */ /* end_ptr = NULL means "copy to end of string" */ /* Return pointer to dynamically allocated string (empty if no */ /* characters in substring; NULL if allocation failed. */ { char *ret; char save1; if ( (start_ptr == NULL) || (start_ptr == end_ptr) ) { ret = (char *) malloc(1); *ret = '\0'; } else { if (end_ptr != NULL) { save1 = *end_ptr; *end_ptr = '\0'; } /* Emulate strdup function. +1 is for terminating '\0' */ ret = (char *) malloc(strlen(start_ptr)+1); if (ret != NULL) strcpy(ret,start_ptr); /* Restore original string, if necessary */ if (end_ptr != NULL) *end_ptr = save1; } return ret; } int get_level(info_source) char *info_source; /* See parse_PATH_INFO_string & info at top. */ /* See get_info argument doc, above */ /* This routine returns the level of output method has been re- */ /* quested to produce. If no level was provided, returns */ /* LEVEL_NOT_SPECIFIED, representing "all levels". Returns */ /* LEVEL_ERROR if format was bad (right now, this means first non- */ /* numeric character after level is not start of options string ({) */ /* or end-of-string (if no options). */ { char *proto_ptr,*lev_ptr,*options_ptr; char *term_char; int lev; parse_PATH_INFO_string(info_source,&proto_ptr,&lev_ptr,&options_ptr); if (lev_ptr == NULL) /* Could also return error if proto_ptr non-NULL if "rules" */ /* say protocol must have level */ lev = LEVEL_NOT_SPECIFIED; else { lev = strtol(lev_ptr,&term_char,10); /* Assume that level is terminated by options or end of string */ /* Further assumes no white space between level and options */ if ( (*term_char == '\0') || (*term_char == PATH_INFO_OPTIONS_DELIM[0]) ) /* Could validate level here (0-9, for example) */ ; else lev = LEVEL_ERROR; } return lev; } char *get_protocol(info_source) char *info_source; /* See parse_PATH_INFO_string & info at top. */ /* See get_info argument doc, above */ /* Returns a NULL pointer if the string is bad somehow (right now */ /* only happens if string is empty, which might not be considered an */ /* error. Oh, well) */ { char *proto_ptr,*lev_ptr,*options_ptr; char *end_proto_ptr,*orig_proto_ptr; parse_PATH_INFO_string(info_source,&proto_ptr,&lev_ptr,&options_ptr); orig_proto_ptr = proto_ptr; if (proto_ptr != NULL) { /* Advance beyond . */ proto_ptr++; /* Assume options follow level (which follows protocol) */ end_proto_ptr = (lev_ptr == NULL) ? options_ptr : lev_ptr; } /* copy_substring should handle NULL end_proto_ptr ... */ proto_ptr = copy_substring(proto_ptr,end_proto_ptr); if (proto_ptr != NULL) { /* Validate protocol here. If bad, free proto_ptr then set to */ /* NULL */ /* Can't have . without protocol */ if ( (*proto_ptr == '\0') && (orig_proto_ptr != NULL) ) { free (proto_ptr); proto_ptr = NULL; } } return proto_ptr; } char *get_object(info_source) char *info_source; /* See parse_PATH_INFO_string & info at top. */ /* See get_info argument doc, above */ { char *object_ptr,*proto_ptr,*lev_ptr,*options_ptr; char *end_object_ptr; object_ptr = parse_PATH_INFO_string(info_source,&proto_ptr,&lev_ptr,&options_ptr); if (object_ptr != NULL) { /* Find end of object string */ /* Assume order of things is object, protocol, level, options */ if ( (end_object_ptr = proto_ptr) == NULL ) if ( (end_object_ptr = lev_ptr) == NULL ) end_object_ptr = options_ptr; } /* copy_substring should handle NULL end_object_ptr ... */ object_ptr = copy_substring(object_ptr,end_object_ptr); if (object_ptr != NULL) { /* Validate object here. If bad, free object_ptr then set to */ /* NULL */ ; } return object_ptr; } char *get_options(info_source) char *info_source; /* See parse_PATH_INFO_string & info at top. */ /* See get_info argument doc, above */ /* Returns NULL if no terminating } is found */ { char *proto_ptr,*lev_ptr,*options_ptr; char *end_options_ptr; parse_PATH_INFO_string(info_source,&proto_ptr,&lev_ptr,&options_ptr); if (options_ptr != NULL) { /* Advance beyond { */ options_ptr++; /* Find {. Must be there if options exist, or we have an */ /* improperly formatted PATH_INFO */ end_options_ptr = strrchr(options_ptr,PATH_INFO_OPTIONS_DELIM[1]); if (end_options_ptr == NULL) return NULL; } options_ptr = copy_substring(options_ptr,end_options_ptr); if (options_ptr != NULL) { /* Validate options here. If bad, free options_ptr then set to */ /* NULL */ ; } return options_ptr; } /************************************************************************/ /* PATH_INFO analysis & formatting routines. */ /************************************************************************/ char *parse_PATH_INFO_string(PATH_INFO_string,proto_ptr,level_ptr,options_ptr) char *PATH_INFO_string; char **proto_ptr; char **level_ptr; char **options_ptr; /* This routine assumes that the final { in PATH_INFO marks the */ /* beginning of the options string. The final ., if any, before the */ /* options, if any, marks the beginning of a protocol/level string. */ /* The protocol consists entirely of non-numeric characters and is */ /* immediately followed by an all-numeric level, the options, or the */ /* end of the string */ /* */ /* This routine accepts PATH_INFO_string as input. If the input is */ /* NULL, it uses the value of environment variable PATH_INFO. */ /* It returns a pointer to the beginning of the string or NULL if */ /* the environment variable is undefined. It returns a pointer to */ /* the appropriate . in *proto_ptr (NULL if none), and a pointer to */ /* the first non-alphanumeric character after the . in *level_ptr */ /* (NULL if none). It returns a pointer to the appropriate { in */ /* *options_ptr (NULL if none). Note that since we do not return */ /* string lengths, routines that call this routine are sensitive */ /* to the order of fields in PATH_INFO and will probably break if */ /* the order is changed */ { char *env_ptr; char *ptr; /* Set up for no protocol, level, or options info */ *proto_ptr = NULL; *options_ptr = NULL; ptr = NULL; env_ptr = (PATH_INFO_string == NULL) ? getenv(PATH_INFO_ENV_VAR) : PATH_INFO_string; if (env_ptr != NULL) { /* Temporarily lop off options, if any */ *options_ptr = strrchr(env_ptr,PATH_INFO_OPTIONS_DELIM[0]); if (*options_ptr != NULL) **options_ptr = '\0'; /* Protocol string, if any, is at end of option-less PATH_INFO */ *proto_ptr = strrchr(env_ptr,PATH_INFO_PROTOCOL_SEP); if (*proto_ptr != NULL) { ptr = *proto_ptr; /* Find first non-alpha character after . */ while (isalpha(*(++ptr))) ; if (*ptr == '\0') ptr = NULL; } } *level_ptr = ptr; /* Restore options if necessary */ if (*options_ptr != NULL) **options_ptr = PATH_INFO_OPTIONS_DELIM[0]; return env_ptr; } char *make_PATH_INFO_putenv_string (env_var,PATH_INFO_default,object,protocol,level,options) char *env_var,*object,*protocol,*options,*PATH_INFO_default; int level; /* See parse_PATH_INFO_string & info at top. */ /* env_var is environment variable to set up. If *env_var = '\0', */ /* the string is constructed without the leading ENV_VAR=. NULL */ /* means set up the default environment variable (PATH_INFO) */ /* PATH_INFO_default is the "base" PATH_INFO string whose mod */ /* will be put into the env_var string. NULL means value of present */ /* PATH_INFO */ /* object, protocol, level, and options are pieces of the string to */ /* be inserted into the PATH_INFO string */ /* *object='\0' means put no object string in output */ /* object = NULL means use the existing object string */ /* *protocol='\0' means put no protocol string in output */ /* protocol = NULL means use the existing protocol */ /* level = LEVEL_NOT_SPECIFIED means put in no level information */ /* level = USE_EXISTING_LEVEL means use the existing level string */ /* *options='\0' means put no options string in output */ /* options = NULL means use the existing options string */ /* */ /* This routine returns a pointer to a dynamically allocated string */ /* suitable for putenv. (Do not free this string after calling */ /* putenv until you've called another putenv for the same environment */ /* variable.) Its value is a string of the form */ /* ENV_VAR=good-PATH_INFO-string */ /* (or just good-PATH_INFO-string - see env_var input argument) */ /* Returns NULL in case of trouble. Cause 1: inability to get */ /* some dynamic memory or other. Cause 2: finished string, when sent */ /* through the get_info routines, returned error. Note that we do */ /* NOT diagnose a properly formatted final string that is too long */ /* (for now, anyway) */ { char *object_ptr,*proto_ptr,*lev_ptr,*options_ptr; char *buf,*env; char *inptr,*outptr,*start_env_val; char levbuf[10]; char empty = '\0'; /* Get pieces from user arguments or PATH_INFO string */ /* (Note that valid string pieces can be empty). */ /* Failure if can't */ /* Do level first so we don't have to worry about free'ing if */ /* bad level */ if (level == USE_EXISTING_LEVEL) level = get_level(PATH_INFO_default); if (level == LEVEL_NOT_SPECIFIED) lev_ptr = ∅ else if (level < 0) return NULL; else sprintf ( (lev_ptr=levbuf), "%d", level ); object_ptr = (object == NULL) ? get_object(PATH_INFO_default) : object; if (object_ptr == NULL) return NULL; proto_ptr = (protocol == NULL) ? get_protocol(PATH_INFO_default) : protocol; if (proto_ptr == NULL) return NULL; options_ptr = (options == NULL) ? get_options(PATH_INFO_default) : options; if (options_ptr == NULL) return NULL; env = (env_var == NULL) ? PATH_INFO_ENV_VAR : env_var; /* +5 is 1 for =, 1 for ., 2 for {}, and 1 for terminating '\0' */ buf = (char *) malloc ( strlen(env) + strlen(object_ptr) + strlen(proto_ptr) + strlen(levbuf) + strlen(options_ptr) + 5 ); if (buf == NULL) { if (object_ptr != object) free (object_ptr); if (proto_ptr != protocol) free (proto_ptr); if (options_ptr != options) free (options_ptr); return NULL; } /* Build string from component pieces */ outptr = buf; if (*env != '\0') { /* strcpy (buf,env) & add = */ inptr = env; while (*inptr != '\0') *(outptr++) = *(inptr++); *(outptr++) = ENV_VAR_DEFN_CHAR; } /* Save this location, which is start of new PATH_INFO */ start_env_val = outptr; /* strcat (buf,object_ptr) */ inptr = object_ptr; while (*inptr != '\0') *(outptr++) = *(inptr++); if (object_ptr != object) free (object_ptr); if ( (*proto_ptr != '\0') || (*lev_ptr != '\0') ) { /* Add ., then strcat protocol & level info */ *(outptr++) = PATH_INFO_PROTOCOL_SEP; inptr = proto_ptr; while (*inptr != '\0') *(outptr++) = *(inptr++); if (proto_ptr != protocol) free (proto_ptr); inptr = lev_ptr; while (*inptr != '\0') *(outptr++) = *(inptr++); } if (*options_ptr != '\0') { /* strcat options; prepend { and add } */ *(outptr++) = PATH_INFO_OPTIONS_DELIM[0]; inptr = options_ptr; while (*inptr != '\0') *(outptr++) = *(inptr++); *(outptr++) = PATH_INFO_OPTIONS_DELIM[1]; if (options_ptr != options) free (options_ptr); } *outptr = '\0'; /* Validate input args by passing them through get_info routines */ /* That way any checks need only be coded in those routines */ if ( (proto_ptr = get_protocol(start_env_val)) == NULL ) return NULL; else free(proto_ptr); if ( (object_ptr = get_object(start_env_val)) == NULL ) return NULL; else free(object_ptr); if ( (options_ptr = get_options(start_env_val)) == NULL ) return NULL; else free(options_ptr); if (get_level(start_env_val) == LEVEL_ERROR) return NULL; return buf; } /************************************************************************/ /* Options package. Need something that verifies structure, */ /* something that picks out value of desired "left sides"; eg */ /* get_option("dir") returns xxx if PATH_INFO has dir=xxx; */ /* and something that builds a valid options string */ /* Much of this code already exists. def's attribute */ /* handling is quite similar to this. Building the attribute */ /* string after adding width=nn is the same as building */ /* the string here (which is actually adding an option, which is */ /* another function we'd like here). Looking for an existing */ /* width= is the same as looking up dir=, and has the same */ /* problems (what if there's an iowidth= attribute?). */ /* The defgb wjstbl package will parse & pick the */ /* a=b; c=d;... format, with some generality (eg, allowing white */ /* space). Structurally, there is a problem in that you only */ /* want to parse the string once, and then do all your picks. */ /* However, that would get away from allowing many PATH_INFO */ /* strings to be parsed by this package. Another problem is that */ /* wjstbl routines use others which might not be modular. In */ /* particular, uses defgb err() function. */