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