#ifndef lint #ifdef sccs static char sccsid[] = "@(#)defaults.c 1.1 94/10/31"; #endif #endif /* * Copyright (c) 1985, 1988 by Sun Microsystems, Inc. */ /* * This file contains routines for reading in and accessing a defaults * database. Any errors are printed on the console as warning messeges. */ /* * Overview of data structures: * * All of the relevent data structures are stored in a defaults object. * The defaults object contains two hash tables. The symbols hash table * is used to cannoncalize (sp?) strings. Every time a string (symbol) * is encountered, it is looked up in the symbols hash table to get a * unique pointer to a string. This means that two symbols can be tested * for equality by simply comparing their pointers. The second hash table * is the nodes hash table. The nodes hash table is indexed by a name, * where a name is a null-terminated list of symbol pointers * (i.e. ["CShell", "Path", "Help", NULL]). The nodes are threaded together * so that each node knows its parent, eldest child, and next youngest * sibling. In addition, each node has a name (i.e. the null-terminated * list of string pointers) and a string value. */ #include /* Standard I/O library */ #include /* Password file stuff */ #include /* Get u_long defined for dir.h */ #include /* Directory access routines */ #include /* File status information */ #include /* Define True, False, etc... */ #include /* Hash table definitions */ #include /* Parsing routine */ #include #include #include #define HUGE_STRING 10000 /* Huge strings for Warren Teitelman */ #define getchr(fp) ((int)getc(fp)) /* Simplify character at a time debugging */ #define ungetchr ungetc /* Simplify character at a time debugging */ /* Use following instead of standard strdup to localize VM accesses */ #define defaults_strdup(str) (strcpy(hash_get_memory((unsigned) strlen(str) + 1), str)) typedef int (*INT_FUNC)(); typedef char (*CHAR_FUNC)(); /* Global variables: */ Defaults defaults; /* = NULL; Global defaults object */ /* Exported routine definitions. */ #include /* Defaults routines */ /* * chr_digit(chr) returns True if CHr is a valid digit. */ #define chr_digit(chr) ((Bool)(('0' <= chr) && (chr <= '9'))) /* * chr_letter(chr) returns True if Chr is a valid letter. */ #define chr_letter(chr) ((Bool)((' ' < chr) && (chr <= '~') && (chr != '/'))) /* Internally used routines: */ void clear_status(); void defaults_create(); long *get_data(); char *getlogindir(); void name_cat(); Name name_copy(); Bool name_equal(); int name_hash(); int name_parse(); void name_quick_parse(); char *name_unparse(); Node node_create(); void node_delete(); void node_delete_private(); Node node_find_name(); Node node_lookup_name(); Node node_lookup_path(); Node path_lookup(); Bool read(); void read_lazy(); void read_master(); void read_master_database(); void read_master_database1(); void read_private_database(); char *slash_append(); Symbol symbol_copy(); Bool symbol_equal(); int symbol_hash(); Symbol symbol_lookup(); void warn(); /* Externally used routines: */ extern char *hash_get_memory(); extern char *getlogin(); extern char *getenv(); extern struct passwd *getpwnam(); extern struct passwd *getpwuid(); extern char *malloc(); /*****************************************************************/ /* Exported routines: */ /*****************************************************************/ /* * NOTE: Any returned string pointers should be considered temporary at best. * If you want to hang onto the data, make your own private copy of the string! */ /* * defaults_exists(path_name, status) will return TRUE if Path_Name exists * in the database. */ Bool defaults_exists(path_name, status) char *path_name; /* Node name to test for existence */ int *status; /* Status flag */ { clear_status(status); return (Bool)(node_lookup_path(path_name, False, status) != NULL); } /* * defaults_get_boolean(path_name, default, status) will lookup Path_Name * in the defaults database and return TRUE if the value is "True", "Yes", * "On", "Enabled", "Set", "Activated", or "1". FALSE will be returned if * the value is "False", "No", "Off", "Disabled", "Reset", "Cleared", * "Deactivated", or "0". If the value is none of the above, a warning * message will be displayed and Default will be returned. */ Bool defaults_get_boolean(path_name, default_bool, status) char *path_name; /* Path name */ Bool default_bool; /* Default value */ int *status; /* Status flag */ { static Defaults_pairs bools[] = { "True", (int)True, "False", (int)False, "Yes", (int)True, "No", (int)False, "On", (int)True, "Off", (int)False, "Enabled", (int)True, "Disabled", (int)False, "Set", (int)True, "Reset", (int)False, "Cleared", (int)False, "Activated", (int)True, "Deactivated", (int)False, "1", (int)True, "0", (int)False, NULL, -1, }; char *string_value; /* String value */ register Bool value; /* Value to return */ clear_status(status); string_value = defaults_get_string(path_name, (char *)NULL, status); if (string_value == NULL){ return default_bool; } value = (Bool)defaults_lookup(string_value, bools); if ((int)value == -1){ warn("%s is '%s'; which is an unrecognized boolean value", path_name, string_value); value = default_bool; } #ifdef TESTDEFAULTS if (defaults->database_dir && value != default_bool) { defaults->errors = 0; warn("%s: .d=%d .c=%d", path_name, value, default_bool); } #endif return value; } /* * defaults_get_character(path_name, default, status) will lookup Path_Name in * the defaults database and return the resulting character value. * Default will be returned if any error occurs. */ int defaults_get_character(path_name, default_character, status) char *path_name; /* Full database node path name */ char default_character; /* Default return value */ int *status; /* Status flag */ { Node node; /* Resulting node */ clear_status(status); node = path_lookup(path_name, status); if (node == NULL) return default_character; if (strlen(node->value) != 1){ warn("%s -- More than one character in character constant", path_name); return default_character; } #ifdef TESTDEFAULTS if (defaults->database_dir && node->value[0] != default_character) { defaults->errors = 0; warn("%s: .d=%c .c=%c", path_name, node->value[0], default_character); } #endif return node->value[0]; } /* * defaults_get_child(path_name, status) will return a pointer to the simple * name assoicated with the first child of Path_Name. NULL will be returned, * if Path_Name does not exist or if Path_Name does not have a first child. */ char * defaults_get_child(path_name, status) char *path_name; /* Full name of parent node */ int *status; /* Status flag */ { register Node node; /* Node from database */ clear_status(status); /* Find the node child. */ node = node_lookup_path(path_name, False, status); if (node == NULL) return NULL; node = node->child; while ((node != NULL) && (node->deleted)) node = node->next; return (node == NULL) ? NULL : node->name[name_length(node->name) - 1]; } /* * defaults_get_default(path_name, default, status) will return the value * associated with Path_Name prior to being overridden by the clients private * database. Default is returned if any error occurs. */ char * defaults_get_default(path_name, default_value, status) char *path_name; /* Full name of node */ char *default_value; /* Default node name */ int *status; /* Status flag */ { register Node node; /* Node from database */ clear_status(status); node = path_lookup(path_name, status); if (node == NULL) return default_value; return node->default_value; } /* * defaults_get_enum(path_name, pairs, status) will lookup the value associated * with Path_Name and scan the Pairs table and return the associated value. * If no match, can be found an error will be generated and the value * associated with last entry (i.e. the NULL entry) will be returned. See, * defaults_lookup(). */ int defaults_get_enum(path_name, pairs, status) char *path_name; /* Full database path name */ Defaults_pairs *pairs; /* Pairs table */ int *status; /* Status flag */ { return defaults_lookup(defaults_get_string(path_name, (char *)NULL, status), pairs); } /* * defaults_get_enumeration(path_name, default, status) will lookup * "Path_Name/VALUE(Path_Name)" in the defaults database and return the * resulting string value. Default will be returned if any error occurs. */ char * defaults_get_enumeration(path_name, default_enumeration, status) char *path_name; /* Full database node name */ char *default_enumeration; /* Default enumerate name */ int *status; /* Status flag */ { register Node node; /* Resulting node */ char name[MAX_STRING]; /* Temporary name */ register char *name_pointer; /* Name pointer */ register Node temp_node; /* Temporary node */ clear_status(status); node = path_lookup(path_name, status); if (node == NULL) return default_enumeration; name_pointer = name; (void)strcpy(name_pointer, path_name); (void)strcat(name_pointer, SEP_STR); (void)strcat(name_pointer, "$Enumeration"); temp_node = node_lookup_path(name_pointer, True, status); if (temp_node == NULL){ warn("%s is not an enumeration type", path_name); return default_enumeration; } (void)strcpy(name_pointer, path_name); (void)strcat(name_pointer, SEP_STR); (void)strcat(name_pointer, node->value); temp_node = node_lookup_path(name_pointer, True, status); if (temp_node == NULL) { warn("%s is not a valid enumeration value for %s", node->value, path_name); return default_enumeration; } #ifdef TESTDEFAULTS if (defaults->database_dir && temp_node->value && default_enumeration && strcmp(temp_node->value, default_enumeration) != 0) { defaults->errors = 0; warn("%s: .d=%s .c=%s", path_name, temp_node->value, default_enumeration); } #endif return temp_node->value; } /* * defaults_get_integer(path_name, default, status) will lookup Path_Name in * the defaults database and return the resulting integer value. * Default will be returned if any error occurs. */ int defaults_get_integer(path_name, default_integer, status) char *path_name; /* Full database node name */ int default_integer; /* Default return value */ int *status; /* Status flag */ { register char chr; /* Temporary character */ Bool error; /* TRUE => an error has occurred */ Bool negative; /* TRUE => Negative number */ register Node node; /* Resulting node */ register int number; /* Resultant value */ register char *pointer; /* Pointer into value */ /* Lookup the node */ clear_status(status); node = path_lookup(path_name, status); if (node == NULL) return default_integer; /* Convert string into integer (with error chacking) */ error = False; pointer = node->value; negative = False; number = 0; chr = *pointer++; if (chr == '-'){ negative = True; chr = *pointer++; } if (chr == '\0') error = True; while (chr != '\0'){ if ((chr < '0') || (chr > '9')){ error = True; break; } number = number * 10 + chr - '0'; chr = *pointer++; } if (error){ warn("%s contains an incorrect integer", path_name); return default_integer; } if (negative) number = -number; #ifdef TESTDEFAULTS if (defaults->database_dir && number != default_integer) { defaults->errors = 0; warn("%s: .d=%d .c=%d", path_name, number, default_integer); } #endif return number; } /* * defaults_get_integer_check(path_name, default, mininum, maximum, status) * will lookup Path_Name in the defaults database and return the resulting * integer value. If the value in the database is not between Minimum and * Maximum (inclusive), an error message will be printed. Default will be * returned if any error occurs. */ int defaults_get_integer_check(path_name, default_int, minimum, maximum, status) char *path_name; /* Full path name of node */ int default_int; /* Default return value */ int minimum; /* Minimum value */ int maximum; /* Maximum value */ int *status; /* Status flag */ { int value; /* Return value */ clear_status(status); value = defaults_get_integer(path_name, default_int, status); if ((minimum <= value) && (value <= maximum)) return value; else { warn("The value of %s is %d which is not between %d and %d", path_name, value, minimum, maximum); return default_int; } } /* * defaults_get_sibling(path_name, status) will return a pointer to the simple * name assoicated with the next sibling of Path_Name. NULL will be returned, * if Path_Name does not exist or if Path_Name does not have an * next sibling. */ char * defaults_get_sibling(path_name, status) char *path_name; /* Full name of parent node */ int *status; /* Status flag */ { register Node node; /* Node from database */ clear_status(status); node = node_lookup_path(path_name, False, status); if (node == NULL){ warn("Could not find '%s'", path_name); return NULL; } do node = node->next; while ((node != NULL) && node->deleted); return (node == NULL) ? NULL : node->name[name_length(node->name) - 1]; } /* * defaults_get_string(path_name, default, status) will lookup and return the * string value assocatied with Path_Name in the defaults database. * Default will be returned if any error occurs. */ char * defaults_get_string(path_name, default_string, status) char *path_name; /* Full database node name */ char *default_string; /* Default return value */ int *status; /* Status flag */ { register Node node; /* Resulting node */ clear_status(status); node = path_lookup(path_name, status); #ifdef TESTDEFAULTS if (defaults->database_dir && node != NULL && node->value && default_string && strcmp(node->value, default_string) != 0) { defaults->errors = 0; warn("%s: .d=%s .c=%s", path_name, node->value, default_string); } #endif return (node == NULL) ? default_string : node->value; } /* * initializes (reads in) the defaults database. defaults_init() will read in * the user's private database, i.e., .defaults file, and then read in the * master database as well if the value of /Defaults/Read_Defaults_Database * is True. defaults_init(True) will read in the master database regardless. */ void defaults_init(read_defaults_database) int read_defaults_database; { if (defaults == NULL){ defaults_create(); read_private_database(); if (read_defaults_database && defaults_get_boolean("/Defaults/Read_Defaults_Database", True, (int *)NULL)) { read_master_database(); } defaults->test_mode = defaults_get_boolean("/Defaults/Test_Mode", False, (int *)NULL); } } /* * defaults_lookup(name, pairs) will linearly scan the Pairs data structure * looking for Name. The value associated with Name will be returned. * If Name can not be found in Pairs, the value assoicated with NULL will * be returned. (The Pairs data structure must be terminated with NULL.) */ int defaults_lookup(name, pairs) register char *name; /* Name to look up */ register Defaults_pairs *pairs; /* Default */ { register Defaults_pairs *pair; /* Current pair */ for (pair = pairs; pair->name != NULL; pair++){ if (name == NULL) continue; if (symbol_equal(name, pair->name)) break; } return pair->value; } /* * defaults_reread(path_name, status) will reread the portion of the database * associated with Path_Name. */ /*ARGSUSED*/ void defaults_reread(path_name, status) char *path_name; /* Path name to reread */ int *status; /* Status flag */ { #ifdef WILL_IMPLEMENT Symbol name[MAX_NAME]; /* Temporary name */ register Symbol *name_pointer; /* Name pointer */ register Node node; /* Parent node */ #endif defaults = NULL; } /* * clear_status(status) will clear the status flag if it is non-NULL. */ void clear_status(status) int *status; /* Status flag */ { if (status != NULL) warn("Somebody passed a non-NULL status code"); } /* * defaults_create() will intialize the global defaults object. */ void defaults_create() { Symbol name[1]; /* Empty name */ register Defaults new_defaults; /* New defaults object */ extern Hash hash_create(); /* Create hash tables. */ new_defaults = (Defaults)hash_get_memory(sizeof *defaults); defaults = new_defaults; new_defaults->database_dir = NULL; new_defaults->errors = 0; new_defaults->nodes = hash_create(250, NULL, (CHAR_FUNC)name_equal, name_hash, (INT_FUNC)NULL, NULL, (INT_FUNC)NULL, 7); new_defaults->prefix = NULL; new_defaults->private_dir = NULL; new_defaults->root = node_create(); new_defaults->symbols = (Hash)hash_create(250, NULL, (CHAR_FUNC)symbol_equal, symbol_hash, (INT_FUNC)NULL, NULL, (INT_FUNC)NULL, 7); new_defaults->test_mode = False; name[0] = NULL; (void)hash_insert((Hash)new_defaults->nodes, (caddr_t)name_copy(name), (caddr_t)new_defaults->root); } /* * get_data(size) will get size words of memory. */ static long * get_data(size) int size; /* Number of words to allocate */ { long *data; /* Pointer to beginning of data */ data = (long *)hash_get_memory(size<<2); /* Machine dependent! */ return data; } /* * name_cat(dest_name, source_name) will concatonate Source_Name onto the * end of Dest_Name. */ static void name_cat(dest_name, source_name) Symbol *dest_name; /* Destination name */ Symbol *source_name; /* Source name */ { name_move(dest_name + name_length(dest_name), source_name); } /* * name_copy(name) will make and return a copy of Name. */ Name name_copy(name) Name name; /* Name to copy */ { Name new; /* New name */ new = (Name)get_data(name_length(name) + 1); name_move(new, name); return new; } /* * name_equal(name1, name2) will return True if Name1 equals Name2. */ static Bool name_equal(name1, name2) register Name name1; /* First name */ register Name name2; /* Second name */ { register Symbol symbol1; /* Symbol from first name */ register Symbol symbol2; /* Symbol from second name */ while (True){ symbol1 = *name1++; symbol2 = *name2++; if (symbol1 != symbol2) return False; if (symbol1 == NULL) return True; } } /* * name_hash(name) will return a hash of Name. */ static int name_hash(name) register Name name; /* Name to hash */ { register Symbol symbol; /* Symbol from name */ register int hash; /* Hash value */ hash = 0; do { symbol = *name++; hash += (int)symbol; } while (symbol != NULL); return hash; } /* * name_length(name) will return the number of symbols in Name. */ int name_length(name) register Name name; /* Name to return length of */ { register int length; /* Length of name */ length = 0; while (*name++ != NULL) length++; return length; } /* * name_move(dest, source) will the name Source over to the name Dest. */ static int name_move(dest, source) register Name dest; /* Destination name */ register Name source; /* Source Name */ { while (*source != NULL) *dest++ = *source++; *dest = NULL; } /* * name_parse(in_file, symbols, default_name, name) will parse a node name * read from In_File into its component symbols and store the resulting * parts into Name. Default symbols will be read from Default_Name. The * symbol hash table is Symbols. The number of simple node names in the full * node name will be returned. 0 will be returned on an error. */ /*ARGSUSED*/ static int name_parse(in_file, symbols, default_name, name) register FILE *in_file; /* Input file */ Hash symbols; /* Symbol hash table */ Name default_name; /* Default name */ register Name name; /* Place to store resultant name */ { register int chr; /* Temporary character */ register int default_size; /* Size of default name */ register Bool fully; /* TRUE => fully specified node name */ register char *symbol; /* Current symbol */ register int size; /* Number of parts in name */ char temp[MAX_STRING]; /* Temporary symbol name */ size = 0; chr = parse_whitespace(in_file); fully = (Bool)(chr == SEP_CHR); if (fully){ /* Fully specified node name. */ (void) getchr(in_file); /* Dispose of leading '/' */ /* Process leading default_names. */ while (True){ chr = getchr(in_file); if (chr != SEP_CHR) break; if (*default_name == NULL){ (void)strcpy(temp, SEP_STR); (void)strcat(temp, SEP_STR); warn("No default available for '%s'", temp); *name = NULL; return size; } *name++ = *default_name++; size++; } (void)ungetchr(chr, in_file); } else { /* Partially specified node name. */ /* Copy over as many default_names as are available. */ default_size = 0; while (*default_name != NULL){ *name++ = *default_name++; default_size++; } } /* Process each simple node name. */ while (chr_letter(chr) || chr_digit(chr)){ if (parse_symbol(in_file, &temp[0]) == NULL) return 0; symbol = symbol_lookup(&temp[0]); *name++ = symbol; size++; if (fully){ *default_name++ = symbol; *default_name = NULL; } chr = getchr(in_file); if ((chr == ' ') || (chr == '\t') || (chr == '\n') || (chr == EOF)) break; if (chr == SEP_CHR){ chr = getchr(in_file); (void)ungetchr(chr, in_file); } else { warn("'%c' found in node name", chr); break; } } (void)ungetchr(chr, in_file); *name = NULL; if (size > 0) size += default_size; return size; } /* * name_quick_parse(path_name, name) will quickly parse Path_Name and store * the result into Name. */ void name_quick_parse(path_name, name) char *path_name; /* Full name string */ Name name; /* Place to store parsed name */ { register char chr; /* Temporary character */ register char *full_pointer; /* Pointer into full name */ register Name name_pointer; /* Pointer into name */ char temp[MAX_STRING]; /* Temporary name */ register char *temp_pointer; /* Pointer into temporary name */ /* Build up the name. */ full_pointer = path_name; if (*full_pointer++ != SEP_CHR){ warn("'%s' does not start with '%c'.", path_name, SEP_CHR); name[0] = NULL; return; } name_pointer = name; while (True){ temp_pointer = &temp[0]; while (True){ chr = *full_pointer++; if ((chr == SEP_CHR) || (chr == '\0')) break; if (chr_letter(chr) || chr_digit(chr)) *temp_pointer++ = chr; else { warn("'%c' is not permitted in %s", chr, path_name); name[0] = NULL; return; } } *temp_pointer = '\0'; *name_pointer++ = symbol_lookup(&temp[0]); if (chr == '\0') break; } *name_pointer = NULL; } /* * name_unparse(name, data) will write the string corresponding to Name * into Data. If Data is NULL, a new string will be allocated. */ char * name_unparse(name, data) register Name name; /* Name to unparse */ register char *data; /* Data to unparse */ { register int size; /* Size of string */ register Name temp; /* Temporary name */ if (name == NULL){ if (data == NULL) data = hash_get_memory(2); (void)strcpy(data, SEP_STR); return data; } if (data == NULL){ size = 0; temp = name; while (*temp != NULL) size += strlen(*temp++) + 1; data = hash_get_memory(size + 1); } data[0] = '\0'; while (*name != NULL){ (void)strcat(data, SEP_STR); (void)strcat(data, *name++); } return data; } /* * node_create() will create and return an empty node. */ static Node node_create() { register Node node; /* New node */ node = (Node)hash_get_memory(sizeof *node); node->child = NULL; node->default_value = DEFAULTS_UNDEFINED; node->deleted = False; node->file = False; node->name = NULL; node->next = NULL; node->parent = NULL; node->private = False; node->value = DEFAULTS_UNDEFINED; return node; } /* * node_delete(node) will cause Node and its descendentes to be "deleted". * The nodes will only be marked as deleted. No storage will be deallocated. */ void node_delete(node) register Node node; /* Node to delete */ { node->deleted = True; for (node = node->child; node != NULL; node = node->next) node_delete(node); } /* * node_delete_private(node) will cause Node and its descendentes to be * "deleted". The nodes will only be marked as deleted. No storage will be * deallocated. */ void node_delete_private(node) register Node node; /* Node to delete */ { if (node->private) node->deleted = True; for (node = node->child; node != NULL; node = node->next) node_delete_private(node); } /* * node_find_name(name, multiple, status) will return the node associated with * Name from the defaults database. The important side effect of this routine * is that it makes for sure that all the nodes between Name and the root node * both exist and are properly threaded together. If Multiple is TRUE, * multiple lookups will be performed to prefixes. */ /*ARGSUSED*/ Node node_find_name(name, multiple, status) register Name name; /* Name to lookup/enter */ Bool multiple; /* TRUE => multiple prefix lookups */ int *status; /* Status flag */ { register int length; /* Name length */ register Node next; /* Next node at same level */ register Node node; /* Node that is looked up */ char path_name[MAX_STRING]; /* Path name */ Node parent; /* Parent node */ Symbol symbol; /* Node symbol */ if (status != NULL){ (void)name_unparse(name, path_name); warn("Status is non-NULL in %s", path_name); } if (defaults == NULL) defaults_init(True); if (defaults->test_mode) return NULL; /* The root node is special. */ if (name[0] == NULL) return defaults->root; /* Just return it if it is already in the Nodes table. */ read_lazy(name); node = (Node)hash_lookup(defaults->nodes, (caddr_t)name); if (node != NULL){ node->deleted = False; return node; } /* Get the parent node. */ length = name_length(name); symbol = name[length - 1]; name[length - 1] = NULL; parent = node_find_name(name, False, status); name[length - 1] = symbol; /* Create the new node. */ node = node_create(); name = name_copy(name); node->name = name; node->parent = parent; /* Thread this node onto the end of the parent next list. */ next = parent->child; if (next == NULL) parent->child = node; else { while (next->next != NULL) next = next->next; next->next = node; } /* Insert the node into the Nodes hash table. */ (void)hash_insert(defaults->nodes, name, node); return node; } /* * node_lookup_name(name, multiple, status) will lookup Name in the defaults * database and return the associated node. If Path_Name is not in the * database, NULL will be returned. */ Node node_lookup_name(name, multiple, status) register Name name; /* Name to lookup */ Bool multiple; /* TRUE => multiple lookups */ int *status; /* Status flag */ { register Node node; /* Resultant node */ char path_name[MAX_STRING]; /* Path name string */ Symbol temp[MAX_NAME]; /* Temporay name */ if (defaults == NULL) defaults_init(True); if (status != NULL){ (void)name_unparse(name, path_name); warn("Status non-NULL in %s", path_name); } if (defaults->test_mode) return NULL; read_lazy(name); if (multiple && (defaults->prefix != NULL)){ temp[0] = NULL; name_cat(temp, defaults->prefix); name_cat(temp, name); node = (Node)hash_lookup(defaults->nodes, temp); if ((node != NULL) && (!node->deleted)) return node; } node = (Node)hash_lookup(defaults->nodes, name); if ((node != NULL) && (!node->deleted)) return node; else return NULL; } /* * node_lookup_path(path_name, multiple, status) will lookup Path_name in the * defaults database and return the associated node. If Path_Name is not in * the database, NULL will be returned. */ Node node_lookup_path(path_name, multiple, status) char *path_name; /* Full path name */ Bool multiple; /* TRUE => multiple lookups */ int *status; /* Status flag */ { Symbol name[MAX_NAME]; /* Node name */ if (defaults == NULL) defaults_init(True); if (strcmp(path_name, SEP_STR) == 0) return defaults->root; name_quick_parse(path_name, name); return node_lookup_name(name, multiple, status); } /* * path_lookup(path_name, status) will lookup Path_Name in the defaults * database and return the associated node. If Path_Name is not in the * database, a warning message will be printed on the console and NULL will * be returned. */ Node path_lookup(path_name, status) char *path_name; /* Full name */ int *status; /* Status flag */ { register Node node; /* Resultant node */ node = node_lookup_path(path_name, True, status); if (node == NULL && defaults->database_dir) warn("'%s' is not in defaults database", path_name); return node; } /* * read(file_name, prefix, private) will read in data into the defaults * database from the file File_Name. Only path names that begin with Prefix * will be read into the database. If Private is True, the file being read * corresponds to a private database file. True will be returned if File_Name * could not be opened. Otherwise, False will be returned. */ static Bool read(file_name, prefix, private) char *file_name; /* File name to read */ Name prefix; /* Prefix path that must match first */ Bool private; /* TRUE => mark nodes as private */ { register int chr; /* Temporary character */ Symbol default_name[MAX_NAME];/* Default name */ Bool error; /* TRUE => error has occured */ char huge[HUGE_STRING]; /* Huge string for W. Teitelman */ register FILE *in_file; /* Input file name */ register int line; /* Current line number */ Symbol name[MAX_NAME]; /* Full name of node */ register Name name_pointer; /* Pointer into name */ register Node node; /* Node associated with name */ register Name prefix_pointer; /* Pointer into prefix */ Hash symbols; /* String table */ char temp[MAX_STRING];/* Temporary symbol */ char *value; /* Node value */ int version; /* Version number */ in_file = fopen(file_name, "r"); if (in_file == NULL) return True; line = 1; /* Make sure the version number is correct. */ error = False; if (parse_symbol(in_file, temp) == NULL) error = True; else { version = parse_int(in_file, &error); (void)parse_eol(in_file); } if (error || !symbol_equal(temp, VERSION_TAG)){ warn("Line 1 in %s is not a correctly formatted defaults file", file_name); (void)fclose(in_file); return False; } if (version != VERSION_NUMBER){ warn("Line 1 in %s has version number %d rather than %d", file_name, version, VERSION_NUMBER); (void)fclose(in_file); return False; } if (defaults == NULL) defaults_init(True); symbols = defaults->symbols; default_name[0] = NULL; while (True){ /* Dispatch on first printing character. */ chr = parse_whitespace(in_file); if ((chr == '\n') || (chr == ';')){ (void)parse_eol(in_file); line++; continue; } if (chr == EOF) break; /* Grab the name */ if (name_parse(in_file, symbols, default_name, name) == 0){ warn("Could not parse a node name on line %d from %s", line, file_name); (void)parse_eol(in_file); line++; continue; } /* See whether name matches prefix. */ name_pointer = name; prefix_pointer = prefix; while (*prefix_pointer == *name_pointer){ prefix_pointer++; name_pointer++; } if (*prefix_pointer != NULL){ /* Prefix does not match. */ (void)parse_eol(in_file); line++; continue; } /* Prefix matches, so look up node and enter value. */ node = node_find_name(name, False, (int *)NULL); if (node == NULL){ /* This really should not happen. */ (void)name_unparse(name, temp); warn("Problem entering '%s' into database on line %d", temp, line); (void)parse_eol(in_file); line++; break; } chr = parse_whitespace(in_file); value = NULL; if (chr == '\n') value = DEFAULTS_UNDEFINED; else if (chr == '"'){ value = parse_string(in_file, huge, HUGE_STRING); if (value != NULL) value = defaults_strdup(huge); } if (value == NULL){ (void)name_unparse(name, temp); warn("%s Could not be assigned a value", temp); value = ""; } if (private){ node->private = True; node->value = value; } else { node->default_value = value; if (!node->private) node->value = value; } (void)parse_eol(in_file); line++; } (void)fclose(in_file); return False; } /* * read_lazy(name) will cause all the nodes associated with Name to be read * in, if they have not already been read in. */ static void read_lazy(name) Name name; /* Name to use */ { register Node node; /* Temporary node */ Symbol temp_name[2]; /* Temporary name */ temp_name[0] = name[0]; temp_name[1] = NULL; node = (Node)hash_lookup(defaults->nodes, temp_name); if ((node != NULL) && (node->file)) read_master(name[0]); } /* * read_master(file_name) will read in File_Name from the master database. */ void read_master(file_name) char *file_name; /* File name to read in */ { register Node node; /* Database node */ char name[MAX_STRING]; /* Temporary file name */ Symbol prefix[2]; /* Empty prefix */ if (defaults->database_dir == NULL) return; prefix[0] = symbol_lookup(file_name); prefix[1] = NULL; node = (Node)hash_lookup(defaults->nodes, prefix); if (node != NULL) node->file = False; /* Read from private database. */ if (defaults->private_dir != NULL){ (void)strcpy(name, defaults->private_dir); (void)strcat(name, file_name); (void)strcat(name, ".d"); prefix[0] = NULL; if (!read(name, prefix, False)) return; } /* Read from master database. */ (void)strcpy(name, defaults->database_dir); (void)strcat(name, file_name); (void)strcat(name, ".d"); prefix[0] = NULL; if (read(name, prefix, False)) warn("Could not open database file '%s'", name); } /* * read_master_database() will find the master database directory and enter * nodes into the database for each file in the master database directory. */ void read_master_database() { char *database_dir; /* Database directory name */ Node node; /* Current node */ char *private_dir; /* Private database directory */ /* Find the defaults directory. */ database_dir = defaults->database_dir; if (database_dir == NULL){ node = node_lookup_path("/Defaults/Directory", True, (int *)NULL); if (node == NULL) database_dir = "/usr/lib/defaults/"; else database_dir = slash_append(node->value); defaults->database_dir = database_dir; } read_master_database1(database_dir); /* Find the private defaults directory. */ private_dir = defaults->private_dir; if (private_dir == NULL){ node = node_lookup_path("/Defaults/Private_Directory", True, (int *)NULL); if (node == NULL) private_dir = NULL; else private_dir = slash_append(node->value); defaults->private_dir = private_dir; } if (private_dir != NULL) read_master_database1(private_dir); } /* * read_master_database1(dir_name) will read in the *.d file names contained * in the Dir_Name directory. */ static void read_master_database1(dir_name) char *dir_name; /* Directory name */ { register DIR *dir; /* Default directory */ register struct direct *dir_entry; /* Directory entry */ Symbol name[MAX_NAME]; /* Name to create */ register Node node; /* Current node */ register char *pointer; /* Pointer into file name */ int size; /* Size of string */ char symbol[MAX_STRING]; /* Temporary symbol */ dir = opendir(dir_name); if (dir == NULL){ warn("Could not find defaults database directory %s\n", dir_name); warn("Suppressing subsequent error messages.\n"); defaults->errors = 1000000; /* A big number */ return; } /* Scan the database for all the top level nodes. */ name[1] = NULL; while (True){ dir_entry = readdir(dir); if (dir_entry == NULL) break; pointer = dir_entry->d_name; size = strlen(pointer); if ((size < 3) || (strcmp(&pointer[size - 2], ".d") != 0)) continue; (void)strcpy(symbol, pointer); symbol[size - 2] = '\0'; name[0] = symbol_lookup(symbol); node = node_lookup_name(name, False, (int *)NULL); if (node == NULL) node = node_find_name(name, False, (int *)NULL); if (node == NULL) warn("Could not create node '%s'\n"); else node->file = True; } closedir(dir); } /* * read_private_database() will read in env var DEFAULTS_FILE */ void read_private_database() { char file_name[MAX_STRING]; /* Local defaults file name */ Symbol prefix[1]; /* Empty prefix */ char *temp; file_name[0] = '0'; if ((temp = getenv("DEFAULTS_FILE")) != NULL) { (void)strcpy(file_name, temp); } else { if ((temp = getlogindir()) != NULL); (void)strcpy(file_name, temp); (void)strcat(file_name, "/.defaults"); } prefix[0] = NULL; read(file_name, prefix, True); } /* * slash_append(text) will return make sure that Text has a slash ('/') * on the end. */ char * slash_append(text) register char *text; /* String to append to */ { char temp[MAX_STRING]; /* Maximum string length */ if (text[strlen(text) - 1] == '/') return text; (void)strcpy(temp, text); (void)strcat(temp, "/"); return symbol_copy(temp); } /* * symbol_copy(symbol) will make and return a copy of Symbol. */ char * symbol_copy(symbol) char *symbol; /* Symbol to copy */ { register char *new; /* New symbol */ new = hash_get_memory(strlen(symbol) + 1); (void)strcpy(new, symbol); return new; } /* * symbol_equal(symbol1, symbol2) will return True if Symbol1 equals Symbol2. */ Bool symbol_equal(symbol1, symbol2) register char *symbol1; /* First symbol */ register char *symbol2; /* Second symbol */ { register char chr1; /* Character from first symbol */ register char chr2; /* Character from second symbol */ while (True){ chr1 = *symbol1++; if (('A' <= chr1) && (chr1 <= 'Z')) chr1 += 'a' - 'A'; chr2 = *symbol2++; if (('A' <= chr2) && (chr2 <= 'Z')) chr2 += 'a' - 'A'; if (chr1 != chr2) return False; if (chr1 == '\0') return True; } } /* * symbol_hash(symbol) will return a hash of Symbol. */ static int symbol_hash(symbol) Symbol symbol; /* Symbol to hash */ { register char chr; /* Character from symbol */ register int hash; /* Hash value */ hash = 0; do { chr = *symbol++; if (('A' <= chr) && (chr <= 'Z')) chr += 'a' - 'A'; hash += chr; } while (chr != '\0'); return hash; } /* * symbol_lookup(symbol) will return the conical value for Symbol from * the symbols hash table. */ Symbol symbol_lookup(symbol) Symbol symbol; /* Symbol to lookup */ { register Symbol new; /* New symbol */ if (defaults == NULL) defaults_init(True); new = (char *)hash_lookup(defaults->symbols, (caddr_t)symbol); if (new == NULL){ new = symbol_copy(symbol); (void)hash_insert(defaults->symbols, (caddr_t)new, (caddr_t)new); } return new; } /* * This is here only because this file seemed a convenient place * to move some data into. The data came from defaults_put.c, * which we want to compile using the `-R' switch. Therefore, * that file cannot contain even the tiniest smidgen of data. */ Defaults_pairs warn_error_action[] = { "Continue", 1, "Abort", 2, "Suppress", 3, NULL, 1, };