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