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