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