1/* YCP and Smalltalk format strings. 2 Copyright (C) 2001-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 "xerror.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 42struct spec 43{ 44 unsigned int directives; 45 unsigned int arg_count; 46 bool args_used[9]; 47}; 48 49 50static void * 51format_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 97static void 98format_free (void *descr) 99{ 100 struct spec *spec = (struct spec *) descr; 101 102 free (spec); 103} 104 105static int 106format_get_number_of_directives (void *descr) 107{ 108 struct spec *spec = (struct spec *) descr; 109 110 return spec->directives; 111} 112 113static bool 114format_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 144struct formatstring_parser formatstring_ycp = 145{ 146 format_parse, 147 format_free, 148 format_get_number_of_directives, 149 format_check 150}; 151 152 153struct formatstring_parser formatstring_smalltalk = 154{ 155 format_parse, 156 format_free, 157 format_get_number_of_directives, 158 format_check 159}; 160 161 162#ifdef TEST 163 164/* Test program: Print the argument list specification returned by 165 format_parse for strings read from standard input. */ 166 167#include <stdio.h> 168#include "getline.h" 169 170static void 171format_print (void *descr) 172{ 173 struct spec *spec = (struct spec *) descr; 174 unsigned int i; 175 176 if (spec == NULL) 177 { 178 printf ("INVALID"); 179 return; 180 } 181 182 printf ("("); 183 for (i = 0; i < spec->arg_count; i++) 184 { 185 if (i > 0) 186 printf (" "); 187 if (spec->args_used[i]) 188 printf ("*"); 189 else 190 printf ("_"); 191 } 192 printf (")"); 193} 194 195int 196main () 197{ 198 for (;;) 199 { 200 char *line = NULL; 201 size_t line_size = 0; 202 int line_len; 203 char *invalid_reason; 204 void *descr; 205 206 line_len = getline (&line, &line_size, stdin); 207 if (line_len < 0) 208 break; 209 if (line_len > 0 && line[line_len - 1] == '\n') 210 line[--line_len] = '\0'; 211 212 invalid_reason = NULL; 213 descr = format_parse (line, false, &invalid_reason); 214 215 format_print (descr); 216 printf ("\n"); 217 if (descr == NULL) 218 printf ("%s\n", invalid_reason); 219 220 free (invalid_reason); 221 free (line); 222 } 223 224 return 0; 225} 226 227/* 228 * For Emacs M-x compile 229 * Local Variables: 230 * 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" 231 * End: 232 */ 233 234#endif /* TEST */ 235