libexplain  1.4.D001
libexplain/option.c
Go to the documentation of this file.
00001 /*
00002  * libexplain - Explain errno values returned by libc functions
00003  * Copyright (C) 2008-2013 Peter Miller
00004  * Written by Peter Miller <pmiller@opensource.org.au>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as
00008  * published by the Free Software Foundation; either version 3 of the
00009  * License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include <libexplain/ac/errno.h>
00021 #include <libexplain/ac/ctype.h>
00022 #include <libexplain/ac/stdlib.h>
00023 #include <libexplain/ac/string.h>
00024 
00025 #include <libexplain/fstrcmp.h>
00026 #include <libexplain/option.h>
00027 #include <libexplain/output.h>
00028 #include <libexplain/parse_bits.h>
00029 #include <libexplain/program_name.h>
00030 #include <libexplain/sizeof.h>
00031 #include <libexplain/string_buffer.h>
00032 #include <libexplain/wrap_and_print.h>
00033 
00034 
00035 typedef enum option_level_t option_level_t;
00036 enum option_level_t
00037 {
00038     option_level_default,
00039     option_level_something_or_die,
00040     option_level_environment_variable,
00041     option_level_client,
00042 };
00043 
00044 typedef int option_value_t;
00045 
00046 typedef enum option_type_t option_type_t;
00047 enum option_type_t
00048 {
00049     option_type_bool,
00050     option_type_int
00051 };
00052 
00053 typedef struct option_t option_t;
00054 struct option_t
00055 {
00056     option_level_t level;
00057     option_value_t value;
00058     option_type_t type;
00059 };
00060 
00061 typedef char value_t;
00062 
00063 static int initialised;
00064 static option_t debug =
00065     { option_level_default, 0, option_type_bool };
00066 static option_t numeric_errno =
00067     { option_level_default, 1, option_type_bool };
00068 static option_t dialect_specific =
00069     { option_level_default, 1, option_type_bool };
00070 static option_t assemble_program_name =
00071     { option_level_default, 1, option_type_bool };
00072 static option_t symbolic_mode_bits =
00073     { option_level_default, 0, option_type_bool };
00074 static option_t internal_strerror =
00075     { option_level_default, 0, option_type_bool };
00076 static option_t hanging_indent =
00077     { option_level_default, 0, option_type_int };
00078 static option_t extra_device_info =
00079     { option_level_default, 1, option_type_bool };
00080 
00081 typedef struct table_t table_t;
00082 struct table_t
00083 {
00084     const char      *name;
00085     option_t        *location;
00086 };
00087 
00088 static const table_t table[] =
00089 {
00090     { "assemble-program-name", &assemble_program_name },
00091     { "debug", &debug },
00092     { "dialect-specific", &dialect_specific },
00093     { "hanging-indent", &hanging_indent },
00094     { "internal-strerror", &internal_strerror },
00095     { "numeric-errno", &numeric_errno },
00096     { "program-name", &assemble_program_name },
00097     { "symbolic-mode-bits", &symbolic_mode_bits },
00098     { "extra-device-info", &extra_device_info },
00099 };
00100 
00101 
00102 static const explain_parse_bits_table_t bool_table[] =
00103 {
00104     { "yes", 1 },
00105     { "no", 0 },
00106     { "true", 1 },
00107     { "false", 0 },
00108 };
00109 
00110 
00111 static void
00112 process(char *name, option_level_t level)
00113 {
00114     const table_t   *tp;
00115     value_t         value;
00116     char            *eq;
00117     int             invert;
00118 
00119     if (!*name)
00120         return;
00121     invert = 0;
00122     value = 1;
00123     if (name[0] == 'n' && name[1] == 'o' && name[2] == '-')
00124     {
00125         invert = 1;
00126         name += 3;
00127     }
00128     eq = strchr(name, '=');
00129     if (eq)
00130     {
00131         int n = 0;
00132         explain_parse_bits(eq + 1, bool_table, SIZEOF(bool_table), &n);
00133         value = n;
00134         while (eq > name && isspace((unsigned char)eq[-1]))
00135             --eq;
00136         *eq = '\0';
00137     }
00138     if (invert)
00139         value = !value;
00140     for (tp = table; tp < ENDOF(table); ++tp)
00141     {
00142         if (0 == strcmp(tp->name, name))
00143         {
00144             if (level >= tp->location->level)
00145             {
00146                 tp->location->level = level;
00147                 tp->location->value = value;
00148             }
00149             return;
00150         }
00151     }
00152     if (debug.value)
00153     {
00154         const table_t   *best_tp;
00155         double          best_weight;
00156         explain_string_buffer_t buf;
00157         char            message[200];
00158 
00159         best_tp = 0;
00160         best_weight = 0.6;
00161         for (tp = table; tp < ENDOF(table); ++tp)
00162         {
00163             double          weight;
00164 
00165             weight = explain_fstrcmp(tp->name, name);
00166             if (best_weight < weight)
00167             {
00168                 best_tp = tp;
00169                 best_weight = weight;
00170             }
00171         }
00172 
00173         explain_string_buffer_init(&buf, message, sizeof(message));
00174         explain_string_buffer_puts(&buf, "libexplain: Warning: option ");
00175         explain_string_buffer_puts_quoted(&buf, name);
00176         explain_string_buffer_puts(&buf, " unknown");
00177         if (best_tp)
00178         {
00179             if (level >= best_tp->location->level)
00180             {
00181                 best_tp->location->level = level;
00182                 best_tp->location->value = value;
00183             }
00184 
00185             explain_string_buffer_puts(&buf, ", assuming you meant ");
00186             explain_string_buffer_puts_quoted(&buf, best_tp->name);
00187             explain_string_buffer_puts(&buf, " instead");
00188         }
00189         explain_wrap_and_print(stderr, message);
00190     }
00191 }
00192 
00193 
00194 static void
00195 initialise(void)
00196 {
00197     const char      *cp;
00198     int             err;
00199 
00200     err = errno;
00201     initialised = 1;
00202     cp = getenv("EXPLAIN_OPTIONS");
00203     if (!cp)
00204     {
00205         /* for historical compatibility */
00206         cp = getenv("LIBEXPLAIN_OPTIONS");
00207     }
00208     if (!cp)
00209         cp = "";
00210     for (;;)
00211     {
00212         char            name[100];
00213         char            *np;
00214         unsigned char   c;
00215 
00216         c = *cp++;
00217         if (!c)
00218             break;
00219         if (isspace(c))
00220             continue;
00221         np = name;
00222         for (;;)
00223         {
00224             if (np < ENDOF(name) -1)
00225             {
00226                 if (isupper(c))
00227                     c = tolower(c);
00228                 *np++ = c;
00229             }
00230             c = *cp;
00231             if (!c)
00232                 break;
00233             ++cp;
00234             if (c == ',')
00235                 break;
00236         }
00237         /* remove trailing white space */
00238         while (np > name && isspace((unsigned char)np[-1]))
00239             --np;
00240         *np = '\0';
00241 
00242         process(name, option_level_environment_variable);
00243     }
00244     errno = err;
00245 }
00246 
00247 
00248 int
00249 explain_option_debug(void)
00250 {
00251     if (!initialised)
00252         initialise();
00253     return debug.value;
00254 }
00255 
00256 
00257 int
00258 explain_option_numeric_errno(void)
00259 {
00260     if (!initialised)
00261         initialise();
00262     return numeric_errno.value;
00263 }
00264 
00265 
00266 int
00267 explain_option_dialect_specific(void)
00268 {
00269     if (!initialised)
00270         initialise();
00271     return dialect_specific.value;
00272 }
00273 
00274 
00275 int
00276 explain_option_assemble_program_name(void)
00277 {
00278     if (!initialised)
00279         initialise();
00280     return assemble_program_name.value;
00281 }
00282 
00283 
00284 void
00285 explain_program_name_assemble(int yesno)
00286 {
00287     /*
00288      * This is the public interface, it has highest
00289      * precedence.  For the internal interface, see the
00290      * #explain_program_name_assemble_internal function.
00291      */
00292     if (!initialised)
00293         initialise();
00294     if (assemble_program_name.level <= option_level_client)
00295     {
00296         assemble_program_name.level = option_level_client;
00297         assemble_program_name.value = !!yesno;
00298     }
00299 }
00300 
00301 
00302 #if 0
00303 void
00304 explain_program_name_assemble_internal(int yesno)
00305 {
00306     /*
00307      * This is the private interface.
00308      */
00309     if (!initialised)
00310         initialise();
00311     if (assemble_program_name.level <= option_level_something_or_die)
00312     {
00313         assemble_program_name.level = option_level_something_or_die;
00314         assemble_program_name.value = !!yesno;
00315     }
00316 }
00317 #endif
00318 
00319 
00320 int
00321 explain_option_symbolic_mode_bits(void)
00322 {
00323     if (!initialised)
00324         initialise();
00325     return symbolic_mode_bits.value;
00326 }
00327 
00328 
00329 int
00330 explain_option_internal_strerror(void)
00331 {
00332     if (!initialised)
00333         initialise();
00334     return internal_strerror.value;
00335 }
00336 
00337 
00338 int
00339 explain_option_hanging_indent(int width)
00340 {
00341     int             max;
00342     int             n;
00343 
00344     if (!initialised)
00345         initialise();
00346     if (width <= 0 || width >= 65536)
00347         width = 80;
00348     max = (width + 5) / 10;
00349     n = hanging_indent.value;
00350     if (n <= 0)
00351         return 0;
00352     if (n > max)
00353         return max;
00354     return n;
00355 }
00356 
00357 
00358 void
00359 explain_option_hanging_indent_set(int columns)
00360 {
00361     if (!initialised)
00362         initialise();
00363     if (hanging_indent.level <= option_level_client)
00364     {
00365         if (columns < 0)
00366             columns = 0;
00367         hanging_indent.value = columns;
00368         hanging_indent.level = option_level_client;
00369     }
00370 }
00371 
00372 
00373 int
00374 explain_option_extra_device_info(void)
00375 {
00376     if (!initialised)
00377         initialise();
00378     return extra_device_info.value;
00379 }
00380 
00381 
00382 /* vim: set ts=8 sw=4 et : */