root/1.8.3/tags/p6/src/conf.c

Revision 1167, 51.1 KB (checked in by shawnw, 13 months ago)

Merge devel into trunk for p6 release

Line 
1/**
2 * \file conf.c
3 *
4 * \brief PennMUSH runtime configuration.
5 *
6 * configuration adjustment. Some of the ideas and bits and pieces of the
7 * code here are based on TinyMUSH 2.0.
8 *
9 */
10
11#include "copyrite.h"
12#include "config.h"
13
14#include <stdio.h>
15#ifdef I_SYS_TIME
16#include <sys/time.h>
17#ifdef TIME_WITH_SYS_TIME
18#include <time.h>
19#endif
20#else
21#include <time.h>
22#endif
23#include <string.h>
24#include <stdlib.h>
25#include <ctype.h>
26
27#include "conf.h"
28#include "externs.h"
29#include "ansi.h"
30#include "pueblo.h"
31#include "mushdb.h"
32#include "parse.h"
33#include "command.h"
34#include "flags.h"
35#include "log.h"
36#include "dbdefs.h"
37#include "game.h"
38#include "attrib.h"
39#include "help.h"
40#include "function.h"
41#include "confmagic.h"
42
43time_t mudtime;                 /**< game time, in seconds */
44
45static void show_compile_options(dbref player);
46static char *config_list_helper(dbref player, PENNCONF *cp, int lc);
47static char *config_list_helper2(dbref player, PENNCONF *cp, int lc);
48
49OPTTAB options;         /**< The table of configuration options */
50HASHTAB local_options;  /**< Hash table for local config options */
51
52int config_set(const char *opt, char *val, int source, int restrictions);
53void conf_default_set(void);
54
55/** Table of all runtime configuration options. */
56PENNCONF conftable[] = {
57  {"input_database", cf_str, options.input_db, sizeof options.input_db, 0,
58   "files"}
59  ,
60  {"output_database", cf_str, options.output_db, sizeof options.output_db, 0,
61   "files"}
62  ,
63  {"crash_database", cf_str, options.crash_db, sizeof options.crash_db, 0,
64   "files"}
65  ,
66  {"mail_database", cf_str, options.mail_db, sizeof options.mail_db, 0, "files"}
67  ,
68  {"chat_database", cf_str, options.chatdb, sizeof options.chatdb, 0, "files"}
69  ,
70  {"compress_suffix", cf_str, options.compresssuff, sizeof options.compresssuff,
71   0,
72   "files"}
73  ,
74  {"compress_program", cf_str, options.compressprog,
75   sizeof options.compressprog, 0,
76   "files"}
77  ,
78  {"uncompress_program", cf_str, options.uncompressprog,
79   sizeof options.uncompressprog, 0,
80   "files"}
81  ,
82  {"access_file", cf_str, options.access_file, sizeof options.access_file, 0,
83   "files"}
84  ,
85  {"names_file", cf_str, options.names_file, sizeof options.access_file, 0,
86   "files"}
87  ,
88  {"connect_file", cf_str, options.connect_file[0],
89   sizeof options.connect_file[0], 0, "messages"}
90  ,
91  {"motd_file", cf_str, options.motd_file[0], sizeof options.motd_file[0], 0,
92   "messages"}
93  ,
94  {"wizmotd_file", cf_str, options.wizmotd_file[0],
95   sizeof options.wizmotd_file[0], 0, "messages"}
96  ,
97  {"newuser_file", cf_str, options.newuser_file[0],
98   sizeof options.newuser_file[0], 0, "messages"}
99  ,
100
101  {"register_create_file", cf_str, options.register_file[0],
102   sizeof options.register_file[0],
103   0, "messages"}
104  ,
105  {"quit_file", cf_str, options.quit_file[0], sizeof options.quit_file[0], 0,
106   "messages"}
107  ,
108  {"down_file", cf_str, options.down_file[0], sizeof options.down_file[0], 0,
109   "messages"}
110  ,
111  {"full_file", cf_str, options.full_file[0], sizeof options.full_file[0], 0,
112   "messages"}
113  ,
114  {"guest_file", cf_str, options.guest_file[0], sizeof options.guest_file[0], 0,
115   "messages"}
116  ,
117
118  {"connect_html_file", cf_str, options.connect_file[1],
119   sizeof options.connect_file[1], 0,
120   "messages"}
121  ,
122  {"motd_html_file", cf_str, options.motd_file[1],
123   sizeof options.connect_file[1], 0,
124   "messages"}
125  ,
126  {"wizmotd_html_file", cf_str, options.wizmotd_file[1],
127   sizeof options.wizmotd_file[1], 0,
128   "messages"}
129  ,
130  {"newuser_html_file", cf_str, options.newuser_file[1],
131   sizeof options.newuser_file[1], 0,
132   "messages"}
133  ,
134  {"register_create_html_file", cf_str, options.register_file[1],
135   sizeof options.register_file[1], 0, "messages"}
136  ,
137  {"quit_html_file", cf_str, options.quit_file[1], sizeof options.quit_file[1],
138   0,
139   "messages"}
140  ,
141  {"down_html_file", cf_str, options.down_file[1], sizeof options.down_file[1],
142   0,
143   "messages"}
144  ,
145  {"full_html_file", cf_str, options.full_file[1], sizeof options.full_file[1],
146   0,
147   "messages"}
148  ,
149  {"guest_html_file", cf_str, options.guest_file[1],
150   sizeof options.guest_file[1], 0,
151   "messages"}
152  ,
153
154  {"player_start", cf_dbref, &options.player_start, 100000, 0, "db"}
155  ,
156  {"master_room", cf_dbref, &options.master_room, 100000, 0, "db"}
157  ,
158  {"base_room", cf_dbref, &options.base_room, 100000, 0, "db"}
159  ,
160  {"default_home", cf_dbref, &options.default_home, 100000, 0, "db"}
161  ,
162  {"exits_connect_rooms", cf_bool, &options.exits_connect_rooms, 2, 0, "db"}
163  ,
164  {"zone_control_zmp_only", cf_bool, &options.zone_control, 2, 0, "db"}
165  ,
166  {"ancestor_room", cf_dbref, &options.ancestor_room, 100000, 0, "db"}
167  ,
168  {"ancestor_exit", cf_dbref, &options.ancestor_exit, 100000, 0, "db"}
169  ,
170  {"ancestor_thing", cf_dbref, &options.ancestor_thing, 100000, 0, "db"}
171  ,
172  {"ancestor_player", cf_dbref, &options.ancestor_player, 100000, 0, "db"}
173  ,
174  {"mud_name", cf_str, options.mud_name, 128, 0, "net"}
175  ,
176  {"ip_addr", cf_str, options.ip_addr, 64, 0, "net"}
177  ,
178  {"ssl_ip_addr", cf_str, options.ssl_ip_addr, 64, 0, "net"}
179  ,
180  {"port", cf_int, &options.port, 65535, 0, "net"}
181  ,
182  {"ssl_port", cf_int, &options.ssl_port, 65535, 0, "net"}
183  ,
184  {"use_dns", cf_bool, &options.use_dns, 2, 0, "net"}
185  ,
186  {"use_ident", cf_bool, &options.use_ident, 2, 0, "net"}
187  ,
188  {"ident_timeout", cf_time, &options.ident_timeout, 60, 0, "net"}
189  ,
190  {"logins", cf_bool, &options.login_allow, 2, 0, "net"}
191  ,
192  {"player_creation", cf_bool, &options.create_allow, 2, 0, "net"}
193  ,
194  {"guests", cf_bool, &options.guest_allow, 2, 0, "net"}
195  ,
196  {"pueblo", cf_bool, &options.support_pueblo, 2, 0, "net"}
197  ,
198  {"sql_platform", cf_str, options.sql_platform, sizeof options.sql_platform, 0,
199   "net"}
200  ,
201  {"sql_host", cf_str, options.sql_host, sizeof options.sql_host, 0, "net"}
202  ,
203  {"sql_username", cf_str, options.sql_username, sizeof options.sql_username, 0,
204   NULL}
205  ,
206  {"sql_password", cf_str, options.sql_password, sizeof options.sql_password, 0,
207   NULL}
208  ,
209  {"sql_database", cf_str, options.sql_database, sizeof options.sql_database, 0,
210   NULL}
211  ,
212  {"forking_dump", cf_bool, &options.forking_dump, 2, 0, "dump"}
213  ,
214  {"dump_message", cf_str, options.dump_message, sizeof options.dump_message, 0,
215   "dump"}
216  ,
217  {"dump_complete", cf_str, options.dump_complete, sizeof options.dump_complete,
218   0, "dump"}
219  ,
220  {"dump_warning_1min", cf_str, options.dump_warning_1min,
221   sizeof options.dump_warning_1min,
222   0, "dump"}
223  ,
224  {"dump_warning_5min", cf_str, options.dump_warning_5min,
225   sizeof options.dump_warning_5min, 0,
226   "dump"}
227  ,
228  {"dump_interval", cf_time, &options.dump_interval, 100000, 0, "dump"}
229  ,
230  {"warn_interval", cf_time, &options.warn_interval, 32000, 0, "dump"}
231  ,
232  {"purge_interval", cf_time, &options.purge_interval, 10000, 0, "dump"}
233  ,
234  {"dbck_interval", cf_time, &options.dbck_interval, 10000, 0, "dump"}
235  ,
236
237  {"money_singular", cf_str, options.money_singular,
238   sizeof options.money_singular, 0,
239   "cosmetic"}
240  ,
241  {"money_plural", cf_str, options.money_plural, sizeof options.money_plural, 0,
242   "cosmetic"}
243  ,
244  {"player_name_spaces", cf_bool, &options.player_name_spaces, 2, 0,
245   "cosmetic"}
246  ,
247  {"max_aliases", cf_int, &options.max_aliases, -1, 0, "limits"}
248  ,
249  {"ansi_names", cf_bool, &options.ansi_names, 2, 0, "cosmetic"}
250  ,
251  {"only_ascii_in_names", cf_bool, &options.ascii_names, 2, 0, "cosmetic"}
252  ,
253  {"float_precision", cf_int, &options.float_precision, 10000, 0, "cosmetic"}
254  ,
255  {"comma_exit_list", cf_bool, &options.comma_exit_list, 2, 0, "cosmetic"}
256  ,
257  {"count_all", cf_bool, &options.count_all, 2, 0, "cosmetic"}
258  ,
259  {"blind_page", cf_bool, &options.blind_page, 2, 0, "cosmetic"}
260  ,
261  {"page_aliases", cf_bool, &options.page_aliases, 2, 0, "cosmetic"}
262  ,
263  {"flags_on_examine", cf_bool, &options.flags_on_examine, 2, 0, "cosmetic"}
264  ,
265  {"ex_public_attribs", cf_bool, &options.ex_public_attribs, 2, 0,
266   "cosmetic"}
267  ,
268  {"wizwall_prefix", cf_str, options.wizwall_prefix,
269   sizeof options.wizwall_prefix, 0,
270   "cosmetic"}
271  ,
272  {"rwall_prefix", cf_str, options.rwall_prefix, sizeof options.rwall_prefix, 0,
273   "cosmetic"}
274  ,
275  {"wall_prefix", cf_str, options.wall_prefix, sizeof options.wall_prefix, 0,
276   "cosmetic"}
277  ,
278  {"announce_connects", cf_bool, &options.announce_connects, 2, 0, "cosmetic"}
279  ,
280  {"chat_strip_quote", cf_bool, &options.chat_strip_quote, 2, 0, "cosmetic"}
281  ,
282  {"newline_one_char", cf_bool, &options.newline_one_char, 2, 0, "cosmetic"}
283  ,
284
285  {"max_dbref", cf_dbref, &options.max_dbref, -1, 0, "limits"}
286  ,
287  {"max_attrs_per_obj", cf_int, &options.max_attrcount, 8192, 0, "limits"}
288  ,
289  {"max_logins", cf_int, &options.max_logins, 128, 0, "limits"}
290  ,
291  {"max_guests", cf_int, &options.max_guests, 128, 0, "limits"}
292  ,
293  {"idle_timeout", cf_time, &options.idle_timeout, 100000, 0, "limits"}
294  ,
295  {"unconnected_idle_timeout", cf_time, &options.unconnected_idle_timeout,
296   100000, 0, "limits"}
297  ,
298  {"keepalive_timeout", cf_time, &options.keepalive_timeout, 10000, 0, "limits"}
299  ,
300  {"whisper_loudness", cf_int, &options.whisper_loudness, 100, 0, "limits"}
301  ,
302  {"starting_quota", cf_int, &options.starting_quota, 10000, 0, "limits"}
303  ,
304  {"starting_money", cf_int, &options.starting_money, 10000, 0, "limits"}
305  ,
306  {"paycheck", cf_int, &options.paycheck, 1000, 0, "limits"}
307  ,
308  {"guest_paycheck", cf_int, &options.guest_paycheck, 1000, 0, "limits"}
309  ,
310  {"max_pennies", cf_int, &options.max_pennies, 100000, 0, "limits"}
311  ,
312  {"max_guest_pennies", cf_int, &options.max_guest_pennies, 100000, 0,
313   "limits"}
314  ,
315  {"max_parents", cf_int, &options.max_parents, 10000, 0, "limits"}
316  ,
317  {"mail_limit", cf_int, &options.mail_limit, 5000, 0, "limits"}
318  ,
319  {"max_depth", cf_int, &options.max_depth, 10000, 0, "limits"}
320  ,
321  {"player_queue_limit", cf_int, &options.player_queue_limit, 100000, 0,
322   "limits"}
323  ,
324  {"queue_loss", cf_int, &options.queue_loss, 10000, 0, "limits"}
325  ,
326  {"queue_chunk", cf_int, &options.queue_chunk, 100000, 0, "limits"}
327  ,
328  {"active_queue_chunk", cf_int, &options.active_q_chunk, 100000, 0,
329   "limits"}
330  ,
331  {"function_recursion_limit", cf_int, &options.func_nest_lim, 100000, 0,
332   "limits"}
333  ,
334  {"function_invocation_limit", cf_int, &options.func_invk_lim, 100000, 0,
335   "limits"}
336  ,
337  {"call_limit", cf_int, &options.call_lim, 1000000, 0,
338   "limits"}
339  ,
340  {"player_name_len", cf_int, &options.player_name_len, BUFFER_LEN, 0,
341   "limits"}
342  ,
343  {"queue_entry_cpu_time", cf_int, &options.queue_entry_cpu_time, 100000, 0,
344   "limits"}
345  ,
346  {"max_global_fns", cf_int, &options.max_global_fns, 2000, 0, 0}
347  ,
348  {"use_quota", cf_bool, &options.use_quota, 2, 0, "limits"}
349  ,
350  {"max_channels", cf_int, &options.max_channels, 1000, 0, "chat"}
351  ,
352  {"max_player_chans", cf_int, &options.max_player_chans, 100, 0, "chat"}
353  ,
354  {"chan_cost", cf_int, &options.chan_cost, 10000, 0, "chat"}
355  ,
356  {"noisy_cemit", cf_bool, &options.noisy_cemit, 2, 0, "chat"}
357  ,
358  {"log_commands", cf_bool, &options.log_commands, 2, 0, "log"}
359  ,
360  {"log_forces", cf_bool, &options.log_forces, 2, 0, "log"}
361  ,
362  {"error_log", cf_str, options.error_log, sizeof options.error_log, 0,
363   "log"}
364  ,
365  {"command_log", cf_str, options.command_log, sizeof options.command_log, 0,
366   "log"}
367  ,
368  {"wizard_log", cf_str, options.wizard_log, sizeof options.wizard_log, 0,
369   "log"}
370  ,
371  {"checkpt_log", cf_str, options.checkpt_log, sizeof options.checkpt_log, 0,
372   "log"}
373  ,
374  {"trace_log", cf_str, options.trace_log, sizeof options.trace_log, 0,
375   "log"}
376  ,
377  {"connect_log", cf_str, options.connect_log, sizeof options.connect_log, 0,
378   "log"}
379  ,
380
381  {"player_flags", cf_flag, options.player_flags, sizeof options.player_flags,
382   0, "flags"}
383  ,
384  {"room_flags", cf_flag, options.room_flags, sizeof options.room_flags, 0,
385   "flags"}
386  ,
387  {"exit_flags", cf_flag, options.exit_flags, sizeof options.exit_flags, 0,
388   "flags"}
389  ,
390  {"thing_flags", cf_flag, options.thing_flags, sizeof options.thing_flags, 0,
391   "flags"}
392  ,
393  {"channel_flags", cf_flag, options.channel_flags,
394   sizeof options.channel_flags, 0,
395   "flags"}
396  ,
397
398  {"safer_ufun", cf_bool, &options.safer_ufun, 2, 0, "funcs"}
399  ,
400  {"function_side_effects", cf_bool, &options.function_side_effects, 2, 0,
401   "funcs"}
402  ,
403
404  {"noisy_whisper", cf_bool, &options.noisy_whisper, 2, 0, "cmds"}
405  ,
406  {"possessive_get", cf_bool, &options.possessive_get, 2, 0, "cmds"}
407  ,
408  {"possessive_get_d", cf_bool, &options.possessive_get_d, 2, 0, "cmds"}
409  ,
410  {"link_to_object", cf_bool, &options.link_to_object, 2, 0, "cmds"}
411  ,
412  {"owner_queues", cf_bool, &options.owner_queues, 2, 0, "cmds"}
413  ,
414  {"full_invis", cf_bool, &options.full_invis, 2, 0, "cmds"}
415  ,
416  {"wiz_noaenter", cf_bool, &options.wiz_noaenter, 2, 0, "cmds"}
417  ,
418  {"really_safe", cf_bool, &options.really_safe, 2, 0, "cmds"}
419  ,
420  {"destroy_possessions", cf_bool, &options.destroy_possessions, 2, 0,
421   "cmds"}
422  ,
423
424  {"null_eq_zero", cf_bool, &options.null_eq_zero, 2, 0, "tiny"}
425  ,
426  {"tiny_booleans", cf_bool, &options.tiny_booleans, 2, 0, "tiny"}
427  ,
428  {"tiny_trim_fun", cf_bool, &options.tiny_trim_fun, 2, 0, "tiny"}
429  ,
430  {"tiny_math", cf_bool, &options.tiny_math, 2, 0, "tiny"}
431  ,
432  {"silent_pemit", cf_bool, &options.silent_pemit, 2, 0, "tiny"}
433  ,
434
435  {"adestroy", cf_bool, &options.adestroy, 2, 0, "attribs"}
436  ,
437  {"amail", cf_bool, &options.amail, 2, 0, "attribs"}
438  ,
439  {"player_listen", cf_bool, &options.player_listen, 2, 0, "attribs"}
440  ,
441  {"player_ahear", cf_bool, &options.player_ahear, 2, 0, "attribs"}
442  ,
443  {"startups", cf_bool, &options.startups, 2, 0, "attribs"}
444  ,
445  {"read_remote_desc", cf_bool, &options.read_remote_desc, 2, 0, "attribs"}
446  ,
447  {"room_connects", cf_bool, &options.room_connects, 2, 0, "attribs"}
448  ,
449  {"reverse_shs", cf_bool, &options.reverse_shs, 2, 0, "attribs"}
450  ,
451  {"empty_attrs", cf_bool, &options.empty_attrs, 2, 0, "attribs"}
452  ,
453  {"object_cost", cf_int, &options.object_cost, 10000, 0, "costs"}
454  ,
455  {"exit_cost", cf_int, &options.exit_cost, 10000, 0, "costs"}
456  ,
457  {"link_cost", cf_int, &options.link_cost, 10000, 0, "costs"}
458  ,
459  {"room_cost", cf_int, &options.room_cost, 10000, 0, "costs"}
460  ,
461  {"queue_cost", cf_int, &options.queue_cost, 10000, 0, "costs"}
462  ,
463  {"quota_cost", cf_int, &options.quota_cost, 10000, 0, "costs"}
464  ,
465  {"find_cost", cf_int, &options.find_cost, 10000, 0, "costs"}
466  ,
467  {"page_cost", cf_int, &options.page_cost, 10000, 0, "costs"}
468  ,
469  {"kill_default_cost", cf_int, &options.kill_default_cost, 10000, 0,
470   "costs"}
471  ,
472  {"kill_min_cost", cf_int, &options.kill_min_cost, 10000, 0, "costs"}
473  ,
474  {"kill_bonus", cf_int, &options.kill_bonus, 100, 0, "costs"}
475  ,
476
477  {"log_wipe_passwd", cf_str, options.log_wipe_passwd,
478   sizeof options.log_wipe_passwd, 0,
479   NULL}
480  ,
481
482  {"chunk_swap_file", cf_str, options.chunk_swap_file,
483   sizeof options.chunk_swap_file, 0, "files"}
484  ,
485  {"chunk_cache_memory", cf_int, &options.chunk_cache_memory,
486   1000000000, 65510 * 2, "files"}
487  ,
488  {"chunk_migrate", cf_int, &options.chunk_migrate_amount, 100000, 0,
489   "limits"}
490  ,
491#ifdef HAS_OPENSSL
492  {"ssl_private_key_file", cf_str, options.ssl_private_key_file,
493   sizeof options.ssl_private_key_file, 0, "files"}
494  ,
495  {"ssl_ca_file", cf_str, options.ssl_ca_file,
496   sizeof options.ssl_ca_file, 0, "files"}
497  ,
498  {"ssl_require_client_cert", cf_bool, &options.ssl_require_client_cert,
499   2, 0, "net"}
500  ,
501#endif
502  {"mem_check", cf_bool, &options.mem_check, 2, 0, "log"}
503  ,
504
505  {NULL, NULL, NULL, 0, 0, NULL}
506};
507
508/** A runtime configuration group.
509 * This struction represents the name and information about a group
510 * of runtime configuration directives. Groups are used to organize
511 * the display of configuration options.
512 */
513typedef struct confgroupparm {
514  const char *name;             /**< name of group */
515  const char *desc;             /**< description of group */
516  int viewperms;                /**< who can view this group */
517} PENNCONFGROUP;
518
519/** The table of all configuration groups. */
520PENNCONFGROUP confgroups[] = {
521  {"attribs", "Options affecting attributes", 0},
522  {"chat", "Chat system options", 0},
523  {"cmds", "Options affecting command behavior", 0},
524  {"compile", "Compile-time options", 0},
525  {"cosmetic", "Cosmetic options", 0},
526  {"costs", "Costs", 0},
527  {"db", "Database options", 0},
528  {"dump", "Options affecting dumps and other periodic processes", 0},
529  {"files", "Files used by the MUSH", CGP_GOD},
530  {"flags", "Default flags for new objects", 0},
531  {"funcs", "Options affecting function behavior", 0},
532  {"limits", "Limits and other constants", 0},
533  {"log", "Logging options", 0},
534  {"messages", "Message files sent by the MUSH", CGP_GOD},
535  {"net", "Networking and connection-related options", 0},
536  {"tiny", "TinyMUSH compatibility options", 0},
537  {NULL, NULL, 0}
538};
539
540#if 0
541/* Just to mark these strings for translation */
542PENNCONFGROUP dummy[] = {
543  {"attribs", T("Options affecting attributes"), 0},
544  {"chat", T("Chat system options"), 0},
545  {"cmds", T("Options affecting command behavior"), 0},
546  {"compile", T("Compile-time options"), 0},
547  {"cosmetic", T("Cosmetic options"), 0},
548  {"costs", T("Costs"), 0},
549  {"db", T("Database options"), 0},
550  {"dump", T("Options affecting dumps and other periodic processes"), 0},
551  {"files", T("Files used by the MUSH"), CGP_GOD},
552  {"flags", T("Default flags for new objects"), 0},
553  {"funcs", T("Options affecting function behavior"), 0},
554  {"limits", T("Limits and other constants"), 0},
555  {"log", T("Logging options"), 0},
556  {"messages", T("Message files sent by the MUSH"), CGP_GOD},
557  {"net", T("Networking and connection-related options"), 0},
558  {"tiny", T("TinyMUSH compatibility options"), 0},
559  {NULL, NULL, 0}
560};
561#endif
562
563/** Returns a pointer to a newly allocated PENNCONF object.
564 * \return pointer to newly allocated PENNCONF object.
565 */
566PENNCONF *
567new_config(void)
568{
569  return ((PENNCONF *) mush_malloc(sizeof(PENNCONF), "config"));
570}
571
572/** Add a new local runtime configuration parameter to local_options.
573 * This function will not override an existing local configuration
574 * option.
575 * \param name name of the configuration option.
576 * \param handler cf_* function to handle the option.
577 * \param loc address to store the value of the option.
578 * \param max maximum value allowed for the option.
579 * \param group name of the option group the option should display with.
580 * \return pointer to configuration structure or NULL for failure.
581 */
582PENNCONF *
583add_config(const char *name, config_func handler, void *loc, int max,
584           const char *group)
585{
586  PENNCONF *cnf;
587  if ((cnf = get_config(name)))
588    return cnf;
589  if ((cnf = new_config()) == NULL)
590    return NULL;
591  cnf->name = mush_strdup(strupper(name), "config name");
592  cnf->handler = handler;
593  cnf->loc = loc;
594  cnf->max = max;
595  cnf->overridden = 0;
596  cnf->group = group;
597  hashadd(name, (void *) cnf, &local_options);
598  return cnf;
599}
600
601/** Return a local runtime configuration parameter by name.
602 * This function returns a point to a configuration structure (PENNCONF *)
603 * if one exists in the local runtime options that matches the given
604 * name. Only local_options is searched.
605 * \param name name of the configuration option.
606 * \return pointer to configuration structure or NULL for failure.
607 */
608PENNCONF *
609get_config(const char *name)
610{
611  return ((PENNCONF *) hashfind(name, &local_options));
612}
613
614/** Parse a boolean configuration option.
615 * \param opt name of the configuration option.
616 * \param val value of the option.
617 * \param loc address to store the value.
618 * \param maxval (unused).
619 * \param from_cmd 0 if read from config file; 1 if from command.
620 * \retval 0 failure (unable to parse val).
621 * \retval 1 success.
622 */
623int
624cf_bool(const char