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 //}}}