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