1 /*
2 * Copyright 2020 University Corporation for Atmospheric Research
3 *
4 * This file is part of the UDUNITS-2 package. See the file COPYRIGHT
5 * in the top-level source-directory of the package for copying and
6 * redistribution conditions.
7 */
8 /*
9 * This module is thread-compatible but not thread-safe.
10 */
11 /*LINTLIBRARY*/
12
13 #include "config.h"
14
15 #include "udunits2.h"
16 #include "unitToIdMap.h"
17
18 #ifdef _MSC_VER
19 #define _USE_MATH_DEFINES
20 #endif
21
22 #include <ctype.h>
23 #include <float.h>
24 #include <limits.h>
25 #include <math.h>
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31
32 typedef const char* (*IdGetter)(const ut_unit*, ut_encoding);
33 typedef int (*ProductPrinter)(const ut_unit* const*, const int*,
34 int, char*, size_t, IdGetter);
35
36 /*
37 * Formatting parameters:
38 */
39 typedef struct {
40 IdGetter getId;
41 ProductPrinter printProduct;
42 char* buf;
43 size_t size;
44 int getDefinition;
45 ut_encoding encoding;
46 int addParens;
47 int nchar;
48 } FormatPar;
49
50 #undef ABS
51 #define ABS(x) ((x) < 0 ? -(x) : (x))
52 #define RETURNS_NAME(getId) ((getId) == getName)
53 #define SUBTRACT_SIZET(a, b) ((a) > (b) ? (a) - (b) : 0)
54
55 static int
56 asciiPrintProduct(
57 const ut_unit* const* const basicUnits,
58 const int* const powers,
59 const int count,
60 char* const buf,
61 size_t max,
62 IdGetter getId);
63 static int
64 latin1PrintProduct(
65 const ut_unit* const* const basicUnits,
66 const int* const powers,
67 const int count,
68 char* const buf,
69 size_t max,
70 IdGetter getId);
71 static int
72 utf8PrintProduct(
73 const ut_unit* const* const basicUnits,
74 const int* const powers,
75 const int count,
76 char* const buf,
77 size_t max,
78 IdGetter getId);
79
80 static ut_visitor formatter;
81
82
83 /*
84 * Returns a name for a unit.
85 *
86 * Arguments:
87 * unit Pointer to the unit to have it's name returned.
88 * encoding The encoding of the name to be returned.
89 * Returns:
90 * NULL A name is not available in the desired encoding.
91 * else Pointer to the name.
92 */
93 static const char*
getName(const ut_unit * const unit,const ut_encoding encoding)94 getName(
95 const ut_unit* const unit,
96 const ut_encoding encoding)
97 {
98 const char* name;
99
100 name = ut_get_name(unit, encoding);
101
102 if (name == NULL)
103 name = ut_get_name(unit, UT_ASCII);
104
105 return name;
106 }
107
108
109 /*
110 * Returns a symbol for a unit.
111 *
112 * Arguments:
113 * unit Pointer to the unit to have it's symbol returned.
114 * encoding The encoding of the symbol to be returned.
115 * Returns:
116 * NULL A symbol is not available in the desired encoding.
117 * else Pointer to the symbol.
118 */
119 static const char*
getSymbol(const ut_unit * const unit,const ut_encoding encoding)120 getSymbol(
121 const ut_unit* const unit,
122 const ut_encoding encoding)
123 {
124 const char* symbol;
125
126 symbol = ut_get_symbol(unit, encoding);
127
128 if (symbol == NULL)
129 symbol = ut_get_symbol(unit, UT_ASCII);
130
131 return symbol;
132 }
133
134
135 /*
136 * Formats a unit.
137 *
138 * Arguments:
139 * unit Pointer to the unit to be formatted.
140 * buf Pointer to the buffer into which to print the formatted
141 * unit.
142 * size Size of the buffer.
143 * useNames Use unit names rather than unit symbols.
144 * getDefinition Returns the definition of "unit" in terms of basic
145 * units.
146 * encoding The type of encoding to use.
147 * addParens Whether or not to add bracketing parentheses if
148 * whitespace is printed.
149 * Returns:
150 * -1 Failure: "utFormStatus()" will be
151 * UT_BAD_ARG "unit" is NULL or "buf" is NULL.
152 * else Success. Number of bytes that would be printed if
153 * "size" were sufficiently large excluding the
154 * terminating NUL.
155 */
156 static int
format(const ut_unit * const unit,char * buf,size_t size,const int useNames,const int getDefinition,ut_encoding encoding,const int addParens)157 format(
158 const ut_unit* const unit,
159 char* buf,
160 size_t size,
161 const int useNames,
162 const int getDefinition,
163 ut_encoding encoding,
164 const int addParens)
165 {
166 int nchar = -1; /* failure */
167
168 if (unit == NULL) {
169 ut_set_status(UT_BAD_ARG);
170 ut_handle_error_message("format(): NULL unit argument");
171 }
172 else if (buf == NULL) {
173 ut_set_status(UT_BAD_ARG);
174 ut_handle_error_message("format(): NULL buffer argument");
175 }
176 else {
177 FormatPar formatPar;
178
179 formatPar.buf = buf;
180 formatPar.size = size;
181 formatPar.getId = useNames ? getName : getSymbol;
182 formatPar.getDefinition = getDefinition;
183 formatPar.encoding = encoding;
184 formatPar.printProduct =
185 encoding == UT_ASCII
186 ? asciiPrintProduct
187 : encoding == UT_LATIN1
188 ? latin1PrintProduct
189 : utf8PrintProduct;
190 formatPar.addParens = addParens;
191 formatPar.nchar = 0;
192
193 if (ut_accept_visitor(unit, &formatter, &formatPar) == UT_SUCCESS)
194 nchar = formatPar.nchar;
195 }
196
197 return nchar;
198 }
199
200
201 /*******************************************************************************
202 * Basic-Unit Formatting:
203 ******************************************************************************/
204
205 /*
206 * Prints a basic-unit.
207 *
208 * Arguments:
209 * unit The basic-unit to be printed.
210 * buf The buffer into which to print "unit".
211 * size The size of "buf".
212 * Returns:
213 * -1 Failure. The identifier for "unit" could not be
214 * obtained.
215 * else Success. Number of bytes that would be printed if
216 * "size" were sufficiently large excluding the
217 * terminating NUL.
218 */
219 static int
printBasic(const ut_unit * const unit,char * const buf,const size_t size,IdGetter getId,ut_encoding encoding)220 printBasic(
221 const ut_unit* const unit,
222 char* const buf,
223 const size_t size,
224 IdGetter getId,
225 ut_encoding encoding)
226 {
227 const char* const id = getId(unit, encoding);
228
229 return
230 id == NULL
231 ? -1
232 : snprintf(buf, size, "%s", id);
233 }
234
235
236 /*
237 * Formats a basic-unit.
238 *
239 * Arguments:
240 * unit The basic-unit to be formatted.
241 * arg The formatting parameters.
242 * Returns:
243 * -1 Failure. The identifier for "unit" could not be
244 * obtained.
245 * else Success. Number of bytes that would be printed if
246 * "size" were sufficiently large excluding the
247 * terminating NUL.
248 */
249 static ut_status
formatBasic(const ut_unit * const unit,void * arg)250 formatBasic(
251 const ut_unit* const unit,
252 void* arg)
253 {
254 FormatPar* formatPar = (FormatPar*)arg;
255 int nchar = printBasic(unit, formatPar->buf, formatPar->size,
256 formatPar->getId, formatPar->encoding);
257
258 formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
259
260 return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
261 }
262
263
264 /*******************************************************************************
265 * Product-Unit Formatting:
266 ******************************************************************************/
267
268 /*
269 * Prints a product-unit using the ASCII character-set.
270 *
271 * Arguments:
272 * basicUnits Pointer to pointers to the basic-units that constitute
273 * the product-unit.
274 * powers Pointer to the powers associated with each basic-unit.
275 * count The number of basic-units.
276 * buf Pointer to the buffer into which to print the basic-
277 * units.
278 * size The size of "buf" in bytes.
279 * getId Returns the identifier for a unit.
280 * Returns:
281 * -1 Failure. See errno.
282 * else Success. Number of bytes that would be printed if
283 * "size" were sufficiently large excluding the
284 * terminating NUL.
285 */
286 static int
asciiPrintProduct(const ut_unit * const * const basicUnits,const int * const powers,const int count,char * const buf,size_t size,IdGetter getId)287 asciiPrintProduct(
288 const ut_unit* const* const basicUnits,
289 const int* const powers,
290 const int count,
291 char* const buf,
292 size_t size,
293 IdGetter getId)
294 {
295 int nchar = snprintf(buf, size, "%s", "");
296
297 if (nchar >= 0) {
298 int i;
299
300 size = SUBTRACT_SIZET(size, nchar);
301
302 for (i = 0; i < count && nchar >= 0; i++) {
303 int n;
304
305 /*
306 * Append separator if appropriate.
307 */
308 if (nchar > 0) {
309 n = RETURNS_NAME(getId)
310 ? snprintf(buf+nchar, size, "%s", "-")
311 : snprintf(buf+nchar, size, "%s", ".");
312
313 if (n < 0) {
314 nchar = n;
315 break;
316 }
317
318 nchar += n;
319 size = SUBTRACT_SIZET(size, n);
320 }
321
322 /*
323 * Append unit identifier.
324 */
325 n = printBasic(basicUnits[i], buf+nchar, size, getId, UT_ASCII);
326
327 if (n < 0) {
328 nchar = n;
329 break;
330 }
331
332 nchar += n;
333 size = SUBTRACT_SIZET(size, n);
334
335 /*
336 * Append exponent if appropriate.
337 */
338 if (powers[i] != 1) {
339 n = RETURNS_NAME(getId)
340 ? snprintf(buf+nchar, size, "^%d", powers[i])
341 : snprintf(buf+nchar, size, "%d", powers[i]);
342
343 if (n < 0) {
344 nchar = n;
345 break;
346 }
347
348 nchar += n;
349 size = SUBTRACT_SIZET(size, n);
350 }
351 } /* loop over basic-units */
352 } /* "buf" initialized */
353
354 return nchar;
355 }
356
357
358 /*
359 * Prints a product of basic-units using the UTF-8 character-set.
360 *
361 * Arguments:
362 * basicUnits Pointer to pointers to the basic-units whose product
363 * is to be printed.
364 * powers Pointer to the powers associated with each basic-unit.
365 * count The number of basic-units.
366 * buf Pointer to the buffer into which to print the basic-
367 * units.
368 * size The size of "buf" in bytes.
369 * getId Returns the identifier for a unit.
370 * Returns:
371 * -1 Failure. See errno.
372 * else Success. Number of bytes that would be printed if
373 * "size" were sufficiently large excluding the
374 * terminating NUL.
375 */
376 static int
utf8PrintProduct(const ut_unit * const * const basicUnits,const int * const powers,const int count,char * const buf,size_t size,IdGetter getId)377 utf8PrintProduct(
378 const ut_unit* const* const basicUnits,
379 const int* const powers,
380 const int count,
381 char* const buf,
382 size_t size,
383 IdGetter getId)
384 {
385 int nchar = snprintf(buf, size, "%s", "");
386
387 if (nchar >= 0) {
388 int iBasic;
389
390 size = SUBTRACT_SIZET(size, nchar);
391
392 for (iBasic = 0; iBasic < count; iBasic++) {
393 int power = powers[iBasic];
394
395 if (power != 0) {
396 /*
397 * The current basic-unit must be printed.
398 */
399 int n;
400
401 if (nchar > 0) {
402 /*
403 * Append mid-dot separator.
404 */
405 n = snprintf(buf+nchar, size, "%s", "\xc2\xb7");
406
407 if (n < 0) {
408 nchar = n;
409 break;
410 }
411
412 nchar += n;
413 size = SUBTRACT_SIZET(size, n);
414 }
415
416 /*
417 * Append unit identifier.
418 */
419 n = printBasic(basicUnits[iBasic], buf+nchar, size, getId,
420 UT_UTF8);
421
422 if (n < 0) {
423 nchar = n;
424 break;
425 }
426
427 nchar += n;
428 size = SUBTRACT_SIZET(size, n);
429
430 if (power != 1) {
431 /*
432 * Append exponent.
433 */
434 static const char* exponentStrings[10] = {
435 "\xe2\x81\xb0", /* 0 */
436 "\xc2\xb9", /* 1 */
437 "\xc2\xb2", /* 2 */
438 "\xc2\xb3", /* 3 */
439 "\xe2\x81\xb4", /* 4 */
440 "\xe2\x81\xb5", /* 5 */
441 "\xe2\x81\xb6", /* 6 */
442 "\xe2\x81\xb7", /* 7 */
443 "\xe2\x81\xb8", /* 8 */
444 "\xe2\x81\xb9", /* 9 */
445 };
446
447 if (power < 0) {
448 /*
449 * Append superscript minus sign.
450 */
451 n = snprintf(buf+nchar, size, "%s", "\xe2\x81\xbb");
452
453 if (n < 0) {
454 nchar = n;
455 break;
456 }
457
458 nchar += n;
459 size = SUBTRACT_SIZET(size, n);
460 power = -power;
461 }
462
463 /*
464 * Append UTF-8 encoding of exponent magnitude.
465 */
466 {
467 static int* digit = NULL;
468
469 digit = realloc(digit, (size_t)((sizeof(powers[0])*
470 CHAR_BIT*(M_LOG10E/M_LOG2E)) + 1));
471
472 if (digit == NULL) {
473 nchar = -1;
474 }
475 else {
476 int idig = 0;
477
478 for (; power > 0; power /= 10)
479 digit[idig++] = power % 10;
480
481 while (idig-- > 0) {
482 n = snprintf(buf+nchar, size, "%s",
483 exponentStrings[digit[idig]]);
484
485 if (n < 0) {
486 nchar = n;
487 break;
488 }
489
490 nchar += n;
491 size = SUBTRACT_SIZET(size, n);
492 }
493
494 if (nchar < 0)
495 break;
496 }
497 } /* exponent digits block */
498 } /* must print exponent */
499 } /* must print basic-unit */
500 } /* loop over basic-units */
501 } /* "buf" initialized */
502
503 return nchar;
504 }
505
506
507 static const int* globalPowers = NULL;
508
509
510 static int
compareExponents(const void * i,const void * j)511 compareExponents(
512 const void* i,
513 const void* j)
514 {
515 return globalPowers[*(const int*)j] - globalPowers[*(const int*)i];
516 }
517
518
519 /*
520 * Returns the order of basic-unit powers in decreasing order.
521 *
522 * Arguments:
523 * powers Pointer to the powers of the basic-units.
524 * count The number of powers.
525 * positiveCount Pointer to pointer to the number of positive powers.
526 * Set on and only on success.
527 * negativeCount Pointer to pointer to the number of negative powers.
528 * Set on and only on success.
529 * Returns:
530 * NULL Failure. See errno.
531 * else Success. Pointer to indexes of "powers" in decreasing
532 * order.
533 */
534 static void
getBasicOrder(const int * const powers,const int count,int * const order,int * const positiveCount,int * const negativeCount)535 getBasicOrder(
536 const int* const powers,
537 const int count,
538 int* const order,
539 int* const positiveCount,
540 int* const negativeCount)
541 {
542 int nNeg = 0;
543 int nPos = 0;
544 int n = 0;
545 int i;
546
547 for (i = 0; i < count; i++) {
548 if (powers[i] < 0) {
549 ++nNeg;
550 order[n++] = i;
551 }
552 else if (powers[i] > 0) {
553 ++nPos;
554 order[n++] = i;
555 }
556 }
557
558 *negativeCount = nNeg;
559 *positiveCount = nPos;
560 globalPowers = powers;
561
562 qsort(order, n, sizeof(int), compareExponents);
563 }
564
565
566 /*
567 * Prints the product of a set of basic-units using the ISO-8859-1 (Latin-1)
568 * character-set.
569 *
570 * Arguments:
571 * buf Pointer to the buffer into which to print the basic-
572 * units.
573 * size The size of "buf" in bytes.
574 * basicUnits Pointer to pointers to the basic-units.
575 * powers Pointer to the powers associated with each basic-unit.
576 * order Pointer to indexes of "powers". "order[i]" is the
577 * index of "basicUnits" and "powers" for the "i"th
578 * position.
579 * count The number of basic-units.
580 * getId Returns the identifier for a unit.
581 * Returns:
582 * -1 Failure. See errno.
583 * else Success. Number of bytes that would be printed if
584 * "size" were sufficiently large excluding the
585 * terminating NUL.
586 */
587 static int
latin1PrintBasics(char * const buf,size_t size,const ut_unit * const * basicUnits,const int * const powers,const int * const order,const int count,IdGetter getId)588 latin1PrintBasics(
589 char* const buf,
590 size_t size,
591 const ut_unit* const* basicUnits,
592 const int* const powers,
593 const int* const order,
594 const int count,
595 IdGetter getId)
596 {
597 int needSeparator = 0;
598 int nchar = 0;
599 int i;
600
601 for (i = 0; i < count; i++) {
602 int n;
603 int j = order[i];
604 int power = ABS(powers[j]);
605
606 if (power != 0) {
607 if (needSeparator) {
608 n = snprintf(buf+nchar, size, "%s", "\xb7"); /* raised dot */
609
610 if (n < 0) {
611 nchar = n;
612 break;
613 }
614
615 nchar += n;
616 size = SUBTRACT_SIZET(size, n);
617 }
618
619 /*
620 * Append unit identifier.
621 */
622 n = printBasic(basicUnits[j], buf+nchar, size, getId, UT_LATIN1);
623
624 if (n < 0) {
625 nchar = n;
626 break;
627 }
628
629 nchar += n;
630 size = SUBTRACT_SIZET(size, n);
631 needSeparator = 1;
632
633 /*
634 * Append exponent if appropriate.
635 */
636 if (power != 1) {
637 n = snprintf(buf+nchar, size, "%s",
638 power == 2 ? "\xb2" : "\xb3"); /* superscript 2, superscript 3 */
639
640 if (n < 0) {
641 nchar = n;
642 break;
643 }
644
645 nchar += n;
646 size = SUBTRACT_SIZET(size, n);
647 }
648 } /* exponent not zero */
649 } /* loop over positive exponents */
650
651 return nchar;
652 }
653
654
655 /*
656 * Prints a product-unit using the ISO-8859-1 (Latin-1) character-set.
657 *
658 * Arguments:
659 * basicUnits Pointer to pointers to the basic-units that constitute
660 * the product-unit.
661 * powers Pointer to the powers associated with each basic-unit.
662 * count The number of basic-units.
663 * buf Pointer to the buffer into which to print the basic-
664 * units.
665 * size The size of "buf" in bytes.
666 * getId Returns the identifier for a unit.
667 * Returns:
668 * -1 Failure. See errno.
669 * else Success. Number of bytes that would be printed if
670 * "size" were sufficiently large excluding the
671 * terminating NUL.
672 */
673 static int
latin1PrintProduct(const ut_unit * const * const basicUnits,const int * const powers,const int count,char * const buf,size_t size,IdGetter getId)674 latin1PrintProduct(
675 const ut_unit* const* const basicUnits,
676 const int* const powers,
677 const int count,
678 char* const buf,
679 size_t size,
680 IdGetter getId)
681 {
682 int nchar;
683 int i;
684
685 for (i = 0; i < count; i++)
686 if (powers[i] < -3 || powers[i] > 3)
687 break;
688
689 if (i < count) {
690 /*
691 * At least one exponent can't be represented in ISO 8859-1. Use
692 * the ASCII encoding instead.
693 */
694 nchar = asciiPrintProduct(basicUnits, powers, count, buf, size, getId);
695 }
696 else {
697 int positiveCount;
698 int negativeCount;
699 int* order = malloc(count*sizeof(int));
700
701 if (order == NULL) {
702 nchar = -1;
703 }
704 else {
705 getBasicOrder(powers, count, order, &positiveCount, &negativeCount);
706
707 nchar = snprintf(buf, size, "%s", "");
708
709 if (nchar >= 0 && (positiveCount + negativeCount > 0)) {
710 int n;
711
712 size = SUBTRACT_SIZET(size, nchar);
713
714 if (positiveCount == 0) {
715 n = snprintf(buf+nchar, size, "%s", "1");
716 if (0 > n) {
717 nchar = n;
718 }
719 else {
720 nchar += n;
721 size = SUBTRACT_SIZET(size, n);
722 }
723 }
724 else {
725 n = latin1PrintBasics(buf+nchar, size, basicUnits,
726 powers, order, positiveCount, getId);
727 if (0 > n) {
728 nchar = n;
729 }
730 else {
731 nchar += n;
732 size = SUBTRACT_SIZET(size, n);
733 }
734 }
735
736 if (nchar >= 0 && negativeCount > 0) {
737 n = snprintf(buf+nchar, size, "%s",
738 negativeCount == 1 ? "/" : "/(");
739 if (0 > n) {
740 nchar = n;
741 }
742 else {
743 nchar += n;
744 size = SUBTRACT_SIZET(size, n);
745
746 n = latin1PrintBasics(buf+nchar, size, basicUnits,
747 powers, order+positiveCount, negativeCount,
748 getId);
749 if (0 > n) {
750 nchar = n;
751 }
752 else {
753 nchar += n;
754 size = SUBTRACT_SIZET(size, n);
755
756 if (negativeCount > 1) {
757 n = snprintf(buf+nchar, size, "%s", ")");
758 if (0 > n) {
759 nchar = n;
760 }
761 else {
762 nchar += n;
763 // size = SUBTRACT_SIZET(size, n); // not used
764 }
765 }
766 }
767 } /* solidus appended */
768 } /* positive exponents printed */
769 } /* "buf" initialized */
770
771 (void)free(order);
772 } /* "order" allocated */
773 } /* using Latin-1 encoding */
774
775 return nchar;
776 }
777
778
779 /*
780 * Prints a product-unit.
781 *
782 * Arguments:
783 * unit Pointer to the product-unit to be formatted.
784 * count The number of basic-units that constitute the
785 * product-unit.
786 * basicUnits Pointer to pointers to the basic-units that constitute
787 * the product-unit.
788 * powers Pointer to the powers associated with each basic-unit
789 * of "basicUnits".
790 * arg The formatting parameters.
791 * Returns:
792 * -1 Failure. See errno.
793 * else Success. Number of bytes that would be printed if
794 * "size" were sufficiently large excluding the
795 * terminating NUL.
796 */
797 static ut_status
formatProduct(const ut_unit * const unit,const int count,const ut_unit * const * const basicUnits,const int * const powers,void * arg)798 formatProduct(
799 const ut_unit* const unit,
800 const int count,
801 const ut_unit* const* const basicUnits,
802 const int* const powers,
803 void* arg)
804 {
805 FormatPar* formatPar = (FormatPar*)arg;
806 int nchar;
807
808 if (ut_compare(unit,
809 ut_get_dimensionless_unit_one(ut_get_system(unit))) == 0) {
810 /*
811 * The dimensionless unit one is special.
812 */
813 (void)strncpy(formatPar->buf, "1", formatPar->size);
814 nchar = formatPar->size > 0 ? 1 : 0;
815 }
816 else {
817 if (formatPar->getDefinition) {
818 nchar = formatPar->printProduct(basicUnits, powers, count,
819 formatPar->buf, formatPar->size, formatPar->getId);
820 }
821 else {
822 const char* id = formatPar->getId(unit, formatPar->encoding);
823
824 nchar =
825 id == NULL
826 ? formatPar->printProduct(basicUnits, powers, count,
827 formatPar->buf, formatPar->size, formatPar->getId)
828 : snprintf(formatPar->buf, formatPar->size, "%s", id);
829 }
830 }
831 formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
832
833 return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
834 }
835
836
837 /*******************************************************************************
838 * Galilean-Unit Formatting:
839 ******************************************************************************/
840
841 /*
842 * Prints a Galilean-unit.
843 *
844 * Arguments:
845 * scale The number of "unit"s in the Galilean-unit.
846 * unit Pointer to the unit underlying the Galilean-unit.
847 * offset The offset of the Galilean-unit in units of "unit".
848 * buf Pointer to the buffer into which to print the Galilean-
849 * unit.
850 * size The size of "buf" in bytes.
851 * getId Returns the identifier for a unit.
852 * getDefinition Returns the definition of "unit" in terms of basic
853 * units.
854 * encoding The type of encoding to use.
855 * addParens Whether or not to add bracketing parentheses if
856 * whitespace is printed.
857 * Returns:
858 * -1 Failure. See errno.
859 * else Success. Number of bytes that would be printed if
860 * "size" were sufficiently large excluding the
861 * terminating NUL.
862 */
863 static int
printGalilean(double scale,const ut_unit * const unit,double offset,char * const buf,size_t size,IdGetter getId,const int getDefinition,const ut_encoding encoding,const int addParens)864 printGalilean(
865 double scale,
866 const ut_unit* const unit,
867 double offset,
868 char* const buf,
869 size_t size,
870 IdGetter getId,
871 const int getDefinition,
872 const ut_encoding encoding,
873 const int addParens)
874 {
875 int n;
876 int nchar = 0;
877 int needParens = 0;
878
879 if (scale != 1) {
880 needParens = addParens;
881 n = snprintf(buf, size, needParens ? "(%.*g " : "%.*g ", DBL_DIG,
882 scale);
883 if (0 > n) {
884 nchar = n;
885 }
886 else {
887 nchar += n;
888 size = SUBTRACT_SIZET(size, n);
889 }
890 }
891
892 if (0 <= nchar) {
893 n = format(unit, buf+nchar, size, RETURNS_NAME(getId),
894 getDefinition, encoding, 1);
895
896 if (n < 0) {
897 nchar = n;
898 }
899 else {
900 nchar += n;
901 size = SUBTRACT_SIZET(size, n);
902
903 if (offset != 0) {
904 needParens = addParens;
905 n = RETURNS_NAME(getId)
906 ? snprintf(buf+nchar, size, " from %.*g", DBL_DIG,
907 offset)
908 : snprintf(buf+nchar, size, " @ %.*g", DBL_DIG, offset);
909 if (0 > n) {
910 nchar = n;
911 }
912 else {
913 nchar += n;
914 size = SUBTRACT_SIZET(size, n);
915 }
916 } /* non-zero offset */
917
918 if (nchar >= 0) {
919 if (needParens) {
920 n = snprintf(buf+nchar, size, "%s", ")");
921 if (0 > n) {
922 nchar = n;
923 }
924 else {
925 nchar += n;
926 // size = SUBTRACT_SIZET(size, n); // Not used
927 }
928 }
929 } /* printed offset if appropriate */
930 } /* underlying unit printed */
931 } /* scale printed if appropriate */
932
933 return nchar;
934 }
935
936
937 /*
938 * Formats a Galilean-unit.
939 *
940 * Arguments:
941 * unit Pointer to the Galilean-unit to be formatted.
942 * scale The number of "underlyingUnit"s in "unit".
943 * underlyingUnit Pointer to the unit that underlies "unit".
944 * offset The offset of "unit" in units of "underlyingUnit".
945 * arg Pointer to the formatting parameters.
946 * Returns:
947 * -1 Failure. See errno.
948 * else Success. Number of bytes that would be printed if
949 * "size" were sufficiently large excluding the
950 * terminating NUL.
951 */
952 static ut_status
formatGalilean(const ut_unit * const unit,const double scale,const ut_unit * const underlyingUnit,double offset,void * arg)953 formatGalilean(
954 const ut_unit* const unit,
955 const double scale,
956 const ut_unit* const underlyingUnit,
957 double offset,
958 void* arg)
959 {
960 FormatPar* formatPar = (FormatPar*)arg;
961 int nchar;
962
963 if (formatPar->getDefinition) {
964 nchar = printGalilean(scale, underlyingUnit, offset, formatPar->buf,
965 formatPar->size, formatPar->getId, formatPar->getDefinition,
966 formatPar->encoding, formatPar->addParens);
967 }
968 else {
969 const char* id = formatPar->getId(unit, formatPar->encoding);
970
971 nchar =
972 id == NULL
973 ? printGalilean(scale, underlyingUnit, offset, formatPar->buf,
974 formatPar->size, formatPar->getId, formatPar->getDefinition,
975 formatPar->encoding, formatPar->addParens)
976 : snprintf(formatPar->buf, formatPar->size, "%s", id);
977 }
978
979 formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
980
981 return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
982 }
983
984
985 /*******************************************************************************
986 * Timestamp-Unit Formatting:
987 ******************************************************************************/
988
989 /*
990 * Prints a timestamp-unit.
991 *
992 * Arguments:
993 * underlyingUnit Pointer to the unit underlying the timestamp-unit.
994 * year The UTC year of the origin.
995 * month The UTC month of the origin (1 through 12).
996 * day The UTC day of the origin (1 through 32).
997 * hour The UTC hour of the origin (0 through 23).
998 * minute The UTC minute of the origin (0 through 59).
999 * second The UTC second of the origin (0 through 60).
1000 * resolution The resolution of the origin in seconds.
1001 * buf Pointer to the buffer into which to print the
1002 * timestamp-unit.
1003 * size The size of "buf" in bytes.
1004 * getId Returns the identifier for a unit.
1005 * getDefinition Returns the definition of "unit" in terms of basic
1006 * units.
1007 * encoding The type of encoding to use.
1008 * addParens Whether or not to add bracketing parentheses if
1009 * whitespace is printed.
1010 * Returns:
1011 * -1 Failure. See errno.
1012 * else Success. Number of bytes that would be printed if
1013 * "size" were sufficiently large excluding the
1014 * terminating NUL.
1015 */
1016 static int
printTimestamp(const ut_unit * const underlyingUnit,const int year,const int month,const int day,const int hour,const int minute,const double second,const double resolution,char * const buf,size_t size,IdGetter getId,const int getDefinition,const ut_encoding encoding,const int addParens)1017 printTimestamp(
1018 const ut_unit* const underlyingUnit,
1019 const int year,
1020 const int month,
1021 const int day,
1022 const int hour,
1023 const int minute,
1024 const double second,
1025 const double resolution,
1026 char* const buf,
1027 size_t size,
1028 IdGetter getId,
1029 const int getDefinition,
1030 const ut_encoding encoding,
1031 const int addParens)
1032 {
1033 int n;
1034 int nchar = 0;
1035
1036 if (addParens) {
1037 n = snprintf(buf, size, "%s", "(");
1038 if (0 > n) {
1039 nchar = -1;
1040 }
1041 else {
1042 nchar += n;
1043 size = SUBTRACT_SIZET(size, n);
1044 }
1045 }
1046
1047 if (nchar >= 0) {
1048 int useNames = RETURNS_NAME(getId);
1049
1050 n = format(underlyingUnit, buf+nchar, size, useNames, getDefinition,
1051 encoding, 1);
1052 nchar = n < 0 ? n : nchar + n;
1053
1054 if (nchar >= 0) {
1055 size = SUBTRACT_SIZET(size, n);
1056
1057 int useSeparators = useNames || year < 1000 || year > 9999;
1058
1059 n = snprintf(buf+nchar, size,
1060 useSeparators
1061 ? " %s %d-%02d-%02d %02d:%02d"
1062 : " %s %d%02d%02dT%02d%02d",
1063 useNames ? "since" : "@",
1064 year, month, day, hour, minute);
1065 if (0 > n) {
1066 nchar = -1;
1067 }
1068 else {
1069 nchar += n;
1070 size = SUBTRACT_SIZET(size, n);
1071 }
1072
1073 if (nchar >= 0) {
1074 int decimalCount = resolution <= 0.0
1075 ? 9 // Nanosecond resolution
1076 : -(int)floor(log10(resolution));
1077
1078 if (decimalCount > -2) {
1079 n = snprintf(buf+nchar, size,
1080 useSeparators ? ":%0*.*f" : "%0*.*f",
1081 decimalCount+3, decimalCount, second);
1082 if (0 > n) {
1083 nchar = -1;
1084 }
1085 else {
1086 nchar += n;
1087 size = SUBTRACT_SIZET(size, n);
1088 }
1089 } /* sufficient precision for seconds */
1090
1091 if (nchar >= 0) {
1092 n = snprintf(buf+nchar, size, "%s",
1093 addParens ? " UTC)" : " UTC");
1094 if (0 > n) {
1095 nchar = -1;
1096 }
1097 else {
1098 nchar += n;
1099 // size = SUBTRACT_SIZET(size, n); // Not used
1100 }
1101 } /* printed seconds if appropriate */
1102 } /* printed year through minute */
1103 } /* underlying unit printed */
1104 } /* leading "(" printed if appropriate */
1105
1106 return nchar;
1107 }
1108
1109
1110 /*
1111 * Formats a timestamp-unit.
1112 *
1113 * Arguments:
1114 * unit Pointer to the timestamp-unit to be formatted.
1115 * underlyingUnit Pointer to the unit that underlies "unit".
1116 * origin The encoded origin of the timestamp-unit.
1117 * arg Pointer to the formatting parameters.
1118 * Returns:
1119 * -1 Failure. See errno.
1120 * else Success. Number of bytes that would be printed if
1121 * "size" were sufficiently large excluding the
1122 * terminating NUL.
1123 */
1124 static ut_status
formatTimestamp(const ut_unit * const unit,const ut_unit * const underlyingUnit,const double origin,void * arg)1125 formatTimestamp(
1126 const ut_unit* const unit,
1127 const ut_unit* const underlyingUnit,
1128 const double origin,
1129 void* arg)
1130 {
1131 FormatPar* formatPar = (FormatPar*)arg;
1132 int nchar;
1133 int year;
1134 int month;
1135 int day;
1136 int hour;
1137 int minute;
1138 double second;
1139 double resolution;
1140
1141 ut_decode_time(origin, &year, &month, &day, &hour, &minute, &second,
1142 &resolution);
1143
1144 if (formatPar->getDefinition) {
1145 nchar = printTimestamp(underlyingUnit, year, month, day, hour, minute,
1146 second, resolution, formatPar->buf, formatPar->size,
1147 formatPar->getId, formatPar->getDefinition, formatPar->encoding,
1148 formatPar->addParens);
1149 }
1150 else {
1151 const char* id = formatPar->getId(unit, formatPar->encoding);
1152
1153 nchar =
1154 id == NULL
1155 ? printTimestamp(underlyingUnit, year, month, day, hour, minute,
1156 second, resolution, formatPar->buf, formatPar->size,
1157 formatPar->getId, formatPar->getDefinition,
1158 formatPar->encoding, formatPar->addParens)
1159 : snprintf(formatPar->buf, formatPar->size, "%s", id);
1160 }
1161
1162 formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
1163
1164 return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
1165 }
1166
1167
1168 /*******************************************************************************
1169 * Logarithmic-Unit Formatting:
1170 ******************************************************************************/
1171
1172 /*
1173 * Prints a logarithmic-unit.
1174 *
1175 * Arguments:
1176 * base The base of the logarithm (e.g., 2, M_E, 10).
1177 * reference Pointer to the reference-level of the logarithmic-unit.
1178 * buf Pointer to the buffer into which to print the
1179 * logarithmic-unit.
1180 * size The size of "buf" in bytes.
1181 * getId Returns the identifier for a unit.
1182 * getDefinition Returns the definition of "unit" in terms of basic
1183 * units.
1184 * encoding The type of encoding to use.
1185 * addParens Whether or not to add bracketing parentheses if
1186 * whitespace is printed.
1187 * Returns:
1188 * -1 Failure. See errno.
1189 * else Success. Number of bytes that would be printed if
1190 * "size" were sufficiently large excluding the
1191 * terminating NUL.
1192 */
1193 static int
printLogarithmic(const double base,const ut_unit * const reference,char * buf,size_t size,IdGetter getId,const int getDefinition,const ut_encoding encoding,const int addParens)1194 printLogarithmic(
1195 const double base,
1196 const ut_unit* const reference,
1197 char* buf,
1198 size_t size,
1199 IdGetter getId,
1200 const int getDefinition,
1201 const ut_encoding encoding,
1202 const int addParens)
1203 {
1204 char refSpec[512];
1205 int nchar = format(reference, refSpec, sizeof(refSpec)-1,
1206 RETURNS_NAME(getId), getDefinition, encoding, 0);
1207
1208 if (nchar >= 0) {
1209 const char* amount;
1210
1211 refSpec[nchar] = 0;
1212 amount = isalpha(refSpec[0]) ? "1 " : "";
1213
1214 if (base == 2) {
1215 nchar = snprintf(buf, size, "lb(re %s%s)", amount, refSpec);
1216 }
1217 else if (base == M_E) {
1218 nchar = snprintf(buf, size, "ln(re %s%s)", amount, refSpec);
1219 }
1220 else if (base == 10) {
1221 nchar = snprintf(buf, size, "lg(re %s%s)", amount, refSpec);
1222 }
1223 else {
1224 nchar = snprintf(buf, size,
1225 addParens ? "(%.*g ln(re %s%s))" : "%.*g ln(re %s%s)",
1226 DBL_DIG, 1/log(base), amount, refSpec);
1227 }
1228 } /* printed reference unit */
1229
1230 return nchar;
1231 }
1232
1233
1234 /*
1235 * Formats a logarithmic-unit.
1236 *
1237 * Arguments:
1238 * unit Pointer to the logarithmic-unit to be formatted.
1239 * base The base of the logarithm (e.g., 2, M_E, 10).
1240 * reference Pointer to the reference-level of "unit".
1241 * arg Pointer to the formatting parameters.
1242 * Returns:
1243 * UT_VISIT_ERROR Failure.
1244 * UT_SUCCESS Success.
1245 */
1246 static ut_status
formatLogarithmic(const ut_unit * const unit,const double base,const ut_unit * const reference,void * arg)1247 formatLogarithmic(
1248 const ut_unit* const unit,
1249 const double base,
1250 const ut_unit* const reference,
1251 void* arg)
1252 {
1253 FormatPar* formatPar = (FormatPar*)arg;
1254 int nchar;
1255
1256 if (formatPar->getDefinition) {
1257 nchar = printLogarithmic(base, reference, formatPar->buf,
1258 formatPar->size, formatPar->getId, formatPar->getDefinition,
1259 formatPar->encoding, formatPar->addParens);
1260 }
1261 else {
1262 const char* id = formatPar->getId(unit, formatPar->encoding);
1263
1264 nchar =
1265 id == NULL
1266 ? printLogarithmic(base, reference, formatPar->buf,
1267 formatPar->size, formatPar->getId, formatPar->getDefinition,
1268 formatPar->encoding, formatPar->addParens)
1269 : snprintf(formatPar->buf, formatPar->size, "%s", id);
1270 }
1271
1272 formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
1273
1274 return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
1275 }
1276
1277
1278 /*******************************************************************************
1279 * This module as a unit-visitor:
1280 ******************************************************************************/
1281
1282 static ut_visitor formatter = {
1283 formatBasic,
1284 formatProduct,
1285 formatGalilean,
1286 formatTimestamp,
1287 formatLogarithmic
1288 };
1289
1290
1291
1292 /******************************************************************************
1293 * Public API:
1294 ******************************************************************************/
1295
1296 /*
1297 * Formats a unit.
1298 *
1299 * Arguments:
1300 * unit Pointer to the unit to be formatted.
1301 * buf Pointer to the buffer into which to format "unit".
1302 * size Size of the buffer in bytes.
1303 * opts Formatting options: bitwise-OR of zero or more of the
1304 * following:
1305 * UT_NAMES Use unit names instead of
1306 * symbols
1307 * UT_DEFINITION The formatted string should be
1308 * the definition of "unit" in
1309 * terms of basic-units instead of
1310 * stopping any expansion at the
1311 * highest level possible.
1312 * UT_ASCII The string should be formatted
1313 * using the ASCII character set
1314 * (default).
1315 * UT_LATIN1 The string should be formatted
1316 * using the ISO Latin-1 (alias
1317 * ISO-8859-1) character set.
1318 * UT_UTF8 The string should be formatted
1319 * using the UTF-8 character set.
1320 * UT_LATIN1 and UT_UTF8 are mutually exclusive: they may
1321 * not both be specified.
1322 * Returns:
1323 * -1 Failure: "ut_get_status()" will be
1324 * UT_BAD_ARG "unit" or "buf" is NULL, or both
1325 * UT_LATIN1 and UT_UTF8 specified.
1326 * UT_CANT_FORMAT "unit" can't be formatted in
1327 * the desired manner.
1328 * else Success. Number of bytes that would be printed if
1329 * "size" were sufficiently large excluding the
1330 * terminating NUL.
1331 */
1332 int
ut_format(const ut_unit * const unit,char * buf,size_t size,unsigned opts)1333 ut_format(
1334 const ut_unit* const unit,
1335 char* buf,
1336 size_t size,
1337 unsigned opts)
1338 {
1339 int nchar = -1; /* failure */
1340 const int useNames = opts & UT_NAMES;
1341 const int getDefinition = opts & UT_DEFINITION;
1342 const ut_encoding encoding =
1343 (ut_encoding)(opts & (unsigned)(UT_ASCII | UT_LATIN1 | UT_UTF8));
1344
1345 if (unit == NULL || buf == NULL) {
1346 ut_set_status(UT_BAD_ARG);
1347 ut_handle_error_message("NULL argument");
1348 }
1349 else if ((encoding & UT_LATIN1) && (encoding & UT_UTF8)) {
1350 ut_set_status(UT_BAD_ARG);
1351 ut_handle_error_message("Both UT_LATIN1 and UT_UTF8 specified");
1352 }
1353 else {
1354 nchar = format(unit, buf, size, useNames, getDefinition, encoding, 0);
1355
1356 if (nchar < 0) {
1357 ut_set_status(UT_CANT_FORMAT);
1358 ut_handle_error_message("Couldn't format unit");
1359 }
1360 else {
1361 ut_set_status(UT_SUCCESS);
1362 }
1363 }
1364
1365 return nchar;
1366 }
1367