libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 2009, 2010, 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 * Loosely based on code from [e]glibc marked 00021 * Copyright (C) 1991-2001, 2006, 2007 Free Software Foundation, Inc. 00022 * The GNU C Library is free software; you can redistribute it and/or 00023 * modify it under the terms of the GNU Lesser General Public 00024 * License as published by the Free Software Foundation; either 00025 * version 2.1 of the License, or (at your option) any later version. 00026 */ 00027 00028 #include <libexplain/ac/errno.h> 00029 #include <libexplain/ac/stdio.h> 00030 #include <libexplain/ac/stdlib.h> 00031 #include <libexplain/ac/string.h> 00032 #include <libexplain/ac/sys/stat.h> 00033 00034 #include <libexplain/path_search.h> 00035 #include <libexplain/program_name.h> 00036 #include <libexplain/buffer/eexist.h> 00037 #include <libexplain/buffer/software_error.h> 00038 00039 00040 static int 00041 direxists(const char *pathname) 00042 { 00043 struct stat st; 00044 00045 return 00046 ( 00047 pathname 00048 && 00049 pathname[0] 00050 && 00051 stat(pathname, &st) >= 0 00052 && 00053 S_ISDIR(st.st_mode) 00054 ); 00055 } 00056 00057 00058 int 00059 explain_path_search(char *pathname, size_t pathname_size, const char *dir, 00060 const char *prefix, int try_tmpdir) 00061 { 00062 const char *where; 00063 size_t where_size; 00064 size_t prefix_size; 00065 00066 where = NULL; 00067 if (try_tmpdir) 00068 { 00069 const char *d; 00070 00071 /* 00072 * Strangley, [e]glibc tries TMPDIR before the directory in 00073 * out argument list. But the comments in the [e]glibc code 00074 * indicate that it is the other way around. Huh?!? 00075 */ 00076 d = getenv("TMPDIR"); 00077 if (direxists(d)) 00078 where = d; 00079 } 00080 if (where == NULL && direxists(dir)) 00081 where = dir; 00082 if (where == NULL && direxists(P_tmpdir)) 00083 where = P_tmpdir; 00084 if (where == NULL) 00085 { 00086 const char *d; 00087 00088 d = "/tmp"; 00089 if (direxists(d)) 00090 where = d; 00091 } 00092 if (where == NULL) 00093 { 00094 errno = ENOENT; 00095 return -1; 00096 } 00097 00098 where_size = strlen(where); 00099 /* remove trailing slashes */ 00100 while (where_size > 1 && where[where_size - 1] == '/') 00101 --where_size; 00102 00103 /* 00104 * Make sure the prefix is sensable. 00105 */ 00106 if (!prefix || !prefix[0]) 00107 prefix = explain_program_name_get(); 00108 if (!prefix || !prefix[0]) 00109 prefix = "file"; 00110 prefix_size = strlen(prefix); 00111 if (prefix_size > 5) 00112 { 00113 /* 00114 * This is the same logic that [e]glibc does. 00115 * 00116 * Only use up to the first 5 characters of prefix, to ensure 00117 * that file name length is <= 11 characters. But why? FAT 00118 * only has 8, V7 Unix had 14, and modern ones have about 255. 00119 * So why 11? 00120 * 00121 * Woudn't it be better to use pathconf(PC_NAMEMAX)? 00122 */ 00123 prefix_size = 5; 00124 } 00125 00126 /* 00127 * Check we have room for "${where}/${prefix}XXXXXX\0" 00128 */ 00129 if (pathname_size < where_size + prefix_size + 8) 00130 { 00131 errno = EINVAL; 00132 return -1; 00133 } 00134 00135 /* 00136 * Build the path. 00137 */ 00138 snprintf 00139 ( 00140 pathname, 00141 pathname_size, 00142 "%.*s/%.*sXXXXXX", 00143 (int)where_size, 00144 where, 00145 (int)prefix_size, 00146 prefix 00147 ); 00148 00149 /* 00150 * Report success. 00151 */ 00152 return 0; 00153 } 00154 00155 00156 static void 00157 paths_push(const char **paths, size_t *paths_count, const char *path) 00158 { 00159 size_t j; 00160 00161 if (!path) 00162 return; 00163 if (!*path) 00164 return; 00165 /* 00166 * Note: we do not check that the path actually is a directory, 00167 * this is for the error message, where we are complaining that we 00168 * couldn't find a directory. 00169 */ 00170 for (j = 0; j < *paths_count; ++j) 00171 if (strcmp(paths[j], path) == 0) 00172 return; 00173 paths[*paths_count] = path; 00174 ++*paths_count; 00175 } 00176 00177 00178 int 00179 explain_path_search_explanation(explain_string_buffer_t *sb, int errnum, 00180 const char *dir, int try_tmpdir) 00181 { 00182 switch (errnum) 00183 { 00184 case ENOENT: 00185 { 00186 const char *paths[4]; 00187 size_t paths_count; 00188 size_t j; 00189 char paths_text[1000]; 00190 explain_string_buffer_t paths_text_sb; 00191 00192 /* 00193 * Build a text string describing the paths searched to 00194 * find a temporary directory. 00195 */ 00196 paths_count = 0; 00197 if (try_tmpdir) 00198 paths_push(paths, &paths_count, getenv("TMPDIR")); 00199 paths_push(paths, &paths_count, dir); 00200 paths_push(paths, &paths_count, P_tmpdir); 00201 paths_push(paths, &paths_count, "/tmp"); 00202 explain_string_buffer_init(&paths_text_sb, paths_text, 00203 sizeof(paths_text_sb)); 00204 explain_string_buffer_printf(&paths_text_sb, "%d: ", 00205 (int)paths_count); 00206 for (j = 0; j < paths_count; ++j) 00207 { 00208 if (j) 00209 explain_string_buffer_puts(&paths_text_sb, ", "); 00210 explain_string_buffer_puts_quoted(&paths_text_sb, paths[j]); 00211 } 00212 00213 explain_string_buffer_printf_gettext 00214 ( 00215 sb, 00216 /* 00217 * xgettext: This error message is issued when we 00218 * are unable to locate a temporary directory in 00219 * which to create temporary files (ENOENT). 00220 * 00221 * %1$s => The list of directories tried, already quoted. 00222 */ 00223 i18n("the system was unable to find a temporary directory, " 00224 "tried %s"), 00225 paths_text 00226 ); 00227 }; 00228 return 0; 00229 00230 case EINVAL: 00231 /* FIXME: i18n */ 00232 explain_string_buffer_puts 00233 ( 00234 sb, 00235 "the buffer allocated for template file name was too small" 00236 ); 00237 explain_buffer_software_error(sb); 00238 return 0; 00239 00240 case EEXIST: 00241 { 00242 const char *where; 00243 00244 where = 0; 00245 if (try_tmpdir) 00246 { 00247 const char *d; 00248 00249 d = getenv("TMPDIR"); 00250 if (direxists(d)) 00251 where = d; 00252 } 00253 if (!where && direxists(dir)) 00254 where = dir; 00255 if (!where && direxists(P_tmpdir)) 00256 where = P_tmpdir; 00257 if (!where) 00258 where = "/tmp"; 00259 00260 explain_buffer_eexist_tempname(sb, where); 00261 } 00262 return 0; 00263 00264 default: 00265 return -1; 00266 } 00267 } 00268 00269 00270 /* vim: set ts=8 sw=4 et : */