14c0f94b39bd514dfe2e6cf24e66a5bdfcd1c882
[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, _("Quality of zoned decimal (Z) output format code is "
362                  "suspect.  Check your results, report bugs to author."));
363       warned = 1;
364     }
365
366   if (v->f == SYSMIS)
367     {
368       msg (ME, _("The system-missing value cannot be output as a zoned "
369                  "decimal number."));
370       return 0;
371     }
372   
373   {
374     char buf[41];
375     double d;
376     int i;
377     
378     d = fabs (floor (v->f));
379     if (d >= power10[fp->w])
380       {
381         msg (ME, _("Number %g too big to fit in field with format Z%d.%d."),
382              v->f, fp->w, fp->d);
383         return 0;
384       }
385
386     sprintf (buf, "%*.0f", fp->w, v->f);
387     for (i = 0; i < fp->w; i++)
388       dst[i] = (buf[i] - '0') | 0xf0;
389     if (v->f < 0)
390       dst[fp->w - 1] &= 0xdf;
391   }
392
393   return 1;
394 }
395
396 static int
397 convert_A (char *dst, const struct fmt_spec *fp, const union value *v)
398 {
399   memcpy (dst, v->c, fp->w);
400   return 1;
401 }
402
403 static int
404 convert_AHEX (char *dst, const struct fmt_spec *fp, const union value *v)
405 {
406   int i;
407
408   for (i = 0; i < fp->w / 2; i++)
409     {
410       ((unsigned char *) dst)[i * 2] = MAKE_HEXIT ((v->c[i]) >> 4);
411       ((unsigned char *) dst)[i * 2 + 1] = MAKE_HEXIT ((v->c[i]) & 0xf);
412     }
413
414   return 1;
415 }
416
417 static int
418 convert_IB (char *dst, const struct fmt_spec *fp, const union value *v)
419 {
420   /* Strategy: Basically the same as convert_PIBHEX() but with base
421      256. Then it's necessary to negate the two's-complement result if
422      v->f is negative. */
423
424   /* Used for constructing the two's-complement result. */
425   unsigned temp[8];
426
427   /* Fraction (mantissa). */
428   double frac;
429
430   /* Exponent. */
431   int exp;
432
433   /* Difference between exponent and (-8*fp->w-1). */
434   int diff;
435
436   /* Counter. */
437   int i;
438
439   /* Make the exponent (-8*fp->w-1). */
440   frac = frexp (fabs (v->f), &exp);
441   diff = exp - (-8 * fp->w - 1);
442   exp -= diff;
443   frac *= ldexp (1.0, diff);
444
445   /* Extract each base-256 digit. */
446   for (i = 0; i < fp->w; i++)
447     {
448       modf (frac, &frac);
449       frac *= 256.0;
450       temp[i] = floor (frac);
451     }
452
453   /* Perform two's-complement negation if v->f is negative. */
454   if (v->f < 0)
455     {
456       /* Perform NOT operation. */
457       for (i = 0; i < fp->w; i++)
458         temp[i] = ~temp[i];
459       /* Add 1 to the whole number. */
460       for (i = fp->w - 1; i >= 0; i--)
461         {
462           temp[i]++;
463           if (temp[i])
464             break;
465         }
466     }
467   memcpy (dst, temp, fp->w);
468 #ifndef WORDS_BIGENDIAN
469   mm_reverse (dst, fp->w);
470 #endif
471
472   return 1;
473 }
474
475 static int
476 convert_P (char *dst, const struct fmt_spec *fp, const union value *v)
477 {
478   /* Buffer for v->f*2-1 characters + a decimal point if library is
479      not quite compliant + a null. */
480   char buf[17];
481
482   /* Counter. */
483   int i;
484
485   /* Main extraction. */
486   sprintf (buf, "%0*.0f", fp->w * 2 - 1, floor (fabs (v->f)));
487
488   for (i = 0; i < fp->w; i++)
489     ((unsigned char *) dst)[i]
490       = ((buf[i * 2] - '0') << 4) + buf[i * 2 + 1] - '0';
491
492   /* Set sign. */
493   dst[fp->w - 1] &= 0xf0;
494   if (v->f >= 0.0)
495     dst[fp->w - 1] |= 0xf;
496   else
497     dst[fp->w - 1] |= 0xd;
498
499   return 1;
500 }
501
502 static int
503 convert_PIB (char *dst, const struct fmt_spec *fp, const union value *v)
504 {
505   /* Strategy: Basically the same as convert_IB(). */
506
507   /* Fraction (mantissa). */
508   double frac;
509
510   /* Exponent. */
511   int exp;
512
513   /* Difference between exponent and (-8*fp->w). */
514   int diff;
515
516   /* Counter. */
517   int i;
518
519   /* Make the exponent (-8*fp->w). */
520   frac = frexp (fabs (v->f), &exp);
521   diff = exp - (-8 * fp->w);
522   exp -= diff;
523   frac *= ldexp (1.0, diff);
524
525   /* Extract each base-256 digit. */
526   for (i = 0; i < fp->w; i++)
527     {
528       modf (frac, &frac);
529       frac *= 256.0;
530       ((unsigned char *) dst)[i] = floor (frac);
531     }
532 #ifndef WORDS_BIGENDIAN
533   mm_reverse (dst, fp->w);
534 #endif
535
536   return 1;
537 }
538
539 static int
540 convert_PIBHEX (char *dst, const struct fmt_spec *fp, const union value *v)
541 {
542   /* Strategy: Use frexp() to create a normalized result (but mostly
543      to find the base-2 exponent), then change the base-2 exponent to
544      (-4*fp->w) using multiplication and division by powers of two.
545      Extract each hexit by multiplying by 16. */
546
547   /* Fraction (mantissa). */
548   double frac;
549
550   /* Exponent. */
551   int exp;
552
553   /* Difference between exponent and (-4*fp->w). */
554   int diff;
555
556   /* Counter. */
557   int i;
558
559   /* Make the exponent (-4*fp->w). */
560   frac = frexp (fabs (v->f), &exp);
561   diff = exp - (-4 * fp->w);
562   exp -= diff;
563   frac *= ldexp (1.0, diff);
564
565   /* Extract each hexit. */
566   for (i = 0; i < fp->w; i++)
567     {
568       modf (frac, &frac);
569       frac *= 16.0;
570       *dst++ = MAKE_HEXIT ((int) floor (frac));
571     }
572
573   return 1;
574 }
575
576 static int
577 convert_PK (char *dst, const struct fmt_spec *fp, const union value *v)
578 {
579   /* Buffer for v->f*2 characters + a decimal point if library is not
580      quite compliant + a null. */
581   char buf[18];
582
583   /* Counter. */
584   int i;
585
586   /* Main extraction. */
587   sprintf (buf, "%0*.0f", fp->w * 2, floor (fabs (v->f)));
588
589   for (i = 0; i < fp->w; i++)
590     ((unsigned char *) dst)[i]
591       = ((buf[i * 2] - '0') << 4) + buf[i * 2 + 1] - '0';
592
593   return 1;
594 }
595
596 static int
597 convert_RB (char *dst, const struct fmt_spec *fp, const union value *v)
598 {
599   union
600     {
601       double d;
602       char c[8];
603     }
604   u;
605
606   u.d = v->f;
607   memcpy (dst, u.c, fp->w);
608
609   return 1;
610 }
611
612 static int
613 convert_RBHEX (char *dst, const struct fmt_spec *fp, const union value *v)
614 {
615   union
616   {
617     double d;
618     char c[8];
619   }
620   u;
621
622   int i;
623
624   u.d = v->f;
625   for (i = 0; i < fp->w / 2; i++)
626     {
627       *dst++ = MAKE_HEXIT (u.c[i] >> 4);
628       *dst++ = MAKE_HEXIT (u.c[i] & 15);
629     }
630
631   return 1;
632 }
633
634 static int
635 convert_CCx (char *dst, const struct fmt_spec *fp, const union value *v)
636 {
637   if (try_CCx (dst, fp, v->f))
638     return 1;
639   else
640     {
641       struct fmt_spec f;
642       
643       f.type = FMT_COMMA;
644       f.w = fp->w;
645       f.d = fp->d;
646   
647       return convert_F (dst, &f, v);
648     }
649 }
650
651 static int
652 convert_date (char *dst, const struct fmt_spec *fp, const union value *v)
653 {
654   static const char *months[12] =
655     {
656       "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
657       "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
658     };
659
660   char buf[64] = {0};
661   int month, day, year;
662
663   julian_to_calendar (v->f / 86400., &year, &month, &day);
664   switch (fp->type)
665     {
666     case FMT_DATE:
667       if (fp->w >= 11)
668         sprintf (buf, "%02d-%s-%04d", day, months[month - 1], year);
669       else
670         sprintf (buf, "%02d-%s-%02d", day, months[month - 1], year % 100);
671       break;
672     case FMT_EDATE:
673       if (fp->w >= 10)
674         sprintf (buf, "%02d.%02d.%04d", day, month, year);
675       else
676         sprintf (buf, "%02d.%02d.%02d", day, month, year % 100);
677       break;
678     case FMT_SDATE:
679       if (fp->w >= 10)
680         sprintf (buf, "%04d/%02d/%02d", year, month, day);
681       else
682         sprintf (buf, "%02d/%02d/%02d", year % 100, month, day);
683       break;
684     case FMT_ADATE:
685       if (fp->w >= 10)
686         sprintf (buf, "%02d/%02d/%04d", month, day, year);
687       else
688         sprintf (buf, "%02d/%02d/%02d", month, day, year % 100);
689       break;
690     case FMT_JDATE:
691       {
692         int yday = (v->f / 86400.) - calendar_to_julian (year, 1, 1) + 1;
693         
694         if (fp->w >= 7)
695           {
696             if (year4 (year))
697               sprintf (buf, "%04d%03d", year, yday);
698           }
699         else
700           sprintf (buf, "%02d%03d", year % 100, yday);
701         break;
702       }
703     case FMT_QYR:
704       if (fp->w >= 8)
705         sprintf (buf, "%d Q% 04d", (month - 1) / 3 + 1, year);
706       else
707         sprintf (buf, "%d Q% 02d", (month - 1) / 3 + 1, year % 100);
708       break;
709     case FMT_MOYR:
710       if (fp->w >= 8)
711         sprintf (buf, "%s% 04d", months[month - 1], year);
712       else
713         sprintf (buf, "%s% 02d", months[month - 1], year % 100);
714       break;
715     case FMT_WKYR:
716       {
717         int yday = (v->f / 86400.) - calendar_to_julian (year, 1, 1) + 1;
718         
719         if (fp->w >= 10)
720           sprintf (buf, "%02d WK% 04d", (yday - 1) / 7 + 1, year);
721         else
722           sprintf (buf, "%02d WK% 02d", (yday - 1) / 7 + 1, year % 100);
723       }
724       break;
725     case FMT_DATETIME:
726       {
727         char *cp;
728
729         cp = spprintf (buf, "%02d-%s-%04d %02d:%02d",
730                        day, months[month - 1], year,
731                        (int) fmod (floor (v->f / 60. / 60.), 24.),
732                        (int) fmod (floor (v->f / 60.), 60.));
733         if (fp->w >= 20)
734           {
735             int w, d;
736
737             if (fp->w >= 22 && fp->d > 0)
738               {
739                 d = min (fp->d, fp->w - 21);
740                 w = 3 + d;
741               }
742             else
743               {
744                 w = 2;
745                 d = 0;
746               }
747
748             cp = spprintf (cp, ":%0*.*f", w, d, fmod (v->f, 60.));
749           }
750       }
751       break;
752     default:
753       assert (0);
754     }
755
756   if (buf[0] == 0)
757     return 0;
758   st_bare_pad_copy (dst, buf, fp->w);
759   return 1;
760 }
761
762 static int
763 convert_time (char *dst, const struct fmt_spec *fp, const union value *v)
764 {
765   char temp_buf[40];
766   char *cp;
767
768   double time;
769   int width;
770
771   if (fabs (v->f) > 1e20)
772     {
773       msg (ME, _("Time value %g too large in magnitude to convert to "
774            "alphanumeric time."), v->f);
775       return 0;
776     }
777
778   time = v->f;
779   width = fp->w;
780   cp = temp_buf;
781   if (time < 0)
782     *cp++ = '-', time = -time;
783   if (fp->type == FMT_DTIME)
784     {
785       double days = floor (time / 60. / 60. / 24.);
786       cp = spprintf (temp_buf, "%02.0f ", days);
787       time = time - days * 60. * 60. * 24.;
788       width -= 3;
789     }
790   else
791     cp = temp_buf;
792
793   cp = spprintf (cp, "%02.0f:%02.0f",
794                  fmod (floor (time / 60. / 60.), 24.),
795                  fmod (floor (time / 60.), 60.));
796
797   if (width >= 8)
798     {
799       int w, d;
800
801       if (width >= 10 && fp->d >= 0 && fp->d != 0)
802         d = min (fp->d, width - 9), w = 3 + d;
803       else
804         w = 2, d = 0;
805
806       cp = spprintf (cp, ":%0*.*f", w, d, fmod (time, 60.));
807     }
808   st_bare_pad_copy (dst, temp_buf, fp->w);
809
810   return 1;
811 }
812
813 static int
814 convert_WKDAY (char *dst, const struct fmt_spec *fp, const union value *v)
815 {
816   static const char *weekdays[7] =
817     {
818       "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
819       "THURSDAY", "FRIDAY", "SATURDAY",
820     };
821
822   int x = v->f;
823
824   if (x < 1 || x > 7)
825     {
826       msg (ME, _("Weekday index %d does not lie between 1 and 7."), x);
827       return 0;
828     }
829   st_bare_pad_copy (dst, weekdays[x - 1], fp->w);
830
831   return 1;
832 }
833
834 static int
835 convert_MONTH (char *dst, const struct fmt_spec *fp, const union value *v)
836 {
837   static const char *months[12] =
838     {
839       "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE",
840       "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER",
841     };
842
843   int x = v->f;
844
845   if (x < 1 || x > 12)
846     {
847       msg (ME, _("Month index %d does not lie between 1 and 12."), x);
848       return 0;
849     }
850   
851   st_bare_pad_copy (dst, months[x - 1], fp->w);
852
853   return 1;
854 }
855 \f
856 /* Helper functions. */
857
858 /* Copies SRC to DST, inserting commas and dollar signs as appropriate
859    for format spec *FP.  */
860 static void
861 insert_commas (char *dst, const char *src, const struct fmt_spec *fp)
862 {
863   /* Number of leading spaces in the number.  This is the amount of
864      room we have for inserting commas and dollar signs. */
865   int n_spaces;
866
867   /* Number of digits before the decimal point.  This is used to
868      determine the Number of commas to insert. */
869   int n_digits;
870
871   /* Number of commas to insert. */
872   int n_commas;
873
874   /* Number of items ,%$ to insert. */
875   int n_items;
876
877   /* Number of n_items items not to use for commas. */
878   int n_reserved;
879
880   /* Digit iterator. */
881   int i;
882
883   /* Source pointer. */
884   const char *sp;
885
886   /* Count spaces and digits. */
887   sp = src;
888   while (sp < src + fp->w && *sp == ' ')
889     sp++;
890   n_spaces = sp - src;
891   sp = src + n_spaces;
892   if (*sp == '-')
893     sp++;
894   n_digits = 0;
895   while (sp + n_digits < src + fp->w && isdigit ((unsigned char) sp[n_digits]))
896     n_digits++;
897   n_commas = (n_digits - 1) / 3;
898   n_items = n_commas + (fp->type == FMT_DOLLAR || fp->type == FMT_PCT);
899
900   /* Check whether we have enough space to do insertions. */
901   if (!n_spaces || !n_items)
902     {
903       memcpy (dst, src, fp->w);
904       return;
905     }
906   if (n_items > n_spaces)
907     {
908       n_items -= n_commas;
909       if (!n_items)
910         {
911           memcpy (dst, src, fp->w);
912           return;
913         }
914     }
915
916   /* Put spaces at the beginning if there's extra room. */
917   if (n_spaces > n_items)
918     {
919       memset (dst, ' ', n_spaces - n_items);
920       dst += n_spaces - n_items;
921     }
922
923   /* Insert $ and reserve space for %. */
924   n_reserved = 0;
925   if (fp->type == FMT_DOLLAR)
926     {
927       *dst++ = '$';
928       n_items--;
929     }
930   else if (fp->type == FMT_PCT)
931     n_reserved = 1;
932
933   /* Copy negative sign and digits, inserting commas. */
934   if (sp - src > n_spaces)
935     *dst++ = '-';
936   for (i = n_digits; i; i--)
937     {
938       if (i % 3 == 0 && n_digits > i && n_items > n_reserved)
939         {
940           n_items--;
941           *dst++ = fp->type == FMT_COMMA ? set_grouping : set_decimal;
942         }
943       *dst++ = *sp++;
944     }
945
946   /* Copy decimal places and insert % if necessary. */
947   memcpy (dst, sp, fp->w - (sp - src));
948   if (fp->type == FMT_PCT && n_items > 0)
949     dst[fp->w - (sp - src)] = '%';
950 }
951
952 /* Returns 1 if YEAR (i.e., 1987) can be represented in four digits, 0
953    otherwise. */
954 static int
955 year4 (int year)
956 {
957   if (year >= 1 && year <= 9999)
958     return 1;
959   msg (ME, _("Year %d cannot be represented in four digits for "
960              "output formatting purposes."), year);
961   return 0;
962 }
963
964 static int
965 try_CCx (char *dst, const struct fmt_spec *fp, double v)
966 {
967   struct set_cust_currency *cc = &set_cc[fp->type - FMT_CCA];
968
969   struct fmt_spec f;
970
971   char buf[64];
972   char buf2[64];
973   char *cp;
974
975   /* Determine length available, decimal character for number
976      proper. */
977   f.type = cc->decimal == set_decimal ? FMT_COMMA : FMT_DOT;
978   f.w = fp->w - strlen (cc->prefix) - strlen (cc->suffix);
979   if (v < 0)
980     f.w -= strlen (cc->neg_prefix) + strlen (cc->neg_suffix) - 1;
981   else
982     /* Convert -0 to +0. */
983     v = fabs (v);
984   f.d = fp->d;
985
986   if (f.w <= 0)
987     return 0;
988
989   /* There's room for all that currency crap.  Let's do the F
990      conversion first. */
991   if (!convert_F (buf, &f, (union value *) &v) || *buf == '*')
992     return 0;
993   insert_commas (buf2, buf, &f);
994
995   /* Postprocess back into buf. */
996   cp = buf;
997   if (v < 0)
998     cp = stpcpy (cp, cc->neg_prefix);
999   cp = stpcpy (cp, cc->prefix);
1000   {
1001     char *bp = buf2;
1002     while (*bp == ' ')
1003       bp++;
1004
1005     assert ((v >= 0) ^ (*bp == '-'));
1006     if (v < 0)
1007       bp++;
1008
1009     memcpy (cp, bp, f.w - (bp - buf2));
1010     cp += f.w - (bp - buf2);
1011   }
1012   cp = stpcpy (cp, cc->suffix);
1013   if (v < 0)
1014     cp = stpcpy (cp, cc->neg_suffix);
1015
1016   /* Copy into dst. */
1017   assert (cp - buf <= fp->w);
1018   if (cp - buf < fp->w)
1019     {
1020       memcpy (&dst[fp->w - (cp - buf)], buf, cp - buf);
1021       memset (dst, ' ', fp->w - (cp - buf));
1022     }
1023   else
1024     memcpy (dst, buf, fp->w);
1025
1026   return 1;
1027 }
1028
1029 /* This routine relies on the underlying implementation of sprintf:
1030
1031    If the number has a magnitude 1e40 or greater, then we needn't
1032    bother with it, since it's guaranteed to need processing in
1033    scientific notation.
1034
1035    Otherwise, do a binary search for the base-10 magnitude of the
1036    thing.  log10() is not accurate enough, and the alternatives are
1037    frightful.  Besides, we never need as many as 6 (pairs of)
1038    comparisons.  The algorithm used for searching is Knuth's Algorithm
1039    6.2.1C (Uniform binary search).
1040
1041    DON'T CHANGE ANYTHING HERE UNLESS YOU'VE THOUGHT ABOUT IT FOR A
1042    LONG TIME!  The rest of the program is heavily dependent on
1043    specific properties of this routine's output.  LOG ALL CHANGES! */
1044 static int
1045 try_F (char *dst, const struct fmt_spec *fp, const union value *value)
1046 {
1047   /* This is the DELTA array from Knuth.
1048      DELTA[j] = floor((40+2**(j-1))/(2**j)). */
1049   static const int delta[8] =
1050   {
1051     0, (40 + 1) / 2, (40 + 2) / 4, (40 + 4) / 8, (40 + 8) / 16,
1052     (40 + 16) / 32, (40 + 32) / 64, (40 + 64) / 128,
1053   };
1054
1055   /* The number of digits in floor(v), including sign.  This is `i'
1056      from Knuth. */
1057   int n_int = (40 + 1) / 2;
1058
1059   /* Used to step through delta[].  This is `j' from Knuth. */
1060   int j = 2;
1061
1062   /* Value. */
1063   double v = value->f;
1064
1065   /* Magnitude of v.  This is `K' from Knuth. */
1066   double mag;
1067
1068   /* Number of characters for the fractional part, including the
1069      decimal point. */
1070   int n_dec;
1071
1072   /* Pointer into buf used for formatting. */
1073   char *cp;
1074
1075   /* Used to count characters formatted by nsprintf(). */
1076   int n;
1077
1078   /* Temporary buffer. */
1079   char buf[128];
1080
1081   /* First check for infinities and NaNs.  12/13/96. */
1082   if (!finite (v))
1083     {
1084       n = nsprintf (buf, "%f", v);
1085       if (n > fp->w)
1086         memset (buf, '*', fp->w);
1087       else if (n < fp->w)
1088         {
1089           memmove (&buf[fp->w - n], buf, n);
1090           memset (buf, ' ', fp->w - n);
1091         }
1092       memcpy (dst, buf, fp->w);
1093       return 1;
1094     }
1095
1096   /* Then check for radically out-of-range values. */
1097   mag = fabs (v);
1098   if (mag >= power10[fp->w])
1099     return 0;
1100
1101   if (mag < 1.0)
1102     {
1103       n_int = 0;
1104
1105       /* Avoid printing `-.000'. 7/6/96. */
1106       if (approx_eq (v, 0.0))
1107         v = 0.0;
1108     }
1109   else
1110     /* Now perform a `uniform binary search' based on the tables
1111        power10[] and delta[].  After this step, nint is the number of
1112        digits in floor(v), including any sign.  */
1113     for (;;)
1114       {
1115         if (mag >= power10[n_int])      /* Should this be approx_ge()? */
1116           {
1117             assert (delta[j]);
1118             n_int += delta[j++];
1119           }
1120         else if (mag < power10[n_int - 1])
1121           {
1122             assert (delta[j]);
1123             n_int -= delta[j++];
1124           }
1125         else
1126           break;
1127       }
1128
1129   /* If we have any decimal places, then there is a decimal point,
1130      too. */
1131   n_dec = fp->d;
1132   if (n_dec)
1133     n_dec++;
1134
1135   /* 1/10/96: If there aren't any digits at all, add one.  This occurs
1136      only when fabs(v) < 1.0. */
1137   if (n_int + n_dec == 0)
1138     n_int++;
1139
1140   /* Give space for a minus sign.  Moved 1/10/96. */
1141   if (v < 0)
1142     n_int++;
1143
1144   /* Normally we only go through the loop once; occasionally twice.
1145      Three times or more indicates a very serious bug somewhere. */
1146   for (;;)
1147     {
1148       /* Check out the total length of the string. */
1149       cp = buf;
1150       if (n_int + n_dec > fp->w)
1151         {
1152           /* The string is too long.  Let's see what can be done. */
1153           if (n_int <= fp->w)
1154             /* If we can, just reduce the number of decimal places. */
1155             n_dec = fp->w - n_int;
1156           else
1157             return 0;
1158         }
1159       else if (n_int + n_dec < fp->w)
1160         {
1161           /* The string is too short.  Left-pad with spaces. */
1162           int n_spaces = fp->w - n_int - n_dec;
1163           memset (cp, ' ', n_spaces);
1164           cp += n_spaces;
1165         }
1166
1167       /* Finally, format the number. */
1168       if (n_dec)
1169         n = nsprintf (cp, "%.*f", n_dec - 1, v);
1170       else
1171         n = nsprintf (cp, "%.0f", v);
1172
1173       /* If v is positive and its magnitude is less than 1...  */
1174       if (n_int == 0)
1175         {
1176           if (*cp == '0')
1177             {
1178               /* The value rounds to `.###'. */
1179               memmove (cp, &cp[1], n - 1);
1180               n--;
1181             }
1182           else
1183             {
1184               /* The value rounds to `1.###'. */
1185               n_int = 1;
1186               continue;
1187             }
1188         }
1189       /* Else if v is negative and its magnitude is less than 1...  */
1190       else if (v < 0 && n_int == 1)
1191         {
1192           if (cp[1] == '0')
1193             {
1194               /* The value rounds to `-.###'. */
1195               memmove (&cp[1], &cp[2], n - 2);
1196               n--;
1197             }
1198           else
1199             {
1200               /* The value rounds to `-1.###'. */
1201               n_int = 2;
1202               continue;
1203             }
1204         }
1205
1206       /* Check for a correct number of digits & decimal places & stuff.
1207          This is just a desperation check.  Hopefully it won't fail too
1208          often, because then we have to run through the whole loop again:
1209          sprintf() is not a fast operation with floating-points! */
1210       if (n == n_int + n_dec)
1211         {
1212           /* Convert periods `.' to commas `,' for our foreign friends. */
1213           if ((set_decimal == ',' && fp->type != FMT_DOT)
1214               || (set_decimal == '.' && fp->type == FMT_DOT))
1215             {
1216               cp = strchr (cp, '.');
1217               if (cp)
1218                 *cp = ',';
1219             }
1220
1221           memcpy (dst, buf, fp->w);
1222           return 1;
1223         }
1224
1225       n_int = n - n_dec; /* FIXME?  Need an idiot check on resulting n_int? */
1226     }
1227 }