1 /* YCP and Smalltalk format strings. 2 Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc. 3 Written by Bruno Haible <haible@clisp.cons.org>, 2001. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2, or (at your option) 8 any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software Foundation, 17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 18 19 #ifdef HAVE_CONFIG_H 20 # include <config.h> 21 #endif 22 23 #include <stdbool.h> 24 #include <stdlib.h> 25 26 #include "format.h" 27 #include "c-ctype.h" 28 #include "xalloc.h" 29 #include "xvasprintf.h" 30 #include "format-invalid.h" 31 #include "gettext.h" 32 33 #define _(str) gettext (str) 34 35 /* YCP sformat strings are described in libycp documentation YCP-builtins.html. 36 A directive starts with '%' and is followed by '%' or a nonzero digit ('1' 37 to '9'). 38 GNU Smalltalk format strings are described in the CharArray documentation, 39 methods 'bindWith:' and 'bindWithArguments:'. They have the same syntax. 40 */ 41 42 struct spec 43 { 44 unsigned int directives; 45 unsigned int arg_count; 46 bool args_used[9]; 47 }; 48 49 50 static void * 51 format_parse (const char *format, bool translated, char **invalid_reason) 52 { 53 struct spec spec; 54 struct spec *result; 55 56 spec.directives = 0; 57 spec.arg_count = 0; 58 59 for (; *format != '\0';) 60 if (*format++ == '%') 61 { 62 /* A directive. */ 63 spec.directives++; 64 65 if (*format == '%') 66 format++; 67 else if (*format >= '1' && *format <= '9') 68 { 69 unsigned int number = *format - '1'; 70 71 while (spec.arg_count <= number) 72 spec.args_used[spec.arg_count++] = false; 73 spec.args_used[number] = true; 74 75 format++; 76 } 77 else 78 { 79 *invalid_reason = 80 (*format == '\0' 81 ? INVALID_UNTERMINATED_DIRECTIVE () 82 : (c_isprint (*format) 83 ? xasprintf (_("In the directive number %u, the character '%c' is not a digit between 1 and 9."), spec.directives, *format) 84 : xasprintf (_("The character that terminates the directive number %u is not a digit between 1 and 9."), spec.directives))); 85 goto bad_format; 86 } 87 } 88 89 result = (struct spec *) xmalloc (sizeof (struct spec)); 90 *result = spec; 91 return result; 92 93 bad_format: 94 return NULL; 95 } 96 97 static void 98 format_free (void *descr) 99 { 100 struct spec *spec = (struct spec *) descr; 101 102 free (spec); 103 } 104 105 static int 106 format_get_number_of_directives (void *descr) 107 { 108 struct spec *spec = (struct spec *) descr; 109 110 return spec->directives; 111 } 112 113 static bool 114 format_check (void *msgid_descr, void *msgstr_descr, bool equality, 115 formatstring_error_logger_t error_logger, 116 const char *pretty_msgstr) 117 { 118 struct spec *spec1 = (struct spec *) msgid_descr; 119 struct spec *spec2 = (struct spec *) msgstr_descr; 120 bool err = false; 121 unsigned int i; 122 123 for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++) 124 { 125 bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]); 126 bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]); 127 128 if (equality ? (arg_used1 != arg_used2) : (!arg_used1 && arg_used2)) 129 { 130 if (error_logger) 131 error_logger (arg_used1 132 ? _("a format specification for argument %u doesn't exist in '%s'") 133 : _("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"), 134 i + 1, pretty_msgstr); 135 err = true; 136 break; 137 } 138 } 139 140 return err; 141 } 142 143 144 struct formatstring_parser formatstring_ycp = 145 { 146 format_parse, 147 format_free, 148 format_get_number_of_directives, 149 NULL, 150 format_check 151 }; 152 153 154 struct formatstring_parser formatstring_smalltalk = 155 { 156 format_parse, 157 format_free, 158 format_get_number_of_directives, 159 NULL, 160 format_check 161 }; 162 163 164 #ifdef TEST 165 166 /* Test program: Print the argument list specification returned by 167 format_parse for strings read from standard input. */ 168 169 #include <stdio.h> 170 #include "getline.h" 171 172 static void 173 format_print (void *descr) 174 { 175 struct spec *spec = (struct spec *) descr; 176 unsigned int i; 177 178 if (spec == NULL) 179 { 180 printf ("INVALID"); 181 return; 182 } 183 184 printf ("("); 185 for (i = 0; i < spec->arg_count; i++) 186 { 187 if (i > 0) 188 printf (" "); 189 if (spec->args_used[i]) 190 printf ("*"); 191 else 192 printf ("_"); 193 } 194 printf (")"); 195 } 196 197 int 198 main () 199 { 200 for (;;) 201 { 202 char *line = NULL; 203 size_t line_size = 0; 204 int line_len; 205 char *invalid_reason; 206 void *descr; 207 208 line_len = getline (&line, &line_size, stdin); 209 if (line_len < 0) 210 break; 211 if (line_len > 0 && line[line_len - 1] == '\n') 212 line[--line_len] = '\0'; 213 214 invalid_reason = NULL; 215 descr = format_parse (line, false, &invalid_reason); 216 217 format_print (descr); 218 printf ("\n"); 219 if (descr == NULL) 220 printf ("%s\n", invalid_reason); 221 222 free (invalid_reason); 223 free (line); 224 } 225 226 return 0; 227 } 228 229 /* 230 * For Emacs M-x compile 231 * Local Variables: 232 * compile-command: "/bin/sh ../libtool --mode=link gcc -o a.out -static -O -g -Wall -I.. -I../lib -I../intl -DHAVE_CONFIG_H -DTEST format-ycp.c ../lib/libgettextlib.la" 233 * End: 234 */ 235 236 #endif /* TEST */ 237