]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/libcdl/cdlmisc.cxx
RedBoot Release TX53-v3 2012-02-08
[karo-tx-redboot.git] / tools / src / libcdl / cdlmisc.cxx
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 <string.h>
50 #include "cdlconfig.h"
51
52 // Get the infrastructure types, assertions, tracing and similar
53 // facilities.
54 #include <cyg/infra/cyg_ass.h>
55 #include <cyg/infra/cyg_trac.h>
56
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>
61
62 // For access to the isdigit(), isupper(), tolower(), ... functions
63 #include <cctype>
64
65 // For access to sprintf(), specifically double to string conversions.
66 #include <cstdio>
67
68 // For access to strtod()
69 #include <cstdlib>
70
71 // strtod() involves errno...
72 #include <cerrno>
73
74 // For access to fmod()
75 #include <cmath>
76
77 // For access to DBL_DIG
78 #include <cfloat>
79
80 //}}}
81
82 //{{{  Cdl::is_valid_xxx()
83
84 // ---------------------------------------------------------------------------
85
86 bool
87 Cdl::is_valid_value_flavor(CdlValueFlavor data)
88 {
89     bool result = false;
90
91     switch(data) {
92       case CdlValueFlavor_None     :
93       case CdlValueFlavor_Bool     :
94       case CdlValueFlavor_BoolData :
95       case CdlValueFlavor_Data     :
96         result = true;
97         break;
98
99       default:
100         break;
101     }
102
103     return result;
104 }
105
106 bool
107 Cdl::is_valid_value_source(CdlValueSource data)
108 {
109     bool result = false;
110
111     switch(data) {
112       case CdlValueSource_Default         :
113       case CdlValueSource_User            :
114       case CdlValueSource_Wizard          :
115       case CdlValueSource_Inferred        :
116         result = true;
117         break;
118
119       default:
120         break;
121     }
122
123     return result;
124 }
125
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.
129
130 bool
131 Cdl::is_valid_cdl_name(const std::string &name)
132 {
133     CYG_REPORT_FUNCNAMETYPE("Cdl::is_valid_cdl_name", "result %d");
134
135     bool result = is_valid_c_preprocessor_symbol(name);
136
137     CYG_REPORT_RETVAL(result);
138     return result;
139 }
140
141 bool
142 Cdl::is_valid_c_preprocessor_symbol(const std::string &symbol)
143 {
144     CYG_REPORT_FUNCNAMETYPE("Cdl::is_valid_c_preprocessor_symbol", "result %d");
145
146     bool result = true;
147     if ("" == symbol) {
148         result = false;
149     } else {
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.
153         //
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'))) {
159
160             result = false;
161         } else {
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'))) {
167
168                     result = false;
169                     break;
170                 }
171             }
172         }
173     }
174
175     CYG_REPORT_RETVAL(result);
176     return result;
177 }
178
179 //}}}
180 //{{{  Cdl::xxx_to_yyy() - strings, ints, doubles, ...
181
182 // ---------------------------------------------------------------------------
183 // Conversion routines between strings, integers, doubles, bools, ...
184 //
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.
189 //
190 // ASCII rather than EBCDIC is assumed.
191 //
192 // Some of the routines may fail, e.g. string to integer conversions.
193 // Others are guaranteed to succeed.
194
195 //{{{  string_to_integer()
196
197 // ----------------------------------------------------------------------------
198 bool
199 Cdl::string_to_integer(std::string data, cdl_int &target)
200 {
201     CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_integer", "success %d");
202
203     bool negative       = false;
204     bool seen_plus      = false;
205     bool seen_minus     = false;
206
207     // Life is a bit easier if I can check for '\0'
208     const char *ptr           = data.c_str();
209
210     // Not essential but harmless.
211     while (isspace(*ptr))
212         ptr++;
213
214     if ('+' == *ptr) {
215         if (seen_plus) {
216             target = 0;
217             CYG_REPORT_RETVAL(false);
218             return false;
219         }
220         seen_plus = true;
221         ptr++;
222     }
223
224     if ('-' == *ptr) {
225         if (seen_minus) {
226             target = 0;
227             CYG_REPORT_RETVAL(false);
228             return false;
229         }
230         seen_minus = true;
231         negative = true;
232         ptr++;
233     }
234
235     cdl_int acc = 0;
236     if ('0' == *ptr) {
237         // This happens sufficiently often to be worth a special case.
238         if ('\0' == ptr[1]) {
239             target = 0;
240             CYG_REPORT_RETVAL(true);
241             return true;
242         }
243         // Hex is always worth supporting.
244         if (('x' == ptr[1]) || ('X' == ptr[1])) {
245             ptr++; ptr++;
246             if (!isxdigit(*ptr)) {
247                 CYG_REPORT_RETVAL(false);
248                 return false;
249             }
250             while (isxdigit(*ptr)) {
251                 cdl_int new_acc = acc * 16;
252                 if (isdigit(*ptr)) {
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');
258                 } else {
259                     CYG_FAIL("this platform's implementation of isxdigit() is broken");
260                 }
261                 if (new_acc < acc) {
262                     CYG_REPORT_RETVAL(false);
263                     return false;
264                 }
265                 acc = new_acc;
266                 ptr++;
267             }
268             if ('\0' != *ptr) {
269                 CYG_REPORT_RETVAL(false);
270                 return false;
271             }
272             if (negative) {
273                 cdl_int new_acc = 0 - acc;
274                 if (new_acc > 0) {
275                     CYG_REPORT_RETVAL(false);
276                     return false;
277                 } else {
278                     acc = new_acc;
279                 }
280             }
281             target = acc;
282             CYG_REPORT_RETVAL(true);
283             return true;
284         }
285
286         // Octal? Oh well, might as well be complete.
287         if (('0' <= ptr[1]) && (ptr[1] <= '7')) {
288             ptr++;
289             do {
290                 cdl_int new_acc = 8 * acc;
291                 new_acc += (*ptr - '0');
292                 if (new_acc < acc) {
293                     CYG_REPORT_RETVAL(false);
294                     return false;
295                 }
296                 acc = new_acc;
297                 ptr++;
298             } while (('0' <= *ptr) && (*ptr <= '7'));
299             if ('\0' != *ptr) {
300                 CYG_REPORT_RETVAL(false);
301                 return false;
302             }
303             if (negative) {
304                 cdl_int new_acc = 0 - acc;
305                 if (new_acc > 0) {
306                     CYG_REPORT_RETVAL(false);
307                     return false;
308                 }
309                 else {
310                     acc = new_acc;
311                 }
312             }
313             target = acc;
314             CYG_REPORT_RETVAL(true);
315             return true;
316         }
317
318         // Drop through for the case of a decimal.
319     }
320
321     while(isdigit(*ptr)) {
322         cdl_int new_acc = 10 * acc;
323         new_acc += (*ptr - '0');
324         if (new_acc < acc) {
325             CYG_REPORT_RETVAL(false);
326             return false;
327         }
328         acc = new_acc;
329         ptr++;
330     }
331     if ('\0' != *ptr) {
332         CYG_REPORT_RETVAL(false);
333         return false;
334     }
335     if (negative) {
336         cdl_int new_acc = 0 - acc;
337         if (new_acc > 0) {
338             CYG_REPORT_RETVAL(false);
339             return false;
340         } else {
341             acc = new_acc;
342         }
343     }
344     target = acc;
345     CYG_REPORT_RETVAL(true);
346     return true;
347 }
348
349 //}}}
350 //{{{  string_to_double()
351
352 // ----------------------------------------------------------------------------
353 // There is no point in doing this the hard way, just use standard
354 // library calls.
355 //
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.
362
363 bool
364 Cdl::string_to_double(std::string value, double &target)
365 {
366     CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_double", "success %d");
367
368     bool        result      = true;
369     const char *start_ptr   = value.c_str();
370     char       *end_ptr;
371     int         old_errno   = errno;
372
373     errno                   = 0;
374     double conv             = strtod(start_ptr, &end_ptr);
375     if (0 != errno) {
376         CYG_ASSERT(ERANGE == errno, "standard-compliant C library");
377         result = false;
378     } else if ('\0' != *end_ptr) {
379         result = false;
380     } else {
381         target = conv;
382         result = true;
383     }
384
385     errno = old_errno;
386     CYG_REPORT_RETVAL(result);
387     return result;
388 }
389
390 //}}}
391 //{{{  string_to_bool()
392
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.
397 bool
398 Cdl::string_to_bool(std::string data, bool &target)
399 {
400     CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_bool", "success %d");
401
402     // Arguably there should be a precondition ( "" != data )
403     bool result = false;
404
405     // What is truth ?
406     if (( data == "1"   ) || (data == "true") ||
407         ( data == "True") || (data == "TRUE") ) {
408         result = true;
409         target = true;
410     } else if ((data == "0"    ) || (data == "false") ||
411                (data == "False") || (data == "FALSE") ) {
412         result = true;
413         target = false;
414     }
415
416     CYG_REPORT_RETVAL(result);
417     return result;
418 }
419
420 //}}}
421
422 //{{{  integer_to_string()
423
424 // ----------------------------------------------------------------------------
425
426 std::string
427 Cdl::integer_to_string(cdl_int value, CdlValueFormat format)
428 {
429     std::string result;
430     Cdl::integer_to_string(value, result, format);
431     return result;
432 }
433
434 void
435 Cdl::integer_to_string(cdl_int value, std::string &target, CdlValueFormat format)
436 {
437     CYG_REPORT_FUNCNAME("Cdl::integer_to_string");
438     CYG_REPORT_FUNCARG2XV((long) value, format);
439
440     // Optimise this special case.
441     if (0 == value) {
442         if (CdlValueFormat_Hex == format) {
443             target = "0x0";
444         } else {
445             target = "0";
446         }
447         CYG_REPORT_RETVAL(true);
448         return;
449     }
450
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.
455     char local_buf[24];
456     char *local_ptr = &(local_buf[23]);
457     *local_ptr-- = '\0';
458
459     if (CdlValueFormat_Hex == format) {
460
461         // Output the data as 0x... with either 8 or 16 digits,
462         // depending on the size.
463         int i;
464         for (i = 0; i < 8; i++) {
465             int tmp = (int) (value & 0x0F);
466             value   = value >> 4;
467             if (tmp < 10) {
468                 *local_ptr-- = '0' + tmp;
469             } else {
470                 *local_ptr-- = 'A' + (tmp - 10);
471             }
472         }
473         // Beware of right shifts that preserve the sign bit.
474         {
475             int tmp1 = (value & 0x0FFFF);
476             int tmp2 = ((value >> 16) & 0x0FFFF);
477             value    = (tmp2 << 16) + tmp1;
478         }
479         if (value != 0) {
480             for (i = 0; i < 8; i++) {
481                 int tmp = (int) (value & 0x0F);
482                 value   = value >> 4;
483                 if (tmp < 10) {
484                     *local_ptr-- = '0' + tmp;
485                 } else {
486                     *local_ptr-- = 'A' + (tmp - 10);
487                 }
488             }
489         }
490         *local_ptr-- = 'x';
491         *local_ptr   = '0';
492         target = std::string(local_ptr);
493
494     } else if (CdlValueFormat_Octal == format) {
495
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
498         // about masking.
499         cdl_int mask = 0x1FFFFFFF;
500         mask = (mask << 16) | 0x0FFFF;
501         mask = (mask << 16) | 0x0FFFF;
502
503         target = "";
504         while (value > 0) {
505             int tmp = value & 0x07;
506             value   = (value >> 3) & mask;
507             *local_ptr-- = '0' + tmp;
508         }
509         *local_ptr = '0';
510         target = std::string(local_ptr);
511
512     } else {
513         // A simple decimal number
514         // Switch to positive arithmetic.
515         bool negative = false;
516         if (value < 0) {
517             negative = true;
518             value    = 0 - value;
519             // Only MININT cannot be converted using the above line
520             if (value < 0) {
521                 target = "-9223372036854775808";
522                 CYG_REPORT_RETVAL(true);
523                 return;
524             }
525         }
526
527         while (value > 0) {
528             int rem = (int) (value % 10);
529             value   = value / 10;
530             *local_ptr-- = '0' + rem;
531         }
532         if (negative) {
533             *local_ptr-- = '-';
534         }
535         local_ptr++;
536         target = std::string(local_ptr);
537     }
538
539     CYG_REPORT_RETURN();
540     return;
541 }
542
543 //}}}
544 //{{{  double_to_string()
545
546 // ----------------------------------------------------------------------------
547
548 std::string
549 Cdl::double_to_string(double value, CdlValueFormat format)
550 {
551     std::string result;
552     Cdl::double_to_string(value, result, format);
553     return result;
554 }
555
556 void
557 Cdl::double_to_string(double value, std::string &result, CdlValueFormat format)
558 {
559     CYG_REPORT_FUNCNAME("Cdl::double_to_String");
560
561     char buf[256];      // This should be plenty :-)
562     sprintf(buf, "%.*G", DBL_DIG, value);
563     result = buf;
564
565     CYG_UNUSED_PARAM(CdlValueFormat, format);
566     CYG_REPORT_RETURN();
567 }
568
569 //}}}
570 //{{{  bool_to_string()
571
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.
575 std::string
576 Cdl::bool_to_string(bool value)
577 {
578     std::string result;
579     Cdl::bool_to_string(value, result);
580     return result;
581 }
582
583 void
584 Cdl::bool_to_string(bool value, std::string &target)
585 {
586     CYG_REPORT_FUNCNAME("Cdl::bool_to_string");
587     CYG_REPORT_FUNCARG1( "value arg %ld", (long) value);
588
589     if (value)
590         target = "1";
591     else
592         target = "0";
593
594     CYG_REPORT_RETURN();
595 }
596
597 //}}}
598
599 //{{{  integer_to_double()
600
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.
605 double
606 Cdl::integer_to_double(cdl_int value)
607 {
608     CYG_REPORT_FUNCNAME("Cdl::integer_to_double");
609
610     double result = (double) value;
611
612     CYG_REPORT_RETURN();
613     return result;
614 }
615
616 void
617 Cdl::integer_to_double(cdl_int value, double &target)
618 {
619     CYG_REPORT_FUNCNAME("Cdl::integer_to_double");
620
621     target = (double) value;
622
623     CYG_REPORT_RETURN();
624 }
625
626 //}}}
627 //{{{  double_to_integer()
628
629 // Conversion from double to integer is only allowed if there is no loss
630 // of data. modf() is useful here
631 bool
632 Cdl::double_to_integer(double value, cdl_int &target)
633 {
634     CYG_REPORT_FUNCNAMETYPE("Cdl::double_to_integer", "result %d");
635
636     bool   result = false;
637
638     double integral;
639     double frac;
640
641     frac = modf(value, &integral);
642     if (0.0 == frac) {
643         // Looking good, but integral may still be too big.
644         cdl_int tmp = (cdl_int) integral;
645         if (tmp == value) {
646             // No fraction, no loss of data, everything looking good
647             target = tmp;
648             result = true;
649         }
650     }
651
652     CYG_REPORT_RETVAL(result);
653     return result;
654 }
655
656 //}}}
657
658 //}}}
659 //{{{  Cdl::xxx_to_yyy() - CDL-specific data types
660
661 // ----------------------------------------------------------------------------
662 // Conversions between strings and flavors.
663
664 static struct {
665     const char          *name;
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  }
673 };
674
675 bool
676 Cdl::string_to_flavor(std::string name, CdlValueFlavor &target)
677 {
678     CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_flavor", "success %d");
679
680     bool result = false;
681
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
684     // to be harmfull.
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);
688         }
689     }
690
691     // Now look for a match in the table.
692     int match           = -1;
693     int i;
694     const char *c_str   = name.c_str();
695     int len             = strlen(c_str);
696
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.
701             if ( -1 != match) {
702                 break;
703             }
704             match = i;
705         }
706
707     }
708     if (-1 != match) {
709         target = valid_flavors[match].flavor;
710         result = true;
711     }
712     CYG_REPORT_RETVAL(result);
713     return result;
714 }
715
716 bool
717 Cdl::flavor_to_string(CdlValueFlavor flavor, std::string &target)
718 {
719     CYG_REPORT_FUNCNAMETYPE("Cdl::flavor_to_string", "success %d");
720
721     bool result = false;
722
723     for (int i = 0; 0 != valid_flavors[i].name; i++) {
724         if (flavor == valid_flavors[i].flavor) {
725             target = valid_flavors[i].name;
726             result = true;
727             break;
728         }
729     }
730
731     CYG_REPORT_RETVAL(result);
732     return result;
733 }
734
735 // ----------------------------------------------------------------------------
736 // Similar support for value sources.
737
738 static struct {
739     const char          *name;
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  }
747 };
748
749 bool
750 Cdl::string_to_source(std::string name, CdlValueSource &target)
751 {
752     CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_source", "success %d");
753
754     bool result = false;
755
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
758     // to be harmfull.
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);
762         }
763     }
764
765     // Now look for a match in the table.
766     int match           = -1;
767     int i;
768     const char *c_str   = name.c_str();
769     int len             = strlen(c_str);
770
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.
775             if ( -1 != match) {
776                 break;
777             }
778             match = i;
779         }
780
781     }
782     if (-1 != match) {
783         target = valid_sources[match].source;
784         result = true;
785     }
786     CYG_REPORT_RETVAL(result);
787     return result;
788 }
789
790 bool
791 Cdl::source_to_string(CdlValueSource source, std::string &target)
792 {
793     CYG_REPORT_FUNCNAMETYPE("Cdl::source_to_string", "success %d");
794
795     bool result = false;
796
797     for (int i = 0; 0 != valid_sources[i].name; i++) {
798         if (source == valid_sources[i].source) {
799             target = valid_sources[i].name;
800             result = true;
801             break;
802         }
803     }
804
805     CYG_REPORT_RETVAL(result);
806     return result;
807 }
808
809 //}}}
810 //{{{  Cdl::get_library_version()
811
812 // ----------------------------------------------------------------------------
813 // The version of the library actually lives inside configure.in. It gets
814 // exported into cdlconfig.h
815 std::string
816 Cdl::get_library_version(void)
817 {
818     return std::string(CYGNUM_LIBCDL_VERSION);
819 }
820
821 //}}}
822 //{{{  Cdl::set_interactive()
823
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.
829
830 bool Cdl::interactive   = false;
831
832 void
833 Cdl::set_interactive(bool value)
834 {
835     CYG_REPORT_FUNCNAME("Cdl::set_interactive");
836     CYG_REPORT_FUNCARG1D(value);
837
838     interactive = value;
839 }
840
841 bool
842 Cdl::is_interactive(void)
843 {
844     CYG_REPORT_FUNCNAMETYPE("Cdl::is_interactive", "interactive %d");
845     CYG_REPORT_RETVAL(interactive);
846     return interactive;
847 }
848
849 //}}}
850 //{{{  version support()
851
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.
860 //
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
864 // identical.
865 //
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.
871
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.
875 //
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.
878
879 static bool
880 is_separator(int ch)
881 {
882     return ('.' == ch) || ('-' == ch) || ('_' == ch);
883 }
884
885 int
886 Cdl::compare_versions(std::string arg1, std::string arg2)
887 {
888     CYG_REPORT_FUNCNAMETYPE("Cdl::compare_versions", "result %d");
889
890     if (arg1 == arg2) {
891         CYG_REPORT_RETVAL(0);
892         return 0;
893     }
894
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);
899         return -1;
900     }
901     if ("current" == arg2) {
902         CYG_REPORT_RETVAL(1);
903         return 1;
904     }
905
906     const char *ptr1 = arg1.c_str();
907     const char *ptr2 = arg2.c_str();
908     int num1    = 0;
909     int num2    = 0;
910
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))) {
915         ptr1++;
916         ptr2++;
917     }
918
919     // Now process the rest of the version string, one unit at a time.
920     while (1) {
921
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);
927             return 0;
928         }
929
930         if ('\0' == *ptr1) {
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);
937                 return 1;
938             } else {
939                 CYG_REPORT_RETVAL(-1);
940                 return -1;
941             }
942         }
943
944         if ('\0' == *ptr2) {
945             // As per the previous test.
946             if (is_separator(*ptr1)) {
947                 CYG_REPORT_RETVAL(-1);
948                 return -1;
949             } else {
950                 CYG_REPORT_RETVAL(1);
951                 return 1;
952             }
953         }
954
955         // If both strings currently point at numerical data, do a conversion and
956         // a numerical comparison.
957         if (isdigit(*ptr1) && isdigit(*ptr2)) {
958             num1 = 0;
959             num2 = 0;
960             // Strictly speaking there should be some overflow tests here, but it
961             // is not worth the trouble.
962             do {
963                 num1 = (10 * num1) + (*ptr1++ - '0');
964             } while(isdigit(*ptr1));
965             do {
966                 num2 = (10 * num2) + (*ptr2++ - '0');
967             } while(isdigit(*ptr2));
968             // v2.0 is newer than v1.0
969             if (num1 < num2) {
970                 CYG_REPORT_RETVAL(1);
971                 return 1;
972             } else if (num1 > num2) {
973                 CYG_REPORT_RETVAL(-1);
974                 return -1;
975             } else {
976                 continue;
977             }
978         }
979
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) {
984             ptr1++; ptr2++;
985             continue;
986         }
987
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)) {
991             ptr1++; ptr2++;
992             continue;
993         }
994
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)) {
999             return -1;
1000         } else if (is_separator(*ptr2)) {
1001             return 1;
1002         }
1003
1004         // Two different characters, e.g. v1.0alpha vs. v1.0beta
1005         if (*ptr1 < *ptr2) {
1006             CYG_REPORT_RETVAL(1);
1007             return 1;
1008         } else {
1009             CYG_REPORT_RETVAL(-1);
1010             return -1;
1011         }
1012     }
1013
1014     // Not reachable.
1015 }
1016
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.
1021
1022 static void
1023 version_extract_number(const std::string &version, unsigned int &index, std::string &result)
1024 {
1025     CYG_REPORT_FUNCNAME("version_extract_number");
1026
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])) {
1031             break;
1032         }
1033     }
1034     if (index != version.size()) {
1035         result = "";
1036         if ((index > 0) && ('-' == version[index-1])) {
1037             result = "-";
1038         }
1039         do {
1040             result += version[index++];
1041         } while ((index < version.size()) && isdigit(version[index]));
1042     }
1043
1044     CYG_REPORT_RETURN();
1045 }
1046
1047 void
1048 Cdl::split_version_string(const std::string &version, std::string &major, std::string &minor, std::string &release)
1049 {
1050     CYG_REPORT_FUNCNAME("CdlMisc::split_version_string");
1051
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);
1056
1057     CYG_REPORT_RETURN();
1058 }
1059
1060 //}}}
1061 //{{{  Cdl::get_short_form()
1062
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.
1068 std::string
1069 Cdl::get_short_form(const std::string &original)
1070 {
1071     CYG_REPORT_FUNCNAME("CdlMisc::get_short_form");
1072
1073     std::string  result = "";
1074     unsigned int size = original.size();
1075     unsigned int i;
1076     for (i = 0; i < size; i++) {
1077         if ('_' == original[i]) {
1078             i++;
1079             break;
1080         }
1081     }
1082
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]);
1087         } else {
1088             result += original[i];
1089         }
1090     }
1091
1092     CYG_REPORT_RETURN();
1093     return result;
1094 }
1095
1096 //}}}