options.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /* Copyright 1995-1996,1998,2000-2001,2006,2008-2011,2018
  2. Free Software Foundation, Inc.
  3. This file is part of Guile.
  4. Guile is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Lesser General Public License as published
  6. by the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. Guile is distributed in the hope that it will be useful, but WITHOUT
  9. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  11. License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with Guile. If not, see
  14. <https://www.gnu.org/licenses/>. */
  15. #ifdef HAVE_CONFIG_H
  16. # include <config.h>
  17. #endif
  18. #include "boolean.h"
  19. #include "list.h"
  20. #include "mallocs.h"
  21. #include "numbers.h"
  22. #include "pairs.h"
  23. #include "strings.h"
  24. #include "symbols.h"
  25. #include "options.h"
  26. /* {Run-time options}
  27. *
  28. * This is the basic interface for low-level configuration of the
  29. * Guile library. It is used for configuring the reader, evaluator,
  30. * printer and debugger.
  31. *
  32. * Motivation:
  33. *
  34. * 1. Altering option settings can have side effects.
  35. * 2. Option values can be stored in native format.
  36. * (Important for efficiency in, e. g., the evaluator.)
  37. * 3. Doesn't use up name space.
  38. * 4. Options can be naturally grouped => ease of use.
  39. */
  40. /* scm_options is the core of all options interface procedures.
  41. *
  42. * Some definitions:
  43. *
  44. * Run time options in Guile are arranged in groups. Each group
  45. * affects a certain aspect of the behaviour of the library.
  46. *
  47. * An "options interface procedure" manages one group of options. It
  48. * can be used to check or set options, or to get documentation for
  49. * all options of a group. The options interface procedure is not
  50. * intended to be called directly by the user. The user should
  51. * instead call
  52. *
  53. * (<group>-options)
  54. * (<group>-options 'help)
  55. * (<group>-options 'full)
  56. *
  57. * to display current option settings (The second version also
  58. * displays documentation. The third version also displays
  59. * information about programmer's options.), and
  60. *
  61. * (<group>-enable '<option-symbol>)
  62. * (<group>-disable '<option-symbol>)
  63. * (<group>-set! <option-symbol> <value>)
  64. * (<group>-options <option setting>)
  65. *
  66. * to alter the state of an option (The last version sets all
  67. * options according to <option setting>.) where <group> is the name
  68. * of the option group.
  69. *
  70. * An "option setting" represents the state of all low-level options
  71. * managed by one options interface procedure. It is a list of
  72. * single symbols and symbols followed by a value.
  73. *
  74. * For boolean options, the presence of the symbol of that option in
  75. * the option setting indicates a true value. If the symbol isn't a
  76. * member of the option setting this represents a false value.
  77. *
  78. * Other options are represented by a symbol followed by the value.
  79. *
  80. * If scm_options is called without arguments, the current option
  81. * setting is returned. If the argument is an option setting, options
  82. * are altered and the old setting is returned. If the argument isn't
  83. * a list, a list of sublists is returned, where each sublist contains
  84. * option name, value and documentation string.
  85. */
  86. SCM_SYMBOL (scm_yes_sym, "yes");
  87. SCM_SYMBOL (scm_no_sym, "no");
  88. /* Return a list of the current option setting. The format of an
  89. * option setting is described in the above documentation. */
  90. static SCM
  91. get_option_setting (const scm_t_option options[])
  92. {
  93. unsigned int i;
  94. SCM ls = SCM_EOL;
  95. for (i = 0; options[i].name; ++i)
  96. {
  97. switch (options[i].type)
  98. {
  99. case SCM_OPTION_BOOLEAN:
  100. if (options[i].val)
  101. ls = scm_cons (SCM_PACK (options[i].name), ls);
  102. break;
  103. case SCM_OPTION_INTEGER:
  104. ls = scm_cons (scm_from_unsigned_integer (options[i].val), ls);
  105. ls = scm_cons (SCM_PACK (options[i].name), ls);
  106. break;
  107. case SCM_OPTION_SCM:
  108. ls = scm_cons (SCM_PACK (options[i].val), ls);
  109. ls = scm_cons (SCM_PACK (options[i].name), ls);
  110. }
  111. }
  112. return ls;
  113. }
  114. /* Return a list of sublists, where each sublist contains option name, value
  115. * and documentation string. */
  116. static SCM
  117. get_documented_option_setting (const scm_t_option options[])
  118. {
  119. SCM ans = SCM_EOL;
  120. unsigned int i;
  121. for (i = 0; options[i].name; ++i)
  122. {
  123. SCM ls = scm_cons (scm_from_utf8_string (options[i].doc), SCM_EOL);
  124. switch (options[i].type)
  125. {
  126. case SCM_OPTION_BOOLEAN:
  127. ls = scm_cons (options[i].val ? scm_yes_sym : scm_no_sym, ls);
  128. break;
  129. case SCM_OPTION_INTEGER:
  130. ls = scm_cons (scm_from_unsigned_integer (options[i].val), ls);
  131. break;
  132. case SCM_OPTION_SCM:
  133. ls = scm_cons (SCM_PACK (options[i].val), ls);
  134. }
  135. ls = scm_cons (SCM_PACK (options[i].name), ls);
  136. ans = scm_cons (ls, ans);
  137. }
  138. return scm_reverse_x (ans, SCM_UNDEFINED);
  139. }
  140. static int
  141. options_length (scm_t_option options[])
  142. {
  143. unsigned int i = 0;
  144. for (; options[i].name != NULL; ++i)
  145. ;
  146. return i;
  147. }
  148. /* Alters options according to the given option setting 'args'. The value of
  149. * args is known to be a list, but it is not known whether the list is a well
  150. * formed option setting, i. e. if for every non-boolean option a value is
  151. * given. For this reason, the function applies all changes to a copy of the
  152. * original setting in memory. Only if 'args' was successfully processed,
  153. * the new setting will overwrite the old one.
  154. *
  155. * If DRY_RUN is set, don't change anything. This is useful for trying out an option
  156. * before entering a critical section.
  157. */
  158. static void
  159. change_option_setting (SCM args, scm_t_option options[], const char *s,
  160. int dry_run)
  161. {
  162. unsigned int i;
  163. scm_t_bits *new_vals;
  164. new_vals = scm_gc_malloc (options_length (options) * sizeof (scm_t_bits),
  165. "new-options");
  166. for (i = 0; options[i].name; ++i)
  167. {
  168. if (options[i].type == SCM_OPTION_BOOLEAN)
  169. new_vals[i] = 0;
  170. else
  171. new_vals[i] = options[i].val;
  172. }
  173. while (!SCM_NULL_OR_NIL_P (args))
  174. {
  175. SCM name = SCM_CAR (args);
  176. int found = 0;
  177. for (i = 0; options[i].name && !found; ++i)
  178. {
  179. if (scm_is_eq (name, SCM_PACK (options[i].name)))
  180. {
  181. switch (options[i].type)
  182. {
  183. case SCM_OPTION_BOOLEAN:
  184. new_vals[i] = 1;
  185. break;
  186. case SCM_OPTION_INTEGER:
  187. args = SCM_CDR (args);
  188. new_vals[i] = scm_to_size_t (scm_car (args));
  189. break;
  190. case SCM_OPTION_SCM:
  191. args = SCM_CDR (args);
  192. new_vals[i] = SCM_UNPACK (scm_car (args));
  193. break;
  194. }
  195. found = 1;
  196. }
  197. }
  198. if (!found)
  199. scm_misc_error (s, "Unknown option name: ~S", scm_list_1 (name));
  200. args = SCM_CDR (args);
  201. }
  202. if (dry_run)
  203. return;
  204. for (i = 0; options[i].name; ++i)
  205. options[i].val = new_vals[i];
  206. }
  207. SCM
  208. scm_options (SCM args, scm_t_option options[], const char *s)
  209. {
  210. return scm_options_try (args, options, s, 0);
  211. }
  212. SCM
  213. scm_options_try (SCM args, scm_t_option options[], const char *s,
  214. int dry_run)
  215. {
  216. if (SCM_UNBNDP (args))
  217. return get_option_setting (options);
  218. else if (!SCM_NULL_OR_NIL_P (args) && !scm_is_pair (args))
  219. /* Dirk:FIXME:: This criterion should be improved. IMO it is better to
  220. * demand that args is #t if documentation should be shown than to say
  221. * that every argument except a list will print out documentation. */
  222. return get_documented_option_setting (options);
  223. else
  224. {
  225. SCM old_setting;
  226. SCM_ASSERT (scm_is_true (scm_list_p (args)), args, 1, s);
  227. old_setting = get_option_setting (options);
  228. change_option_setting (args, options, s, dry_run);
  229. return old_setting;
  230. }
  231. }
  232. void
  233. scm_init_opts (SCM (*func) (SCM), scm_t_option options[])
  234. {
  235. unsigned int i;
  236. for (i = 0; options[i].name; ++i)
  237. {
  238. SCM name = scm_from_utf8_symbol (options[i].name);
  239. options[i].name = (char *) SCM_UNPACK (name);
  240. }
  241. func (SCM_UNDEFINED);
  242. }
  243. void
  244. scm_init_options ()
  245. {
  246. #include "options.x"
  247. }