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 = &currency;
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 = &currency;
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 &currency;
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