Fix assertion for proper Huffman merge pattern: 0 == 1 modulo 1.
[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 "error.h"
28 #include "format.h"
29 #include "julcal/julcal.h"
30 #include "magic.h"
31 #include "misc.h"
32 #include "misc.h"
33 #include "settings.h"
34 #include "str.h"
35 #include "var.h"
36
37 #include "debug-print.h"
38
39 /* In older versions, numbers got their trailing zeros stripped.
40    Newer versions leave them on when there's room.  Comment this next
41    line out for retro styling. */
42 #define NEW_STYLE 1
43 \f
44 /* Public functions. */
45
46 typedef int numeric_converter (char *, const struct fmt_spec *, double);
47 static numeric_converter convert_F, convert_N, convert_E, convert_F_plus;
48 static numeric_converter convert_Z, convert_IB, convert_P, convert_PIB;
49 static numeric_converter convert_PIBHEX, convert_PK, convert_RB;
50 static numeric_converter convert_RBHEX, convert_CCx, convert_date;
51 static numeric_converter convert_time, convert_WKDAY, convert_MONTH, try_F;
52
53 typedef int string_converter (char *, const struct fmt_spec *, const char *);
54 static string_converter convert_A, convert_AHEX;
55
56 /* Converts binary value V into printable form in the exactly
57    FP->W character in buffer S according to format specification
58    FP.  No null terminator is appended to the buffer.  */
59 void
60 data_out (char *s, const struct fmt_spec *fp, const union value *v)
61 {
62   int cat = formats[fp->type].cat;
63   int ok;
64
65   if (!(cat & FCAT_STRING)) 
66     {
67       /* Numeric formatting. */
68       double number = v->f;
69
70       /* Handle SYSMIS turning into blanks. */
71       if ((cat & FCAT_BLANKS_SYSMIS) && number == SYSMIS)
72         {
73           memset (s, ' ', fp->w);
74           s[fp->w - fp->d - 1] = '.';
75           return;
76         }
77
78       /* Handle decimal shift. */
79       if ((cat & FCAT_SHIFT_DECIMAL) && number != SYSMIS && fp->d)
80         number *= pow (10.0, fp->d);
81
82       switch (fp->type) 
83         {
84         case FMT_F:
85           ok = convert_F (s, fp, number);
86           break;
87
88         case FMT_N:
89           ok = convert_N (s, fp, number);
90           break;
91
92         case FMT_E:
93           ok = convert_E (s, fp, number);
94           break;
95
96         case FMT_COMMA: case FMT_DOT: case FMT_DOLLAR: case FMT_PCT:
97           ok = convert_F_plus (s, fp, number);
98           break;
99
100         case FMT_Z:
101           ok = convert_Z (s, fp, number);
102           break;
103
104         case FMT_A:
105           assert (0);
106           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 month, day, year;
756
757   julian_to_calendar (number / 86400., &year, &month, &day);
758   switch (fp->type)
759     {
760     case FMT_DATE:
761       if (fp->w >= 11)
762         sprintf (buf, "%02d-%s-%04d", day, months[month - 1], year);
763       else
764         sprintf (buf, "%02d-%s-%02d", day, months[month - 1], year % 100);
765       break;
766     case FMT_EDATE:
767       if (fp->w >= 10)
768         sprintf (buf, "%02d.%02d.%04d", day, month, year);
769       else
770         sprintf (buf, "%02d.%02d.%02d", day, month, year % 100);
771       break;
772     case FMT_SDATE:
773       if (fp->w >= 10)
774         sprintf (buf, "%04d/%02d/%02d", year, month, day);
775       else
776         sprintf (buf, "%02d/%02d/%02d", year % 100, month, day);
777       break;
778     case FMT_ADATE:
779       if (fp->w >= 10)
780         sprintf (buf, "%02d/%02d/%04d", month, day, year);
781       else
782         sprintf (buf, "%02d/%02d/%02d", month, day, year % 100);
783       break;
784     case FMT_JDATE:
785       {
786         int yday = (number / 86400.) - calendar_to_julian (year, 1, 1) + 1;
787         
788         if (fp->w >= 7)
789           {
790             if (year4 (year))
791               sprintf (buf, "%04d%03d", year, yday);
792           }
793         else
794           sprintf (buf, "%02d%03d", year % 100, yday);
795         break;
796       }
797     case FMT_QYR:
798       if (fp->w >= 8)
799         sprintf (buf, "%d Q% 04d", (month - 1) / 3 + 1, year);
800       else
801         sprintf (buf, "%d Q% 02d", (month - 1) / 3 + 1, year % 100);
802       break;
803     case FMT_MOYR:
804       if (fp->w >= 8)
805         sprintf (buf, "%s% 04d", months[month - 1], year);
806       else
807         sprintf (buf, "%s% 02d", months[month - 1], year % 100);
808       break;
809     case FMT_WKYR:
810       {
811         int yday = (number / 86400.) - calendar_to_julian (year, 1, 1) + 1;
812         
813         if (fp->w >= 10)
814           sprintf (buf, "%02d WK% 04d", (yday - 1) / 7 + 1, year);
815         else
816           sprintf (buf, "%02d WK% 02d", (yday - 1) / 7 + 1, year % 100);
817       }
818       break;
819     case FMT_DATETIME:
820       {
821         char *cp;
822
823         cp = spprintf (buf, "%02d-%s-%04d %02d:%02d",
824                        day, months[month - 1], year,
825                        (int) fmod (floor (number / 60. / 60.), 24.),
826                        (int) fmod (floor (number / 60.), 60.));
827         if (fp->w >= 20)
828           {
829             int w, d;
830
831             if (fp->w >= 22 && fp->d > 0)
832               {
833                 d = min (fp->d, fp->w - 21);
834                 w = 3 + d;
835               }
836             else
837               {
838                 w = 2;
839                 d = 0;
840               }
841
842             cp = spprintf (cp, ":%0*.*f", w, d, fmod (number, 60.));
843           }
844       }
845       break;
846     default:
847       assert (0);
848     }
849
850   if (buf[0] == 0)
851     return 0;
852   st_bare_pad_copy (dst, buf, fp->w);
853   return 1;
854 }
855
856 static int
857 convert_time (char *dst, const struct fmt_spec *fp, double number)
858 {
859   char temp_buf[40];
860   char *cp;
861
862   double time;
863   int width;
864
865   if (fabs (number) > 1e20)
866     {
867       msg (ME, _("Time value %g too large in magnitude to convert to "
868            "alphanumeric time."), number);
869       return 0;
870     }
871
872   time = number;
873   width = fp->w;
874   cp = temp_buf;
875   if (time < 0)
876     *cp++ = '-', time = -time;
877   if (fp->type == FMT_DTIME)
878     {
879       double days = floor (time / 60. / 60. / 24.);
880       cp = spprintf (temp_buf, "%02.0f ", days);
881       time = time - days * 60. * 60. * 24.;
882       width -= 3;
883     }
884   else
885     cp = temp_buf;
886
887   cp = spprintf (cp, "%02.0f:%02.0f",
888                  fmod (floor (time / 60. / 60.), 24.),
889                  fmod (floor (time / 60.), 60.));
890
891   if (width >= 8)
892     {
893       int w, d;
894
895       if (width >= 10 && fp->d >= 0 && fp->d != 0)
896         d = min (fp->d, width - 9), w = 3 + d;
897       else
898         w = 2, d = 0;
899
900       cp = spprintf (cp, ":%0*.*f", w, d, fmod (time, 60.));
901     }
902   st_bare_pad_copy (dst, temp_buf, fp->w);
903
904   return 1;
905 }
906
907 static int
908 convert_WKDAY (char *dst, const struct fmt_spec *fp, double wkday)
909 {
910   static const char *weekdays[7] =
911     {
912       "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
913       "THURSDAY", "FRIDAY", "SATURDAY",
914     };
915
916   if (wkday < 1 || wkday > 7)
917     {
918       msg (ME, _("Weekday index %f does not lie between 1 and 7."),
919            (double) wkday);
920       return 0;
921     }
922   st_bare_pad_copy (dst, weekdays[(int) wkday - 1], fp->w);
923
924   return 1;
925 }
926
927 static int
928 convert_MONTH (char *dst, const struct fmt_spec *fp, double month)
929 {
930   static const char *months[12] =
931     {
932       "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE",
933       "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER",
934     };
935
936   if (month < 1 || month > 12)
937     {
938       msg (ME, _("Month index %f does not lie between 1 and 12."),
939            month);
940       return 0;
941     }
942   
943   st_bare_pad_copy (dst, months[(int) month - 1], fp->w);
944
945   return 1;
946 }
947 \f
948 /* Helper functions. */
949
950 /* Copies SRC to DST, inserting commas and dollar signs as appropriate
951    for format spec *FP.  */
952 static void
953 insert_commas (char *dst, const char *src, const struct fmt_spec *fp)
954 {
955   /* Number of leading spaces in the number.  This is the amount of
956      room we have for inserting commas and dollar signs. */
957   int n_spaces;
958
959   /* Number of digits before the decimal point.  This is used to
960      determine the Number of commas to insert. */
961   int n_digits;
962
963   /* Number of commas to insert. */
964   int n_commas;
965
966   /* Number of items ,%$ to insert. */
967   int n_items;
968
969   /* Number of n_items items not to use for commas. */
970   int n_reserved;
971
972   /* Digit iterator. */
973   int i;
974
975   /* Source pointer. */
976   const char *sp;
977
978   /* Count spaces and digits. */
979   sp = src;
980   while (sp < src + fp->w && *sp == ' ')
981     sp++;
982   n_spaces = sp - src;
983   sp = src + n_spaces;
984   if (*sp == '-')
985     sp++;
986   n_digits = 0;
987   while (sp + n_digits < src + fp->w && isdigit ((unsigned char) sp[n_digits]))
988     n_digits++;
989   n_commas = (n_digits - 1) / 3;
990   n_items = n_commas + (fp->type == FMT_DOLLAR || fp->type == FMT_PCT);
991
992   /* Check whether we have enough space to do insertions. */
993   if (!n_spaces || !n_items)
994     {
995       memcpy (dst, src, fp->w);
996       return;
997     }
998   if (n_items > n_spaces)
999     {
1000       n_items -= n_commas;
1001       if (!n_items)
1002         {
1003           memcpy (dst, src, fp->w);
1004           return;
1005         }
1006     }
1007
1008   /* Put spaces at the beginning if there's extra room. */
1009   if (n_spaces > n_items)
1010     {
1011       memset (dst, ' ', n_spaces - n_items);
1012       dst += n_spaces - n_items;
1013     }
1014
1015   /* Insert $ and reserve space for %. */
1016   n_reserved = 0;
1017   if (fp->type == FMT_DOLLAR)
1018     {
1019       *dst++ = '$';
1020       n_items--;
1021     }
1022   else if (fp->type == FMT_PCT)
1023     n_reserved = 1;
1024
1025   /* Copy negative sign and digits, inserting commas. */
1026   if (sp - src > n_spaces)
1027     *dst++ = '-';
1028   for (i = n_digits; i; i--)
1029     {
1030       if (i % 3 == 0 && n_digits > i && n_items > n_reserved)
1031         {
1032           n_items--;
1033           *dst++ = fp->type == FMT_COMMA ? get_grouping() : get_decimal();
1034         }
1035       *dst++ = *sp++;
1036     }
1037
1038   /* Copy decimal places and insert % if necessary. */
1039   memcpy (dst, sp, fp->w - (sp - src));
1040   if (fp->type == FMT_PCT && n_items > 0)
1041     dst[fp->w - (sp - src)] = '%';
1042 }
1043
1044 /* Returns 1 if YEAR (i.e., 1987) can be represented in four digits, 0
1045    otherwise. */
1046 static int
1047 year4 (int year)
1048 {
1049   if (year >= 1 && year <= 9999)
1050     return 1;
1051   msg (ME, _("Year %d cannot be represented in four digits for "
1052              "output formatting purposes."), year);
1053   return 0;
1054 }
1055
1056 static int
1057 try_CCx (char *dst, const struct fmt_spec *fp, double number)
1058 {
1059   const struct set_cust_currency *cc = get_cc(fp->type - FMT_CCA);
1060
1061   struct fmt_spec f;
1062
1063   char buf[64];
1064   char buf2[64];
1065   char *cp;
1066
1067   /* Determine length available, decimal character for number
1068      proper. */
1069   f.type = cc->decimal == get_decimal() ? FMT_COMMA : FMT_DOT;
1070   f.w = fp->w - strlen (cc->prefix) - strlen (cc->suffix);
1071   if (number < 0)
1072     f.w -= strlen (cc->neg_prefix) + strlen (cc->neg_suffix) - 1;
1073   else
1074     /* Convert -0 to +0. */
1075     number = fabs (number);
1076   f.d = fp->d;
1077
1078   if (f.w <= 0)
1079     return 0;
1080
1081   /* There's room for all that currency crap.  Let's do the F
1082      conversion first. */
1083   if (!convert_F (buf, &f, number) || *buf == '*')
1084     return 0;
1085   insert_commas (buf2, buf, &f);
1086
1087   /* Postprocess back into buf. */
1088   cp = buf;
1089   if (number < 0)
1090     cp = stpcpy (cp, cc->neg_prefix);
1091   cp = stpcpy (cp, cc->prefix);
1092   {
1093     char *bp = buf2;
1094     while (*bp == ' ')
1095       bp++;
1096
1097     assert ((number >= 0) ^ (*bp == '-'));
1098     if (number < 0)
1099       bp++;
1100
1101     memcpy (cp, bp, f.w - (bp - buf2));
1102     cp += f.w - (bp - buf2);
1103   }
1104   cp = stpcpy (cp, cc->suffix);
1105   if (number < 0)
1106     cp = stpcpy (cp, cc->neg_suffix);
1107
1108   /* Copy into dst. */
1109   assert (cp - buf <= fp->w);
1110   if (cp - buf < fp->w)
1111     {
1112       memcpy (&dst[fp->w - (cp - buf)], buf, cp - buf);
1113       memset (dst, ' ', fp->w - (cp - buf));
1114     }
1115   else
1116     memcpy (dst, buf, fp->w);
1117
1118   return 1;
1119 }
1120
1121 /* This routine relies on the underlying implementation of sprintf:
1122
1123    If the number has a magnitude 1e40 or greater, then we needn't
1124    bother with it, since it's guaranteed to need processing in
1125    scientific notation.
1126
1127    Otherwise, do a binary search for the base-10 magnitude of the
1128    thing.  log10() is not accurate enough, and the alternatives are
1129    frightful.  Besides, we never need as many as 6 (pairs of)
1130    comparisons.  The algorithm used for searching is Knuth's Algorithm
1131    6.2.1C (Uniform binary search).
1132
1133    DON'T CHANGE ANYTHING HERE UNLESS YOU'VE THOUGHT ABOUT IT FOR A
1134    LONG TIME!  The rest of the program is heavily dependent on
1135    specific properties of this routine's output.  LOG ALL CHANGES! */
1136 static int
1137 try_F (char *dst, const struct fmt_spec *fp, double number)
1138 {
1139   /* This is the DELTA array from Knuth.
1140      DELTA[j] = floor((40+2**(j-1))/(2**j)). */
1141   static const int delta[8] =
1142   {
1143     0, (40 + 1) / 2, (40 + 2) / 4, (40 + 4) / 8, (40 + 8) / 16,
1144     (40 + 16) / 32, (40 + 32) / 64, (40 + 64) / 128,
1145   };
1146
1147   /* The number of digits in floor(number), including sign.  This
1148      is `i' from Knuth. */
1149   int n_int = (40 + 1) / 2;
1150
1151   /* Used to step through delta[].  This is `j' from Knuth. */
1152   int j = 2;
1153
1154   /* Magnitude of number.  This is `K' from Knuth. */
1155   double mag;
1156
1157   /* Number of characters for the fractional part, including the
1158      decimal point. */
1159   int n_dec;
1160
1161   /* Pointer into buf used for formatting. */
1162   char *cp;
1163
1164   /* Used to count characters formatted by nsprintf(). */
1165   int n;
1166
1167   /* Temporary buffer. */
1168   char buf[128];
1169
1170   /* First check for infinities and NaNs.  12/13/96. */
1171   if (!finite (number))
1172     {
1173       n = nsprintf (buf, "%f", number);
1174       if (n > fp->w)
1175         memset (buf, '*', fp->w);
1176       else if (n < fp->w)
1177         {
1178           memmove (&buf[fp->w - n], buf, n);
1179           memset (buf, ' ', fp->w - n);
1180         }
1181       memcpy (dst, buf, fp->w);
1182       return 1;
1183     }
1184
1185   /* Then check for radically out-of-range values. */
1186   mag = fabs (number);
1187   if (mag >= power10[fp->w])
1188     return 0;
1189
1190   if (mag < 1.0)
1191     {
1192       n_int = 0;
1193
1194       /* Avoid printing `-.000'. 7/6/96. */
1195       if (mag < EPSILON)
1196         number = 0.0;
1197     }
1198   else
1199     /* Now perform a `uniform binary search' based on the tables
1200        power10[] and delta[].  After this step, nint is the number of
1201        digits in floor(number), including any sign.  */
1202     for (;;)
1203       {
1204         if (mag >= power10[n_int])
1205           {
1206             assert (delta[j]);
1207             n_int += delta[j++];
1208           }
1209         else if (mag < power10[n_int - 1])
1210           {
1211             assert (delta[j]);
1212             n_int -= delta[j++];
1213           }
1214         else
1215           break;
1216       }
1217
1218   /* If we have any decimal places, then there is a decimal point,
1219      too. */
1220   n_dec = fp->d;
1221   if (n_dec)
1222     n_dec++;
1223
1224   /* 1/10/96: If there aren't any digits at all, add one.  This occurs
1225      only when fabs(number) < 1.0. */
1226   if (n_int + n_dec == 0)
1227     n_int++;
1228
1229   /* Give space for a minus sign.  Moved 1/10/96. */
1230   if (number < 0)
1231     n_int++;
1232
1233   /* Normally we only go through the loop once; occasionally twice.
1234      Three times or more indicates a very serious bug somewhere. */
1235   for (;;)
1236     {
1237       /* Check out the total length of the string. */
1238       cp = buf;
1239       if (n_int + n_dec > fp->w)
1240         {
1241           /* The string is too long.  Let's see what can be done. */
1242           if (n_int <= fp->w)
1243             /* If we can, just reduce the number of decimal places. */
1244             n_dec = fp->w - n_int;
1245           else
1246             return 0;
1247         }
1248       else if (n_int + n_dec < fp->w)
1249         {
1250           /* The string is too short.  Left-pad with spaces. */
1251           int n_spaces = fp->w - n_int - n_dec;
1252           memset (cp, ' ', n_spaces);
1253           cp += n_spaces;
1254         }
1255
1256       /* Finally, format the number. */
1257       if (n_dec)
1258         n = nsprintf (cp, "%.*f", n_dec - 1, number);
1259       else
1260         n = nsprintf (cp, "%.0f", number);
1261
1262       /* If number is positive and its magnitude is less than
1263          1...  */
1264       if (n_int == 0)
1265         {
1266           if (*cp == '0')
1267             {
1268               /* The value rounds to `.###'. */
1269               memmove (cp, &cp[1], n - 1);
1270               n--;
1271             }
1272           else
1273             {
1274               /* The value rounds to `1.###'. */
1275               n_int = 1;
1276               continue;
1277             }
1278         }
1279       /* Else if number is negative and its magnitude is less
1280          than 1...  */
1281       else if (number < 0 && n_int == 1)
1282         {
1283           if (cp[1] == '0')
1284             {
1285               /* The value rounds to `-.###'. */
1286               memmove (&cp[1], &cp[2], n - 2);
1287               n--;
1288             }
1289           else
1290             {
1291               /* The value rounds to `-1.###'. */
1292               n_int = 2;
1293               continue;
1294             }
1295         }
1296
1297       /* Check for a correct number of digits & decimal places & stuff.
1298          This is just a desperation check.  Hopefully it won't fail too
1299          often, because then we have to run through the whole loop again:
1300          sprintf() is not a fast operation with floating-points! */
1301       if (n == n_int + n_dec)
1302         {
1303           /* Convert periods `.' to commas `,' for our foreign friends. */
1304           if ((get_decimal() == ',' && fp->type != FMT_DOT)
1305               || (get_decimal() == '.' && fp->type == FMT_DOT))
1306             {
1307               cp = strchr (cp, '.');
1308               if (cp)
1309                 *cp = ',';
1310             }
1311
1312           memcpy (dst, buf, fp->w);
1313           return 1;
1314         }
1315
1316       n_int = n - n_dec; /* FIXME?  Need an idiot check on resulting n_int? */
1317     }
1318 }