libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 2013 Peter Miller 00004 * 00005 * This program is free software; you can redistribute it and/or modify it 00006 * under the terms of the GNU Lesser General Public License as published by 00007 * the Free Software Foundation; either version 3 of the License, or (at 00008 * your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, but 00011 * WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 00013 * General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 #include <libexplain/ac/assert.h> 00020 #include <libexplain/ac/ctype.h> 00021 #include <libexplain/ac/errno.h> 00022 #include <libexplain/ac/stdio.h> 00023 #include <libexplain/ac/stdlib.h> 00024 #include <libexplain/ac/string.h> 00025 00026 #include <libexplain/buffer/einval.h> 00027 #include <libexplain/buffer/errno/generic.h> 00028 #include <libexplain/buffer/errno/iconv_open.h> 00029 #include <libexplain/buffer/pathname.h> 00030 #include <libexplain/buffer/is_the_null_pointer.h> 00031 #include <libexplain/explanation.h> 00032 #include <libexplain/fstrcmp.h> 00033 00034 00035 static void 00036 explain_buffer_errno_iconv_open_system_call(explain_string_buffer_t *sb, int 00037 errnum, const char *tocode, const char *fromcode) 00038 { 00039 (void)errnum; 00040 explain_string_buffer_puts(sb, "iconv_open(tocode = "); 00041 explain_buffer_pathname(sb, tocode); 00042 explain_string_buffer_puts(sb, ", fromcode = "); 00043 explain_buffer_pathname(sb, fromcode); 00044 explain_string_buffer_putc(sb, ')'); 00045 } 00046 00047 00048 static char **known_names; 00049 static size_t known_names_size; 00050 static size_t known_names_allocated; 00051 00052 00053 static char * 00054 xstrdup(const char *text) 00055 { 00056 char *result; 00057 size_t text_size; 00058 00059 text_size = strlen(text); 00060 result = malloc(text_size + 1); 00061 if (!result) 00062 return (char *)text; 00063 memcpy(result, text, text_size + 1); 00064 return result; 00065 } 00066 00067 00068 static void 00069 one_more_name(char *name) 00070 { 00071 if (known_names_size >= known_names_allocated) 00072 { 00073 char **new_known_names; 00074 size_t new_known_names_allocated; 00075 size_t j; 00076 00077 new_known_names_allocated = 00078 known_names_allocated ? known_names_allocated * 2 : 19; 00079 while (known_names_size > new_known_names_allocated) 00080 new_known_names_allocated *= 2; 00081 assert(known_names_size < new_known_names_allocated); 00082 new_known_names = 00083 malloc(new_known_names_allocated * sizeof(char *)); 00084 for (j = 0; j < known_names_size; ++j) 00085 new_known_names[j] = known_names[j]; 00086 if (known_names) 00087 free(known_names); 00088 known_names_allocated = new_known_names_allocated; 00089 known_names = new_known_names; 00090 assert(known_names_size < known_names_allocated); 00091 } 00092 known_names[known_names_size++] = xstrdup(name); 00093 } 00094 00095 00096 static void 00097 upcase_insitu(char *name) 00098 { 00099 char *ip; 00100 char *op; 00101 00102 /* 00103 * eglibc::iconv/iconv_open.c 00104 * Normalizes the name, by remove all characters beside alpha-numeric, 00105 * '_', '-', '/', '.', and ':'. 00106 */ 00107 ip = name; 00108 op = name; 00109 for(;;) 00110 { 00111 unsigned char c = *ip++; 00112 if (!c) 00113 { 00114 *op = '\0'; 00115 return; 00116 } 00117 if (islower(c)) 00118 *op++ = toupper(c); 00119 else if (isalnum(c)) 00120 *op++ = c; 00121 else if (strchr("_-/.:", c)) 00122 *op++ = c; 00123 else 00124 { 00125 /* discard */ 00126 } 00127 } 00128 } 00129 00130 00131 static char * 00132 strdup_upcase(const char *text) 00133 { 00134 const char *ip; 00135 char *op; 00136 char *result; 00137 00138 result = malloc(strlen(text) + 1); 00139 if (!result) 00140 return NULL; 00141 ip = text; 00142 op = result; 00143 for (;;) 00144 { 00145 unsigned char c = *ip++; 00146 if (!c) 00147 { 00148 *op = '\0'; 00149 return result; 00150 } 00151 if (islower(c)) 00152 *op++ = toupper(c); 00153 else if (isalnum(c)) 00154 *op++ = c; 00155 else if (strchr("_-/.:", c)) 00156 *op++ = c; 00157 else 00158 { 00159 /* discard */ 00160 } 00161 } 00162 return result; 00163 } 00164 00165 00166 static void 00167 get_list_of_known_names(void) 00168 { 00169 FILE *fp; 00170 00171 /* 00172 * The only way to do this is via the iconv() command, which has 00173 * special eglibc support 00174 */ 00175 fp = popen("iconv --list", "r"); 00176 if (!fp) 00177 return; 00178 for (;;) 00179 { 00180 char buffer[200]; 00181 char *slash; 00182 00183 if (!fgets(buffer, sizeof(buffer), fp)) 00184 break; 00185 slash = strpbrk(buffer, "/\r\n"); 00186 if (slash) 00187 *slash = '\0'; 00188 upcase_insitu(buffer); 00189 one_more_name(buffer); 00190 } 00191 pclose(fp); 00192 } 00193 00194 00195 static int 00196 is_known_name(const char *name) 00197 { 00198 size_t j; 00199 00200 for (j = 0; j < known_names_size; ++j) 00201 { 00202 char *known_name; 00203 00204 known_name = known_names[j]; 00205 #ifdef HAV_STRVERSCMP 00206 if (0 == strverscmp(known_name, name)) 00207 return 1; 00208 #else 00209 if (0 == strcmp(known_name, name)) 00210 return 1; 00211 #endif 00212 } 00213 return 0; 00214 } 00215 00216 00217 static char * 00218 known_names_fuzzy(const char *name) 00219 { 00220 char *best_name; 00221 double best_weight; 00222 size_t j; 00223 char *name2; 00224 00225 name2 = strdup_upcase(name); 00226 if (!name2) 00227 return NULL; 00228 get_list_of_known_names(); 00229 best_name = NULL; 00230 best_weight = 0.6; 00231 for (j = 0; j < known_names_size; ++j) 00232 { 00233 char *known_name; 00234 double w; 00235 00236 known_name = known_names[j]; 00237 w = explain_fstrcmp(name2, known_name); 00238 if (w > best_weight) 00239 { 00240 best_name = known_name; 00241 best_weight = w; 00242 } 00243 } 00244 free(name2); 00245 return best_name; 00246 } 00247 00248 00249 static int 00250 known_names_check(explain_string_buffer_t *sb, const char *locale, 00251 const char *locale_caption) 00252 { 00253 char *locale_fuzzy; 00254 00255 if (!locale) 00256 { 00257 explain_buffer_is_the_null_pointer(sb, locale_caption); 00258 return 1; 00259 } 00260 00261 get_list_of_known_names(); 00262 if (is_known_name(locale)) 00263 { 00264 /* no error here */ 00265 return 0; 00266 } 00267 00268 explain_string_buffer_printf 00269 ( 00270 sb, 00271 /* FIXME: i18n */ 00272 "the %s argument is not a known locale name", 00273 locale_caption 00274 ); 00275 00276 locale_fuzzy = known_names_fuzzy(locale); 00277 if (locale_fuzzy) 00278 { 00279 explain_string_buffer_puts(sb->footnotes, "; "); 00280 explain_string_buffer_printf 00281 ( 00282 sb->footnotes, 00283 /* FIXME: i18n */ 00284 "did you mean the \"%s\" locale instead?", 00285 locale_fuzzy 00286 ); 00287 } 00288 return 1; 00289 } 00290 00291 00292 void 00293 explain_buffer_errno_iconv_open_explanation(explain_string_buffer_t *sb, int 00294 errnum, const char *syscall_name, const char *tocode, const char *fromcode) 00295 { 00296 (void)tocode; 00297 (void)fromcode; 00298 switch (errnum) 00299 { 00300 case EINVAL: 00301 if (known_names_check(sb, tocode, "tocode")) 00302 break; 00303 if (known_names_check(sb, fromcode, "fromcode")) 00304 break; 00305 00306 explain_string_buffer_puts 00307 ( 00308 sb, 00309 /* FIXME: i18n */ 00310 "The conversion from fromcode to tocode is not supported by " 00311 "the implementation" 00312 ); 00313 break; 00314 00315 default: 00316 explain_buffer_errno_generic(sb, errnum, syscall_name); 00317 break; 00318 } 00319 } 00320 00321 00322 void 00323 explain_buffer_errno_iconv_open(explain_string_buffer_t *sb, int errnum, const 00324 char *tocode, const char *fromcode) 00325 { 00326 explain_explanation_t exp; 00327 00328 explain_explanation_init(&exp, errnum); 00329 explain_buffer_errno_iconv_open_system_call(&exp.system_call_sb, errnum, 00330 tocode, fromcode); 00331 explain_buffer_errno_iconv_open_explanation(&exp.explanation_sb, errnum, 00332 "iconv_open", tocode, fromcode); 00333 explain_explanation_assemble(&exp, sb); 00334 } 00335 00336 00337 /* vim: set ts=8 sw=4 et : */