1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * go-format.c :
4 *
5 * Copyright (C) 2003-2005 Jody Goldberg (jody@gnome.org)
6 * Copyright (C) 2005-2014 Morten Welinder (terra@gnome.org)
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 */
23
24 /*
25 * NOTE - NOTE - NOTE
26 *
27 * This file includes itself in order to provide both "double" and "long
28 * double" versions of most functions.
29 *
30 * Most source lines thus correspond to two functions, gdb is having
31 * a hard time sorting things out. Feel with it.
32 */
33
34 #include <goffice/goffice-config.h>
35 #include <gsf/gsf-msole-utils.h>
36 #include <gsf/gsf-opendoc-utils.h>
37 #include "go-format.h"
38 #include "go-locale.h"
39 #include "go-font.h"
40 #include "go-color.h"
41 #include "datetime.h"
42 #include "go-glib-extras.h"
43 #include "go-pango-extras.h"
44 #include <goffice/math/go-math.h>
45 #include <glib/gi18n-lib.h>
46
47 #include <time.h>
48 #include <math.h>
49 #include <string.h>
50 #include <stdio.h>
51 #include <errno.h>
52 #include <stdlib.h>
53
54 #undef DEBUG_GENERAL
55
56 /**
57 * GOFormatFamily:
58 * @GO_FORMAT_UNKNOWN: unknown ,should not occur.
59 * @GO_FORMAT_GENERAL: general.
60 * @GO_FORMAT_NUMBER: number.
61 * @GO_FORMAT_CURRENCY: currency.
62 * @GO_FORMAT_ACCOUNTING: accounting.
63 * @GO_FORMAT_DATE: date.
64 * @GO_FORMAT_TIME: tipe.
65 * @GO_FORMAT_PERCENTAGE: percentage.
66 * @GO_FORMAT_FRACTION: fraction.
67 * @GO_FORMAT_SCIENTIFIC: scientific.
68 * @GO_FORMAT_TEXT: text.
69 * @GO_FORMAT_SPECIAL: custom.
70 **/
71
72 /**
73 * GOFormatMagic:
74 * @GO_FORMAT_MAGIC_NONE: none.
75 * @GO_FORMAT_MAGIC_LONG_DATE: long date (Official).
76 * @GO_FORMAT_MAGIC_MEDIUM_DATE: medium date.
77 * @GO_FORMAT_MAGIC_SHORT_DATE: short date.
78 * @GO_FORMAT_MAGIC_SHORT_DATETIME: short date with time.
79 * @GO_FORMAT_MAGIC_LONG_TIME: long time (Official).
80 * @GO_FORMAT_MAGIC_MEDIUM_TIME: medium time.
81 * @GO_FORMAT_MAGIC_SHORT_TIME: short time.
82 **/
83
84 /**
85 * GOFormatNumberError:
86 * @GO_FORMAT_NUMBER_OK: no error.
87 * @GO_FORMAT_NUMBER_INVALID_FORMAT: invalid format.
88 * @GO_FORMAT_NUMBER_DATE_ERROR: date error.
89 **/
90
91 /**
92 * GOFormatCurrency:
93 * @symbol: currency symbol.
94 * @description: description.
95 * @precedes: whether the symbol precedes the amount.
96 * @has_space: whether to add a space between amount and symbol.
97 **/
98
99 /**
100 * GOFormatDetails:
101 * @family: #GOFormatFamily.
102 * @magic: #GOFormatMagic.
103 * @min_digits: minimum digits number.
104 * @num_decimals: decimals number.
105 * @thousands_sep: thousands separator.
106 * @negative_red: display negative number using red ink.
107 * @negative_paren: uses parenthersis around negative numbers.
108 * @currency: #GOFormatCurrency.
109 * @force_quoted: force quotes use.
110 * @exponent_step: steps between allowed exponents in scientific notation.
111 * @exponent_digits: digits number in exponent.
112 * @exponent_sign_forced: whether the sign in the exponent is always shown.
113 * @use_markup: whether to use a markup.
114 * @simplify_mantissa: simplify the mantissa.
115 * @append_SI: append an SI unit.
116 * @appended_SI_unit: the SI unit to append.
117 * @scale: scale.
118 * @automatic_denominator: use an automatic denominator for fractions.
119 * @split_fraction: split the fraction.
120 * @pi_scale: use multiples of pi for fractions, e.g. 1/2*pi.
121 * @numerator_min_digits: minimum digits number for the numerator.
122 * @denominator_min_digits: minimum digits number for the denominator.
123 * @denominator_max_digits: minimum digits number for the denominator.
124 * @denominator: fixed denominator.
125 **/
126
127 #define OBSERVE_XL_CONDITION_LIMITS
128 #define OBSERVE_XL_EXPONENT_1
129 #define ALLOW_NEGATIVE_TIMES
130 #define MAX_DECIMALS 100
131
132 /* Define ALLOW_DENOM_REMOVAL to remove /1s. This is not XL compatible.*/
133 #undef ALLOW_DENOM_REMOVAL
134
135 /* Define ALLOW_NO_SIGN_AFTER_E to permit formats such as '00E00' and '00E +00' */
136 #define ALLOW_NO_SIGN_AFTER_E
137
138 #define ALLOW_EE_MARKUP
139 #define ALLOW_SI_APPEND
140 #define ALLOW_PI_SLASH
141
142 /* ------------------------------------------------------------------------- */
143
144 #ifndef DOUBLE
145
146 #define DEFINE_COMMON
147 #define DOUBLE double
148 #define SUFFIX(_n) _n
149 #define PREFIX(_n) DBL_ ## _n
150 #define FORMAT_e "e"
151 #define FORMAT_f "f"
152 #define FORMAT_E "E"
153 #define FORMAT_G "G"
154 #define STRTO go_strtod
155
156 #ifdef GOFFICE_WITH_LONG_DOUBLE
157 /*
158 * We need two versions. Include ourself in order to get regular
159 * definition first.
160 */
161 #include "go-format.c"
162
163 /* Now change definitions of macros for the long double version. */
164 #undef DEFINE_COMMON
165 #undef DOUBLE
166 #undef SUFFIX
167 #undef PREFIX
168 #undef FORMAT_e
169 #undef FORMAT_f
170 #undef FORMAT_E
171 #undef FORMAT_G
172 #undef STRTO
173
174 #ifdef HAVE_SUNMATH_H
175 #include <sunmath.h>
176 #endif
177 #define DOUBLE long double
178 #define SUFFIX(_n) _n ## l
179 #define PREFIX(_n) LDBL_ ## _n
180 #define FORMAT_e "Le"
181 #define FORMAT_f "Lf"
182 #define FORMAT_E "LE"
183 #define FORMAT_G "LG"
184 #define STRTO go_strtold
185 #endif
186
187 #endif
188
189 /* ------------------------------------------------------------------------- */
190
191 #undef DEBUG_PROGRAMS
192 #undef DEBUG_REF_COUNT
193
194 /***************************************************************************/
195
196 #ifdef DEFINE_COMMON
197
198 typedef enum {
199 OP_END = 0,
200 OP_CHAR, /* unichar */
201 OP_CHAR_INVISIBLE, /* unichar */
202 OP_CHAR_REPEAT,
203 OP_STRING, /* 0-termined */
204 OP_FILL, /* unichar */
205 OP_LOCALE, /* locale langstr */
206 OP_NUMERAL_SHAPE, /* flags shape */
207 /* ------------------------------- */
208 OP_DATE_ROUND, /* decimals seen_elapsed */
209 OP_DATE_SPLIT,
210 OP_DATE_YEAR,
211 OP_DATE_YEAR_2,
212 OP_DATE_YEAR_THAI,
213 OP_DATE_YEAR_THAI_2,
214 OP_DATE_MONTH,
215 OP_DATE_MONTH_2,
216 OP_DATE_MONTH_NAME,
217 OP_DATE_MONTH_NAME_1,
218 OP_DATE_MONTH_NAME_3,
219 OP_DATE_DAY,
220 OP_DATE_DAY_2,
221 OP_DATE_WEEKDAY,
222 OP_DATE_WEEKDAY_3,
223 /* ------------------------------- */
224 OP_TIME_SPLIT_24,
225 OP_TIME_SPLIT_12,
226 OP_TIME_SPLIT_ELAPSED_HOUR,
227 OP_TIME_SPLIT_ELAPSED_MINUTE,
228 OP_TIME_SPLIT_ELAPSED_SECOND,
229 OP_TIME_HOUR,
230 OP_TIME_HOUR_2,
231 OP_TIME_HOUR_N, /* n */
232 OP_TIME_AMPM,
233 OP_TIME_AP, /* [aA][pP] */
234 OP_TIME_MINUTE,
235 OP_TIME_MINUTE_2,
236 OP_TIME_MINUTE_N, /* n */
237 OP_TIME_SECOND,
238 OP_TIME_SECOND_2,
239 OP_TIME_SECOND_N, /* n */
240 OP_TIME_SECOND_DECIMAL_START,
241 OP_TIME_SECOND_DECIMAL_DIGIT,
242 /* ------------------------------- */
243 OP_NUM_SCALE, /* orders */
244 OP_NUM_ENABLE_THOUSANDS,
245 OP_NUM_DISABLE_THOUSANDS,
246 OP_NUM_PRINTF_E, /* prec wd */
247 OP_NUM_PRINTF_F, /* prec */
248 OP_NUM_SIGN,
249 OP_NUM_VAL_SIGN,
250 OP_NUM_MOVETO_ONES,
251 OP_NUM_MOVETO_DECIMALS,
252 OP_NUM_REST_WHOLE,
253 OP_NUM_APPEND_MODE,
254 OP_NUM_DECIMAL_POINT,
255 OP_NUM_DIGIT_1, /* [0?#] */
256 OP_NUM_DECIMAL_1, /* [0?#] */
257 OP_NUM_DIGIT_1_0, /* [0?#] */
258 OP_NUM_DENUM_DIGIT_Q,
259 OP_NUM_EXPONENT_SIGN, /* forced-p */
260 OP_NUM_EXPONENT_1,
261 OP_NUM_VAL_EXPONENT,
262 OP_NUM_STORE_POS,
263 #ifdef ALLOW_EE_MARKUP
264 OP_NUM_MARK_MANTISSA,
265 OP_NUM_SIMPLIFY_MANTISSA,
266 OP_NUM_SIMPLIFY_MARKUP_MANTISSA,
267 OP_MARKUP_SUPERSCRIPT_START,
268 OP_MARKUP_SUPERSCRIPT_END,
269 #endif
270 #ifdef ALLOW_SI_APPEND
271 OP_NUM_SIMPLIFY_MANTISSA_SI,
272 OP_NUM_REDUCE_EXPONENT_SI,
273 OP_NUM_SIMPLIFY_EXPONENT_SI,
274 OP_NUM_SI_EXPONENT,
275 #endif
276
277 OP_NUM_FRACTION, /* wholep explicitp (digits|denominator) */
278 OP_NUM_FRACTION_WHOLE,
279 OP_NUM_FRACTION_NOMINATOR,
280 OP_NUM_FRACTION_DENOMINATOR,
281 OP_NUM_FRACTION_BLANK,
282 OP_NUM_FRACTION_BLANK_WHOLE,
283 OP_NUM_FRACTION_ALIGN,
284 OP_NUM_FRACTION_SLASH,
285 OP_NUM_FRACTION_SIGN,
286 #ifdef ALLOW_DENOM_REMOVAL
287 OP_NUM_FRACTION_SIMPLIFY,
288 OP_NUM_FRACTION_SIMPLIFY_NUMERATOR,
289 #endif
290 #ifdef ALLOW_PI_SLASH
291 OP_NUM_FRACTION_BLANK_PI,
292 OP_NUM_FRACTION_SCALE_PI,
293 OP_NUM_FRACTION_SIMPLIFY_PI,
294 OP_NUM_FRACTION_SIMPLIFY_NUMERATOR_PI,
295 OP_NUM_FRACTION_PI_SUM_START,
296 #endif
297 OP_NUM_GENERAL_MARK,
298 OP_NUM_GENERAL_DO,
299 /* ------------------------------- */
300 OP_STR_APPEND_SVAL
301 } GOFormatOp;
302
303 typedef enum {
304 TOK_NULL = 0,
305 TOK_GENERAL = 256, /* General */
306
307 TOK_STRING, /* "xyz" */
308 TOK_CHAR, /* non-ascii */
309 TOK_ESCAPED_CHAR, /* \x */
310 TOK_INVISIBLE_CHAR, /* _x */
311 TOK_REPEATED_CHAR, /* *x */
312
313 TOK_AMPM5, /* am/pm */
314 TOK_AMPM3, /* a/p */
315
316 TOK_ELAPSED_H, /* [hhh...] */
317 TOK_ELAPSED_M, /* [mmm...] */
318 TOK_ELAPSED_S, /* [sss...] */
319
320 TOK_COLOR, /* [red] */
321 TOK_CONDITION, /* [>0] */
322 TOK_LOCALE, /* [$txt-F800] */
323
324 TOK_DECIMAL, /* Decimal sep */
325 TOK_THOUSAND, /* Thousand sep */
326
327 TOK_EXP, /* E */
328 #ifdef ALLOW_EE_MARKUP
329 TOK_EXP_MU, /* EE */
330 #endif
331 #ifdef ALLOW_SI_APPEND
332 TOK_EXP_SI, /* ESI */
333 #endif
334 #if defined(ALLOW_EE_MARKUP) && defined(ALLOW_SI_APPEND)
335 TOK_EXP_MU_SI, /* EESI */
336 #endif
337
338 TOK_ERROR
339 } GOFormatToken;
340
341 typedef enum {
342 TT_TERMINATES_SINGLE = 1,
343 TT_ERROR = 2,
344
345 TT_ALLOWED_IN_DATE = 0x10,
346 TT_STARTS_DATE = 0x20,
347
348 TT_ALLOWED_IN_NUMBER = 0x100,
349 TT_STARTS_NUMBER = 0x200,
350
351 TT_ALLOWED_IN_TEXT = 0x1000,
352 TT_STARTS_TEXT = 0x2000
353 } GOFormatTokenType;
354
355 typedef enum {
356 GO_FMT_INVALID,
357 GO_FMT_COND,
358 GO_FMT_NUMBER,
359 GO_FMT_EMPTY,
360 GO_FMT_TEXT,
361 GO_FMT_MARKUP
362 } GOFormatClass;
363
364 typedef enum {
365 GO_FMT_COND_NONE,
366 GO_FMT_COND_EQ,
367 GO_FMT_COND_NE,
368 GO_FMT_COND_LT,
369 GO_FMT_COND_LE,
370 GO_FMT_COND_GT,
371 GO_FMT_COND_GE,
372 GO_FMT_COND_TEXT,
373 GO_FMT_COND_NONTEXT
374 } GOFormatConditionOp;
375
376 typedef enum {
377 GO_FMT_SHAPE_SIGNS = 1,
378 /* GENERAL implies GO_FMT_POSITION_MARKERS whether this is set or not */
379 /* This is only useful for Chinese/Japanese/Korean numerals */
380 GO_FMT_POSITION_MARKERS = 2
381 } GOFormatShapeFlags;
382
383 typedef struct {
384 GOFormatConditionOp op;
385 unsigned implicit : 1;
386 unsigned true_inhibits_minus : 1;
387 unsigned false_inhibits_minus : 1;
388 /* Plain "double". It isn't worth it to have two types. */
389 double val;
390 GOFormat *fmt;
391 } GOFormatCondition;
392
393 typedef struct {
394 guint64 locale;
395 } GOFormatLocale;
396
397
398 struct _GOFormat {
399 unsigned int typ : 8;
400 unsigned int ref_count : 24;
401 GOColor color;
402 unsigned int has_fill : 7;
403 GOFormatMagic magic;
404 char *format;
405 union {
406 struct {
407 int n;
408 GOFormatCondition *conditions;
409 } cond;
410
411 struct {
412 guchar *program;
413 unsigned int E_format : 1;
414 unsigned int use_markup : 1;
415 unsigned int use_SI : 1;
416 unsigned int has_date : 1;
417 unsigned int date_ybm : 1; /* year, then month. */
418 unsigned int date_mbd : 1; /* month, then day. */
419 unsigned int date_dbm : 1; /* day, then month. */
420 unsigned int has_year : 1;
421 unsigned int has_month : 1;
422 unsigned int has_day : 1;
423 unsigned int has_time : 1;
424 unsigned int has_hour : 1;
425 unsigned int has_minute : 1;
426 unsigned int has_elapsed : 1;
427 unsigned int fraction : 1;
428 unsigned int scale_is_2 : 1;
429 unsigned int has_general : 1;
430 unsigned int is_general : 1;
431 } number;
432
433 struct {
434 guchar *program;
435 } text;
436
437 PangoAttrList *markup;
438 } u;
439 };
440
441 typedef struct {
442 const char *tstr;
443 int token;
444 GOFormatTokenType tt;
445 } GOFormatParseItem;
446
447 typedef struct {
448 /* Set by go_format_preparse: */
449 GArray *tokens;
450
451 GOFormatClass typ;
452 gboolean is_date;
453 gboolean is_number; /* has TT_STARTS_NUMBER token */
454
455 GOFormatCondition cond;
456 gboolean have_cond;
457
458 GOColor color;
459 int color_n;
460 gboolean color_named;
461 gboolean have_color;
462
463 GOFormatLocale locale;
464 gboolean have_locale;
465
466 gunichar fill_char;
467
468 int tno_slash;
469 int tno_E;
470
471 gboolean has_general, is_general;
472
473 /* Set by go_format_parse_number_new_1: */
474 int scale;
475
476 /* Set by go_format_parse_number_fraction: */
477 int force_zero_pos;
478 gboolean explicit_denom;
479 gboolean forced_exponent_sign;
480 } GOFormatParseState;
481
482 #define REPEAT_CHAR_MARKER 0
483 #define UNICODE_PI 0x1d70b /* mathematical small italic pi */
484 #define UNICODE_PI_number_of_bytes 4
485 #define UNICODE_THINSPACE 0x2009
486 #define UNICODE_TIMES 0x00D7
487 #define UNICODE_MINUS 0x2212
488 #define UNICODE_EURO 0x20ac
489 #define UNICODE_POUNDS1 0x00a3
490 #define UNICODE_POUNDS2 0x20a4
491 #define UNICODE_YEN 0x00a5
492 #define UNICODE_YEN_WIDE 0xffe5
493
494 #define UTF8_MINUS "\xe2\x88\x92"
495 #define UTF8_FULLWIDTH_MINUS "\357\274\215"
496 #define UTF8_FULLWIDTH_PLUS "\357\274\213"
497
498 /* Number of digits required for roundtrip of DOUBLE. */
499 #ifdef DEFINE_COMMON
500 static int go_format_roundtrip_digits;
501 #ifdef GOFFICE_WITH_LONG_DOUBLE
502 static int go_format_roundtrip_digitsl;
503 #endif
504 #endif
505
506 gboolean
go_format_allow_ee_markup(void)507 go_format_allow_ee_markup (void)
508 {
509 #ifdef ALLOW_EE_MARKUP
510 return TRUE;
511 #else
512 return FALSE;
513 #endif
514 }
515
516 gboolean
go_format_allow_si(void)517 go_format_allow_si (void)
518 {
519 #ifdef ALLOW_SI_APPEND
520 return TRUE;
521 #else
522 return FALSE;
523 #endif
524 }
525
526 gboolean
go_format_allow_pi_slash(void)527 go_format_allow_pi_slash (void)
528 {
529 #ifdef ALLOW_PI_SLASH
530 return TRUE;
531 #else
532 return FALSE;
533 #endif
534 }
535
536
537 GOFormatFamily
go_format_get_family(GOFormat const * fmt)538 go_format_get_family (GOFormat const *fmt)
539 {
540 g_return_val_if_fail (fmt != NULL, GO_FORMAT_UNKNOWN);
541
542 switch (fmt->typ) {
543 case GO_FMT_MARKUP:
544 return GO_FORMAT_MARKUP;
545 case GO_FMT_INVALID:
546 case GO_FMT_EMPTY:
547 return GO_FORMAT_UNKNOWN;
548 case GO_FMT_TEXT:
549 return GO_FORMAT_TEXT;
550 case GO_FMT_NUMBER:
551 if (fmt->u.number.is_general)
552 return GO_FORMAT_GENERAL;
553 if (fmt->u.number.has_date)
554 return GO_FORMAT_DATE;
555 if (fmt->u.number.has_time)
556 return GO_FORMAT_TIME;
557 if (fmt->u.number.fraction)
558 return GO_FORMAT_FRACTION;
559 if (fmt->u.number.E_format)
560 return GO_FORMAT_SCIENTIFIC;
561 if (fmt->u.number.scale_is_2)
562 return GO_FORMAT_PERCENTAGE;
563 return GO_FORMAT_NUMBER;
564 default:
565 case GO_FMT_COND: {
566 int i;
567 GOFormatFamily t0 = GO_FORMAT_UNKNOWN;
568
569 for (i = 0; i < fmt->u.cond.n; i++) {
570 const GOFormatCondition *ci =
571 fmt->u.cond.conditions + i;
572 if (i == 0)
573 t0 = go_format_get_family (ci->fmt);
574 if (ci->op == GO_FMT_COND_TEXT &&
575 i == fmt->u.cond.n - 1)
576 continue;
577 if (t0 != go_format_get_family (ci->fmt))
578 return GO_FORMAT_UNKNOWN;
579 }
580
581 return t0;
582 }
583 }
584 }
585
586 const PangoAttrList *
go_format_get_markup(GOFormat const * fmt)587 go_format_get_markup (GOFormat const *fmt)
588 {
589 g_return_val_if_fail (fmt != NULL, NULL);
590 g_return_val_if_fail (fmt->typ == GO_FMT_MARKUP, NULL);
591 return fmt->u.markup;
592 }
593
594 gboolean
go_format_is_simple(GOFormat const * fmt)595 go_format_is_simple (GOFormat const *fmt)
596 {
597 g_return_val_if_fail (fmt != NULL, TRUE);
598 return (fmt->typ != GO_FMT_COND);
599 }
600
601 /*
602 * This function returns the format string to be used for a magic format.
603 */
604 static char *
go_format_magic_fmt_str(GOFormatMagic m)605 go_format_magic_fmt_str (GOFormatMagic m)
606 {
607 switch (m) {
608 default:
609 return NULL;
610
611 case GO_FORMAT_MAGIC_LONG_DATE: {
612 /* xgettext: See http://www.gnumeric.org/date-time-formats.shtml */
613 const char *fmt = _("*Long Date Format");
614 if (fmt[0] && fmt[0] != '*')
615 return g_strdup (fmt);
616 return g_strdup ("dddd, mmmm dd, yyyy");
617 }
618
619 case GO_FORMAT_MAGIC_MEDIUM_DATE: {
620 /* xgettext: See http://www.gnumeric.org/date-time-formats.shtml */
621 const char *fmt = _("*Medium Date Format");
622 if (fmt[0] && fmt[0] != '*')
623 return g_strdup (fmt);
624 return g_strdup ("d-mmm-yyyy"); /* Excel has yy only. */
625 }
626
627 case GO_FORMAT_MAGIC_SHORT_DATE: {
628 /* xgettext: See http://www.gnumeric.org/date-time-formats.shtml */
629 const char *fmt = _("*Short Date Format");
630 if (fmt[0] && fmt[0] != '*')
631 return g_strdup (fmt);
632 switch (go_locale_month_before_day ()) {
633 case 0: return g_strdup ("d/m/yy");
634 default:
635 case 1: return g_strdup ("m/d/yy");
636 case 2: return g_strdup ("yy/m/d");
637 }
638 }
639
640 case GO_FORMAT_MAGIC_SHORT_DATETIME: {
641 /* xgettext: See http://www.gnumeric.org/date-time-formats.shtml */
642 const char *fmt = _("*Short Date/Time Format");
643 if (fmt[0] && fmt[0] != '*')
644 return g_strdup (fmt);
645 else {
646 char *d = go_format_magic_fmt_str (GO_FORMAT_MAGIC_SHORT_DATE);
647 char *t = go_format_magic_fmt_str (GO_FORMAT_MAGIC_SHORT_TIME);
648 char *res = g_strconcat (d, " ", t, NULL);
649 g_free (d);
650 g_free (t);
651 return res;
652 }
653 }
654
655 case GO_FORMAT_MAGIC_LONG_TIME: {
656 /* xgettext: See http://www.gnumeric.org/date-time-formats.shtml */
657 const char *fmt = _("*Long Time Format");
658 if (fmt[0] && fmt[0] != '*')
659 return g_strdup (fmt);
660 if (go_locale_24h ())
661 return g_strdup ("hh:mm:ss");
662 else
663 return g_strdup ("h:mm:ss AM/PM");
664 break;
665 }
666
667 case GO_FORMAT_MAGIC_MEDIUM_TIME: {
668 /* xgettext: See http://www.gnumeric.org/date-time-formats.shtml */
669 const char *fmt = _("*Medium Time Format");
670 if (fmt[0] && fmt[0] != '*')
671 return g_strdup (fmt);
672 if (go_locale_24h ())
673 return g_strdup ("hh:mm");
674 else
675 return g_strdup ("h:mm AM/PM");
676 break;
677 }
678
679 case GO_FORMAT_MAGIC_SHORT_TIME: {
680 /* xgettext: See http://www.gnumeric.org/date-time-formats.shtml */
681 const char *fmt = _("*Short Time Format");
682 if (fmt[0] && fmt[0] != '*')
683 return g_strdup (fmt);
684 return g_strdup ("hh:mm");
685 }
686 }
687 }
688
689
690 static GOFormat *default_percentage_fmt;
691 static GOFormat *default_money_fmt;
692 static GOFormat *default_accounting_fmt;
693 static GOFormat *default_date_fmt;
694 static GOFormat *default_time_fmt;
695 static GOFormat *default_date_time_fmt;
696 static GOFormat *default_general_fmt;
697 static GOFormat *default_empty_fmt;
698
699 /***************************************************************************/
700
701 /* WARNING : Global */
702 static GHashTable *style_format_hash = NULL;
703
704 /**
705 * go_format_foreach:
706 * @func: (scope call): function to execute for each known format
707 * @user_data: user data for @func
708 *
709 * Executes @func for each registered #GOFormat
710 **/
711 void
go_format_foreach(GHFunc func,gpointer user_data)712 go_format_foreach (GHFunc func, gpointer user_data)
713 {
714 if (style_format_hash != NULL)
715 g_hash_table_foreach (style_format_hash,
716 func, user_data);
717 }
718
719 /* used to generate formats when delocalizing so keep the leadings caps */
720 static struct {
721 char const *name;
722 GOColor go_color;
723 } const format_colors[8] = {
724 { N_("Black"), GO_COLOR_BLACK }, /* Color 1 */
725 { N_("White"), GO_COLOR_WHITE }, /* Color 2 */
726 { N_("Red"), GO_COLOR_RED }, /* Color 3 */
727 { N_("Green"), GO_COLOR_GREEN }, /* Color 4 */
728 { N_("Blue"), GO_COLOR_BLUE }, /* Color 5 */
729 { N_("Yellow"), GO_COLOR_YELLOW }, /* Color 6 */
730 { N_("Magenta"), GO_COLOR_VIOLET }, /* Color 7 */
731 { N_("Cyan"), GO_COLOR_CYAN } /* Color 8 */
732 };
733
734 static const GOColor
735 format_numbered_colors[56 + 1] = {
736 GO_COLOR_BLACK, /* Invalid */
737 GO_COLOR_BLACK,
738 GO_COLOR_WHITE,
739 GO_COLOR_RED,
740 GO_COLOR_GREEN,
741 GO_COLOR_BLUE,
742 GO_COLOR_YELLOW,
743 GO_COLOR_VIOLET,
744 GO_COLOR_CYAN,
745 GO_COLOR_FROM_RGB (0x80, 0x00, 0x00),
746 GO_COLOR_FROM_RGB (0x00, 0x80, 0x00),
747 GO_COLOR_FROM_RGB (0x00, 0x00, 0x80),
748 GO_COLOR_FROM_RGB (0x80, 0x80, 0x00),
749 GO_COLOR_FROM_RGB (0x80, 0x00, 0x80),
750 GO_COLOR_FROM_RGB (0x00, 0x80, 0x80),
751 GO_COLOR_FROM_RGB (0xC0, 0xC0, 0xC0),
752 GO_COLOR_FROM_RGB (0x80, 0x80, 0x80),
753 GO_COLOR_FROM_RGB (0x99, 0x99, 0xFF),
754 GO_COLOR_FROM_RGB (0x99, 0x33, 0x66),
755 GO_COLOR_FROM_RGB (0xFF, 0xFF, 0xCC),
756 GO_COLOR_FROM_RGB (0xCC, 0xFF, 0xFF),
757 GO_COLOR_FROM_RGB (0x66, 0x00, 0x66),
758 GO_COLOR_FROM_RGB (0xFF, 0x80, 0x80),
759 GO_COLOR_FROM_RGB (0x00, 0x66, 0xCC),
760 GO_COLOR_FROM_RGB (0xCC, 0xCC, 0xFF),
761 GO_COLOR_FROM_RGB (0x00, 0x00, 0x80),
762 GO_COLOR_FROM_RGB (0xFF, 0x00, 0xFF),
763 GO_COLOR_FROM_RGB (0xFF, 0xFF, 0x00),
764 GO_COLOR_FROM_RGB (0x00, 0xFF, 0xFF),
765 GO_COLOR_FROM_RGB (0x80, 0x00, 0x80),
766 GO_COLOR_FROM_RGB (0x80, 0x00, 0x00),
767 GO_COLOR_FROM_RGB (0x00, 0x80, 0x80),
768 GO_COLOR_FROM_RGB (0x00, 0x00, 0xFF),
769 GO_COLOR_FROM_RGB (0x00, 0xCC, 0xFF),
770 GO_COLOR_FROM_RGB (0xCC, 0xFF, 0xFF),
771 GO_COLOR_FROM_RGB (0xCC, 0xFF, 0xCC),
772 GO_COLOR_FROM_RGB (0xFF, 0xFF, 0x99),
773 GO_COLOR_FROM_RGB (0x99, 0xCC, 0xFF),
774 GO_COLOR_FROM_RGB (0xFF, 0x99, 0xCC),
775 GO_COLOR_FROM_RGB (0xCC, 0x99, 0xFF),
776 GO_COLOR_FROM_RGB (0xFF, 0xCC, 0x99),
777 GO_COLOR_FROM_RGB (0x33, 0x66, 0xFF),
778 GO_COLOR_FROM_RGB (0x33, 0xCC, 0xCC),
779 GO_COLOR_FROM_RGB (0x99, 0xCC, 0x00),
780 GO_COLOR_FROM_RGB (0xFF, 0xCC, 0x00),
781 GO_COLOR_FROM_RGB (0xFF, 0x99, 0x00),
782 GO_COLOR_FROM_RGB (0xFF, 0x66, 0x00),
783 GO_COLOR_FROM_RGB (0x66, 0x66, 0x99),
784 GO_COLOR_FROM_RGB (0x96, 0x96, 0x96),
785 GO_COLOR_FROM_RGB (0x00, 0x33, 0x66),
786 GO_COLOR_FROM_RGB (0x33, 0x99, 0x66),
787 GO_COLOR_FROM_RGB (0x00, 0x33, 0x00),
788 GO_COLOR_FROM_RGB (0x33, 0x33, 0x00),
789 GO_COLOR_FROM_RGB (0x99, 0x33, 0x00),
790 GO_COLOR_FROM_RGB (0x99, 0x33, 0x66),
791 GO_COLOR_FROM_RGB (0x33, 0x33, 0x99),
792 GO_COLOR_FROM_RGB (0x33, 0x33, 0x33)
793 };
794
795 GOColor
go_format_palette_color_of_index(int i)796 go_format_palette_color_of_index (int i)
797 {
798 g_return_val_if_fail (i >= 1, 0);
799
800 return (i < (int)G_N_ELEMENTS (format_numbered_colors))
801 ? format_numbered_colors[i]
802 : 0;
803 }
804
805 char *
go_format_palette_name_of_index(int i)806 go_format_palette_name_of_index (int i)
807 {
808 g_return_val_if_fail (i >= 1, NULL);
809 g_return_val_if_fail (i < (int)G_N_ELEMENTS (format_numbered_colors), NULL);
810
811 if (i <= 8)
812 return g_strdup (format_colors[i - 1].name);
813 else
814 return g_strdup_printf ("Color%d", i);
815 }
816
817
818 static int
color_dist(GOColor c1,GOColor c2)819 color_dist (GOColor c1, GOColor c2)
820 {
821 /* Simple 2-norm in rgb space. */
822 int dr = (int)GO_COLOR_UINT_R(c1) - (int)GO_COLOR_UINT_R(c2);
823 int dg = (int)GO_COLOR_UINT_G(c1) - (int)GO_COLOR_UINT_G(c2);
824 int db = (int)GO_COLOR_UINT_B(c1) - (int)GO_COLOR_UINT_B(c2);
825
826 return dr * dr + dg * dg + db * db;
827 }
828
829 /**
830 * go_format_palette_index_from_color:
831 * @c: color
832 *
833 * Returns: the index of the color closest to the argument color in some
834 * sense.
835 */
836 int
go_format_palette_index_from_color(GOColor c)837 go_format_palette_index_from_color (GOColor c)
838 {
839 int mindist = G_MAXINT;
840 unsigned ui;
841 int res = -1;
842
843 for (ui = 1; ui < G_N_ELEMENTS (format_numbered_colors); ui++) {
844 GOColor c2 = format_numbered_colors[ui];
845 int d = color_dist (c, c2);
846 if (d < mindist) {
847 mindist = d;
848 res = ui;
849 if (d == 0)
850 break;
851 }
852 }
853
854 return res;
855 }
856
857 /*
858 * go_format_parse_color :
859 * @str:
860 * @color:
861 * @n:
862 * @named:
863 *
864 * Return: TRUE, if ok. Then @color will be filled in, and @n will be
865 * a number 0-7 for standard colors.
866 * Returns FALSE otherwise and @color will be zeroed.
867 **/
868 static gboolean
go_format_parse_color(char const * str,GOColor * color,int * n,gboolean * named,gboolean is_localized)869 go_format_parse_color (char const *str, GOColor *color,
870 int *n, gboolean *named, gboolean is_localized)
871 {
872 char const *close;
873 unsigned int ui;
874 const char *color_txt = N_("color");
875 gsize len;
876
877 *color = 0;
878
879 if (*str++ != '[')
880 return FALSE;
881
882 close = strchr (str, ']');
883 if (!close)
884 return FALSE;
885
886 for (ui = 0; ui < G_N_ELEMENTS (format_colors); ui++) {
887 const char *name = format_colors[ui].name;
888 if (is_localized)
889 name = _(name);
890 if (g_ascii_strncasecmp (str, name, strlen (name)) == 0) {
891 *color = format_colors[ui].go_color;
892 if (n)
893 *n = ui;
894 if (named)
895 *named = TRUE;
896 return TRUE;
897 }
898 }
899
900 if (is_localized)
901 color_txt = _(color_txt);
902 len = strlen (color_txt);
903 if (g_ascii_strncasecmp (str, color_txt, len) == 0) {
904 char *end;
905 guint64 ull = g_ascii_strtoull (str + len, &end, 10);
906 if (end == str || errno == ERANGE || ull > 56 || ull <= 0)
907 return FALSE;
908 if (n)
909 *n = ull;
910 if (named)
911 *named = FALSE;
912
913 *color = format_numbered_colors[ull];
914 return TRUE;
915 }
916
917 return FALSE;
918 }
919
920 static gboolean
go_format_parse_locale(const char * str,GOFormatLocale * locale,gsize * nchars)921 go_format_parse_locale (const char *str, GOFormatLocale *locale, gsize *nchars)
922 {
923 guint64 ull;
924 const char *close;
925 char *end;
926 gsize n;
927
928 if (*str++ != '[' ||
929 *str++ != '$')
930 return FALSE;
931
932 close = strchr (str, ']');
933 if (!close)
934 return FALSE;
935
936 n = 0;
937 while (*str != '-' && *str != ']') {
938 str = g_utf8_next_char (str);
939 n++;
940 }
941 if (nchars)
942 *nchars = n;
943
944 if (*str == '-') {
945 str++;
946 ull = g_ascii_strtoull (str, &end, 16);
947 if (str == end || errno == ERANGE ||
948 ull > G_GINT64_CONSTANT(0x0001ffffffffffffU))
949 return FALSE;
950 } else {
951 ull = 0;
952 }
953 if (locale)
954 locale->locale = ull;
955
956 return TRUE;
957 }
958
959 static void
determine_inhibit_minus(GOFormatCondition * cond)960 determine_inhibit_minus (GOFormatCondition *cond)
961 {
962 cond->true_inhibits_minus = FALSE;
963 cond->false_inhibits_minus = FALSE;
964
965 switch (cond->op) {
966 case GO_FMT_COND_GT:
967 /* ">" and ">=" strangely follow the same rule. */
968 case GO_FMT_COND_GE:
969 cond->false_inhibits_minus = (cond->val <= 0);
970 break;
971 case GO_FMT_COND_LT:
972 cond->true_inhibits_minus = (cond->val <= 0);
973 break;
974 case GO_FMT_COND_LE:
975 cond->true_inhibits_minus = (cond->val < 0);
976 break;
977 case GO_FMT_COND_EQ:
978 cond->true_inhibits_minus = (cond->val < 0);
979 break;
980 case GO_FMT_COND_NE:
981 cond->false_inhibits_minus = (cond->val < 0);
982 break;
983 default:
984 break;
985 }
986 }
987
988
989 static gboolean
go_format_parse_condition(const char * str,GOFormatCondition * cond)990 go_format_parse_condition (const char *str, GOFormatCondition *cond)
991 {
992 char *end;
993
994 cond->op = GO_FMT_COND_NONE;
995 cond->val = 0;
996 cond->fmt = NULL;
997 cond->implicit = TRUE;
998
999 if (*str++ != '[')
1000 return FALSE;
1001
1002 if (str[0] == '>' && str[1] == '=')
1003 cond->op = GO_FMT_COND_GE, str += 2;
1004 else if (str[0] == '>')
1005 cond->op = GO_FMT_COND_GT, str++;
1006 else if (str[0] == '<' && str[1] == '=')
1007 cond->op = GO_FMT_COND_LE, str += 2;
1008 else if (str[0] == '<' && str[1] == '>')
1009 cond->op = GO_FMT_COND_NE, str += 2;
1010 else if (str[0] == '<')
1011 cond->op = GO_FMT_COND_LT, str++;
1012 else if (str[0] == '=')
1013 cond->op = GO_FMT_COND_EQ, str++;
1014 else
1015 return FALSE;
1016 cond->implicit = FALSE;
1017
1018 cond->val = go_ascii_strtod (str, &end);
1019 if (end == str || errno == ERANGE)
1020 return FALSE;
1021 end = strchr (end, ']');
1022 if (!end)
1023 return FALSE;
1024
1025 determine_inhibit_minus (cond);
1026
1027 return TRUE;
1028 }
1029
1030 static GOFormatToken
go_format_token2(char const ** pstr,GOFormatTokenType * ptt,gboolean localized)1031 go_format_token2 (char const **pstr, GOFormatTokenType *ptt, gboolean localized)
1032 {
1033 const GString *decimal = go_locale_get_decimal ();
1034 const GString *comma = go_locale_get_thousand ();
1035 const char *str = *pstr;
1036 GOFormatTokenType tt =
1037 TT_ALLOWED_IN_DATE | TT_ALLOWED_IN_NUMBER | TT_ALLOWED_IN_TEXT;
1038 int t;
1039 int len = 1;
1040 const char *general = N_("General");
1041 size_t general_len = 7;
1042
1043 if (localized) {
1044 general = _(general);
1045 general_len = strlen (general);
1046 }
1047
1048 if (str == NULL)
1049 goto error;
1050
1051 t = *(guchar *)str;
1052
1053 /* "Ascii" is probably wrong for localized, but it is not clear what to do. */
1054 if (g_ascii_strncasecmp (str, general, general_len) == 0) {
1055 t = TOK_GENERAL;
1056 tt = TT_ALLOWED_IN_DATE;
1057 len = general_len;
1058 goto got_token;
1059 }
1060
1061 if (localized
1062 ? strncmp (str, decimal->str, decimal->len) == 0
1063 : *str == '.') {
1064 t = TOK_DECIMAL;
1065 if (localized)
1066 len = decimal->len;
1067 tt = TT_ALLOWED_IN_DATE | TT_ALLOWED_IN_NUMBER | TT_STARTS_NUMBER;
1068 goto got_token;
1069 }
1070
1071 if (localized
1072 ? strncmp (str, comma->str, comma->len) == 0
1073 : *str == ',') {
1074 t = TOK_THOUSAND;
1075 if (localized)
1076 len = comma->len;
1077 goto got_token;
1078 }
1079
1080 switch (t) {
1081 case 0:
1082 len = 0; /* Note: str not advanced. */
1083 case ';':
1084 tt = TT_TERMINATES_SINGLE;
1085 break;
1086
1087 case 'g': case 'G':
1088 case 'd': case 'D':
1089 case 'y': case 'Y':
1090 case 'b': case 'B':
1091 case 'e':
1092 case 'h': case 'H':
1093 case 'm': case 'M':
1094 case 's': case 'S':
1095 tt = TT_ALLOWED_IN_DATE | TT_STARTS_DATE;
1096 break;
1097
1098 case 'n': case 'N':
1099 goto error;
1100
1101 case 'a': case 'A':
1102 if (str[1] == '/' && (str[2] == 'p' || str[2] == 'P')) {
1103 tt = TT_ALLOWED_IN_DATE | TT_STARTS_DATE;
1104 t = TOK_AMPM3;
1105 len = 3;
1106 } else if ((str[1] == 'm' || str[1] == 'M') &&
1107 str[2] == '/' &&
1108 (str[3] == 'p' || str[3] == 'P') &&
1109 (str[4] == 'm' || str[4] == 'M')) {
1110 tt = TT_ALLOWED_IN_DATE | TT_STARTS_DATE;
1111 t = TOK_AMPM5;
1112 len = 5;
1113 }
1114 break;
1115
1116 case '[':
1117 switch (str[1]) {
1118 case 's': case 'S':
1119 case 'm': case 'M':
1120 case 'h': case 'H': {
1121 char c = g_ascii_toupper (str[1]);
1122 len++;
1123 while (g_ascii_toupper (str[len]) == c)
1124 len++;
1125 if (str[len] == ']') {
1126 t = (c == 'S'
1127 ? TOK_ELAPSED_S
1128 : (c == 'M'
1129 ? TOK_ELAPSED_M
1130 : TOK_ELAPSED_H));
1131 tt = TT_ALLOWED_IN_DATE | TT_STARTS_DATE;
1132 } else
1133 t = TOK_COLOR;
1134 break;
1135 }
1136 case '=':
1137 case '>':
1138 case '<':
1139 t = TOK_CONDITION;
1140 break;
1141
1142 case '$':
1143 t = TOK_LOCALE;
1144 break;
1145
1146 default:
1147 t = TOK_COLOR;
1148 break;
1149 }
1150
1151 while (str[len] != ']') {
1152 if (str[len] == 0)
1153 goto error;
1154 len++;
1155 }
1156 len++;
1157 break;
1158
1159 case '0':
1160 case '/':
1161 tt = TT_ALLOWED_IN_DATE | TT_ALLOWED_IN_NUMBER | TT_STARTS_NUMBER;
1162 break;
1163
1164 case '1': case '2': case '3': case '4':
1165 case '5': case '6': case '7': case '8': case '9':
1166 case '#':
1167 case '?':
1168 case '%':
1169 tt = TT_ALLOWED_IN_NUMBER | TT_STARTS_NUMBER;
1170 break;
1171
1172 case 'E':
1173 tt = TT_ALLOWED_IN_NUMBER | TT_STARTS_NUMBER;
1174 #ifdef ALLOW_SI_APPEND
1175 if (str[1] == 'S' && str[2] == 'I') {
1176 t = TOK_EXP_SI,
1177 len = 3;
1178 } else
1179 #endif
1180 #if defined(ALLOW_EE_MARKUP) && defined(ALLOW_SI_APPEND)
1181 if (str[1] == 'E' && str[2] == 'S' && str[3] == 'I') {
1182 t = TOK_EXP_MU_SI;
1183 len = 4;
1184 } else
1185 #endif
1186 #ifdef ALLOW_EE_MARKUP
1187 if (str[1] == 'E') {
1188 t = TOK_EXP_MU;
1189 len = 2;
1190 } else
1191 #endif
1192 t = TOK_EXP;
1193 break;
1194
1195 case '@':
1196 tt = TT_ALLOWED_IN_TEXT | TT_STARTS_TEXT;
1197 break;
1198
1199 case '\\':
1200 t = TOK_ESCAPED_CHAR;
1201 if (str[1] == 0)
1202 goto error;
1203 len += g_utf8_skip[((guchar *)str)[1]];
1204 break;
1205
1206 case '_':
1207 t = TOK_INVISIBLE_CHAR;
1208 if (str[1] == 0)
1209 goto error;
1210 len += g_utf8_skip[((guchar *)str)[1]];
1211 break;
1212
1213 case '*':
1214 t = TOK_REPEATED_CHAR;
1215 if (str[1] == 0)
1216 goto error;
1217 len += g_utf8_skip[((guchar *)str)[1]];
1218 break;
1219
1220 case '"':
1221 t = TOK_STRING;
1222 while (str[len] != '"') {
1223 if (str[len] == 0)
1224 goto error;
1225 len++;
1226 }
1227 len++;
1228 break;
1229
1230 default:
1231 if (t >= 0x80) {
1232 t = TOK_CHAR;
1233 len = g_utf8_skip[((guchar *)str)[0]];
1234 }
1235 break;
1236 }
1237
1238 got_token:
1239 *ptt = tt;
1240 *pstr = str + len;
1241 return t;
1242
1243 error:
1244 *ptt = TT_ERROR;
1245 return TOK_ERROR;
1246 }
1247
1248 static GOFormatToken
go_format_token(char const ** pstr,GOFormatTokenType * ptt)1249 go_format_token (char const **pstr, GOFormatTokenType *ptt)
1250 {
1251 return go_format_token2 (pstr, ptt, FALSE);
1252 }
1253
1254 #define GET_TOKEN(_i) g_array_index (pstate->tokens, GOFormatParseItem, _i)
1255
1256 static const char *
go_format_preparse(const char * str,GOFormatParseState * pstate,gboolean all_tokens,gboolean is_localized)1257 go_format_preparse (const char *str, GOFormatParseState *pstate,
1258 gboolean all_tokens, gboolean is_localized)
1259 {
1260 gboolean ntokens = 0; /* Excluding cond,color,locale unless all_tokens. */
1261 gboolean is_date = FALSE;
1262 gboolean is_number = FALSE;
1263 gboolean is_text = FALSE;
1264 gboolean has_general = FALSE;
1265
1266 pstate->tokens =
1267 g_array_new (FALSE, FALSE, sizeof (GOFormatParseItem));
1268 pstate->tno_slash = -1;
1269 pstate->tno_E = -1;
1270
1271 while (1) {
1272 GOFormatTokenType tt;
1273 const char *tstr = str;
1274 int t = go_format_token2 (&str, &tt, is_localized);
1275 int tno = pstate->tokens->len;
1276
1277 if (tt & TT_TERMINATES_SINGLE) {
1278 if (pstate->tno_E >= 0 && pstate->tno_slash >= 0)
1279 goto error;
1280 if (has_general && (is_number || is_text))
1281 goto error;
1282
1283 if (ntokens == 0 &&
1284 (pstate->have_color || pstate->have_cond) &&
1285 !pstate->have_locale) {
1286 /* Pretend to have seen "General" */
1287 has_general = TRUE;
1288 g_array_set_size (pstate->tokens, tno + 1);
1289 GET_TOKEN(tno).tstr = tstr;
1290 GET_TOKEN(tno).token = TOK_GENERAL;
1291 GET_TOKEN(tno).tt = TT_ALLOWED_IN_DATE;
1292 ntokens++;
1293 }
1294
1295 pstate->is_date = is_date;
1296 pstate->is_number = is_number;
1297 pstate->has_general = has_general;
1298 pstate->is_general = has_general && ntokens == 1;
1299
1300 if (ntokens == 0)
1301 pstate->typ = GO_FMT_EMPTY;
1302 else if (is_text)
1303 pstate->typ = GO_FMT_TEXT;
1304 else
1305 pstate->typ = GO_FMT_NUMBER;
1306 return tstr;
1307 }
1308
1309 if (is_date) {
1310 if (!(tt & TT_ALLOWED_IN_DATE))
1311 goto error;
1312 } else if (is_number) {
1313 if (!(tt & TT_ALLOWED_IN_NUMBER))
1314 goto error;
1315 } else if (is_text) {
1316 if (!(tt & TT_ALLOWED_IN_TEXT))
1317 goto error;
1318 } else {
1319 if (tt & TT_STARTS_DATE)
1320 is_date = TRUE;
1321 else if (tt & TT_STARTS_NUMBER)
1322 is_number = TRUE;
1323 else if (tt & TT_STARTS_TEXT)
1324 is_text = TRUE;
1325 }
1326
1327 g_array_set_size (pstate->tokens, tno + 1);
1328 GET_TOKEN(tno).tstr = tstr;
1329 GET_TOKEN(tno).token = t;
1330 GET_TOKEN(tno).tt = tt;
1331
1332 switch (t) {
1333 case TOK_ERROR:
1334 goto error;
1335
1336 case TOK_GENERAL:
1337 if (has_general)
1338 goto error;
1339 has_general = TRUE;
1340 ntokens++;
1341 break;
1342
1343 case TOK_CONDITION:
1344 if (pstate->have_cond ||
1345 !go_format_parse_condition (tstr, &pstate->cond))
1346 goto error;
1347 pstate->have_cond = TRUE;
1348 if (all_tokens)
1349 ntokens++;
1350 break;
1351
1352 case TOK_COLOR:
1353 if (pstate->have_color ||
1354 !go_format_parse_color (tstr, &pstate->color,
1355 &pstate->color_n,
1356 &pstate->color_named,
1357 is_localized))
1358 goto error;
1359 pstate->have_color = TRUE;
1360 if (all_tokens)
1361 ntokens++;
1362 break;
1363
1364 case TOK_LOCALE:
1365 if (pstate->have_locale ||
1366 !go_format_parse_locale (tstr, &pstate->locale, NULL))
1367 goto error;
1368 pstate->have_locale = TRUE;
1369 if (all_tokens)
1370 ntokens++;
1371 break;
1372
1373 case TOK_REPEATED_CHAR:
1374 /* Last one counts. */
1375 pstate->fill_char = g_utf8_get_char (tstr + 1);
1376 ntokens++;
1377 break;
1378
1379 case TOK_EXP:
1380 #ifdef ALLOW_EE_MARKUP
1381 case TOK_EXP_MU:
1382 #endif
1383 #ifdef ALLOW_SI_APPEND
1384 case TOK_EXP_SI:
1385 #endif
1386 #if defined(ALLOW_EE_MARKUP) && defined(ALLOW_SI_APPEND)
1387 case TOK_EXP_MU_SI:
1388 #endif
1389 ntokens++;
1390 if (pstate->tno_E >= 0)
1391 goto error;
1392 pstate->tno_E = tno;
1393 break;
1394
1395 case '/':
1396 ntokens++;
1397 if (is_number) {
1398 if (pstate->tno_slash >= 0)
1399 goto error;
1400 pstate->tno_slash = tno;
1401 break;
1402 }
1403 break;
1404
1405 default:
1406 ntokens++;
1407 break;
1408 }
1409 }
1410
1411 error:
1412 pstate->typ = GO_FMT_INVALID;
1413 return NULL;
1414 }
1415
1416 static gboolean
tail_forces_minutes(const char * str)1417 tail_forces_minutes (const char *str)
1418 {
1419 while (1) {
1420 GOFormatTokenType tt;
1421 int t = go_format_token (&str, &tt);
1422
1423 switch (t) {
1424 case 0:
1425 case ';':
1426 case 'b': case 'B':
1427 case 'd': case 'D':
1428 case 'm': case 'M':
1429 case 'h': case 'H':
1430 case 'y': case 'Y':
1431 return FALSE;
1432 case 's': case 'S':
1433 return TRUE;
1434 }
1435 }
1436 }
1437
1438
1439 static GOFormat *
go_format_create(GOFormatClass cl,const char * format)1440 go_format_create (GOFormatClass cl, const char *format)
1441 {
1442 GOFormat *fmt = g_new0 (GOFormat, 1);
1443 fmt->typ = cl;
1444 fmt->ref_count = 1;
1445 fmt->format = g_strdup (format);
1446 return fmt;
1447 }
1448
1449 #define ADD_OP(op) g_string_append_c (prg,(op))
1450 #define ADD_OPuc(op,uc) do { g_string_append_c (prg,(op)); g_string_append_unichar (prg, (uc)); } while (0)
1451 #define ADD_OP2(op1,op2) do { ADD_OP(op1); ADD_OP(op2); } while (0)
1452 #define ADD_OP3(op1,op2,op3) do { ADD_OP2(op1,op2); ADD_OP(op3); } while (0)
1453
1454 /*
1455 * Handle literal characters, including quoted segments and invisible
1456 * characters.
1457 */
1458 static void
handle_common_token(const char * tstr,GOFormatToken t,GString * prg)1459 handle_common_token (const char *tstr, GOFormatToken t, GString *prg)
1460 {
1461 switch (t) {
1462 case TOK_STRING: {
1463 size_t len = strchr (tstr + 1, '"') - (tstr + 1);
1464 if (len > 0) {
1465 ADD_OP (OP_STRING);
1466 g_string_append_len (prg, tstr + 1, len);
1467 g_string_append_c (prg, '\0');
1468 }
1469 tstr += len + 2;
1470 break;
1471 }
1472
1473 case TOK_DECIMAL:
1474 case TOK_THOUSAND:
1475 ADD_OP2 (OP_CHAR, *tstr);
1476 break;
1477
1478 case TOK_CHAR:
1479 ADD_OPuc (OP_CHAR, g_utf8_get_char (tstr));
1480 break;
1481
1482 case TOK_ESCAPED_CHAR:
1483 ADD_OPuc (OP_CHAR, g_utf8_get_char (tstr + 1));
1484 break;
1485
1486 case TOK_INVISIBLE_CHAR:
1487 ADD_OPuc (OP_CHAR_INVISIBLE, g_utf8_get_char (tstr + 1));
1488 break;
1489
1490 case TOK_REPEATED_CHAR:
1491 ADD_OP (OP_CHAR_REPEAT);
1492 break;
1493
1494 case TOK_LOCALE: {
1495 char *oldlocale;
1496 GOFormatLocale locale;
1497 const char *lang;
1498 gsize nchars;
1499 guint shape, flags;
1500 gboolean ok = go_format_parse_locale (tstr, &locale, &nchars);
1501 /* Already parsed elsewhere */
1502 g_return_if_fail (ok);
1503
1504 tstr += 2;
1505 if (nchars > 0) {
1506 const char *next =
1507 g_utf8_offset_to_pointer (tstr, nchars);
1508 ADD_OP (OP_STRING);
1509 g_string_append_len (prg, tstr, next - tstr);
1510 g_string_append_c (prg, '\0');
1511 tstr = next;
1512 }
1513
1514 lang = gsf_msole_language_for_lid (locale.locale & 0xffff);
1515
1516 if (0 != strcmp (lang, "-none-")) {
1517 char *lname = NULL;
1518 oldlocale = g_strdup (setlocale (LC_ALL, NULL));
1519 ok = setlocale (LC_ALL, lang) != NULL;
1520
1521 if (!ok) {
1522 lname = g_strdup_printf ("%s.utf-8",lang);
1523 lang = lname;
1524 ok = setlocale (LC_ALL, lang) != NULL;
1525 }
1526
1527 setlocale (LC_ALL, oldlocale);
1528 g_free (oldlocale);
1529
1530 if (ok) {
1531 ADD_OP (OP_LOCALE);
1532 g_string_append_len (prg, (void *)&locale,
1533 sizeof (locale));
1534 /* Include the terminating zero: */
1535 g_string_append_len (prg, lang, strlen (lang) + 1);
1536 }
1537 g_free (lname);
1538 }
1539 shape = (locale.locale & G_GINT64_CONSTANT(0x0ff000000U)) >> 24;
1540 flags = (locale.locale & G_GINT64_CONSTANT(0xf00000000U)) >> 32;
1541 if (shape != 0 || flags != 0)
1542 ADD_OP3 (OP_NUMERAL_SHAPE, flags, shape);
1543 break;
1544 }
1545
1546 case TOK_NULL:
1547 break;
1548
1549 default:
1550 if (t < 0x80) {
1551 ADD_OP2 (OP_CHAR, t);
1552 }
1553 break;
1554 }
1555 }
1556
1557 static void
handle_fill(GString * prg,const GOFormatParseState * pstate)1558 handle_fill (GString *prg, const GOFormatParseState *pstate)
1559 {
1560 if (pstate->fill_char) {
1561 ADD_OP (OP_FILL);
1562 g_string_append_unichar (prg, pstate->fill_char);
1563 }
1564 }
1565
1566
1567 static GOFormat *
go_format_parse_sequential(const char * str,GString * prg,const GOFormatParseState * pstate)1568 go_format_parse_sequential (const char *str, GString *prg,
1569 const GOFormatParseState *pstate)
1570 {
1571 int date_decimals = 0;
1572 gboolean seen_date = FALSE;
1573 gboolean seen_year = FALSE;
1574 gboolean seen_month = FALSE;
1575 gboolean seen_day = FALSE;
1576 gboolean seen_time = FALSE;
1577 gboolean seen_hour = FALSE;
1578 gboolean seen_ampm = FALSE;
1579 gboolean seen_minute = FALSE;
1580 gboolean seen_elapsed = FALSE;
1581 gboolean m_is_minutes = FALSE;
1582 gboolean seconds_trigger_minutes = TRUE;
1583 gboolean date_ybm = FALSE;
1584 gboolean date_mbd = FALSE;
1585 gboolean date_dbm = FALSE;
1586 gboolean prg_was_null = (prg == NULL);
1587
1588 if (!prg)
1589 prg = g_string_new (NULL);
1590
1591 while (1) {
1592 const char *token = str;
1593 GOFormatTokenType tt;
1594 int t = go_format_token (&str, &tt);
1595
1596 switch (t) {
1597 case 0: case ';':
1598 goto done;
1599
1600 case 'd': case 'D': {
1601 int n = 1;
1602 while (*str == 'd' || *str == 'D')
1603 str++, n++;
1604 seen_date = TRUE;
1605 switch (n) {
1606 case 1: ADD_OP (OP_DATE_DAY); break;
1607 case 2: ADD_OP (OP_DATE_DAY_2); break;
1608 case 3: ADD_OP (OP_DATE_WEEKDAY_3); break;
1609 default: ADD_OP (OP_DATE_WEEKDAY); break;
1610 }
1611 if (!seen_day) {
1612 seen_day = TRUE;
1613 if (seen_month)
1614 date_mbd = TRUE;
1615 }
1616 break;
1617 }
1618
1619 case 'y': case 'Y': {
1620 int n = 1;
1621 while (*str == 'y' || *str == 'Y')
1622 str++, n++;
1623 seen_date = TRUE;
1624 seen_year = TRUE;
1625 ADD_OP (n <= 2 ? OP_DATE_YEAR_2 : OP_DATE_YEAR);
1626 break;
1627 }
1628
1629 case 'b': case 'B': {
1630 int n = 1;
1631 while (*str == 'b' || *str == 'B')
1632 str++, n++;
1633 seen_date = TRUE;
1634 seen_year = TRUE;
1635 ADD_OP (n <= 2 ? OP_DATE_YEAR_THAI_2 : OP_DATE_YEAR_THAI);
1636 break;
1637 }
1638
1639 case 'e':
1640 while (*str == 'e') str++;
1641 seen_date = TRUE;
1642 seen_year = TRUE;
1643 ADD_OP (OP_DATE_YEAR);
1644 break;
1645
1646 case 'g': case 'G':
1647 /* Something with Japanese eras. Blank for me. */
1648 seen_date = TRUE;
1649 break;
1650
1651 case 'h': case 'H': {
1652 int n = 1;
1653 while (*str == 'h' || *str == 'H')
1654 str++, n++;
1655 seen_time = TRUE;
1656 ADD_OP (n == 1 ? OP_TIME_HOUR : OP_TIME_HOUR_2);
1657 seen_hour = TRUE;
1658 m_is_minutes = TRUE;
1659 break;
1660 }
1661
1662 case 'm': case 'M': {
1663 int n = 1;
1664 while (*str == 'm' || *str == 'M')
1665 str++, n++;
1666 m_is_minutes = (n <= 2) && (m_is_minutes || tail_forces_minutes (str));
1667
1668 if (m_is_minutes) {
1669 seen_time = TRUE;
1670 seconds_trigger_minutes = FALSE;
1671 ADD_OP (n == 1 ? OP_TIME_MINUTE : OP_TIME_MINUTE_2);
1672 m_is_minutes = FALSE;
1673 seen_minute = TRUE;
1674 } else {
1675 seen_date = TRUE;
1676 switch (n) {
1677 case 1: ADD_OP (OP_DATE_MONTH); break;
1678 case 2: ADD_OP (OP_DATE_MONTH_2); break;
1679 case 3: ADD_OP (OP_DATE_MONTH_NAME_3); break;
1680 case 5: ADD_OP (OP_DATE_MONTH_NAME_1); break;
1681 default: ADD_OP (OP_DATE_MONTH_NAME); break;
1682 }
1683 if (!seen_month) {
1684 seen_month = TRUE;
1685 if (seen_day)
1686 date_dbm = TRUE;
1687 if (seen_year && !seen_day)
1688 date_ybm = TRUE;
1689 }
1690 }
1691 break;
1692 }
1693
1694 case 's': case 'S': {
1695 int n = 1;
1696 while (*str == 's' || *str == 'S')
1697 str++, n++;
1698
1699 if (seconds_trigger_minutes) {
1700 seconds_trigger_minutes = FALSE;
1701 m_is_minutes = TRUE;
1702 }
1703
1704 seen_time = TRUE;
1705 ADD_OP (n == 1 ? OP_TIME_SECOND : OP_TIME_SECOND_2);
1706
1707 break;
1708 }
1709
1710 case TOK_AMPM3:
1711 if (seen_elapsed)
1712 goto error;
1713 seen_time = TRUE;
1714 seen_ampm = TRUE;
1715 ADD_OP3 (OP_TIME_AP, token[0], token[2]);
1716 break;
1717
1718 case TOK_AMPM5:
1719 if (seen_elapsed)
1720 goto error;
1721 seen_time = TRUE;
1722 seen_ampm = TRUE;
1723 ADD_OP (OP_TIME_AMPM);
1724 break;
1725
1726 case TOK_DECIMAL:
1727 if (*str == '0') {
1728 int n = 0;
1729 seen_time = TRUE;
1730 ADD_OP (OP_TIME_SECOND_DECIMAL_START);
1731 while (*str == '0') {
1732 str++, n++;
1733 ADD_OP (OP_TIME_SECOND_DECIMAL_DIGIT);
1734 }
1735 /* The actual limit is debatable. This is what XL does. */
1736 if (n > 3)
1737 goto error;
1738 date_decimals = MAX (date_decimals, n);
1739 } else {
1740 ADD_OP2 (OP_CHAR, '.');
1741 }
1742 break;
1743
1744 case '0':
1745 goto error;
1746
1747 case TOK_ELAPSED_H:
1748 if (seen_elapsed || seen_ampm)
1749 goto error;
1750 seen_time = TRUE;
1751 seen_elapsed = TRUE;
1752 seen_hour = TRUE;
1753 m_is_minutes = TRUE;
1754 ADD_OP2 (OP_TIME_HOUR_N, str - token - 2);
1755 break;
1756
1757 case TOK_ELAPSED_M:
1758 if (seen_elapsed || seen_ampm)
1759 goto error;
1760 seen_time = TRUE;
1761 seen_elapsed = TRUE;
1762 seen_minute = TRUE;
1763 m_is_minutes = FALSE;
1764 ADD_OP2 (OP_TIME_MINUTE_N, str - token - 2);
1765 break;
1766
1767 case TOK_ELAPSED_S:
1768 if (seen_elapsed || seen_ampm)
1769 goto error;
1770 seen_time = TRUE;
1771 seen_elapsed = TRUE;
1772 if (seconds_trigger_minutes) {
1773 m_is_minutes = TRUE;
1774 seconds_trigger_minutes = FALSE;
1775 }
1776 ADD_OP2 (OP_TIME_SECOND_N, str - token - 2);
1777 break;
1778
1779 case TOK_GENERAL:
1780 ADD_OP (OP_NUM_GENERAL_MARK);
1781 break;
1782
1783 case '@':
1784 ADD_OP (OP_STR_APPEND_SVAL);
1785 break;
1786
1787 default:
1788 handle_common_token (token, t, prg);
1789 break;
1790 }
1791 }
1792
1793 done:
1794 if (pstate->typ == GO_FMT_TEXT) {
1795 GOFormat *fmt = go_format_create (GO_FMT_TEXT, NULL);
1796 handle_fill (prg, pstate);
1797 fmt->u.text.program = g_string_free (prg, FALSE);
1798 return fmt;
1799 } else {
1800 GOFormat *fmt = go_format_create (GO_FMT_NUMBER, NULL);
1801 guchar splits[5] = { OP_DATE_ROUND, date_decimals, seen_elapsed };
1802 guchar *p = splits + 3;
1803 if (seen_date) {
1804 *p++ = OP_DATE_SPLIT;
1805 fmt->u.number.has_date = TRUE;
1806 fmt->u.number.date_ybm = date_ybm;
1807 fmt->u.number.date_mbd = date_mbd;
1808 fmt->u.number.date_dbm = date_dbm;
1809 fmt->u.number.has_year = seen_year;
1810 fmt->u.number.has_month = seen_month;
1811 fmt->u.number.has_day = seen_day;
1812 }
1813 if (seen_time) {
1814 guchar op;
1815 if (seen_elapsed) {
1816 if (seen_hour)
1817 op = OP_TIME_SPLIT_ELAPSED_HOUR;
1818 else if (seen_minute)
1819 op = OP_TIME_SPLIT_ELAPSED_MINUTE;
1820 else
1821 op = OP_TIME_SPLIT_ELAPSED_SECOND;
1822 } else {
1823 op = seen_ampm
1824 ? OP_TIME_SPLIT_12
1825 : OP_TIME_SPLIT_24;
1826 }
1827 *p++ = op;
1828 fmt->u.number.has_time = TRUE;
1829 fmt->u.number.has_hour = seen_hour;
1830 fmt->u.number.has_minute = seen_minute;
1831 fmt->u.number.has_elapsed = seen_elapsed;
1832 }
1833 if (pstate->has_general) {
1834 ADD_OP (OP_NUM_GENERAL_DO);
1835 fmt->u.number.has_general = pstate->has_general;
1836 fmt->u.number.is_general = pstate->is_general;
1837 }
1838 handle_fill (prg, pstate);
1839 g_string_insert_len (prg, 0, splits, p - splits);
1840 fmt->u.number.program = g_string_free (prg, FALSE);
1841 return fmt;
1842 }
1843
1844 error:
1845 if (prg_was_null)
1846 g_string_free (prg, TRUE);
1847
1848 return NULL;
1849 }
1850
1851 static gboolean
comma_is_thousands(const char * str)1852 comma_is_thousands (const char *str)
1853 {
1854 while (1) {
1855 GOFormatTokenType tt;
1856 int t = go_format_token (&str, &tt);
1857
1858 switch (t) {
1859 case '0': case '?': case '#':
1860 return TRUE;
1861 case 0:
1862 case ';':
1863 case TOK_DECIMAL:
1864 return FALSE;
1865 }
1866 }
1867 }
1868
1869 static gboolean
go_format_parse_number_new_1(GString * prg,GOFormatParseState * pstate,int tno_start,int tno_end,int E_part,int frac_part,gboolean * inhibit_blank)1870 go_format_parse_number_new_1 (GString *prg, GOFormatParseState *pstate,
1871 int tno_start, int tno_end,
1872 int E_part, int frac_part,
1873 gboolean *inhibit_blank)
1874 {
1875 int decimals = 0;
1876 int whole_digits = 0;
1877 gboolean inhibit_thousands = (E_part == 2) || (frac_part >= 2);
1878 gboolean thousands = FALSE;
1879 gboolean whole_part = TRUE;
1880 int scale = 0;
1881 int first_digit_pos = -1;
1882 int dot_pos = -1;
1883 int one_pos;
1884 int i;
1885 int tno_numstart = -1;
1886 int force_zero_pos = frac_part == 3 ? pstate->force_zero_pos : INT_MAX;
1887 gboolean inhibit_b = FALSE;
1888
1889 for (i = tno_start; i < tno_end; i++) {
1890 const GOFormatParseItem *ti = &GET_TOKEN(i);
1891 if (ti->tt & TT_STARTS_NUMBER)
1892 break;
1893 handle_common_token (ti->tstr, ti->token, prg);
1894 }
1895
1896 for (i = tno_start; i < tno_end; i++) {
1897 const GOFormatParseItem *ti = &GET_TOKEN(i);
1898
1899 if (tno_numstart == -1 && (ti->tt & TT_STARTS_NUMBER))
1900 tno_numstart = i;
1901
1902 switch (ti->token) {
1903 case TOK_DECIMAL:
1904 if (!whole_part)
1905 break;
1906 dot_pos = i;
1907 if (first_digit_pos == -1)
1908 first_digit_pos = i;
1909 whole_part = FALSE;
1910 break;
1911
1912 case '0':
1913 inhibit_b = TRUE;
1914 /* no break */
1915 case '?': case '#':
1916 if (first_digit_pos == -1)
1917 first_digit_pos = i;
1918 if (whole_part)
1919 whole_digits++;
1920 else
1921 decimals++;
1922 break;
1923
1924 case '%':
1925 if (E_part != 1 && E_part != 2)
1926 scale += 2;
1927 break;
1928
1929 case '\'':
1930 if (E_part == 1)
1931 scale += 3;
1932 break;
1933
1934 case TOK_THOUSAND:
1935 if (tno_numstart != -1 && E_part != 2) {
1936 if (comma_is_thousands (ti->tstr)) {
1937 if (whole_part)
1938 thousands = TRUE;
1939 if (ti->tstr[1] == ' ')
1940 inhibit_thousands = TRUE;
1941 } else {
1942 if (frac_part == 0)
1943 scale -= 3;
1944 }
1945 }
1946 break;
1947
1948 default:
1949 break;
1950 }
1951 }
1952
1953 if (E_part == 1) {
1954 if (tno_numstart == -1)
1955 goto error;
1956 if (scale)
1957 ADD_OP2 (OP_NUM_SCALE, scale);
1958 ADD_OP3 (OP_NUM_PRINTF_E, decimals, whole_digits);
1959 #ifdef OBSERVE_XL_EXPONENT_1
1960 if (whole_digits + decimals == 0) {
1961 /*
1962 * If no digits precede "E", pretend that the
1963 * exponent is 1. Don't ask!
1964 */
1965 ADD_OP (OP_NUM_EXPONENT_1);
1966 }
1967 #endif
1968 } else {
1969 /*
1970 * It's unclear whether this is the correct action, but it
1971 * happens for
1972 * "_($* /,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)"
1973 * in bug 750047.
1974 */
1975 if (tno_numstart == -1)
1976 goto error;
1977
1978 if (scale && !frac_part && E_part != 2)
1979 ADD_OP2 (OP_NUM_SCALE, scale);
1980 ADD_OP2 (OP_NUM_PRINTF_F, decimals);
1981 if (thousands && !inhibit_thousands)
1982 ADD_OP (OP_NUM_ENABLE_THOUSANDS);
1983 }
1984
1985 ADD_OP (OP_NUM_SIGN);
1986
1987 if (E_part == 1) {
1988 #ifdef ALLOW_EE_MARKUP
1989 ADD_OP (OP_NUM_MARK_MANTISSA);
1990 #endif
1991 } else if (E_part == 2) {
1992 ADD_OP2 (OP_NUM_EXPONENT_SIGN, pstate->forced_exponent_sign);
1993 if (first_digit_pos == -1)
1994 goto error;
1995 }
1996
1997 one_pos = (dot_pos == -1) ? tno_end - 1 : dot_pos - 1;
1998 ADD_OP (OP_NUM_MOVETO_ONES);
1999 if (E_part == 2)
2000 ADD_OP (OP_NUM_STORE_POS);
2001 for (i = one_pos; i >= tno_numstart; i--) {
2002 const GOFormatParseItem *ti = &GET_TOKEN(i);
2003
2004 if (pstate->explicit_denom && g_ascii_isdigit (ti->token)) {
2005 ADD_OP2 (OP_CHAR, ti->token);
2006 continue;
2007 }
2008
2009 switch (ti->token) {
2010 case '?':
2011 if (frac_part == 3 && i < force_zero_pos) {
2012 ADD_OP (OP_NUM_DENUM_DIGIT_Q);
2013 break;
2014 }
2015 /* Fall through */
2016 case '0':
2017 case '#':
2018 if (i >= force_zero_pos)
2019 ADD_OP2 (OP_NUM_DIGIT_1_0, ti->token);
2020 else
2021 ADD_OP2 (OP_NUM_DIGIT_1, ti->token);
2022 break;
2023 case TOK_THOUSAND:
2024 if (frac_part == 3)
2025 ADD_OP2 (OP_CHAR, ',');
2026 break;
2027 case '\'':
2028 if (E_part == 1)
2029 break;
2030 /* Fall through */
2031 default:
2032 handle_common_token (ti->tstr, ti->token, prg);
2033 }
2034 if (i == first_digit_pos)
2035 ADD_OP (OP_NUM_REST_WHOLE);
2036 }
2037 ADD_OP (OP_NUM_APPEND_MODE);
2038
2039 if (dot_pos >= 0) {
2040 ADD_OP (OP_NUM_DECIMAL_POINT);
2041 ADD_OP (OP_NUM_MOVETO_DECIMALS);
2042 for (i = dot_pos + 1; i < tno_end; i++) {
2043 const GOFormatParseItem *ti = &GET_TOKEN(i);
2044
2045 switch (ti->token) {
2046 case '0':
2047 case '?':
2048 case '#':
2049 ADD_OP2 (OP_NUM_DECIMAL_1, ti->token);
2050 break;
2051 case TOK_THOUSAND:
2052 break;
2053 case '\'':
2054 if (E_part == 1)
2055 break;
2056 /* Fall through */
2057 default:
2058 handle_common_token (ti->tstr, ti->token, prg);
2059 }
2060 }
2061 }
2062
2063 pstate->scale = scale;
2064
2065 if (inhibit_blank)
2066 *inhibit_blank = inhibit_b;
2067 return TRUE;
2068
2069 error:
2070 g_string_free (prg, TRUE);
2071 return FALSE;
2072 }
2073
2074
2075 static GOFormat *
go_format_parse_number_plain(GOFormatParseState * pstate)2076 go_format_parse_number_plain (GOFormatParseState *pstate)
2077 {
2078 GOFormat *fmt;
2079 GString *prg = g_string_new (NULL);
2080
2081 if (!go_format_parse_number_new_1 (prg, pstate,
2082 0, pstate->tokens->len,
2083 0, 0, NULL))
2084 return NULL;
2085
2086 handle_fill (prg, pstate);
2087
2088 fmt = go_format_create (GO_FMT_NUMBER, NULL);
2089 fmt->u.number.program = g_string_free (prg, FALSE);
2090 fmt->u.number.scale_is_2 = (pstate->scale == 2);
2091 return fmt;
2092 }
2093
2094 static GOFormat *
go_format_parse_number_E(GOFormatParseState * pstate)2095 go_format_parse_number_E (GOFormatParseState *pstate)
2096 {
2097 GOFormat *fmt;
2098 GString *prg;
2099 gboolean use_markup = FALSE;
2100 gboolean append_SI = FALSE;
2101 gboolean simplify_mantissa = TRUE;
2102 int tno_end = pstate->tokens->len;
2103 int tno_exp_start = pstate->tno_E + 1;
2104
2105 if (tno_exp_start >= tno_end)
2106 return NULL;
2107
2108 switch (GET_TOKEN (pstate->tno_E).token) {
2109 #ifdef ALLOW_SI_APPEND
2110 case TOK_EXP_SI:
2111 append_SI = TRUE;
2112 break;
2113 #endif
2114 #if defined(ALLOW_EE_MARKUP) && defined(ALLOW_SI_APPEND)
2115 case TOK_EXP_MU_SI:
2116 append_SI = TRUE;
2117 use_markup = TRUE;
2118 break;
2119 #endif
2120 #ifdef ALLOW_EE_MARKUP
2121 case TOK_EXP_MU:
2122 use_markup = TRUE;
2123 break;
2124 #endif
2125 case TOK_EXP:
2126 default:
2127 break;
2128 }
2129
2130 if (use_markup) {
2131 int i;
2132 simplify_mantissa = TRUE;
2133 for (i = 0; i < pstate->tno_E; i++) {
2134 if (GET_TOKEN(i).token == TOK_DECIMAL)
2135 break;
2136 if (GET_TOKEN(i).token == '0') {
2137 simplify_mantissa = FALSE;
2138 break;
2139 }
2140 }
2141 } else
2142 simplify_mantissa = FALSE;
2143
2144 switch (GET_TOKEN (tno_exp_start).token) {
2145 case '-':
2146 pstate->forced_exponent_sign = FALSE;
2147 tno_exp_start++;
2148 break;
2149 case '+':
2150 pstate->forced_exponent_sign = TRUE;
2151 tno_exp_start++;
2152 break;
2153 default:
2154 #ifndef ALLOW_NO_SIGN_AFTER_E
2155 if (!use_markup && !append_SI)
2156 return NULL;
2157 #endif
2158 break;
2159 }
2160
2161 prg = g_string_new (NULL);
2162
2163 if (!go_format_parse_number_new_1 (prg, pstate,
2164 0, pstate->tno_E,
2165 1, 0, NULL))
2166 return NULL;
2167
2168 #ifdef ALLOW_SI_APPEND
2169 if (append_SI)
2170 ADD_OP (OP_NUM_REDUCE_EXPONENT_SI);
2171 #endif
2172 ADD_OP (OP_NUM_VAL_EXPONENT);
2173 #ifdef ALLOW_EE_MARKUP
2174 if (use_markup) {
2175 ADD_OPuc (OP_CHAR, UNICODE_TIMES);
2176 if (simplify_mantissa) {
2177 if (append_SI)
2178 ADD_OP (OP_NUM_SIMPLIFY_MANTISSA_SI);
2179 else
2180 ADD_OP (OP_NUM_SIMPLIFY_MANTISSA);
2181 }
2182 ADD_OP2 (OP_CHAR, '1');
2183 ADD_OP2 (OP_CHAR, '0');
2184 ADD_OP (OP_MARKUP_SUPERSCRIPT_START);
2185 } else
2186 #endif
2187 ADD_OP2 (OP_CHAR, 'E');
2188
2189 if (!go_format_parse_number_new_1 (prg, pstate,
2190 tno_exp_start, tno_end,
2191 2, 0, NULL))
2192 return NULL;
2193
2194 #ifdef ALLOW_EE_MARKUP
2195 if (use_markup) {
2196 if (simplify_mantissa)
2197 ADD_OP (OP_NUM_SIMPLIFY_MARKUP_MANTISSA);
2198 ADD_OP (OP_MARKUP_SUPERSCRIPT_END);
2199 }
2200 #endif
2201 #ifdef ALLOW_SI_APPEND
2202 if (append_SI) {
2203 ADD_OP (OP_NUM_SIMPLIFY_EXPONENT_SI);
2204 ADD_OP (OP_NUM_SI_EXPONENT);
2205 }
2206 #endif
2207
2208 handle_fill (prg, pstate);
2209
2210 fmt = go_format_create (GO_FMT_NUMBER, NULL);
2211 fmt->u.number.program = g_string_free (prg, FALSE);
2212 fmt->u.number.E_format = TRUE;
2213 fmt->u.number.use_markup = use_markup;
2214 fmt->u.number.use_SI = append_SI;
2215 fmt->u.number.scale_is_2 = (pstate->scale == 2);
2216 return fmt;
2217 }
2218
2219 static GOFormat *
go_format_parse_number_fraction(GOFormatParseState * pstate)2220 go_format_parse_number_fraction (GOFormatParseState *pstate)
2221 {
2222 GOFormat *fmt;
2223 GString *prg;
2224 int tno_slash = pstate->tno_slash;
2225 int tno_end = pstate->tokens->len;
2226 int tno_suffix = tno_end;
2227 int tno_endwhole, tno_denom;
2228 int i;
2229 double denominator = 0;
2230 gboolean explicit_denom = FALSE;
2231 int denominator_digits = 0;
2232 gboolean inhibit_blank = FALSE;
2233 gboolean inhibit_blank_denom = FALSE;
2234 gboolean inhibit_blank_numerator = FALSE;
2235 gboolean inhibit_blank_whole = TRUE;
2236 int scale = 0;
2237 #ifdef ALLOW_PI_SLASH
2238 gboolean pi_scale = (tno_slash >= 2 &&
2239 GET_TOKEN(tno_slash - 2).token == 'p' &&
2240 GET_TOKEN(tno_slash - 1).token == 'i');
2241 #else
2242 gboolean pi_scale = FALSE;
2243 #endif
2244
2245
2246 /*
2247 * First determine where the whole part, if any, ends.
2248 *
2249 * ???? ???/??? xxx
2250 * ^ ^ ^
2251 * | | +--- tno_suffix
2252 * | +-------- tno_slash
2253 * +------------ tno_endwhole
2254 */
2255
2256 i = tno_slash - 1;
2257 /* Back up to digit. */
2258 while (i >= 0) {
2259 int t = GET_TOKEN (i).token;
2260 if (t == '0' || t == '#' || t == '?')
2261 break;
2262 i--;
2263 }
2264
2265 /* Back up to space. */
2266 while (i >= 0) {
2267 int t = GET_TOKEN (i).token;
2268 if (t == ' ')
2269 break;
2270 i--;
2271 }
2272 tno_endwhole = i;
2273
2274 /* Back up to digit. */
2275 while (i >= 0) {
2276 int t = GET_TOKEN (i).token;
2277 if (t == '0' || t == '#' || t == '?')
2278 break;
2279 i--;
2280 }
2281
2282 if (i < 0) {
2283 tno_endwhole = -1;
2284 inhibit_blank = TRUE;
2285 } else {
2286 for (i = tno_endwhole; i < tno_slash; i++) {
2287 int t = GET_TOKEN (i).token;
2288 if (t == '0')
2289 inhibit_blank = TRUE;
2290 }
2291 }
2292
2293 /* ---------------------------------------- */
2294
2295 i = tno_slash + 1;
2296 while (i < tno_end) {
2297 int t = GET_TOKEN (i).token;
2298 if (g_ascii_isdigit (t) || t == '#' || t == '?')
2299 break;
2300 i++;
2301 }
2302 if (i == tno_end)
2303 return NULL;
2304 tno_denom = i;
2305
2306 for (i = tno_denom; i < tno_end; i++) {
2307 int t = GET_TOKEN (i).token;
2308 if (g_ascii_isdigit (t)) {
2309 denominator = denominator * 10 + (t - '0');
2310 if (t != '0')
2311 explicit_denom = TRUE;
2312 }
2313 }
2314
2315 for (i = tno_denom; i < tno_end; i++) {
2316 const GOFormatParseItem *ti = &GET_TOKEN(i);
2317 if (ti->token == TOK_THOUSAND)
2318 return NULL;
2319 if (ti->token == TOK_CONDITION ||
2320 ti->token == TOK_LOCALE ||
2321 ti->token == TOK_COLOR)
2322 continue;
2323 if (!(ti->tt & TT_STARTS_NUMBER))
2324 break;
2325 if (explicit_denom && (ti->token == '?' || ti->token == '#'))
2326 return NULL;
2327 denominator_digits++;
2328 }
2329 pstate->force_zero_pos = i;
2330
2331 while (tno_suffix >= i) {
2332 int token = GET_TOKEN(tno_suffix - 1).token;
2333 if (token == '#' || token == '?' || g_ascii_isdigit (token))
2334 break;
2335 tno_suffix--;
2336 }
2337
2338 /* ---------------------------------------- */
2339
2340 prg = g_string_new (NULL);
2341
2342 #ifdef ALLOW_PI_SLASH
2343 if (pi_scale)
2344 ADD_OP (OP_NUM_FRACTION_SCALE_PI);
2345 #endif
2346
2347 ADD_OP3 (OP_NUM_FRACTION, tno_endwhole != -1, explicit_denom);
2348 if (explicit_denom)
2349 g_string_append_len (prg, (void*)&denominator, sizeof (denominator));
2350 else
2351 ADD_OP (MIN (10, denominator_digits));
2352
2353 ADD_OP (OP_NUM_VAL_SIGN);
2354
2355 if (tno_endwhole != -1) {
2356 ADD_OP (OP_NUM_FRACTION_WHOLE);
2357 if (!go_format_parse_number_new_1 (prg, pstate,
2358 0, tno_endwhole + 1,
2359 0, 1, &inhibit_blank_whole))
2360 return NULL;
2361 scale += pstate->scale;
2362
2363 #ifdef ALLOW_PI_SLASH
2364 if (pi_scale) {
2365 /* ADD_OPuc (OP_CHAR, UNICODE_THINSPACE); */
2366 ADD_OPuc (OP_CHAR, UNICODE_PI); /* "pi" */
2367 ADD_OP (OP_NUM_FRACTION_PI_SUM_START);
2368 ADD_OPuc (OP_CHAR, UNICODE_THINSPACE);
2369 ADD_OP (OP_NUM_FRACTION_SIGN);
2370 ADD_OPuc (OP_CHAR, UNICODE_THINSPACE);
2371 }
2372 #endif
2373 }
2374
2375
2376 ADD_OP (OP_NUM_DISABLE_THOUSANDS);
2377
2378 ADD_OP (OP_NUM_FRACTION_NOMINATOR);
2379 if (!go_format_parse_number_new_1 (prg, pstate,
2380 tno_endwhole + 1,
2381 pi_scale ? tno_slash - 2 :tno_slash,
2382 0, 2, &inhibit_blank_numerator))
2383 return NULL;
2384 scale += pstate->scale;
2385
2386 ADD_OP (OP_NUM_FRACTION_SLASH);
2387 if (pi_scale)
2388 ADD_OPuc (OP_CHAR, UNICODE_PI); /* "pi" */
2389 ADD_OP2 (OP_CHAR, '/');
2390
2391 pstate->explicit_denom = explicit_denom;
2392 ADD_OP (OP_NUM_FRACTION_DENOMINATOR);
2393 if (!go_format_parse_number_new_1 (prg, pstate,
2394 tno_slash + 1, tno_suffix,
2395 0, 3, &inhibit_blank_denom))
2396 return NULL;
2397 scale += pstate->scale;
2398 ADD_OP (OP_NUM_FRACTION_ALIGN);
2399 #ifdef ALLOW_PI_SLASH
2400 if (pi_scale) {
2401 if (!inhibit_blank)
2402 ADD_OP (OP_NUM_FRACTION_BLANK_PI);
2403 if (!inhibit_blank_denom && !inhibit_blank_whole)
2404 ADD_OP (OP_NUM_FRACTION_SIMPLIFY_PI);
2405 if (!inhibit_blank_numerator)
2406 ADD_OP (OP_NUM_FRACTION_SIMPLIFY_NUMERATOR_PI);
2407
2408 } else
2409 #endif
2410 {
2411 if (!inhibit_blank)
2412 ADD_OP (OP_NUM_FRACTION_BLANK);
2413 #ifdef ALLOW_DENOM_REMOVAL
2414 if (!inhibit_blank_denom && !inhibit_blank_whole)
2415 ADD_OP (OP_NUM_FRACTION_SIMPLIFY);
2416 if (!inhibit_blank_numerator)
2417 ADD_OP (OP_NUM_FRACTION_SIMPLIFY_NUMERATOR);
2418 #endif
2419 }
2420 if (!inhibit_blank_whole)
2421 ADD_OP (OP_NUM_FRACTION_BLANK_WHOLE);
2422
2423 for (i = tno_suffix; i < tno_end; i++) {
2424 const GOFormatParseItem *ti = &GET_TOKEN(i);
2425 if (ti->token == '%')
2426 scale += 2;
2427 handle_common_token (ti->tstr, ti->token, prg);
2428 }
2429
2430 if (scale) {
2431 guchar scaling[2] = { OP_NUM_SCALE, scale };
2432 g_string_insert_len (prg, 0, scaling, 2);
2433 }
2434
2435 handle_fill (prg, pstate);
2436
2437 fmt = go_format_create (GO_FMT_NUMBER, NULL);
2438 fmt->u.number.program = g_string_free (prg, FALSE);
2439 fmt->u.number.fraction = TRUE;
2440 fmt->u.number.scale_is_2 = (pstate->scale == 2);
2441 return fmt;
2442 }
2443
2444 static GOFormat *
go_format_parse(const char * str)2445 go_format_parse (const char *str)
2446 {
2447 GOFormat *fmt;
2448 const char *str0 = str;
2449 GOFormatCondition *conditions = NULL;
2450 int i, nparts = 0;
2451 gboolean has_text_format = FALSE;
2452
2453 #if 0
2454 g_printerr ("Parse: [%s]\n", str0);
2455 #endif
2456 while (1) {
2457 GOFormatCondition *condition;
2458 const char *tail;
2459 GOFormatParseState state;
2460 GOFormat *fmt = NULL;
2461 gboolean is_magic = FALSE;
2462 char *magic_fmt_str;
2463
2464 memset (&state, 0, sizeof (state));
2465 tail = go_format_preparse (str, &state, FALSE, FALSE);
2466 if (!tail) {
2467 g_array_free (state.tokens, TRUE);
2468 goto bail;
2469 }
2470
2471 nparts++;
2472 conditions = g_renew (GOFormatCondition, conditions, nparts);
2473 condition = conditions + (nparts - 1);
2474 *condition = state.cond;
2475 if (!state.have_cond)
2476 condition->implicit = TRUE;
2477
2478 magic_fmt_str = go_format_magic_fmt_str (state.locale.locale & 0xffffffff);
2479 if (magic_fmt_str) {
2480 is_magic = TRUE;
2481 /* Make the upcoming switch do nothing. */
2482 state.typ = GO_FMT_INVALID;
2483 fmt = go_format_parse_sequential (magic_fmt_str, NULL, &state);
2484 g_free (magic_fmt_str);
2485 }
2486
2487 switch (state.typ) {
2488 case GO_FMT_EMPTY:
2489 fmt = go_format_create (state.typ, NULL);
2490 break;
2491
2492 case GO_FMT_TEXT:
2493 fmt = go_format_parse_sequential (str, NULL, &state);
2494 break;
2495
2496 case GO_FMT_NUMBER:
2497 if (state.is_date || state.has_general)
2498 fmt = go_format_parse_sequential (str, NULL, &state);
2499 else if (state.tno_E >= 0)
2500 fmt = go_format_parse_number_E (&state);
2501 else if (state.tno_slash >= 0)
2502 fmt = go_format_parse_number_fraction (&state);
2503 else if (state.is_number) {
2504 fmt = go_format_parse_number_plain (&state);
2505 } else {
2506 GString *prg = g_string_new (NULL);
2507 /* Crazy number. Sign only. */
2508 ADD_OP (OP_NUM_VAL_SIGN);
2509 fmt = go_format_parse_sequential (str, prg, &state);
2510 }
2511 break;
2512
2513 default:
2514 ; /* Nothing */
2515 }
2516 g_array_free (state.tokens, TRUE);
2517 if (!fmt)
2518 goto bail;
2519
2520 condition->fmt = fmt;
2521 fmt->format = g_strndup (str, tail - str);
2522 fmt->has_fill = state.fill_char != 0;
2523 fmt->color = state.color;
2524 if (is_magic) fmt->magic = state.locale.locale;
2525
2526 if (go_format_is_text (fmt)) {
2527 /* Only one text format. */
2528 if (has_text_format)
2529 goto bail;
2530 has_text_format = TRUE;
2531 if (condition->implicit)
2532 condition->op = GO_FMT_COND_TEXT;
2533 }
2534
2535 str = tail;
2536 if (*str == 0)
2537 break;
2538 str++;
2539 }
2540
2541 if (nparts == 1 && conditions[0].implicit) {
2542 /* Simple. */
2543 fmt = conditions[0].fmt;
2544 g_free (conditions);
2545 } else {
2546 int i;
2547
2548 fmt = go_format_create (GO_FMT_COND, str0);
2549 fmt->u.cond.n = nparts;
2550 fmt->u.cond.conditions = conditions;
2551
2552 for (i = 0; i < nparts; i++) {
2553 gboolean no_zero_format =
2554 (nparts <= 2 ||
2555 conditions[2].op != GO_FMT_COND_NONE);
2556 gboolean negative_explicit =
2557 (nparts >= 2 &&
2558 conditions[1].op != GO_FMT_COND_NONE);
2559 static const GOFormatConditionOp ops[4] = {
2560 GO_FMT_COND_GT,
2561 GO_FMT_COND_LT,
2562 GO_FMT_COND_EQ,
2563 GO_FMT_COND_TEXT
2564 };
2565 GOFormatCondition *cond = conditions + i;
2566 if (i < 4 && cond->op == GO_FMT_COND_NONE) {
2567 cond->implicit = TRUE;
2568 cond->val = 0;
2569 if (i == 0 && no_zero_format && !negative_explicit)
2570 cond->op = GO_FMT_COND_GE;
2571 else if (i == 1 && no_zero_format) {
2572 if (!conditions[0].implicit &&
2573 conditions[0].true_inhibits_minus)
2574 conditions[0].false_inhibits_minus = TRUE;
2575 cond->op = GO_FMT_COND_NONTEXT;
2576 } else
2577 cond->op = ops[i];
2578 determine_inhibit_minus (cond);
2579 }
2580 #ifdef OBSERVE_XL_CONDITION_LIMITS
2581 if (i >= 2 && !cond->implicit) {
2582 go_format_unref (fmt);
2583 nparts = 0;
2584 conditions = NULL;
2585 goto bail;
2586 }
2587 #endif
2588 }
2589 }
2590
2591 return fmt;
2592
2593 bail:
2594 for (i = 0; i < nparts; i++)
2595 go_format_unref (conditions[i].fmt);
2596 g_free (conditions);
2597 return go_format_create (GO_FMT_INVALID, str0);
2598 }
2599
2600 #undef ADD_OP
2601 #undef ADD_OP2
2602 #undef ADD_OP3
2603
2604 #ifdef DEBUG_PROGRAMS
2605 #define REGULAR(op) case op: g_printerr ("%s\n", #op); break
2606 static void
go_format_dump_program(const guchar * prg)2607 go_format_dump_program (const guchar *prg)
2608 {
2609 const guchar *next;
2610 size_t len;
2611
2612 while (1) {
2613 GOFormatOp op = *prg++;
2614
2615 switch (op) {
2616 case OP_END:
2617 g_printerr ("OP_END\n");
2618 return;
2619 case OP_CHAR:
2620 next = g_utf8_next_char (prg);
2621 len = next - prg;
2622 g_printerr ("OP_CHAR '%-.*s'\n",
2623 (int)len, prg);
2624 prg = next;
2625 break;
2626 case OP_CHAR_INVISIBLE:
2627 next = g_utf8_next_char (prg);
2628 len = next - prg;
2629 g_printerr ("OP_CHAR_INVISIBLE '%-.*s'\n",
2630 (int)len, prg);
2631 prg = next;
2632 break;
2633 case OP_STRING:
2634 len = strlen (prg);
2635 g_printerr ("OP_STRING \"%s\"\n", prg);
2636 prg += len + 1;
2637 break;
2638 case OP_FILL:
2639 next = g_utf8_next_char (prg);
2640 len = next - prg;
2641 g_printerr ("OP_FILL '%-.*s'\n", (int)len, prg);
2642 prg = next;
2643 break;
2644 case OP_LOCALE: {
2645 GOFormatLocale locale;
2646 const char *lang;
2647 memcpy (&locale, prg, sizeof (locale));
2648 prg += sizeof (locale);
2649 lang = (const char *)prg;
2650 prg += strlen (lang) + 1;
2651 g_printerr ("OP_LOCALE -- \"%s\" -- numeral shape: %#x -- calendar: %#x\n",
2652 lang,
2653 (guint)((locale.locale & 0xFFF000000) >> 24),
2654 (guint)((locale.locale & 0x000FF0000) >> 16));
2655 break;
2656 }
2657 case OP_NUMERAL_SHAPE:
2658 g_printerr ("OP_NUMERAL_SHAPE flags:%#x shape:%#x\n", prg[0], prg[1]);
2659 prg += 2;
2660 break;
2661 case OP_DATE_ROUND:
2662 g_printerr ("OP_DATE_ROUND %d %d\n", prg[0], prg[1]);
2663 prg += 2;
2664 break;
2665 case OP_TIME_HOUR_N:
2666 g_printerr ("OP_TIME_HOUR_N %d\n", prg[0]);
2667 prg += 1;
2668 break;
2669 case OP_TIME_AP:
2670 g_printerr ("OP_TIME_AP '%c' '%c'\n", prg[0], prg[1]);
2671 prg += 2;
2672 break;
2673 case OP_TIME_MINUTE_N:
2674 g_printerr ("OP_TIME_MINUTE_N %d\n", prg[0]);
2675 prg += 1;
2676 break;
2677 case OP_TIME_SECOND_N:
2678 g_printerr ("OP_TIME_SECOND_N %d\n", prg[0]);
2679 prg += 1;
2680 break;
2681 case OP_NUM_SCALE:
2682 g_printerr ("OP_NUM_SCALE %d\n", (signed char)(prg[0]));
2683 prg += 1;
2684 break;
2685 case OP_NUM_PRINTF_E:
2686 g_printerr ("OP_NUM_PRINTF_E %d %d\n", prg[0], prg[1]);
2687 prg += 2;
2688 break;
2689 case OP_NUM_PRINTF_F:
2690 g_printerr ("OP_NUM_PRINTF_F %d\n", prg[0]);
2691 prg += 1;
2692 break;
2693 case OP_NUM_DIGIT_1:
2694 g_printerr ("OP_NUM_DIGIT_1 '%c'\n", prg[0]);
2695 prg += 1;
2696 break;
2697 case OP_NUM_DECIMAL_1:
2698 g_printerr ("OP_NUM_DECIMAL_1 '%c'\n", prg[0]);
2699 prg += 1;
2700 break;
2701 case OP_NUM_DIGIT_1_0:
2702 g_printerr ("OP_NUM_DIGIT_1_0 '%c'\n", prg[0]);
2703 prg += 1;
2704 break;
2705 case OP_NUM_EXPONENT_SIGN: /* forced-p */
2706 g_printerr ("OP_NUM_EXPONENT_SIGN %d\n", prg[0]);
2707 prg += 1;
2708 break;
2709 case OP_NUM_FRACTION: {
2710 gboolean wp = *prg++;
2711 gboolean explicit_denom = *prg++;
2712
2713 if (explicit_denom) {
2714 double plaind; /* Plain double */
2715 memcpy (&plaind, prg, sizeof (plaind));
2716 prg += sizeof (plaind);
2717
2718 g_printerr ("OP_NUM_FRACTION %d %d %g\n", wp, explicit_denom, plaind);
2719 } else {
2720 int digits = *prg++;
2721
2722 g_printerr ("OP_NUM_FRACTION %d %d %d\n", wp, explicit_denom, digits);
2723 }
2724 break;
2725 }
2726
2727 REGULAR(OP_CHAR_REPEAT);
2728 REGULAR(OP_DATE_SPLIT);
2729 REGULAR(OP_DATE_YEAR);
2730 REGULAR(OP_DATE_YEAR_2);
2731 REGULAR(OP_DATE_YEAR_THAI);
2732 REGULAR(OP_DATE_YEAR_THAI_2);
2733 REGULAR(OP_DATE_MONTH);
2734 REGULAR(OP_DATE_MONTH_2);
2735 REGULAR(OP_DATE_MONTH_NAME);
2736 REGULAR(OP_DATE_MONTH_NAME_1);
2737 REGULAR(OP_DATE_MONTH_NAME_3);
2738 REGULAR(OP_DATE_DAY);
2739 REGULAR(OP_DATE_DAY_2);
2740 REGULAR(OP_DATE_WEEKDAY);
2741 REGULAR(OP_DATE_WEEKDAY_3);
2742 REGULAR(OP_TIME_SPLIT_24);
2743 REGULAR(OP_TIME_SPLIT_12);
2744 REGULAR(OP_TIME_SPLIT_ELAPSED_HOUR);
2745 REGULAR(OP_TIME_SPLIT_ELAPSED_MINUTE);
2746 REGULAR(OP_TIME_SPLIT_ELAPSED_SECOND);
2747 REGULAR(OP_TIME_HOUR);
2748 REGULAR(OP_TIME_HOUR_2);
2749 REGULAR(OP_TIME_AMPM);
2750 REGULAR(OP_TIME_MINUTE);
2751 REGULAR(OP_TIME_MINUTE_2);
2752 REGULAR(OP_TIME_SECOND);
2753 REGULAR(OP_TIME_SECOND_2);
2754 REGULAR(OP_TIME_SECOND_DECIMAL_START);
2755 REGULAR(OP_TIME_SECOND_DECIMAL_DIGIT);
2756 REGULAR(OP_NUM_ENABLE_THOUSANDS);
2757 REGULAR(OP_NUM_DISABLE_THOUSANDS);
2758 REGULAR(OP_NUM_SIGN);
2759 REGULAR(OP_NUM_VAL_SIGN);
2760 REGULAR(OP_NUM_FRACTION_SIGN);
2761 REGULAR(OP_NUM_MOVETO_ONES);
2762 REGULAR(OP_NUM_MOVETO_DECIMALS);
2763 REGULAR(OP_NUM_REST_WHOLE);
2764 REGULAR(OP_NUM_APPEND_MODE);
2765 REGULAR(OP_NUM_DECIMAL_POINT);
2766 REGULAR(OP_NUM_DENUM_DIGIT_Q);
2767 REGULAR(OP_NUM_EXPONENT_1);
2768 REGULAR(OP_NUM_VAL_EXPONENT);
2769 REGULAR(OP_NUM_STORE_POS);
2770 #ifdef ALLOW_EE_MARKUP
2771 REGULAR(OP_NUM_MARK_MANTISSA);
2772 REGULAR(OP_NUM_SIMPLIFY_MANTISSA);
2773 REGULAR(OP_NUM_SIMPLIFY_MARKUP_MANTISSA);
2774 REGULAR(OP_MARKUP_SUPERSCRIPT_START);
2775 REGULAR(OP_MARKUP_SUPERSCRIPT_END);
2776 #endif
2777 #ifdef ALLOW_SI_APPEND
2778 REGULAR(OP_NUM_SIMPLIFY_MANTISSA_SI);
2779 REGULAR(OP_NUM_REDUCE_EXPONENT_SI);
2780 REGULAR(OP_NUM_SIMPLIFY_EXPONENT_SI);
2781 REGULAR(OP_NUM_SI_EXPONENT);
2782 #endif
2783 REGULAR(OP_NUM_FRACTION_WHOLE);
2784 REGULAR(OP_NUM_FRACTION_NOMINATOR);
2785 REGULAR(OP_NUM_FRACTION_DENOMINATOR);
2786 REGULAR(OP_NUM_FRACTION_BLANK);
2787 REGULAR(OP_NUM_FRACTION_BLANK_WHOLE);
2788 REGULAR(OP_NUM_FRACTION_ALIGN);
2789 REGULAR(OP_NUM_FRACTION_SLASH);
2790 #ifdef ALLOW_DENOM_REMOVAL
2791 REGULAR(OP_NUM_FRACTION_SIMPLIFY);
2792 REGULAR(OP_NUM_FRACTION_SIMPLIFY_NUMERATOR);
2793 #endif
2794 #ifdef ALLOW_PI_SLASH
2795 REGULAR(OP_NUM_FRACTION_BLANK_PI);
2796 REGULAR(OP_NUM_FRACTION_SCALE_PI);
2797 REGULAR(OP_NUM_FRACTION_SIMPLIFY_PI);
2798 REGULAR(OP_NUM_FRACTION_SIMPLIFY_NUMERATOR_PI);
2799 REGULAR(OP_NUM_FRACTION_PI_SUM_START);
2800 #endif
2801 REGULAR(OP_NUM_GENERAL_MARK);
2802 REGULAR(OP_NUM_GENERAL_DO);
2803 REGULAR(OP_STR_APPEND_SVAL);
2804
2805 default:
2806 g_printerr ("???\n");
2807 return;
2808 }
2809 }
2810 }
2811 #undef REGULAR
2812 #endif
2813
2814 static void
append_i2(GString * dst,int i)2815 append_i2 (GString *dst, int i)
2816 {
2817 g_string_append_printf (dst, "%02d", i);
2818 }
2819
2820 static void
append_i(GString * dst,int i)2821 append_i (GString *dst, int i)
2822 {
2823 g_string_append_printf (dst, "%d", i);
2824 }
2825
2826 #define SETUP_LAYOUT do { if (layout) pango_layout_set_text (layout, str->str, -1); } while (0)
2827
2828 static void
fill_with_char(GString * str,PangoLayout * layout,gsize fill_pos,gunichar fill_char,GOFormatMeasure measure,int col_width)2829 fill_with_char (GString *str, PangoLayout *layout, gsize fill_pos,
2830 gunichar fill_char,
2831 GOFormatMeasure measure, int col_width)
2832 {
2833 int w, w1, wbase;
2834 gsize n, gap;
2835 char fill_utf8[7];
2836 gsize fill_utf8_len;
2837
2838 SETUP_LAYOUT;
2839 wbase = measure (str, layout);
2840 if (wbase >= col_width)
2841 return;
2842
2843 fill_utf8_len = g_unichar_to_utf8 (fill_char, fill_utf8);
2844
2845 g_string_insert_len (str, fill_pos, fill_utf8, fill_utf8_len);
2846 SETUP_LAYOUT;
2847 w = measure (str, layout);
2848 w1 = w - wbase;
2849 if (w > col_width || w1 <= 0) {
2850 g_string_erase (str, fill_pos, fill_utf8_len);
2851 return;
2852 }
2853
2854 n = (col_width - w) / w1;
2855 if (n == 0)
2856 return;
2857
2858 gap = n * fill_utf8_len;
2859 g_string_set_size (str, str->len + gap);
2860 memmove (str->str + fill_pos + gap,
2861 str->str + fill_pos,
2862 str->len - (fill_pos + gap));
2863 while (n > 0) {
2864 memcpy (str->str + fill_pos, fill_utf8, fill_utf8_len);
2865 fill_pos += fill_utf8_len;
2866 n--;
2867 }
2868 }
2869
2870 #endif
2871
2872 static void
SUFFIX(printf_engineering)2873 SUFFIX(printf_engineering) (GString *dst, DOUBLE val, int n, int wd)
2874 {
2875 int exponent_guess = 0;
2876 int exponent;
2877 int nde = 0;
2878 char *epos;
2879 char *dot;
2880 const GString *decimal = go_locale_get_decimal ();
2881
2882 if (wd <= 1 || val == 0 || !SUFFIX(go_finite) (val)) {
2883 go_dtoa (dst, "=^.*" FORMAT_E, n, val);
2884 return;
2885 }
2886
2887 exponent_guess = (int)floor (SUFFIX(log10) (SUFFIX(fabs) (val)));
2888 /* Extra digits we need assuming guess correct */
2889 nde = (exponent_guess >= 0)
2890 ? exponent_guess % wd
2891 : (wd - ((-exponent_guess) % wd)) % wd;
2892
2893 go_dtoa (dst, "=^.*" FORMAT_E, n + nde, val);
2894 epos = (char *)strchr (dst->str, 'E');
2895 if (!epos)
2896 return;
2897
2898 exponent = atoi (epos + 1);
2899 g_string_truncate (dst, epos - dst->str);
2900 dot = (char *)strstr (dst->str, decimal->str);
2901 if (exponent != exponent_guess) {
2902 /*
2903 * We rounded from 9.99Exx to
2904 * 1.00Eyy
2905 * with yy=xx+1.
2906 */
2907 nde = (nde + 1) % wd;
2908 if (nde == 0)
2909 g_string_truncate (dst, dst->len - (wd - 1));
2910 else if (dot) /* only add a 0 when a decimal separator is present,
2911 * see #785669 */
2912 g_string_append_c (dst, '0');
2913 }
2914
2915 /* we need to adjust exponent before any modification to nde, see #785669 */
2916 exponent -= nde;
2917 if (dot) {
2918 memmove (dot, dot + decimal->len, nde);
2919 memcpy (dot + nde, decimal->str, decimal->len);
2920 } else {
2921 while (nde > 0) {
2922 g_string_append_c (dst, '0');
2923 nde--;
2924 }
2925 }
2926
2927 g_string_append_printf (dst, "E%+d", exponent);
2928 }
2929
2930 #ifdef DEFINE_COMMON
2931 static int
go_format_get_width(GString * dst,PangoAttrList * attrs,int start,int length,PangoLayout * layout)2932 go_format_get_width (GString *dst, PangoAttrList *attrs, int start,
2933 int length, PangoLayout *layout)
2934 {
2935 GList *plist, *l;
2936 PangoContext *context;
2937 int width = 0;
2938
2939 if (layout == NULL ||
2940 (context = pango_layout_get_context (layout)) == NULL
2941 || pango_context_get_font_map (context) == NULL)
2942 return 0;
2943
2944 plist = pango_itemize (context, dst->str, start, length, attrs, NULL);
2945 for (l = plist; l != NULL; l = l->next) {
2946 PangoItem *pi = l->data;
2947 PangoGlyphString *glyphs = pango_glyph_string_new ();
2948
2949 pango_shape (dst->str + pi->offset, pi->length, &pi->analysis, glyphs);
2950 width += pango_glyph_string_get_width (glyphs);
2951 pango_glyph_string_free (glyphs);
2952 }
2953 g_list_free_full (plist, (GDestroyNotify) pango_item_free);
2954 return width;
2955 }
2956 #endif
2957
2958 #ifdef DEFINE_COMMON
2959 static int
go_format_desired_width(PangoLayout * layout,PangoAttrList * attrs,int digits)2960 go_format_desired_width (PangoLayout *layout, PangoAttrList *attrs, int digits)
2961 {
2962 char str[2] = {'0',0};
2963 const gchar *strp = &(str[0]);
2964 GList *plist, *l;
2965 int width = 0;
2966 PangoContext *context;
2967
2968 if (layout == NULL ||
2969 (context = pango_layout_get_context (layout)) == NULL
2970 || pango_context_get_font_map (context) == NULL)
2971 return 0;
2972
2973 plist = pango_itemize (context, strp, 0, 1, attrs, NULL);
2974 for (l = plist; l != NULL; l = l->next) {
2975 PangoItem *pi = l->data;
2976 PangoGlyphString *glyphs = pango_glyph_string_new ();
2977 PangoRectangle ink_rect;
2978 PangoRectangle logical_rect;
2979
2980 pango_shape (strp + pi->offset, pi->length, &pi->analysis, glyphs);
2981 pango_glyph_string_extents (glyphs,
2982 pi->analysis.font,
2983 &ink_rect,
2984 &logical_rect);
2985 pango_glyph_string_free (glyphs);
2986 width += logical_rect.width;
2987 }
2988 g_list_free_full (plist, (GDestroyNotify) pango_item_free);
2989
2990 return (int)(1.1 * width *digits);
2991 }
2992 #endif
2993
2994
2995 #ifdef DEFINE_COMMON
2996 static void
blank_characters(GString * dst,PangoAttrList * attrs,int start,int length,PangoLayout * layout)2997 blank_characters (GString *dst, PangoAttrList *attrs, int start, int length,
2998 PangoLayout *layout)
2999 {
3000 /* We have layouts that have no fontmap set, we need to avoid them */
3001 if (layout && pango_context_get_font_map (pango_layout_get_context (layout))) {
3002 int full_width, short_width;
3003 PangoAttribute *attr;
3004 PangoAttrList *new_attrs = pango_attr_list_new ();
3005 PangoRectangle logical_rect = {0, 0, 0, 2 * PANGO_SCALE};
3006
3007 pango_layout_set_text (layout, dst->str, -1);
3008 pango_layout_set_attributes (layout, attrs);
3009 full_width = go_format_measure_pango (NULL, layout);
3010 g_string_erase (dst, start, length);
3011 go_pango_attr_list_erase (attrs, start, length);
3012 pango_layout_set_text (layout, dst->str, -1);
3013 pango_layout_set_attributes (layout, attrs);
3014 short_width = go_format_measure_pango (NULL, layout);
3015 logical_rect.width = full_width - short_width;
3016 g_string_insert_c (dst, start, ' ');
3017 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
3018 attr->start_index = 0;
3019 attr->end_index = 1;
3020 pango_attr_list_insert (new_attrs, attr);
3021 pango_attr_list_splice (attrs, new_attrs, start, 1);
3022 pango_attr_list_unref (new_attrs);
3023 } else
3024 memset (dst->str + start, ' ', length);
3025 }
3026 #endif
3027
3028 #ifdef DEFINE_COMMON
3029 static int
cnt_digits(int d)3030 cnt_digits (int d)
3031 {
3032 int cnt = 0;
3033
3034 while (d > 0) {
3035 cnt++;
3036 d /= 10;
3037 }
3038
3039 return cnt;
3040 }
3041 #endif
3042
3043 #if defined(ALLOW_SI_APPEND) && defined (DEFINE_COMMON)
3044 static int
si_reduction(int exponent,char const ** si)3045 si_reduction (int exponent, char const **si)
3046 {
3047 static struct {
3048 char const *prefix;
3049 int power;
3050 } si_prefixes[] = {
3051 {"Y" , 24},
3052 {"Z" , 21},
3053 {"E" , 18},
3054 {"P" , 15},
3055 {"T" , 12},
3056 {"G" , 9},
3057 {"M" , 6},
3058 {"k" , 3},
3059 {"h" , 2},
3060 {"da" , 1},
3061 {"" , 0},
3062 {"d" , -1},
3063 {"c" , -2},
3064 {"m" , -3},
3065 {"\302\265" , -6},
3066 {"n" , -9},
3067 {"p" ,-12},
3068 {"f" ,-15},
3069 {"a" ,-18},
3070 {"z" ,-21},
3071 {"y" ,-24}
3072 };
3073 guint i;
3074
3075 for (i = 0; i < G_N_ELEMENTS (si_prefixes) - 1; i++)
3076 if (si_prefixes[i].power <= exponent)
3077 break;
3078
3079 *si = si_prefixes[i].prefix;
3080 return si_prefixes[i].power;
3081 }
3082
3083 #endif
3084
3085 #ifdef DEFINE_COMMON
3086 static char const *minus_shapes[] =
3087 {
3088 UTF8_MINUS, /* 00 Unused */
3089 UTF8_MINUS, /* 01 Unused */
3090 UTF8_MINUS, /* 02 Arabic Indic */
3091 UTF8_MINUS, /* 03 Extended Arabic Indic */
3092 UTF8_MINUS, /* 04 Devanagari */
3093 UTF8_MINUS, /* 05 Bengali */
3094 UTF8_MINUS, /* 06 Gurmukhi */
3095 UTF8_MINUS, /* 07 Gujarati */
3096 UTF8_MINUS, /* 08 Orija */
3097 UTF8_MINUS, /* 09 Tamil */
3098 UTF8_MINUS, /* 0A Telugu */
3099 UTF8_MINUS, /* 0B Kannada*/
3100 UTF8_MINUS, /* 0C Malayalam*/
3101 UTF8_MINUS, /* 0D Thai */
3102 UTF8_MINUS, /* 0E Lao */
3103 UTF8_MINUS, /* 0F Tibetan */
3104 UTF8_MINUS, /* 10 Myanmar */
3105 UTF8_MINUS, /* 11 Ethiopic ? Not really a decimal system */
3106 UTF8_MINUS, /* 12 Khmer */
3107 UTF8_MINUS, /* 13 Mongolian */
3108 UTF8_MINUS, /* 14 Unused */
3109 UTF8_MINUS, /* 15 Unused */
3110 UTF8_MINUS, /* 16 Unused */
3111 UTF8_MINUS, /* 17 Unused */
3112 UTF8_MINUS, /* 18 Unused */
3113 UTF8_MINUS, /* 19 Unused */
3114 UTF8_MINUS, /* 1A Unused */
3115 UTF8_FULLWIDTH_MINUS, /* 1B Japanese 1 ? */
3116 UTF8_FULLWIDTH_MINUS, /* 1C Japanese 2 ? */
3117 UTF8_FULLWIDTH_MINUS, /* 1D Japanese 3 ? */
3118 "\350\264\237", /* 1E Simplified Chinese 1 */
3119 "\350\264\237", /* 1F Simplified Chinese 2 */
3120 UTF8_FULLWIDTH_MINUS, /* 20 Simplified Chinese 3 */
3121 "\350\262\240", /* 21 Traditional Chinese 1 */
3122 "\350\262\240", /* 22 Traditional Chinese 2 */
3123 UTF8_FULLWIDTH_MINUS, /* 23 Traditional Chinese 3 */
3124 UTF8_FULLWIDTH_MINUS, /* 24 Korean 1 ? */
3125 UTF8_FULLWIDTH_MINUS, /* 25 Korean 2 ? */
3126 UTF8_FULLWIDTH_MINUS, /* 26 Korean 3 ? */
3127 UTF8_FULLWIDTH_MINUS, /* 27 Korean 4 ? */
3128 };
3129
3130 static char const *plus_shapes[] =
3131 {
3132 "+", /* 00 Unused */
3133 "+", /* 01 Unused */
3134 "+", /* 02 Arabic Indic */
3135 "+", /* 03 Extended Arabic Indic */
3136 "+", /* 04 Devanagari */
3137 "+", /* 05 Bengali */
3138 "+", /* 06 Gurmukhi */
3139 "+", /* 07 Gujarati */
3140 "+", /* 08 Orija */
3141 "+", /* 09 Tamil */
3142 "+", /* 0A Telugu */
3143 "+", /* 0B Kannada*/
3144 "+", /* 0C Malayalam*/
3145 "+", /* 0D Thai */
3146 "+", /* 0E Lao */
3147 "+", /* 0F Tibetan */
3148 "+", /* 10 Myanmar */
3149 "+", /* 11 Ethiopic ? Not really a decimal system */
3150 "+", /* 12 Khmer */
3151 "+", /* 13 Mongolian */
3152 "+", /* 14 Unused */
3153 "+", /* 15 Unused */
3154 "+", /* 16 Unused */
3155 "+", /* 17 Unused */
3156 "+", /* 18 Unused */
3157 "+", /* 19 Unused */
3158 "+", /* 1A Unused */
3159 UTF8_FULLWIDTH_PLUS, /* 1B Japanese 1 ? */
3160 UTF8_FULLWIDTH_PLUS, /* 1C Japanese 2 ? */
3161 UTF8_FULLWIDTH_PLUS, /* 1D Japanese 3 ? */
3162 UTF8_FULLWIDTH_PLUS, /* 1E Simplified Chinese 1 */
3163 UTF8_FULLWIDTH_PLUS, /* 1F Simplified Chinese 2 */
3164 UTF8_FULLWIDTH_PLUS, /* 20 Simplified Chinese 3 */
3165 UTF8_FULLWIDTH_PLUS, /* 21 Traditional Chinese 1 */
3166 UTF8_FULLWIDTH_PLUS, /* 22 Traditional Chinese 2 */
3167 UTF8_FULLWIDTH_PLUS, /* 23 Traditional Chinese 3 */
3168 UTF8_FULLWIDTH_PLUS, /* 24 Korean 1 ? */
3169 UTF8_FULLWIDTH_PLUS, /* 25 Korean 2 ? */
3170 UTF8_FULLWIDTH_PLUS, /* 26 Korean 3 ? */
3171 UTF8_FULLWIDTH_PLUS, /* 27 Korean 4 ? */
3172 };
3173 #endif
3174
3175
3176 #ifdef DEFINE_COMMON
3177
3178 static char const *numeral_shapes[][10]
3179 = {{NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, /* 00 Unused */
3180 {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, /* 01 Unused */
3181 {"\331\240","\331\241","\331\242","\331\243","\331\244","\331\245","\331\246",
3182 "\331\247","\331\250","\331\251"}, /* 02 Arabic Indic */
3183 {"\333\260","\333\261","\333\262","\333\263","\333\264","\333\265","\333\266",
3184 "\333\267","\333\270","\333\271"}, /* 03 Extended Arabic Indic */
3185 {"\340\245\246","\340\245\247","\340\245\250","\340\245\251","\340\245\252",
3186 "\340\245\253","\340\245\254","\340\245\255","\340\245\256","\340\245\257"}, /* 04 Devanagari */
3187 {"\340\247\246","\340\247\247","\340\247\250","\340\247\251","\340\247\252",
3188 "\340\247\253","\340\247\254","\340\247\255","\340\247\256","\340\247\257",}, /* 05 Bengali */
3189 {"\340\251\246","\340\251\247","\340\251\250","\340\251\251","\340\251\252",
3190 "\340\251\253","\340\251\254","\340\251\255","\340\251\256","\340\251\257"}, /* 06 Gurmukhi */
3191 {"\340\253\246","\340\253\247","\340\253\250","\340\253\251","\340\253\252",
3192 "\340\253\253","\340\253\254","\340\253\255","\340\253\256","\340\253\257"}, /* 07 Gujarati */
3193 {"\340\255\246","\340\255\247","\340\255\250","\340\255\251","\340\255\252",
3194 "\340\255\253","\340\255\254","\340\255\255","\340\255\256","\340\255\257"}, /* 08 Orija */
3195 {"\340\257\246","\340\257\247","\340\257\250","\340\257\251","\340\257\252",
3196 "\340\257\253","\340\257\254","\340\257\255","\340\257\256","\340\257\257"}, /* 09 Tamil */
3197 {"\340\261\246","\340\261\247","\340\261\250","\340\261\251","\340\261\252",
3198 "\340\261\253","\340\261\254","\340\261\255","\340\261\256","\340\261\257"}, /* 0A Telugu */
3199 {"\340\263\246","\340\263\247","\340\263\250","\340\263\251","\340\263\252",
3200 "\340\263\253","\340\263\254","\340\263\255","\340\263\256","\340\263\257"}, /* 0B Kannada*/
3201 {"\340\265\246","\340\265\247","\340\265\250","\340\265\251","\340\265\252",
3202 "\340\265\253","\340\265\254","\340\265\255","\340\265\256","\340\265\257"}, /* 0C Malayalam*/
3203 {"\340\271\220","\340\271\221","\340\271\222","\340\271\223","\340\271\224",
3204 "\340\271\225","\340\271\226","\340\271\227","\340\271\230","\340\271\231"}, /* 0D Thai */
3205 {"\340\273\220","\340\273\221","\340\273\222","\340\273\223","\340\273\224",
3206 "\340\273\225","\340\273\226","\340\273\227","\340\273\230","\340\273\231"}, /* 0E Lao */
3207 {"\340\274\240","\340\274\241","\340\274\242","\340\274\243","\340\274\244",
3208 "\340\274\245","\340\274\246","\340\274\247","\340\274\250","\340\274\251",}, /* 0F Tibetan */
3209 {"\341\201\200","\341\201\201","\341\201\202","\341\201\203","\341\201\204",
3210 "\341\201\205","\341\201\206","\341\201\207","\341\201\210","\341\201\211"}, /* 10 Myanmar */
3211 {"0","\341\215\251","\341\215\252","\341\215\253","\341\215\254",
3212 "\341\215\255","\341\215\256","\341\215\257","\341\215\260","\341\215\261"}, /* 11 Ethiopic Not really a decimal system */
3213 {"\341\237\240","\341\237\241","\341\237\242","\341\237\243","\341\237\244",
3214 "\341\237\245","\341\237\246","\341\237\247","\341\237\250","\341\237\251"}, /* 12 Khmer */
3215 {"\341\240\220","\341\240\221","\341\240\222","\341\240\223","\341\240\224",
3216 "\341\240\225","\341\240\226","\341\240\227","\341\240\230","\341\240\231"}, /* 13 Mongolian */
3217 {"0","1","2","3","4","5","6","7","8","9"}, /* 14 Unused */
3218 {"0","1","2","3","4","5","6","7","8","9"}, /* 15 Unused */
3219 {"0","1","2","3","4","5","6","7","8","9"}, /* 16 Unused */
3220 {"0","1","2","3","4","5","6","7","8","9"}, /* 17 Unused */
3221 {"0","1","2","3","4","5","6","7","8","9"}, /* 18 Unused */
3222 {"0","1","2","3","4","5","6","7","8","9"}, /* 19 Unused */
3223 {"0","1","2","3","4","5","6","7","8","9"}, /* 1A Unused */
3224 {"\343\200\207","\344\270\200","\344\272\214","\344\270\211","\345\233\233",
3225 "\344\272\224","\345\205\255","\344\270\203","\345\205\253","\344\271\235"}, /* 1B Japanese 1 (same as Traditional Chinese 1) */
3226 {"\343\200\207","\345\243\261","\345\274\220","\345\217\202","\345\233\233",
3227 "\344\274\215","\345\205\255","\344\270\203","\345\205\253","\344\271\235"}, /* 1C Japanese 2 */
3228 {"\357\274\220","\357\274\221","\357\274\222","\357\274\223","\357\274\224",
3229 "\357\274\225","\357\274\226","\357\274\227","\357\274\230","\357\274\231"}, /* 1D Japanese 3 (same as Traditional Chinese 3) */
3230 /* see http://www.w3.org/TR/css3-lists/ */
3231 {"\351\233\266","\344\270\200","\344\272\214","\344\270\211","\345\233\233",
3232 "\344\272\224","\345\205\255","\344\270\203","\345\205\253","\344\271\235"}, /* 1E Simplified Chinese 1 */
3233 {"\351\233\266","\345\243\271","\350\264\260","\345\217\201","\350\202\206",
3234 "\344\274\215","\351\231\206","\346\237\222","\346\215\214","\347\216\226"}, /* 1F Simplified Chinese 2 */
3235 {"\357\274\220","\357\274\221","\357\274\222","\357\274\223","\357\274\224",
3236 "\357\274\225","\357\274\226","\357\274\227","\357\274\230","\357\274\231"}, /* 20 Simplified Chinese 3 */
3237 {"\351\233\266","\344\270\200","\344\272\214","\344\270\211","\345\233\233",
3238 "\344\272\224","\345\205\255","\344\270\203","\345\205\253","\344\271\235"}, /* 21 Traditional Chinese 1 */
3239 {"\351\233\266","\345\243\271","\350\262\263","\345\217\203","\350\202\206",
3240 "\344\274\215","\351\231\270","\346\237\222","\346\215\214","\347\216\226"}, /* 22 Traditional Chinese 2 */
3241 {"\357\274\220","\357\274\221","\357\274\222","\357\274\223","\357\274\224",
3242 "\357\274\225","\357\274\226","\357\274\227","\357\274\230","\357\274\231"}, /* 23 Traditional Chinese 3 */
3243 {"\343\200\207","\344\270\200","\344\272\214","\344\270\211","\345\233\233",
3244 "\344\272\224","\345\205\255","\344\270\203","\345\205\253","\344\271\235"}, /* 24 Korean 1 (same as Traditional Chinese 1) */
3245 {"\351\233\266","\345\243\271","\350\262\263","\345\217\203","\345\233\233",
3246 "\344\274\215","\345\205\255","\344\270\203","\345\205\253","\344\271\235"}, /* 25 Korean 2 */
3247 {"\357\274\220","\357\274\221","\357\274\222","\357\274\223","\357\274\224",
3248 "\357\274\225","\357\274\226","\357\274\227","\357\274\230","\357\274\231"}, /* 26 Korean 3 (same as Traditional Chinese 3) */
3249 {"\354\230\201","\354\235\274","\354\235\264","\354\202\274","\354\202\254",
3250 "\354\230\244","\354\234\241","\354\271\240","\355\214\224","\352\265\254"} /* 27 Korean 4 */
3251 };
3252
3253 static char const *ethiopic_additional_digits[] =
3254 {
3255 "\341\215\262", /* 10 U+1372 */
3256 "\341\215\263", /* 20 U+1373 */
3257 "\341\215\264", /* 30 U+1374 */
3258 "\341\215\265", /* 40 U+1375 */
3259 "\341\215\266", /* 50 U+1376 */
3260 "\341\215\267", /* 60 U+1377 */
3261 "\341\215\270", /* 70 U+1378 */
3262 "\341\215\271", /* 80 U+1379 */
3263 "\341\215\272", /* 90 U+137A */
3264 "\341\215\273", /* 100 U+137B */
3265 "\341\215\274" /* 10000 U+137C */
3266 };
3267
3268 static char const *chinese_marker_shapes[][20] =
3269 {
3270 {"\345\215\201", /* 10 U+5341 */
3271 "\347\231\276", /* 100 U+767E */
3272 "\345\215\203", /* 1000 U+5343 */
3273 "\344\270\207", /* 10^4 U+4E07 */
3274 "\345\204\204", /* 10^8 U+5104 */
3275 "\345\205\206", /* 10^12 U+5146 */
3276 "\344\270\244", /* 10^16 U+4E24 */
3277 "\345\236\223", /* 10^20 垓 */
3278 "\360\245\235\261", /* 10^24 or 秭 */
3279 "\347\251\243", /* 10^28 穣 */
3280 "\346\272\235", /* 10^32 溝 */
3281 "\346\276\227", /* 10^36 澗 */
3282 "\346\255\243", /* 10^40 正 */
3283 "\350\274\211", /* 10^44 載 */
3284 "\346\245\265", /* 10^48 極 */
3285 "\346\201\222\346\262\263\346\262\231", /* 10^52 恒河沙 */
3286 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3287 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 or 那由多 */
3288 "\344\270\215\345\217\257\346\200\235\350\255\260", /* 10^64 不可思議 */
3289 "\347\204\241\351\207\217\345\244\247\346\225\260" /* 10^68 無量大数 */
3290 }, /* 1B Japanese 1 */
3291 {"\346\213\276", /* 10 U+62FE */
3292 "\347\231\276", /* 100 U+767E */
3293 "\351\230\241", /* 1000 U+9621 */
3294 "\350\220\254", /* 10^4 U+842C */
3295 "\345\204\204", /* 10^8 U+5104 */
3296 "\345\205\206", /* 10^12 U+5146 */
3297 "\344\270\244", /* 10^16 U+4E24 */
3298 "\345\236\223", /* 10^20 垓 */
3299 "\360\245\235\261", /* 10^24 or 秭 */
3300 "\347\251\243", /* 10^28 穣 */
3301 "\346\272\235", /* 10^32 溝 */
3302 "\346\276\227", /* 10^36 澗 */
3303 "\346\255\243", /* 10^40 正 */
3304 "\350\274\211", /* 10^44 載 */
3305 "\346\245\265", /* 10^48 極 */
3306 "\346\201\222\346\262\263\346\262\231", /* 10^52 恒河沙 */
3307 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3308 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 or 那由多 */
3309 "\344\270\215\345\217\257\346\200\235\350\255\260", /* 10^64 不可思議 */
3310 "\347\204\241\351\207\217\345\244\247\346\225\260" /* 10^68 無量大数 */
3311 }, /* 1C Japanese 2 */
3312 {"\345\215\201", /* 10 U+5341 */
3313 "\347\231\276", /* 100 U+767E */
3314 "\345\215\203", /* 1000 U+5343 */
3315 "\344\270\207", /* 10^4 U+4E07 */
3316 "\345\204\204", /* 10^8 U+5104 */
3317 "\345\205\206", /* 10^12 U+5146 */
3318 "\344\270\244", /* 10^16 U+4E24 */
3319 "\345\236\223", /* 10^20 垓 */
3320 "\360\245\235\261", /* 10^24 or 秭 */
3321 "\347\251\243", /* 10^28 穣 */
3322 "\346\272\235", /* 10^32 溝 */
3323 "\346\276\227", /* 10^36 澗 */
3324 "\346\255\243", /* 10^40 正 */
3325 "\350\274\211", /* 10^44 載 */
3326 "\346\245\265", /* 10^48 極 */
3327 "\346\201\222\346\262\263\346\262\231", /* 10^52 恒河沙 */
3328 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3329 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 or 那由多 */
3330 "\344\270\215\345\217\257\346\200\235\350\255\260", /* 10^64 不可思議 */
3331 "\347\204\241\351\207\217\345\244\247\346\225\260" /* 10^68 無量大数 */
3332 }, /* 1D Japanese 3 */
3333 {"\345\215\201", /* 10 U+5341 */
3334 "\347\231\276", /* 100 U+767E */
3335 "\345\215\203", /* 1000 U+5343 */
3336 "\344\270\207", /* 10^4 U+4E07 */
3337 "\344\272\277", /* 10^8 U+4EBF 亿 */
3338 "\345\205\206", /* 10^12 U+5146 兆 */
3339 "\345\205\251", /* 10^16 U+5169 京 */
3340 "\345\236\223", /* 10^20 垓 */
3341 "\347\247\255", /* 10^24 秭 */
3342 "\347\251\260", /* 10^28 穰 */
3343 "\346\262\237", /* 10^32 沟 */
3344 "\346\266\247", /* 10^36 涧 */
3345 "\346\255\243", /* 10^40 正 */
3346 "\350\275\275", /* 10^44 载 */
3347 "\346\236\201", /* 10^48 极 */
3348 "\346\201\222\346\262\263\346\262\231", /* 10^52 恒河沙 */
3349 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3350 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 */
3351 "\344\270\215\345\217\257\346\200\235\350\256\256", /* 10^64 不可思议 */
3352 "\346\227\240\351\207\217" /* 10^68 无量 */
3353 }, /* 1E Simplified Chinese 1 */
3354 {"\346\213\276", /* 10 U+62FE */
3355 "\344\275\260", /* 100 U+4F70 */
3356 "\344\273\237", /* 1000 U+4EDF */
3357 "\344\270\207", /* 10^4 U+4E07 */
3358 "\344\272\277", /* 10^8 U+4EBF */
3359 "\345\205\206", /* 10^12 U+5146 */
3360 "\345\205\251", /* 10^16 U+5169 */
3361 "\345\236\223", /* 10^20 垓 */
3362 "\347\247\255", /* 10^24 秭 */
3363 "\347\251\260", /* 10^28 穰 */
3364 "\346\262\237", /* 10^32 沟 */
3365 "\346\266\247", /* 10^36 涧 */
3366 "\346\255\243", /* 10^40 正 */
3367 "\350\275\275", /* 10^44 载 */
3368 "\346\236\201", /* 10^48 极 */
3369 "\346\201\222\346\262\263\346\262\231", /* 10^52 恒河沙 */
3370 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3371 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 */
3372 "\344\270\215\345\217\257\346\200\235\350\256\256", /* 10^64 不可思议 */
3373 "\346\227\240\351\207\217" /* 10^68 无量 */
3374 }, /* 1F Simplified Chinese 2 */
3375 {"\345\215\201", /* 10 U+5341 */
3376 "\347\231\276", /* 100 U+767E */
3377 "\345\215\203", /* 1000 U+5343 */
3378 "\344\270\207", /* 10^4 U+4E07 */
3379 "\344\272\277", /* 10^8 U+4EBF */
3380 "\345\205\206", /* 10^12 U+5146 */
3381 "\345\205\251", /* 10^16 U+5169 */
3382 "\345\236\223", /* 10^20 垓 */
3383 "\347\247\255", /* 10^24 秭 */
3384 "\347\251\260", /* 10^28 穰 */
3385 "\346\262\237", /* 10^32 沟 */
3386 "\346\266\247", /* 10^36 涧 */
3387 "\346\255\243", /* 10^40 正 */
3388 "\350\275\275", /* 10^44 载 */
3389 "\346\236\201", /* 10^48 极 */
3390 "\346\201\222\346\262\263\346\262\231", /* 10^52 恒河沙 */
3391 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3392 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 */
3393 "\344\270\215\345\217\257\346\200\235\350\256\256", /* 10^64 不可思议 */
3394 "\346\227\240\351\207\217" /* 10^68 无量 */
3395 }, /* 20 Simplified Chinese 3 */
3396 {"\345\215\201", /* 10 U+5341 */
3397 "\347\231\276", /* 100 U+767E */
3398 "\345\215\203", /* 1000 U+5343 */
3399 "\350\220\254", /* 10^4 U+842C */
3400 "\345\204\204", /* 10^8 U+5104 */
3401 "\345\205\206", /* 10^12 U+5146 */
3402 "\344\270\244", /* 10^16 U+4E24 */
3403 "\345\236\223", /* 10^20 垓 */
3404 "\347\247\255", /* 10^24 秭 */
3405 "\347\251\260", /* 10^28 穰 */
3406 "\346\272\235", /* 10^32 溝 */
3407 "\346\276\227", /* 10^36 澗 */
3408 "\346\255\243", /* 10^40 正 */
3409 "\350\274\211", /* 10^44 載 */
3410 "\346\245\265", /* 10^48 極 */
3411 "\346\201\206\346\262\263\346\262\231", /* 10^52 恆河沙 */
3412 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3413 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 */
3414 "\344\270\215\345\217\257 \346\200\235\350\255\260", /* 10^64 不可思議 */
3415 "\347\204\241\351\207\217" /* 10^68 無量 */
3416 }, /* 21 Traditional Chinese 1 */
3417 {"\346\213\276", /* 10 U+62FE */
3418 "\344\275\260", /* 100 U+4F70 */
3419 "\344\273\237", /* 1000 U+4EDF */
3420 "\350\220\254", /* 10^4 U+842C */
3421 "\345\204\204", /* 10^8 U+5104 億 */
3422 "\345\205\206", /* 10^12 U+5146 兆 */
3423 "\344\270\244", /* 10^16 U+4E24 京 */
3424 "\345\236\223", /* 10^20 垓 */
3425 "\347\247\255", /* 10^24 秭 */
3426 "\347\251\260", /* 10^28 穰 */
3427 "\346\272\235", /* 10^32 溝 */
3428 "\346\276\227", /* 10^36 澗 */
3429 "\346\255\243", /* 10^40 正 */
3430 "\350\274\211", /* 10^44 載 */
3431 "\346\245\265", /* 10^48 極 */
3432 "\346\201\206\346\262\263\346\262\231", /* 10^52 恆河沙 */
3433 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3434 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 */
3435 "\344\270\215\345\217\257 \346\200\235\350\255\260", /* 10^64 不可思議 */
3436 "\347\204\241\351\207\217" /* 10^68 無量 */
3437 }, /* 22 Traditional Chinese 2 */
3438 {"\345\215\201", /* 10 U+5341 */
3439 "\347\231\276", /* 100 U+767E */
3440 "\345\215\203", /* 1000 U+5343 */
3441 "\350\220\254", /* 10^4 U+842C */
3442 "\345\204\204", /* 10^8 U+5104 */
3443 "\345\205\206", /* 10^12 U+5146 */
3444 "\344\270\244", /* 10^16 U+4E24 */
3445 "\345\236\223", /* 10^20 垓 */
3446 "\347\247\255", /* 10^24 秭 */
3447 "\347\251\260", /* 10^28 穰 */
3448 "\346\272\235", /* 10^32 溝 */
3449 "\346\276\227", /* 10^36 澗 */
3450 "\346\255\243", /* 10^40 正 */
3451 "\350\274\211", /* 10^44 載 */
3452 "\346\245\265", /* 10^48 極 */
3453 "\346\201\206\346\262\263\346\262\231", /* 10^52 恆河沙 */
3454 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3455 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 */
3456 "\344\270\215\345\217\257 \346\200\235\350\255\260", /* 10^64 不可思議 */
3457 "\347\204\241\351\207\217" /* 10^68 無量 */
3458 }, /* 23 Traditional Chinese 3 */
3459 {"\345\215\201", /* 10 U+5341 */
3460 "\347\231\276", /* 100 U+767E */
3461 "\345\215\203", /* 1000 U+5343 */
3462 "\344\270\207", /* 10^4 U+4E07 */
3463 "\345\204\204", /* 10^8 U+5104 */
3464 "\345\205\206", /* 10^12 U+5146 */
3465 "\344\270\244", /* 10^16 U+4E24 */
3466 "\345\236\223", /* 10^20 垓 */
3467 "\347\247\255", /* 10^24 秭 */
3468 "\347\251\260", /* 10^28 穰 */
3469 "\346\272\235", /* 10^32 溝 */
3470 "\346\276\227", /* 10^36 澗 */
3471 "\346\255\243", /* 10^40 正 */
3472 "\350\274\211", /* 10^44 載 */
3473 "\346\245\265", /* 10^48 極 */
3474 "\346\201\222\346\262\263\346\262\231", /* 10^52 恒河沙 */
3475 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3476 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 */
3477 "\344\270\215\345\217\257\346\200\235\350\255\260", /* 10^64 不可思議 */
3478 "\347\204\241\351\207\217\345\244\247\346\225\270" /* 10^68 無量大數 */
3479 }, /* 24 Korean 1 */
3480 {"\346\213\276", /* 10 U+62FE */
3481 "\347\231\276", /* 100 U+767E */
3482 "\344\273\237", /* 1000 U+4EDF */
3483 "\350\220\254", /* 10^4 U+842C */
3484 "\345\204\204", /* 10^8 U+5104 */
3485 "\345\205\206", /* 10^12 U+5146 */
3486 "\344\270\244", /* 10^16 U+4E24 */
3487 "\345\236\223", /* 10^20 垓 */
3488 "\347\247\255", /* 10^24 秭 */
3489 "\347\251\260", /* 10^28 穰 */
3490 "\346\272\235", /* 10^32 溝 */
3491 "\346\276\227", /* 10^36 澗 */
3492 "\346\255\243", /* 10^40 正 */
3493 "\350\274\211", /* 10^44 載 */
3494 "\346\245\265", /* 10^48 極 */
3495 "\346\201\222\346\262\263\346\262\231", /* 10^52 恒河沙 */
3496 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3497 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 */
3498 "\344\270\215\345\217\257\346\200\235\350\255\260", /* 10^64 不可思議 */
3499 "\347\204\241\351\207\217\345\244\247\346\225\270" /* 10^68 無量大數 */
3500 }, /* 25 Korean 2 */
3501 {"\345\215\201", /* 10 U+5341 */
3502 "\347\231\276", /* 100 U+767E */
3503 "\345\215\203", /* 1000 U+5343 */
3504 "\350\220\254", /* 10^4 U+842C */
3505 "\345\204\204", /* 10^8 U+5104 */
3506 "\345\205\206", /* 10^12 U+5146 */
3507 "\344\270\244", /* 10^16 U+4E24 */
3508 "\345\236\223", /* 10^20 垓 */
3509 "\347\247\255", /* 10^24 秭 */
3510 "\347\251\260", /* 10^28 穰 */
3511 "\346\272\235", /* 10^32 溝 */
3512 "\346\276\227", /* 10^36 澗 */
3513 "\346\255\243", /* 10^40 正 */
3514 "\350\274\211", /* 10^44 載 */
3515 "\346\245\265", /* 10^48 極 */
3516 "\346\201\222\346\262\263\346\262\231", /* 10^52 恒河沙 */
3517 "\351\230\277\345\203\247\347\245\207", /* 10^56 阿僧祇 */
3518 "\351\202\243\347\224\261\344\273\226", /* 10^60 那由他 */
3519 "\344\270\215\345\217\257\346\200\235\350\255\260", /* 10^64 不可思議 */
3520 "\347\204\241\351\207\217\345\244\247\346\225\270" /* 10^68 無量大數 */
3521 }, /* 26 Korean 3 */
3522 {"\354\213\255", /* 10 십 */
3523 "\353\260\261", /* 100 백 */
3524 "\354\262\234", /* 1000 천 */
3525 "\353\247\214", /* 10^4 U+B9CC 만 */
3526 "\354\226\265", /* 10^8 U+C5B5 억 */
3527 "\354\241\260", /* 10^12 U+C870 조 */
3528 "\352\262\275", /* 10^16 U+ACBD 경 */
3529 "\355\225\264", /* 10^20 해 */
3530 "\354\236\220", /* 10^24 자 */
3531 "\354\226\221", /* 10^28 양 */
3532 "\352\265\254", /* 10^32 구 */
3533 "\352\260\204", /* 10^36 간 */
3534 "\354\240\225", /* 10^40 정 */
3535 "\354\236\254", /* 10^44 재 */
3536 "\352\267\271", /* 10^48 극 */
3537 "\355\225\255\355\225\230\354\202\254", /* 10^52 항하사 */
3538 "\354\225\204\354\212\271\352\270\260", /* 10^56 아승기 */
3539 "\353\202\230\354\234\240\355\203\200", /* 10^60 나유타 */
3540 "\353\266\210352\260\200\354\202\254\354\235\230", /* 10^64 불가사의 */
3541 "\353\254\264\353\237\211\353\214\200\354\210\230" /* 10^68 무량대수 */
3542 } /* 27 Korean 4 */
3543 };
3544
3545
3546
3547 G_STATIC_ASSERT (G_N_ELEMENTS (minus_shapes) == G_N_ELEMENTS (numeral_shapes));
3548 G_STATIC_ASSERT (G_N_ELEMENTS (plus_shapes) == G_N_ELEMENTS (numeral_shapes));
3549
3550 static gboolean
convert_numerals(GString * str,gsize from,gsize to,guint shape)3551 convert_numerals (GString *str, gsize from, gsize to, guint shape)
3552 {
3553 int i;
3554 gboolean val = FALSE;
3555 g_return_val_if_fail (shape > 1, FALSE);
3556 if (shape >= G_N_ELEMENTS (numeral_shapes))
3557 return FALSE;
3558 for (i = to; i >= (int)from; i--) {
3559 if (str->str[i] >= '0' &&
3560 str->str[i] <= '9') {
3561 gint num = str->str[i] - '0';
3562 char const *num_str = numeral_shapes[shape][num];
3563 if (*num_str != 0) {
3564 go_string_replace (str, i, 1, num_str, -1);
3565 val = TRUE;
3566 }
3567 } else if (shape >= 0x1b && shape <= 0x27 &&
3568 str->str[i] >= 'a' &&
3569 str->str[i] <= 't') {
3570 gint num = str->str[i] - 'a';
3571 char const *num_str = chinese_marker_shapes[shape - 0x1B][num];
3572 if (*num_str != 0) {
3573 go_string_replace (str, i, 1, num_str, -1);
3574 val = TRUE;
3575 }
3576 } else if (shape == 0x11 &&
3577 str->str[i] >= 'a' &&
3578 str->str[i] <= 'k') {
3579 gint num = str->str[i] - 'a';
3580 char const *num_str = ethiopic_additional_digits[num];
3581 if (*num_str != 0) {
3582 go_string_replace (str, i, 1, num_str, -1);
3583 val = TRUE;
3584 }
3585 }
3586 }
3587 return val;
3588 }
3589
3590 static gboolean
convert_sign(GString * str,size_t i,guint shape,guint shape_flags)3591 convert_sign (GString *str, size_t i, guint shape, guint shape_flags)
3592 {
3593 gchar const *shaped_sign;
3594 gchar const **shapes;
3595 gchar const *sign;
3596
3597 switch (str->str[i]) {
3598 case '-':
3599 shapes = minus_shapes;
3600 sign = UTF8_MINUS;
3601 break;
3602 case '+':
3603 shapes = plus_shapes;
3604 sign = "+";
3605 break;
3606 default:
3607 return FALSE;
3608 }
3609
3610 if (((shape_flags & GO_FMT_SHAPE_SIGNS) == 0) ||
3611 (shape <= 1) ||
3612 (shape > G_N_ELEMENTS (minus_shapes)))
3613 shaped_sign = sign;
3614 else
3615 shaped_sign = shapes [shape];
3616
3617 if (*shaped_sign != '+')
3618 go_string_replace (str, i, 1, shaped_sign, -1);
3619 return TRUE;
3620 }
3621
3622 static void
handle_ethiopic(GString * numtxt,const char ** dot,guint numeral_shape,guint shape_flags)3623 handle_ethiopic (GString *numtxt, const char **dot, guint numeral_shape,
3624 guint shape_flags)
3625 {
3626 gint last;
3627 gboolean hundred = FALSE;
3628 gboolean cnt = 0;
3629 gint tail = 0;
3630
3631 if ((shape_flags & GO_FMT_POSITION_MARKERS) == 0 ||
3632 numeral_shape != 0x11)
3633 return;
3634 if (dot && *dot) {
3635 last = *dot - numtxt->str - 1;
3636 tail = numtxt->len - last - 1;
3637 } else {
3638 last = (int)numtxt->len - 1;
3639 }
3640 if (last == 0 && numtxt->str[0] == '0')
3641 g_string_erase (numtxt, 0, 1);
3642 else if (last > 0) {
3643 for (; last >= 0; last--) {
3644 char digit = numtxt->str[last];
3645 if (digit >= '0' && digit <= '9') {
3646 if (cnt == 2)
3647 g_string_insert_c (numtxt, last + 1, 'j');
3648 else if (cnt == 4)
3649 g_string_insert_c (numtxt, last + 1, 'k');
3650 if (hundred) {
3651 if (digit == '0') {
3652 if (numtxt->str[last + 1] == '0') {
3653 if (numtxt->str[last + 2] == 'j')
3654 g_string_erase (numtxt, last, 3);
3655 else
3656 g_string_erase (numtxt, last, 2);
3657 } else {
3658 g_string_erase (numtxt, last, 1);
3659 if (numtxt->str[last] == '1' &&
3660 numtxt->str[last + 1] == 'j')
3661 g_string_erase (numtxt, last, 1);
3662 }
3663 } else {
3664 numtxt->str[last] += ('a'-'1');
3665 if (numtxt->str[last + 1] == '0')
3666 g_string_erase (numtxt, last + 1, 1);
3667 }
3668 }
3669 hundred = !hundred;
3670 cnt++;
3671 if (cnt > 5)
3672 cnt -= 4;
3673 }
3674 }
3675 if (hundred) {
3676 if (numtxt->str[0] == '0')
3677 g_string_erase (numtxt, 0, 2);
3678 else
3679 if (numtxt->str[0] == '1' &&
3680 numtxt->str[1] == 'j')
3681 g_string_erase (numtxt, 0, 1);
3682 }
3683 }
3684
3685 if (dot && *dot)
3686 *dot = numtxt->str + (numtxt->len - tail);
3687 }
3688
3689
3690 static void
handle_chinese(GString * numtxt,const char ** dot,guint numeral_shape,guint shape_flags)3691 handle_chinese (GString *numtxt, const char **dot, guint numeral_shape,
3692 guint shape_flags)
3693 {
3694 GString *ntxt;
3695 char const *last;
3696 gint i, wan;
3697 gboolean wan_written = TRUE;
3698 gboolean digit_written = FALSE;
3699 gboolean suppress_ten, suppress_ten_always;
3700 if ((shape_flags & GO_FMT_POSITION_MARKERS) == 0 ||
3701 numeral_shape < 0x1B || numeral_shape > 0x27)
3702 return;
3703 last = ((dot && *dot) ? *dot - 1 : numtxt->str + (numtxt->len - 1));
3704 if (last <= numtxt->str + 1)
3705 return;
3706
3707 ntxt = g_string_sized_new (100);
3708 suppress_ten = (numeral_shape == 0x1b || numeral_shape == 0x1d
3709 || numeral_shape == 0x26);
3710 suppress_ten_always = (numeral_shape == 0x26);
3711 i = 0;
3712 wan = 0;
3713 while (last >= numtxt->str) {
3714 if (*last >= '0' && *last <= '9') {
3715 if (*last > '0' || digit_written) {
3716 if (!wan_written) {
3717 if (wan + 'c' > 't')
3718 g_string_prepend_c (ntxt, '?');
3719 else
3720 g_string_prepend_c (ntxt, 'c' + wan);
3721 wan_written = TRUE;
3722 }
3723 if (i > 0)
3724 g_string_prepend_c (ntxt, 'a' + i - 1);
3725 if (!suppress_ten_always ||
3726 !(suppress_ten && wan == 0) ||
3727 *last != '1')
3728 g_string_prepend_c (ntxt, *last);
3729 digit_written = TRUE;
3730 }
3731 } else g_string_prepend_c (ntxt, *last);
3732 if (++i > 3) {
3733 i = i % 4;
3734 wan++;
3735 wan_written = FALSE;
3736 digit_written = FALSE;
3737 }
3738 last --;
3739 }
3740
3741 if (dot && *dot) {
3742 gint len = ntxt->len;
3743 g_string_append (ntxt, *dot);
3744 *dot = ntxt->str + len;
3745 }
3746 g_string_assign (numtxt, ntxt->str);
3747 g_string_free (ntxt, TRUE);
3748 }
3749 #endif
3750
3751 #define INSERT_MINUS(pos) do { \
3752 if (unicode_minus || \
3753 ((shape_flags & GO_FMT_SHAPE_SIGNS) && \
3754 numeral_shape)) { \
3755 if ((shape_flags & GO_FMT_SHAPE_SIGNS) && \
3756 numeral_shape > 1 && \
3757 numeral_shape < G_N_ELEMENTS (minus_shapes)) \
3758 g_string_insert \
3759 (dst, (pos), \
3760 minus_shapes[numeral_shape]); \
3761 else \
3762 g_string_insert_len \
3763 (dst, (pos), UTF8_MINUS, 3); \
3764 } else \
3765 g_string_insert_c (dst, (pos), '-'); \
3766 } while (0)
3767
3768 #define INSERT_PLUS(pos) do { \
3769 if ((shape_flags & GO_FMT_SHAPE_SIGNS) && \
3770 numeral_shape > 1 && \
3771 numeral_shape < G_N_ELEMENTS (plus_shapes)) \
3772 g_string_insert \
3773 (dst, (pos), \
3774 plus_shapes[numeral_shape]); \
3775 else \
3776 g_string_insert_c (dst, (pos), '+'); \
3777 } while (0)
3778
3779
3780
3781 static GOFormatNumberError
SUFFIX(go_format_execute)3782 SUFFIX(go_format_execute) (PangoLayout *layout, GString *dst,
3783 const GOFormatMeasure measure,
3784 const GOFontMetrics *metrics,
3785 const guchar *prg,
3786 int col_width,
3787 DOUBLE val, const char *sval,
3788 GODateConventions const *date_conv,
3789 gboolean unicode_minus)
3790 {
3791 GOFormatNumberError res = GO_FORMAT_NUMBER_OK;
3792 DOUBLE valsecs = 0;
3793 GDateYear year = 0;
3794 GDateMonth month = 0;
3795 GDateDay day = 0;
3796 GDateWeekday weekday = 0;
3797 DOUBLE hour = 0, minute = 0, second = 0;
3798 gboolean ispm = FALSE;
3799 char fsecond[PREFIX(DIG) + 10];
3800 const char *date_dec_ptr = NULL;
3801 GString *numtxt = NULL;
3802 size_t dotpos = 0;
3803 size_t numi = 0;
3804 int numpos = -1;
3805 int numpos_end = -1;
3806 int generalpos = -1;
3807 const GString *decimal = go_locale_get_decimal ();
3808 const GString *comma = go_locale_get_thousand ();
3809 gboolean thousands = FALSE;
3810 gboolean digit_count = 0;
3811 int exponent = 0;
3812 #ifdef ALLOW_SI_APPEND
3813 char const *si_str = NULL;
3814 int si_pos = -1;
3815 #endif
3816 struct {
3817 DOUBLE w, n, d, val;
3818 int digits;
3819 gsize whole_start, nominator_start, denominator_start, pi_sum_start;
3820 gboolean blanked, use_whole, denom_blanked;
3821 } fraction = {0., 0., 0., 0., 0, 0, 0, 0, 0, FALSE, FALSE, FALSE};
3822 char *oldlocale = NULL;
3823 guint numeral_shape = 0;
3824 guint shape_flags = 0;
3825 PangoAttrList *attrs = NULL;
3826
3827 #ifdef ALLOW_EE_MARKUP
3828 int mantissa_start = -1;
3829 int special_mantissa = INT_MAX;
3830 GSList *markup_stack = NULL;
3831 #endif
3832
3833 if (layout) {
3834 attrs = pango_attr_list_copy (pango_layout_get_attributes (layout));
3835 if (attrs == NULL)
3836 attrs = pango_attr_list_new ();
3837 }
3838
3839 while (1) {
3840 GOFormatOp op = *prg++;
3841
3842 switch (op) {
3843 case OP_END:
3844 if (layout) {
3845 pango_layout_set_text (layout, dst->str, -1);
3846 if (attrs) {
3847 pango_layout_set_attributes (layout, attrs);
3848 pango_attr_list_unref (attrs);
3849 attrs = NULL;
3850 }
3851 #ifdef ALLOW_EE_MARKUP
3852 g_slist_free (markup_stack);
3853 #endif
3854 }
3855 if (numtxt)
3856 g_string_free (numtxt, TRUE);
3857 if (oldlocale) {
3858 go_setlocale (LC_ALL, oldlocale);
3859 g_free (oldlocale);
3860 }
3861 return res;
3862
3863 case OP_CHAR: {
3864 const guchar *next = g_utf8_next_char (prg);
3865 g_string_insert_len (dst, numpos, prg, next - prg);
3866 prg = next;
3867 break;
3868 }
3869
3870 case OP_CHAR_INVISIBLE: {
3871 const guchar *next = g_utf8_next_char (prg);
3872 /* This ignores actual width for now. */
3873 g_string_insert_c (dst, numpos, ' ');
3874 prg = next;
3875 break;
3876 }
3877
3878 case OP_CHAR_REPEAT: {
3879 g_string_insert_c (dst, numpos, REPEAT_CHAR_MARKER);
3880 break;
3881 }
3882
3883 case OP_STRING: {
3884 size_t len = strlen (prg);
3885 g_string_insert_len (dst, numpos, prg, len);
3886 prg += len + 1;
3887 break;
3888 }
3889
3890 case OP_FILL: {
3891 gssize fill_pos = -1;
3892 gsize i = 0;
3893 gunichar fill_char = g_utf8_get_char (prg);
3894
3895 prg = g_utf8_next_char (prg);
3896
3897 while (i < dst->len) {
3898 if (dst->str[i] == REPEAT_CHAR_MARKER) {
3899 fill_pos = i;
3900 g_string_erase (dst, i, 1);
3901 } else
3902 i++;
3903 }
3904
3905 if (fill_pos >= 0 && col_width >= 0) {
3906 fill_with_char (dst, layout, fill_pos,
3907 fill_char,
3908 measure, col_width);
3909 if (fill_char == ' ' && metrics->thin_space)
3910 fill_with_char (dst, layout, fill_pos,
3911 metrics->thin_space,
3912 measure, col_width);
3913 }
3914 break;
3915 }
3916
3917 case OP_LOCALE: {
3918 const char *lang;
3919 /* GOFormatLocale locale; */
3920 /* memcpy (&locale, prg, sizeof (locale)); */
3921 prg += sizeof (GOFormatLocale);
3922 lang = (const char *)prg;
3923 prg += strlen (lang) + 1;
3924
3925 if (oldlocale == NULL)
3926 oldlocale = g_strdup (go_setlocale (LC_ALL, NULL));
3927 /* Setting LC_TIME should be enough, but glib gets
3928 confused over character sets. */
3929 go_setlocale (LC_TIME, lang);
3930 go_setlocale (LC_CTYPE, lang);
3931 break;
3932 }
3933
3934 case OP_NUMERAL_SHAPE:
3935 shape_flags = (*prg++ & 0x000f);
3936 numeral_shape = (*prg++ & 0x00ff);
3937 break;
3938
3939 case OP_DATE_ROUND: {
3940 int date_decimals = *prg++;
3941 gboolean seen_elapsed = *prg++;
3942 DOUBLE unit = SUFFIX(go_pow10)(date_decimals);
3943 #ifdef ALLOW_NEGATIVE_TIMES
3944 gboolean isneg = (val < 0);
3945 #else
3946 gboolean isneg = FALSE;
3947 #endif
3948
3949 valsecs = SUFFIX(floor)(SUFFIX(go_add_epsilon)(SUFFIX(fabs)(val)) * (unit * 86400) + 0.5);
3950 if (date_decimals) {
3951 DOUBLE vs = (seen_elapsed || !isneg) ? valsecs : 0 - valsecs;
3952 DOUBLE f = SUFFIX(fmod) (vs, unit);
3953 #ifdef ALLOW_NEGATIVE_TIMES
3954 if (f < 0)
3955 f += unit;
3956 #endif
3957 sprintf (fsecond, "%0*.0" FORMAT_f,
3958 date_decimals, f);
3959 valsecs = SUFFIX(floor)(valsecs / unit);
3960 }
3961 if (isneg)
3962 valsecs = 0 - valsecs;
3963 break;
3964 }
3965
3966 case OP_DATE_SPLIT: {
3967 GDate date;
3968 go_date_serial_to_g (&date,
3969 (int)SUFFIX(floor)(valsecs / 86400),
3970 date_conv);
3971 if (!g_date_valid (&date)) {
3972 res = GO_FORMAT_NUMBER_DATE_ERROR;
3973 g_date_set_dmy (&date, 1, 1, 1900);
3974 }
3975 year = g_date_get_year (&date);
3976 month = g_date_get_month (&date);
3977 day = g_date_get_day (&date);
3978 weekday = g_date_get_weekday (&date);
3979 if (year > 9999)
3980 res = GO_FORMAT_NUMBER_DATE_ERROR;
3981 break;
3982 }
3983
3984 case OP_DATE_YEAR:
3985 append_i (dst, year);
3986 break;
3987
3988 case OP_DATE_YEAR_2:
3989 append_i2 (dst, year % 100);
3990 break;
3991
3992 case OP_DATE_YEAR_THAI:
3993 append_i (dst, year + 543);
3994 break;
3995
3996 case OP_DATE_YEAR_THAI_2:
3997 append_i2 (dst, (year + 543) % 100);
3998 break;
3999
4000 case OP_DATE_MONTH:
4001 append_i (dst, month);
4002 break;
4003
4004 case OP_DATE_MONTH_2:
4005 append_i2 (dst, month);
4006 break;
4007
4008 case OP_DATE_MONTH_NAME: {
4009 char *s = go_date_month_name (month, FALSE);
4010 g_string_append (dst, s);
4011 g_free (s);
4012 break;
4013 }
4014
4015 case OP_DATE_MONTH_NAME_1: {
4016 char *s = go_date_month_name (month, TRUE);
4017 g_string_append_unichar (dst, g_utf8_get_char (s));
4018 g_free (s);
4019 break;
4020 }
4021
4022 case OP_DATE_MONTH_NAME_3: {
4023 char *s = go_date_month_name (month, TRUE);
4024 g_string_append (dst, s);
4025 g_free (s);
4026 break;
4027 }
4028
4029 case OP_DATE_DAY:
4030 append_i (dst, day);
4031 break;
4032
4033 case OP_DATE_DAY_2:
4034 append_i2 (dst, day);
4035 break;
4036
4037 case OP_DATE_WEEKDAY: {
4038 char *s = go_date_weekday_name (weekday, FALSE);
4039 g_string_append (dst, s);
4040 g_free (s);
4041 break;
4042 }
4043
4044 case OP_DATE_WEEKDAY_3: {
4045 char *s = go_date_weekday_name (weekday, TRUE);
4046 g_string_append (dst, s);
4047 g_free (s);
4048 break;
4049 }
4050
4051 case OP_TIME_SPLIT_12:
4052 case OP_TIME_SPLIT_24: {
4053 int secs = (int)SUFFIX(fmod)(valsecs, 86400);
4054 #ifdef ALLOW_NEGATIVE_TIMES
4055 if (secs < 0)
4056 secs += 86400;
4057 #endif
4058 hour = secs / 3600;
4059 minute = (secs / 60) % 60;
4060 second = secs % 60;
4061 if (op == OP_TIME_SPLIT_12) {
4062 ispm = (hour >= 12);
4063 if (ispm) hour -= 12;
4064 if (hour == 0) hour = 12;
4065 }
4066 break;
4067 }
4068
4069 case OP_TIME_SPLIT_ELAPSED_HOUR:
4070 case OP_TIME_SPLIT_ELAPSED_MINUTE:
4071 case OP_TIME_SPLIT_ELAPSED_SECOND: {
4072 DOUBLE s = SUFFIX(fabs)(valsecs);
4073
4074 if (op == OP_TIME_SPLIT_ELAPSED_SECOND)
4075 second = s;
4076 else {
4077 second = SUFFIX(fmod)(s, 60);
4078 s = SUFFIX(floor)(s / 60);
4079 if (op == OP_TIME_SPLIT_ELAPSED_MINUTE)
4080 minute = s;
4081 else {
4082 minute = SUFFIX(fmod)(s, 60);
4083 s = SUFFIX(floor)(s / 60);
4084 hour = s;
4085 }
4086 }
4087 break;
4088 }
4089
4090 case OP_TIME_HOUR_2:
4091 if (hour < 100) {
4092 append_i2 (dst, (int)hour);
4093 break;
4094 }
4095 /* Fall through */
4096 case OP_TIME_HOUR:
4097 g_string_append_printf (dst, "%.0" FORMAT_f, hour);
4098 break;
4099
4100 case OP_TIME_HOUR_N: {
4101 int n = *prg++;
4102 #ifdef ALLOW_NEGATIVE_TIMES
4103 if (valsecs < 0)
4104 INSERT_MINUS(-1);
4105 #endif
4106 g_string_append_printf (dst, "%0*.0" FORMAT_f, n, hour);
4107 break;
4108 }
4109
4110 case OP_TIME_AMPM:
4111 g_string_append (dst, ispm ? "PM" : "AM");
4112 break;
4113
4114 case OP_TIME_AP: {
4115 char ca = *prg++;
4116 char cp = *prg++;
4117 g_string_append_c (dst, ispm ? cp : ca);
4118 break;
4119 }
4120
4121 case OP_TIME_MINUTE_2:
4122 if (minute < 100) {
4123 append_i2 (dst, (int)minute);
4124 break;
4125 }
4126 /* Fall through */
4127 case OP_TIME_MINUTE:
4128 g_string_append_printf (dst, "%.0" FORMAT_f, minute);
4129 break;
4130
4131 case OP_TIME_MINUTE_N: {
4132 int n = *prg++;
4133 #ifdef ALLOW_NEGATIVE_TIMES
4134 if (valsecs < 0)
4135 INSERT_MINUS(-1);
4136 #endif
4137 g_string_append_printf (dst, "%0*.0" FORMAT_f, n, minute);
4138 break;
4139 }
4140
4141 case OP_TIME_SECOND_2:
4142 if (second < 100) {
4143 append_i2 (dst, (int)second);
4144 break;
4145 }
4146 /* Fall through */
4147 case OP_TIME_SECOND:
4148 g_string_append_printf (dst, "%.0" FORMAT_f, second);
4149 break;
4150
4151 case OP_TIME_SECOND_N: {
4152 int n = *prg++;
4153 #ifdef ALLOW_NEGATIVE_TIMES
4154 if (valsecs < 0)
4155 INSERT_MINUS(-1);
4156 #endif
4157 g_string_append_printf (dst, "%0*.0" FORMAT_f, n, second);
4158 break;
4159 }
4160
4161 case OP_TIME_SECOND_DECIMAL_START:
4162 /* Reset to start of decimal string. */
4163 date_dec_ptr = fsecond;
4164 go_string_append_gstring (dst, decimal);
4165 break;
4166
4167 case OP_TIME_SECOND_DECIMAL_DIGIT:
4168 g_string_append_c (dst, *date_dec_ptr++);
4169 break;
4170
4171 case OP_NUM_SCALE: {
4172 int n = *(const signed char *)prg;
4173 prg++;
4174 if (n >= 0)
4175 val *= SUFFIX(go_pow10) (n);
4176 else
4177 val /= SUFFIX(go_pow10) (-n);
4178 break;
4179 }
4180
4181 case OP_NUM_PRINTF_E: {
4182 int n = *prg++;
4183 int wd = *prg++;
4184 char *dot;
4185 const char *epos;
4186
4187 if (!numtxt)
4188 numtxt = g_string_sized_new (100);
4189
4190 SUFFIX(printf_engineering) (numtxt, val, n, wd);
4191
4192 epos = strchr (numtxt->str, 'E');
4193 if (epos) {
4194 exponent = atoi (epos + 1);
4195 g_string_truncate (numtxt, epos - numtxt->str);
4196 }
4197
4198 dot = strstr (numtxt->str, decimal->str);
4199 if (dot) {
4200 size_t i = numtxt->len;
4201 dotpos = dot - numtxt->str;
4202 while (numtxt->str[i - 1] == '0')
4203 i--;
4204 /* Kill zeroes in "xxx.xxx000" */
4205 g_string_truncate (numtxt, i);
4206 } else {
4207 dotpos = numtxt->len;
4208 }
4209
4210 #ifdef ALLOW_EE_MARKUP
4211 if (!dot || numtxt->str[dotpos + decimal->len] == 0) {
4212 if (dotpos == 2 &&
4213 numtxt->str[0] == '-' &&
4214 numtxt->str[1] == '1')
4215 special_mantissa = -1;
4216 else if (dotpos == 1 && numtxt->str[0] == '0')
4217 special_mantissa = 0;
4218 else if (dotpos == 1 && numtxt->str[0] == '1')
4219 special_mantissa = +1;
4220 }
4221 #endif
4222
4223 break;
4224 }
4225
4226 case OP_NUM_PRINTF_F: {
4227 int n = *prg++;
4228 const char *dot;
4229 if (!numtxt)
4230 numtxt = g_string_sized_new (100);
4231 go_dtoa (numtxt, "=^.*" FORMAT_f, n, val);
4232 dot = strstr (numtxt->str, decimal->str);
4233 handle_chinese (numtxt, &dot,
4234 numeral_shape, shape_flags);
4235 handle_ethiopic (numtxt, &dot,
4236 numeral_shape, shape_flags);
4237 if (dot) {
4238 size_t i = numtxt->len;
4239 dotpos = dot - numtxt->str;
4240 while (numtxt->str[i - 1] == '0')
4241 i--;
4242 /* Kill zeroes in "xxx.xxx000" */
4243 g_string_truncate (numtxt, i);
4244
4245 if (numtxt->str[0] == '-' &&
4246 numtxt->str[1] == '0' &&
4247 dotpos == 2 &&
4248 numtxt->len == dotpos + decimal->len) {
4249 g_string_erase (numtxt, 0, 1);
4250 dotpos--;
4251 }
4252 } else {
4253 if (numtxt->str[0] == '-' &&
4254 numtxt->str[1] == '0' &&
4255 numtxt->len == 2)
4256 g_string_erase (numtxt, 0, 1);
4257 dotpos = numtxt->len;
4258 }
4259 break;
4260 }
4261
4262 case OP_NUM_ENABLE_THOUSANDS:
4263 thousands = TRUE;
4264 break;
4265
4266 case OP_NUM_DISABLE_THOUSANDS:
4267 thousands = FALSE;
4268 break;
4269
4270 case OP_NUM_SIGN:
4271 if (numtxt->str[0] == '-') {
4272 g_string_erase (numtxt, 0, 1);
4273 dotpos--;
4274 INSERT_MINUS(0);
4275 }
4276 break;
4277
4278 case OP_NUM_VAL_SIGN:
4279 if (val < 0)
4280 INSERT_MINUS(numpos);
4281 break;
4282
4283 case OP_NUM_FRACTION_SIGN:
4284 if (fraction.val < 0)
4285 INSERT_MINUS(numpos);
4286 else
4287 g_string_insert_c (dst, numpos, '+');
4288 break;
4289
4290 case OP_NUM_MOVETO_ONES: {
4291 numi = dotpos;
4292 /* Ignore the zero in "0.xxx" or "0 xx/xx" */
4293 if (numi == 1 && numtxt->str[0] == '0' &&
4294 numtxt->str[dotpos] != 0)
4295 numi--;
4296 numpos = dst->len;
4297 break;
4298 }
4299
4300 case OP_NUM_MOVETO_DECIMALS:
4301 if (dotpos == numtxt->len)
4302 numi = dotpos;
4303 else
4304 numi = dotpos + decimal->len;
4305 break;
4306
4307 case OP_NUM_REST_WHOLE:
4308 while (numi > 0) {
4309 char c = numtxt->str[--numi];
4310 digit_count++;
4311 if (thousands && digit_count > 3 &&
4312 digit_count % 3 == 1) {
4313 g_string_insert_len (dst, numpos,
4314 comma->str,
4315 comma->len);
4316 if (numpos_end >= 0)
4317 numpos_end += comma->len;
4318 }
4319 g_string_insert_c (dst, numpos, c);
4320 if ((numeral_shape) > 1) /* 0: not set; 1: Western */
4321 convert_numerals (dst, numpos, numpos, numeral_shape);
4322 if (numpos_end >= 0)
4323 numpos_end++;
4324
4325 }
4326 break;
4327
4328 case OP_NUM_APPEND_MODE:
4329 numpos = -1;
4330 break;
4331
4332 case OP_NUM_DECIMAL_POINT:
4333 go_string_append_gstring (dst, decimal);
4334 break;
4335
4336 case OP_NUM_DIGIT_1: {
4337 char fc = *prg++;
4338 char c;
4339 if (numi == 0) {
4340 if (fc == '0')
4341 c = '0';
4342 else if (fc == '?')
4343 c = ' ';
4344 else
4345 break;
4346 } else {
4347 c = numtxt->str[numi - 1];
4348 numi--;
4349 }
4350 digit_count++;
4351 if (thousands && digit_count > 3 &&
4352 digit_count % 3 == 1) {
4353 g_string_insert_len (dst, numpos,
4354 comma->str, comma->len);
4355 if (numpos_end >= 0)
4356 numpos_end += comma->len;
4357 }
4358 g_string_insert_c (dst, numpos, c);
4359 if ((numeral_shape) > 1) /* 0: not set; 1: Western */
4360 convert_numerals (dst, numpos, numpos, numeral_shape);
4361 if (numpos_end >= 0)
4362 numpos_end++;
4363 break;
4364 }
4365
4366 case OP_NUM_DECIMAL_1: {
4367 char fc = *prg++;
4368 char c;
4369 if (numi == numtxt->len) {
4370 if (fc == '0')
4371 c = '0';
4372 else if (fc == '?')
4373 c = ' ';
4374 else
4375 break;
4376 } else {
4377 c = numtxt->str[numi];
4378 numi++;
4379 }
4380 g_string_append_c (dst, c);
4381 if ((numeral_shape) > 1) /* 0: not set; 1: Western */
4382 convert_numerals (dst, dst->len - 1, dst->len - 1,
4383 numeral_shape);
4384 break;
4385 }
4386
4387 case OP_NUM_DIGIT_1_0: {
4388 char fc = *prg++;
4389 if (fc == '0') {
4390 g_string_insert_c (dst, numpos, '0');
4391 if ((numeral_shape) > 1) /* 0: not set; 1: Western */
4392 convert_numerals (dst, numpos, numpos, numeral_shape);
4393 } else if (fc == '?')
4394 g_string_insert_c (dst, numpos, ' ');
4395 break;
4396 }
4397
4398 case OP_NUM_DENUM_DIGIT_Q:
4399 if (numi == 0)
4400 g_string_append_c (dst, ' ');
4401 else {
4402 char c = numtxt->str[numi - 1];
4403 numi--;
4404 g_string_insert_c (dst, numpos, c);
4405 if ((numeral_shape) > 1) /* 0: not set; 1: Western */
4406 convert_numerals (dst, numpos, numpos, numeral_shape);
4407 }
4408 break;
4409
4410 case OP_NUM_EXPONENT_SIGN: {
4411 gboolean forced = (*prg++ != 0);
4412 if (exponent >= 0) {
4413 if (forced)
4414 INSERT_PLUS(numpos);
4415 } else
4416 INSERT_MINUS(numpos);
4417 break;
4418 }
4419
4420 case OP_NUM_VAL_EXPONENT:
4421 val = SUFFIX (fabs) (exponent + 0.0);
4422 break;
4423
4424 case OP_NUM_STORE_POS:
4425 numpos_end = numpos;
4426 break;
4427
4428 case OP_NUM_EXPONENT_1:
4429 exponent = 1;
4430 break;
4431
4432 #ifdef ALLOW_EE_MARKUP
4433 case OP_NUM_MARK_MANTISSA:
4434 mantissa_start = dst->len;
4435 break;
4436
4437 case OP_NUM_SIMPLIFY_MANTISSA:
4438 if (special_mantissa != INT_MAX)
4439 g_string_truncate (dst, mantissa_start);
4440 break;
4441
4442 case OP_NUM_SIMPLIFY_MARKUP_MANTISSA:
4443 if (special_mantissa == 0) {
4444 g_string_erase (dst, mantissa_start,
4445 numpos_end - mantissa_start);
4446 g_string_insert_c (dst, mantissa_start, '0');
4447 numpos_end = mantissa_start + 1;
4448 }
4449 break;
4450
4451 case OP_MARKUP_SUPERSCRIPT_START:
4452 if (layout) {
4453 /* FIXME: we need to fix the exponent handling */
4454 /* until that time use only latin numerals */
4455 numeral_shape = 0;
4456
4457 markup_stack = g_slist_prepend
4458 (markup_stack, GSIZE_TO_POINTER (dst->len));
4459 }
4460 break;
4461
4462 case OP_MARKUP_SUPERSCRIPT_END:
4463 if (layout) {
4464 guint start = 0,
4465 end = (guint)numpos_end;
4466 PangoAttribute *attr;
4467
4468 if (markup_stack) {
4469 start = (guint)GPOINTER_TO_SIZE (markup_stack->data);
4470 markup_stack = g_slist_delete_link (markup_stack, markup_stack);
4471 }
4472
4473 attr = go_pango_attr_superscript_new (TRUE);
4474 attr->start_index = start;
4475 attr->end_index = end;
4476 pango_attr_list_insert (attrs, attr);
4477 }
4478 break;
4479 #endif
4480 #ifdef ALLOW_SI_APPEND
4481 case OP_NUM_SIMPLIFY_MANTISSA_SI:
4482 if (exponent != 0 && special_mantissa != INT_MAX) {
4483 g_string_truncate (dst, mantissa_start);
4484 si_pos = mantissa_start;
4485 }
4486
4487 break;
4488
4489 case OP_NUM_REDUCE_EXPONENT_SI:
4490 exponent -= si_reduction (exponent, &si_str);
4491 si_pos = dst->len;
4492 break;
4493
4494 case OP_NUM_SIMPLIFY_EXPONENT_SI:
4495 if (exponent == 0 && si_pos > 0 && numpos_end >= 0) {
4496 int len = numpos_end - si_pos;
4497 if (attrs)
4498 go_pango_attr_list_erase (attrs, si_pos, len);
4499 g_string_erase (dst, si_pos, len);
4500 numpos_end = si_pos;
4501 }
4502 break;
4503
4504 case OP_NUM_SI_EXPONENT:
4505 g_string_insert_c (dst, numpos_end, ' ');
4506 numpos_end++;
4507 if (si_str != NULL) {
4508 g_string_insert (dst, numpos_end, si_str);
4509 numpos_end += strlen (si_str);
4510 }
4511 si_pos = -1;
4512 break;
4513 #endif
4514
4515 case OP_NUM_FRACTION: {
4516 gboolean wp = *prg++;
4517 gboolean explicit_denom = *prg++;
4518 DOUBLE aval = SUFFIX(go_add_epsilon) (SUFFIX(fabs)(val));
4519
4520 fraction.val = val;
4521 fraction.w = SUFFIX(floor) (aval);
4522 aval -= fraction.w;
4523
4524 if (explicit_denom) {
4525 double plaind; /* Plain double */
4526 memcpy (&plaind, prg, sizeof (plaind));
4527 prg += sizeof (plaind);
4528
4529 fraction.d = plaind;
4530 fraction.digits = cnt_digits (fraction.d);
4531 fraction.n = SUFFIX(floor) (0.5 + aval * fraction.d);
4532 } else {
4533 int ni, di;
4534 fraction.digits = *prg++;
4535 go_continued_fraction (aval, SUFFIX(go_pow10) (fraction.digits) - 1, &ni, &di);
4536 fraction.n = ni;
4537 fraction.d = di;
4538 }
4539
4540 if (wp && fraction.n == fraction.d) {
4541 fraction.w += 1;
4542 fraction.n = 0;
4543 }
4544 if (!wp)
4545 fraction.n += fraction.d * fraction.w;
4546
4547 break;
4548 }
4549
4550 #ifdef ALLOW_PI_SLASH
4551 case OP_NUM_FRACTION_SCALE_PI:
4552 /* FIXME: not long-double safe. */
4553 val /= G_PI;
4554 break;
4555 #endif
4556
4557 case OP_NUM_FRACTION_WHOLE:
4558 fraction.use_whole = TRUE;
4559 fraction.whole_start = dst->len;
4560 val = fraction.w;
4561 break;
4562
4563 case OP_NUM_FRACTION_NOMINATOR:
4564 fraction.nominator_start = dst->len;
4565 val = fraction.n;
4566 break;
4567
4568 case OP_NUM_FRACTION_SLASH:
4569 {
4570 int desired_width = go_format_desired_width (layout, attrs, fraction.digits);
4571 int length;
4572 PangoRectangle logical_rect = {0, 0, 0, 2 * PANGO_SCALE};
4573 char *end = dst->str + dst->len;
4574 char *nom = dst->str + fraction.nominator_start;
4575 end = g_utf8_prev_char (end);
4576 while (!g_unichar_isdigit (g_utf8_get_char (end)) && end > nom)
4577 end = g_utf8_prev_char (end);
4578 end = g_utf8_find_next_char (end, NULL);
4579 length = end - nom;
4580
4581 logical_rect.width = go_format_get_width
4582 (dst, attrs, fraction.nominator_start,
4583 length, layout);
4584
4585 if (logical_rect.width < desired_width) {
4586 PangoAttribute *attr;
4587 PangoAttrList *new_attrs = pango_attr_list_new ();
4588 logical_rect.width = desired_width - logical_rect.width;
4589 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
4590 attr->start_index = 0;
4591 attr->end_index = 1;
4592 pango_attr_list_insert (new_attrs, attr);
4593 g_string_insert_c (dst, fraction.nominator_start, ' ');
4594 pango_attr_list_splice (attrs, new_attrs, fraction.nominator_start, 1);
4595 pango_attr_list_unref (new_attrs);
4596 fraction.nominator_start++;
4597 }
4598 }
4599 break;
4600
4601 case OP_NUM_FRACTION_DENOMINATOR:
4602 fraction.denominator_start = dst->len;
4603 val = fraction.d;
4604 break;
4605
4606 case OP_NUM_FRACTION_BLANK:
4607 if (fraction.n == 0) {
4608 /* Replace all added characters by spaces of the right length. */
4609 if (dst->len > fraction.nominator_start) {
4610 blank_characters (dst, attrs, fraction.nominator_start,
4611 dst->len - fraction.nominator_start, layout);
4612 fraction.blanked = TRUE;
4613 }
4614 }
4615 break;
4616
4617 #ifdef ALLOW_PI_SLASH
4618 case OP_NUM_FRACTION_PI_SUM_START:
4619 fraction.pi_sum_start = dst->len;
4620 break;
4621
4622 case OP_NUM_FRACTION_BLANK_PI:
4623 if (fraction.n == 0) {
4624 /* Replace all added characters by spaces of the right length. */
4625 if (dst->len > fraction.pi_sum_start + 1) {
4626 blank_characters (dst, attrs, fraction.pi_sum_start,
4627 dst->len - fraction.pi_sum_start, layout);
4628 fraction.blanked = TRUE;
4629 }
4630 }
4631 break;
4632 #endif
4633
4634 case OP_NUM_FRACTION_BLANK_WHOLE:
4635 if (!fraction.blanked && fraction.w == 0) {
4636 gsize start = fraction.whole_start;
4637 gsize length = fraction.nominator_start - start;
4638 g_string_erase (dst, start, length);
4639 go_pango_attr_list_erase (attrs, start, length);
4640 fraction.nominator_start -= length;
4641 fraction.denominator_start -= length;
4642 fraction.pi_sum_start = 0;
4643 } else if (fraction.pi_sum_start > 0) {
4644 if (fraction.w == 1) {
4645 gsize start = fraction.whole_start;
4646 gsize length = fraction.pi_sum_start
4647 - UNICODE_PI_number_of_bytes - start;
4648 g_string_erase (dst, start, length);
4649 go_pango_attr_list_erase (attrs, start, length);
4650 fraction.nominator_start -= length;
4651 fraction.denominator_start -= length;
4652 fraction.pi_sum_start = 0;
4653 } else if (fraction.w == 0) {
4654 blank_characters
4655 (dst, attrs,
4656 fraction.pi_sum_start - UNICODE_PI_number_of_bytes,
4657 UNICODE_PI_number_of_bytes, layout);
4658 }
4659 }
4660 break;
4661
4662 case OP_NUM_FRACTION_ALIGN:
4663 if (layout && pango_context_get_font_map (pango_layout_get_context (layout))) {
4664 int desired_width = go_format_desired_width (layout, attrs, fraction.digits);
4665 PangoRectangle logical_rect = {0, 0, 0, 2 * PANGO_SCALE};
4666 int existing_width;
4667
4668 existing_width = go_format_get_width
4669 (dst, attrs, fraction.denominator_start,
4670 dst->len - fraction.denominator_start, layout);
4671 if (existing_width < desired_width) {
4672 PangoAttribute *attr;
4673 int start = dst->len;
4674 logical_rect.width = desired_width - existing_width;
4675 attr = pango_attr_shape_new (&logical_rect, &logical_rect);
4676 attr->start_index = start;
4677 attr->end_index = start + 1;
4678 g_string_append_c (dst, ' ');
4679 pango_attr_list_insert (attrs, attr);
4680 }
4681 }
4682 break;
4683
4684
4685 #ifdef ALLOW_PI_SLASH
4686 case OP_NUM_FRACTION_SIMPLIFY_PI:
4687 if (!fraction.blanked && fraction.d == 1 &&
4688 fraction.w == 0) {
4689 blank_characters (dst, attrs, fraction.denominator_start - 1,
4690 2, layout);
4691 fraction.denom_blanked = TRUE;
4692 }
4693 break;
4694 #endif
4695 #ifdef ALLOW_DENOM_REMOVAL
4696 case OP_NUM_FRACTION_SIMPLIFY:
4697 if (!fraction.blanked && fraction.d == 1 && fraction.w == 0) {
4698 blank_characters (dst, attrs, fraction.denominator_start - 1,
4699 2, layout);
4700 fraction.denom_blanked = TRUE;
4701 }
4702 break;
4703 #endif
4704
4705 #ifdef ALLOW_DENOM_REMOVAL
4706 case OP_NUM_FRACTION_SIMPLIFY_NUMERATOR:
4707 if (fraction.denom_blanked && fraction.n == 0){
4708 gsize p = fraction.nominator_start;
4709 gsize length = fraction.denominator_start - p;
4710 blank_characters (dst, attrs, p, length, layout);
4711 fraction.denominator_start -= length - 1;
4712 }
4713 break;
4714 #endif
4715
4716 #ifdef ALLOW_PI_SLASH
4717 case OP_NUM_FRACTION_SIMPLIFY_NUMERATOR_PI:
4718 if (!fraction.blanked &&
4719 (fraction.n == 1 || fraction.n == -1)) {
4720 /* Remove "1". */
4721 gsize p = fraction.nominator_start;
4722 gsize length = fraction.denominator_start - p - 1 -
4723 UNICODE_PI_number_of_bytes;
4724 blank_characters (dst, attrs, p, length, layout);
4725 fraction.denominator_start -= length - 1;
4726 }
4727 break;
4728 #endif
4729
4730 case OP_NUM_GENERAL_MARK:
4731 generalpos = dst->len;
4732 break;
4733
4734 case OP_NUM_GENERAL_DO: {
4735 gboolean is_empty = (dst->len == 0);
4736 GString *gen;
4737 int w = col_width;
4738 if (is_empty) {
4739 gen = dst;
4740 } else {
4741 if (w >= 0) {
4742 w -= measure (dst, layout);
4743 if (w < 0) w = 0;
4744 }
4745 gen = g_string_new (NULL);
4746 }
4747 SUFFIX(go_render_general)
4748 (layout, gen, measure, metrics,
4749 val, w, unicode_minus, numeral_shape,
4750 shape_flags);
4751 if (!is_empty) {
4752 g_string_insert_len (dst, generalpos,
4753 gen->str, gen->len);
4754 g_string_free (gen, TRUE);
4755 }
4756 break;
4757 }
4758
4759 case OP_STR_APPEND_SVAL:
4760 g_string_append (dst, sval);
4761 break;
4762 }
4763 }
4764 }
4765
4766 /*********************************************************************/
4767
4768 #ifdef DEFINE_COMMON
4769 int
go_format_measure_zero(G_GNUC_UNUSED const GString * str,G_GNUC_UNUSED PangoLayout * layout)4770 go_format_measure_zero (G_GNUC_UNUSED const GString *str,
4771 G_GNUC_UNUSED PangoLayout *layout)
4772 {
4773 return 0;
4774 }
4775 #endif
4776
4777 #ifdef DEFINE_COMMON
4778 int
go_format_measure_pango(G_GNUC_UNUSED const GString * str,PangoLayout * layout)4779 go_format_measure_pango (G_GNUC_UNUSED const GString *str,
4780 PangoLayout *layout)
4781 {
4782 int w;
4783 pango_layout_get_size (layout, &w, NULL);
4784 #ifdef DEBUG_GENERAL
4785 g_printerr ("[%s] --> %d\n", str->str, w);
4786 #endif
4787 return w;
4788 }
4789 #endif
4790
4791 #ifdef DEFINE_COMMON
4792 int
go_format_measure_strlen(const GString * str,G_GNUC_UNUSED PangoLayout * layout)4793 go_format_measure_strlen (const GString *str,
4794 G_GNUC_UNUSED PangoLayout *layout)
4795 {
4796 return g_utf8_strlen (str->str, -1);
4797 }
4798 #endif
4799
4800 /*********************************************************************/
4801
4802 #define HANDLE_SIGN(i) do { \
4803 if (unicode_minus || \
4804 ((shape_flags & GO_FMT_SHAPE_SIGNS) \
4805 && (num_shape > 1))) \
4806 convert_sign (str, (i), num_shape, shape_flags); \
4807 } while (0)
4808
4809
4810 #define HANDLE_NUMERAL_SHAPE \
4811 do { \
4812 if (num_shape > 1) { \
4813 /* 0: not set; 1: Western */ \
4814 handle_chinese (str, NULL, num_shape, \
4815 shape_flags | GO_FMT_POSITION_MARKERS); \
4816 handle_ethiopic (str, NULL, num_shape, \
4817 shape_flags | GO_FMT_POSITION_MARKERS); \
4818 convert_numerals (str, 0, str->len - 1, num_shape); \
4819 } \
4820 } while (0)
4821
4822 #ifdef DEFINE_COMMON
4823 static void
drop_zeroes(GString * str,int * prec)4824 drop_zeroes (GString *str, int *prec)
4825 {
4826 if (*prec == 0)
4827 return;
4828
4829 while (str->str[str->len - 1] == '0') {
4830 g_string_truncate (str, str->len - 1);
4831 (*prec)--;
4832 }
4833 if (*prec == 0) {
4834 /* We got "xxxxxx.000" and dropped the zeroes. */
4835 const char *dot = g_utf8_prev_char (str->str + str->len);
4836 g_string_truncate (str, dot - str->str);
4837 }
4838 }
4839
4840 static gboolean
cheap_drop_decimal(GString * str,size_t pos)4841 cheap_drop_decimal (GString *str, size_t pos)
4842 {
4843 char l = str->str[pos];
4844 int carry;
4845
4846 if (!g_ascii_isdigit (l) || l == '5')
4847 return FALSE;
4848
4849 g_string_erase (str, pos, 1);
4850 pos--;
4851
4852 carry = (l > '5');
4853 if (!carry && !g_ascii_isdigit (str->str[pos])) {
4854 const char *dot = g_utf8_prev_char (str->str + pos + 1);
4855 g_string_erase (str, dot - str->str, str->str + pos + 1 - dot);
4856 #ifdef DEBUG_GENERAL
4857 g_printerr ("Removing decimal cheaply\n");
4858 #endif
4859 }
4860
4861 while (carry) {
4862 l = str->str[pos];
4863 if (!g_ascii_isdigit (l))
4864 return FALSE;
4865
4866 if (l < '9') {
4867 str->str[pos] = l + 1;
4868 carry = 0;
4869 } else
4870 str->str[pos] = '0';
4871 pos--;
4872 }
4873
4874 #ifdef DEBUG_GENERAL
4875 g_printerr ("Cheaply dropped decimal\n");
4876 #endif
4877 return TRUE;
4878 }
4879 #endif
4880
4881 static int
SUFFIX(ilog10)4882 SUFFIX(ilog10) (DOUBLE x)
4883 {
4884 if (x >= 1000)
4885 return (int)log10 (x);
4886 if (x >= 100)
4887 return 2;
4888 if (x >= 10)
4889 return 1;
4890 return 0;
4891 }
4892
4893
4894 /**
4895 * go_render_general:
4896 * @layout: Optional #PangoLayout, probably preseeded with font attribute.
4897 * @str: a GString to store (not append!) the resulting string in.
4898 * @measure: (scope call): Function to measure width of string/layout.
4899 * @metrics: Font metrics corresponding to @measure.
4900 * @val: floating-point value. Must be finite.
4901 * @col_width: intended max width of layout in the units that @measure uses.
4902 * A width of -1 means no restriction.
4903 * @unicode_minus: Use unicode minuses, not hyphens.
4904 * @numeral_shape: numeral shape identifier.
4905 * @custom_shape_flags: flags for using @numeral_shape.
4906 *
4907 * Render a floating-point value into @layout in such a way that the
4908 * layouting width does not needlessly exceed @col_width. Optionally
4909 * use unicode minus instead of hyphen.
4910 **/
4911 /**
4912 * go_render_generall:
4913 * @layout: Optional #PangoLayout, probably preseeded with font attribute.
4914 * @str: a GString to store (not append!) the resulting string in.
4915 * @measure: (scope call): Function to measure width of string/layout.
4916 * @metrics: Font metrics corresponding to @mesaure.
4917 * @val: floating-point value. Must be finite.
4918 * @col_width: intended max width of layout in the units that @measure uses.
4919 * A width of -1 means no restriction.
4920 * @unicode_minus: Use unicode minuses, not hyphens.
4921 * @numeral_shape: numeral shape identifier.
4922 * @custom_shape_flags: flags for using @numeral_shape.
4923 *
4924 * Render a floating-point value into @layout in such a way that the
4925 * layouting width does not needlessly exceed @col_width. Optionally
4926 * use unicode minus instead of hyphen.
4927 **/
4928 void
SUFFIX(go_render_general)4929 SUFFIX(go_render_general) (PangoLayout *layout, GString *str,
4930 GOFormatMeasure measure,
4931 const GOFontMetrics *metrics,
4932 DOUBLE val,
4933 int col_width,
4934 gboolean unicode_minus,
4935 guint num_shape,
4936 guint shape_flags)
4937 {
4938 DOUBLE aval;
4939 int prec, safety, digs, digs_as_int;
4940 int maxdigits = SUFFIX(go_format_roundtrip_digits) - 1;
4941 size_t epos;
4942 gboolean rounds_to_0;
4943 int sign_width;
4944 int min_digit_width = metrics->min_digit_width;
4945 gboolean check_val = TRUE;
4946 gboolean try_needed = FALSE;
4947
4948 if (num_shape > 0) {
4949 /* FIXME: We should adjust min_digit_width if num_shape != 0 */
4950 }
4951
4952 sign_width = unicode_minus
4953 ? metrics->minus_width
4954 : metrics->hyphen_width;
4955
4956 if (col_width == -1) {
4957 try_needed = TRUE;
4958 } else {
4959 int w;
4960
4961 w = (col_width - (val <= -0.5 ? sign_width : 0)) / min_digit_width;
4962 if (w <= maxdigits) {
4963 /* We're limited by width. */
4964 maxdigits = w;
4965 check_val = FALSE;
4966 }
4967
4968 try_needed = (w >= maxdigits);
4969 }
4970
4971 if (try_needed) {
4972 #ifdef DEBUG_GENERAL
4973 g_printerr ("Rendering %.20" FORMAT_G " to needed width\n", val);
4974 #endif
4975 go_dtoa (str, "=!^" FORMAT_G, val);
4976 HANDLE_NUMERAL_SHAPE;
4977 HANDLE_SIGN (0);
4978 SETUP_LAYOUT;
4979 if (col_width == -1 || measure (str, layout) <= col_width)
4980 return;
4981 }
4982
4983 #ifdef DEBUG_GENERAL
4984 g_printerr ("Rendering %.20" FORMAT_G " to width %d (<=%d digits)\n",
4985 val, col_width, maxdigits);
4986 #endif
4987 if (val == 0)
4988 goto zero;
4989
4990 aval = SUFFIX(fabs) (val);
4991 if (aval >= SUFFIX(1e15) || aval < SUFFIX(1e-4))
4992 goto e_notation;
4993
4994 /* Number of digits in round(aval). */
4995 digs_as_int = (aval >= 9.5 ? 1 + SUFFIX(ilog10) (aval + 0.5) : 1);
4996
4997 /* Check if there is room for the whole part, including sign. */
4998 safety = metrics->avg_digit_width / 2;
4999
5000 if (digs_as_int * min_digit_width > col_width) {
5001 #ifdef DEBUG_GENERAL
5002 g_printerr ("No room for whole part.\n");
5003 #endif
5004 goto e_notation;
5005 } else if (digs_as_int * metrics->max_digit_width + safety <
5006 col_width - (val > 0 ? 0 : sign_width)) {
5007 #ifdef DEBUG_GENERAL
5008 g_printerr ("Room for whole part.\n");
5009 #endif
5010 if (val == SUFFIX(floor) (val) || digs_as_int == maxdigits) {
5011 #ifdef DEBUG_GENERAL
5012 g_printerr ("Integer or room for nothing else.\n");
5013 #endif
5014 go_dtoa (str, "=^.0" FORMAT_f, val);
5015 HANDLE_NUMERAL_SHAPE;
5016 HANDLE_SIGN (0);
5017 SETUP_LAYOUT;
5018 return;
5019 }
5020 } else {
5021 int w;
5022 #ifdef DEBUG_GENERAL
5023 g_printerr ("Maybe room for whole part.\n");
5024 #endif
5025
5026 go_dtoa (str, "=^.0" FORMAT_f, val);
5027 HANDLE_NUMERAL_SHAPE;
5028 HANDLE_SIGN (0);
5029 SETUP_LAYOUT;
5030 w = measure (str, layout);
5031 if (w > col_width)
5032 goto e_notation;
5033
5034 if (val == SUFFIX(floor) (val) || digs_as_int == maxdigits) {
5035 #ifdef DEBUG_GENERAL
5036 g_printerr ("Integer or room for nothing else.\n");
5037 #endif
5038 return;
5039 }
5040 }
5041
5042 /* Number of digits in [aval]. */
5043 digs = (aval >= 10 ? 1 + SUFFIX(ilog10) (aval) : 1);
5044
5045 prec = maxdigits - digs;
5046 #ifdef DEBUG_GENERAL
5047 g_printerr ("Starting with %d decimals\n", prec);
5048 #endif
5049 go_dtoa (str, "=^.*" FORMAT_f, prec, val);
5050 if (check_val) {
5051 /*
5052 * We're not width-limited; we may have to increase maxdigits
5053 * by one. This is a terribly wasteful way of doing this.
5054 */
5055 if (val != STRTO(str->str, NULL)) {
5056 maxdigits++, prec++;
5057 go_dtoa (str, "=^.*" FORMAT_f, prec, val);
5058 }
5059 }
5060 HANDLE_NUMERAL_SHAPE;
5061 HANDLE_SIGN (0);
5062 drop_zeroes (str, &prec);
5063
5064 while (prec > 0) {
5065 int w;
5066
5067 #ifdef DEBUG_GENERAL
5068 g_printerr ("Trying with %d decimals\n", prec);
5069 #endif
5070 SETUP_LAYOUT;
5071 w = measure (str, layout);
5072 if (w <= col_width) {
5073 #ifdef DEBUG_GENERAL
5074 g_printerr ("Success\n");
5075 #endif
5076 return;
5077 }
5078
5079 prec--;
5080 if (num_shape > 1 || !cheap_drop_decimal (str, str->len - 1)) {
5081 go_dtoa (str, "=^.*" FORMAT_f, prec, val);
5082 drop_zeroes (str, &prec);
5083 HANDLE_NUMERAL_SHAPE;
5084 HANDLE_SIGN (0);
5085 } else
5086 drop_zeroes (str, &prec);
5087 }
5088
5089 SETUP_LAYOUT;
5090 return;
5091
5092 e_notation:
5093 #ifdef DEBUG_GENERAL
5094 g_printerr ("Trying E-notation\n");
5095 #endif
5096 rounds_to_0 = (aval < 0.5);
5097 prec = (col_width -
5098 (val >= 0 ? 0 : sign_width) -
5099 (aval < 1 ? sign_width : metrics->plus_width) -
5100 metrics->E_width) / min_digit_width - 3;
5101 if (prec <= 0) {
5102 #ifdef DEBUG_GENERAL
5103 if (prec == 0)
5104 g_printerr ("Maybe room for E notation with no decimals.\n");
5105 else
5106 g_printerr ("No room for E notation.\n");
5107 #endif
5108 /* Certainly too narrow for precision. */
5109 if (prec == 0 || !rounds_to_0) {
5110 int w;
5111
5112 go_dtoa (str, "=^.0" FORMAT_E, val);
5113 HANDLE_NUMERAL_SHAPE;
5114 HANDLE_SIGN (0);
5115 epos = strchr (str->str, 'E') - str->str;
5116 HANDLE_SIGN (epos + 1);
5117 SETUP_LAYOUT;
5118 if (!rounds_to_0)
5119 return;
5120
5121 w = measure (str, layout);
5122 if (w <= col_width)
5123 return;
5124 }
5125
5126 goto zero;
5127 }
5128 prec = MIN (prec, PREFIX(DIG) - 1);
5129 go_dtoa (str, "=^.*" FORMAT_E, prec, val);
5130 epos = strchr (str->str, 'E') - str->str;
5131 digs = 0;
5132 while (str->str[epos - 1 - digs] == '0')
5133 digs++;
5134 if (digs) {
5135 epos -= digs;
5136 g_string_erase (str, epos, digs);
5137 prec -= digs;
5138 if (prec == 0) {
5139 int dot = 1 + (str->str[0] == '-');
5140 g_string_erase (str, dot, epos - dot);
5141 }
5142 }
5143
5144 while (1) {
5145 int w;
5146
5147 HANDLE_NUMERAL_SHAPE;
5148 HANDLE_SIGN (0);
5149 epos = strchr (str->str + prec + 1, 'E') - str->str;
5150 HANDLE_SIGN (epos + 1);
5151 SETUP_LAYOUT;
5152 w = measure (str, layout);
5153 if (w <= col_width)
5154 return;
5155
5156 if (prec > 2 && w - metrics->max_digit_width > col_width)
5157 prec -= 2;
5158 else {
5159 prec--;
5160 if (prec < 0)
5161 break;
5162 }
5163 go_dtoa (str, "=^.*" FORMAT_E, prec, val);
5164 }
5165
5166 if (rounds_to_0)
5167 goto zero;
5168
5169 SETUP_LAYOUT;
5170 return;
5171
5172 zero:
5173 #ifdef DEBUG_GENERAL
5174 g_printerr ("Zero.\n");
5175 #endif
5176 g_string_assign (str, "0");
5177 SETUP_LAYOUT;
5178 return;
5179 }
5180
5181 #define FREE_NEW_STR do { if (new_str) (void)g_string_free (new_str, TRUE); } while (0)
5182
5183 /**
5184 * go_format_value_gstring:
5185 * @layout: Optional PangoLayout, probably preseeded with font attribute.
5186 * @str: a GString to store (not append!) the resulting string in.
5187 * @measure: (scope call): Function to measure width of string/layout.
5188 * @metrics: Font metrics corresponding to @mesaure.
5189 * @fmt: #GOFormat
5190 * @val: floating-point value. Must be finite.
5191 * @type: a format character
5192 * @sval: a string to append to @str after @val
5193 * @go_color: a color to rende
5194 * @col_width: intended max width of layout in pango units. -1 means
5195 * no restriction.
5196 * @date_conv: #GODateConventions
5197 * @unicode_minus: Use unicode minuses, not hyphens.
5198 *
5199 * Render a floating-point value into @layout in such a way that the
5200 * layouting width does not needlessly exceed @col_width. Optionally
5201 * use unicode minus instead of hyphen.
5202 * Returns: a #GOFormatNumberError
5203 **/
5204 /**
5205 * go_format_value_gstringl:
5206 * @layout: Optional PangoLayout, probably preseeded with font attribute.
5207 * @str: a GString to store (not append!) the resulting string in.
5208 * @measure: (scope call): Function to measure width of string/layout.
5209 * @metrics: Font metrics corresponding to @mesaure.
5210 * @fmt: #GOFormat
5211 * @val: floating-point value. Must be finite.
5212 * @type: a format character
5213 * @sval: a string to append to @str after @val
5214 * @go_color: a color to rende
5215 * @col_width: intended max width of layout in pango units. -1 means
5216 * no restriction.
5217 * @date_conv: #GODateConventions
5218 * @unicode_minus: Use unicode minuses, not hyphens.
5219 *
5220 * Render a floating-point value into @layout in such a way that the
5221 * layouting width does not needlessly exceed @col_width. Optionally
5222 * use unicode minus instead of hyphen.
5223 * Returns: a #GOFormatNumberError
5224 **/
5225 GOFormatNumberError
SUFFIX(go_format_value_gstring)5226 SUFFIX(go_format_value_gstring) (PangoLayout *layout, GString *str,
5227 const GOFormatMeasure measure,
5228 const GOFontMetrics *metrics,
5229 GOFormat const *fmt,
5230 DOUBLE val, char type, const char *sval,
5231 GOColor *go_color,
5232 int col_width,
5233 GODateConventions const *date_conv,
5234 gboolean unicode_minus)
5235 {
5236 gboolean inhibit = FALSE;
5237 GString *new_str = NULL;
5238 GOFormatNumberError err;
5239
5240 g_return_val_if_fail (type == 'F' || sval != NULL,
5241 (GOFormatNumberError)-1);
5242
5243 if (str == NULL)
5244 new_str = str = g_string_new (NULL);
5245 else
5246 g_string_truncate (str, 0);
5247
5248 if (fmt)
5249 fmt = SUFFIX(go_format_specialize) (fmt, val, type, &inhibit);
5250 if (!fmt)
5251 fmt = go_format_general ();
5252 if (go_color)
5253 *go_color = fmt->color;
5254
5255 if (layout && fmt->color != 0) {
5256 /*
5257 * We ignore fully-transparent black, no-one should be able to
5258 * specify that as a color anyway. And it is invisible.
5259 */
5260 PangoAttrList *attrs = pango_layout_get_attributes (layout);
5261 PangoAttribute *attr;
5262 attrs = attrs ? pango_attr_list_copy (attrs) : pango_attr_list_new ();
5263 attr = go_color_to_pango (fmt->color, TRUE);
5264 attr->start_index = 0;
5265 attr->end_index = G_MAXUINT;
5266 pango_attr_list_insert (attrs, attr);
5267 pango_layout_set_attributes (layout, attrs);
5268 pango_attr_list_unref (attrs);
5269 }
5270
5271 if (type == 'F') {
5272 switch (fmt->typ) {
5273 case GO_FMT_TEXT:
5274 if (inhibit)
5275 val = SUFFIX(fabs)(val);
5276 SUFFIX(go_render_general)
5277 (layout, str, measure, metrics,
5278 val,
5279 col_width, unicode_minus, 0, 0);
5280 FREE_NEW_STR;
5281 return GO_FORMAT_NUMBER_OK;
5282
5283 case GO_FMT_NUMBER:
5284 if (val < 0) {
5285 #ifndef ALLOW_NEGATIVE_TIMES
5286 if (fmt->u.number.has_date ||
5287 fmt->u.number.has_time) {
5288 FREE_NEW_STR;
5289 return GO_FORMAT_NUMBER_DATE_ERROR;
5290 }
5291 #endif
5292 if (inhibit)
5293 val = SUFFIX(fabs)(val);
5294 }
5295 #ifdef DEBUG_PROGRAMS
5296 g_printerr ("Executing %s\n", fmt->format);
5297 go_format_dump_program (fmt->u.number.program);
5298 #endif
5299
5300 err = SUFFIX(go_format_execute)
5301 (layout, str,
5302 measure, metrics,
5303 fmt->u.number.program,
5304 col_width,
5305 val, sval, date_conv,
5306 unicode_minus);
5307 FREE_NEW_STR;
5308 return err;
5309
5310 case GO_FMT_EMPTY:
5311 SETUP_LAYOUT;
5312 FREE_NEW_STR;
5313 return GO_FORMAT_NUMBER_OK;
5314
5315 default:
5316 case GO_FMT_INVALID:
5317 case GO_FMT_MARKUP:
5318 case GO_FMT_COND:
5319 SETUP_LAYOUT;
5320 FREE_NEW_STR;
5321 return GO_FORMAT_NUMBER_INVALID_FORMAT;
5322 }
5323 } else {
5324 switch (fmt->typ) {
5325 case GO_FMT_TEXT:
5326 err = SUFFIX(go_format_execute)
5327 (layout, str,
5328 measure, metrics,
5329 fmt->u.text.program,
5330 col_width,
5331 val, sval, date_conv,
5332 unicode_minus);
5333 FREE_NEW_STR;
5334 return err;
5335 case GO_FMT_NUMBER:
5336 g_string_assign (str, sval);
5337 SETUP_LAYOUT;
5338 FREE_NEW_STR;
5339 return GO_FORMAT_NUMBER_OK;
5340
5341 case GO_FMT_EMPTY:
5342 SETUP_LAYOUT;
5343 FREE_NEW_STR;
5344 return GO_FORMAT_NUMBER_OK;
5345
5346 default:
5347 case GO_FMT_INVALID:
5348 case GO_FMT_MARKUP:
5349 case GO_FMT_COND:
5350 SETUP_LAYOUT;
5351 FREE_NEW_STR;
5352 return GO_FORMAT_NUMBER_INVALID_FORMAT;
5353 }
5354 }
5355 }
5356
5357 #undef FREE_NEW_STR
5358
5359 /**
5360 * go_format_value:
5361 * @fmt: a #GOFormat
5362 * @val: value to format
5363 *
5364 * Converts @val into a string using format specified by @fmt.
5365 *
5366 * returns: a newly allocated string containing formated value.
5367 **/
5368
5369 char *
SUFFIX(go_format_value)5370 SUFFIX(go_format_value) (GOFormat const *fmt, DOUBLE val)
5371 {
5372 GString *res = g_string_sized_new (20);
5373 GOFormatNumberError err = SUFFIX(go_format_value_gstring)
5374 (NULL, res,
5375 go_format_measure_strlen,
5376 go_font_metrics_unit,
5377 fmt,
5378 val, 'F', NULL, NULL,
5379 -1, NULL, FALSE);
5380 if (err) {
5381 /* Invalid number for format. */
5382 g_string_assign (res, "#####");
5383 }
5384 return g_string_free (res, FALSE);
5385 }
5386
5387
5388
5389 #ifdef DEFINE_COMMON
5390 /**
5391 * _go_number_format_init: (skip)
5392 */
5393 void
_go_number_format_init(void)5394 _go_number_format_init (void)
5395 {
5396 double l10 = log10 (FLT_RADIX);
5397 go_format_roundtrip_digits =
5398 (int)ceil (DBL_MANT_DIG * l10) + (l10 != (int)l10);
5399 #ifdef GOFFICE_WITH_LONG_DOUBLE
5400 go_format_roundtrip_digitsl =
5401 (int)ceil (LDBL_MANT_DIG * l10) + (l10 != (int)l10);
5402 #endif
5403
5404 style_format_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
5405 NULL, (GDestroyNotify) go_format_unref);
5406 }
5407 #endif
5408
5409 #ifdef DEFINE_COMMON
5410 static void
cb_format_leak(G_GNUC_UNUSED gpointer key,gpointer value,G_GNUC_UNUSED gpointer user_data)5411 cb_format_leak (G_GNUC_UNUSED gpointer key, gpointer value, G_GNUC_UNUSED gpointer user_data)
5412 {
5413 GOFormat const *gf = value;
5414 if (gf->ref_count != 1)
5415 g_printerr ("Leaking GOFormat at %p [%s].\n", gf, gf->format);
5416 }
5417 #endif
5418
5419 #ifdef DEFINE_COMMON
5420 /**
5421 * _go_number_format_shutdown: (skip)
5422 */
5423 void
_go_number_format_shutdown(void)5424 _go_number_format_shutdown (void)
5425 {
5426 GHashTable *tmp;
5427
5428 go_format_unref (default_percentage_fmt);
5429 default_percentage_fmt = NULL;
5430
5431 go_format_unref (default_money_fmt);
5432 default_money_fmt = NULL;
5433
5434 go_format_unref (default_accounting_fmt);
5435 default_accounting_fmt = NULL;
5436
5437 go_format_unref (default_date_fmt);
5438 default_date_fmt = NULL;
5439
5440 go_format_unref (default_time_fmt);
5441 default_time_fmt = NULL;
5442
5443 go_format_unref (default_date_time_fmt);
5444 default_date_time_fmt = NULL;
5445
5446 go_format_unref (default_general_fmt);
5447 default_general_fmt = NULL;
5448
5449 go_format_unref (default_empty_fmt);
5450 default_empty_fmt = NULL;
5451
5452 tmp = style_format_hash;
5453 style_format_hash = NULL;
5454 g_hash_table_foreach (tmp, cb_format_leak, NULL);
5455 g_hash_table_destroy (tmp);
5456 }
5457 #endif
5458
5459 /****************************************************************************/
5460
5461 #ifdef DEFINE_COMMON
5462
5463 #undef DEBUG_LOCALIZATION
5464
5465 /**
5466 * go_format_str_localize:
5467 * @str: A *valid* format string
5468 *
5469 * Localizes the given format string, i.e., changes decimal dots to the
5470 * locale's notion of that and performs other such transformations.
5471 *
5472 * Returns: a localized format string, or %NULL if the format was not valid.
5473 **/
5474 char *
go_format_str_localize(char const * str)5475 go_format_str_localize (char const *str)
5476 {
5477 GString *res;
5478 GString const *comma = go_locale_get_thousand ();
5479 GString const *decimal = go_locale_get_decimal ();
5480
5481 g_return_val_if_fail (str != NULL, NULL);
5482
5483 #ifdef DEBUG_LOCALIZATION
5484 g_printerr ("Localize in : [%s]\n", str);
5485 #endif
5486
5487 res = g_string_new (NULL);
5488 while (1) {
5489 GOFormatParseState state;
5490 const GOFormatParseState *pstate = &state;
5491 const char *tail;
5492 unsigned tno;
5493
5494 memset (&state, 0, sizeof (state));
5495 tail = go_format_preparse (str, &state, TRUE, FALSE);
5496 if (!tail) {
5497 g_array_free (state.tokens, TRUE);
5498 g_string_free (res, TRUE);
5499 return NULL;
5500 }
5501
5502 for (tno = 0; tno < state.tokens->len; tno++) {
5503 const GOFormatParseItem *ti = &GET_TOKEN(tno);
5504 const char *tstr = ti->tstr;
5505 const char *end = (tno + 1 == state.tokens->len)
5506 ? tail
5507 : GET_TOKEN(tno + 1).tstr;
5508
5509 switch (ti->token) {
5510 case TOK_ERROR:
5511 g_array_free (state.tokens, TRUE);
5512 g_string_free (res, TRUE);
5513 return NULL;
5514 case TOK_GENERAL:
5515 g_string_append (res, _("General"));
5516 break;
5517 case TOK_CONDITION:
5518 while (tstr != end) {
5519 if (*tstr == '.') {
5520 go_string_append_gstring (res, decimal);
5521 tstr++;
5522 } else if (strncmp (tstr, decimal->str, decimal->len) == 0) {
5523 /* 1000,00 becomes 1000\,00 */
5524 g_string_append_c (res, '\\');
5525 g_string_append_c (res, *tstr++);
5526 } else
5527 g_string_append_c (res, *tstr++);
5528 }
5529 break;
5530
5531 case TOK_COLOR:
5532 g_string_append_c (res, '[');
5533 if (state.color_named)
5534 g_string_append (res, _(format_colors[state.color_n].name));
5535 else
5536 g_string_append_printf (res, "Color%d", state.color_n);
5537 g_string_append_c (res, ']');
5538 break;
5539
5540 case TOK_DECIMAL:
5541 if (state.is_number ||
5542 (state.is_date && *end == '0'))
5543 go_string_append_gstring (res, decimal);
5544 else
5545 goto regular;
5546 break;
5547
5548 case TOK_THOUSAND:
5549 if (!state.is_number)
5550 goto regular;
5551 go_string_append_gstring (res, comma);
5552 break;
5553
5554 default:
5555 if (strncmp (tstr, decimal->str, decimal->len) == 0 ||
5556 (state.is_number &&
5557 strncmp (tstr, comma->str, comma->len) == 0)) {
5558 /* In particular, neither "." nor "," */
5559 g_string_append_c (res, '\\');
5560 }
5561
5562 regular:
5563 g_string_append_len (res, tstr, end - tstr);
5564 }
5565 }
5566
5567 g_array_free (state.tokens, TRUE);
5568
5569 str = tail;
5570 if (*str == 0)
5571 break;
5572 g_string_append_c (res, *str++);
5573 }
5574
5575 #ifdef DEBUG_LOCALIZATION
5576 g_printerr ("Localize out: [%s]\n", res->str);
5577 #endif
5578 return g_string_free (res, FALSE);
5579 }
5580
5581 /**
5582 * go_format_str_delocalize:
5583 * @str: A *valid* localized format string
5584 *
5585 * De-localizes the given format string, i.e., changes locale's decimal
5586 * separators to dots and performs other such transformations.
5587 *
5588 * Returns: a non-local format string, or %NULL if the format was not valid.
5589 **/
5590 char *
go_format_str_delocalize(char const * str)5591 go_format_str_delocalize (char const *str)
5592 {
5593 GString *res;
5594 GString const *comma = go_locale_get_thousand ();
5595 GString const *decimal = go_locale_get_decimal ();
5596 gboolean decimal_needs_quoting =
5597 strcmp (decimal->str, ".") == 0 ||
5598 strcmp (decimal->str, ",") == 0;
5599 gboolean comma_needs_quoting =
5600 strcmp (comma->str, ".") == 0 ||
5601 strcmp (comma->str, ",") == 0;
5602
5603 g_return_val_if_fail (str != NULL, NULL);
5604
5605 #ifdef DEBUG_LOCALIZATION
5606 g_printerr ("Delocalize in : [%s]\n", str);
5607 #endif
5608
5609 res = g_string_new (NULL);
5610 while (1) {
5611 GOFormatParseState state;
5612 const GOFormatParseState *pstate = &state;
5613 const char *tail;
5614 unsigned tno;
5615
5616 memset (&state, 0, sizeof (state));
5617 tail = go_format_preparse (str, &state, TRUE, TRUE);
5618 if (!tail) {
5619 g_array_free (state.tokens, TRUE);
5620 g_string_free (res, TRUE);
5621 return NULL;
5622 }
5623
5624 for (tno = 0; tno < state.tokens->len; tno++) {
5625 const GOFormatParseItem *ti = &GET_TOKEN(tno);
5626 const char *tstr = ti->tstr;
5627 const char *end = (tno + 1 == state.tokens->len)
5628 ? tail
5629 : GET_TOKEN(tno + 1).tstr;
5630
5631 switch (ti->token) {
5632 case TOK_ERROR:
5633 g_array_free (state.tokens, TRUE);
5634 g_string_free (res, TRUE);
5635 return NULL;
5636 case TOK_GENERAL:
5637 g_string_append (res, "General");
5638 break;
5639 case TOK_CONDITION:
5640 while (tstr != end) {
5641 if (strncmp (tstr, decimal->str, decimal->len) == 0) {
5642 g_string_append_c (res, '.');
5643 tstr += decimal->len;
5644 } else if (*tstr == '.') {
5645 /* 1000.00 becomes 1000\.00 */
5646 g_string_append_c (res, '\\');
5647 g_string_append_c (res, *tstr++);
5648 } else
5649 g_string_append_c (res, *tstr++);
5650 }
5651 break;
5652
5653 case TOK_COLOR:
5654 g_string_append_c (res, '[');
5655 if (state.color_named)
5656 g_string_append (res, format_colors[state.color_n].name);
5657 else
5658 g_string_append_printf (res, "Color%d", state.color_n);
5659 g_string_append_c (res, ']');
5660 break;
5661
5662 case '\\':
5663 if ((strncmp (tstr + 1, decimal->str, decimal->len) == 0 && !decimal_needs_quoting) ||
5664 (strncmp (str + 1, comma->str, comma->len) == 0 && !comma_needs_quoting))
5665 tstr++;
5666 g_string_append_len (res, tstr, str - tstr);
5667 break;
5668
5669 case TOK_DECIMAL:
5670 if (state.is_number ||
5671 (state.is_date && *end == '0'))
5672 g_string_append_c (res, '.');
5673 else
5674 goto regular;
5675 break;
5676
5677 case TOK_THOUSAND:
5678 if (!state.is_number)
5679 goto regular;
5680 g_string_append_c (res, ',');
5681 break;
5682
5683 case TOK_ESCAPED_CHAR:
5684 if (ti->tstr[1] == ' ') {
5685 g_string_append_c (res, ' ');
5686 break;
5687 }
5688 /* no break */
5689 default:
5690 if (*tstr == '.' &&
5691 (state.is_number || (state.is_date && *str == '0')))
5692 g_string_append_c (res, '\\');
5693 else if (*tstr == ',' && state.is_number)
5694 g_string_append_c (res, '\\');
5695 /* Fall through. */
5696
5697 regular:
5698 g_string_append_len (res, tstr, end - tstr);
5699 }
5700 }
5701
5702 g_array_free (state.tokens, TRUE);
5703
5704 str = tail;
5705 if (*str == 0)
5706 break;
5707 g_string_append_c (res, *str++);
5708 }
5709
5710 #ifdef DEBUG_LOCALIZATION
5711 g_printerr ("Delocalize out: [%s]\n", res->str);
5712 #endif
5713 return g_string_free (res, FALSE);
5714 }
5715
5716 static GOFormat *
make_frobbed_format(char * str,const GOFormat * fmt)5717 make_frobbed_format (char *str, const GOFormat *fmt)
5718 {
5719 GOFormat *res;
5720
5721 if (strcmp (str, fmt->format) == 0)
5722 res = NULL;
5723 else {
5724 #if 0
5725 g_printerr ("Frob: [%s] -> [%s]\n", fmt->format, str);
5726 #endif
5727 res = go_format_new_from_XL (str);
5728 if (res->typ == GO_FMT_INVALID) {
5729 go_format_unref (res);
5730 res = NULL;
5731 }
5732 }
5733
5734 g_free (str);
5735 return res;
5736 }
5737
5738 /**
5739 * go_format_inc_precision :
5740 * @fmt: #GOFormat
5741 *
5742 * Increases the displayed precision for @fmt by one digit.
5743 *
5744 * Returns: NULL if the new format would not change things
5745 **/
5746 GOFormat *
go_format_inc_precision(GOFormat const * fmt)5747 go_format_inc_precision (GOFormat const *fmt)
5748 {
5749 GString *res = g_string_new (NULL);
5750 const char *str = fmt->format;
5751 gssize last_zero = -1;
5752 GOFormatDetails details;
5753 gboolean exact;
5754
5755 go_format_get_details (fmt, &details, &exact);
5756 if (exact) {
5757 switch (details.family) {
5758 case GO_FORMAT_NUMBER:
5759 case GO_FORMAT_SCIENTIFIC:
5760 case GO_FORMAT_CURRENCY:
5761 case GO_FORMAT_ACCOUNTING:
5762 case GO_FORMAT_PERCENTAGE:
5763 if (details.num_decimals >= MAX_DECIMALS)
5764 return NULL;
5765 details.num_decimals++;
5766 go_format_generate_str (res, &details);
5767 return make_frobbed_format (g_string_free (res, FALSE),
5768 fmt);
5769 case GO_FORMAT_GENERAL:
5770 case GO_FORMAT_TEXT:
5771 return NULL;
5772 default:
5773 break;
5774 }
5775 }
5776
5777 /* Fall-back. */
5778 while (1) {
5779 const char *tstr = str;
5780 GOFormatTokenType tt;
5781 int t = go_format_token (&str, &tt);
5782
5783 switch (t) {
5784 case TOK_ERROR:
5785 g_string_free (res, TRUE);
5786 return NULL;
5787
5788 case 0:
5789 case ';':
5790 g_string_append_len (res, tstr, str - tstr);
5791 if (last_zero >= 0)
5792 g_string_insert_len (res, last_zero + 1,
5793 ".0", 2);
5794 last_zero = -1;
5795 if (t == ';')
5796 break;
5797 return make_frobbed_format (g_string_free (res, FALSE), fmt);
5798
5799 case 's': case 'S':
5800 g_string_append_c (res, t);
5801 while (*str == 's' || *str == 'S')
5802 g_string_append_c (res, *str++);
5803 if (str[0] != '.')
5804 g_string_append_c (res, '.');
5805 else
5806 g_string_append_c (res, *str++);
5807 g_string_append_c (res, '0');
5808 last_zero = -2;
5809 break;
5810
5811 case TOK_DECIMAL: {
5812 int n = 0;
5813 g_string_append_c (res, *tstr);
5814 while (*str == '0') {
5815 g_string_append_c (res, *str++);
5816 n++;
5817 }
5818 if (n < DBL_DIG)
5819 g_string_append_c (res, '0');
5820 last_zero = -2;
5821 break;
5822 }
5823
5824 case 'E':
5825 if (last_zero != -2) {
5826 if (last_zero>=0)
5827 g_string_insert_len (res, last_zero + 1, ".0",2);
5828 else
5829 g_string_append (res, ".0");
5830 last_zero = -2;
5831 }
5832 g_string_append_c (res, t);
5833 break;
5834
5835 case '0':
5836 if (last_zero != -2)
5837 last_zero = res->len;
5838 /* Fall through. */
5839
5840 default:
5841 g_string_append_len (res, tstr, str - tstr);
5842 }
5843 }
5844 }
5845
5846 /**
5847 * go_format_dec_precision :
5848 * @fmt: #GOFormat
5849 *
5850 * Decreases the displayed precision for @fmt by one digit.
5851 *
5852 * Returns: NULL if the new format would not change things
5853 **/
5854 GOFormat *
go_format_dec_precision(GOFormat const * fmt)5855 go_format_dec_precision (GOFormat const *fmt)
5856 {
5857 GString *res = g_string_new (NULL);
5858 const char *str = fmt->format;
5859 GOFormatDetails details;
5860 gboolean exact;
5861
5862 go_format_get_details (fmt, &details, &exact);
5863 if (exact) {
5864 switch (details.family) {
5865 case GO_FORMAT_NUMBER:
5866 case GO_FORMAT_SCIENTIFIC:
5867 case GO_FORMAT_CURRENCY:
5868 case GO_FORMAT_ACCOUNTING:
5869 case GO_FORMAT_PERCENTAGE:
5870 if (details.num_decimals <= 0)
5871 return NULL;
5872 details.num_decimals--;
5873 go_format_generate_str (res, &details);
5874 return make_frobbed_format (g_string_free (res, FALSE),
5875 fmt);
5876 case GO_FORMAT_GENERAL:
5877 case GO_FORMAT_TEXT:
5878 return NULL;
5879 default:
5880 break;
5881 }
5882 }
5883
5884 /* Fall-back. */
5885 while (1) {
5886 const char *tstr = str;
5887 GOFormatTokenType tt;
5888 int t = go_format_token (&str, &tt);
5889
5890 switch (t) {
5891 case TOK_ERROR:
5892 g_string_free (res, TRUE);
5893 return NULL;
5894
5895 case 0:
5896 return make_frobbed_format (g_string_free (res, FALSE), fmt);
5897
5898 case TOK_DECIMAL:
5899 if (str[0] == '0') {
5900 if (str[1] == '0')
5901 g_string_append_c (res, '.');
5902 str++;
5903 break;
5904 }
5905 /* Fall through */
5906
5907 default:
5908 g_string_append_len (res, tstr, str - tstr);
5909 }
5910 }
5911 }
5912
5913 GOFormat *
go_format_toggle_1000sep(GOFormat const * fmt)5914 go_format_toggle_1000sep (GOFormat const *fmt)
5915 {
5916 GString *res;
5917 const char *str;
5918 int commapos = -1;
5919 int numstart = -1;
5920
5921 g_return_val_if_fail (fmt != NULL, NULL);
5922
5923 res = g_string_new (NULL);
5924 str = fmt->format;
5925
5926 /* No need to go via go_format_get_details since we can handle
5927 all the standard formats with the code below. */
5928
5929 while (1) {
5930 const char *tstr = str;
5931 GOFormatTokenType tt;
5932 int t = go_format_token (&str, &tt);
5933
5934 if (numstart == -1 && (tt & TT_STARTS_NUMBER))
5935 numstart = res->len;
5936
5937 switch (t) {
5938 case TOK_ERROR:
5939 g_string_free (res, TRUE);
5940 return NULL;
5941
5942 case 0:
5943 case ';':
5944 if (numstart == -1) {
5945 /* Nothing. */
5946 } else if (commapos == -1) {
5947 g_string_insert (res, numstart, "#,##");
5948 } else {
5949 int n = 0;
5950
5951 g_string_erase (res, commapos, 1);
5952 commapos = -1;
5953
5954 while (res->str[numstart + n] == '#')
5955 n++;
5956
5957 if (n && res->str[numstart + n] == '0')
5958 g_string_erase (res, numstart, n);
5959 }
5960
5961 if (t == 0)
5962 return make_frobbed_format
5963 (g_string_free (res, FALSE), fmt);
5964
5965 numstart = -1;
5966 break;
5967
5968 case TOK_THOUSAND:
5969 if (numstart != -1 && comma_is_thousands (tstr)) {
5970 if (commapos != -1)
5971 g_string_erase (res, commapos, 1);
5972 commapos = res->len;
5973 }
5974 break;
5975
5976 case TOK_GENERAL:
5977 if (go_format_is_general (fmt)) {
5978 /* Format is just "General" and color etc. */
5979 g_string_append (res, "#,##0");
5980 continue;
5981 }
5982 break;
5983
5984 default:
5985 break;
5986 }
5987
5988 g_string_append_len (res, tstr, str - tstr);
5989 }
5990 }
5991 #endif
5992
5993 #ifdef DEFINE_COMMON
5994 static gboolean
cb_attrs_as_string(PangoAttribute * a,GString * accum)5995 cb_attrs_as_string (PangoAttribute *a, GString *accum)
5996 {
5997 PangoColor const *c;
5998 char buf[16];
5999
6000 if (a->start_index >= a->end_index)
6001 return FALSE;
6002
6003 switch (a->klass->type) {
6004 case PANGO_ATTR_FAMILY:
6005 g_string_append_printf (accum, "[family=%s",
6006 ((PangoAttrString *)a)->value);
6007 break;
6008 case PANGO_ATTR_SIZE:
6009 g_string_append_printf (accum, "[size=%d",
6010 ((PangoAttrInt *)a)->value);
6011 break;
6012 case PANGO_ATTR_RISE:
6013 g_string_append_printf (accum, "[rise=%d",
6014 ((PangoAttrInt *)a)->value);
6015 break;
6016 case PANGO_ATTR_SCALE:
6017 g_string_append (accum, "[scale=");
6018 g_ascii_formatd (buf, sizeof (buf), "%.2f",
6019 ((PangoAttrFloat *)a)->value);
6020 g_string_append (accum, buf);
6021 break;
6022 case PANGO_ATTR_STYLE:
6023 g_string_append_printf (accum, "[italic=%d",
6024 (((PangoAttrInt *)a)->value == PANGO_STYLE_ITALIC) ? 1 : 0);
6025 break;
6026 case PANGO_ATTR_WEIGHT: {
6027 int w = ((PangoAttrInt *)a)->value;
6028 /* We are scaling the weight so that earlier versions that used only 0/1 */
6029 /* can still read the new formats and we can read the old ones. */
6030 const int Z = PANGO_WEIGHT_NORMAL;
6031 const double R = PANGO_WEIGHT_BOLD - Z;
6032 double d = (w - Z) / R;
6033 /* We cap to prevent older versions from seeing -1 or less. */
6034 if (d <= -1) d = -0.999;
6035 g_string_append (accum, "[bold=");
6036 g_ascii_formatd (buf, sizeof (buf), "%.3g", d);
6037 g_string_append (accum, buf);
6038 break;
6039 }
6040 case PANGO_ATTR_STRIKETHROUGH:
6041 g_string_append_printf (accum, "[strikethrough=%d",
6042 ((PangoAttrInt *)a)->value ? 1 : 0);
6043 break;
6044 case PANGO_ATTR_UNDERLINE:
6045 switch (((PangoAttrInt *)a)->value) {
6046 case PANGO_UNDERLINE_NONE :
6047 g_string_append (accum, "[underline=none");
6048 break;
6049 case PANGO_UNDERLINE_SINGLE :
6050 g_string_append (accum, "[underline=single");
6051 break;
6052 case PANGO_UNDERLINE_DOUBLE :
6053 g_string_append (accum, "[underline=double");
6054 break;
6055 case PANGO_UNDERLINE_LOW :
6056 g_string_append (accum, "[underline=low");
6057 break;
6058 case PANGO_UNDERLINE_ERROR :
6059 g_string_append (accum, "[underline=error");
6060 break;
6061 }
6062 break;
6063
6064 case PANGO_ATTR_FOREGROUND:
6065 c = &((PangoAttrColor *)a)->color;
6066 g_string_append_printf (accum, "[color=%02xx%02xx%02x",
6067 ((c->red & 0xff00) >> 8),
6068 ((c->green & 0xff00) >> 8),
6069 ((c->blue & 0xff00) >> 8));
6070 break;
6071 default:
6072 if (a->klass->type == go_pango_attr_subscript_get_attr_type ()) {
6073 g_string_append_printf (accum, "[subscript=%d",
6074 ((GOPangoAttrSubscript *)a)->val ?
6075 1:0);
6076 break;
6077 } else if (a->klass->type == go_pango_attr_superscript_get_attr_type ()) {
6078 g_string_append_printf (accum, "[superscript=%d",
6079 ((GOPangoAttrSuperscript *)a)->val ?
6080 1:0);
6081 break;
6082 } else return FALSE; /* ignored */
6083 }
6084 g_string_append_printf (accum, ":%u:%u]", a->start_index, a->end_index);
6085 return FALSE;
6086 }
6087 #endif
6088
6089 #ifdef DEFINE_COMMON
6090 static PangoAttrList *
go_format_parse_markup(char * str)6091 go_format_parse_markup (char *str)
6092 {
6093 PangoAttrList *attrs;
6094 PangoAttribute *a;
6095 char *closer, *val, *val_end;
6096 unsigned len;
6097 int r, g, b;
6098
6099 g_return_val_if_fail (*str == '@', NULL);
6100
6101 attrs = pango_attr_list_new ();
6102 for (str++ ; *str ; str = closer + 1) {
6103 if (*str != '[') goto bail;
6104 str++;
6105
6106 val = strchr (str, '=');
6107 if (!val) goto bail;
6108 len = val - str;
6109 val++;
6110
6111 val_end = strchr (val, ':');
6112 if (!val_end) goto bail;
6113
6114 closer = strchr (val_end, ']');
6115 if (!closer) goto bail;
6116 *val_end = '\0';
6117 *closer = '\0';
6118
6119 a = NULL;
6120 switch (len) {
6121 case 4:
6122 if (0 == strncmp (str, "size", 4))
6123 a = pango_attr_size_new (atoi (val));
6124 else if (0 == strncmp (str, "bold", 4)) {
6125 double d = g_ascii_strtod (val, NULL);
6126 const int Z = PANGO_WEIGHT_NORMAL;
6127 const double R = PANGO_WEIGHT_BOLD - Z;
6128 int w = (int)(d * R + Z);
6129 a = pango_attr_weight_new (w);
6130 } else if (0 == strncmp (str, "rise", 4))
6131 a = pango_attr_rise_new (atoi (val));
6132 break;
6133
6134 case 5:
6135 if (0 == strncmp (str, "color", 5) &&
6136 3 == sscanf (val, "%02xx%02xx%02x", &r, &g, &b))
6137 a = pango_attr_foreground_new ((r << 8) | r, (g << 8) | g, (b << 8) | b);
6138 else if (0 == strncmp (str, "scale", 5))
6139 a = pango_attr_scale_new (g_ascii_strtod (val, NULL));
6140 break;
6141
6142 case 6:
6143 if (0 == strncmp (str, "family", 6))
6144 a = pango_attr_family_new (val);
6145 else if (0 == strncmp (str, "italic", 6))
6146 a = pango_attr_style_new (atoi (val) ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
6147 break;
6148
6149 case 9:
6150 if (0 == strncmp (str, "underline", 9)) {
6151 if (0 == strcmp (val, "none"))
6152 a = pango_attr_underline_new (PANGO_UNDERLINE_NONE);
6153 else if (0 == strcmp (val, "single"))
6154 a = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
6155 else if (0 == strcmp (val, "double"))
6156 a = pango_attr_underline_new (PANGO_UNDERLINE_DOUBLE);
6157 else if (0 == strcmp (val, "low"))
6158 a = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
6159 else if (0 == strcmp (val, "error"))
6160 a = pango_attr_underline_new (PANGO_UNDERLINE_ERROR);
6161 } else if (0 == strncmp (str, "subscript", 9))
6162 a = go_pango_attr_subscript_new (atoi (val));
6163 break;
6164
6165 case 11:
6166 if (0 == strncmp (str, "superscript", 11))
6167 a = go_pango_attr_superscript_new (atoi (val));
6168 break;
6169
6170 case 13:
6171 if (0 == strncmp (str, "strikethrough", 13))
6172 a = pango_attr_strikethrough_new (atoi (val) != 0);
6173 break;
6174 }
6175
6176 if (a != NULL && val_end != NULL) {
6177 if (sscanf (val_end+1, "%u:%u]", &a->start_index, &a->end_index) == 2 &&
6178 a->start_index < a->end_index)
6179 pango_attr_list_insert (attrs, a);
6180 else
6181 pango_attribute_destroy (a);
6182 }
6183
6184 *val_end = ':';
6185 *closer = ']';
6186 }
6187
6188 return attrs;
6189
6190 bail:
6191 pango_attr_list_unref (attrs);
6192 return NULL;
6193 }
6194 #endif
6195
6196 #ifdef DEFINE_COMMON
6197 /**
6198 * go_format_new_from_XL :
6199 * @str: XL descriptor in UTF-8 encoding.
6200 *
6201 * Returns: Looks up and potentially creates a GOFormat from the supplied
6202 * string in XL format.
6203 **/
6204 GOFormat *
go_format_new_from_XL(char const * str)6205 go_format_new_from_XL (char const *str)
6206 {
6207 GOFormat *format;
6208
6209 g_return_val_if_fail (str != NULL, go_format_general ());
6210
6211 format = g_hash_table_lookup (style_format_hash, str);
6212
6213 if (format == NULL) {
6214 if (str[0] == '@' && str[1] == '[') {
6215 PangoAttrList *attrs;
6216 char *desc_copy = g_strdup (str);
6217 attrs = go_format_parse_markup (desc_copy);
6218 if (attrs) {
6219 format = go_format_create (GO_FMT_MARKUP, str);
6220 format->u.markup = attrs;
6221 } else
6222 format = go_format_create (GO_FMT_INVALID, str);
6223
6224 g_free (desc_copy);
6225 } else
6226 format = go_format_parse (str);
6227
6228 g_hash_table_insert (style_format_hash,
6229 format->format,
6230 format);
6231 }
6232
6233 #ifdef DEBUG_REF_COUNT
6234 g_message ("%s: format=%p '%s' ref_count=%d",
6235 G_GNUC_FUNCTION,
6236 format, format->format, format->ref_count);
6237 #endif
6238
6239 return go_format_ref (format);
6240 }
6241 #endif
6242
6243 #ifdef DEFINE_COMMON
6244 /**
6245 * go_format_new_markup :
6246 * @markup: #PangoAttrList
6247 * @add_ref: boolean
6248 *
6249 * If @add_ref is FALSE absorb the reference to @markup, otherwise add a
6250 * reference.
6251 *
6252 * Returns: A new format.
6253 **/
6254 GOFormat *
go_format_new_markup(PangoAttrList * markup,gboolean add_ref)6255 go_format_new_markup (PangoAttrList *markup, gboolean add_ref)
6256 {
6257 GOFormat *fmt;
6258
6259 GString *accum = g_string_new ("@");
6260 pango_attr_list_filter (markup,
6261 (PangoAttrFilterFunc) cb_attrs_as_string, accum);
6262 fmt = go_format_new_from_XL (accum->str);
6263 g_string_free (accum, TRUE);
6264
6265 if (!add_ref)
6266 pango_attr_list_unref (markup);
6267
6268 return fmt;
6269 }
6270 #endif
6271
6272
6273 #ifdef DEFINE_COMMON
6274 /**
6275 * go_format_as_XL:
6276 * @fmt: a #GOFormat
6277 *
6278 * Returns: the XL style format strint.
6279 */
6280 const char *
go_format_as_XL(GOFormat const * fmt)6281 go_format_as_XL (GOFormat const *fmt)
6282 {
6283 g_return_val_if_fail (fmt != NULL, "General");
6284
6285 return fmt->format;
6286 }
6287 #endif
6288
6289 #ifdef DEFINE_COMMON
6290 gboolean
go_format_eq(GOFormat const * a,GOFormat const * b)6291 go_format_eq (GOFormat const *a, GOFormat const *b)
6292 {
6293 /*
6294 * The way we create GOFormat *s ensures that we don't need
6295 * to compare anything but pointers.
6296 */
6297 return (a == b);
6298 }
6299 #endif
6300
6301 #ifdef DEFINE_COMMON
6302 /**
6303 * go_format_ref :
6304 * @fmt: a #GOFormat
6305 *
6306 * Adds a reference to a GOFormat.
6307 *
6308 * Returns: @gf
6309 **/
6310 GOFormat *
go_format_ref(GOFormat const * gf_)6311 go_format_ref (GOFormat const *gf_)
6312 {
6313 GOFormat *gf = (GOFormat *)gf_;
6314
6315 g_return_val_if_fail (gf != NULL, NULL);
6316
6317 gf->ref_count++;
6318 #ifdef DEBUG_REF_COUNT
6319 g_message ("%s: format=%p '%s' ref_count=%d",
6320 G_GNUC_FUNCTION,
6321 gf, gf->format, gf->ref_count);
6322 #endif
6323
6324 return gf;
6325 }
6326 #endif
6327
6328
6329 #ifdef DEFINE_COMMON
6330 /**
6331 * go_format_unref :
6332 * @fmt: (transfer full) (nullable): a #GOFormat
6333 *
6334 * Removes a reference to @fmt, freeing when it goes to zero.
6335 **/
6336 void
go_format_unref(GOFormat const * gf_)6337 go_format_unref (GOFormat const *gf_)
6338 {
6339 GOFormat *gf = (GOFormat *)gf_;
6340
6341 if (gf == NULL)
6342 return;
6343
6344 g_return_if_fail (gf->ref_count > 0);
6345
6346 gf->ref_count--;
6347 #ifdef DEBUG_REF_COUNT
6348 g_message ("%s: format=%p '%s' ref_count=%d",
6349 G_GNUC_FUNCTION,
6350 gf, gf->format, gf->ref_count);
6351 #endif
6352 if (gf->ref_count > 1)
6353 return;
6354
6355 if (gf->ref_count == 1) {
6356 if (NULL != style_format_hash &&
6357 gf_ == g_hash_table_lookup (style_format_hash, gf_->format))
6358 g_hash_table_remove (style_format_hash, gf_->format);
6359 return;
6360 }
6361
6362 switch (gf->typ) {
6363 case GO_FMT_COND: {
6364 int i;
6365 for (i = 0; i < gf->u.cond.n; i++)
6366 go_format_unref (gf->u.cond.conditions[i].fmt);
6367 g_free (gf->u.cond.conditions);
6368 break;
6369 }
6370 case GO_FMT_NUMBER:
6371 g_free (gf->u.number.program);
6372 break;
6373 case GO_FMT_TEXT:
6374 g_free (gf->u.text.program);
6375 break;
6376 case GO_FMT_EMPTY:
6377 case GO_FMT_INVALID:
6378 break;
6379 case GO_FMT_MARKUP:
6380 if (gf->u.markup)
6381 pango_attr_list_unref (gf->u.markup);
6382 break;
6383 }
6384
6385 g_free (gf->format);
6386 g_free (gf);
6387 }
6388 #endif
6389
6390 #ifdef DEFINE_COMMON
6391 GType
go_format_get_type(void)6392 go_format_get_type (void)
6393 {
6394 static GType t = 0;
6395
6396 if (t == 0)
6397 t = g_boxed_type_register_static ("GOFormat",
6398 (GBoxedCopyFunc)go_format_ref,
6399 (GBoxedFreeFunc)go_format_unref);
6400 return t;
6401 }
6402 #endif
6403
6404
6405 #ifdef DEFINE_COMMON
6406 /**
6407 * go_format_is_invalid:
6408 * @fmt: Format to query
6409 *
6410 * Returns: TRUE if, and if only, the format is invalid
6411 **/
6412 gboolean
go_format_is_invalid(GOFormat const * fmt)6413 go_format_is_invalid (GOFormat const *fmt)
6414 {
6415 g_return_val_if_fail (fmt != NULL, TRUE);
6416 return fmt->typ == GO_FMT_INVALID;
6417 }
6418 #endif
6419
6420 #ifdef DEFINE_COMMON
6421 /**
6422 * go_format_is_general:
6423 * @fmt: Format to query
6424 *
6425 * Returns: %TRUE if the format is "General", possibly with condition,
6426 * color, and/or locale. ("xGeneral" is thus not considered to be General
6427 * for the purpose of this function.)
6428 * Returns %FALSE otherwise.
6429 **/
6430 gboolean
go_format_is_general(GOFormat const * fmt)6431 go_format_is_general (GOFormat const *fmt)
6432 {
6433 g_return_val_if_fail (fmt != NULL, FALSE);
6434 return fmt->typ == GO_FMT_NUMBER && fmt->u.number.is_general;
6435 }
6436 #endif
6437
6438 #ifdef DEFINE_COMMON
6439 /**
6440 * go_format_is_markup:
6441 * @fmt: Format to query
6442 *
6443 * Returns: %TRUE if the format is a markup format and %FALSE otherwise.
6444 */
6445 gboolean
go_format_is_markup(GOFormat const * fmt)6446 go_format_is_markup (GOFormat const *fmt)
6447 {
6448 g_return_val_if_fail (fmt != NULL, FALSE);
6449 return fmt->typ == GO_FMT_MARKUP;
6450 }
6451 #endif
6452
6453 #ifdef DEFINE_COMMON
6454 /**
6455 * go_format_is_text:
6456 * @fmt: Format to query
6457 *
6458 * Returns: %TRUE if the format is a text format and %FALSE otherwise.
6459 **/
6460 gboolean
go_format_is_text(GOFormat const * fmt)6461 go_format_is_text (GOFormat const *fmt)
6462 {
6463 g_return_val_if_fail (fmt != NULL, FALSE);
6464 return fmt->typ == GO_FMT_TEXT;
6465 }
6466 #endif
6467
6468 #ifdef DEFINE_COMMON
6469 /**
6470 * go_format_is_var_width:
6471 * @fmt: Format to query
6472 *
6473 * Returns: %TRUE if the format is variable width, i.e., can stretch.
6474 **/
6475 gboolean
go_format_is_var_width(GOFormat const * fmt)6476 go_format_is_var_width (GOFormat const *fmt)
6477 {
6478 g_return_val_if_fail (fmt != NULL, FALSE);
6479
6480 if (fmt->has_fill != 0)
6481 return TRUE;
6482
6483 switch (fmt->typ) {
6484 case GO_FMT_COND: {
6485 int i;
6486 for (i = 0; i < fmt->u.cond.n; i++)
6487 if (go_format_is_var_width (fmt->u.cond.conditions[i].fmt))
6488 return TRUE;
6489 return FALSE;
6490 }
6491 case GO_FMT_NUMBER:
6492 return fmt->u.number.has_general;
6493 default:
6494 return FALSE;
6495 }
6496 }
6497 #endif
6498
6499 #ifdef DEFINE_COMMON
6500 /**
6501 * go_format_is_date:
6502 * @fmt: Format to query
6503 *
6504 * Returns:
6505 * +2 if the format is a date format with time
6506 * +1 if the format is any other date format.
6507 * 0 if the format is not a date format.
6508 * -1 if the format is inconsistent.
6509 **/
6510 int
go_format_is_date(GOFormat const * fmt)6511 go_format_is_date (GOFormat const *fmt)
6512 {
6513 g_return_val_if_fail (fmt != NULL, -1);
6514 if (go_format_get_family (fmt) != GO_FORMAT_DATE)
6515 return 0;
6516 return fmt->u.number.has_time ? +2 : +1;
6517 }
6518 #endif
6519
6520 #ifdef DEFINE_COMMON
6521 /**
6522 * go_format_is_time:
6523 * @fmt: Format to query
6524 *
6525 * Returns:
6526 * +2 if the format is a time format with elapsed hour/minute/second
6527 * +1 if the format is any other time format
6528 * 0 if the format is not a time format
6529 * -1 if the format is inconsistent.
6530 **/
6531 int
go_format_is_time(GOFormat const * fmt)6532 go_format_is_time (GOFormat const *fmt)
6533 {
6534 g_return_val_if_fail (fmt != NULL, -1);
6535 if (go_format_get_family (fmt) != GO_FORMAT_TIME)
6536 return 0;
6537 return fmt->u.number.has_elapsed ? +2 : +1;
6538 }
6539 #endif
6540
6541 #ifdef DEFINE_COMMON
6542 /**
6543 * go_format_month_before_day:
6544 * @fmt: Format to query
6545 *
6546 * Returns:
6547 * 0, if format is a date format with day and month in that order
6548 * 1, if format is a date format with month and day in that order, unless
6549 * 2, if format is a date with year before month before day
6550 * -1, otherwise.
6551 **/
6552 int
go_format_month_before_day(GOFormat const * fmt)6553 go_format_month_before_day (GOFormat const *fmt)
6554 {
6555 g_return_val_if_fail (fmt != NULL, -1);
6556
6557 if (go_format_is_date (fmt) < 1)
6558 return -1;
6559 if (fmt->u.number.date_ybm)
6560 return +2;
6561 if (fmt->u.number.date_mbd)
6562 return +1;
6563 if (fmt->u.number.date_dbm)
6564 return 0;
6565 return -1;
6566 }
6567 #endif
6568
6569
6570 #ifdef DEFINE_COMMON
6571 /**
6572 * go_format_has_year:
6573 * @fmt: Format to query
6574 *
6575 * Returns: TRUE if format is a number format with a year specifier
6576 * FALSE otherwise.
6577 **/
6578 gboolean
go_format_has_year(GOFormat const * fmt)6579 go_format_has_year (GOFormat const *fmt)
6580 {
6581 g_return_val_if_fail (fmt != NULL, FALSE);
6582
6583 return (fmt->typ == GO_FMT_NUMBER &&
6584 fmt->u.number.has_year);
6585 }
6586 #endif
6587
6588 #ifdef DEFINE_COMMON
6589 /**
6590 * go_format_has_month:
6591 * @fmt: Format to query
6592 *
6593 * Returns: TRUE if format is a number format with a year specifier
6594 * FALSE otherwise.
6595 **/
6596 gboolean
go_format_has_month(GOFormat const * fmt)6597 go_format_has_month (GOFormat const *fmt)
6598 {
6599 g_return_val_if_fail (fmt != NULL, FALSE);
6600
6601 return (fmt->typ == GO_FMT_NUMBER &&
6602 fmt->u.number.has_month);
6603 }
6604 #endif
6605
6606 #ifdef DEFINE_COMMON
6607 /**
6608 * go_format_has_day:
6609 * @fmt: Format to query
6610 *
6611 * Returns: TRUE if format is a number format with a day-of-month specifier
6612 * FALSE otherwise.
6613 **/
6614 gboolean
go_format_has_day(GOFormat const * fmt)6615 go_format_has_day (GOFormat const *fmt)
6616 {
6617 g_return_val_if_fail (fmt != NULL, FALSE);
6618
6619 return (fmt->typ == GO_FMT_NUMBER &&
6620 fmt->u.number.has_day);
6621 }
6622 #endif
6623
6624 #ifdef DEFINE_COMMON
6625 /**
6626 * go_format_has_hour:
6627 * @fmt: Format to query
6628 *
6629 * Returns: TRUE if format is a number format with an hour specifier
6630 * FALSE otherwise.
6631 **/
6632 gboolean
go_format_has_hour(GOFormat const * fmt)6633 go_format_has_hour (GOFormat const *fmt)
6634 {
6635 g_return_val_if_fail (fmt != NULL, FALSE);
6636
6637 return (fmt->typ == GO_FMT_NUMBER &&
6638 fmt->u.number.has_time &&
6639 fmt->u.number.has_hour);
6640 }
6641 #endif
6642
6643
6644 #ifdef DEFINE_COMMON
6645 /**
6646 * go_format_has_minute:
6647 * @fmt: Format to query
6648 *
6649 * Returns: TRUE if format is a number format with a minute specifier
6650 * FALSE otherwise.
6651 **/
6652 gboolean
go_format_has_minute(GOFormat const * fmt)6653 go_format_has_minute (GOFormat const *fmt)
6654 {
6655 g_return_val_if_fail (fmt != NULL, FALSE);
6656
6657 return (fmt->typ == GO_FMT_NUMBER &&
6658 fmt->u.number.has_time &&
6659 fmt->u.number.has_minute);
6660 }
6661 #endif
6662
6663
6664 #ifdef DEFINE_COMMON
6665 /**
6666 * go_format_get_magic:
6667 * @fmt: Format to query
6668 *
6669 * Returns: a non-zero magic code for certain formats, such as system date.
6670 **/
6671 GOFormatMagic
go_format_get_magic(GOFormat const * fmt)6672 go_format_get_magic (GOFormat const *fmt)
6673 {
6674 g_return_val_if_fail (fmt != NULL, GO_FORMAT_MAGIC_NONE);
6675
6676 return fmt->magic;
6677 }
6678 #endif
6679
6680
6681 #ifdef DEFINE_COMMON
6682 GOFormat *
go_format_new_magic(GOFormatMagic m)6683 go_format_new_magic (GOFormatMagic m)
6684 {
6685 const char *suffix;
6686 char *s;
6687 GOFormat *res;
6688
6689 /*
6690 * Note: the format strings here are actually fixed and do not relate
6691 * to how these formats are rendered.
6692 */
6693
6694 switch (m) {
6695 default:
6696 return NULL;
6697
6698 case GO_FORMAT_MAGIC_LONG_DATE:
6699 suffix = "dddd, mmmm dd, yyyy";
6700 break;
6701
6702 case GO_FORMAT_MAGIC_MEDIUM_DATE:
6703 suffix = "d-mmm-yy";
6704 break;
6705
6706 case GO_FORMAT_MAGIC_SHORT_DATE:
6707 suffix = "m/d/yy";
6708 break;
6709
6710 case GO_FORMAT_MAGIC_SHORT_DATETIME:
6711 suffix = "m/d/yy h:mm";
6712 break;
6713
6714 case GO_FORMAT_MAGIC_LONG_TIME:
6715 suffix = "h:mm:ss AM/PM";
6716 break;
6717
6718 case GO_FORMAT_MAGIC_MEDIUM_TIME:
6719 suffix = "h:mm AM/PM";
6720 break;
6721
6722 case GO_FORMAT_MAGIC_SHORT_TIME:
6723 suffix = "hh:mm";
6724 break;
6725 }
6726
6727 s = g_strdup_printf ("[$-%x]%s", (unsigned)m, suffix);
6728 res = go_format_new_from_XL (s);
6729 g_free (s);
6730 return res;
6731 }
6732 #endif
6733
6734
6735 /**
6736 * go_format_specialize:
6737 * @fmt: the format to specialize
6738 * @val: the value to use
6739 * @type: the type of value; 'F' for numeric, 'B' for boolean, 'S' for string.
6740 * @inhibit_minus: (out): set to %TRUE if the format dictates that a minus
6741 * should be inhibited when rendering negative values.
6742 *
6743 * Returns: (transfer none): @fmt format, presumably a conditional format,
6744 * specialized to @value of @type.
6745 */
6746
6747 /**
6748 * go_format_specializel:
6749 * @fmt: the format to specialize
6750 * @val: the value to use
6751 * @type: the type of value; 'F' for numeric, 'B' for boolean, 'S' for string.
6752 * @inhibit_minus: (out): set to %TRUE if the format dictates that a minus
6753 * should be inhibited when rendering negative values.
6754 *
6755 * Returns: (transfer none): @fmt format, presumably a conditional format,
6756 * specialized to @value of @type.
6757 */
6758 const GOFormat *
SUFFIX(go_format_specialize)6759 SUFFIX(go_format_specialize) (GOFormat const *fmt, DOUBLE val, char type,
6760 gboolean *inhibit_minus)
6761 {
6762 int i;
6763 gboolean is_number = (type == 'F');
6764 GOFormat *last_implicit_num = NULL;
6765 gboolean has_implicit = FALSE;
6766 gboolean dummy;
6767
6768 g_return_val_if_fail (fmt != NULL, NULL);
6769
6770 if (inhibit_minus == NULL)
6771 inhibit_minus = &dummy;
6772
6773 *inhibit_minus = FALSE;
6774
6775 if (fmt->typ != GO_FMT_COND) {
6776 if (fmt->typ == GO_FMT_EMPTY && !is_number)
6777 return go_format_general ();
6778 return fmt;
6779 }
6780
6781 for (i = 0; i < fmt->u.cond.n; i++) {
6782 GOFormatCondition *c = fmt->u.cond.conditions + i;
6783 gboolean cond;
6784
6785 if (c->implicit) {
6786 if (c->op != GO_FMT_COND_TEXT)
6787 last_implicit_num = c->fmt;
6788 has_implicit = TRUE;
6789 } else {
6790 if (has_implicit)
6791 *inhibit_minus = FALSE;
6792 last_implicit_num = NULL;
6793 has_implicit = FALSE;
6794 }
6795
6796 switch (c->op) {
6797 case GO_FMT_COND_EQ:
6798 cond = (is_number && val == c->val);
6799 break;
6800 case GO_FMT_COND_NE:
6801 cond = (is_number && val != c->val);
6802 break;
6803 case GO_FMT_COND_LT:
6804 cond = (is_number && val < c->val);
6805 break;
6806 case GO_FMT_COND_LE:
6807 cond = (is_number && val <= c->val);
6808 break;
6809 case GO_FMT_COND_GT:
6810 cond = (is_number && val > c->val);
6811 break;
6812 case GO_FMT_COND_GE:
6813 cond = (is_number && val >= c->val);
6814 break;
6815 case GO_FMT_COND_TEXT:
6816 cond = (type == 'S' || type == 'B');
6817 break;
6818 case GO_FMT_COND_NONTEXT:
6819 cond = is_number;
6820 break;
6821 default:
6822 cond = TRUE;
6823 break;
6824 }
6825
6826 if (cond) {
6827 if (c->true_inhibits_minus)
6828 *inhibit_minus = TRUE;
6829 return c->fmt;
6830 }
6831
6832 if (c->false_inhibits_minus)
6833 *inhibit_minus = TRUE;
6834 }
6835
6836 *inhibit_minus = FALSE;
6837
6838 if (is_number) {
6839 if (last_implicit_num)
6840 return last_implicit_num;
6841 else if (has_implicit)
6842 return go_format_empty ();
6843 }
6844
6845 return go_format_general ();
6846 }
6847
6848 #ifdef DEFINE_COMMON
6849 /**
6850 * go_format_general:
6851 *
6852 * Returns: (transfer none): the general format
6853 */
6854 GOFormat *
go_format_general(void)6855 go_format_general (void)
6856 {
6857 if (!default_general_fmt)
6858 default_general_fmt = go_format_new_from_XL (
6859 _go_format_builtins (GO_FORMAT_GENERAL)[0]);
6860 return default_general_fmt;
6861 }
6862 #endif
6863
6864 #ifdef DEFINE_COMMON
6865 /**
6866 * go_format_empty:
6867 *
6868 * Returns: (transfer none): the empty format
6869 */
6870 GOFormat *
go_format_empty(void)6871 go_format_empty (void)
6872 {
6873 if (!default_empty_fmt)
6874 default_empty_fmt = go_format_new_from_XL ("");
6875 return default_empty_fmt;
6876 }
6877 #endif
6878
6879 #ifdef DEFINE_COMMON
6880 /**
6881 * go_format_default_date:
6882 *
6883 * Returns: (transfer none): the default date format
6884 */
6885 GOFormat *
go_format_default_date(void)6886 go_format_default_date (void)
6887 {
6888 if (!default_date_fmt)
6889 default_date_fmt =
6890 go_format_new_magic (GO_FORMAT_MAGIC_SHORT_DATE);
6891 return default_date_fmt;
6892 }
6893 #endif
6894
6895 #ifdef DEFINE_COMMON
6896 /**
6897 * go_format_default_time:
6898 *
6899 * Returns: (transfer none): the default time format
6900 */
6901 GOFormat *
go_format_default_time(void)6902 go_format_default_time (void)
6903 {
6904 if (!default_time_fmt)
6905 default_time_fmt =
6906 go_format_new_magic (GO_FORMAT_MAGIC_SHORT_TIME);
6907 return default_time_fmt;
6908 }
6909 #endif
6910
6911 #ifdef DEFINE_COMMON
6912 /**
6913 * go_format_default_date_time:
6914 *
6915 * Returns: (transfer none): the default date-and-time format
6916 */
6917 GOFormat *
go_format_default_date_time(void)6918 go_format_default_date_time (void)
6919 {
6920 if (!default_date_time_fmt)
6921 default_date_time_fmt =
6922 go_format_new_magic (GO_FORMAT_MAGIC_SHORT_DATETIME);
6923 return default_date_time_fmt;
6924 }
6925 #endif
6926
6927 #ifdef DEFINE_COMMON
6928 /**
6929 * go_format_default_percentage:
6930 *
6931 * Returns: (transfer none): the default percentage format
6932 */
6933 GOFormat *
go_format_default_percentage(void)6934 go_format_default_percentage (void)
6935 {
6936 if (!default_percentage_fmt)
6937 default_percentage_fmt = go_format_new_from_XL (
6938 _go_format_builtins (GO_FORMAT_PERCENTAGE) [1]);
6939 return default_percentage_fmt;
6940 }
6941 #endif
6942
6943 #ifdef DEFINE_COMMON
6944 /**
6945 * go_format_default_money:
6946 *
6947 * Returns: (transfer none): the default monetary format
6948 */
6949 GOFormat *
go_format_default_money(void)6950 go_format_default_money (void)
6951 {
6952 if (!default_money_fmt)
6953 default_money_fmt = go_format_new_from_XL (
6954 _go_format_builtins (GO_FORMAT_CURRENCY)[2]);
6955 return default_money_fmt;
6956 }
6957 #endif
6958
6959 #ifdef DEFINE_COMMON
6960 /**
6961 * go_format_default_accounting:
6962 *
6963 * Returns: (transfer none): the default accounting format
6964 */
6965 GOFormat *
go_format_default_accounting(void)6966 go_format_default_accounting (void)
6967 {
6968 if (!default_accounting_fmt)
6969 default_accounting_fmt = go_format_new_from_XL (
6970 _go_format_builtins (GO_FORMAT_ACCOUNTING)[2]);
6971 return default_accounting_fmt;
6972 }
6973 #endif
6974
6975 #ifdef DEFINE_COMMON
6976 /**
6977 * go_format_generate_number_str:
6978 * @dst: GString to append format string to.
6979 * @min_digits: minimum number of digits before decimal separator.
6980 * @num_decimals: number of decimals
6981 * @thousands_sep: if true, use a thousands separator.
6982 * @negative_red: if true, make negative values red.
6983 * @negative_paren: if true, enclose negative values in parentheses.
6984 * @prefix: optional string to place before number part of the format
6985 * @postfix: optional string to place after number part of the format
6986 *
6987 * Generates a format string for a number format with the given
6988 * parameters and appends it to @dst.
6989 **/
6990 void
go_format_generate_number_str(GString * dst,int min_digits,int num_decimals,gboolean thousands_sep,gboolean negative_red,gboolean negative_paren,const char * prefix,const char * postfix)6991 go_format_generate_number_str (GString *dst,
6992 int min_digits,
6993 int num_decimals,
6994 gboolean thousands_sep,
6995 gboolean negative_red,
6996 gboolean negative_paren,
6997 const char *prefix, const char *postfix)
6998 {
6999 size_t init_len = dst->len;
7000 size_t plain_len;
7001
7002 min_digits = CLAMP (min_digits, 0, MAX_DECIMALS);
7003 num_decimals = CLAMP (num_decimals, 0, MAX_DECIMALS);
7004
7005 if (prefix)
7006 g_string_append (dst, prefix);
7007
7008 if (thousands_sep) {
7009 switch (min_digits) {
7010 case 0: g_string_append (dst, "#,###"); break;
7011 case 1: g_string_append (dst, "#,##0"); break;
7012 case 2: g_string_append (dst, "#,#00"); break;
7013 case 3: g_string_append (dst, "#,000"); break;
7014 default: {
7015 int r = min_digits % 3;
7016 go_string_append_c_n (dst, '0', r ? r : 3);
7017 for (r = min_digits; r > 3; r -= 3)
7018 g_string_append (dst, ",000");
7019 }
7020 }
7021 } else {
7022 if (min_digits > 0)
7023 go_string_append_c_n (dst, '0', min_digits);
7024 else
7025 g_string_append_c (dst, '#');
7026 }
7027
7028 if (num_decimals > 0) {
7029 g_string_append_c (dst, '.');
7030 go_string_append_c_n (dst, '0', num_decimals);
7031 }
7032
7033 if (postfix)
7034 g_string_append (dst, postfix);
7035
7036 plain_len = dst->len - init_len;
7037
7038 if (negative_paren)
7039 g_string_append (dst, "_)");
7040 if (negative_paren || negative_red)
7041 g_string_append_c (dst, ';');
7042 if (negative_red)
7043 g_string_append (dst, "[Red]");
7044 if (negative_paren)
7045 g_string_append (dst, "(");
7046 if (negative_paren || negative_red)
7047 g_string_append_len (dst, dst->str + init_len, plain_len);
7048 if (negative_paren)
7049 g_string_append (dst, ")");
7050 }
7051 #endif
7052
7053 #ifdef DEFINE_COMMON
7054 static void
go_format_generate_scientific_str(GString * dst,GOFormatDetails const * details)7055 go_format_generate_scientific_str (GString *dst, GOFormatDetails const *details)
7056 {
7057 /* Maximum not terribly important. */
7058 int step = CLAMP (details->exponent_step, 1, 10);
7059 int num_decimals = CLAMP (details->num_decimals, 0, MAX_DECIMALS);
7060 int digits;
7061
7062 go_string_append_c_n (dst, '#', step - 1);
7063 if (details->simplify_mantissa)
7064 g_string_append_c (dst, '#');
7065 else
7066 g_string_append_c (dst, '0');
7067
7068 if (num_decimals > 0) {
7069 g_string_append_c (dst, '.');
7070 go_string_append_c_n (dst, '0', num_decimals);
7071 }
7072 if (details->scale == 3)
7073 g_string_append_c (dst, '\'');
7074 if (details->use_markup)
7075 g_string_append_c (dst, 'E');
7076 g_string_append_c (dst, 'E');
7077 if (details->append_SI)
7078 g_string_append_len (dst, "SI", 2);
7079
7080 g_string_append_c (dst, details->exponent_sign_forced ? '+' : '-');
7081 /* Maximum not terribly important. */
7082 digits = CLAMP (details->exponent_digits, 1, 10);
7083 go_string_append_c_n (dst, '0', digits);
7084
7085 if (details->append_SI && details->appended_SI_unit != NULL) {
7086 g_string_append_c (dst, '\"');
7087 g_string_append (dst, details->appended_SI_unit);
7088 g_string_append_c (dst, '\"');
7089 }
7090 }
7091 #endif
7092
7093 #ifdef DEFINE_COMMON
7094 static void
go_format_generate_fraction_str(GString * dst,GOFormatDetails const * details)7095 go_format_generate_fraction_str (GString *dst, GOFormatDetails const *details)
7096 {
7097 /* Maximum not terribly important. */
7098 int numerator_min_digits = CLAMP (details->numerator_min_digits, 0, 30);
7099 int denominator_max_digits = CLAMP (details->denominator_max_digits, 1, 30);
7100 int denominator_min_digits = CLAMP (details->denominator_min_digits, 0, denominator_max_digits);
7101 int denominator = CLAMP (details->denominator, 2, G_MAXINT);
7102 int num_digits;
7103
7104 if (details->split_fraction) {
7105 /* Maximum not terribly important. */
7106 int min_digits = CLAMP (details->min_digits, 0, 30);
7107 if (min_digits > 0)
7108 go_string_append_c_n (dst, '0', min_digits);
7109 else
7110 g_string_append_c (dst, '#');
7111 g_string_append_c (dst, ' ');
7112 }
7113
7114 if (details->automatic_denominator)
7115 num_digits = denominator_max_digits - numerator_min_digits;
7116 else
7117 num_digits = cnt_digits (denominator) - numerator_min_digits;
7118
7119 if (num_digits > 0)
7120 go_string_append_c_n (dst, '?', num_digits);
7121 if (numerator_min_digits > 0)
7122 go_string_append_c_n (dst, '0', numerator_min_digits);
7123
7124 if (details->pi_scale)
7125 g_string_append (dst, " pi/");
7126 else
7127 g_string_append_c (dst, '/');
7128
7129 if (details->automatic_denominator) {
7130 go_string_append_c_n (dst, '?',
7131 denominator_max_digits - denominator_min_digits);
7132 go_string_append_c_n (dst, '0',
7133 denominator_min_digits);
7134 } else
7135 g_string_append_printf (dst, "%d", denominator);
7136 }
7137 #endif
7138
7139 #ifdef DEFINE_COMMON
7140 static void
go_format_generate_accounting_str(GString * dst,GOFormatDetails const * details)7141 go_format_generate_accounting_str (GString *dst,
7142 GOFormatDetails const *details)
7143 {
7144 int num_decimals = CLAMP (details->num_decimals, 0, MAX_DECIMALS);
7145 GString *num = g_string_new (NULL);
7146 GString *sym = g_string_new (NULL);
7147 GString *q = g_string_new (NULL);
7148 const char *symstr;
7149 const char *quote = "\"";
7150 GOFormatCurrency const *currency = details->currency;
7151
7152 if (!currency)
7153 currency = _go_format_currencies ();
7154
7155 symstr = currency->symbol;
7156 switch (g_utf8_get_char (symstr)) {
7157 case '$':
7158 case UNICODE_POUNDS1:
7159 case UNICODE_YEN:
7160 case UNICODE_EURO:
7161 if ((g_utf8_next_char (symstr))[0])
7162 break; /* Something follows. */
7163 /* Fall through. */
7164 case 0:
7165 case '[':
7166 quote = "";
7167 break;
7168 default:
7169 break;
7170 }
7171
7172 go_format_generate_number_str (num, details->min_digits,
7173 num_decimals,
7174 details->thousands_sep,
7175 FALSE, FALSE, NULL, NULL);
7176 go_string_append_c_n (q, '?', num_decimals);
7177
7178 if (currency->precedes) {
7179 g_string_append (sym, quote);
7180 g_string_append (sym, symstr);
7181 g_string_append (sym, quote);
7182 g_string_append (sym, "* ");
7183 if (currency->has_space) g_string_append_c (sym, ' ');
7184
7185 g_string_append_printf
7186 (dst,
7187 "_(%s%s_);_(%s(%s);_(%s\"-\"%s_);_(@_)",
7188 sym->str, num->str,
7189 sym->str, num->str,
7190 sym->str, q->str);
7191 } else {
7192 g_string_append (sym, "* ");
7193 if (currency->has_space) g_string_append_c (sym, ' ');
7194 g_string_append (sym, quote);
7195 g_string_append (sym, symstr);
7196 g_string_append (sym, quote);
7197
7198 g_string_append_printf
7199 (dst,
7200 "_(%s%s_);_((%s)%s;_(\"-\"%s%s_);_(@_)",
7201 num->str, sym->str,
7202 num->str, sym->str,
7203 q->str, sym->str);
7204 }
7205
7206 g_string_free (num, TRUE);
7207 g_string_free (q, TRUE);
7208 g_string_free (sym, TRUE);
7209 }
7210 #endif
7211
7212 #ifdef DEFINE_COMMON
7213 static void
go_format_generate_currency_str(GString * dst,GOFormatDetails const * details)7214 go_format_generate_currency_str (GString *dst,
7215 GOFormatDetails const *details)
7216 {
7217 GString *prefix = NULL;
7218 GString *postfix = NULL;
7219 gboolean extra_quotes;
7220 GOFormatCurrency const *currency = details->currency;
7221
7222 if (!currency)
7223 currency = _go_format_currencies ();
7224
7225 extra_quotes = (details->force_quoted &&
7226 currency->symbol[0] != '"' &&
7227 currency->symbol[0] != 0);
7228
7229 if (currency->precedes) {
7230 prefix = g_string_new (NULL);
7231 if (extra_quotes) g_string_append_c (prefix, '"');
7232 g_string_append (prefix, currency->symbol);
7233 if (extra_quotes) g_string_append_c (prefix, '"');
7234 if (currency->has_space) g_string_append_c (prefix, ' ');
7235 } else {
7236 postfix = g_string_new (NULL);
7237 if (currency->has_space)
7238 g_string_append_c (postfix, ' ');
7239 if (extra_quotes) g_string_append_c (postfix, '"');
7240 g_string_append (postfix, currency->symbol);
7241 if (extra_quotes) g_string_append_c (postfix, '"');
7242 }
7243
7244 go_format_generate_number_str (dst,
7245 details->min_digits,
7246 details->num_decimals,
7247 details->thousands_sep,
7248 details->negative_red,
7249 details->negative_paren,
7250 prefix ? prefix->str : NULL,
7251 postfix ? postfix->str : NULL);
7252
7253 if (prefix) g_string_free (prefix, TRUE);
7254 if (postfix) g_string_free (postfix, TRUE);
7255 }
7256 #endif
7257
7258 #ifdef DEFINE_COMMON
7259 static const GOFormatCurrency *
find_currency(char const * ptr,gsize len,gboolean precedes)7260 find_currency (char const *ptr, gsize len, gboolean precedes)
7261 {
7262 int i;
7263 gboolean has_space;
7264 gboolean quoted;
7265
7266 if (len <= 0)
7267 return NULL;
7268
7269 if (precedes) {
7270 has_space = ptr[len - 1] == ' ';
7271 if (has_space)
7272 len--;
7273 } else {
7274 has_space = ptr[0] == ' ';
7275 if (has_space)
7276 len--, ptr++;
7277 }
7278
7279 quoted = len > 2 && ptr[0] == '\"' && ptr[len - 1] == '\"';
7280
7281 for (i = 1; _go_format_currencies ()[i].symbol; i++) {
7282 const GOFormatCurrency *ci = _go_format_currencies() + i;
7283
7284 if (ci->precedes != precedes)
7285 continue;
7286
7287 if (strncmp (ci->symbol, ptr, len) == 0) {
7288 return ci;
7289 }
7290
7291 /* Allow quoting of things that aren't [$FOO] */
7292 if (quoted && ci->symbol[0] != '[' &&
7293 strncmp (ci->symbol, ptr + 1, len - 2) == 0) {
7294 return ci;
7295 }
7296 }
7297
7298 return NULL;
7299 }
7300 #endif
7301
7302 #ifdef DEFINE_COMMON
7303 void
go_format_generate_str(GString * dst,GOFormatDetails const * details)7304 go_format_generate_str (GString *dst, GOFormatDetails const *details)
7305 {
7306 switch (details->family) {
7307 case GO_FORMAT_TEXT:
7308 g_string_append (dst, "@");
7309 break;
7310 case GO_FORMAT_GENERAL:
7311 g_string_append (dst, "General");
7312 break;
7313 case GO_FORMAT_NUMBER:
7314 go_format_generate_number_str
7315 (dst,
7316 details->min_digits,
7317 details->num_decimals,
7318 details->thousands_sep,
7319 details->negative_red,
7320 details->negative_paren,
7321 NULL, NULL);
7322 break;
7323 case GO_FORMAT_CURRENCY:
7324 go_format_generate_currency_str (dst, details);
7325 break;
7326 case GO_FORMAT_ACCOUNTING:
7327 go_format_generate_accounting_str (dst, details);
7328 break;
7329 case GO_FORMAT_PERCENTAGE:
7330 go_format_generate_number_str
7331 (dst,
7332 details->min_digits,
7333 details->num_decimals,
7334 details->thousands_sep,
7335 details->negative_red,
7336 details->negative_paren,
7337 NULL, "%");
7338 break;
7339 case GO_FORMAT_SCIENTIFIC:
7340 go_format_generate_scientific_str (dst, details);
7341 break;
7342 case GO_FORMAT_FRACTION:
7343 go_format_generate_fraction_str (dst, details);
7344 break;
7345 default:
7346 break;
7347 }
7348 }
7349 #endif
7350
7351 #ifdef DEFINE_COMMON
7352 static GOFormatDetails *
go_format_details_ref(GOFormatDetails * details)7353 go_format_details_ref (GOFormatDetails * details)
7354 {
7355 details->ref_count++;
7356 return details;
7357 }
7358
7359 GType
go_format_details_get_type(void)7360 go_format_details_get_type (void)
7361 {
7362 static GType t = 0;
7363
7364 if (t == 0) {
7365 t = g_boxed_type_register_static ("GOFormatDetails",
7366 (GBoxedCopyFunc)go_format_details_ref,
7367 (GBoxedFreeFunc)go_format_details_free);
7368 }
7369 return t;
7370 }
7371
7372 GOFormatDetails *
go_format_details_new(GOFormatFamily family)7373 go_format_details_new (GOFormatFamily family)
7374 {
7375 GOFormatDetails *res = g_new (GOFormatDetails, 1);
7376 go_format_details_init (res, family);
7377 res->ref_count = 1;
7378 return res;
7379 }
7380 #endif
7381
7382
7383 #ifdef DEFINE_COMMON
7384 void
go_format_details_finalize(GOFormatDetails * details)7385 go_format_details_finalize (GOFormatDetails *details)
7386 {
7387 g_free (details->appended_SI_unit);
7388 details->appended_SI_unit = NULL;
7389 /* We do not own ->currency. */
7390 }
7391 #endif
7392
7393 #ifdef DEFINE_COMMON
7394 void
go_format_details_free(GOFormatDetails * details)7395 go_format_details_free (GOFormatDetails *details)
7396 {
7397 if (!details || details->ref_count-- > 1)
7398 return;
7399 go_format_details_finalize (details);
7400 g_free (details);
7401 }
7402 #endif
7403
7404
7405 #ifdef DEFINE_COMMON
7406 void
go_format_details_init(GOFormatDetails * details,GOFormatFamily family)7407 go_format_details_init (GOFormatDetails *details, GOFormatFamily family)
7408 {
7409 g_return_if_fail (details != NULL);
7410
7411 memset (details, 0, sizeof (*details));
7412 /* Assign reasonable defaults. For most, the memset is just fine. */
7413 details->family = family;
7414 details->thousands_sep = (family == GO_FORMAT_ACCOUNTING ||
7415 family == GO_FORMAT_CURRENCY);
7416 details->magic = GO_FORMAT_MAGIC_NONE;
7417 details->exponent_sign_forced = FALSE;
7418 details->exponent_step = 1;
7419 details->exponent_digits = 2;
7420 details->min_digits = (family == GO_FORMAT_FRACTION) ? 0 : 1;
7421 details->split_fraction = TRUE;
7422 details->denominator_max_digits = 1;
7423 details->denominator = 8;
7424 details->automatic_denominator = TRUE;
7425 }
7426 #endif
7427
7428
7429 #ifdef DEFINE_COMMON
7430 /**
7431 * go_format_get_details:
7432 * @fmt: Format to quert
7433 * @dst: (out): #GOFormatDetails
7434 * @exact: (out) (optional): whether @dst describes @fmt exactly.
7435 */
7436 void
go_format_get_details(GOFormat const * fmt,GOFormatDetails * dst,gboolean * exact)7437 go_format_get_details (GOFormat const *fmt,
7438 GOFormatDetails *dst,
7439 gboolean *exact)
7440 {
7441 const char *str;
7442 GString *newstr = NULL;
7443 static GOFormatCurrency currency;
7444
7445 g_return_if_fail (fmt != NULL);
7446 g_return_if_fail (dst != NULL);
7447
7448 if (exact) *exact = FALSE;
7449 go_format_details_init (dst, go_format_get_family (fmt));
7450 dst->magic = go_format_get_magic (fmt);
7451
7452 str = go_format_as_XL (fmt);
7453
7454 switch (dst->family) {
7455 case GO_FORMAT_NUMBER:
7456 case GO_FORMAT_CURRENCY:
7457 case GO_FORMAT_ACCOUNTING:
7458 case GO_FORMAT_PERCENTAGE:
7459 case GO_FORMAT_SCIENTIFIC: {
7460 /*
7461 * These guesses only have to be good enough to work on
7462 * the formats we generate ourselves.
7463 */
7464
7465 const char *dot = strchr (str, '.');
7466 const char *zero = strchr (str, '0');
7467 const char *comma = strchr (str, ',');
7468
7469 if (dot) {
7470 while (dot[dst->num_decimals + 1] == '0')
7471 dst->num_decimals++;
7472 }
7473
7474 dst->min_digits = 0;
7475 if (zero) {
7476 const char *p = zero;
7477 while (*p == ',' || *p == '0') {
7478 if (*p == '0')
7479 dst->min_digits++;
7480 p++;
7481 }
7482 }
7483
7484 dst->negative_red = (strstr (str, ";[Red]") != NULL);
7485 dst->negative_paren = (strstr (str, "_);") != NULL);
7486
7487 if (str[0] == '_' && str[1] == '(') {
7488 const char *start = str + 2;
7489 gboolean precedes = start[0] != '#';
7490 gsize len = 0;
7491 const GOFormatCurrency *pcurr;
7492
7493 if (precedes) {
7494 while (start[len] && start[len] != '*')
7495 len++;
7496 } else {
7497 while (start[0] == '0' || start[0] == '.' ||
7498 start[0] == '#' || start[0] == ',')
7499 start++;
7500 if (start[0] == '*' && start[1])
7501 start += 2;
7502 while (start[len] && start[len] != '_')
7503 len++;
7504 }
7505
7506 pcurr = find_currency (start, len, precedes);
7507 if (pcurr) {
7508 dst->currency = ¤cy;
7509 currency = *pcurr;
7510 }
7511 dst->force_quoted = (start[0] == '"');
7512 dst->family = GO_FORMAT_ACCOUNTING;
7513 } else {
7514 gboolean precedes = str[0] != '0' && str[0] != '#';
7515 const char *start;
7516 gsize len = 0;
7517 const GOFormatCurrency *pcurr;
7518
7519 if (precedes) {
7520 start = str;
7521 while (start[len] && start[len] != '0' && start[len] != '#')
7522 len++;
7523 } else {
7524 start = str + strlen (str);
7525 if (start > str && start[-1] == ')')
7526 start--;
7527 while (start > str && start[-1] != '0' && start[-1] != '#')
7528 start--, len++;
7529 }
7530
7531 pcurr = find_currency (start, len, precedes);
7532 if (pcurr) {
7533 dst->currency = ¤cy;
7534 currency = *pcurr;
7535 dst->force_quoted = (start[0] == '"');
7536 dst->family = GO_FORMAT_CURRENCY;
7537 }
7538 }
7539
7540 dst->thousands_sep = (comma > str &&
7541 (comma[-1] == '0' || comma[-1] == '#') &&
7542 (comma[1] == '0' || comma[1] == '#') &&
7543 (comma[2] == '0' || comma[2] == '#') &&
7544 (comma[3] == '0' || comma[3] == '#'));
7545
7546 if (dst->family == GO_FORMAT_SCIENTIFIC) {
7547 const char *epos = strchr (str, 'E');
7548 const char *scale_pos = strstr (str, "'E");
7549 const char *mend = dot ? dot : (scale_pos ? scale_pos : epos);
7550 dst->append_SI = (strstr (str, "ESI") != NULL);
7551 dst->use_markup = (strstr (str, "EE") != NULL);
7552 dst->scale = ((scale_pos != NULL) ? 3 : 0);
7553 dst->exponent_step = mend - str;
7554 dst->simplify_mantissa = mend != str && mend[-1] == '#';
7555 if (dst->simplify_mantissa)
7556 dst->min_digits = 0;
7557
7558 dst->exponent_digits = 0;
7559 if (dst->use_markup) epos++;
7560 epos++;
7561 if (dst->use_markup)
7562 epos++;
7563 if (dst->append_SI)
7564 epos += 2;
7565 if (epos[0] == '+') {
7566 epos++;
7567 dst->exponent_sign_forced = TRUE;
7568 } else if (epos[0] == '-')
7569 epos++;
7570 while (epos[0] == '0' || epos[0] == '#' || epos[0] == '?') {
7571 epos++;
7572 dst->exponent_digits++;
7573 }
7574 }
7575
7576 if (exact != NULL) {
7577 newstr = g_string_new (NULL);
7578 go_format_generate_str (newstr, dst);
7579 }
7580
7581 break;
7582 }
7583
7584 case GO_FORMAT_DATE:
7585 case GO_FORMAT_TIME: {
7586 const char *sdot = strstr (str, "s.");
7587
7588 if (sdot) {
7589 while (sdot[dst->num_decimals + 2] == '0')
7590 dst->num_decimals++;
7591 }
7592
7593 break;
7594 }
7595
7596 case GO_FORMAT_FRACTION: {
7597 char *c_str = g_strdup (str), *p;
7598 gchar **tokens;
7599 int numerator_base;
7600 char const *integer;
7601 int d;
7602 gboolean pi_token;
7603
7604 // Trim spaces
7605 for (p = c_str + strlen (c_str); p > c_str && p[-1] == ' '; p--)
7606 p[-1] = 0;
7607 p = c_str;
7608 while (p[0] == ' ')
7609 p++;
7610
7611 // This is really a hack that doesn't take quoted string
7612 // into account.
7613 tokens = g_strsplit_set (c_str, " /", 3);
7614
7615 /* Since it is a fraction we get at least 2 tokens */
7616 g_return_if_fail (tokens[1] != NULL);
7617
7618 dst->pi_scale = (NULL != strstr (str, "pi/"));
7619 pi_token = (0 == strcmp (tokens[1], "pi"));
7620
7621 dst->split_fraction = (tokens[2] != NULL) && !pi_token;
7622
7623 if (dst->split_fraction) {
7624 integer = tokens[0];
7625 dst->min_digits = 0;
7626 while (*integer != 0)
7627 if (*integer++ == '0')
7628 dst->min_digits++;
7629 numerator_base = 1;
7630 } else
7631 numerator_base = 0;
7632
7633 integer = tokens[numerator_base];
7634 dst->numerator_min_digits = 0;
7635 while (*integer != 0)
7636 if (*integer++ == '0')
7637 dst->numerator_min_digits++;
7638
7639 integer = tokens[numerator_base + (pi_token ? 2 : 1)];
7640 d = atoi (integer);
7641 if (d > 1) {
7642 dst->denominator = d;
7643 dst->automatic_denominator = FALSE;
7644 } else {
7645 dst->automatic_denominator = TRUE;
7646 dst->denominator_min_digits = 0;
7647 dst->denominator_max_digits = 0;
7648 while (*integer != 0) {
7649 if (*integer == '#' || *integer == '?'
7650 || g_ascii_isdigit (*integer))
7651 dst->denominator_max_digits++;
7652 if (*integer == '0') {
7653 dst->denominator_min_digits++;
7654 }
7655 integer++;
7656 }
7657 }
7658
7659 g_strfreev (tokens);
7660 g_free (c_str);
7661
7662 if (exact != NULL) {
7663 newstr = g_string_new (NULL);
7664 go_format_generate_str (newstr, dst);
7665 }
7666
7667 break;
7668 }
7669
7670 default:
7671 break;
7672 }
7673
7674 if (newstr) {
7675 *exact = (strcmp (str, newstr->str) == 0);
7676 if (!*exact && dst->family == GO_FORMAT_SCIENTIFIC &&
7677 dst->append_SI && g_str_has_prefix (str, newstr->str) &&
7678 str [newstr->len] == '\"') {
7679 int len = strlen (str);
7680 if (str [len - 1] == '\"') {
7681 dst->appended_SI_unit = g_strndup (str + newstr->len + 1,
7682 len - newstr->len - 2);
7683 g_string_truncate (newstr, 0);
7684 go_format_generate_str (newstr, dst);
7685 *exact = (strcmp (str, newstr->str) == 0);
7686 }
7687 }
7688 g_string_free (newstr, TRUE);
7689 }
7690 }
7691 #endif
7692
7693
7694 #ifdef DEFINE_COMMON
7695
7696 /* making GOFormatCurrency a boxed type for introspection. ref and unref don't
7697 * do anything because we always return a static object */
7698 static GOFormatCurrency *
go_format_currency_ref(GOFormatCurrency * currency)7699 go_format_currency_ref (GOFormatCurrency * currency)
7700 {
7701 return currency;
7702 }
7703
7704 static void
go_format_currency_unref(G_GNUC_UNUSED GOFormatCurrency * currency)7705 go_format_currency_unref (G_GNUC_UNUSED GOFormatCurrency * currency)
7706 {
7707 }
7708
7709 GType
go_format_currency_get_type(void)7710 go_format_currency_get_type (void)
7711 {
7712 static GType t = 0;
7713
7714 if (t == 0) {
7715 t = g_boxed_type_register_static ("GOFormatCurrency",
7716 (GBoxedCopyFunc)go_format_currency_ref,
7717 (GBoxedFreeFunc)go_format_currency_unref);
7718 }
7719 return t;
7720 }
7721
7722 /**
7723 * go_format_locale_currency:
7724 *
7725 * Returns: (transfer none): The #GOFormatCurrency matches the current locale.
7726 */
7727 GOFormatCurrency const *
go_format_locale_currency(void)7728 go_format_locale_currency (void)
7729 {
7730 static GOFormatCurrency currency;
7731 const GOFormatCurrency *pcurr;
7732 gboolean precedes, has_space;
7733 const GString *lcurr = go_locale_get_currency (&precedes, &has_space);
7734
7735 #if 0
7736 g_printerr ("go_format_locale_currency: looking for [%s] %d %d\n", symbol, precedes, has_space);
7737 #endif
7738
7739 pcurr = find_currency (lcurr->str, lcurr->len, precedes);
7740 if (pcurr)
7741 return pcurr;
7742
7743 currency.has_space = has_space;
7744 currency.precedes = precedes;
7745 currency.symbol = lcurr->str;
7746 currency.description = NULL;
7747 return ¤cy;
7748 }
7749 #endif
7750
7751
7752 /********************* GOFormat ODF Support ***********************/
7753
7754 #define STYLE "style:"
7755 #define FOSTYLE "fo:"
7756 #define NUMBER "number:"
7757 #define GNMSTYLE "gnm:"
7758 #define OFFICE "office:"
7759
7760
7761 #ifdef DEFINE_COMMON
7762 char *
go_format_odf_style_map(GOFormat const * fmt,int cond_part)7763 go_format_odf_style_map (GOFormat const *fmt, int cond_part)
7764 {
7765 char const *format_string;
7766 GString *valstr;
7767
7768 g_return_val_if_fail (fmt != NULL, NULL);
7769 g_return_val_if_fail (fmt->typ == GO_FMT_COND, NULL);
7770
7771 if (cond_part > fmt->u.cond.n)
7772 return NULL;
7773
7774 switch (fmt->u.cond.conditions[cond_part].op) {
7775 case GO_FMT_COND_EQ:
7776 format_string = "value()=";
7777 break;
7778 case GO_FMT_COND_NE:
7779 format_string = "value()!=";
7780 break;
7781 case GO_FMT_COND_NONTEXT: /* Under certain circumstances this */
7782 /*appears for second of two conditions */
7783 case GO_FMT_COND_LT:
7784 format_string = "value()<";
7785 break;
7786 case GO_FMT_COND_LE:
7787 format_string = "value()<=";
7788 break;
7789 case GO_FMT_COND_GT:
7790 format_string = "value()>";
7791 break;
7792 case GO_FMT_COND_GE:
7793 format_string = "value()>=";
7794 break;
7795 default:
7796 return NULL;
7797 }
7798
7799 valstr = g_string_new (format_string);
7800 go_dtoa (valstr, "!g", fmt->u.cond.conditions[cond_part].val);
7801 return g_string_free (valstr, FALSE);
7802 }
7803 #endif
7804
7805 #ifdef DEFINE_COMMON
7806
7807 static void
odf_add_bool(GsfXMLOut * xout,char const * id,gboolean val)7808 odf_add_bool (GsfXMLOut *xout, char const *id, gboolean val)
7809 {
7810 gsf_xml_out_add_cstr_unchecked (xout, id, val ? "true" : "false");
7811 }
7812
7813 static void
odf_output_color(GsfXMLOut * xout,GOFormat const * fmt)7814 odf_output_color (GsfXMLOut *xout, GOFormat const *fmt)
7815 {
7816 /* Ignore transparent black. */
7817 if (fmt->color) {
7818 char *str = g_strdup_printf ("#%.2X%.2X%.2X",
7819 GO_COLOR_UINT_R (fmt->color),
7820 GO_COLOR_UINT_G (fmt->color),
7821 GO_COLOR_UINT_B (fmt->color));
7822
7823 gsf_xml_out_start_element (xout, STYLE "text-properties");
7824 gsf_xml_out_add_cstr_unchecked (xout, FOSTYLE "color", str);
7825 gsf_xml_out_end_element (xout); /*<style:text-properties>*/
7826
7827 g_free (str);
7828 }
7829 }
7830
7831
7832 #define ODF_CLOSE_STRING if (string_is_open) { \
7833 gsf_xml_out_add_cstr (xout, NULL, accum->str); \
7834 gsf_xml_out_end_element (xout); /* </number:text> */ \
7835 string_is_open = FALSE; \
7836 }
7837 #define ODF_OPEN_STRING if (!string_is_open) { \
7838 gsf_xml_out_start_element (xout, NUMBER "text"); \
7839 string_is_open = TRUE; \
7840 text_written = TRUE; \
7841 g_string_erase (accum, 0, -1); \
7842 }
7843
7844 static void
go_format_output_date_to_odf(GsfXMLOut * xout,GOFormat const * fmt,char const * name,GOFormatFamily family,gboolean with_extension)7845 go_format_output_date_to_odf (GsfXMLOut *xout, GOFormat const *fmt,
7846 char const *name,
7847 GOFormatFamily family,
7848 gboolean with_extension)
7849 {
7850 char const *xl = go_format_as_XL (fmt);
7851 GString *accum = g_string_new (NULL);
7852 gboolean time_only = (family == GO_FORMAT_TIME);
7853 gboolean seen_year = FALSE;
7854 gboolean seen_month = FALSE;
7855 gboolean seen_day = FALSE;
7856 gboolean seen_weekday = FALSE;
7857 gboolean seen_hour = FALSE;
7858 gboolean seen_ampm = FALSE;
7859 gboolean seen_minute = FALSE;
7860 gboolean seen_second = FALSE;
7861 gboolean seen_elapsed = FALSE;
7862 gboolean m_is_minutes = FALSE;
7863 gboolean string_is_open = FALSE;
7864 gboolean seconds_trigger_minutes = TRUE;
7865 gboolean element_written = FALSE;
7866 gboolean text_written = FALSE;
7867 GOFormatMagic magic = go_format_get_magic (fmt);
7868
7869 gsf_xml_out_start_element (xout, time_only ?
7870 NUMBER "time-style" : NUMBER "date-style");
7871 gsf_xml_out_add_cstr (xout, STYLE "name", name);
7872 if (magic == GO_FORMAT_MAGIC_NONE)
7873 gsf_xml_out_add_cstr (xout, NUMBER "format-source", "fixed");
7874 else {
7875 xl = _(xl);
7876 gsf_xml_out_add_cstr (xout, NUMBER "format-source", "language");
7877 if (with_extension)
7878 gsf_xml_out_add_int (xout, GNMSTYLE "format-magic", magic);
7879 }
7880 odf_output_color (xout, fmt);
7881
7882 while (1) {
7883 const char *token = xl;
7884 GOFormatTokenType tt;
7885 int t = go_format_token (&xl, &tt);
7886
7887 switch (t) {
7888 case 0: case ';':
7889 ODF_CLOSE_STRING;
7890 if (!element_written)
7891 gsf_xml_out_simple_element (xout, NUMBER "am-pm", NULL);
7892 /* keep element open */
7893 g_string_free (accum, TRUE);
7894 return;
7895
7896 case 'd': case 'D': {
7897 int n = 1;
7898 while (*xl == 'd' || *xl == 'D')
7899 xl++, n++;
7900 if (time_only) break;
7901 switch (n) {
7902 case 1:
7903 case 2: if (seen_day) break;
7904 seen_day = TRUE;
7905 ODF_CLOSE_STRING;
7906 element_written = TRUE;
7907 gsf_xml_out_start_element (xout, NUMBER "day");
7908 gsf_xml_out_add_cstr (xout, NUMBER "style",
7909 (n==1) ? "short" : "long");
7910 gsf_xml_out_end_element (xout); /* </number:day> */
7911 break;
7912 case 3:
7913 default: if (seen_weekday) break;
7914 seen_weekday = TRUE;
7915 ODF_CLOSE_STRING;
7916 element_written = TRUE;
7917 gsf_xml_out_start_element (xout, NUMBER "day-of-week");
7918 gsf_xml_out_add_cstr (xout, NUMBER "style",
7919 (n==3) ? "short" : "long");
7920 gsf_xml_out_end_element (xout); /* </number:day-of-week> */
7921 break;
7922 }
7923 break;
7924 }
7925
7926 case 'y': case 'Y': {
7927 int n = 1;
7928 while (*xl == 'y' || *xl == 'Y')
7929 xl++, n++;
7930 if (time_only || seen_year) break;
7931 seen_year = TRUE;
7932 ODF_CLOSE_STRING;
7933 element_written = TRUE;
7934 gsf_xml_out_start_element (xout, NUMBER "year");
7935 gsf_xml_out_add_cstr (xout, NUMBER "style",
7936 (n <= 2) ? "short" : "long");
7937 gsf_xml_out_add_cstr (xout, NUMBER "calendar", "gregorian");
7938 gsf_xml_out_end_element (xout); /* </number:year> */
7939 break;
7940 }
7941
7942 case 'b': case 'B': {
7943 int n = 1;
7944 while (*xl == 'b' || *xl == 'B')
7945 xl++, n++;
7946 if (time_only || seen_year) break;
7947 seen_year = TRUE;
7948 ODF_CLOSE_STRING;
7949 element_written = TRUE;
7950 gsf_xml_out_start_element (xout, NUMBER "year");
7951 gsf_xml_out_add_cstr (xout, NUMBER "style",
7952 (n <= 2) ? "short" : "long");
7953 gsf_xml_out_add_cstr (xout, NUMBER "calendar", "buddhist");
7954 gsf_xml_out_end_element (xout); /* </number:year> */
7955 break;
7956 }
7957
7958 case 'e': { /* What is 'e' really? */
7959 while (*xl == 'e') xl++;
7960 if (time_only || seen_year) break;
7961 seen_year = TRUE;
7962 ODF_CLOSE_STRING;
7963 gsf_xml_out_start_element (xout, NUMBER "year");
7964 gsf_xml_out_add_cstr (xout, NUMBER "style", "long");
7965 gsf_xml_out_add_cstr (xout, NUMBER "calendar", "gregorian");
7966 gsf_xml_out_end_element (xout); /* </number:year> */
7967 break;
7968 }
7969
7970 case 'g': case 'G':
7971 /* Something with Japanese eras. Blank for me. */
7972 break;
7973
7974 case 'h': case 'H': {
7975 int n = 1;
7976 while (*xl == 'h' || *xl == 'H')
7977 xl++, n++;
7978 if (seen_hour) break;
7979 seen_hour = TRUE;
7980 ODF_CLOSE_STRING;
7981 element_written = TRUE;
7982 gsf_xml_out_start_element (xout, NUMBER "hours");
7983 gsf_xml_out_add_cstr (xout, NUMBER "style",
7984 (n == 1) ? "short" : "long");
7985 if (with_extension)
7986 gsf_xml_out_add_cstr (xout, GNMSTYLE "truncate-on-overflow", "true");
7987 gsf_xml_out_end_element (xout); /* </number:hours> */
7988 m_is_minutes = TRUE;
7989 break;
7990 }
7991
7992 case 'm': case 'M': {
7993 int n = 1;
7994 while (*xl == 'm' || *xl == 'M')
7995 xl++, n++;
7996 m_is_minutes = (n <= 2) && (m_is_minutes || tail_forces_minutes (xl));
7997
7998 element_written = TRUE;
7999 if (m_is_minutes) {
8000 if (seen_minute) break;
8001 seen_minute = TRUE;
8002 m_is_minutes = FALSE;
8003 seconds_trigger_minutes = FALSE;
8004 ODF_CLOSE_STRING;
8005 gsf_xml_out_start_element (xout, NUMBER "minutes");
8006 gsf_xml_out_add_cstr (xout, NUMBER "style",
8007 (n == 1) ? "short" : "long");
8008 if (with_extension)
8009 gsf_xml_out_add_cstr (xout, GNMSTYLE "truncate-on-overflow", "true");
8010 gsf_xml_out_end_element (xout); /* </number:minutes> */
8011 } else {
8012 if (seen_month || time_only) break;
8013 seen_month = TRUE;
8014 ODF_CLOSE_STRING;
8015 gsf_xml_out_start_element (xout, NUMBER "month");
8016 gsf_xml_out_add_cstr (xout, NUMBER "possessive-form", "false");
8017 switch (n) {
8018 case 1:
8019 gsf_xml_out_add_cstr (xout, NUMBER "textual", "false");
8020 gsf_xml_out_add_cstr (xout, NUMBER "style", "short");
8021 break;
8022 case 2:
8023 gsf_xml_out_add_cstr (xout, NUMBER "textual", "false");
8024 gsf_xml_out_add_cstr (xout, NUMBER "style", "long");
8025 break;
8026 case 3: /* ODF does not support single letter abbreviation */
8027 case 5:
8028 gsf_xml_out_add_cstr (xout, NUMBER "textual", "true");
8029 gsf_xml_out_add_cstr (xout, NUMBER "style", "short");
8030 break;
8031 default:
8032 gsf_xml_out_add_cstr (xout, NUMBER "textual", "true");
8033 gsf_xml_out_add_cstr (xout, NUMBER "style", "long");
8034 break;
8035 }
8036
8037 gsf_xml_out_end_element (xout); /* </number:month> */
8038 }
8039 break;
8040 }
8041
8042 case 's': case 'S': {
8043 int n = 1, d = 0;
8044 while (*xl == 's' || *xl == 'S')
8045 xl++, n++;
8046 if (*xl == '.' && *(xl + 1) == '0') {
8047 xl++;
8048 while (*xl == '0')
8049 xl++, d++;
8050 }
8051 if (seconds_trigger_minutes) {
8052 seconds_trigger_minutes = FALSE;
8053 m_is_minutes = TRUE;
8054 }
8055 if (seen_second) break;
8056 seen_second = TRUE;
8057 ODF_CLOSE_STRING;
8058 element_written = TRUE;
8059 gsf_xml_out_start_element (xout, NUMBER "seconds");
8060 gsf_xml_out_add_cstr (xout, NUMBER "style",
8061 (n == 1) ? "short" : "long");
8062 gsf_xml_out_add_int (xout, NUMBER "decimal-places", d);
8063 if (with_extension)
8064 gsf_xml_out_add_cstr (xout, GNMSTYLE "truncate-on-overflow", "true");
8065 gsf_xml_out_end_element (xout); /* </number:seconds> */
8066 break;
8067 }
8068
8069 case TOK_AMPM3:
8070 if (seen_elapsed || seen_ampm) break;
8071 seen_ampm = TRUE;
8072 ODF_CLOSE_STRING;
8073 element_written = TRUE;
8074 gsf_xml_out_start_element (xout, NUMBER "am-pm");
8075 if (with_extension) {
8076 gchar *suffix = g_strndup (token, 1);
8077 gsf_xml_out_add_cstr (xout, GNMSTYLE "am-suffix", suffix);
8078 g_free (suffix);
8079 suffix = g_strndup (token + 2, 1);
8080 gsf_xml_out_add_cstr (xout, GNMSTYLE "pm-suffix", suffix);
8081 g_free (suffix);
8082 }
8083 gsf_xml_out_end_element (xout); /* </number:am-pm> */
8084 break;
8085
8086 case TOK_AMPM5:
8087 if (seen_elapsed || seen_ampm) break;
8088 seen_ampm = TRUE;
8089 ODF_CLOSE_STRING;
8090 element_written = TRUE;
8091 gsf_xml_out_start_element (xout, NUMBER "am-pm");
8092 if (with_extension) {
8093 gsf_xml_out_add_cstr (xout, GNMSTYLE "am-suffix", "AM");
8094 gsf_xml_out_add_cstr (xout, GNMSTYLE "pm-suffix", "PM");
8095 }
8096 gsf_xml_out_end_element (xout); /* </number:am-pm> */
8097 break;
8098
8099 case TOK_ELAPSED_H:
8100 if (seen_elapsed || seen_ampm || seen_hour) break;
8101 seen_hour = TRUE;
8102 seen_elapsed = TRUE;
8103 ODF_CLOSE_STRING;
8104 if ((!text_written) && !element_written && time_only)
8105 gsf_xml_out_add_cstr (xout, NUMBER "truncate-on-overflow", "false");
8106 gsf_xml_out_start_element (xout, NUMBER "hours");
8107 gsf_xml_out_add_cstr (xout, NUMBER "style", "short");
8108 /* ODF can mark elapsed time in the time-style only and then not clearly */
8109 if (with_extension)
8110 gsf_xml_out_add_cstr (xout, GNMSTYLE "truncate-on-overflow", "false");
8111 gsf_xml_out_end_element (xout); /* </number:hours> */
8112 m_is_minutes = TRUE;
8113 element_written = TRUE;
8114 break;
8115
8116 case TOK_ELAPSED_M:
8117 if (seen_elapsed || seen_ampm || seen_minute) break;
8118 seen_minute = TRUE;
8119 seen_elapsed = TRUE;
8120 m_is_minutes = FALSE;
8121 seconds_trigger_minutes = FALSE;
8122 ODF_CLOSE_STRING;
8123 if ((!text_written) && !element_written && time_only)
8124 gsf_xml_out_add_cstr (xout, NUMBER "truncate-on-overflow", "false");
8125 gsf_xml_out_start_element (xout, NUMBER "minutes");
8126 gsf_xml_out_add_cstr (xout, NUMBER "style", "long");
8127 if (with_extension)
8128 gsf_xml_out_add_cstr (xout, GNMSTYLE "truncate-on-overflow", "false");
8129 gsf_xml_out_end_element (xout); /* </number:minutes> */
8130 element_written = TRUE;
8131
8132 case TOK_ELAPSED_S:
8133 if (seen_elapsed || seen_ampm || seen_second) break;
8134 seen_elapsed = TRUE;
8135 seen_second = TRUE;
8136 if (seconds_trigger_minutes) {
8137 m_is_minutes = TRUE;
8138 seconds_trigger_minutes = FALSE;
8139 }
8140 ODF_CLOSE_STRING;
8141 if ((!text_written) && !element_written && time_only)
8142 gsf_xml_out_add_cstr (xout, NUMBER "truncate-on-overflow", "false");
8143 gsf_xml_out_start_element (xout, NUMBER "seconds");
8144 gsf_xml_out_add_cstr (xout, NUMBER "style", "short");
8145 if (with_extension)
8146 gsf_xml_out_add_cstr (xout, GNMSTYLE "truncate-on-overflow", "false");
8147 gsf_xml_out_end_element (xout); /* </number:seconds> */
8148 element_written = TRUE;
8149 break;
8150
8151 case TOK_STRING: {
8152 size_t len = strchr (token + 1, '"') - (token + 1);
8153 if (len > 0) {
8154 ODF_OPEN_STRING;
8155 g_string_append_len (accum, token + 1, len);
8156 }
8157 break;
8158 }
8159
8160 case TOK_CHAR: {
8161 size_t len = g_utf8_next_char(token) - (token);
8162 if (len > 0) {
8163 ODF_OPEN_STRING;
8164 g_string_append_len (accum, token, len);
8165 }
8166 break;
8167 }
8168
8169 case TOK_ESCAPED_CHAR: {
8170 size_t len = g_utf8_next_char(token + 1) - (token + 1);
8171 if (len > 0) {
8172 ODF_OPEN_STRING;
8173 g_string_append_len (accum, token + 1, len);
8174 }
8175 break;
8176 }
8177
8178 case TOK_THOUSAND:
8179 ODF_OPEN_STRING;
8180 g_string_append_c (accum, ',');
8181 break;
8182
8183 case TOK_DECIMAL:
8184 ODF_OPEN_STRING;
8185 g_string_append_c (accum, '.');
8186 break;
8187
8188 case TOK_COLOR:
8189 case TOK_GENERAL:
8190 case TOK_INVISIBLE_CHAR:
8191 case TOK_REPEATED_CHAR:
8192 case TOK_CONDITION:
8193 case TOK_LOCALE:
8194 break;
8195 case TOK_ERROR:
8196 xl++;
8197 break;
8198
8199 default:
8200 if (t <= 0x7f) {
8201 ODF_OPEN_STRING;
8202 g_string_append_c (accum, t);
8203 }
8204 break;
8205 }
8206 }
8207 }
8208
8209 #undef ODF_CLOSE_STRING
8210 #undef ODF_OPEN_STRING
8211
8212 #define ODF_CLOSE_STRING if (string_is_open) { \
8213 gsf_xml_out_add_cstr (xout, NULL, accum->str); \
8214 gsf_xml_out_end_element (xout); /* </number:text> */ \
8215 string_is_open = FALSE; \
8216 }
8217 #define ODF_OPEN_STRING if (fraction_in_progress) break;\
8218 if (!string_is_open) { \
8219 gsf_xml_out_start_element (xout, NUMBER "text");\
8220 string_is_open = TRUE; \
8221 g_string_erase (accum, 0, -1); \
8222 }
8223
8224 static void
go_format_output_fraction_to_odf(GsfXMLOut * xout,GOFormat const * fmt,char const * name,gboolean with_extension)8225 go_format_output_fraction_to_odf (GsfXMLOut *xout, GOFormat const *fmt,
8226 char const *name,
8227 gboolean with_extension)
8228 {
8229 char const *xl = go_format_as_XL (fmt);
8230 GString *accum = g_string_new (NULL);
8231 int odf_version = gsf_odf_out_get_version (GSF_ODF_OUT (xout));
8232
8233 int int_digits = -1; /* -1 means no integer part */
8234 int min_numerator_digits = 0;
8235 int zeroes = 0;
8236
8237 gboolean fraction_in_progress = FALSE;
8238 gboolean fraction_completed = FALSE;
8239 gboolean string_is_open = FALSE;
8240 gboolean pi_scale = FALSE;
8241
8242
8243 gsf_xml_out_start_element (xout, NUMBER "number-style");
8244 gsf_xml_out_add_cstr (xout, STYLE "name", name);
8245 odf_output_color (xout, fmt);
8246
8247 while (1) {
8248 const char *token = xl;
8249 GOFormatTokenType tt;
8250 int t = go_format_token (&xl, &tt);
8251
8252 switch (t) {
8253 case 0: case ';':
8254 ODF_CLOSE_STRING;
8255 if (!fraction_completed) {
8256 /* We need a fraction element */
8257 gsf_xml_out_start_element (xout, NUMBER "fraction");
8258 odf_add_bool (xout, NUMBER "grouping", FALSE);
8259 gsf_xml_out_add_int (xout, NUMBER "min-denominator-digits", 3);
8260 if (int_digits >= 0)
8261 gsf_xml_out_add_int (xout, NUMBER "min-integer-digits", int_digits);
8262 else if (odf_version < 102) {
8263 gsf_xml_out_add_int (xout, NUMBER "min-integer-digits", 0);
8264 if (with_extension)
8265 gsf_xml_out_add_cstr_unchecked
8266 (xout, GNMSTYLE "no-integer-part", "true");
8267
8268 }
8269 /* In ODF1.2, absence of NUMBER "min-integer-digits" means not to show an */
8270 /* integer part. In ODF 1.1 we used a foreign element: gnm:no-integer-part=true */
8271 gsf_xml_out_add_int (xout, NUMBER "min-numerator-digits", 1);
8272 gsf_xml_out_end_element (xout); /* </number:fraction> */
8273 }
8274 /* keep element open */
8275 g_string_free (accum, TRUE);
8276 return;
8277
8278 case '0':
8279 zeroes = 1;
8280 /* fall through */
8281 case '#': case '?': {
8282 int i = 1;
8283 char const *slash;
8284 char const *look;
8285 if (fraction_completed) {
8286 zeroes = 0;
8287 break;
8288 }
8289 ODF_CLOSE_STRING;
8290 /* ODF allows only for a single fraction specification */
8291 fraction_in_progress = TRUE;
8292 while (*xl == '0' || *xl == '?' ||*xl == '#') {
8293 if (*xl == '0') zeroes++;
8294 xl++; i++;
8295 }
8296 if (zeroes > 0 && *(xl - 1) != '0') zeroes++;
8297
8298 slash = strchr (xl, '/');
8299 if (slash) {
8300 for (look = xl; look < slash; look++)
8301 if (*look == '0' || *look == '?' || *look == '#')
8302 break;
8303 if (look < slash)
8304 int_digits = zeroes;
8305 else
8306 min_numerator_digits = zeroes;
8307 zeroes = 0;
8308 break;
8309 }
8310 zeroes = 0;
8311 break;
8312 }
8313
8314 case '/': {
8315 int fixed_denominator;
8316 int digits = 0;
8317 int i = 0;
8318
8319 if (fraction_completed) break;
8320 /* ODF allows only for a single fraction specification */
8321 ODF_CLOSE_STRING;
8322
8323 fixed_denominator = atoi (xl);
8324 while (g_ascii_isdigit (*xl) || *xl == '?' ||*xl == '#') {
8325 if (*xl == '0') zeroes++;
8326 if (g_ascii_isdigit (*xl)) digits ++;
8327 xl++; i++;
8328 }
8329
8330 gsf_xml_out_start_element (xout, NUMBER "fraction");
8331 odf_add_bool (xout, NUMBER "grouping", FALSE);
8332 if ((fixed_denominator > 0) && (digits == i)) {
8333 gsf_xml_out_add_int (xout, NUMBER "denominator-value",
8334 fixed_denominator);
8335 gsf_xml_out_add_int (xout, NUMBER "min-denominator-digits", digits);
8336 } else
8337 gsf_xml_out_add_int (xout, NUMBER "min-denominator-digits", zeroes);
8338 if (with_extension)
8339 gsf_xml_out_add_int (xout, GNMSTYLE "max-denominator-digits", i);
8340
8341 if (int_digits >= 0)
8342 gsf_xml_out_add_int (xout, NUMBER "min-integer-digits", int_digits);
8343 else if (odf_version < 102) {
8344 gsf_xml_out_add_int (xout, NUMBER "min-integer-digits", 0);
8345 if (with_extension)
8346 gsf_xml_out_add_cstr_unchecked
8347 (xout, GNMSTYLE "no-integer-part", "true");
8348 }
8349 /* In ODF1.2, absence of NUMBER "min-integer-digits" means not to show an */
8350 /* integer part. In ODF 1.1 we used a foreign element: gnm:no-integer-part=true */
8351 gsf_xml_out_add_int (xout, NUMBER "min-numerator-digits",
8352 min_numerator_digits);
8353 if (pi_scale && with_extension) {
8354 gsf_xml_out_add_cstr_unchecked (xout, GNMSTYLE "display-factor", "pi");
8355 }
8356 gsf_xml_out_end_element (xout); /* </number:fraction> */
8357 fraction_completed = TRUE;
8358 fraction_in_progress = FALSE;
8359 break;
8360 }
8361
8362 case 'd': case 'D':
8363 case 'y': case 'Y':
8364 case 'b': case 'B':
8365 case 'e':
8366 case 'g': case 'G':
8367 case 'h': case 'H':
8368 case 'm': case 'M':
8369 case 's': case 'S':
8370 case TOK_AMPM3:
8371 case TOK_AMPM5:
8372 case TOK_ELAPSED_H:
8373 case TOK_ELAPSED_M:
8374 case TOK_ELAPSED_S:
8375 case TOK_GENERAL:
8376 case TOK_INVISIBLE_CHAR:
8377 case TOK_REPEATED_CHAR:
8378 case TOK_CONDITION:
8379 case TOK_LOCALE:
8380 case TOK_ERROR:
8381 case TOK_COLOR:
8382 break;
8383
8384 case TOK_STRING: {
8385 size_t len = strchr (token + 1, '"') - (token + 1);
8386 if (len > 0) {
8387 ODF_OPEN_STRING;
8388 g_string_append_len (accum, token + 1, len);
8389 }
8390 break;
8391 }
8392
8393 case TOK_CHAR: {
8394 size_t len = g_utf8_next_char(token) - (token);
8395 if (len > 0) {
8396 ODF_OPEN_STRING;
8397 g_string_append_len (accum, token, len);
8398 }
8399 break;
8400 }
8401
8402 case TOK_ESCAPED_CHAR: {
8403 size_t len = g_utf8_next_char(token + 1) - (token + 1);
8404 if (len > 0) {
8405 ODF_OPEN_STRING;
8406 g_string_append_len (accum, token + 1, len);
8407 }
8408 break;
8409 }
8410
8411 case TOK_THOUSAND:
8412 ODF_OPEN_STRING;
8413 g_string_append_c (accum, ',');
8414 break;
8415
8416 case TOK_DECIMAL:
8417 ODF_OPEN_STRING;
8418 g_string_append_c (accum, '.');
8419 break;
8420
8421 case 'p':
8422 if (fraction_in_progress && g_str_has_prefix (token, "pi/")) {
8423 pi_scale = TRUE;
8424 xl++;
8425 break;
8426 }
8427 /* no break */
8428 default:
8429 if (t <= 0x7f) {
8430 ODF_OPEN_STRING;
8431 g_string_append_c (accum, t);
8432 }
8433 break;
8434 }
8435 }
8436 }
8437
8438 #undef ODF_CLOSE_STRING
8439 #undef ODF_OPEN_STRING
8440
8441 static void
go_format_output_number_element_to_odf(GsfXMLOut * xout,int min_integer_digits,int min_integer_chars,int min_decimal_digits,gboolean comma_seen,GSList * embedded,gboolean with_extension)8442 go_format_output_number_element_to_odf (GsfXMLOut *xout,
8443 int min_integer_digits,
8444 int min_integer_chars,
8445 int min_decimal_digits,
8446 gboolean comma_seen,
8447 GSList *embedded,
8448 gboolean with_extension)
8449 {
8450 GSList *l;
8451
8452 gsf_xml_out_start_element (xout, NUMBER "number");
8453 gsf_xml_out_add_int (xout, NUMBER "decimal-places", min_decimal_digits);
8454 odf_add_bool (xout, NUMBER "grouping", comma_seen);
8455 gsf_xml_out_add_int (xout, NUMBER "min-integer-digits", min_integer_digits);
8456 if (with_extension && (min_integer_chars > min_integer_digits))
8457 gsf_xml_out_add_int (xout, GNMSTYLE "min-integer-chars", min_integer_chars);
8458
8459 embedded = g_slist_reverse (embedded);
8460 for (l = embedded; l; l = l->next->next) {
8461 int pos = GPOINTER_TO_INT (l->data);
8462 char *str = l->next->data;
8463 gsf_xml_out_start_element (xout, NUMBER "embedded-text");
8464 gsf_xml_out_add_int (xout, NUMBER "position", pos);
8465 gsf_xml_out_add_cstr (xout, NULL, str);
8466 gsf_xml_out_end_element (xout); /* </number:embedded-text> */
8467 g_free (str);
8468 }
8469 g_slist_free (embedded);
8470
8471 gsf_xml_out_end_element (xout); /* </number:number> */
8472 }
8473
8474 #define ODF_OPEN_STRING do { \
8475 if (!string_is_open) { \
8476 string_is_open = TRUE; \
8477 gsf_xml_out_start_element (xout, NUMBER "text"); \
8478 } \
8479 } while (0)
8480
8481 #define ODF_CLOSE_STRING do { \
8482 if (string_is_open) { \
8483 string_is_open = FALSE; \
8484 gsf_xml_out_end_element (xout); /* NUMBER "text" */ \
8485 } \
8486 } while (0)
8487
8488
8489 #define ODF_FLUSH_STRING do { \
8490 if (accum->len == 0) { \
8491 /* Nothing */ \
8492 } else if (phase == 1) { \
8493 if (allow_embedded) { \
8494 embedded = g_slist_prepend \
8495 (embedded, \
8496 GINT_TO_POINTER (digits)); \
8497 embedded = g_slist_prepend \
8498 (embedded, \
8499 g_strdup (accum->str)); \
8500 } \
8501 } else { \
8502 ODF_OPEN_STRING; \
8503 gsf_xml_out_add_cstr (xout, NULL, accum->str); \
8504 } \
8505 g_string_truncate (accum, 0); \
8506 ODF_CLOSE_STRING; \
8507 } while (0)
8508
8509 /*
8510 * This is to be called when the decimal point (or its location) is reached.
8511 * The digit counts in embedded will be patched up to become the position
8512 * before the decimal point.
8513 */
8514 static void
fixup_embedded(GSList * embedded,int digits)8515 fixup_embedded (GSList *embedded, int digits)
8516 {
8517 while (embedded) {
8518 int pos = digits - GPOINTER_TO_INT (embedded->next->data);
8519 embedded->next->data = GINT_TO_POINTER (pos);
8520 embedded = embedded->next->next;
8521 }
8522 }
8523
8524 static void
go_format_output_number_to_odf(GsfXMLOut * xout,GOFormat const * fmt,GOFormatFamily family,char const * name,gboolean with_extension)8525 go_format_output_number_to_odf (GsfXMLOut *xout, GOFormat const *fmt,
8526 GOFormatFamily family,
8527 char const *name, gboolean with_extension)
8528 {
8529 char const *xl = go_format_as_XL (fmt);
8530 int digits = 0;
8531 int min_integer_digits = 0;
8532 int min_integer_chars = 0;
8533 int min_decimal_places = 0;
8534 gboolean comma_seen = FALSE;
8535 GSList *embedded = NULL;
8536 GString *accum = g_string_new (NULL);
8537 /*
8538 * 0: before number
8539 * 1: in number, before dot
8540 * 2: in number, after dot
8541 * 3: after number
8542 */
8543 int phase = 0;
8544 gboolean string_is_open = FALSE;
8545 gboolean allow_embedded;
8546 gboolean has_currency = FALSE;
8547 gboolean has_number = TRUE;
8548 GOFormatParseState state;
8549 const GOFormatParseState *pstate = &state;
8550 unsigned tno, tno_end;
8551 unsigned tno_phase1 = 0, tno_phase2 = 0, tno_phase3 = 0;
8552
8553 switch (family) {
8554 case GO_FORMAT_TEXT:
8555 allow_embedded = FALSE;
8556 has_number = FALSE;
8557 gsf_xml_out_start_element (xout, NUMBER "text-style");
8558 break;
8559 case GO_FORMAT_PERCENTAGE:
8560 allow_embedded = TRUE;
8561 gsf_xml_out_start_element (xout, NUMBER "percentage-style");
8562 break;
8563 case GO_FORMAT_ACCOUNTING:
8564 case GO_FORMAT_CURRENCY:
8565 allow_embedded = FALSE;
8566 has_currency = TRUE;
8567 gsf_xml_out_start_element (xout, NUMBER "currency-style");
8568 break;
8569 case GO_FORMAT_NUMBER:
8570 allow_embedded = TRUE;
8571 gsf_xml_out_start_element (xout, NUMBER "number-style");
8572 break;
8573 default:
8574 g_assert_not_reached ();
8575 }
8576 gsf_xml_out_add_cstr (xout, STYLE "name", name);
8577
8578 odf_output_color (xout, fmt);
8579
8580 memset (&state, 0, sizeof (state));
8581 (void)go_format_preparse (xl, &state, TRUE, FALSE);
8582
8583 /* Find the start of each phase. */
8584 for (tno = 0; tno < state.tokens->len; tno++) {
8585 const GOFormatParseItem *ti = &GET_TOKEN(tno);
8586
8587 switch (ti->token) {
8588 case TOK_DECIMAL:
8589 tno_phase2 = tno + 1;
8590 /* Fall through */
8591 case '0': case '?': case '#':
8592 case '@':
8593 if (!tno_phase3)
8594 tno_phase1 = tno;
8595 tno_phase3 = tno + 1;
8596 break;
8597 default:
8598 break;
8599 }
8600 }
8601 if (!tno_phase2)
8602 tno_phase2 = tno_phase3;
8603
8604 /* Add terminator token */
8605 tno_end = pstate->tokens->len;
8606 g_array_set_size (pstate->tokens, tno_end + 1);
8607 GET_TOKEN(tno_end).tstr = "";
8608 GET_TOKEN(tno_end).token = 0;
8609 GET_TOKEN(tno_end).tt = 0;
8610
8611 for (tno = 0; tno < state.tokens->len; tno++) {
8612 const GOFormatParseItem *ti = &GET_TOKEN(tno);
8613 const char *tstr = ti->tstr;
8614
8615 if (phase == 0 && tno >= tno_phase1) {
8616 ODF_FLUSH_STRING;
8617 phase = 1;
8618 }
8619 if (phase == 1 && tno >= tno_phase2) {
8620 ODF_FLUSH_STRING;
8621 fixup_embedded (embedded, digits);
8622 phase = 2;
8623 }
8624 if (phase == 2 && tno >= tno_phase3) {
8625 ODF_FLUSH_STRING;
8626 if (has_number) {
8627 go_format_output_number_element_to_odf
8628 (xout,
8629 min_integer_digits, min_integer_chars,
8630 min_decimal_places,
8631 comma_seen, embedded,
8632 with_extension);
8633 embedded = NULL;
8634 }
8635 phase = 3;
8636 }
8637
8638 switch (ti->token) {
8639 case 0:
8640 ODF_FLUSH_STRING;
8641 g_array_free (state.tokens, TRUE);
8642 /* keep element open */
8643 g_string_free (accum, TRUE);
8644 return;
8645
8646 case '@':
8647 gsf_xml_out_simple_element (xout, NUMBER "text-content", NULL);
8648 break;
8649
8650 case TOK_DECIMAL:
8651 /* Nothing special */
8652 break;
8653
8654 case TOK_THOUSAND:
8655 comma_seen = TRUE;
8656 break;
8657
8658 case '?':
8659 ODF_FLUSH_STRING;
8660 if (phase != 2)
8661 min_integer_chars++;
8662 break;
8663
8664 case '0':
8665 ODF_FLUSH_STRING;
8666 if (phase == 2)
8667 min_decimal_places++;
8668 else {
8669 min_integer_digits++;
8670 min_integer_chars++;
8671 }
8672 digits++;
8673 break;
8674
8675 case '#':
8676 ODF_FLUSH_STRING;
8677 digits++;
8678 break;
8679
8680 case TOK_COLOR:
8681 case TOK_CONDITION:
8682 /* Handled elsewhere */
8683 break;
8684
8685 default:
8686 if (ti->token <= 0x7f) {
8687 g_string_append_c (accum, *tstr);
8688 break;
8689 }
8690 /* Fall through */
8691
8692 case 'd': case 'D':
8693 case 'y': case 'Y':
8694 case 'b': case 'B':
8695 case 'e': case 'E':
8696 case 'g': case 'G':
8697 case 'h': case 'H':
8698 case 'm': case 'M':
8699 case 's': case 'S':
8700 case ';':
8701 case '/':
8702 case TOK_AMPM3:
8703 case TOK_AMPM5:
8704 case TOK_ELAPSED_H:
8705 case TOK_ELAPSED_M:
8706 case TOK_ELAPSED_S:
8707 case TOK_GENERAL:
8708 case TOK_ERROR:
8709 g_printerr ("Unexpected token: %d\n", ti->token);
8710 break;
8711
8712 case TOK_REPEATED_CHAR: {
8713 size_t len = g_utf8_next_char (tstr + 1) - (tstr + 1);
8714 char *text;
8715
8716 if (!with_extension)
8717 break;
8718
8719 if (phase == 1 || phase == 2)
8720 break; /* Don't know what to do */
8721
8722 /* Flush visible prior contents */
8723 ODF_OPEN_STRING;
8724 gsf_xml_out_add_cstr (xout, NULL, accum->str);
8725 g_string_truncate (accum, 0);
8726
8727 text = g_strndup (tstr + 1, len);
8728 gsf_xml_out_start_element (xout, GNMSTYLE "repeated");
8729 gsf_xml_out_add_cstr (xout, NULL, text);
8730 gsf_xml_out_end_element (xout); /* </gnm:repeated> */
8731 g_free (text);
8732
8733 break;
8734 }
8735
8736 case TOK_INVISIBLE_CHAR:
8737 if ((phase == 1 || phase == 2) || !with_extension) {
8738 g_string_append_c (accum, ' ');
8739 } else {
8740 size_t len = g_utf8_next_char (tstr + 1) - (tstr + 1);
8741 gchar *text = g_strndup (tstr + 1, len);
8742
8743 /* Flush visible prior contents */
8744 ODF_OPEN_STRING;
8745 gsf_xml_out_add_cstr (xout, NULL, accum->str);
8746 g_string_truncate (accum, 0);
8747
8748 /*
8749 * Readers that do not understand gnm:invisible will
8750 * see this space. Readers that do understand the tag
8751 * will reach back and replace this space.
8752 */
8753 gsf_xml_out_add_cstr (xout, NULL, " ");
8754
8755 gsf_xml_out_start_element (xout, GNMSTYLE "invisible");
8756 gsf_xml_out_add_cstr (xout, GNMSTYLE "char", text);
8757 odf_add_bool (xout, OFFICE "process-content", TRUE);
8758 gsf_xml_out_end_element (xout); /* </gnm:invisible> */
8759 g_free (text);
8760 }
8761 break;
8762
8763 case TOK_STRING: {
8764 size_t len = strchr (tstr + 1, '"') - (tstr + 1);
8765 g_string_append_len (accum, tstr + 1, len);
8766 break;
8767 }
8768
8769 case '$':
8770 case TOK_CHAR: {
8771 size_t len = g_utf8_next_char (tstr) - tstr;
8772 gunichar uc = has_currency
8773 ? g_utf8_get_char (tstr)
8774 : 0;
8775 switch (uc) {
8776 case '$':
8777 case UNICODE_POUNDS1:
8778 case UNICODE_POUNDS2:
8779 case UNICODE_YEN:
8780 case UNICODE_YEN_WIDE:
8781 case UNICODE_EURO: {
8782 char *str = g_strndup (tstr, len);
8783 ODF_FLUSH_STRING;
8784 gsf_xml_out_simple_element (xout, NUMBER "currency-symbol", str);
8785 g_free (str);
8786 break;
8787 }
8788
8789 default:
8790 g_string_append_len (accum, tstr, len);
8791 }
8792 break;
8793 }
8794
8795 #if 0
8796 case TOK_LOCALE:
8797 ODF_WRITE_NUMBER;
8798 if (has_currency && (*str == '[') && (*(tstr + 1) == '$')) {
8799 int len;
8800 tstr += 2;
8801 len = strcspn (tstr, "-]");
8802 if (len > 0) {
8803 char *str;
8804 ODF_CLOSE_STRING;
8805 str = g_strndup (tstr, len);
8806 gsf_xml_out_simple_element (xout, NUMBER "currency-symbol", str);
8807 g_free (str);
8808 }
8809 }
8810 break;
8811 #endif
8812
8813 case TOK_ESCAPED_CHAR: {
8814 size_t len = g_utf8_next_char (tstr + 1) - (tstr + 1);
8815 g_string_append_len (accum, tstr + 1, len);
8816 break;
8817 }
8818 }
8819 }
8820
8821 g_assert_not_reached ();
8822 }
8823
8824 #undef ODF_FLUSH_STRING
8825 #undef ODF_OPEN_STRING
8826 #undef ODF_CLOSE_STRING
8827
8828
8829
8830 #define ODF_CLOSE_STRING if (string_is_open) { \
8831 gsf_xml_out_add_cstr (xout, NULL, accum->str); \
8832 gsf_xml_out_end_element (xout); /* </number:text> */ \
8833 string_is_open = FALSE; \
8834 }
8835 #define ODF_OPEN_STRING if (!string_is_open) { \
8836 gsf_xml_out_start_element (xout, NUMBER "text"); \
8837 string_is_open = TRUE; \
8838 g_string_erase (accum, 0, -1); \
8839 }
8840
8841 static void
go_format_output_scientific_number_element_to_odf(GsfXMLOut * xout,int min_integer_digits,int min_decimal_digits,int min_exponent_digits,gboolean comma_seen,gboolean forced_exponent_sign,gboolean engineering,gboolean use_literal_E,gboolean with_extension)8842 go_format_output_scientific_number_element_to_odf (GsfXMLOut *xout,
8843 int min_integer_digits,
8844 int min_decimal_digits,
8845 int min_exponent_digits,
8846 gboolean comma_seen,
8847 gboolean forced_exponent_sign,
8848 gboolean engineering,
8849 gboolean use_literal_E,
8850 gboolean with_extension)
8851 {
8852 gsf_xml_out_start_element (xout, NUMBER "scientific-number");
8853 gsf_xml_out_add_int (xout, NUMBER "decimal-places", min_decimal_digits);
8854 odf_add_bool (xout, NUMBER "grouping", comma_seen);
8855 gsf_xml_out_add_int (xout, NUMBER "min-integer-digits", min_integer_digits);
8856 gsf_xml_out_add_int (xout, NUMBER "min-exponent-digits", min_exponent_digits);
8857 if (with_extension) {
8858 odf_add_bool (xout, GNMSTYLE "forced-exponent-sign", forced_exponent_sign);
8859 odf_add_bool (xout, GNMSTYLE "engineering", engineering);
8860 odf_add_bool (xout, GNMSTYLE "literal-E", use_literal_E);
8861 }
8862 gsf_xml_out_end_element (xout); /* </number:scientific-number> */
8863 }
8864
8865 static void
go_format_output_scientific_number_to_odf(GsfXMLOut * xout,GOFormat const * fmt,char const * name,gboolean with_extension)8866 go_format_output_scientific_number_to_odf (GsfXMLOut *xout, GOFormat const *fmt,
8867 char const *name,
8868 gboolean with_extension)
8869 {
8870 char const *xl = go_format_as_XL (fmt);
8871 GString *accum = g_string_new (NULL);
8872
8873 int min_integer_digits = 0;
8874 int min_decimal_digits = 0;
8875 int min_exponent_digits = 0;
8876 int hashes = 0;
8877 gboolean comma_seen = FALSE;
8878 gboolean dot_seen = FALSE;
8879
8880 gboolean forced_exponent_sign = FALSE;
8881 gboolean number_completed = FALSE;
8882 gboolean string_is_open = FALSE;
8883
8884 gsf_xml_out_start_element (xout, NUMBER "number-style");
8885 gsf_xml_out_add_cstr (xout, STYLE "name", name);
8886 odf_output_color (xout, fmt);
8887
8888 while (1) {
8889 const char *token = xl;
8890 GOFormatTokenType tt;
8891 int t = go_format_token (&xl, &tt);
8892
8893 switch (t) {
8894 case 0: case ';':
8895 ODF_CLOSE_STRING;
8896 /* keep element open */
8897 g_string_free (accum, TRUE);
8898 return;
8899
8900 case TOK_DECIMAL:
8901 if (number_completed) break;
8902 /* ODF allows only for a single number specification */
8903 ODF_CLOSE_STRING;
8904 dot_seen = TRUE;
8905 break;
8906
8907 case TOK_THOUSAND:
8908 if (number_completed) break;
8909 /* ODF allows only for a single number specification */
8910 ODF_CLOSE_STRING;
8911 comma_seen = TRUE;
8912 break;
8913
8914 case '0':
8915 if (number_completed) break;
8916 /* ODF allows only for a single number specification */
8917 ODF_CLOSE_STRING;
8918 if (dot_seen)
8919 min_decimal_digits++;
8920 else
8921 min_integer_digits++;
8922 break;
8923
8924 case '?':
8925 if (number_completed) break;
8926 /* ODF allows only for a single number specification */
8927 ODF_CLOSE_STRING;
8928 break;
8929
8930 case '#':
8931 if (number_completed) break;
8932 /* ODF allows only for a single number specification */
8933 ODF_CLOSE_STRING;
8934 if (!dot_seen)
8935 hashes++;
8936 break;
8937
8938 #ifdef ALLOW_SI_APPEND
8939 case TOK_EXP_SI:
8940 #endif
8941 #ifdef ALLOW_EE_MARKUP
8942 case TOK_EXP_MU:
8943 #ifdef ALLOW_SI_APPEND
8944 case TOK_EXP_MU_SI:
8945 #endif
8946 #endif
8947 case TOK_EXP: {
8948 gboolean use_literal_E = TRUE;
8949
8950 if (number_completed)
8951 break;
8952
8953 #ifdef ALLOW_EE_MARKUP
8954 if (t == TOK_EXP_MU)
8955 use_literal_E = FALSE;
8956 #ifdef ALLOW_SI_APPEND
8957 if (t == TOK_EXP_MU_SI)
8958 use_literal_E = FALSE;
8959 #endif
8960 #endif
8961
8962 if (*xl == '+') {
8963 forced_exponent_sign = TRUE;
8964 xl++;
8965 }
8966 if (*xl == '-')
8967 xl++;
8968 while (*xl == '0') {
8969 xl++;
8970 min_exponent_digits++;
8971 }
8972 go_format_output_scientific_number_element_to_odf
8973 (xout, min_integer_digits, min_decimal_digits,
8974 min_exponent_digits, comma_seen,
8975 forced_exponent_sign,
8976 hashes > 0 && (hashes + min_integer_digits == 3),
8977 use_literal_E, with_extension);
8978 number_completed = TRUE;
8979 break;
8980 }
8981
8982 case '/':
8983 case 'd': case 'D':
8984 case 'y': case 'Y':
8985 case 'b': case 'B':
8986 case 'g': case 'G':
8987 case 'h': case 'H':
8988 case 'm': case 'M':
8989 case 's': case 'S':
8990 case TOK_AMPM3:
8991 case TOK_AMPM5:
8992 case TOK_ELAPSED_H:
8993 case TOK_ELAPSED_M:
8994 case TOK_ELAPSED_S:
8995 case TOK_GENERAL:
8996 case TOK_INVISIBLE_CHAR:
8997 case TOK_REPEATED_CHAR:
8998 case TOK_CONDITION:
8999 case TOK_LOCALE:
9000 case TOK_ERROR:
9001 case TOK_COLOR:
9002 break;
9003
9004 case TOK_STRING: {
9005 size_t len = strchr (token + 1, '"') - (token + 1);
9006 if (len > 0) {
9007 ODF_OPEN_STRING;
9008 g_string_append_len (accum, token + 1, len);
9009 }
9010 break;
9011 }
9012
9013 case TOK_CHAR: {
9014 size_t len = g_utf8_next_char(token) - (token);
9015 if (len > 0) {
9016 ODF_OPEN_STRING;
9017 if (*token == '-')
9018 g_string_append_unichar (accum, UNICODE_MINUS);
9019 else
9020 g_string_append_len (accum, token, len);
9021 }
9022 break;
9023 }
9024
9025 case TOK_ESCAPED_CHAR: {
9026 size_t len = g_utf8_next_char(token + 1) - (token + 1);
9027 if (len > 0) {
9028 ODF_OPEN_STRING;
9029 if (*(token+1) == '-')
9030 g_string_append_unichar (accum, UNICODE_MINUS);
9031 else
9032 g_string_append_len (accum, token + 1, len);
9033 }
9034 break;
9035 }
9036
9037 default:
9038 if (t <= 0x7f) {
9039 ODF_OPEN_STRING;
9040 g_string_append_c (accum, t);
9041 }
9042 break;
9043 }
9044 }
9045 }
9046
9047 #undef ODF_CLOSE_STRING
9048 #undef ODF_OPEN_STRING
9049
9050 static void
go_format_output_general_to_odf(GsfXMLOut * xout,GOFormat const * fmt,char const * name)9051 go_format_output_general_to_odf (GsfXMLOut *xout, GOFormat const *fmt,
9052 char const *name)
9053 {
9054 gsf_xml_out_start_element (xout, NUMBER "number-style");
9055 gsf_xml_out_add_cstr (xout, STYLE "name", name);
9056 odf_output_color (xout, fmt);
9057 gsf_xml_out_start_element (xout, NUMBER "number");
9058 gsf_xml_out_add_int (xout, NUMBER "min-integer-digits", 1);
9059 gsf_xml_out_end_element (xout); /* </number:number> */
9060 gsf_xml_out_simple_element (xout, NUMBER "text", NULL);
9061 /* keep element open */
9062 }
9063
9064 static gboolean
go_format_output_simple_to_odf(GsfXMLOut * xout,gboolean with_extension,GOFormat const * fmt,GOFormat const * parent_fmt,char const * name,gboolean keep_open)9065 go_format_output_simple_to_odf (GsfXMLOut *xout, gboolean with_extension,
9066 GOFormat const *fmt, GOFormat const *parent_fmt,
9067 char const *name, gboolean keep_open)
9068 {
9069 gboolean result = TRUE;
9070 GOFormatFamily family;
9071 GOFormatDetails details;
9072 gboolean exact;
9073
9074 family = go_format_get_family (fmt);
9075 if (family == GO_FORMAT_UNKNOWN) {
9076 go_format_get_details (parent_fmt, &details, &exact);
9077 } else {
9078 go_format_get_details (fmt, &details, &exact);
9079 }
9080 family = details.family;
9081
9082 switch (family) {
9083 case GO_FORMAT_GENERAL:
9084 go_format_output_general_to_odf (xout, fmt, name);
9085 result = FALSE;
9086 break;
9087 case GO_FORMAT_DATE:
9088 go_format_output_date_to_odf (xout, fmt, name, family, with_extension);
9089 break;
9090 case GO_FORMAT_TIME:
9091 go_format_output_date_to_odf (xout, fmt, name, family, with_extension);
9092 break;
9093 case GO_FORMAT_FRACTION:
9094 go_format_output_fraction_to_odf (xout, fmt, name, with_extension);
9095 break;
9096 case GO_FORMAT_SCIENTIFIC:
9097 go_format_output_scientific_number_to_odf (xout, fmt, name, with_extension);
9098 break;
9099 case GO_FORMAT_ACCOUNTING:
9100 case GO_FORMAT_CURRENCY:
9101 case GO_FORMAT_PERCENTAGE:
9102 case GO_FORMAT_NUMBER:
9103 case GO_FORMAT_TEXT:
9104 go_format_output_number_to_odf (xout, fmt, family, name, with_extension);
9105 break;
9106 default: {
9107 /* We need to output something and we don't need any details for this */
9108 int date = 0, digit = 0;
9109 char const *fstr, *str = go_format_as_XL (fmt);
9110 fstr = str;
9111 while (*str != '\0') {
9112 switch (*str) {
9113 case 'd': case 'm': case 'y': case 'h': case 's':
9114 date++;
9115 break;
9116 case '#': case '.': case '0': case 'e':
9117 digit++;
9118 break;
9119 default:
9120 break;
9121 }
9122 str++;
9123 }
9124 if (digit < date)
9125 go_format_output_date_to_odf (xout, fmt, name,
9126 GO_FORMAT_DATE, with_extension);
9127 else {
9128 /* We have a format that we can't identify */
9129 /* The following is really only appropriate for "" or so */
9130 gsf_xml_out_start_element (xout, NUMBER "number-style");
9131 gsf_xml_out_add_cstr (xout, STYLE "name", name);
9132 gsf_xml_out_start_element (xout, NUMBER "text");
9133 gsf_xml_out_add_cstr (xout, NULL, fstr);
9134 gsf_xml_out_end_element (xout);
9135 }
9136 result = FALSE;
9137 break;
9138 }
9139 }
9140
9141 if (!keep_open)
9142 gsf_xml_out_end_element (xout);
9143
9144 return result;
9145 }
9146
9147 static void
go_format_output_conditional_to_odf(GsfXMLOut * xout,gboolean with_extension,GOFormat const * fmt,char const * name)9148 go_format_output_conditional_to_odf (GsfXMLOut *xout, gboolean with_extension,
9149 GOFormat const *fmt, char const *name)
9150 {
9151 int i, N, defi = -1;
9152 GOFormatCondition const *cond0;
9153 int last_implicit_num = -1;
9154
9155 g_return_if_fail (fmt->typ == GO_FMT_COND);
9156
9157 N = fmt->u.cond.n;
9158 g_return_if_fail (N > 0);
9159 cond0 = &fmt->u.cond.conditions[0];
9160 g_return_if_fail (cond0->op >= GO_FMT_COND_EQ && cond0->op <= GO_FMT_COND_GE);
9161
9162 /*
9163 * Output all sub-formats, except perhaps one deemed the default.
9164 */
9165 for (i = 0; i < fmt->u.cond.n; i++) {
9166 GOFormatCondition const *cond = &fmt->u.cond.conditions[i];
9167
9168 if (cond->implicit && cond->op != GO_FMT_COND_TEXT)
9169 last_implicit_num = i;
9170
9171 switch (cond->op) {
9172 case GO_FMT_COND_TEXT:
9173 case GO_FMT_COND_NONTEXT:
9174 if (defi != -1 || i >= 4)
9175 g_warning ("This shouldn't happen.");
9176 defi = i;
9177 break;
9178 case GO_FMT_COND_NONE:
9179 if (i < 4)
9180 g_warning ("This shouldn't happen.");
9181 if (defi == -1)
9182 defi = i;
9183 break;
9184 default: {
9185 char *partname = g_strdup_printf ("%s-%d", name, i);
9186 go_format_output_simple_to_odf (xout, with_extension,
9187 cond->fmt, fmt,
9188 partname, FALSE);
9189 g_free (partname);
9190 }
9191 }
9192 }
9193
9194 if (defi == -1 && last_implicit_num != -1) {
9195 /*
9196 * The default could be this one, but we cannot reorder
9197 * conditions unless we can prove it doesn't matter. Just
9198 * check if it is last.
9199 */
9200 if (last_implicit_num == fmt->u.cond.n - 1)
9201 defi = last_implicit_num;
9202 }
9203
9204 if (defi == -1) {
9205 /* We don't have a default; make one. */
9206 go_format_output_general_to_odf (xout, go_format_general (),
9207 name);
9208 } else {
9209 go_format_output_simple_to_odf (xout, with_extension,
9210 fmt->u.cond.conditions[defi].fmt, fmt,
9211 name, TRUE);
9212 }
9213
9214 for (i = 0; i < fmt->u.cond.n; i++) {
9215 GOFormatCondition const *cond = &fmt->u.cond.conditions[i];
9216 const char *oper = NULL;
9217 double val = cond->val;
9218 char *partname;
9219 GString *condition;
9220
9221 if (i == defi)
9222 continue;
9223
9224 switch (cond->op) {
9225 case GO_FMT_COND_TEXT:
9226 case GO_FMT_COND_NONE:
9227 case GO_FMT_COND_NONTEXT:
9228 /* Already handled or to be ignored. */
9229 continue;
9230
9231 case GO_FMT_COND_EQ: oper = "="; break;
9232 case GO_FMT_COND_NE: oper = "!="; break;
9233 case GO_FMT_COND_LT: oper = "<"; break;
9234 case GO_FMT_COND_LE: oper = "<="; break;
9235 case GO_FMT_COND_GT: oper = ">"; break;
9236 case GO_FMT_COND_GE: oper = ">="; break;
9237 }
9238
9239 partname = g_strdup_printf ("%s-%d", name, i);
9240
9241 condition = g_string_new ("value()");
9242 g_string_append (condition, oper);
9243 go_dtoa (condition, "!g", val);
9244
9245 gsf_xml_out_start_element (xout, STYLE "map");
9246 gsf_xml_out_add_cstr (xout, STYLE "condition", condition->str);
9247 gsf_xml_out_add_cstr (xout, STYLE "apply-style-name", partname);
9248 gsf_xml_out_end_element (xout); /* </style:map> */
9249
9250 g_free (partname);
9251 g_string_free (condition, TRUE);
9252 }
9253
9254 gsf_xml_out_end_element (xout); /* </number:text-style> or </number:number-style> */
9255 }
9256
9257 gboolean
go_format_output_to_odf(GsfXMLOut * xout,GOFormat const * fmt,G_GNUC_UNUSED int cond_part,char const * name,gboolean with_extension)9258 go_format_output_to_odf (GsfXMLOut *xout, GOFormat const *fmt,
9259 G_GNUC_UNUSED int cond_part, char const *name,
9260 gboolean with_extension)
9261 {
9262 gboolean pp, result;
9263
9264 g_object_get (G_OBJECT (xout), "pretty-print", &pp, NULL);
9265 if (pp) {
9266 /* We need to switch off pretty printing since number:text preserves whitespace */
9267 g_object_set (G_OBJECT (xout), "pretty-print", FALSE, NULL);
9268 }
9269
9270 if (fmt->typ == GO_FMT_COND) {
9271 go_format_output_conditional_to_odf (xout, with_extension, fmt, name);
9272 result = TRUE;
9273 } else {
9274 result = go_format_output_simple_to_odf (xout, with_extension,
9275 fmt, NULL, name, FALSE);
9276 }
9277
9278 if (pp)
9279 g_object_set (G_OBJECT (xout), "pretty-print", pp, NULL);
9280
9281 return result;
9282 }
9283 #endif
9284