libexplain  1.4.D001
libexplain/explanation/assemble_common.c
Go to the documentation of this file.
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 : */