Fixed some issues with internationalisation
[pspp-builds.git] / src / data-out.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <ctype.h>
23 #include <math.h>
24 #include <float.h>
25 #include <stdlib.h>
26 #include <time.h>
27 #include "approx.h"
28 #include "error.h"
29 #include "format.h"
30 #include "julcal/julcal.h"
31 #include "magic.h"
32 #include "misc.h"
33 #include "misc.h"
34 #include "settings.h"
35 #include "str.h"
36 #include "var.h"
37
38 #include "debug-print.h"
39
40 /* In older versions, numbers got their trailing zeros stripped.
41    Newer versions leave them on when there's room.  Comment this next
42    line out for retro styling. */
43 #define NEW_STYLE 1
44 \f
45 /* Public functions. */
46
47 typedef int convert_func (char *, const struct fmt_spec *,
48                           const union value *);
49
50 static convert_func convert_F, convert_N, convert_E, convert_F_plus;
51 static convert_func convert_Z, convert_A, convert_AHEX, convert_IB;
52 static convert_func convert_P, convert_PIB, convert_PIBHEX, convert_PK;
53 static convert_func convert_RB, convert_RBHEX, convert_CCx, convert_date;
54 static convert_func convert_time, convert_WKDAY, convert_MONTH;
55 static convert_func try_F;
56
57 /* Converts binary value V into printable form in string S according
58    to format specification FP.  The string as written has exactly
59    FP->W characters.  It is not null-terminated.  Returns 1 on
60    success, 0 on failure. */
61 int
62 data_out (char *s, const struct fmt_spec *fp, const union value *v)
63 {
64   union value tmp_val;
65   
66   {
67     int cat = formats[fp->type].cat;
68     if ((cat & FCAT_BLANKS_SYSMIS) && v->f == SYSMIS)
69       {
70         memset (s, ' ', fp->w);
71         s[fp->w - fp->d - 1] = '.';
72         return 1;
73       }
74     if ((cat & FCAT_SHIFT_DECIMAL) && v->f != SYSMIS && fp->d)
75       {
76         tmp_val.f = v->f * pow (10.0, fp->d);
77         v = &tmp_val;
78       }
79   }
80   
81   {
82     static convert_func *const handlers[FMT_NUMBER_OF_FORMATS] =
83       {
84         convert_F, convert_N, convert_E, convert_F_plus,
85         convert_F_plus, convert_F_plus, convert_F_plus,
86         convert_Z, convert_A, convert_AHEX, convert_IB, convert_P, convert_PIB,
87         convert_PIBHEX, convert_PK, convert_RB, convert_RBHEX,
88         convert_CCx, convert_CCx, convert_CCx, convert_CCx, convert_CCx,
89         convert_date, convert_date, convert_date, convert_date, convert_date,
90         convert_date, convert_date, convert_date, convert_date,
91         convert_time, convert_time,
92         convert_WKDAY, convert_MONTH,
93       };
94
95     return handlers[fp->type] (s, fp, v);
96   }
97 }
98
99 /* Converts V into S in F format with width W and D decimal places,
100    then deletes trailing zeros.  S is not null-terminated. */
101 void
102 num_to_string (double v, char *s, int w, int d)
103 {
104   /* Dummies to pass to convert_F. */
105   union value val;
106   struct fmt_spec f;
107
108 #if !NEW_STYLE
109   /* Pointer to `.' in S. */
110   char *decp;
111
112   /* Pointer to `E' in S. */
113   char *expp;
114
115   /* Number of characters to delete. */
116   int n = 0;
117 #endif
118
119   f.w = w;
120   f.d = d;
121   val.f = v;
122
123   /* Cut out the jokers. */
124   if (!finite (v))
125     {
126       char temp[9];
127       int len;
128
129       if (isnan (v))
130         {
131           memcpy (temp, "NaN", 3);
132           len = 3;
133         }
134       else if (isinf (v))
135         {
136           memcpy (temp, "+Infinity", 9);
137           if (v < 0)
138             temp[0] = '-';
139           len = 9;
140         }
141       else
142         {
143           memcpy (temp, _("Unknown"), 7);
144           len = 7;
145         }
146       if (w > len)
147         {
148           int pad = w - len;
149           memset (s, ' ', pad);
150           s += pad;
151           w -= pad;
152         }
153       memcpy (s, temp, w);
154       return;
155     }
156
157   try_F (s, &f, &val);
158
159 #if !NEW_STYLE
160   decp = memchr (s, set_decimal, w);
161   if (!decp)
162     return;
163
164   /* If there's an `E' we can only delete 0s before the E. */
165   expp = memchr (s, 'E', w);
166   if (expp)
167     {
168       while (expp[-n - 1] == '0')
169         n++;
170       if (expp[-n - 1] == set_decimal)
171         n++;
172       memmove (&s[n], s, expp - s - n);
173       memset (s, ' ', n);
174       return;
175     }
176
177   /* Otherwise delete all trailing 0s. */
178   n++;
179   while (s[w - n] == '0')
180     n++;
181   if (s[w - n] != set_decimal)
182     {
183       /* Avoid stripping `.0' to `'. */
184       if (w == n || !isdigit ((unsigned char) s[w - n - 1]))
185         n -= 2;
186     }
187   else
188     n--;
189   memmove (&s[n], s, w - n);
190   memset (s, ' ', n);
191 #endif
192 }
193 \f
194 /* Main conversion functions. */
195
196 static void insert_commas (char *dst, const char *src,
197                            const struct fmt_spec *fp);
198 static int year4 (int year);
199 static int try_CCx (char *s, const struct fmt_spec *fp, double v);
200
201 #if FLT_RADIX!=2
202 #error Write your own floating-point output routines.
203 #endif
204
205 /* PORTME:
206
207    Some of the routines in this file are likely very specific to
208    base-2 representation of floating-point numbers, most notably the
209    routines that use frexp() or ldexp().  These attempt to extract
210    individual digits by setting the base-2 exponent and
211    multiplying/dividing by powers of 2.  In base-2 numeration systems,
212    this just nudges the exponent up or down, but in base-10 floating
213    point, such multiplications/division can cause catastrophic loss of
214    precision.
215
216    The author has never personally used a machine that didn't use
217    binary floating point formats, so he is unwilling, and perhaps
218    unable, to code around this "problem".  */
219
220 /* Converts a number between 0 and 15 inclusive to a `hexit'
221    [0-9A-F]. */
222 #define MAKE_HEXIT(X) ("0123456789ABCDEF"[X])
223
224 /* Table of powers of 10. */
225 static const double power10[] =
226   {
227     0,  /* Not used. */
228     1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09, 1e10,
229     1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20,
230     1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30,
231     1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, 1e40,
232   };
233
234 /* Handles F format. */
235 static int
236 convert_F (char *dst, const struct fmt_spec *fp, const union value *v)
237 {
238   if (!try_F (dst, fp, v))
239     convert_E (dst, fp, v);
240   return 1;
241 }
242
243 /* Handles N format. */
244 static int
245 convert_N (char *dst, const struct fmt_spec *fp, const union value *v)
246 {
247   double d = floor (v->f);
248
249   if (d < 0 || d == SYSMIS)
250     {
251       msg (ME, _("The N output format cannot be used to output a "
252                  "negative number or the system-missing value."));
253       return 0;
254     }
255   
256   if (d < power10[fp->w])
257     {
258       char buf[128];
259       sprintf (buf, "%0*.0f", fp->w, v->f);
260       memcpy (dst, buf, fp->w);
261     }
262   else
263     memset (dst, '*', fp->w);
264
265   return 1;
266 }
267
268 /* Handles E format.  Also operates as fallback for some other
269    formats. */
270 static int
271 convert_E (char *dst, const struct fmt_spec *fp, const union value *v)
272 {
273   /* Temporary buffer. */
274   char buf[128];
275   
276   /* Ranged number of decimal places. */
277   int d;
278
279   /* Check that the format is width enough.
280      Although PSPP generally checks this, convert_E() can be called as
281      a fallback from other formats which do not check. */
282   if (fp->w < 6)
283     {
284       memset (dst, '*', fp->w);
285       return 1;
286     }
287
288   /* Put decimal places in usable range. */
289   d = min (fp->d, fp->w - 6);
290   if (v->f < 0)
291     d--;
292   if (d < 0)
293     d = 0;
294   sprintf (buf, "%*.*E", fp->w, d, v->f);
295
296   /* What we do here is force the exponent part to have four
297      characters whenever possible.  That is, 1.00E+99 is okay (`E+99')
298      but 1.00E+100 (`E+100') must be coerced to 1.00+100 (`+100').  On
299      the other hand, 1.00E1000 (`E+100') cannot be canonicalized.
300      Note that ANSI C guarantees at least two digits in the
301      exponent. */
302   if (fabs (v->f) > 1e99)
303     {
304       /* Pointer to the `E' in buf. */
305       char *cp;
306
307       cp = strchr (buf, 'E');
308       if (cp)
309         {
310           /* Exponent better not be bigger than an int. */
311           int exp = atoi (cp + 1); 
312
313           if (abs (exp) > 99 && abs (exp) < 1000)
314             {
315               /* Shift everything left one place: 1.00e+100 -> 1.00+100. */
316               cp[0] = cp[1];
317               cp[1] = cp[2];
318               cp[2] = cp[3];
319               cp[3] = cp[4];
320             }
321           else if (abs (exp) >= 1000)
322             memset (buf, '*', fp->w);
323         }
324     }
325
326   /* The C locale always uses a period `.' as a decimal point.
327      Translate to comma if necessary. */
328   if ((set_decimal == ',' && fp->type != FMT_DOT)
329       || (set_decimal == '.' && fp->type == FMT_DOT))
330     {
331       char *cp = strchr (buf, '.');
332       if (cp)
333         *cp = ',';
334     }
335
336   memcpy (dst, buf, fp->w);
337   return 1;
338 }
339
340 /* Handles COMMA, DOT, DOLLAR, and PCT formats. */
341 static int
342 convert_F_plus (char *dst, const struct fmt_spec *fp, const union value *v)
343 {
344   char buf[40];
345   
346   if (try_F (buf, fp, v))
347     insert_commas (dst, buf, fp);
348   else
349     convert_E (dst, fp, v);
350
351   return 1;
352 }
353
354 static int
355 convert_Z (char *dst, const struct fmt_spec *fp, const union value *v)
356 {
357   static int warned = 0;
358
359   if (!warned)
360     {
361       msg (MW, 
362         _("Quality of zoned decimal (Z) output format code is suspect.  Check your results. Report bugs to %s."),
363         PACKAGE_BUGREPORT);
364       warned = 1;
365     }
366
367   if (v->f == SYSMIS)
368     {
369       msg (ME, _("The system-missing value cannot be output as a zoned "
370                  "decimal number."));
371       return 0;
372     }
373   
374   {
375     char buf[41];
376     double d;
377     int i;
378     
379     d = fabs (floor (v->f));
380     if (d >= power10[fp->w])
381       {
382         msg (ME, _("Number %g too big to fit in field with format Z%d.%d."),
383              v->f, fp->w, fp->d);
384         return 0;
385       }
386
387     sprintf (buf, "%*.0f", fp->w, v->f);
388     for (i = 0; i < fp->w; i++)
389       dst[i] = (buf[i] - '0') | 0xf0;
390     if (v->f < 0)
391       dst[fp->w - 1] &= 0xdf;
392   }
393
394   return 1;
395 }
396
397 static int
398 convert_A (char *dst, const struct fmt_spec *fp, const union value *v)
399 {
400   memcpy (dst, v->c, fp->w);
401   return 1;
402 }
403
404 static int
405 convert_AHEX (char *dst, const struct fmt_spec *fp, const union value *v)
406 {
407   int i;
408
409   for (i = 0; i < fp->w / 2; i++)
410     {
411       ((unsigned char *) dst)[i * 2] = MAKE_HEXIT ((v->c[i]) >> 4);
412       ((unsigned char *) dst)[i * 2 + 1] = MAKE_HEXIT ((v->c[i]) & 0xf);
413     }
414
415   return 1;
416 }
417
418 static int
419 convert_IB (char *dst, const struct fmt_spec *fp, const union value *v)
420 {
421   /* Strategy: Basically the same as convert_PIBHEX() but with base
422      256. Then it's necessary to negate the two's-complement result if
423      v->f is negative. */
424
425   /* Used for constructing the two's-complement result. */
426   unsigned temp[8];
427
428   /* Fraction (mantissa). */
429   double frac;
430
431   /* Exponent. */
432   int exp;
433
434   /* Difference between exponent and (-8*fp->w-1). */
435   int diff;
436
437   /* Counter. */
438   int i;
439
440   /* Make the exponent (-8*fp->w-1). */
441   frac = frexp (fabs (v->f), &exp);
442   diff = exp - (-8 * fp->w - 1);
443   exp -= diff;
444   frac *= ldexp (1.0, diff);
445
446   /* Extract each base-256 digit. */
447   for (i = 0; i < fp->w; i++)
448     {
449       modf (frac, &frac);
450       frac *= 256.0;
451       temp[i] = floor (frac);
452     }
453
454   /* Perform two's-complement negation if v->f is negative. */
455   if (v->f < 0)
456     {
457       /* Perform NOT operation. */
458       for (i = 0; i < fp->w; i++)
459         temp[i] = ~temp[i];
460       /* Add 1 to the whole number. */
461       for (i = fp->w - 1; i >= 0; i--)
462         {
463           temp[i]++;
464           if (temp[i])
465             break;
466         }
467     }
468   memcpy (dst, temp, fp->w);
469 #ifndef WORDS_BIGENDIAN
470   mm_reverse (dst, fp->w);
471 #endif
472
473   return 1;
474 }
475
476 static int
477 convert_P (char *dst, const struct fmt_spec *fp, const union value *v)
478 {
479   /* Buffer for v->f*2-1 characters + a decimal point if library is
480      not quite compliant + a null. */
481   char buf[17];
482
483   /* Counter. */
484   int i;
485
486   /* Main extraction. */
487   sprintf (buf, "%0*.0f", fp->w * 2 - 1, floor (fabs (v->f)));
488
489   for (i = 0; i < fp->w; i++)
490     ((unsigned char *) dst)[i]
491       = ((buf[i * 2] - '0') << 4) + buf[i * 2 + 1] - '0';
492
493   /* Set sign. */
494   dst[fp->w - 1] &= 0xf0;
495   if (v->f >= 0.0)
496     dst[fp->w - 1] |= 0xf;
497   else
498     dst[fp->w - 1] |= 0xd;
499
500   return 1;
501 }
502
503 static int
504 convert_PIB (char *dst, const struct fmt_spec *fp, const union value *v)
505 {
506   /* Strategy: Basically the same as convert_IB(). */
507
508   /* Fraction (mantissa). */
509   double frac;
510
511   /* Exponent. */
512   int exp;
513
514   /* Difference between exponent and (-8*fp->w). */
515   int diff;
516
517   /* Counter. */
518   int i;
519
520   /* Make the exponent (-8*fp->w). */
521   frac = frexp (fabs (v->f), &exp);
522   diff = exp - (-8 * fp->w);
523   exp -= diff;
524   frac *= ldexp (1.0, diff);
525
526   /* Extract each base-256 digit. */
527   for (i = 0; i < fp->w; i++)
528     {
529       modf (frac, &frac);
530       frac *= 256.0;
531       ((unsigned char *) dst)[i] = floor (frac);
532     }
533 #ifndef WORDS_BIGENDIAN
534   mm_reverse (dst, fp->w);
535 #endif
536
537   return 1;
538 }
539
540 static int
541 convert_PIBHEX (char *dst, const struct fmt_spec *fp, const union value *v)
542 {
543   /* Strategy: Use frexp() to create a normalized result (but mostly
544      to find the base-2 exponent), then change the base-2 exponent to
545      (-4*fp->w) using multiplication and division by powers of two.
546      Extract each hexit by multiplying by 16. */
547
548   /* Fraction (mantissa). */
549   double frac;
550
551   /* Exponent. */
552   int exp;
553
554   /* Difference between exponent and (-4*fp->w). */
555   int diff;
556
557   /* Counter. */
558   int i;
559
560   /* Make the exponent (-4*fp->w). */
561   frac = frexp (fabs (v->f), &exp);
562   diff = exp - (-4 * fp->w);
563   exp -= diff;
564   frac *= ldexp (1.0, diff);
565
566   /* Extract each hexit. */
567   for (i = 0; i < fp->w; i++)
568     {
569       modf (frac, &frac);
570       frac *= 16.0;
571       *dst++ = MAKE_HEXIT ((int) floor (frac));
572     }
573
574   return 1;
575 }
576
577 static int
578 convert_PK (char *dst, const struct fmt_spec *fp, const union value *v)
579 {
580   /* Buffer for v->f*2 characters + a decimal point if library is not
581      quite compliant + a null. */
582   char buf[18];
583
584   /* Counter. */
585   int i;
586
587   /* Main extraction. */
588   sprintf (buf, "%0*.0f", fp->w * 2, floor (fabs (v->f)));
589
590   for (i = 0; i < fp->w; i++)
591     ((unsigned char *) dst)[i]
592       = ((buf[i * 2] - '0') << 4) + buf[i * 2 + 1] - '0';
593
594   return 1;
595 }
596
597 static int
598 convert_RB (char *dst, const struct fmt_spec *fp, const union value *v)
599 {
600   union
601     {
602       double d;
603       char c[8];
604     }
605   u;
606
607   u.d = v->f;
608   memcpy (dst, u.c, fp->w);
609
610   return 1;
611 }
612
613 static int
614 convert_RBHEX (char *dst, const struct fmt_spec *fp, const union value *v)
615 {
616   union
617   {
618     double d;
619     char c[8];
620   }
621   u;
622
623   int i;
624
625   u.d = v->f;
626   for (i = 0; i < fp->w / 2; i++)
627     {
628       *dst++ = MAKE_HEXIT (u.c[i] >> 4);
629       *dst++ = MAKE_HEXIT (u.c[i] & 15);
630     }
631
632   return 1;
633 }
634
635 static int
636 convert_CCx (char *dst, const struct fmt_spec *fp, const union value *v)
637 {
638   if (try_CCx (dst, fp, v->f))
639     return 1;
640   else
641     {
642       struct fmt_spec f;
643       
644       f.type = FMT_COMMA;
645       f.w = fp->w;
646       f.d = fp->d;
647   
648       return convert_F (dst, &f, v);
649     }
650 }
651
652 static int
653 convert_date (char *dst, const struct fmt_spec *fp, const union value *v)
654 {
655   static const char *months[12] =
656     {
657       "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
658       "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
659     };
660
661   char buf[64] = {0};
662   int month, day, year;
663
664   julian_to_calendar (v->f / 86400., &year, &month, &day);
665   switch (fp->type)
666     {
667     case FMT_DATE:
668       if (fp->w >= 11)
669         sprintf (buf, "%02d-%s-%04d", day, months[month - 1], year);
670       else
671         sprintf (buf, "%02d-%s-%02d", day, months[month - 1], year % 100);
672       break;
673     case FMT_EDATE:
674       if (fp->w >= 10)
675         sprintf (buf, "%02d.%02d.%04d", day, month, year);
676       else
677         sprintf (buf, "%02d.%02d.%02d", day, month, year % 100);
678       break;
679     case FMT_SDATE:
680       if (fp->w >= 10)
681         sprintf (buf, "%04d/%02d/%02d", year, month, day);
682       else
683         sprintf (buf, "%02d/%02d/%02d", year % 100, month, day);
684       break;
685     case FMT_ADATE:
686       if (fp->w >= 10)
687         sprintf (buf, "%02d/%02d/%04d", month, day, year);
688       else
689         sprintf (buf, "%02d/%02d/%02d", month, day, year % 100);
690       break;
691     case FMT_JDATE:
692       {
693         int yday = (v->f / 86400.) - calendar_to_julian (year, 1, 1) + 1;
694         
695         if (fp->w >= 7)
696           {
697             if (year4 (year))
698               sprintf (buf, "%04d%03d", year, yday);
699           }
700         else
701           sprintf (buf, "%02d%03d", year % 100, yday);
702         break;
703       }
704     case FMT_QYR:
705       if (fp->w >= 8)
706         sprintf (buf, "%d Q% 04d", (month - 1) / 3 + 1, year);
707       else
708         sprintf (buf, "%d Q% 02d", (month - 1) / 3 + 1, year % 100);
709       break;
710     case FMT_MOYR:
711       if (fp->w >= 8)
712         sprintf (buf, "%s% 04d", months[month - 1], year);
713       else
714         sprintf (buf, "%s% 02d", months[month - 1], year % 100);
715       break;
716     case FMT_WKYR:
717       {
718         int yday = (v->f / 86400.) - calendar_to_julian (year, 1, 1) + 1;
719         
720         if (fp->w >= 10)
721           sprintf (buf, "%02d WK% 04d", (yday - 1) / 7 + 1, year);
722         else
723           sprintf (buf, "%02d WK% 02d", (yday - 1) / 7 + 1, year % 100);
724       }
725       break;
726     case FMT_DATETIME:
727       {
728         char *cp;
729
730         cp = spprintf (buf, "%02d-%s-%04d %02d:%02d",
731                        day, months[month - 1], year,
732                        (int) fmod (floor (v->f / 60. / 60.), 24.),
733                        (int) fmod (floor (v->f / 60.), 60.));
734         if (fp->w >= 20)
735           {
736             int w, d;
737
738             if (fp->w >= 22 && fp->d > 0)
739               {
740                 d = min (fp->d, fp->w - 21);
741                 w = 3 + d;
742               }
743             else
744               {
745                 w = 2;
746                 d = 0;
747               }
748
749             cp = spprintf (cp, ":%0*.*f", w, d, fmod (v->f, 60.));
750           }
751       }
752       break;
753     default:
754       assert (0);
755     }
756
757   if (buf[0] == 0)
758     return 0;
759   st_bare_pad_copy (dst, buf, fp->w);
760   return 1;
761 }
762
763 static int
764 convert_time (char *dst, const struct fmt_spec *fp, const union value *v)
765 {
766   char temp_buf[40];
767   char *cp;
768
769   double time;
770   int width;
771
772   if (fabs (v->f) > 1e20)
773     {
774       msg (ME, _("Time value %g too large in magnitude to convert to "
775            "alphanumeric time."), v->f);
776       return 0;
777     }
778
779   time = v->f;
780   width = fp->w;
781   cp = temp_buf;
782   if (time < 0)
783     *cp++ = '-', time = -time;
784   if (fp->type == FMT_DTIME)
785     {
786       double days = floor (time / 60. / 60. / 24.);
787       cp = spprintf (temp_buf, "%02.0f ", days);
788       time = time - days * 60. * 60. * 24.;
789       width -= 3;
790     }
791   else
792     cp = temp_buf;
793
794   cp = spprintf (cp, "%02.0f:%02.0f",
795                  fmod (floor (time / 60. / 60.), 24.),
796                  fmod (floor (time / 60.), 60.));
797
798   if (width >= 8)
799     {
800       int w, d;
801
802       if (width >= 10 && fp->d >= 0 && fp->d != 0)
803         d = min (fp->d, width - 9), w = 3 + d;
804       else
805         w = 2, d = 0;
806
807       cp = spprintf (cp, ":%0*.*f", w, d, fmod (time, 60.));
808     }
809   st_bare_pad_copy (dst, temp_buf, fp->w);
810
811   return 1;
812 }
813
814 static int
815 convert_WKDAY (char *dst, const struct fmt_spec *fp, const union value *v)
816 {
817   static const char *weekdays[7] =
818     {
819       "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
820       "THURSDAY", "FRIDAY", "SATURDAY",
821     };
822
823   int x = v->f;
824
825   if (x < 1 || x > 7)
826     {
827       msg (ME, _("Weekday index %d does not lie between 1 and 7."), x);
828       return 0;
829     }
830   st_bare_pad_copy (dst, weekdays[x - 1], fp->w);
831
832   return 1;
833 }
834
835 static int
836 convert_MONTH (char *dst, const struct fmt_spec *fp, const union value *v)
837 {
838   static const char *months[12] =
839     {
840       "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE",
841       "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER",
842     };
843
844   int x = v->f;
845
846   if (x < 1 || x > 12)
847     {
848       msg (ME, _("Month index %d does not lie between 1 and 12."), x);
849       return 0;
850     }
851   
852   st_bare_pad_copy (dst, months[x - 1], fp->w);
853
854   return 1;
855 }
856 \f
857 /* Helper functions. */
858
859 /* Copies SRC to DST, inserting commas and dollar signs as appropriate
860    for format spec *FP.  */
861 static void
862 insert_commas (char *dst, const char *src, const struct fmt_spec *fp)
863 {
864   /* Number of leading spaces in the number.  This is the amount of
865      room we have for inserting commas and dollar signs. */
866   int n_spaces;
867
868   /* Number of digits before the decimal point.  This is used to
869      determine the Number of commas to insert. */
870   int n_digits;
871
872   /* Number of commas to insert. */
873   int n_commas;
874
875   /* Number of items ,%$ to insert. */
876   int n_items;
877
878   /* Number of n_items items not to use for commas. */
879   int n_reserved;
880
881   /* Digit iterator. */
882   int i;
883
884   /* Source pointer. */
885   const char *sp;
886
887   /* Count spaces and digits. */
888   sp = src;
889   while (sp < src + fp->w && *sp == ' ')
890     sp++;
891   n_spaces = sp - src;
892   sp = src + n_spaces;
893   if (*sp == '-')
894     sp++;
895   n_digits = 0;
896   while (sp + n_digits < src + fp->w && isdigit ((unsigned char) sp[n_digits]))
897     n_digits++;
898   n_commas = (n_digits - 1) / 3;
899   n_items = n_commas + (fp->type == FMT_DOLLAR || fp->type == FMT_PCT);
900
901   /* Check whether we have enough space to do insertions. */
902   if (!n_spaces || !n_items)
903     {
904       memcpy (dst, src, fp->w);
905       return;
906     }
907   if (n_items > n_spaces)
908     {
909       n_items -= n_commas;
910       if (!n_items)
911         {
912           memcpy (dst, src, fp->w);
913           return;
914         }
915     }
916
917   /* Put spaces at the beginning if there's extra room. */
918   if (n_spaces > n_items)
919     {
920       memset (dst, ' ', n_spaces - n_items);
921       dst += n_spaces - n_items;
922     }
923
924   /* Insert $ and reserve space for %. */
925   n_reserved = 0;
926   if (fp->type == FMT_DOLLAR)
927     {
928       *dst++ = '$';
929       n_items--;
930     }
931   else if (fp->type == FMT_PCT)
932     n_reserved = 1;
933
934   /* Copy negative sign and digits, inserting commas. */
935   if (sp - src > n_spaces)
936     *dst++ = '-';
937   for (i = n_digits; i; i--)
938     {
939       if (i % 3 == 0 && n_digits > i && n_items > n_reserved)
940         {
941           n_items--;
942           *dst++ = fp->type == FMT_COMMA ? set_grouping : set_decimal;
943         }
944       *dst++ = *sp++;
945     }
946
947   /* Copy decimal places and insert % if necessary. */
948   memcpy (dst, sp, fp->w - (sp - src));
949   if (fp->type == FMT_PCT && n_items > 0)
950     dst[fp->w - (sp - src)] = '%';
951 }
952
953 /* Returns 1 if YEAR (i.e., 1987) can be represented in four digits, 0
954    otherwise. */
955 static int
956 year4 (int year)
957 {
958   if (year >= 1 && year <= 9999)
959     return 1;
960   msg (ME, _("Year %d cannot be represented in four digits for "
961              "output formatting purposes."), year);
962   return 0;
963 }
964
965 static int
966 try_CCx (char *dst, const struct fmt_spec *fp, double v)
967 {
968   struct set_cust_currency *cc = &set_cc[fp->type - FMT_CCA];
969
970   struct fmt_spec f;
971
972   char buf[64];
973   char buf2[64];
974   char *cp;
975
976   /* Determine length available, decimal character for number
977      proper. */
978   f.type = cc->decimal == set_decimal ? FMT_COMMA : FMT_DOT;
979   f.w = fp->w - strlen (cc->prefix) - strlen (cc->suffix);
980   if (v < 0)
981     f.w -= strlen (cc->neg_prefix) + strlen (cc->neg_suffix) - 1;
982   else
983     /* Convert -0 to +0. */
984     v = fabs (v);
985   f.d = fp->d;
986
987   if (f.w <= 0)
988     return 0;
989
990   /* There's room for all that currency crap.  Let's do the F
991      conversion first. */
992   if (!convert_F (buf, &f, (union value *) &v) || *buf == '*')
993     return 0;
994   insert_commas (buf2, buf, &f);
995
996   /* Postprocess back into buf. */
997   cp = buf;
998   if (v < 0)
999     cp = stpcpy (cp, cc->neg_prefix);
1000   cp = stpcpy (cp, cc->prefix);
1001   {
1002     char *bp = buf2;
1003     while (*bp == ' ')
1004       bp++;
1005
1006     assert ((v >= 0) ^ (*bp == '-'));
1007     if (v < 0)
1008       bp++;
1009
1010     memcpy (cp, bp, f.w - (bp - buf2));
1011     cp += f.w - (bp - buf2);
1012   }
1013   cp = stpcpy (cp, cc->suffix);
1014   if (v < 0)
1015     cp = stpcpy (cp, cc->neg_suffix);
1016
1017   /* Copy into dst. */
1018   assert (cp - buf <= fp->w);
1019   if (cp - buf < fp->w)
1020     {
1021       memcpy (&dst[fp->w - (cp - buf)], buf, cp - buf);
1022       memset (dst, ' ', fp->w - (cp - buf));
1023     }
1024   else
1025     memcpy (dst, buf, fp->w);
1026
1027   return 1;
1028 }
1029
1030 /* This routine relies on the underlying implementation of sprintf:
1031
1032    If the number has a magnitude 1e40 or greater, then we needn't
1033    bother with it, since it's guaranteed to need processing in
1034    scientific notation.
1035
1036    Otherwise, do a binary search for the base-10 magnitude of the
1037    thing.  log10() is not accurate enough, and the alternatives are
1038    frightful.  Besides, we never need as many as 6 (pairs of)
1039    comparisons.  The algorithm used for searching is Knuth's Algorithm
1040    6.2.1C (Uniform binary search).
1041
1042    DON'T CHANGE ANYTHING HERE UNLESS YOU'VE THOUGHT ABOUT IT FOR A
1043    LONG TIME!  The rest of the program is heavily dependent on
1044    specific properties of this routine's output.  LOG ALL CHANGES! */
1045 static int
1046 try_F (char *dst, const struct fmt_spec *fp, const union value *value)
1047 {
1048   /* This is the DELTA array from Knuth.
1049      DELTA[j] = floor((40+2**(j-1))/(2**j)). */
1050   static const int delta[8] =
1051   {
1052     0, (40 + 1) / 2, (40 + 2) / 4, (40 + 4) / 8, (40 + 8) / 16,
1053     (40 + 16) / 32, (40 + 32) / 64, (40 + 64) / 128,
1054   };
1055
1056   /* The number of digits in floor(v), including sign.  This is `i'
1057      from Knuth. */
1058   int n_int = (40 + 1) / 2;
1059
1060   /* Used to step through delta[].  This is `j' from Knuth. */
1061   int j = 2;
1062
1063   /* Value. */
1064   double v = value->f;
1065
1066   /* Magnitude of v.  This is `K' from Knuth. */
1067   double mag;
1068
1069   /* Number of characters for the fractional part, including the
1070      decimal point. */
1071   int n_dec;
1072
1073   /* Pointer into buf used for formatting. */
1074   char *cp;
1075
1076   /* Used to count characters formatted by nsprintf(). */
1077   int n;
1078
1079   /* Temporary buffer. */
1080   char buf[128];
1081
1082   /* First check for infinities and NaNs.  12/13/96. */
1083   if (!finite (v))
1084     {
1085       n = nsprintf (buf, "%f", v);
1086       if (n > fp->w)
1087         memset (buf, '*', fp->w);
1088       else if (n < fp->w)
1089         {
1090           memmove (&buf[fp->w - n], buf, n);
1091           memset (buf, ' ', fp->w - n);
1092         }
1093       memcpy (dst, buf, fp->w);
1094       return 1;
1095     }
1096
1097   /* Then check for radically out-of-range values. */
1098   mag = fabs (v);
1099   if (mag >= power10[fp->w])
1100     return 0;
1101
1102   if (mag < 1.0)
1103     {
1104       n_int = 0;
1105
1106       /* Avoid printing `-.000'. 7/6/96. */
1107       if (approx_eq (v, 0.0))
1108         v = 0.0;
1109     }
1110   else
1111     /* Now perform a `uniform binary search' based on the tables
1112        power10[] and delta[].  After this step, nint is the number of
1113        digits in floor(v), including any sign.  */
1114     for (;;)
1115       {
1116         if (mag >= power10[n_int])      /* Should this be approx_ge()? */
1117           {
1118             assert (delta[j]);
1119             n_int += delta[j++];
1120           }
1121         else if (mag < power10[n_int - 1])
1122           {
1123             assert (delta[j]);
1124             n_int -= delta[j++];
1125           }
1126         else
1127           break;
1128       }
1129
1130   /* If we have any decimal places, then there is a decimal point,
1131      too. */
1132   n_dec = fp->d;
1133   if (n_dec)
1134     n_dec++;
1135
1136   /* 1/10/96: If there aren't any digits at all, add one.  This occurs
1137      only when fabs(v) < 1.0. */
1138   if (n_int + n_dec == 0)
1139     n_int++;
1140
1141   /* Give space for a minus sign.  Moved 1/10/96. */
1142   if (v < 0)
1143     n_int++;
1144
1145   /* Normally we only go through the loop once; occasionally twice.
1146      Three times or more indicates a very serious bug somewhere. */
1147   for (;;)
1148     {
1149       /* Check out the total length of the string. */
1150       cp = buf;
1151       if (n_int + n_dec > fp->w)
1152         {
1153           /* The string is too long.  Let's see what can be done. */
1154           if (n_int <= fp->w)
1155             /* If we can, just reduce the number of decimal places. */
1156             n_dec = fp->w - n_int;
1157           else
1158             return 0;
1159         }
1160       else if (n_int + n_dec < fp->w)
1161         {
1162           /* The string is too short.  Left-pad with spaces. */
1163           int n_spaces = fp->w - n_int - n_dec;
1164           memset (cp, ' ', n_spaces);
1165           cp += n_spaces;
1166         }
1167
1168       /* Finally, format the number. */
1169       if (n_dec)
1170         n = nsprintf (cp, "%.*f", n_dec - 1, v);
1171       else
1172         n = nsprintf (cp, "%.0f", v);
1173
1174       /* If v is positive and its magnitude is less than 1...  */
1175       if (n_int == 0)
1176         {
1177           if (*cp == '0')
1178             {
1179               /* The value rounds to `.###'. */
1180               memmove (cp, &cp[1], n - 1);
1181               n--;
1182             }
1183           else
1184             {
1185               /* The value rounds to `1.###'. */
1186               n_int = 1;
1187               continue;
1188             }
1189         }
1190       /* Else if v is negative and its magnitude is less than 1...  */
1191       else if (v < 0 && n_int == 1)
1192         {
1193           if (cp[1] == '0')
1194             {
1195               /* The value rounds to `-.###'. */
1196               memmove (&cp[1], &cp[2], n - 2);
1197               n--;
1198             }
1199           else
1200             {
1201               /* The value rounds to `-1.###'. */
1202               n_int = 2;
1203               continue;
1204             }
1205         }
1206
1207       /* Check for a correct number of digits & decimal places & stuff.
1208          This is just a desperation check.  Hopefully it won't fail too
1209          often, because then we have to run through the whole loop again:
1210          sprintf() is not a fast operation with floating-points! */
1211       if (n == n_int + n_dec)
1212         {
1213           /* Convert periods `.' to commas `,' for our foreign friends. */
1214           if ((set_decimal == ',' && fp->type != FMT_DOT)
1215               || (set_decimal == '.' && fp->type == FMT_DOT))
1216             {
1217               cp = strchr (cp, '.');
1218               if (cp)
1219                 *cp = ',';
1220             }
1221
1222           memcpy (dst, buf, fp->w);
1223           return 1;
1224         }
1225
1226       n_int = n - n_dec; /* FIXME?  Need an idiot check on resulting n_int? */
1227     }
1228 }