"Fossies" - the Fresh Open Source Software Archive

Member "udunits-2.2.28/prog/udunits2.c" (7 Dec 2020, 16302 Bytes) of package /linux/privat/udunits-2.2.28.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "udunits2.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.2.26_vs_2.2.28.

    1 /*
    2  * Copyright 2020 University Corporation for Atmospheric Research. All rights
    3  * reserved.
    4  *
    5  * This file is part of the UDUNITS-2 package.  See the file COPYRIGHT
    6  * in the top-level source-directory of the package for copying and
    7  * redistribution conditions.
    8  */
    9 /*
   10  * This program prints definitions of units of physical quantities and converts
   11  * values between such units.
   12  */
   13 
   14 #include "config.h"
   15 
   16 #ifdef _MSC_VER
   17 #include "XGetOpt.h"
   18 #endif
   19 
   20 #include <errno.h>
   21 #include <ctype.h>
   22 #ifndef _MSC_VER
   23 #include <libgen.h>
   24 #endif
   25 #include <limits.h>
   26 #include <sys/types.h>
   27 #include <stdarg.h>
   28 #include <stdio.h>
   29 #include <stdlib.h>
   30 #include <string.h>
   31 #ifndef _MSC_VER
   32 #include <strings.h>
   33 #include <unistd.h>
   34 #endif
   35 #include <udunits2.h>
   36 
   37 #ifndef _POSIX_MAX_INPUT
   38 #   define _POSIX_MAX_INPUT 255
   39 #endif
   40 
   41 static const char*      _cmdHave; /* command-line "have" unit specification */
   42 static const char*      _cmdWant; /* command-line "want" unit specification */
   43 static int      _reveal; /* reveal problems with unit database? */
   44 static int      _encodingSet; /* is the character encoding set? */
   45 static ut_encoding  _encoding; /* the character encoding to use */
   46 static char             _progname[1024];
   47 static const char*  _xmlPath = NULL; /* use default path */
   48 static ut_system*   _unitSystem;
   49 static double           _haveUnitAmount; /* amount of "have" unit */
   50 static char     _haveUnitSpec[_POSIX_MAX_INPUT+1]; /* "have" unit minus
   51                                                               amount */
   52 static char     _wantSpec[_POSIX_MAX_INPUT+1]; /* complete "want" unit
   53                                                           specification */
   54 static ut_unit*     _haveUnit; /* "have" unit minus amount */
   55 static ut_unit*     _wantUnit; /* complete "want" unit */
   56 static int      _wantDefinition; /* "have" unit definition desired? */
   57 static int      _formattingOptions = UT_DEFINITION;
   58 static int      _exitStatus = EXIT_FAILURE;
   59 
   60 static void
   61 usage(void)
   62 {
   63     ut_status    status;
   64     const char * default_xml = ut_get_path_xml(NULL, &status);
   65 
   66     (void)fprintf(stderr,
   67 "Usage:\n"
   68 "    %s -h\n"
   69 "    %s [-A|-L|-U] [-r] [-H <have>] [-W <want>] [<XML_file>]\n"
   70 "\n"
   71 "where:\n"
   72 "    -A          Use ASCII encoding (default).\n"
   73 "    -L          Use ISO-8859-1 (ISO Latin-1) encoding.\n"
   74 "    -U          Use UTF-8 encoding.\n"
   75 "    -h          Help.  Print this message.\n"
   76 "    -r          Reveal any problems in the database.\n"
   77 "    -H <have>   Use <have> unit for conversion. Default is reply to prompt.\n"
   78 "    -W <want>   Use <want> unit for conversion. Empty string requests\n"
   79 "                definition of <have> unit. Default is reply to prompt.\n"
   80 "    <XML_file>  XML database file. Default is \"%s\".\n",
   81         _progname, _progname, default_xml);
   82 }
   83 
   84 /**
   85  * Prints an error-message to the standard error stream.
   86  *
   87  * @param format        The format for the error-message. It shouldn't have a
   88  *                      trailing newline.
   89  * @param ...           Arguments referenced by the format.
   90  */
   91 static void
   92 errMsg(
   93     const char* const   format,
   94     ...)
   95 {
   96     (void)fprintf(stderr, "%s: ", _progname);
   97     {
   98         va_list     ap;
   99 
  100         va_start(ap, format);
  101         (void)vfprintf(stderr, format, ap);
  102         va_end(ap);
  103     }
  104     (void)fputc('\n', stderr);
  105 }
  106 
  107 
  108 static int
  109 decodeCommandLine(
  110     int         argc,
  111     char**      argv)
  112 {
  113     int     c;
  114     int     success = 0;
  115 
  116 #ifndef _MSC_VER
  117     char* filename = basename(argv[0]);
  118 
  119     if (strlen(filename)+1 > sizeof(_progname))
  120         filename = "udunits2";
  121 
  122     (void)strcpy(_progname, filename);
  123 #else
  124     {
  125         size_t len = strlen(argv[0]);
  126         char*  m_fname = (char*)malloc(sizeof(char)*(len+1));
  127         char*  m_ext = (char*)malloc(sizeof(char)*(len+1));
  128 
  129         _splitpath(argv[0], NULL, NULL, m_fname, m_ext);
  130 
  131         if (strlen(m_fname)+1 > sizeof(_progname))
  132             (void)strcpy(_progname, "udunits2");
  133         else
  134             (void)strcpy(_progname, m_fname);
  135 
  136         free(m_fname);
  137         free(m_ext);
  138     }
  139 #endif
  140 
  141     while ((c = getopt(argc, argv, "ALUhrH:W:")) != -1) {
  142     switch (c) {
  143         case 'A':
  144         _encoding = UT_ASCII;
  145         _encodingSet = 1;
  146         continue;
  147             case 'H':
  148                 _cmdHave = optarg;
  149                 continue;
  150             case 'W':
  151                 _cmdWant = optarg;
  152                 continue;
  153         case 'L':
  154         _encoding = UT_LATIN1;
  155         _encodingSet = 1;
  156         continue;
  157         case 'U':
  158         _encoding = UT_UTF8;
  159         _encodingSet = 1;
  160         continue;
  161         case 'r':
  162         _reveal = 1;
  163         continue;
  164         case 'h':
  165         _exitStatus = EXIT_SUCCESS;
  166         /*FALLTHROUGH*/
  167         case '?':
  168         usage();
  169         break;
  170         default:
  171         errMsg("Unknown option \"%c\"", c);
  172         usage();
  173     }
  174 
  175     break;
  176     }
  177 
  178     if (c == -1) {
  179         if (optind < argc)
  180             _xmlPath = argv[optind];
  181 
  182         success = 1;
  183     }
  184 
  185     return success;
  186 }
  187 
  188 
  189 /**
  190  * Returns a lower-case copy of a NUL-terminated string.
  191  *
  192  * @param string        The NUL-terminated string to be copied.
  193  * @retval NULL         The string couldn't be copied. An error-message is
  194  *                      printed to the standard error stream.
  195  * @return              A NUL-terminated lower-case copy of the string. The
  196  *                      caller should call free() on the string when it is no
  197  *                      longer needed.
  198  * @raise SIGSEGV       if "string" is NULL.
  199  */
  200 static char*
  201 duplower(
  202     const char*         string)
  203 {
  204     char* const         copy = malloc(strlen(string)+1);
  205 
  206     if (copy == NULL) {
  207         errMsg("Couldn't copy string \"%s\": %s", string, strerror(errno));
  208     }
  209     else {
  210         char*   cp = copy;
  211 
  212         while ((*cp++ = tolower(*string++)))
  213             ; /* empty */
  214     }
  215 
  216     return copy;
  217 }
  218 
  219 
  220 /**
  221  * Sets the character encoding from a (case insensitive) string specification
  222  * of the encoding that might be embedded in a larger string.
  223  *
  224  * @param value         The string specification of the encoding. Contains
  225  *                      one of the substrings "ascii", "latin1", "latin?1",
  226  *                      "8859", "8859?1", "utf8", or "utf?8" (where the '?'
  227  *                      is one of the characters ' ', '-', '_', or '.'). If the
  228  *                      string contains more than one of these substrings, then
  229  *                      the result is unspecified.
  230  * @return              Whether or not the character encoding was set from the
  231  *                      given string specification. 0 means no; otherwise, yes.
  232  */
  233 static int
  234 setEncodingFromEmbeddedString(
  235     char*   value)
  236 {
  237     char*       lowerValue = duplower(value);
  238     int         success = 0;
  239 
  240     if (lowerValue != NULL) {
  241         typedef struct {
  242             const char*       string;
  243             const ut_encoding encoding;
  244         } Entry;
  245         static const Entry entries[] = {
  246             {"ascii",   UT_ASCII},
  247             {"latin1",  UT_LATIN1},
  248             {"latin 1", UT_LATIN1},
  249             {"latin-1", UT_LATIN1},
  250             {"latin_1", UT_LATIN1},
  251             {"latin.1", UT_LATIN1},
  252             {"8859 1",  UT_LATIN1},
  253             {"8859-1",  UT_LATIN1},
  254             {"8859_1",  UT_LATIN1},
  255             {"8859.1",  UT_LATIN1},
  256             {"utf8",    UT_UTF8},
  257             {"utf 8",   UT_UTF8},
  258             {"utf-8",   UT_UTF8},
  259             {"utf_8",   UT_UTF8},
  260             {"utf.8",   UT_UTF8}
  261         };
  262         const Entry* entry;
  263 
  264         for (entry = entries;
  265                 entry < entries + sizeof(entries)/sizeof(entries[0]);
  266                 entry++) {
  267             if (strstr(lowerValue, entry->string) != NULL) {
  268                 _encoding = entry->encoding;
  269                 _encodingSet = 1;
  270                 success = 1;
  271                 break;
  272             }
  273         }
  274 
  275         free(lowerValue);
  276     }
  277 
  278     return success;
  279 }
  280 
  281 
  282 /**
  283  * Sets the character encoding from a (case insensitive but exact) string
  284  * specification of the encoding.
  285  *
  286  * @param value         The string specification of the encoding. One of
  287  *                      "c" or "posix".
  288  * @return              Whether or not the encoding was set. 0 means no;
  289  *                      otherwise, yes.
  290  */
  291 static int
  292 setEncodingFromExactString(
  293     char*   value)
  294 {
  295     if (strcasecmp(value, "c") == 0 || strcasecmp(value, "posix") == 0) {
  296         _encoding = UT_ASCII;
  297         _encodingSet = 1;
  298         return 1;
  299     }
  300 
  301     return 0;
  302 }
  303 
  304 
  305 /**
  306  * Sets the character encoding from a (case insensitive) string specification
  307  * of the encoding.
  308  *
  309  * @param value         The string specification of the encoding or NULL. If
  310  *                      not NULL, then one of "c" or "posix", or a string that
  311  *                      contains one of the substrings "ascii", "latin1",
  312  *                      "latin?1", "8859", "8859?1", "utf8", or "utf?8" (where
  313  *                      the '?' is one of the characters ' ', '-', '_', or '.').
  314  *                      If the string contains multiple instances of the
  315  *                      substrings, then the result is unspecified.
  316  */
  317 static void
  318 setEncoding(
  319     char*   value)
  320 {
  321     if (value != NULL) {
  322         if (!setEncodingFromExactString(value))
  323             (void)setEncodingFromEmbeddedString(value);
  324     }
  325 }
  326 
  327 
  328 static int
  329 ensureEncodingSet()
  330 {
  331     if (!_encodingSet) {
  332     setEncoding(getenv("LC_ALL"));
  333 
  334     if (!_encodingSet) {
  335         setEncoding(getenv("LC_CTYPE"));
  336 
  337         if (!_encodingSet) {
  338         setEncoding(getenv("LANG"));
  339 
  340         if (!_encodingSet) {
  341             errMsg("Character encoding not specified and not settable "
  342                         "from environment variables LC_ALL, LC_CTYPE, or LANG. "
  343                         "Assuming ASCII encoding.");
  344 
  345             setEncoding("ASCII");
  346         }
  347         }
  348     }
  349     }
  350 
  351     if (_encodingSet)
  352     _formattingOptions |= _encoding;
  353 
  354     return _encodingSet;
  355 }
  356 
  357 
  358 static int
  359 readXmlDatabase(void)
  360 {
  361     int     success = 0;
  362 
  363     if (!_reveal)
  364         ut_set_error_message_handler(ut_ignore);
  365 
  366     _unitSystem = ut_read_xml(_xmlPath);
  367 
  368     ut_set_error_message_handler(ut_write_to_stderr);
  369 
  370     if (_unitSystem != NULL) {
  371         success = 1;
  372     }
  373     else {
  374         ut_status   status;
  375 
  376         errMsg("Couldn't initialize unit-system from database \"%s\": %s",
  377                 ut_get_path_xml(_xmlPath, &status), strerror(errno));
  378     }
  379 
  380     return success;
  381 }
  382 
  383 
  384 /*
  385  * Prompt the user and get a specification.
  386  */
  387 static int
  388 getSpec(
  389     const char* const   prompt,
  390     char* const     spec,
  391     const size_t    size)
  392 {
  393     int     nbytes = -1;        /* failure */
  394 
  395     if (fputs(prompt, stdout) == EOF) {
  396     errMsg("Couldn't write prompt: %s", strerror(errno));
  397     } else if (fgets(spec, (int)size, stdin) == NULL) {
  398     putchar('\n');
  399 
  400     if (feof(stdin)) {
  401         _exitStatus = EXIT_SUCCESS;
  402     } else {
  403         errMsg("Couldn't read from standard input: %s", strerror(errno));
  404     }
  405     } else {
  406     /*
  407      * Trim any whitespace from the specification.
  408      */
  409     (void)ut_trim(spec, _encoding);
  410 
  411         nbytes = (int)strlen(spec);
  412     }
  413 
  414     return nbytes;
  415 }
  416 
  417 
  418 static int
  419 decodeInput(
  420     const char* input)
  421 {
  422     int success = 0;
  423 
  424     ut_free(_haveUnit);
  425 
  426     int nbytes;
  427     if (sscanf(input, "%lg %n", &_haveUnitAmount, &nbytes) == 1) {
  428         input += nbytes;
  429     }
  430     else {
  431         _haveUnitAmount = 1;
  432     }
  433 
  434     (void)strncpy(_haveUnitSpec, input, sizeof(_haveUnitSpec));
  435     _haveUnitSpec[sizeof(_haveUnitSpec)-1] = 0;
  436     _haveUnit = ut_parse(_unitSystem, _haveUnitSpec, _encoding);
  437     if (_haveUnit == NULL) {
  438         errMsg("Don't recognize \"%s\"", _haveUnitSpec);
  439     }
  440     else {
  441         success = 1;
  442     }
  443 
  444     return success;
  445 }
  446 
  447 
  448 static int
  449 getInputValue(void)
  450 {
  451     int     success = 0;
  452 
  453     if (_cmdHave) {
  454         static int      initialized = 0;
  455 
  456         if (initialized) {
  457             if (_cmdWant == NULL) {
  458                 /*
  459                  * Multiple, prompt-driven conversions desired.
  460                  */
  461                 success = 1;
  462             }
  463             else {
  464                 /*
  465                  * Single, previous, command-line-driven conversion desired.
  466                  */
  467                 success = 0;
  468                 _exitStatus = EXIT_SUCCESS;
  469             }
  470         }
  471         else {
  472             success = decodeInput(_cmdHave);
  473             initialized = 1;
  474         }
  475     }
  476     else {
  477         for (;;) {
  478             char    buf[sizeof(_haveUnitSpec)];
  479             int nbytes = getSpec("You have: ", buf, sizeof(buf));
  480 
  481             if (nbytes < 0)
  482                 break;
  483 
  484             if (nbytes > 0) {
  485                 success = decodeInput(buf);
  486 
  487                 if (success)
  488                     break;
  489             }
  490         }
  491     }
  492 
  493     return success;
  494 }
  495 
  496 
  497 static int
  498 decodeOutput(
  499     const char* const   buf)
  500 {
  501     int         success = 0;
  502     size_t  nbytes = strlen(buf);
  503 
  504     if (nbytes == 0) {
  505         _wantDefinition = 1;
  506         success = 1;
  507     }
  508     else {
  509         ut_free(_wantUnit);
  510 
  511         _wantDefinition = 0;
  512         _wantUnit = ut_parse(_unitSystem, buf, _encoding);
  513 
  514         if (_wantUnit == NULL) {
  515             errMsg("Don't recognize \"%s\"", buf);
  516         }
  517         else {
  518             success = 1;
  519         }
  520     }
  521 
  522     return success;
  523 }
  524 
  525 
  526 static int
  527 getOutputRequest(void)
  528 {
  529     int     success = 0;
  530 
  531     if (_cmdWant) {
  532         static int      initialized = 0;
  533 
  534         if (initialized) {
  535             success = 1;
  536         }
  537         else {
  538             (void)strncpy(_wantSpec, _cmdWant, sizeof(_wantSpec));
  539             _wantSpec[sizeof(_wantSpec)-1] = 0;
  540 
  541             success = decodeOutput(_wantSpec);
  542             initialized = 1;
  543         }
  544     }
  545     else {
  546         for (;;) {
  547             int nbytes = getSpec("You want: ", _wantSpec, sizeof(_wantSpec));
  548 
  549             if (nbytes < 0)
  550                 break;
  551 
  552             success = decodeOutput(_wantSpec);
  553 
  554             if (success)
  555                 break;
  556         }
  557     }
  558 
  559     return success;
  560 }
  561 
  562 
  563 static int
  564 handleRequest(void)
  565 {
  566     int     success = 0;
  567 
  568     if (getInputValue()) {
  569     if (getOutputRequest()) {
  570         if (_wantDefinition) {
  571                 char            buf[256];
  572                 ut_unit*        unit = ut_scale(_haveUnitAmount, _haveUnit);
  573                 int         nbytes = ut_format(unit, buf, sizeof(buf),
  574                         _formattingOptions);
  575 
  576                 if (nbytes >= sizeof(buf)) {
  577                     errMsg("Resulting unit specification is too long");
  578                 }
  579                 else if (nbytes >= 0) {
  580                     buf[nbytes] = 0;
  581 
  582                     (void)printf("    %s\n", buf);
  583                 }
  584 
  585                 ut_free(unit);
  586         }
  587         else if (!ut_are_convertible(_wantUnit, _haveUnit)) {
  588         errMsg("Units are not convertible");
  589         }
  590         else {
  591         cv_converter*   conv = ut_get_converter(_haveUnit, _wantUnit);
  592 
  593         if (conv == NULL) {
  594             errMsg("Couldn't get unit converter");
  595         }
  596         else {
  597                     char        haveExp[_POSIX_MAX_INPUT+1];
  598                     char        exp[_POSIX_MAX_INPUT+1];
  599                     char        whiteSpace[] = " \t\n\r\f\v\xa0";
  600             int         needsParens =
  601                         strpbrk(_wantSpec, whiteSpace) != NULL;
  602                     int         n;
  603 
  604             (void)printf(
  605             needsParens
  606                 ? "    %g %s = %g (%s)\n"
  607                 : "    %g %s = %g %s\n",
  608                         _haveUnitAmount,
  609             _haveUnitSpec,
  610             cv_convert_double(conv, _haveUnitAmount),
  611                         _wantSpec);
  612 
  613                     (void)sprintf(haveExp,
  614                         strpbrk(_haveUnitSpec, whiteSpace) ||
  615                                 strpbrk(_haveUnitSpec, "/")
  616                             ? "(x/(%s))"
  617                             : "(x/%s)",
  618                         _haveUnitSpec);
  619 
  620                     n = cv_get_expression(conv, exp, sizeof(exp), haveExp);
  621 
  622                     if (n >= 0)
  623                         (void)printf(
  624                             strpbrk(_wantSpec, whiteSpace) ||
  625                                     strpbrk(_wantSpec, "/")
  626                                 ?  "    x/(%s) = %*s\n"
  627                                 :  "    x/%s = %*s\n",
  628                         _wantSpec, n, exp);
  629 
  630                     cv_free(conv);
  631         }
  632         }
  633 
  634         success = 1;
  635     }
  636     }
  637 
  638     return success;
  639 }
  640 
  641 
  642 int
  643 main(
  644     const int       argc,
  645     char**  argv)
  646 {
  647     if (decodeCommandLine(argc, argv)) {
  648         if (ensureEncodingSet()) {
  649             if (readXmlDatabase()) {
  650                 while (handleRequest())
  651                     ; /* EMPTY */
  652             }
  653         }
  654     }
  655 
  656     return _exitStatus;
  657 }