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