1/* Qt format strings. 2 Copyright (C) 2003-2004 Free Software Foundation, Inc. 3 Written by Bruno Haible <bruno@clisp.org>, 2003. 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 "xalloc.h" 28#include "xerror.h" 29#include "gettext.h" 30 31#define _(str) gettext (str) 32 33/* Qt format strings are processed by QString::arg and are documented in 34 qt-3.0.5/doc/html/qstring.html. 35 A directive starts with '%' and is followed by a digit ('0' to '9'). 36 Each %n must occur only once in the given string. 37 The first .arg() invocation replaces the %n with the lowest numbered n, 38 the next .arg() invocation then replaces the %n with the second-lowest 39 numbered n, and so on. 40 (This is inherently buggy because a '%' in the first replacement confuses 41 the second .arg() invocation.) 42 Although %0 is supported, usually %1 denotes the first argument, %2 the 43 second argument etc. */ 44 45struct spec 46{ 47 unsigned int directives; 48 unsigned int arg_count; 49 bool args_used[10]; 50}; 51 52 53static void * 54format_parse (const char *format, bool translated, char **invalid_reason) 55{ 56 struct spec spec; 57 struct spec *result; 58 59 spec.directives = 0; 60 spec.arg_count = 0; 61 62 for (; *format != '\0';) 63 if (*format++ == '%') 64 if (*format >= '0' && *format <= '9') 65 { 66 /* A directive. */ 67 unsigned int number; 68 69 spec.directives++; 70 71 number = *format - '0'; 72 73 while (spec.arg_count <= number) 74 spec.args_used[spec.arg_count++] = false; 75 if (spec.args_used[number]) 76 { 77 *invalid_reason = 78 xasprintf (_("Multiple references to %%%c."), *format); 79 goto bad_format; 80 } 81 spec.args_used[number] = true; 82 83 format++; 84 } 85 86 result = (struct spec *) xmalloc (sizeof (struct spec)); 87 *result = spec; 88 return result; 89 90 bad_format: 91 return NULL; 92} 93 94static void 95format_free (void *descr) 96{ 97 struct spec *spec = (struct spec *) descr; 98 99 free (spec); 100} 101 102static int 103format_get_number_of_directives (void *descr) 104{ 105 struct spec *spec = (struct spec *) descr; 106 107 return spec->directives; 108} 109 110static bool 111format_check (void *msgid_descr, void *msgstr_descr, bool equality, 112 formatstring_error_logger_t error_logger, 113 const char *pretty_msgstr) 114{ 115 struct spec *spec1 = (struct spec *) msgid_descr; 116 struct spec *spec2 = (struct spec *) msgstr_descr; 117 bool err = false; 118 unsigned int i; 119 120 for (i = 0; i < spec1->arg_count || i < spec2->arg_count; i++) 121 { 122 bool arg_used1 = (i < spec1->arg_count && spec1->args_used[i]); 123 bool arg_used2 = (i < spec2->arg_count && spec2->args_used[i]); 124 125 /* The translator cannot omit a %n from the msgstr because that would 126 yield a "Argument missing" warning at runtime. */ 127 if (arg_used1 != arg_used2) 128 { 129 if (error_logger) 130 error_logger (arg_used1 131 ? _("a format specification for argument %u doesn't exist in '%s'") 132 : _("a format specification for argument %u, as in '%s', doesn't exist in 'msgid'"), 133 i, pretty_msgstr); 134 err = true; 135 break; 136 } 137 } 138 139 return err; 140} 141 142 143struct formatstring_parser formatstring_qt = 144{ 145 format_parse, 146 format_free, 147 format_get_number_of_directives, 148 format_check 149}; 150 151 152#ifdef TEST 153 154/* Test program: Print the argument list specification returned by 155 format_parse for strings read from standard input. */ 156 157#include <stdio.h> 158#include "getline.h" 159 160static void 161format_print (void *descr) 162{ 163 struct spec *spec = (struct spec *) descr; 164 unsigned int i; 165 166 if (spec == NULL) 167 { 168 printf ("INVALID"); 169 return; 170 } 171 172 printf ("("); 173 for (i = 0; i < spec->arg_count; i++) 174 { 175 if (i > 0) 176 printf (" "); 177 if (spec->args_used[i]) 178 printf ("*"); 179 else 180 printf ("_"); 181 } 182 printf (")"); 183} 184 185int 186main () 187{ 188 for (;;) 189 { 190 char *line = NULL; 191 size_t line_size = 0; 192 int line_len; 193 char *invalid_reason; 194 void *descr; 195 196 line_len = getline (&line, &line_size, stdin); 197 if (line_len < 0) 198 break; 199 if (line_len > 0 && line[line_len - 1] == '\n') 200 line[--line_len] = '\0'; 201 202 invalid_reason = NULL; 203 descr = format_parse (line, false, &invalid_reason); 204 205 format_print (descr); 206 printf ("\n"); 207 if (descr == NULL) 208 printf ("%s\n", invalid_reason); 209 210 free (invalid_reason); 211 free (line); 212 } 213 214 return 0; 215} 216 217/* 218 * For Emacs M-x compile 219 * Local Variables: 220 * 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-qt.c ../lib/libgettextlib.la" 221 * End: 222 */ 223 224#endif /* TEST */ 225