libexplain
1.4.D001
|
00001 /* 00002 * libexplain - a library of system-call-specific strerror replacements 00003 * Copyright (C) 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 it 00007 * under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 3 of the License, or (at your 00009 * option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, but WITHOUT 00012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00014 * 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/string.h> 00021 00022 #include <libexplain/explanation/assemble_common.h> 00023 #include <libexplain/gettext.h> 00024 00025 00026 static long 00027 count_non_format_bytes(const char *msgid) 00028 { 00029 const char *msgstr; 00030 long result; 00031 00032 msgstr = explain_gettext(msgid); 00033 result = 0; 00034 for (;;) 00035 { 00036 unsigned char c; 00037 00038 c = *msgstr++; 00039 if (c == '\0') 00040 return result; 00041 if (c != '%') 00042 { 00043 ++result; 00044 continue; 00045 } 00046 00047 for (;;) 00048 { 00049 c = *msgstr++; 00050 switch (c) 00051 { 00052 case '\0': /* oops, end of string */ 00053 return result; 00054 00055 case ' ': /* positive prefix request */ 00056 case '#': /* alternate format request */ 00057 case '$': /* poisition introducer */ 00058 case '\'': /* SUSv2 only: use thousands separator */ 00059 continue; 00060 00061 case '%': /* literal */ 00062 ++result; 00063 break; 00064 00065 case '+': /* positive prefix request */ 00066 case '-': /* left aligned */ 00067 case '.': /* precision intriducer */ 00068 case '0': /* position, width or precision */ 00069 case '1': /* position, width or precision */ 00070 case '2': /* position, width or precision */ 00071 case '3': /* position, width or precision */ 00072 case '4': /* position, width or precision */ 00073 case '5': /* position, width or precision */ 00074 case '6': /* position, width or precision */ 00075 case '7': /* position, width or precision */ 00076 case '8': /* position, width or precision */ 00077 case '9': /* position, width or precision */ 00078 continue; 00079 00080 case 'A': /* floating point format, hex notation */ 00081 case 'a': /* floating point format, hex notation */ 00082 case 'C': /* SUSv2 only: synonly for %lc */ 00083 case 'c': /* character format */ 00084 case 'd': /* integer format */ 00085 case 'E': /* floating point format */ 00086 case 'e': /* floating point format */ 00087 case 'F': /* floating point format */ 00088 case 'f': /* floating point format */ 00089 case 'G': /* floating point format */ 00090 case 'g': /* floating point format */ 00091 break; 00092 00093 case 'h': /* short size */ 00094 case 'I': /* use alternate locale digits */ 00095 continue; 00096 00097 case 'i': /* integer format */ 00098 break; 00099 00100 case 'j': /* uintmax_t size */ 00101 case 'L': /* long size */ 00102 case 'l': /* long size */ 00103 continue; 00104 00105 case 'm': /* strerror(errno). Glibc only */ 00106 case 'n': /* num chars so far */ 00107 case 'o': /* octal format */ 00108 case 'p': /* pointer format */ 00109 break; 00110 00111 case 'q': 00112 continue; 00113 00114 case 's': /* string format */ 00115 break; 00116 00117 case 't': /* ptrdiff_t size */ 00118 continue; 00119 00120 case 'X': /* hex format */ 00121 case 'x': /* hex format */ 00122 break; 00123 00124 case 'z': /* size_t size */ 00125 continue; 00126 00127 default: 00128 break; 00129 } 00130 break; 00131 } 00132 } 00133 return result; 00134 } 00135 00136 00137 void 00138 explain_explanation_assemble_common(explain_explanation_t *exp, 00139 const char *strerror_text, explain_string_buffer_t *result) 00140 { 00141 long overhead; 00142 long prob_len; 00143 long exp_len; 00144 int err_len; 00145 00146 if (exp->errnum == 0) 00147 { 00148 explain_string_buffer_printf_gettext 00149 ( 00150 result, 00151 /* 00152 * xgettext: this message is issued when a system call 00153 * succeeds, when there was, in fact, no error. 00154 * 00155 * %1$s => the C text of the system call and its arguments 00156 */ 00157 i18n("%s: success"), 00158 exp->system_call 00159 ); 00160 explain_string_buffer_puts(result, exp->footnotes); 00161 return; 00162 } 00163 00164 /* 00165 * If there is no extended explanation available, 00166 * use a shortened form of the message. 00167 */ 00168 prob_len = exp->system_call_sb.position; 00169 err_len = strlen(strerror_text); 00170 if (exp->explanation_sb.position == 0) 00171 { 00172 /* 00173 * NOTE: this string MUST be exactly the same as the one used, 00174 * below, to glue the explanation parts together. 00175 */ 00176 use_short_form: 00177 overhead = count_non_format_bytes("%s failed, %s"); 00178 00179 if (prob_len + err_len + overhead > (long)result->maximum) 00180 { 00181 long new_len = (long)result->maximum - (prob_len + overhead); 00182 explain_string_buffer_truncate(&exp->explanation_sb, new_len); 00183 } 00184 00185 explain_string_buffer_printf_gettext 00186 ( 00187 result, 00188 /* 00189 * xgettext: this message is printed when there is no 00190 * extended explanation available. In english, the stuff 00191 * to the left of "because" is a statement of the problem, 00192 * including function name and function argument names and 00193 * values. 00194 * 00195 * Usually a longer message, including a prose explanation, is 00196 * used. This shorter message is used when there is no extended 00197 * explanation, or when the user-supplied message buffer is too 00198 * small. 00199 * 00200 * Depending on the grammar of the natural language being 00201 * translated to, you may need to rearrange these two pieces 00202 * using positional arguments. 00203 * 00204 * %1$s => the C text of the system call and its arguments 00205 * e.g. "open(pathname = "foo/bar", flags = O_RDONLY)" 00206 * %2$s => the strerror text, plus the name and number of 00207 * the errno.h constant 00208 * e.g. "No such file or directory (2, ENOENT)" 00209 */ 00210 i18n("%s failed, %s"), 00211 exp->system_call, 00212 strerror_text 00213 ); 00214 explain_string_buffer_puts(result, exp->footnotes); 00215 return; 00216 } 00217 00218 /* 00219 * NOTE: this string MUST be exactly the same as the one used 00220 * below, to glue the explanation parts together. 00221 */ 00222 overhead = count_non_format_bytes("%s failed, %s because %s"); 00223 00224 exp_len = exp->explanation_sb.position; 00225 if (prob_len + err_len + exp_len + overhead > (long)result->maximum) 00226 { 00227 long new_exp_len = 00228 (long)result->maximum - (prob_len + err_len + overhead); 00229 if (new_exp_len <= 0) 00230 { 00231 goto use_short_form; 00232 } 00233 explain_string_buffer_truncate(&exp->explanation_sb, new_exp_len); 00234 } 00235 00236 explain_string_buffer_printf_gettext 00237 ( 00238 result, 00239 /* 00240 * xgettext: This message is used to join the problem to the 00241 * explanation. In english, the stuff to the left of "because" 00242 * is a statement of the problem, including function name and 00243 * function argument names and values; and the stuff to the 00244 * right of "because" is the explanation text. 00245 * 00246 * Depending on the grammar of the language being translated to, 00247 * you may need to rearrange these two pieces using positional 00248 * arguments. 00249 * 00250 * %1$s => the C text of the system call and its arguments 00251 * e.g. "open(pathname = 'foo/bar', flags = O_RDONLY)" 00252 * %2$s => the strerror text, plus the name and number of 00253 * the errno.h constant 00254 * e.g. "No such file or directory (2, ENOENT)" 00255 * %3$s => the explanation text 00256 * e.g. "there is no 'bar' file in the pathname 00257 * 'foo'; directory" 00258 * 00259 * For example: 00260 * 00261 * msgid "%s failed, %s because %s" 00262 * msgstr "%3$s caused %2$s to be returned by %1$s" 00263 * 00264 * msgid "%s failed, %s because %s" 00265 * msgstr "a %2$s error, due to %3$s, was reported by %1$s" 00266 * 00267 * This has a follow-on effect for how the explanations themselves 00268 * are translated, to ensure that sensible sentences result. In 00269 * particular, the explanation portion should only ever be one 00270 * sentence, so that a clause (e.g. above) may be appended. 00271 */ 00272 i18n("%s failed, %s because %s"), 00273 exp->system_call, 00274 strerror_text, 00275 exp->explanation 00276 ); 00277 explain_string_buffer_puts(result, exp->footnotes); 00278 } 00279 00280 00281 /* vim: set ts=8 sw=4 et : */