83666297da070b5fce8d3eec8d04f639493f156a
[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 "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     bool seen_plus      = false;
204     bool seen_minus     = false;
205     
206     // Life is a bit easier if I can check for '\0'
207     const char* ptr           = data.c_str();
208
209     // Not essential but harmless.
210     while (isspace(*ptr))
211         ptr++;
212
213     if ('+' == *ptr) {
214         if (seen_plus) {
215             target = 0;
216             CYG_REPORT_RETVAL(false);
217             return false;
218         }
219         seen_plus = true;
220         ptr++;
221     }
222     
223     if ('-' == *ptr) {
224         if (seen_minus) {
225             target = 0;
226             CYG_REPORT_RETVAL(false);
227             return false;
228         }
229         seen_minus = true;
230         negative = true;
231         ptr++;
232     }
233
234     cdl_int acc = 0;
235     if ('0' == *ptr) {
236         // This happens sufficiently often to be worth a special case.
237         if ('\0' == ptr[1]) {
238             target = 0;
239             CYG_REPORT_RETVAL(true);
240             return true;
241         }
242         // Hex is always worth supporting. 
243         if (('x' == ptr[1]) || ('X' == ptr[1])) {
244             ptr++; ptr++;
245             if (!isxdigit(*ptr)) {
246                 CYG_REPORT_RETVAL(false);
247                 return false;
248             }
249             while (isxdigit(*ptr)) {
250                 cdl_int new_acc = acc * 16;
251                 if (isdigit(*ptr)) {
252                     new_acc += (*ptr - '0');
253                 } else if (('a' <= *ptr) && (*ptr <= 'f')) {
254                     new_acc += (*ptr + 10 - 'a');
255                 } else if (('A' <= *ptr) && (*ptr <= 'F')) {
256                     new_acc += (*ptr + 10 - 'A');
257                 } else {
258                     CYG_FAIL("this platform's implementation of isxdigit() is broken");
259                 }
260                 if (new_acc < acc) {
261                     CYG_REPORT_RETVAL(false);
262                     return false;
263                 }
264                 acc = new_acc;
265                 ptr++;
266             }
267             if ('\0' != *ptr) {
268                 CYG_REPORT_RETVAL(false);
269                 return false;
270             }
271             if (negative) {
272                 cdl_int new_acc = 0 - acc;
273                 if (new_acc > 0) {
274                     CYG_REPORT_RETVAL(false);
275                     return false;
276                 } else {
277                     acc = new_acc;
278                 }
279             }
280             target = acc;
281             CYG_REPORT_RETVAL(true);
282             return true;
283         }
284
285         // Octal? Oh well, might as well be complete.
286         if (('0' <= ptr[1]) && (ptr[1] <= '7')) {
287             ptr++;
288             do {
289                 cdl_int new_acc = 8 * acc;
290                 new_acc += (*ptr - '0');
291                 if (new_acc < acc) {
292                     CYG_REPORT_RETVAL(false);
293                     return false;
294                 }
295                 acc = new_acc;
296                 ptr++;
297             } while (('0' <= *ptr) && (*ptr <= '7'));
298             if ('\0' != *ptr) {
299                 CYG_REPORT_RETVAL(false);
300                 return false;
301             }
302             if (negative) {
303                 cdl_int new_acc = 0 - acc;
304                 if (new_acc > 0) {
305                     CYG_REPORT_RETVAL(false);
306                     return false;
307                 }
308                 else {
309                     acc = new_acc;
310                 }
311             }
312             target = acc;
313             CYG_REPORT_RETVAL(true);
314             return true;
315         }
316
317         // Drop through for the case of a decimal.
318     }
319
320     while(isdigit(*ptr)) {
321         cdl_int new_acc = 10 * acc;
322         new_acc += (*ptr - '0');
323         if (new_acc < acc) {
324             CYG_REPORT_RETVAL(false);
325             return false;
326         }
327         acc = new_acc;
328         ptr++;
329     }
330     if ('\0' != *ptr) {
331         CYG_REPORT_RETVAL(false);
332         return false;
333     }
334     if (negative) {
335         cdl_int new_acc = 0 - acc;
336         if (new_acc > 0) {
337             CYG_REPORT_RETVAL(false);
338             return false;
339         } else {
340             acc = new_acc;
341         }
342     }
343     target = acc;
344     CYG_REPORT_RETVAL(true);
345     return true;
346 }
347
348 //}}}
349 //{{{  string_to_double()                       
350
351 // ----------------------------------------------------------------------------
352 // There is no point in doing this the hard way, just use standard
353 // library calls.
354 //
355 // There is an obvious question as to how much precision can get lost
356 // doing the conversion to a string. In practice this should not matter
357 // too much, since the expression handling code generally keeps the
358 // original double precision lying around to be re-used. However it may
359 // be desirable to keep the libcdl behaviour in synch with Tcl's
360 // tcl_precision variable.
361
362 bool
363 Cdl::string_to_double(std::string value, double& target)
364 {
365     CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_double", "success %d");
366
367     bool        result      = true;
368     const char* start_ptr   = value.c_str();
369     char*       end_ptr;
370     int         old_errno   = errno;
371     
372     errno                   = 0;
373     double conv             = strtod(start_ptr, &end_ptr);
374     if (0 != errno) {
375         CYG_ASSERT(ERANGE == errno, "standard-compliant C library");
376         result = false;
377     } else if ('\0' != *end_ptr) {
378         result = false;
379     } else {
380         target = conv;
381         result = true;
382     }
383      
384     errno = old_errno;
385     CYG_REPORT_RETVAL(result);
386     return result;
387 }
388
389 //}}}
390 //{{{  string_to_bool()                         
391
392 // ----------------------------------------------------------------------------
393 // Conversions to and from bools. The only real issue here is exactly
394 // what strings should be accepted as synonyms for true and false.
395 // It is not actually clear that these functions are useful.
396 bool
397 Cdl::string_to_bool(std::string data, bool& target)
398 {
399     CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_bool", "success %d");
400
401     // Arguably there should be a precondition ( "" != data )
402     bool result = false;
403
404     // What is truth ?
405     if (( data == "1"   ) || (data == "true") ||
406         ( data == "True") || (data == "TRUE") ) {
407         result = true;
408         target = true;
409     } else if ((data == "0"    ) || (data == "false") ||
410                (data == "False") || (data == "FALSE") ) {
411         result = true;
412         target = false;
413     }
414
415     CYG_REPORT_RETVAL(result);
416     return result;
417 }
418
419 //}}}
420
421 //{{{  integer_to_string()                      
422
423 // ----------------------------------------------------------------------------
424
425 std::string
426 Cdl::integer_to_string(cdl_int value, CdlValueFormat format)
427 {
428     std::string result;
429     Cdl::integer_to_string(value, result, format);
430     return result;
431 }
432
433 void
434 Cdl::integer_to_string(cdl_int value, std::string& target, CdlValueFormat format)
435 {
436     CYG_REPORT_FUNCNAME("Cdl::integer_to_string");
437     CYG_REPORT_FUNCARG2XV((long) value, format);
438
439     // Optimise this special case.
440     if (0 == value) {
441         if (CdlValueFormat_Hex == format) {
442             target = "0x0";
443         } else {
444             target = "0";
445         }
446         CYG_REPORT_RETVAL(true);
447         return;
448     }
449
450     // A local buffer to construct partial strings. This avoids
451     // unnecessary std::string reallocation.
452     // 64 bits and three bits at a time for octal numbers gives 21 digits,
453     // plus spares for the leading '0' and the terminator.
454     char local_buf[24];
455     char *local_ptr = &(local_buf[23]);
456     *local_ptr-- = '\0';
457     
458     if (CdlValueFormat_Hex == format) {
459
460         // Output the data as 0x... with either 8 or 16 digits,
461         // depending on the size.
462         int i;
463         for (i = 0; i < 8; i++) {
464             int tmp = (int) (value & 0x0F);
465             value   = value >> 4;
466             if (tmp < 10) {
467                 *local_ptr-- = '0' + tmp;
468             } else {
469                 *local_ptr-- = 'A' + (tmp - 10);
470             }
471         }
472         // Beware of right shifts that preserve the sign bit.
473         {
474             int tmp1 = (value & 0x0FFFF);
475             int tmp2 = ((value >> 16) & 0x0FFFF);
476             value    = (tmp2 << 16) + tmp1;
477         }
478         if (value != 0) {
479             for (i = 0; i < 8; i++) {
480                 int tmp = (int) (value & 0x0F);
481                 value   = value >> 4;
482                 if (tmp < 10) {
483                     *local_ptr-- = '0' + tmp;
484                 } else {
485                     *local_ptr-- = 'A' + (tmp - 10);
486                 }
487             }
488         }
489         *local_ptr-- = 'x';
490         *local_ptr   = '0';
491         target = std::string(local_ptr);
492         
493     } else if (CdlValueFormat_Octal == format) {
494
495         // Simply output the data three bits at a time, do not worry about any
496         // particular width restrictions. However it is necessary to worry
497         // about masking.
498         cdl_int mask = 0x1FFFFFFF;
499         mask = (mask << 16) | 0x0FFFF;
500         mask = (mask << 16) | 0x0FFFF;
501
502         target = "";
503         while (value > 0) {
504             int tmp = value & 0x07;
505             value   = (value >> 3) & mask;
506             *local_ptr-- = '0' + tmp;
507         }
508         *local_ptr = '0';
509         target = std::string(local_ptr);
510         
511     } else {
512         // A simple decimal number      
513         // Switch to positive arithmetic.
514         bool negative = false;
515         if (value < 0) {
516             negative = true;
517             value    = 0 - value;
518             // Only MININT cannot be converted using the above line
519             if (value < 0) {
520                 target = "-9223372036854775808";
521                 CYG_REPORT_RETVAL(true);
522                 return;
523             }
524         }
525
526         while (value > 0) {
527             int rem = (int) (value % 10);
528             value   = value / 10;
529             *local_ptr-- = '0' + rem;
530         }
531         if (negative) {
532             *local_ptr-- = '-';
533         }
534         local_ptr++;
535         target = std::string(local_ptr);
536     }
537     
538     CYG_REPORT_RETURN();
539     return;
540 }
541
542 //}}}
543 //{{{  double_to_string()                       
544
545 // ----------------------------------------------------------------------------
546
547 std::string
548 Cdl::double_to_string(double value, CdlValueFormat format)
549 {
550     std::string result;
551     Cdl::double_to_string(value, result, format);
552     return result;
553 }
554
555 void
556 Cdl::double_to_string(double value, std::string& result, CdlValueFormat format)
557 {
558     CYG_REPORT_FUNCNAME("Cdl::double_to_String");
559
560     char buf[256];      // This should be plenty :-)
561     sprintf(buf, "%.*G", DBL_DIG, value);
562     result = buf;
563
564     CYG_UNUSED_PARAM(CdlValueFormat, format);
565     CYG_REPORT_RETURN();
566 }
567
568 //}}}
569 //{{{  bool_to_string()                         
570
571 // ----------------------------------------------------------------------------
572 // Should the string results be 1/0 or true/false? Not that
573 // it really matters. The testcase in cdl1.cxx expects 1/0.
574 std::string
575 Cdl::bool_to_string(bool value)
576 {
577     std::string result;
578     Cdl::bool_to_string(value, result);
579     return result;
580 }
581
582 void
583 Cdl::bool_to_string(bool value, std::string& target)
584 {
585     CYG_REPORT_FUNCNAME("Cdl::bool_to_string");
586     CYG_REPORT_FUNCARG1( "value arg %ld", (long) value);
587
588     if (value)
589         target = "1";
590     else
591         target = "0";
592
593     CYG_REPORT_RETURN();
594 }
595
596 //}}}
597
598 //{{{  integer_to_double()                      
599
600 // ----------------------------------------------------------------------------
601 // Currently integer to double cannot fail, although there may well be loss
602 // of accurary. Eventually cdl_int may be an arbitrary precision integer
603 // in which case conversion to double is not guaranteed.
604 double
605 Cdl::integer_to_double(cdl_int value)
606 {
607     CYG_REPORT_FUNCNAME("Cdl::integer_to_double");
608
609     double result = (double) value;
610
611     CYG_REPORT_RETURN();
612     return result;
613 }
614
615 void
616 Cdl::integer_to_double(cdl_int value, double& target)
617 {
618     CYG_REPORT_FUNCNAME("Cdl::integer_to_double");
619
620     target = (double) value;
621
622     CYG_REPORT_RETURN();
623 }
624
625 //}}}
626 //{{{  double_to_integer()                      
627
628 // Conversion from double to integer is only allowed if there is no loss
629 // of data. modf() is useful here
630 bool
631 Cdl::double_to_integer(double value, cdl_int& target)
632 {
633     CYG_REPORT_FUNCNAMETYPE("Cdl::double_to_integer", "result %d");
634
635     bool   result = false;
636     
637     double integral;
638     double frac;
639
640     frac = modf(value, &integral);
641     if (0.0 == frac) {
642         // Looking good, but integral may still be too big.
643         cdl_int tmp = (cdl_int) integral;
644         if (tmp == value) {
645             // No fraction, no loss of data, everything looking good
646             target = tmp;
647             result = true;
648         }
649     }
650
651     CYG_REPORT_RETVAL(result);
652     return result;
653 }
654
655 //}}}
656
657 //}}}
658 //{{{  Cdl::xxx_to_yyy() - CDL-specific data types              
659
660 // ----------------------------------------------------------------------------
661 // Conversions between strings and flavors.
662
663 static struct {
664     char*               name;
665     CdlValueFlavor      flavor;
666 } valid_flavors[] = {
667     { "none",           CdlValueFlavor_None     },
668     { "bool",           CdlValueFlavor_Bool     },
669     { "booldata",       CdlValueFlavor_BoolData },
670     { "data",           CdlValueFlavor_Data     },
671     { 0,                CdlValueFlavor_Invalid  }
672 };
673
674 bool
675 Cdl::string_to_flavor(std::string name, CdlValueFlavor& target)
676 {
677     CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_flavor", "success %d");
678
679     bool result = false;
680
681     // First convert the argument to lower case. Arguably this is incorrect,
682     // Tcl is a case-sensitive language, but the conversion is unlikely ever
683     // to be harmfull.
684     for (std::string::iterator str_i = name.begin(); str_i != name.end(); str_i++) {
685         if (isupper(*str_i)) {
686             *str_i = tolower(*str_i);
687         }
688     }
689            
690     // Now look for a match in the table.
691     int match           = -1;
692     int i;
693     const char* c_str   = name.c_str();
694     int len             = strlen(c_str);
695
696     for (i = 0; 0 != valid_flavors[i].name; i++) {
697         if (0 == strncmp(c_str, valid_flavors[i].name, len)) {
698             // Check for an ambiguous string match.
699             // This cannot actually happen with the current flavor names.
700             if ( -1 != match) {
701                 break;
702             }
703             match = i;
704         }
705         
706     }
707     if (-1 != match) {
708         target = valid_flavors[match].flavor;
709         result = true;
710     }
711     CYG_REPORT_RETVAL(result);
712     return result;
713 }
714
715 bool
716 Cdl::flavor_to_string(CdlValueFlavor flavor, std::string& target)
717 {
718     CYG_REPORT_FUNCNAMETYPE("Cdl::flavor_to_string", "success %d");
719
720     bool result = false;
721
722     for (int i = 0; 0 != valid_flavors[i].name; i++) {
723         if (flavor == valid_flavors[i].flavor) {
724             target = valid_flavors[i].name;
725             result = true;
726             break;
727         }
728     }
729     
730     CYG_REPORT_RETVAL(result);
731     return result;
732 }
733
734 // ----------------------------------------------------------------------------
735 // Similar support for value sources.
736
737 static struct {
738     char*               name;
739     CdlValueSource      source;
740 } valid_sources[] = {
741     { "default",        CdlValueSource_Default  },
742     { "inferred",       CdlValueSource_Inferred },
743     { "wizard",         CdlValueSource_Wizard   },
744     { "user",           CdlValueSource_User     },
745     { 0,                CdlValueSource_Invalid  }
746 };
747
748 bool
749 Cdl::string_to_source(std::string name, CdlValueSource& target)
750 {
751     CYG_REPORT_FUNCNAMETYPE("Cdl::string_to_source", "success %d");
752
753     bool result = false;
754
755     // First convert the argument to lower case. Arguably this is incorrect,
756     // Tcl is a case-sensitive language, but the conversion is unlikely ever
757     // to be harmfull.
758     for (std::string::iterator str_i = name.begin(); str_i != name.end(); str_i++) {
759         if (isupper(*str_i)) {
760             *str_i = tolower(*str_i);
761         }
762     }
763            
764     // Now look for a match in the table.
765     int match           = -1;
766     int i;
767     const char* c_str   = name.c_str();
768     int len             = strlen(c_str);
769
770     for (i = 0; 0 != valid_sources[i].name; i++) {
771         if (0 == strncmp(c_str, valid_sources[i].name, len)) {
772             // Check for an ambiguous string match.
773             // This cannot actually happen with the current source names.
774             if ( -1 != match) {
775                 break;
776             }
777             match = i;
778         }
779         
780     }
781     if (-1 != match) {
782         target = valid_sources[match].source;
783         result = true;
784     }
785     CYG_REPORT_RETVAL(result);
786     return result;
787 }
788
789 bool
790 Cdl::source_to_string(CdlValueSource source, std::string& target)
791 {
792     CYG_REPORT_FUNCNAMETYPE("Cdl::source_to_string", "success %d");
793
794     bool result = false;
795
796     for (int i = 0; 0 != valid_sources[i].name; i++) {
797         if (source == valid_sources[i].source) {
798             target = valid_sources[i].name;
799             result = true;
800             break;
801         }
802     }
803     
804     CYG_REPORT_RETVAL(result);
805     return result;
806 }
807
808 //}}}
809 //{{{  Cdl::get_library_version()                               
810
811 // ----------------------------------------------------------------------------
812 // The version of the library actually lives inside configure.in. It gets
813 // exported into cdlconfig.h
814 std::string
815 Cdl::get_library_version(void)
816 {
817     return std::string(CYGNUM_LIBCDL_VERSION);
818 }
819
820 //}}}
821 //{{{  Cdl::set_interactive()                                   
822
823 // ----------------------------------------------------------------------------
824 // Some CDL scripts and some bits of the library may want to adapt depending
825 // on whether or not the application is running fully interactively or in
826 // batch mode. The primary distinction is that a batch program should never
827 // attempt to obtain user input, whether via Tk widgets or by other means.
828
829 bool Cdl::interactive   = false;
830
831 void
832 Cdl::set_interactive(bool value)
833 {
834     CYG_REPORT_FUNCNAME("Cdl::set_interactive");
835     CYG_REPORT_FUNCARG1D(value);
836
837     interactive = value;
838 }
839
840 bool
841 Cdl::is_interactive(void)
842 {
843     CYG_REPORT_FUNCNAMETYPE("Cdl::is_interactive", "interactive %d");
844     CYG_REPORT_RETVAL(interactive);
845     return interactive;
846 }
847
848 //}}}
849 //{{{  version support()                                        
850
851 // ----------------------------------------------------------------------------
852 // Packages may need to impose constraints on which versions of other
853 // packages they can coexist with. This requires some way of achieving
854 // a partial ordering of version numbers. Unfortunately there are many
855 // different ways of specifying a version number, and we cannot impose
856 // a single model on all third party package developers. Instead this
857 // routine performs some semi-intelligent comparisons of two version
858 // strings which should work in the vast majority of cases.
859 //
860 // The return value is as per strcmp(), -1 if the first entry is
861 // smaller (i.e. the more recent and hence hopefully the first in
862 // a list), +1 if the second entry is smaller, 0 if the two are
863 // identical.
864 //
865 // There is a big ambiguity between "derived" versions and "experimental"
866 // versions. Something like v0.3beta is experimental, i.e. it is older
867 // than the full release v0.3. On the other hand v0.3.p1 is a patched
868 // version of v0.3 and hence newer. This code uses the presence or otherwise
869 // of a separator to decide between the two cases.
870
871 // A utility routine which checks whether or not a character counts
872 // as a separator. Currently the characters . - and _ are all accepted
873 // as field separators.
874 //
875 // Arguably - should not be accepted as a separator. Instead if it preceeds
876 // a digit it could be interpreted as part of a prerelease number.
877
878 static bool
879 is_separator(int ch)
880 {
881     return ('.' == ch) || ('-' == ch) || ('_' == ch);
882 }
883     
884 int
885 Cdl::compare_versions(std::string arg1, std::string arg2)
886 {
887     CYG_REPORT_FUNCNAMETYPE("Cdl::compare_versions", "result %d");
888
889     if (arg1 == arg2) {
890         CYG_REPORT_RETVAL(0);
891         return 0;
892     }
893
894     // The version number "current" is special, it always indicates the most
895     // recent version e.g. as checked out from a CVS repository.
896     if ("current" == arg1) {
897         CYG_REPORT_RETVAL(-1);
898         return -1;
899     }
900     if ("current" == arg2) {
901         CYG_REPORT_RETVAL(1);
902         return 1;
903     }
904
905     const char* ptr1 = arg1.c_str();
906     const char* ptr2 = arg2.c_str();      
907     int num1    = 0;
908     int num2    = 0;
909
910     // If both strings start with 'v' or 'V', skip this. A minor variation in
911     // case at the start of a string should be ignored.
912     if ((('v' == *ptr1) || ('V' == *ptr1)) &&
913         (('v' == *ptr2) || ('V' == *ptr2))) {
914         ptr1++;
915         ptr2++;
916     }
917
918     // Now process the rest of the version string, one unit at a time.
919     while (1) {
920
921         if (('\0' == *ptr1) && ('\0' == *ptr2)) {
922             // Both strings have terminated at the same time. There
923             // may have been some spurious leading zeroes in numbers,
924             // or irrelevant differences in the separators.
925             CYG_REPORT_RETVAL(0);
926             return 0;
927         }
928         
929         if ('\0' == *ptr1) {
930             // The first string has ended first. If the second string currently
931             // points at a separator then arg2 is a derived version, e.g.
932             // v0.3.p1, and hence newer. Otherwise arg2 is an experimental
933             // version v0.3beta and hence older.
934             if (is_separator(*ptr2)) {
935                 CYG_REPORT_RETVAL(1);
936                 return 1;
937             } else {
938                 CYG_REPORT_RETVAL(-1);
939                 return -1;
940             }
941         }
942
943         if ('\0' == *ptr2) {
944             // As per the previous test.
945             if (is_separator(*ptr1)) {
946                 CYG_REPORT_RETVAL(-1);
947                 return -1;
948             } else {
949                 CYG_REPORT_RETVAL(1);
950                 return 1;
951             }
952         }
953
954         // If both strings currently point at numerical data, do a conversion and
955         // a numerical comparison.
956         if (isdigit(*ptr1) && isdigit(*ptr2)) {
957             num1 = 0;
958             num2 = 0;
959             // Strictly speaking there should be some overflow tests here, but it
960             // is not worth the trouble.
961             do {
962                 num1 = (10 * num1) + (*ptr1++ - '0');
963             } while(isdigit(*ptr1));
964             do {
965                 num2 = (10 * num2) + (*ptr2++ - '0');
966             } while(isdigit(*ptr2));
967             // v2.0 is newer than v1.0
968             if (num1 < num2) {
969                 CYG_REPORT_RETVAL(1);
970                 return 1;
971             } else if (num1 > num2) {
972                 CYG_REPORT_RETVAL(-1);
973                 return -1;
974             } else {
975                 continue;
976             }
977         }
978
979         // Non-numerical data. If the two characters are the same then
980         // move on. Note: this has to happen after numerical conversions
981         // to distinguish v10.0 and v1.0
982         if (*ptr1 == *ptr2) {
983             ptr1++; ptr2++;
984             continue;
985         }
986
987         // If both strings are currently at a separator then move on. All
988         // separators can be used interchangeably.
989         if (is_separator(*ptr1) && is_separator(*ptr2)) {
990             ptr1++; ptr2++;
991             continue;
992         }
993
994         // If only one string is at a separator, special action
995         // is needed. v1.1alpha is interpreted as earlier than
996         // v1.1, but v1.1.3 is a later release.
997         if (is_separator(*ptr1)) {
998             return -1;
999         } else if (is_separator(*ptr2)) {
1000             return 1;
1001         }
1002         
1003         // Two different characters, e.g. v1.0alpha vs. v1.0beta
1004         if (*ptr1 < *ptr2) {
1005             CYG_REPORT_RETVAL(1);
1006             return 1;
1007         } else {
1008             CYG_REPORT_RETVAL(-1);
1009             return -1;
1010         }
1011     }
1012
1013     // Not reachable.
1014 }
1015
1016 // ----------------------------------------------------------------------------
1017 // Given a version string, extract major, minor and release numbers.
1018 // Some or all of these may be absent. Basically the code just
1019 // iterates through the string looking for sequences of numbers.
1020
1021 static void
1022 version_extract_number(const std::string& version, unsigned int& index, std::string& result)
1023 {
1024     CYG_REPORT_FUNCNAME("version_extract_number");
1025
1026     // The calling code is expected to supply a sensible default.
1027     // Search for a digit
1028     for ( ; index < version.size(); index++) {
1029         if (isdigit(version[index])) {
1030             break;
1031         }
1032     }
1033     if (index != version.size()) {
1034         result = "";
1035         if ((index > 0) && ('-' == version[index-1])) {
1036             result = "-";
1037         }
1038         do {
1039             result += version[index++];
1040         } while ((index < version.size()) && isdigit(version[index]));
1041     }
1042     
1043     CYG_REPORT_RETURN();
1044 }
1045
1046 void
1047 Cdl::split_version_string(const std::string& version, std::string& major, std::string& minor, std::string& release)
1048 {
1049     CYG_REPORT_FUNCNAME("CdlMisc::split_version_string");
1050
1051     unsigned int index = 0;
1052     version_extract_number(version, index, major);
1053     version_extract_number(version, index, minor);
1054     version_extract_number(version, index, release);
1055     
1056     CYG_REPORT_RETURN();
1057 }
1058
1059 //}}}
1060 //{{{  Cdl::get_short_form()                                    
1061
1062 // ----------------------------------------------------------------------------
1063 // It is occasionally useful to take a full CDL name such as CYgpkg_KERNEL
1064 // and turn it into a short form such as "kernel". This involves discarding
1065 // everything up to and including the first underscore, then lowercasing
1066 // all subsequent characters.
1067 std::string
1068 Cdl::get_short_form(const std::string& original)
1069 {
1070     CYG_REPORT_FUNCNAME("CdlMisc::get_short_form");
1071
1072     std::string  result = "";
1073     unsigned int size = original.size();
1074     unsigned int i;
1075     for (i = 0; i < size; i++) {
1076         if ('_' == original[i]) {
1077             i++;
1078             break;
1079         }
1080     }
1081
1082     // Either at end of string, or just past the first underscore
1083     for ( ; i < size; i++) {
1084         if (isupper(original[i])) {
1085             result += tolower(original[i]);
1086         } else {
1087             result += original[i];
1088         }
1089     }
1090     
1091     CYG_REPORT_RETURN();
1092     return result;
1093 }
1094
1095 //}}}