Mercurial > ecos
comparison host/libcdl/cdlmisc.cxx @ 76:435cced73e2f ecos-v1_3_1-release
eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
| author | jlarmour |
|---|---|
| date | Tue, 28 Mar 2000 14:10:45 +0000 |
| parents | |
| children | 6736c52df507 |
comparison
equal
deleted
inserted
replaced
| 75:41bf073c0c32 | 76:435cced73e2f |
|---|---|
| 1 //{{{ Banner | |
| 2 | |
| 3 //============================================================================ | |
| 4 // | |
| 5 // cdlmisc.cxx | |
| 6 // | |
| 7 // Implementation of the various CDL utility member functions. | |
| 8 // | |
| 9 //============================================================================ | |
| 10 //####COPYRIGHTBEGIN#### | |
| 11 // | |
| 12 // ---------------------------------------------------------------------------- | |
| 13 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc. | |
| 14 // | |
| 15 // This file is part of the eCos host tools. | |
| 16 // | |
| 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) | |
| 20 // any later version. | |
| 21 // | |
| 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 | |
| 25 // more details. | |
| 26 // | |
| 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. | |
| 30 // | |
| 31 // ---------------------------------------------------------------------------- | |
| 32 // | |
| 33 //####COPYRIGHTEND#### | |
| 34 //============================================================================ | |
| 35 //#####DESCRIPTIONBEGIN#### | |
| 36 // | |
| 37 // Author(s): bartv | |
| 38 // Contact(s): bartv | |
| 39 // Date: 1998/03/04 | |
| 40 // Version: 0.01 | |
| 41 // | |
| 42 //####DESCRIPTIONEND#### | |
| 43 //============================================================================ | |
| 44 | |
| 45 //}}} | |
| 46 //{{{ #include's | |
| 47 | |
| 48 // ---------------------------------------------------------------------------- | |
| 49 #include "cdlconfig.h" | |
| 50 | |
| 51 // Get the infrastructure types, assertions, tracing and similar | |
| 52 // facilities. | |
| 53 #include <cyg/infra/cyg_ass.h> | |
| 54 #include <cyg/infra/cyg_trac.h> | |
| 55 | |
| 56 // <cdlcore.hxx> defines everything implemented in this module. | |
| 57 // It implicitly supplies <string>, <vector> and <map> because | |
| 58 // the class definitions rely on these headers. | |
| 59 #include <cdlcore.hxx> | |
| 60 | |
| 61 // For access to the isdigit(), isupper(), tolower(), ... functions | |
| 62 #include <cctype> | |
| 63 | |
| 64 // For access to sprintf(), specifically double to string conversions. | |
| 65 #include <cstdio> | |
| 66 | |
| 67 // For access to strtod() | |
| 68 #include <cstdlib> | |
| 69 | |
| 70 // strtod() involves errno... | |
| 71 #include <cerrno> | |
| 72 | |
| 73 // For access to fmod() | |
| 74 #include <cmath> | |
| 75 | |
| 76 // For access to DBL_DIG | |
| 77 #include <cfloat> | |
| 78 | |
| 79 //}}} | |
| 80 | |
| 81 //{{{ Cdl::is_valid_xxx() | |
| 82 | |
| 83 // --------------------------------------------------------------------------- | |
| 84 | |
| 85 bool | |
| 86 Cdl::is_valid_value_flavor(CdlValueFlavor data) | |
| 87 { | |
| 88 bool result = false; | |
| 89 | |
| 90 switch(data) { | |
| 91 case CdlValueFlavor_None : | |
| 92 case CdlValueFlavor_Bool : | |
| 93 case CdlValueFlavor_BoolData : | |
| 94 case CdlValueFlavor_Data : | |
| 95 result = true; | |
| 96 break; | |
| 97 | |
| 98 default: | |
| 99 break; | |
| 100 } | |
| 101 | |
| 102 return result; | |
| 103 } | |
| 104 | |
| 105 bool | |
| 106 Cdl::is_valid_value_source(CdlValueSource data) | |
| 107 { | |
| 108 bool result = false; | |
| 109 | |
| 110 switch(data) { | |
| 111 case CdlValueSource_Default : | |
| 112 case CdlValueSource_User : | |
| 113 case CdlValueSource_Wizard : | |
| 114 case CdlValueSource_Inferred : | |
| 115 result = true; | |
| 116 break; | |
| 117 | |
| 118 default: | |
| 119 break; | |
| 120 } | |
| 121 | |
| 122 return result; | |
| 123 } | |
| 124 | |
| 125 // ---------------------------------------------------------------------------- | |
| 126 // For now CDL names are restricted to what is acceptable to the C | |
| 127 // preprocessor. This may cause problems in future, e.g. i18n. | |
| 128 | |
| 129 bool | |
| 130 Cdl::is_valid_cdl_name(const std::string& name) | |
| 131 { | |
| 132 CYG_REPORT_FUNCNAMETYPE("Cdl::is_valid_cdl_name", "result %d"); | |
| 133 | |
| 134 bool result = is_valid_c_preprocessor_symbol(name); | |
| 135 | |
| 136 CYG_REPORT_RETVAL(result); | |
| 137 return result; | |
| 138 } | |
| 139 | |
| 140 bool | |
| 141 Cdl::is_valid_c_preprocessor_symbol(const std::string& symbol) | |
| 142 { | |
| 143 CYG_REPORT_FUNCNAMETYPE("Cdl::is_valid_c_preprocessor_symbol", "result %d"); | |
| 144 | |
| 145 bool result = true; | |
| 146 if ("" == symbol) { | |
| 147 result = false; | |
| 148 } else { | |
| 149 // A valid preprocessor symbol should begin with either an underscore | |
| 150 // or a letter. It should then be followed by some number of underscores, | |
| 151 // letters, or digits. | |
| 152 // | |
| 153 // In some locales isalpha() may succeed for characters which are not | |
| 154 // legal for C preprocessor symbols. Instead ASCII is assumed here. | |
| 155 if (('_' != symbol[0]) && | |
| 156 !(('a' <= symbol[0]) && (symbol[0] <= 'z')) && | |
| 157 !(('A' <= symbol[0]) && (symbol[0] <= 'Z'))) { | |
| 158 | |
| 159 result = false; | |
| 160 } else { | |
| 161 for (unsigned int i = 1; i < symbol.size(); i++) { | |
| 162 if (('_' != symbol[i]) && | |
| 163 !(('a' <= symbol[i]) && (symbol[i] <= 'z')) && | |
| 164 !(('A' <= symbol[i]) && (symbol[i] <= 'Z')) && | |
| 165 !(('0' <= symbol[i]) && (symbol[i] <= '9'))) { | |
| 166 | |
| 167 result = false; | |
| 168 break; | |
| 169 } | |
| 170 } | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 CYG_REPORT_RETVAL(result); | |
| 175 return result; | |
| 176 } | |
| 177 | |
| 178 //}}} | |
| 179 //{{{ Cdl::xxx_to_yyy() - strings, ints, doubles, ... | |
| 180 | |
| 181 // --------------------------------------------------------------------------- | |
| 182 // Conversion routines between strings, integers, doubles, bools, ... | |
| 183 // | |
| 184 // Conversions to/from integers are complicated somewhat because the | |
| 185 // data type in question is cdl_int. In the initial implementation this | |
| 186 // is 64 bits. In the long term it will be arbitrary precision and | |
| 187 // the conversion routines will need to be reimplemented. | |
| 188 // | |
| 189 // ASCII rather than EBCDIC is assumed. | |
| 190 // | |
| 191 // Some of the routines may fail, e.g. string to integer conversions. | |
| 192 // Others are guaranteed to succeed. | |
| 193 | |
| 194 //{{{ string_to_integer() | |
| 195 | |
| 196 // ---------------------------------------------------------------------------- | |
| 197 bool | |
| 198 Cdl::string_to_integer(std::string data, cdl_int& target) | |
| 199 { | |
| 200 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_integer", "success %d"); | |
| 201 | |
| 202 bool negative = false; | |
| 203 // Life is a bit easier if I can check for '\0' | |
| 204 const char* ptr = data.c_str(); | |
| 205 | |
| 206 // Not essential but harmless. | |
| 207 while (isspace(*ptr)) | |
| 208 ptr++; | |
| 209 | |
| 210 if ('-' == *ptr) { | |
| 211 negative = true; | |
| 212 ptr++; | |
| 213 } | |
| 214 | |
| 215 cdl_int acc = 0; | |
| 216 if ('0' == *ptr) { | |
| 217 // This happens sufficiently often to be worth a special case. | |
| 218 if ('\0' == ptr[1]) { | |
| 219 target = 0; | |
| 220 CYG_REPORT_RETVAL(true); | |
| 221 return true; | |
| 222 } | |
| 223 // Hex is always worth supporting. | |
| 224 if (('x' == ptr[1]) || ('X' == ptr[1])) { | |
| 225 ptr++; ptr++; | |
| 226 if (!isxdigit(*ptr)) { | |
| 227 CYG_REPORT_RETVAL(false); | |
| 228 return false; | |
| 229 } | |
| 230 while (isxdigit(*ptr)) { | |
| 231 cdl_int new_acc = acc * 16; | |
| 232 if (isdigit(*ptr)) { | |
| 233 new_acc += (*ptr - '0'); | |
| 234 } else if (('a' <= *ptr) && (*ptr <= 'f')) { | |
| 235 new_acc += (*ptr + 10 - 'a'); | |
| 236 } else if (('A' <= *ptr) && (*ptr <= 'F')) { | |
| 237 new_acc += (*ptr + 10 - 'A'); | |
| 238 } else { | |
| 239 CYG_FAIL("this platform's implementation of isxdigit() is broken"); | |
| 240 } | |
| 241 if (new_acc < acc) { | |
| 242 CYG_REPORT_RETVAL(false); | |
| 243 return false; | |
| 244 } | |
| 245 acc = new_acc; | |
| 246 ptr++; | |
| 247 } | |
| 248 if ('\0' != *ptr) { | |
| 249 CYG_REPORT_RETVAL(false); | |
| 250 return false; | |
| 251 } | |
| 252 if (negative) { | |
| 253 cdl_int new_acc = 0 - acc; | |
| 254 if (new_acc > 0) { | |
| 255 CYG_REPORT_RETVAL(false); | |
| 256 return false; | |
| 257 } else { | |
| 258 acc = new_acc; | |
| 259 } | |
| 260 } | |
| 261 target = acc; | |
| 262 CYG_REPORT_RETVAL(true); | |
| 263 return true; | |
| 264 } | |
| 265 | |
| 266 // Octal? Oh well, might as well be complete. | |
| 267 if (('0' <= ptr[1]) && (ptr[1] <= '7')) { | |
| 268 ptr++; | |
| 269 do { | |
| 270 cdl_int new_acc = 8 * acc; | |
| 271 new_acc += (*ptr - '0'); | |
| 272 if (new_acc < acc) { | |
| 273 CYG_REPORT_RETVAL(false); | |
| 274 return false; | |
| 275 } | |
| 276 acc = new_acc; | |
| 277 ptr++; | |
| 278 } while (('0' <= *ptr) && (*ptr <= '7')); | |
| 279 if ('\0' != *ptr) { | |
| 280 CYG_REPORT_RETVAL(false); | |
| 281 return false; | |
| 282 } | |
| 283 if (negative) { | |
| 284 cdl_int new_acc = 0 - acc; | |
| 285 if (new_acc > 0) { | |
| 286 CYG_REPORT_RETVAL(false); | |
| 287 return false; | |
| 288 } | |
| 289 else { | |
| 290 acc = new_acc; | |
| 291 } | |
| 292 } | |
| 293 target = acc; | |
| 294 CYG_REPORT_RETVAL(true); | |
| 295 return true; | |
| 296 } | |
| 297 | |
| 298 // Drop through for the case of a decimal. | |
| 299 } | |
| 300 | |
| 301 while(isdigit(*ptr)) { | |
| 302 cdl_int new_acc = 10 * acc; | |
| 303 new_acc += (*ptr - '0'); | |
| 304 if (new_acc < acc) { | |
| 305 CYG_REPORT_RETVAL(false); | |
| 306 return false; | |
| 307 } | |
| 308 acc = new_acc; | |
| 309 ptr++; | |
| 310 } | |
| 311 if ('\0' != *ptr) { | |
| 312 CYG_REPORT_RETVAL(false); | |
| 313 return false; | |
| 314 } | |
| 315 if (negative) { | |
| 316 cdl_int new_acc = 0 - acc; | |
| 317 if (new_acc > 0) { | |
| 318 CYG_REPORT_RETVAL(false); | |
| 319 return false; | |
| 320 } else { | |
| 321 acc = new_acc; | |
| 322 } | |
| 323 } | |
| 324 target = acc; | |
| 325 CYG_REPORT_RETVAL(true); | |
| 326 return true; | |
| 327 } | |
| 328 | |
| 329 //}}} | |
| 330 //{{{ string_to_double() | |
| 331 | |
| 332 // ---------------------------------------------------------------------------- | |
| 333 // There is no point in doing this the hard way, just use standard | |
| 334 // library calls. | |
| 335 // | |
| 336 // There is an obvious question as to how much precision can get lost | |
| 337 // doing the conversion to a string. In practice this should not matter | |
| 338 // too much, since the expression handling code generally keeps the | |
| 339 // original double precision lying around to be re-used. However it may | |
| 340 // be desirable to keep the libcdl behaviour in synch with Tcl's | |
| 341 // tcl_precision variable. | |
| 342 | |
| 343 bool | |
| 344 Cdl::string_to_double(std::string value, double& target) | |
| 345 { | |
| 346 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_double", "success %d"); | |
| 347 | |
| 348 bool result = true; | |
| 349 const char* start_ptr = value.c_str(); | |
| 350 char* end_ptr; | |
| 351 int old_errno = errno; | |
| 352 | |
| 353 errno = 0; | |
| 354 double conv = strtod(start_ptr, &end_ptr); | |
| 355 if (0 != errno) { | |
| 356 CYG_ASSERT(ERANGE == errno, "standard-compliant C library"); | |
| 357 result = false; | |
| 358 } else if ('\0' != *end_ptr) { | |
| 359 result = false; | |
| 360 } else { | |
| 361 target = conv; | |
| 362 result = true; | |
| 363 } | |
| 364 | |
| 365 errno = old_errno; | |
| 366 CYG_REPORT_RETVAL(result); | |
| 367 return result; | |
| 368 } | |
| 369 | |
| 370 //}}} | |
| 371 //{{{ string_to_bool() | |
| 372 | |
| 373 // ---------------------------------------------------------------------------- | |
| 374 // Conversions to and from bools. The only real issue here is exactly | |
| 375 // what strings should be accepted as synonyms for true and false. | |
| 376 // It is not actually clear that these functions are useful. | |
| 377 bool | |
| 378 Cdl::string_to_bool(std::string data, bool& target) | |
| 379 { | |
| 380 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_bool", "success %d"); | |
| 381 | |
| 382 // Arguably there should be a precondition ( "" != data ) | |
| 383 bool result = false; | |
| 384 | |
| 385 // What is truth ? | |
| 386 if (( data == "1" ) || (data == "true") || | |
| 387 ( data == "True") || (data == "TRUE") ) { | |
| 388 result = true; | |
| 389 target = true; | |
| 390 } else if ((data == "0" ) || (data == "false") || | |
| 391 (data == "False") || (data == "FALSE") ) { | |
| 392 result = true; | |
| 393 target = false; | |
| 394 } | |
| 395 | |
| 396 CYG_REPORT_RETVAL(result); | |
| 397 return result; | |
| 398 } | |
| 399 | |
| 400 //}}} | |
| 401 | |
| 402 //{{{ integer_to_string() | |
| 403 | |
| 404 // ---------------------------------------------------------------------------- | |
| 405 | |
| 406 std::string | |
| 407 Cdl::integer_to_string(cdl_int value, CdlValueFormat format) | |
| 408 { | |
| 409 std::string result; | |
| 410 Cdl::integer_to_string(value, result, format); | |
| 411 return result; | |
| 412 } | |
| 413 | |
| 414 void | |
| 415 Cdl::integer_to_string(cdl_int value, std::string& target, CdlValueFormat format) | |
| 416 { | |
| 417 CYG_REPORT_FUNCNAME("Cdl::integer_to_string"); | |
| 418 CYG_REPORT_FUNCARG2XV((long) value, format); | |
| 419 | |
| 420 // Optimise this special case. | |
| 421 if (0 == value) { | |
| 422 if (CdlValueFormat_Hex == format) { | |
| 423 target = "0x0"; | |
| 424 } else { | |
| 425 target = "0"; | |
| 426 } | |
| 427 CYG_REPORT_RETVAL(true); | |
| 428 return; | |
| 429 } | |
| 430 | |
| 431 // A local buffer to construct partial strings. This avoids | |
| 432 // unnecessary std::string reallocation. | |
| 433 // 64 bits and three bits at a time for octal numbers gives 21 digits, | |
| 434 // plus spares for the leading '0' and the terminator. | |
| 435 char local_buf[24]; | |
| 436 char *local_ptr = &(local_buf[23]); | |
| 437 *local_ptr-- = '\0'; | |
| 438 | |
| 439 if (CdlValueFormat_Hex == format) { | |
| 440 | |
| 441 // Output the data as 0x... with either 8 or 16 digits, | |
| 442 // depending on the size. | |
| 443 int i; | |
| 444 for (i = 0; i < 8; i++) { | |
| 445 int tmp = (int) (value & 0x0F); | |
| 446 value = value >> 4; | |
| 447 if (tmp < 10) { | |
| 448 *local_ptr-- = '0' + tmp; | |
| 449 } else { | |
| 450 *local_ptr-- = 'A' + (tmp - 10); | |
| 451 } | |
| 452 } | |
| 453 // Beware of right shifts that preserve the sign bit. | |
| 454 { | |
| 455 int tmp1 = (value & 0x0FFFF); | |
| 456 int tmp2 = ((value >> 16) & 0x0FFFF); | |
| 457 value = (tmp2 << 16) + tmp1; | |
| 458 } | |
| 459 if (value != 0) { | |
| 460 for (i = 0; i < 8; i++) { | |
| 461 int tmp = (int) (value & 0x0F); | |
| 462 value = value >> 4; | |
| 463 if (tmp < 10) { | |
| 464 *local_ptr-- = '0' + tmp; | |
| 465 } else { | |
| 466 *local_ptr-- = 'A' + (tmp - 10); | |
| 467 } | |
| 468 } | |
| 469 } | |
| 470 *local_ptr-- = 'x'; | |
| 471 *local_ptr = '0'; | |
| 472 target = std::string(local_ptr); | |
| 473 | |
| 474 } else if (CdlValueFormat_Octal == format) { | |
| 475 | |
| 476 // Simply output the data three bits at a time, do not worry about any | |
| 477 // particular width restrictions. However it is necessary to worry | |
| 478 // about masking. | |
| 479 cdl_int mask = 0x1FFFFFFF; | |
| 480 mask = (mask << 16) | 0x0FFFF; | |
| 481 mask = (mask << 16) | 0x0FFFF; | |
| 482 | |
| 483 target = ""; | |
| 484 while (value > 0) { | |
| 485 int tmp = value & 0x07; | |
| 486 value = (value >> 3) & mask; | |
| 487 *local_ptr-- = '0' + tmp; | |
| 488 } | |
| 489 *local_ptr = '0'; | |
| 490 target = std::string(local_ptr); | |
| 491 | |
| 492 } else { | |
| 493 // A simple decimal number | |
| 494 // Switch to positive arithmetic. | |
| 495 bool negative = false; | |
| 496 if (value < 0) { | |
| 497 negative = true; | |
| 498 value = 0 - value; | |
| 499 // Only MININT cannot be converted using the above line | |
| 500 if (value < 0) { | |
| 501 target = "-9223372036854775808"; | |
| 502 CYG_REPORT_RETVAL(true); | |
| 503 return; | |
| 504 } | |
| 505 } | |
| 506 | |
| 507 while (value > 0) { | |
| 508 int rem = (int) (value % 10); | |
| 509 value = value / 10; | |
| 510 *local_ptr-- = '0' + rem; | |
| 511 } | |
| 512 if (negative) { | |
| 513 *local_ptr-- = '-'; | |
| 514 } | |
| 515 local_ptr++; | |
| 516 target = std::string(local_ptr); | |
| 517 } | |
| 518 | |
| 519 CYG_REPORT_RETURN(); | |
| 520 return; | |
| 521 } | |
| 522 | |
| 523 //}}} | |
| 524 //{{{ double_to_string() | |
| 525 | |
| 526 // ---------------------------------------------------------------------------- | |
| 527 | |
| 528 std::string | |
| 529 Cdl::double_to_string(double value, CdlValueFormat format) | |
| 530 { | |
| 531 std::string result; | |
| 532 Cdl::double_to_string(value, result, format); | |
| 533 return result; | |
| 534 } | |
| 535 | |
| 536 void | |
| 537 Cdl::double_to_string(double value, std::string& result, CdlValueFormat format) | |
| 538 { | |
| 539 CYG_REPORT_FUNCNAME("Cdl::double_to_String"); | |
| 540 | |
| 541 char buf[256]; // This should be plenty :-) | |
| 542 sprintf(buf, "%.*G", DBL_DIG, value); | |
| 543 result = buf; | |
| 544 | |
| 545 CYG_UNUSED_PARAM(CdlValueFormat, format); | |
| 546 CYG_REPORT_RETURN(); | |
| 547 } | |
| 548 | |
| 549 //}}} | |
| 550 //{{{ bool_to_string() | |
| 551 | |
| 552 // ---------------------------------------------------------------------------- | |
| 553 // Should the string results be 1/0 or true/false? Not that | |
| 554 // it really matters. The testcase in cdl1.cxx expects 1/0. | |
| 555 std::string | |
| 556 Cdl::bool_to_string(bool value) | |
| 557 { | |
| 558 std::string result; | |
| 559 Cdl::bool_to_string(value, result); | |
| 560 return result; | |
| 561 } | |
| 562 | |
| 563 void | |
| 564 Cdl::bool_to_string(bool value, std::string& target) | |
| 565 { | |
| 566 CYG_REPORT_FUNCNAME("Cdl::bool_to_string"); | |
| 567 CYG_REPORT_FUNCARG1( "value arg %ld", (long) value); | |
| 568 | |
| 569 if (value) | |
| 570 target = "1"; | |
| 571 else | |
| 572 target = "0"; | |
| 573 | |
| 574 CYG_REPORT_RETURN(); | |
| 575 } | |
| 576 | |
| 577 //}}} | |
| 578 | |
| 579 //{{{ integer_to_double() | |
| 580 | |
| 581 // ---------------------------------------------------------------------------- | |
| 582 // Currently integer to double cannot fail, although there may well be loss | |
| 583 // of accurary. Eventually cdl_int may be an arbitrary precision integer | |
| 584 // in which case conversion to double is not guaranteed. | |
| 585 double | |
| 586 Cdl::integer_to_double(cdl_int value) | |
| 587 { | |
| 588 CYG_REPORT_FUNCNAME("Cdl::integer_to_double"); | |
| 589 | |
| 590 double result = (double) value; | |
| 591 | |
| 592 CYG_REPORT_RETURN(); | |
| 593 return result; | |
| 594 } | |
| 595 | |
| 596 void | |
| 597 Cdl::integer_to_double(cdl_int value, double& target) | |
| 598 { | |
| 599 CYG_REPORT_FUNCNAME("Cdl::integer_to_double"); | |
| 600 | |
| 601 target = (double) value; | |
| 602 | |
| 603 CYG_REPORT_RETURN(); | |
| 604 } | |
| 605 | |
| 606 //}}} | |
| 607 //{{{ double_to_integer() | |
| 608 | |
| 609 // Conversion from double to integer is only allowed if there is no loss | |
| 610 // of data. modf() is useful here | |
| 611 bool | |
| 612 Cdl::double_to_integer(double value, cdl_int& target) | |
| 613 { | |
| 614 CYG_REPORT_FUNCNAMETYPE("Cdl::double_to_integer", "result %d"); | |
| 615 | |
| 616 bool result = false; | |
| 617 | |
| 618 double integral; | |
| 619 double frac; | |
| 620 | |
| 621 frac = modf(value, &integral); | |
| 622 if (0.0 == frac) { | |
| 623 // Looking good, but integral may still be too big. | |
| 624 cdl_int tmp = (cdl_int) integral; | |
| 625 if (tmp == value) { | |
| 626 // No fraction, no loss of data, everything looking good | |
| 627 target = tmp; | |
| 628 result = true; | |
| 629 } | |
| 630 } | |
| 631 | |
| 632 CYG_REPORT_RETVAL(result); | |
| 633 return result; | |
| 634 } | |
| 635 | |
| 636 //}}} | |
| 637 | |
| 638 //}}} | |
| 639 //{{{ Cdl::xxx_to_yyy() - CDL-specific data types | |
| 640 | |
| 641 // ---------------------------------------------------------------------------- | |
| 642 // Conversions between strings and flavors. | |
| 643 | |
| 644 static struct { | |
| 645 char* name; | |
| 646 CdlValueFlavor flavor; | |
| 647 } valid_flavors[] = { | |
| 648 { "none", CdlValueFlavor_None }, | |
| 649 { "bool", CdlValueFlavor_Bool }, | |
| 650 { "booldata", CdlValueFlavor_BoolData }, | |
| 651 { "data", CdlValueFlavor_Data }, | |
| 652 { 0, CdlValueFlavor_Invalid } | |
| 653 }; | |
| 654 | |
| 655 bool | |
| 656 Cdl::string_to_flavor(std::string name, CdlValueFlavor& target) | |
| 657 { | |
| 658 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_flavor", "success %d"); | |
| 659 | |
| 660 bool result = false; | |
| 661 | |
| 662 // First convert the argument to lower case. Arguably this is incorrect, | |
| 663 // Tcl is a case-sensitive language, but the conversion is unlikely ever | |
| 664 // to be harmfull. | |
| 665 for (std::string::iterator str_i = name.begin(); str_i != name.end(); str_i++) { | |
| 666 if (isupper(*str_i)) { | |
| 667 *str_i = tolower(*str_i); | |
| 668 } | |
| 669 } | |
| 670 | |
| 671 // Now look for a match in the table. | |
| 672 int match = -1; | |
| 673 int i; | |
| 674 const char* c_str = name.c_str(); | |
| 675 int len = strlen(c_str); | |
| 676 | |
| 677 for (i = 0; 0 != valid_flavors[i].name; i++) { | |
| 678 if (0 == strncmp(c_str, valid_flavors[i].name, len)) { | |
| 679 // Check for an ambiguous string match. | |
| 680 // This cannot actually happen with the current flavor names. | |
| 681 if ( -1 != match) { | |
| 682 break; | |
| 683 } | |
| 684 match = i; | |
| 685 } | |
| 686 | |
| 687 } | |
| 688 if (-1 != match) { | |
| 689 target = valid_flavors[match].flavor; | |
| 690 result = true; | |
| 691 } | |
| 692 CYG_REPORT_RETVAL(result); | |
| 693 return result; | |
| 694 } | |
| 695 | |
| 696 bool | |
| 697 Cdl::flavor_to_string(CdlValueFlavor flavor, std::string& target) | |
| 698 { | |
| 699 CYG_REPORT_FUNCNAMETYPE("Cdl::flavor_to_string", "success %d"); | |
| 700 | |
| 701 bool result = false; | |
| 702 | |
| 703 for (int i = 0; 0 != valid_flavors[i].name; i++) { | |
| 704 if (flavor == valid_flavors[i].flavor) { | |
| 705 target = valid_flavors[i].name; | |
| 706 result = true; | |
| 707 break; | |
| 708 } | |
| 709 } | |
| 710 | |
| 711 CYG_REPORT_RETVAL(result); | |
| 712 return result; | |
| 713 } | |
| 714 | |
| 715 // ---------------------------------------------------------------------------- | |
| 716 // Similar support for value sources. | |
| 717 | |
| 718 static struct { | |
| 719 char* name; | |
| 720 CdlValueSource source; | |
| 721 } valid_sources[] = { | |
| 722 { "default", CdlValueSource_Default }, | |
| 723 { "inferred", CdlValueSource_Inferred }, | |
| 724 { "wizard", CdlValueSource_Wizard }, | |
| 725 { "user", CdlValueSource_User }, | |
| 726 { 0, CdlValueSource_Invalid } | |
| 727 }; | |
| 728 | |
| 729 bool | |
| 730 Cdl::string_to_source(std::string name, CdlValueSource& target) | |
| 731 { | |
| 732 CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_source", "success %d"); | |
| 733 | |
| 734 bool result = false; | |
| 735 | |
| 736 // First convert the argument to lower case. Arguably this is incorrect, | |
| 737 // Tcl is a case-sensitive language, but the conversion is unlikely ever | |
| 738 // to be harmfull. | |
| 739 for (std::string::iterator str_i = name.begin(); str_i != name.end(); str_i++) { | |
| 740 if (isupper(*str_i)) { | |
| 741 *str_i = tolower(*str_i); | |
| 742 } | |
| 743 } | |
| 744 | |
| 745 // Now look for a match in the table. | |
| 746 int match = -1; | |
| 747 int i; | |
| 748 const char* c_str = name.c_str(); | |
| 749 int len = strlen(c_str); | |
| 750 | |
| 751 for (i = 0; 0 != valid_sources[i].name; i++) { | |
| 752 if (0 == strncmp(c_str, valid_sources[i].name, len)) { | |
| 753 // Check for an ambiguous string match. | |
| 754 // This cannot actually happen with the current source names. | |
| 755 if ( -1 != match) { | |
| 756 break; | |
| 757 } | |
| 758 match = i; | |
| 759 } | |
| 760 | |
| 761 } | |
| 762 if (-1 != match) { | |
| 763 target = valid_sources[match].source; | |
| 764 result = true; | |
| 765 } | |
| 766 CYG_REPORT_RETVAL(result); | |
| 767 return result; | |
| 768 } | |
| 769 | |
| 770 bool | |
| 771 Cdl::source_to_string(CdlValueSource source, std::string& target) | |
| 772 { | |
| 773 CYG_REPORT_FUNCNAMETYPE("Cdl::source_to_string", "success %d"); | |
| 774 | |
| 775 bool result = false; | |
| 776 | |
| 777 for (int i = 0; 0 != valid_sources[i].name; i++) { | |
| 778 if (source == valid_sources[i].source) { | |
| 779 target = valid_sources[i].name; | |
| 780 result = true; | |
| 781 break; | |
| 782 } | |
| 783 } | |
| 784 | |
| 785 CYG_REPORT_RETVAL(result); | |
| 786 return result; | |
| 787 } | |
| 788 | |
| 789 //}}} | |
| 790 //{{{ Cdl::get_library_version() | |
| 791 | |
| 792 // ---------------------------------------------------------------------------- | |
| 793 // The version of the library actually lives inside configure.in. It gets | |
| 794 // exported into cdlconfig.h | |
| 795 std::string | |
| 796 Cdl::get_library_version(void) | |
| 797 { | |
| 798 return std::string(CYGNUM_LIBCDL_VERSION); | |
| 799 } | |
| 800 | |
| 801 //}}} | |
| 802 //{{{ Cdl::set_interactive() | |
| 803 | |
| 804 // ---------------------------------------------------------------------------- | |
| 805 // Some CDL scripts and some bits of the library may want to adapt depending | |
| 806 // on whether or not the application is running fully interactively or in | |
| 807 // batch mode. The primary distinction is that a batch program should never | |
| 808 // attempt to obtain user input, whether via Tk widgets or by other means. | |
| 809 | |
| 810 bool Cdl::interactive = false; | |
| 811 | |
| 812 void | |
| 813 Cdl::set_interactive(bool value) | |
| 814 { | |
| 815 CYG_REPORT_FUNCNAME("Cdl::set_interactive"); | |
| 816 CYG_REPORT_FUNCARG1D(value); | |
| 817 | |
| 818 interactive = value; | |
| 819 } | |
| 820 | |
| 821 bool | |
| 822 Cdl::is_interactive(void) | |
| 823 { | |
| 824 CYG_REPORT_FUNCNAMETYPE("Cdl::is_interactive", "interactive %d"); | |
| 825 CYG_REPORT_RETVAL(interactive); | |
| 826 return interactive; | |
| 827 } | |
| 828 | |
| 829 //}}} | |
| 830 //{{{ Cdl::compare_versions() | |
| 831 | |
| 832 // ---------------------------------------------------------------------------- | |
| 833 // Packages may need to impose constraints on which versions of other | |
| 834 // packages they can coexist with. This requires some way of achieving | |
| 835 // a partial ordering of version numbers. Unfortunately there are many | |
| 836 // different ways of specifying a version number, and we cannot impose | |
| 837 // a single model on all third party package developers. Instead this | |
| 838 // routine performs some semi-intelligent comparisons of two version | |
| 839 // strings which should work in the vast majority of cases. | |
| 840 // | |
| 841 // The return value is as per strcmp(), -1 if the first entry is | |
| 842 // smaller (i.e. the more recent and hence hopefully the first in | |
| 843 // a list), +1 if the second entry is smaller, 0 if the two are | |
| 844 // identical. | |
| 845 // | |
| 846 // There is a big ambiguity between "derived" versions and "experimental" | |
| 847 // versions. Something like v0.3beta is experimental, i.e. it is older | |
| 848 // than the full release v0.3. On the other hand v0.3.p1 is a patched | |
| 849 // version of v0.3 and hence newer. This code uses the presence or otherwise | |
| 850 // of a separator to decide between the two cases. | |
| 851 | |
| 852 // A utility routine which checks whether or not a character counts | |
| 853 // as a separator. Currently the characters . - and _ are all accepted | |
| 854 // as field separators. | |
| 855 // | |
| 856 // Arguably - should not be accepted as a separator. Instead if it preceeds | |
| 857 // a digit it could be interpreted as part of a prerelease number. | |
| 858 | |
| 859 static bool | |
| 860 is_separator(int ch) | |
| 861 { | |
| 862 return ('.' == ch) || ('-' == ch) || ('_' == ch); | |
| 863 } | |
| 864 | |
| 865 int | |
| 866 Cdl::compare_versions(std::string arg1, std::string arg2) | |
| 867 { | |
| 868 CYG_REPORT_FUNCNAMETYPE("Cdl::compare_versions", "result %d"); | |
| 869 | |
| 870 if (arg1 == arg2) { | |
| 871 CYG_REPORT_RETVAL(0); | |
| 872 return 0; | |
| 873 } | |
| 874 | |
| 875 // The version number "current" is special, it always indicates the most | |
| 876 // recent version e.g. as checked out from a CVS repository. | |
| 877 if ("current" == arg1) { | |
| 878 CYG_REPORT_RETVAL(-1); | |
| 879 return -1; | |
| 880 } | |
| 881 if ("current" == arg2) { | |
| 882 CYG_REPORT_RETVAL(1); | |
| 883 return 1; | |
| 884 } | |
| 885 | |
| 886 const char* ptr1 = arg1.c_str(); | |
| 887 const char* ptr2 = arg2.c_str(); | |
| 888 int num1 = 0; | |
| 889 int num2 = 0; | |
| 890 | |
| 891 // If both strings start with 'v' or 'V', skip this. A minor variation in | |
| 892 // case at the start of a string should be ignored. | |
| 893 if ((('v' == *ptr1) || ('V' == *ptr1)) && | |
| 894 (('v' == *ptr2) || ('V' == *ptr2))) { | |
| 895 ptr1++; | |
| 896 ptr2++; | |
| 897 } | |
| 898 | |
| 899 // Now process the rest of the version string, one unit at a time. | |
| 900 while (1) { | |
| 901 | |
| 902 if (('\0' == *ptr1) && ('\0' == *ptr2)) { | |
| 903 // Both strings have terminated at the same time. There | |
| 904 // may have been some spurious leading zeroes in numbers, | |
| 905 // or irrelevant differences in the separators. | |
| 906 CYG_REPORT_RETVAL(0); | |
| 907 return 0; | |
| 908 } | |
| 909 | |
| 910 if ('\0' == *ptr1) { | |
| 911 // The first string has ended first. If the second string currently | |
| 912 // points at a separator then arg2 is a derived version, e.g. | |
| 913 // v0.3.p1, and hence newer. Otherwise arg2 is an experimental | |
| 914 // version v0.3beta and hence older. | |
| 915 if (is_separator(*ptr2)) { | |
| 916 CYG_REPORT_RETVAL(1); | |
| 917 return 1; | |
| 918 } else { | |
| 919 CYG_REPORT_RETVAL(-1); | |
| 920 return -1; | |
| 921 } | |
| 922 } | |
| 923 | |
| 924 if ('\0' == *ptr2) { | |
| 925 // As per the previous test. | |
| 926 if (is_separator(*ptr1)) { | |
| 927 CYG_REPORT_RETVAL(-1); | |
| 928 return -1; | |
| 929 } else { | |
| 930 CYG_REPORT_RETVAL(1); | |
| 931 return 1; | |
| 932 } | |
| 933 } | |
| 934 | |
| 935 // If both strings currently point at numerical data, do a conversion and | |
| 936 // a numerical comparison. | |
| 937 if (isdigit(*ptr1) && isdigit(*ptr2)) { | |
| 938 num1 = 0; | |
| 939 num2 = 0; | |
| 940 // Strictly speaking there should be some overflow tests here, but it | |
| 941 // is not worth the trouble. | |
| 942 do { | |
| 943 num1 = (10 * num1) + (*ptr1++ - '0'); | |
| 944 } while(isdigit(*ptr1)); | |
| 945 do { | |
| 946 num2 = (10 * num2) + (*ptr2++ - '0'); | |
| 947 } while(isdigit(*ptr2)); | |
| 948 // v2.0 is newer than v1.0 | |
| 949 if (num1 < num2) { | |
| 950 CYG_REPORT_RETVAL(1); | |
| 951 return 1; | |
| 952 } else if (num1 > num2) { | |
| 953 CYG_REPORT_RETVAL(-1); | |
| 954 return -1; | |
| 955 } else { | |
| 956 continue; | |
| 957 } | |
| 958 } | |
| 959 | |
| 960 // Non-numerical data. If the two characters are the same then | |
| 961 // move on. Note: this has to happen after numerical conversions | |
| 962 // to distinguish v10.0 and v1.0 | |
| 963 if (*ptr1 == *ptr2) { | |
| 964 ptr1++; ptr2++; | |
| 965 continue; | |
| 966 } | |
| 967 | |
| 968 // If both strings are currently at a separator then move on. All | |
| 969 // separators can be used interchangeably. | |
| 970 if (is_separator(*ptr1) && is_separator(*ptr2)) { | |
| 971 ptr1++; ptr2++; | |
| 972 continue; | |
| 973 } | |
| 974 | |
| 975 // If only one string is at a separator, special action | |
| 976 // is needed. v1.1alpha is interpreted as earlier than | |
| 977 // v1.1, but v1.1.3 is a later release. | |
| 978 if (is_separator(*ptr1)) { | |
| 979 return -1; | |
| 980 } else if (is_separator(*ptr2)) { | |
| 981 return 1; | |
| 982 } | |
| 983 | |
| 984 // Two different characters, e.g. v1.0alpha vs. v1.0beta | |
| 985 if (*ptr1 < *ptr2) { | |
| 986 CYG_REPORT_RETVAL(1); | |
| 987 return 1; | |
| 988 } else { | |
| 989 CYG_REPORT_RETVAL(-1); | |
| 990 return -1; | |
| 991 } | |
| 992 } | |
| 993 | |
| 994 // Not reachable. | |
| 995 } | |
| 996 | |
| 997 //}}} | |
| 998 //{{{ Cdl::get_short_form() | |
| 999 | |
| 1000 // ---------------------------------------------------------------------------- | |
| 1001 // It is occasionally useful to take a full CDL name such as CYgpkg_KERNEL | |
| 1002 // and turn it into a short form such as "kernel". This involves discarding | |
| 1003 // everything up to and including the first underscore, then lowercasing | |
| 1004 // all subsequent characters. | |
| 1005 std::string | |
| 1006 Cdl::get_short_form(const std::string& original) | |
| 1007 { | |
| 1008 CYG_REPORT_FUNCNAME("CdlMisc::get_short_form"); | |
| 1009 | |
| 1010 std::string result = ""; | |
| 1011 unsigned int size = original.size(); | |
| 1012 unsigned int i; | |
| 1013 for (i = 0; i < size; i++) { | |
| 1014 if ('_' == original[i]) { | |
| 1015 i++; | |
| 1016 break; | |
| 1017 } | |
| 1018 } | |
| 1019 | |
| 1020 // Either at end of string, or just past the first underscore | |
| 1021 for ( ; i < size; i++) { | |
| 1022 if (isupper(original[i])) { | |
| 1023 result += tolower(original[i]); | |
| 1024 } else { | |
| 1025 result += original[i]; | |
| 1026 } | |
| 1027 } | |
| 1028 | |
| 1029 CYG_REPORT_RETURN(); | |
| 1030 return result; | |
| 1031 } | |
| 1032 | |
| 1033 //}}} |
