argmatch.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /* argmatch.c -- find a match for a string in an array
  2. Copyright (C) 1990, 1998-1999, 2001-2007, 2009-2013 Free Software
  3. Foundation, Inc.
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  14. /* Written by David MacKenzie <djm@ai.mit.edu>
  15. Modified by Akim Demaille <demaille@inf.enst.fr> */
  16. #include <config.h>
  17. /* Specification. */
  18. #include "argmatch.h"
  19. #include <stdbool.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include "gettext.h"
  24. #define _(msgid) gettext (msgid)
  25. #include "error.h"
  26. #include "quotearg.h"
  27. #include "quote.h"
  28. #if USE_UNLOCKED_IO
  29. # include "unlocked-io.h"
  30. #endif
  31. /* When reporting an invalid argument, show nonprinting characters
  32. by using the quoting style ARGMATCH_QUOTING_STYLE. Do not use
  33. literal_quoting_style. */
  34. #ifndef ARGMATCH_QUOTING_STYLE
  35. # define ARGMATCH_QUOTING_STYLE locale_quoting_style
  36. #endif
  37. /* Non failing version of argmatch call this function after failing. */
  38. #ifndef ARGMATCH_DIE
  39. # include "exitfail.h"
  40. # define ARGMATCH_DIE exit (exit_failure)
  41. #endif
  42. #ifdef ARGMATCH_DIE_DECL
  43. ARGMATCH_DIE_DECL;
  44. #endif
  45. static void
  46. __argmatch_die (void)
  47. {
  48. ARGMATCH_DIE;
  49. }
  50. /* Used by XARGMATCH and XARGCASEMATCH. See description in argmatch.h.
  51. Default to __argmatch_die, but allow caller to change this at run-time. */
  52. argmatch_exit_fn argmatch_die = __argmatch_die;
  53. /* If ARG is an unambiguous match for an element of the
  54. NULL-terminated array ARGLIST, return the index in ARGLIST
  55. of the matched element, else -1 if it does not match any element
  56. or -2 if it is ambiguous (is a prefix of more than one element).
  57. If VALLIST is none null, use it to resolve ambiguities limited to
  58. synonyms, i.e., for
  59. "yes", "yop" -> 0
  60. "no", "nope" -> 1
  61. "y" is a valid argument, for 0, and "n" for 1. */
  62. ptrdiff_t
  63. argmatch (const char *arg, const char *const *arglist,
  64. const char *vallist, size_t valsize)
  65. {
  66. size_t i; /* Temporary index in ARGLIST. */
  67. size_t arglen; /* Length of ARG. */
  68. ptrdiff_t matchind = -1; /* Index of first nonexact match. */
  69. bool ambiguous = false; /* If true, multiple nonexact match(es). */
  70. arglen = strlen (arg);
  71. /* Test all elements for either exact match or abbreviated matches. */
  72. for (i = 0; arglist[i]; i++)
  73. {
  74. if (!strncmp (arglist[i], arg, arglen))
  75. {
  76. if (strlen (arglist[i]) == arglen)
  77. /* Exact match found. */
  78. return i;
  79. else if (matchind == -1)
  80. /* First nonexact match found. */
  81. matchind = i;
  82. else
  83. {
  84. /* Second nonexact match found. */
  85. if (vallist == NULL
  86. || memcmp (vallist + valsize * matchind,
  87. vallist + valsize * i, valsize))
  88. {
  89. /* There is a real ambiguity, or we could not
  90. disambiguate. */
  91. ambiguous = true;
  92. }
  93. }
  94. }
  95. }
  96. if (ambiguous)
  97. return -2;
  98. else
  99. return matchind;
  100. }
  101. /* Error reporting for argmatch.
  102. CONTEXT is a description of the type of entity that was being matched.
  103. VALUE is the invalid value that was given.
  104. PROBLEM is the return value from argmatch. */
  105. void
  106. argmatch_invalid (const char *context, const char *value, ptrdiff_t problem)
  107. {
  108. char const *format = (problem == -1
  109. ? _("invalid argument %s for %s")
  110. : _("ambiguous argument %s for %s"));
  111. error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value),
  112. quote_n (1, context));
  113. }
  114. /* List the valid arguments for argmatch.
  115. ARGLIST is the same as in argmatch.
  116. VALLIST is a pointer to an array of values.
  117. VALSIZE is the size of the elements of VALLIST */
  118. void
  119. argmatch_valid (const char *const *arglist,
  120. const char *vallist, size_t valsize)
  121. {
  122. size_t i;
  123. const char *last_val = NULL;
  124. /* We try to put synonyms on the same line. The assumption is that
  125. synonyms follow each other */
  126. fputs (_("Valid arguments are:"), stderr);
  127. for (i = 0; arglist[i]; i++)
  128. if ((i == 0)
  129. || memcmp (last_val, vallist + valsize * i, valsize))
  130. {
  131. fprintf (stderr, "\n - %s", quote (arglist[i]));
  132. last_val = vallist + valsize * i;
  133. }
  134. else
  135. {
  136. fprintf (stderr, ", %s", quote (arglist[i]));
  137. }
  138. putc ('\n', stderr);
  139. }
  140. /* Never failing versions of the previous functions.
  141. CONTEXT is the context for which argmatch is called (e.g.,
  142. "--version-control", or "$VERSION_CONTROL" etc.). Upon failure,
  143. calls the (supposed never to return) function EXIT_FN. */
  144. ptrdiff_t
  145. __xargmatch_internal (const char *context,
  146. const char *arg, const char *const *arglist,
  147. const char *vallist, size_t valsize,
  148. argmatch_exit_fn exit_fn)
  149. {
  150. ptrdiff_t res = argmatch (arg, arglist, vallist, valsize);
  151. if (res >= 0)
  152. /* Success. */
  153. return res;
  154. /* We failed. Explain why. */
  155. argmatch_invalid (context, arg, res);
  156. argmatch_valid (arglist, vallist, valsize);
  157. (*exit_fn) ();
  158. return -1; /* To please the compilers. */
  159. }
  160. /* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
  161. return the first corresponding argument in ARGLIST */
  162. const char *
  163. argmatch_to_argument (const char *value,
  164. const char *const *arglist,
  165. const char *vallist, size_t valsize)
  166. {
  167. size_t i;
  168. for (i = 0; arglist[i]; i++)
  169. if (!memcmp (value, vallist + valsize * i, valsize))
  170. return arglist[i];
  171. return NULL;
  172. }
  173. #ifdef TEST
  174. /*
  175. * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
  176. */
  177. char *program_name;
  178. /* When to make backup files. */
  179. enum backup_type
  180. {
  181. /* Never make backups. */
  182. no_backups,
  183. /* Make simple backups of every file. */
  184. simple_backups,
  185. /* Make numbered backups of files that already have numbered backups,
  186. and simple backups of the others. */
  187. numbered_existing_backups,
  188. /* Make numbered backups of every file. */
  189. numbered_backups
  190. };
  191. /* Two tables describing arguments (keys) and their corresponding
  192. values */
  193. static const char *const backup_args[] =
  194. {
  195. "no", "none", "off",
  196. "simple", "never",
  197. "existing", "nil",
  198. "numbered", "t",
  199. 0
  200. };
  201. static const enum backup_type backup_vals[] =
  202. {
  203. no_backups, no_backups, no_backups,
  204. simple_backups, simple_backups,
  205. numbered_existing_backups, numbered_existing_backups,
  206. numbered_backups, numbered_backups
  207. };
  208. int
  209. main (int argc, const char *const *argv)
  210. {
  211. const char *cp;
  212. enum backup_type backup_type = no_backups;
  213. program_name = (char *) argv[0];
  214. if (argc > 2)
  215. {
  216. fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
  217. exit (1);
  218. }
  219. if ((cp = getenv ("VERSION_CONTROL")))
  220. backup_type = XARGMATCH ("$VERSION_CONTROL", cp,
  221. backup_args, backup_vals);
  222. if (argc == 2)
  223. backup_type = XARGMATCH (program_name, argv[1],
  224. backup_args, backup_vals);
  225. printf ("The version control is '%s'\n",
  226. ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
  227. return 0;
  228. }
  229. #endif