3 //============================================================================
7 // Implementation of the various CDL utility member functions.
9 //============================================================================
10 //####COPYRIGHTBEGIN####
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
15 // This file is part of the eCos host tools.
17 // This program is free software; you can redistribute it and/or modify it
18 // under the terms of the GNU General Public License as published by the Free
19 // Software Foundation; either version 2 of the License, or (at your option)
22 // This program is distributed in the hope that it will be useful, but WITHOUT
23 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
24 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
27 // You should have received a copy of the GNU General Public License along with
28 // this program; if not, write to the Free Software Foundation, Inc.,
29 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 // ----------------------------------------------------------------------------
33 //####COPYRIGHTEND####
34 //============================================================================
35 //#####DESCRIPTIONBEGIN####
42 //####DESCRIPTIONEND####
43 //============================================================================
48 // ----------------------------------------------------------------------------
50 #include "cdlconfig.h"
52 // Get the infrastructure types, assertions, tracing and similar
54 #include <cyg/infra/cyg_ass.h>
55 #include <cyg/infra/cyg_trac.h>
57 // <cdlcore.hxx> defines everything implemented in this module.
58 // It implicitly supplies <string>, <vector> and <map> because
59 // the class definitions rely on these headers.
60 #include <cdlcore.hxx>
62 // For access to the isdigit(), isupper(), tolower(), ... functions
65 // For access to sprintf(), specifically double to string conversions.
68 // For access to strtod()
71 // strtod() involves errno...
74 // For access to fmod()
77 // For access to DBL_DIG
82 //{{{ Cdl::is_valid_xxx()
84 // ---------------------------------------------------------------------------
87 Cdl::is_valid_value_flavor(CdlValueFlavor data)
92 case CdlValueFlavor_None :
93 case CdlValueFlavor_Bool :
94 case CdlValueFlavor_BoolData :
95 case CdlValueFlavor_Data :
107 Cdl::is_valid_value_source(CdlValueSource data)
112 case CdlValueSource_Default :
113 case CdlValueSource_User :
114 case CdlValueSource_Wizard :
115 case CdlValueSource_Inferred :
126 // ----------------------------------------------------------------------------
127 // For now CDL names are restricted to what is acceptable to the C
128 // preprocessor. This may cause problems in future, e.g. i18n.
131 Cdl::is_valid_cdl_name(const std::string &name)
133 CYG_REPORT_FUNCNAMETYPE("Cdl::is_valid_cdl_name", "result %d");
135 bool result = is_valid_c_preprocessor_symbol(name);
137 CYG_REPORT_RETVAL(result);
142 Cdl::is_valid_c_preprocessor_symbol(const std::string &symbol)
144 CYG_REPORT_FUNCNAMETYPE("Cdl::is_valid_c_preprocessor_symbol", "result %d");
150 // A valid preprocessor symbol should begin with either an underscore
151 // or a letter. It should then be followed by some number of underscores,
152 // letters, or digits.
154 // In some locales isalpha() may succeed for characters which are not
155 // legal for C preprocessor symbols. Instead ASCII is assumed here.
156 if (('_' != symbol[0]) &&
157 !(('a' <= symbol[0]) && (symbol[0] <= 'z')) &&
158 !(('A' <= symbol[0]) && (symbol[0] <= 'Z'))) {
162 for (unsigned int i = 1; i < symbol.size(); i++) {
163 if (('_' != symbol[i]) &&
164 !(('a' <= symbol[i]) && (symbol[i] <= 'z')) &&
165 !(('A' <= symbol[i]) && (symbol[i] <= 'Z')) &&
166 !(('0' <= symbol[i]) && (symbol[i] <= '9'))) {
175 CYG_REPORT_RETVAL(result);
180 //{{{ Cdl::xxx_to_yyy() - strings, ints, doubles, ...
182 // ---------------------------------------------------------------------------
183 // Conversion routines between strings, integers, doubles, bools, ...
185 // Conversions to/from integers are complicated somewhat because the
186 // data type in question is cdl_int. In the initial implementation this
187 // is 64 bits. In the long term it will be arbitrary precision and
188 // the conversion routines will need to be reimplemented.
190 // ASCII rather than EBCDIC is assumed.
192 // Some of the routines may fail, e.g. string to integer conversions.
193 // Others are guaranteed to succeed.
195 //{{{ string_to_integer()
197 // ----------------------------------------------------------------------------
199 Cdl::string_to_integer(std::string data, cdl_int &target)
201 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_integer", "success %d");
203 bool negative = false;
204 bool seen_plus = false;
205 bool seen_minus = false;
207 // Life is a bit easier if I can check for '\0'
208 const char *ptr = data.c_str();
210 // Not essential but harmless.
211 while (isspace(*ptr))
217 CYG_REPORT_RETVAL(false);
227 CYG_REPORT_RETVAL(false);
237 // This happens sufficiently often to be worth a special case.
238 if ('\0' == ptr[1]) {
240 CYG_REPORT_RETVAL(true);
243 // Hex is always worth supporting.
244 if (('x' == ptr[1]) || ('X' == ptr[1])) {
246 if (!isxdigit(*ptr)) {
247 CYG_REPORT_RETVAL(false);
250 while (isxdigit(*ptr)) {
251 cdl_int new_acc = acc * 16;
253 new_acc += (*ptr - '0');
254 } else if (('a' <= *ptr) && (*ptr <= 'f')) {
255 new_acc += (*ptr + 10 - 'a');
256 } else if (('A' <= *ptr) && (*ptr <= 'F')) {
257 new_acc += (*ptr + 10 - 'A');
259 CYG_FAIL("this platform's implementation of isxdigit() is broken");
262 CYG_REPORT_RETVAL(false);
269 CYG_REPORT_RETVAL(false);
273 cdl_int new_acc = 0 - acc;
275 CYG_REPORT_RETVAL(false);
282 CYG_REPORT_RETVAL(true);
286 // Octal? Oh well, might as well be complete.
287 if (('0' <= ptr[1]) && (ptr[1] <= '7')) {
290 cdl_int new_acc = 8 * acc;
291 new_acc += (*ptr - '0');
293 CYG_REPORT_RETVAL(false);
298 } while (('0' <= *ptr) && (*ptr <= '7'));
300 CYG_REPORT_RETVAL(false);
304 cdl_int new_acc = 0 - acc;
306 CYG_REPORT_RETVAL(false);
314 CYG_REPORT_RETVAL(true);
318 // Drop through for the case of a decimal.
321 while(isdigit(*ptr)) {
322 cdl_int new_acc = 10 * acc;
323 new_acc += (*ptr - '0');
325 CYG_REPORT_RETVAL(false);
332 CYG_REPORT_RETVAL(false);
336 cdl_int new_acc = 0 - acc;
338 CYG_REPORT_RETVAL(false);
345 CYG_REPORT_RETVAL(true);
350 //{{{ string_to_double()
352 // ----------------------------------------------------------------------------
353 // There is no point in doing this the hard way, just use standard
356 // There is an obvious question as to how much precision can get lost
357 // doing the conversion to a string. In practice this should not matter
358 // too much, since the expression handling code generally keeps the
359 // original double precision lying around to be re-used. However it may
360 // be desirable to keep the libcdl behaviour in synch with Tcl's
361 // tcl_precision variable.
364 Cdl::string_to_double(std::string value, double &target)
366 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_double", "success %d");
369 const char *start_ptr = value.c_str();
371 int old_errno = errno;
374 double conv = strtod(start_ptr, &end_ptr);
376 CYG_ASSERT(ERANGE == errno, "standard-compliant C library");
378 } else if ('\0' != *end_ptr) {
386 CYG_REPORT_RETVAL(result);
391 //{{{ string_to_bool()
393 // ----------------------------------------------------------------------------
394 // Conversions to and from bools. The only real issue here is exactly
395 // what strings should be accepted as synonyms for true and false.
396 // It is not actually clear that these functions are useful.
398 Cdl::string_to_bool(std::string data, bool &target)
400 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_bool", "success %d");
402 // Arguably there should be a precondition ( "" != data )
406 if (( data == "1" ) || (data == "true") ||
407 ( data == "True") || (data == "TRUE") ) {
410 } else if ((data == "0" ) || (data == "false") ||
411 (data == "False") || (data == "FALSE") ) {
416 CYG_REPORT_RETVAL(result);
422 //{{{ integer_to_string()
424 // ----------------------------------------------------------------------------
427 Cdl::integer_to_string(cdl_int value, CdlValueFormat format)
430 Cdl::integer_to_string(value, result, format);
435 Cdl::integer_to_string(cdl_int value, std::string &target, CdlValueFormat format)
437 CYG_REPORT_FUNCNAME("Cdl::integer_to_string");
438 CYG_REPORT_FUNCARG2XV((long) value, format);
440 // Optimise this special case.
442 if (CdlValueFormat_Hex == format) {
447 CYG_REPORT_RETVAL(true);
451 // A local buffer to construct partial strings. This avoids
452 // unnecessary std::string reallocation.
453 // 64 bits and three bits at a time for octal numbers gives 21 digits,
454 // plus spares for the leading '0' and the terminator.
456 char *local_ptr = &(local_buf[23]);
459 if (CdlValueFormat_Hex == format) {
461 // Output the data as 0x... with either 8 or 16 digits,
462 // depending on the size.
464 for (i = 0; i < 8; i++) {
465 int tmp = (int) (value & 0x0F);
468 *local_ptr-- = '0' + tmp;
470 *local_ptr-- = 'A' + (tmp - 10);
473 // Beware of right shifts that preserve the sign bit.
475 int tmp1 = (value & 0x0FFFF);
476 int tmp2 = ((value >> 16) & 0x0FFFF);
477 value = (tmp2 << 16) + tmp1;
480 for (i = 0; i < 8; i++) {
481 int tmp = (int) (value & 0x0F);
484 *local_ptr-- = '0' + tmp;
486 *local_ptr-- = 'A' + (tmp - 10);
492 target = std::string(local_ptr);
494 } else if (CdlValueFormat_Octal == format) {
496 // Simply output the data three bits at a time, do not worry about any
497 // particular width restrictions. However it is necessary to worry
499 cdl_int mask = 0x1FFFFFFF;
500 mask = (mask << 16) | 0x0FFFF;
501 mask = (mask << 16) | 0x0FFFF;
505 int tmp = value & 0x07;
506 value = (value >> 3) & mask;
507 *local_ptr-- = '0' + tmp;
510 target = std::string(local_ptr);
513 // A simple decimal number
514 // Switch to positive arithmetic.
515 bool negative = false;
519 // Only MININT cannot be converted using the above line
521 target = "-9223372036854775808";
522 CYG_REPORT_RETVAL(true);
528 int rem = (int) (value % 10);
530 *local_ptr-- = '0' + rem;
536 target = std::string(local_ptr);
544 //{{{ double_to_string()
546 // ----------------------------------------------------------------------------
549 Cdl::double_to_string(double value, CdlValueFormat format)
552 Cdl::double_to_string(value, result, format);
557 Cdl::double_to_string(double value, std::string &result, CdlValueFormat format)
559 CYG_REPORT_FUNCNAME("Cdl::double_to_String");
561 char buf[256]; // This should be plenty :-)
562 sprintf(buf, "%.*G", DBL_DIG, value);
565 CYG_UNUSED_PARAM(CdlValueFormat, format);
570 //{{{ bool_to_string()
572 // ----------------------------------------------------------------------------
573 // Should the string results be 1/0 or true/false? Not that
574 // it really matters. The testcase in cdl1.cxx expects 1/0.
576 Cdl::bool_to_string(bool value)
579 Cdl::bool_to_string(value, result);
584 Cdl::bool_to_string(bool value, std::string &target)
586 CYG_REPORT_FUNCNAME("Cdl::bool_to_string");
587 CYG_REPORT_FUNCARG1( "value arg %ld", (long) value);
599 //{{{ integer_to_double()
601 // ----------------------------------------------------------------------------
602 // Currently integer to double cannot fail, although there may well be loss
603 // of accurary. Eventually cdl_int may be an arbitrary precision integer
604 // in which case conversion to double is not guaranteed.
606 Cdl::integer_to_double(cdl_int value)
608 CYG_REPORT_FUNCNAME("Cdl::integer_to_double");
610 double result = (double) value;
617 Cdl::integer_to_double(cdl_int value, double &target)
619 CYG_REPORT_FUNCNAME("Cdl::integer_to_double");
621 target = (double) value;
627 //{{{ double_to_integer()
629 // Conversion from double to integer is only allowed if there is no loss
630 // of data. modf() is useful here
632 Cdl::double_to_integer(double value, cdl_int &target)
634 CYG_REPORT_FUNCNAMETYPE("Cdl::double_to_integer", "result %d");
641 frac = modf(value, &integral);
643 // Looking good, but integral may still be too big.
644 cdl_int tmp = (cdl_int) integral;
646 // No fraction, no loss of data, everything looking good
652 CYG_REPORT_RETVAL(result);
659 //{{{ Cdl::xxx_to_yyy() - CDL-specific data types
661 // ----------------------------------------------------------------------------
662 // Conversions between strings and flavors.
666 CdlValueFlavor flavor;
667 } valid_flavors[] = {
668 { "none", CdlValueFlavor_None },
669 { "bool", CdlValueFlavor_Bool },
670 { "booldata", CdlValueFlavor_BoolData },
671 { "data", CdlValueFlavor_Data },
672 { 0, CdlValueFlavor_Invalid }
676 Cdl::string_to_flavor(std::string name, CdlValueFlavor &target)
678 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_flavor", "success %d");
682 // First convert the argument to lower case. Arguably this is incorrect,
683 // Tcl is a case-sensitive language, but the conversion is unlikely ever
685 for (std::string::iterator str_i = name.begin(); str_i != name.end(); str_i++) {
686 if (isupper(*str_i)) {
687 *str_i = tolower(*str_i);
691 // Now look for a match in the table.
694 const char *c_str = name.c_str();
695 int len = strlen(c_str);
697 for (i = 0; 0 != valid_flavors[i].name; i++) {
698 if (0 == strncmp(c_str, valid_flavors[i].name, len)) {
699 // Check for an ambiguous string match.
700 // This cannot actually happen with the current flavor names.
709 target = valid_flavors[match].flavor;
712 CYG_REPORT_RETVAL(result);
717 Cdl::flavor_to_string(CdlValueFlavor flavor, std::string &target)
719 CYG_REPORT_FUNCNAMETYPE("Cdl::flavor_to_string", "success %d");
723 for (int i = 0; 0 != valid_flavors[i].name; i++) {
724 if (flavor == valid_flavors[i].flavor) {
725 target = valid_flavors[i].name;
731 CYG_REPORT_RETVAL(result);
735 // ----------------------------------------------------------------------------
736 // Similar support for value sources.
740 CdlValueSource source;
741 } valid_sources[] = {
742 { "default", CdlValueSource_Default },
743 { "inferred", CdlValueSource_Inferred },
744 { "wizard", CdlValueSource_Wizard },
745 { "user", CdlValueSource_User },
746 { 0, CdlValueSource_Invalid }
750 Cdl::string_to_source(std::string name, CdlValueSource &target)
752 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_source", "success %d");
756 // First convert the argument to lower case. Arguably this is incorrect,
757 // Tcl is a case-sensitive language, but the conversion is unlikely ever
759 for (std::string::iterator str_i = name.begin(); str_i != name.end(); str_i++) {
760 if (isupper(*str_i)) {
761 *str_i = tolower(*str_i);
765 // Now look for a match in the table.
768 const char *c_str = name.c_str();
769 int len = strlen(c_str);
771 for (i = 0; 0 != valid_sources[i].name; i++) {
772 if (0 == strncmp(c_str, valid_sources[i].name, len)) {
773 // Check for an ambiguous string match.
774 // This cannot actually happen with the current source names.
783 target = valid_sources[match].source;
786 CYG_REPORT_RETVAL(result);
791 Cdl::source_to_string(CdlValueSource source, std::string &target)
793 CYG_REPORT_FUNCNAMETYPE("Cdl::source_to_string", "success %d");
797 for (int i = 0; 0 != valid_sources[i].name; i++) {
798 if (source == valid_sources[i].source) {
799 target = valid_sources[i].name;
805 CYG_REPORT_RETVAL(result);
810 //{{{ Cdl::get_library_version()
812 // ----------------------------------------------------------------------------
813 // The version of the library actually lives inside configure.in. It gets
814 // exported into cdlconfig.h
816 Cdl::get_library_version(void)
818 return std::string(CYGNUM_LIBCDL_VERSION);
822 //{{{ Cdl::set_interactive()
824 // ----------------------------------------------------------------------------
825 // Some CDL scripts and some bits of the library may want to adapt depending
826 // on whether or not the application is running fully interactively or in
827 // batch mode. The primary distinction is that a batch program should never
828 // attempt to obtain user input, whether via Tk widgets or by other means.
830 bool Cdl::interactive = false;
833 Cdl::set_interactive(bool value)
835 CYG_REPORT_FUNCNAME("Cdl::set_interactive");
836 CYG_REPORT_FUNCARG1D(value);
842 Cdl::is_interactive(void)
844 CYG_REPORT_FUNCNAMETYPE("Cdl::is_interactive", "interactive %d");
845 CYG_REPORT_RETVAL(interactive);
850 //{{{ version support()
852 // ----------------------------------------------------------------------------
853 // Packages may need to impose constraints on which versions of other
854 // packages they can coexist with. This requires some way of achieving
855 // a partial ordering of version numbers. Unfortunately there are many
856 // different ways of specifying a version number, and we cannot impose
857 // a single model on all third party package developers. Instead this
858 // routine performs some semi-intelligent comparisons of two version
859 // strings which should work in the vast majority of cases.
861 // The return value is as per strcmp(), -1 if the first entry is
862 // smaller (i.e. the more recent and hence hopefully the first in
863 // a list), +1 if the second entry is smaller, 0 if the two are
866 // There is a big ambiguity between "derived" versions and "experimental"
867 // versions. Something like v0.3beta is experimental, i.e. it is older
868 // than the full release v0.3. On the other hand v0.3.p1 is a patched
869 // version of v0.3 and hence newer. This code uses the presence or otherwise
870 // of a separator to decide between the two cases.
872 // A utility routine which checks whether or not a character counts
873 // as a separator. Currently the characters . - and _ are all accepted
874 // as field separators.
876 // Arguably - should not be accepted as a separator. Instead if it preceeds
877 // a digit it could be interpreted as part of a prerelease number.
882 return ('.' == ch) || ('-' == ch) || ('_' == ch);
886 Cdl::compare_versions(std::string arg1, std::string arg2)
888 CYG_REPORT_FUNCNAMETYPE("Cdl::compare_versions", "result %d");
891 CYG_REPORT_RETVAL(0);
895 // The version number "current" is special, it always indicates the most
896 // recent version e.g. as checked out from a CVS repository.
897 if ("current" == arg1) {
898 CYG_REPORT_RETVAL(-1);
901 if ("current" == arg2) {
902 CYG_REPORT_RETVAL(1);
906 const char *ptr1 = arg1.c_str();
907 const char *ptr2 = arg2.c_str();
911 // If both strings start with 'v' or 'V', skip this. A minor variation in
912 // case at the start of a string should be ignored.
913 if ((('v' == *ptr1) || ('V' == *ptr1)) &&
914 (('v' == *ptr2) || ('V' == *ptr2))) {
919 // Now process the rest of the version string, one unit at a time.
922 if (('\0' == *ptr1) && ('\0' == *ptr2)) {
923 // Both strings have terminated at the same time. There
924 // may have been some spurious leading zeroes in numbers,
925 // or irrelevant differences in the separators.
926 CYG_REPORT_RETVAL(0);
931 // The first string has ended first. If the second string currently
932 // points at a separator then arg2 is a derived version, e.g.
933 // v0.3.p1, and hence newer. Otherwise arg2 is an experimental
934 // version v0.3beta and hence older.
935 if (is_separator(*ptr2)) {
936 CYG_REPORT_RETVAL(1);
939 CYG_REPORT_RETVAL(-1);
945 // As per the previous test.
946 if (is_separator(*ptr1)) {
947 CYG_REPORT_RETVAL(-1);
950 CYG_REPORT_RETVAL(1);
955 // If both strings currently point at numerical data, do a conversion and
956 // a numerical comparison.
957 if (isdigit(*ptr1) && isdigit(*ptr2)) {
960 // Strictly speaking there should be some overflow tests here, but it
961 // is not worth the trouble.
963 num1 = (10 * num1) + (*ptr1++ - '0');
964 } while(isdigit(*ptr1));
966 num2 = (10 * num2) + (*ptr2++ - '0');
967 } while(isdigit(*ptr2));
968 // v2.0 is newer than v1.0
970 CYG_REPORT_RETVAL(1);
972 } else if (num1 > num2) {
973 CYG_REPORT_RETVAL(-1);
980 // Non-numerical data. If the two characters are the same then
981 // move on. Note: this has to happen after numerical conversions
982 // to distinguish v10.0 and v1.0
983 if (*ptr1 == *ptr2) {
988 // If both strings are currently at a separator then move on. All
989 // separators can be used interchangeably.
990 if (is_separator(*ptr1) && is_separator(*ptr2)) {
995 // If only one string is at a separator, special action
996 // is needed. v1.1alpha is interpreted as earlier than
997 // v1.1, but v1.1.3 is a later release.
998 if (is_separator(*ptr1)) {
1000 } else if (is_separator(*ptr2)) {
1004 // Two different characters, e.g. v1.0alpha vs. v1.0beta
1005 if (*ptr1 < *ptr2) {
1006 CYG_REPORT_RETVAL(1);
1009 CYG_REPORT_RETVAL(-1);
1017 // ----------------------------------------------------------------------------
1018 // Given a version string, extract major, minor and release numbers.
1019 // Some or all of these may be absent. Basically the code just
1020 // iterates through the string looking for sequences of numbers.
1023 version_extract_number(const std::string &version, unsigned int &index, std::string &result)
1025 CYG_REPORT_FUNCNAME("version_extract_number");
1027 // The calling code is expected to supply a sensible default.
1028 // Search for a digit
1029 for ( ; index < version.size(); index++) {
1030 if (isdigit(version[index])) {
1034 if (index != version.size()) {
1036 if ((index > 0) && ('-' == version[index-1])) {
1040 result += version[index++];
1041 } while ((index < version.size()) && isdigit(version[index]));
1044 CYG_REPORT_RETURN();
1048 Cdl::split_version_string(const std::string &version, std::string &major, std::string &minor, std::string &release)
1050 CYG_REPORT_FUNCNAME("CdlMisc::split_version_string");
1052 unsigned int index = 0;
1053 version_extract_number(version, index, major);
1054 version_extract_number(version, index, minor);
1055 version_extract_number(version, index, release);
1057 CYG_REPORT_RETURN();
1061 //{{{ Cdl::get_short_form()
1063 // ----------------------------------------------------------------------------
1064 // It is occasionally useful to take a full CDL name such as CYgpkg_KERNEL
1065 // and turn it into a short form such as "kernel". This involves discarding
1066 // everything up to and including the first underscore, then lowercasing
1067 // all subsequent characters.
1069 Cdl::get_short_form(const std::string &original)
1071 CYG_REPORT_FUNCNAME("CdlMisc::get_short_form");
1073 std::string result = "";
1074 unsigned int size = original.size();
1076 for (i = 0; i < size; i++) {
1077 if ('_' == original[i]) {
1083 // Either at end of string, or just past the first underscore
1084 for ( ; i < size; i++) {
1085 if (isupper(original[i])) {
1086 result += tolower(original[i]);
1088 result += original[i];
1092 CYG_REPORT_RETURN();