Parameters.cpp

00001 /*
00002  *  Copyright (C) 2010  Regents of the University of Michigan
00003  *
00004  *   This program is free software: you can redistribute it and/or modify
00005  *   it under the terms of the GNU General Public License as published by
00006  *   the Free Software Foundation, either version 3 of the License, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details.
00013  *
00014  *   You should have received a copy of the GNU General Public License
00015  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  */
00017 
00018 #include "Parameters.h"
00019 #include "Constant.h"
00020 #include "MathConstant.h"
00021 #include "Error.h"
00022 
00023 #include <stdio.h>
00024 #include <ctype.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027 
00028 int Parameter::nameCol = 30;
00029 int Parameter::statusCol = 15;
00030 
00031 Parameter::Parameter(char c, const char * desc, void * v)
00032 {
00033     ch = (char) tolower(c);
00034     description = new char [strlen(desc) + 1];
00035     strcpy(description, desc);
00036     var = v;
00037     warnings = NULL;
00038 }
00039 
00040 bool Parameter::Read(int , char ** argv, int argn)
00041 {
00042     int   p = 0;
00043     char  c = (char) tolower(argv[argn][p]);
00044 
00045     if ((c == '-') || (c == '/'))
00046     {
00047         p++;
00048         c = (char) tolower(argv[argn][p]);
00049     }
00050 
00051     if (c == ch)
00052     {
00053         Translate(&(argv[argn][++p]));
00054         return true;
00055     }
00056     return false;
00057 }
00058 
00059 bool Parameter::TranslateExtras(const char * , const char *)
00060 {
00061     return false;
00062 }
00063 
00064 void Parameter::warning(const char * format, ...)
00065 {
00066     String buffer;
00067 
00068     va_list ap;
00069     va_start(ap, format);
00070     buffer.vprintf(format, ap);
00071     va_end(ap);
00072 
00073     if (warnings == NULL)
00074         ::warning(buffer);
00075     else
00076         (*warnings) += buffer;
00077 }
00078 
00079 void IntParameter::Translate(const char * value)
00080 {
00081     *(int *) var = atoi(value);
00082 }
00083 
00084 bool IntParameter::TranslateExtras(const char * value, const char * extras)
00085 {
00086     if (value[0] != 0 || !CheckInteger(extras))
00087         return false;
00088 
00089     Translate(extras);
00090 
00091     return true;
00092 }
00093 
00094 void IntParameter::Status()
00095 {
00096     fprintf(stderr, "%*s : %*d (-%c9999)\n", nameCol, description,
00097            statusCol, *(int *) var, ch);
00098 }
00099 
00100 void SwitchParameter::Translate(const char * value)
00101 {
00102     switch (*value)
00103     {
00104         case '+' :
00105             *(bool *) var = true;
00106             break;
00107         case '-' :
00108             *(bool *) var = false;
00109             break;
00110         case 0 :
00111             *(bool *) var = ! * (bool *) var;
00112             break;
00113         default :
00114             warning("Command line parameter -%c%s: the option '%c' has no meaning\n",
00115                     ch, value, value[0]);
00116     }
00117 }
00118 
00119 void SwitchParameter::Status()
00120 {
00121     fprintf(stderr, "%*s : %*s (-%c[+|-])\n", nameCol, description,
00122            statusCol, *(bool *) var == false ? "OFF" : "ON", ch);
00123 }
00124 
00125 DoubleParameter::DoubleParameter(char c, const char * desc, double & v)
00126         : Parameter(c, desc, &v)
00127 {
00128     precision = 2;
00129 }
00130 
00131 void DoubleParameter::Translate(const char * value)
00132 {
00133     if (value[0])
00134         *(double *) var = atof(value);
00135     else
00136         *(double *) var = _NAN_;
00137 }
00138 
00139 bool DoubleParameter::TranslateExtras(const char * value, const char * extras)
00140 {
00141     if (value[0] != 0 || !CheckDouble(extras))
00142         return false;
00143 
00144     Translate(extras);
00145 
00146     return true;
00147 }
00148 
00149 void DoubleParameter::Status()
00150 {
00151     double absolute_value = fabs(* (double *) var);
00152 
00153     if (*(double *) var == _NAN_)
00154         fprintf(stderr, "%*s : %*s (-%c99.999)\n", nameCol, description,
00155                statusCol, "NAN", ch);
00156     else if (absolute_value >= 0.00095)
00157         fprintf(stderr, "%*s : % *.*f (-%c99.999)\n", nameCol, description,
00158                 statusCol, precision, * (double *) var, ch);
00159     else if (absolute_value <= 1e-15)
00160         fprintf(stderr, "%*s : % *.0f (-%c99.999)\n", nameCol, description,
00161                statusCol, * (double *) var, ch);
00162     else
00163         fprintf(stderr, "%*s : %*.0e (-%c99.999)\n", nameCol, description,
00164                statusCol, *(double *) var, ch);
00165 }
00166 
00167 void StringParameter::Translate(const char * value)
00168 {
00169     String * s = (String *) var;
00170 
00171     *s = value;
00172 }
00173 
00174 bool StringParameter::TranslateExtras(const char * value, const char * extras)
00175 {
00176     if ((value[0] != 0) || ((!required) && (extras[0] == '-')))
00177         return false;
00178 
00179     String * s = (String *) var;
00180 
00181     *s = extras;
00182 
00183     return true;
00184 }
00185 
00186 void StringParameter::Status()
00187 {
00188     fprintf(stderr, "%*s : %*s (-%cname)\n", nameCol, description,
00189            statusCol, (const char *)(*(String *) var), ch);
00190 }
00191 
00192 void ListParameter::Status()
00193 {
00194     OptionList * l;
00195 
00196     for (l = options; l->ch != 0; l++)
00197         if (l->code == *((int *)var))
00198             break;
00199 
00200     fprintf(stderr, "%*s : %*s (-%c[%s])\n", nameCol, description,
00201            statusCol, l->description, ch, (const char *) key);
00202 }
00203 
00204 void ListParameter::Translate(const char * value)
00205 {
00206     OptionList * l;
00207 
00208     for (l = options; l->ch != 0; l++)
00209         if (tolower(l->ch) == tolower(value[0]))
00210             break;
00211 
00212     if (l->ch == 0 && tolower(value[0]) != 0)
00213         warning("Command line parameter -%c%s: the option '%c' has no meaning\n",
00214                 ch, value, value[0], (const char *) key);
00215 
00216     *((int*) var) = l->code;
00217 }
00218 
00219 ListParameter::ListParameter(char c, const char * desc, int & v, OptionList * opt)
00220         : Parameter(c, desc, &v)
00221 {
00222     options = opt;
00223 
00224     for (OptionList * l = options; l->ch != 0; l++)
00225     {
00226         key += l->ch;
00227         key += '|';
00228     }
00229 
00230     key.SetLength(key.Length() - 1);
00231 }
00232 
00233 SetParameter::SetParameter(char c, const char * desc, int & v, OptionList * opt)
00234         : Parameter(c, desc, &v)
00235 {
00236     options = opt;
00237 
00238     for (OptionList * l = options; l->ch != 0; l++)
00239     {
00240         key += l->ch;
00241         key += '|';
00242     }
00243     key.SetLength(key.Length() - 1);
00244 }
00245 
00246 void SetParameter::Status()
00247 {
00248     bool first = 0;
00249     int  temp = * (int *) var;
00250 
00251     for (OptionList * l = options; l->ch != 0; l++)
00252         if ((l->code & temp) || (l->code == *(int *) var))
00253         {
00254             if (!first)
00255                 fprintf(stderr, "%*s : %*s (-%c{%s})\n", nameCol, description,
00256                        statusCol, l->description, ch, (const char *) key);
00257             else
00258                 fprintf(stderr, "%*s & %*s\n", nameCol, "",
00259                        statusCol, l->description);
00260             first = true;
00261             temp &= ~l->code;
00262         }
00263 }
00264 
00265 void SetParameter::Translate(const char * value)
00266 {
00267     *(int*)var = 0;
00268 
00269     for (const char * chr = value; *chr != 0; chr++)
00270     {
00271         int valid = false;
00272 
00273         for (OptionList * l = options; l->ch != 0; l++)
00274             if (tolower(l->ch) == tolower(*chr))
00275             {
00276                 *((int*) var) |= l->code;
00277                 valid = true;
00278             }
00279 
00280         if (!valid)
00281             warning("Command line parameter -%c%s: the option '%c' has no meaning\n",
00282                     ch, value, *chr);
00283     }
00284 }
00285 
00286 LongParameters::LongParameters(const char * desc, LongParameterList * lst)
00287         : Parameter('-', desc, NULL)
00288 {
00289     list = lst;
00290 
00291     index.Clear();
00292     legacyIndex.Clear();
00293     group_len = 0;
00294 
00295     LongParameterList * ptr = list + 1;
00296 
00297     while (ptr->description != NULL)
00298     {
00299         if (ptr->type == LP_LEGACY_PARAMETERS)
00300             break;
00301 
00302         if (ptr->value != NULL)
00303             index.Add(ptr->description, ptr);
00304         else
00305             group_len = max(strlen(ptr->description), group_len);
00306 
00307         ptr++;
00308     }
00309 
00310     while (ptr->description != NULL)
00311     {
00312         if (ptr->value != NULL)
00313             legacyIndex.Add(ptr->description, ptr);
00314 
00315         ptr++;
00316     }
00317 
00318     precision = 2;
00319 }
00320 
00321 void LongParameters::ExplainAmbiguity(const char * cstr)
00322 {
00323     String value(cstr);
00324 
00325     int p = value.FastFindChar(':');
00326     String stem = p == -1 ? value : value.Left(p);
00327     String matches;
00328 
00329     for (int i = 0; i < index.Length(); i++)
00330         if (index[i].SlowCompareToStem(stem) == 0)
00331         {
00332             if (matches.Length() + index[i].Length() > 50)
00333             {
00334                 matches += " ...";
00335                 break;
00336             }
00337 
00338             matches.catprintf(" --%s", (const char *) index[i]);
00339         }
00340 
00341     warning("Ambiguous --%s matches%s\n",
00342             (const char *) value, (const char *) matches);
00343 }
00344 
00345 void LongParameters::Translate(const char * cstr)
00346 {
00347     String value(cstr);
00348 
00349     int p = value.FastFindChar(':');
00350     int option = p == -1 ? index.FindStem(value) : index.FindStem(value.Left(p));
00351 
00352     if (option == -2)
00353     {
00354         ExplainAmbiguity(cstr);
00355         return;
00356     }
00357 
00358     LongParameterList * ptr;
00359 
00360     if (option >= 0)
00361         ptr = (LongParameterList *) index.Object(option);
00362     else
00363     {
00364         int alternate = p == -1 ? legacyIndex.FindFirstStem(value) :
00365                         legacyIndex.FindFirstStem(value.Left(p));
00366 
00367         if (alternate < 0)
00368         {
00369             warning("Command line parameter --%s is undefined\n", (const char *) value);
00370             return;
00371         }
00372 
00373         ptr = (LongParameterList *) legacyIndex.Object(alternate);
00374         ptr->touched = true;
00375     }
00376 
00377     if (ptr->type == LP_BOOL_PARAMETER)
00378     {
00379         if (p == -1)
00380             * (bool *) ptr->value ^= true;
00381         else
00382             *(bool *) ptr->value = value.SubStr(p + 1).SlowCompare("ON") == 0;
00383 
00384         // In exclusive groups, only one option may be selected
00385         if (ptr->exclusive)
00386         {
00387             for (int i = -1; ptr[i].exclusive; i--) *(bool *)ptr[i].value = false;
00388             for (int i =  1; ptr[i].exclusive; i++) *(bool *)ptr[i].value = false;
00389         }
00390     }
00391     else if (ptr->type == LP_INT_PARAMETER)
00392         if (p == -1)
00393             * (int *) ptr->value = * (int *) ptr->value ? 0 : 1;
00394         else
00395             *(int *) ptr->value = value.SubStr(p + 1).SlowCompare("ON") == 0 ?
00396                                   1 : value.SubStr(p + 1).AsInteger();
00397     else if (ptr->type == LP_DOUBLE_PARAMETER)
00398     {
00399         if (p != -1)
00400             * (double *) ptr->value = value.SubStr(p + 1).AsDouble();
00401     }
00402     else if (ptr->type == LP_STRING_PARAMETER)
00403     {
00404         if (p != -1)
00405             * (String *) ptr->value = value.SubStr(p + 1);
00406     }
00407 }
00408 
00409 bool LongParameters::TranslateExtras(const char * cstr, const char * extras)
00410 {
00411     if (strchr(cstr, ':') != NULL)
00412         return false;
00413 
00414     int option = index.FindStem(cstr);
00415 
00416     if (option == -2)
00417     {
00418         // No need to explain ambiguity here ... will be handle by later call
00419         // to Translate()
00420         // ExplainAmbiguity(cstr);
00421         return false;
00422     }
00423 
00424     LongParameterList * ptr;
00425 
00426     if (option >= 0)
00427         ptr = (LongParameterList *) index.Object(option);
00428     else
00429     {
00430         option = legacyIndex.FindFirstStem(cstr);
00431 
00432         if (option < 0)
00433             return false;
00434 
00435         ptr = (LongParameterList *) legacyIndex.Object(option);
00436         ptr->touched = true;
00437     }
00438 
00439     if (ptr->type == LP_INT_PARAMETER && CheckInteger(extras))
00440     {
00441         *(int *) ptr->value = atoi(extras);
00442         return true;
00443     }
00444     else if (ptr->type == LP_DOUBLE_PARAMETER && CheckDouble(extras))
00445     {
00446         *(double *) ptr->value = atof(extras);
00447         return true;
00448     }
00449     else if (ptr->type == LP_STRING_PARAMETER)
00450     {
00451         *(String *) ptr->value = extras;
00452         return true;
00453     }
00454 
00455     return false;
00456 }
00457 
00458 void LongParameters::Status(LongParameterList * ptr, int & line_len, bool & need_a_comma)
00459 {
00460     String state;
00461     int line_start = group_len ? group_len + 5 : 0;
00462 
00463     if (ptr->value == NULL)
00464     {
00465         fprintf(stderr, "%s %*s :", need_a_comma ? "\n" : "", group_len + 2, ptr->description);
00466         need_a_comma = false;
00467         line_len = line_start;
00468     }
00469     else
00470     {
00471         if (ptr->type == LP_BOOL_PARAMETER)
00472             state = * (bool *) ptr->value ? " [ON]" : "";
00473         else if (ptr->type == LP_INT_PARAMETER)
00474             if (((* (int *) ptr->value == 1) && (ptr->exclusive)) || (* (int *) ptr->value == 0))
00475                 state = * (int *) ptr->value ? " [ON]" : "";
00476             else
00477                 state = " [", state += * (int *) ptr->value, state += ']';
00478         else if (ptr->type == LP_DOUBLE_PARAMETER)
00479             if (* (double *) ptr->value != _NAN_)
00480             {
00481                 double value = * (double *) ptr->value;
00482 
00483                 state = " [";
00484                 if (value == 0.0 || value >= 0.01)
00485                     state.catprintf("%.*f", precision, value);
00486                 else
00487                     state.catprintf("%.1e", value);
00488                 state += ']';
00489             }
00490             else
00491                 state = "";
00492         else if (ptr->type == LP_STRING_PARAMETER)
00493             state = " [" + * (String *) ptr->value + "]";
00494 
00495         int item_len = 3 + strlen(ptr->description) + need_a_comma + state.Length();
00496 
00497         if (item_len + line_len > 78 && line_len > line_start)
00498         {
00499             line_len = line_start;
00500             fprintf(stderr, "%s\n%*s", need_a_comma ? "," : "", line_len,  "");
00501             need_a_comma = 0;
00502             item_len -= 1;
00503         }
00504 
00505         fprintf(stderr, "%s --%s%s", need_a_comma ? "," : (need_a_comma = true, ""),
00506                ptr->description, (const char *) state);
00507 
00508         need_a_comma = true;
00509         line_len += item_len;
00510     }
00511 }
00512 
00513 void LongParameters::Status()
00514 {
00515     if (description != NULL && description[0] != 0)
00516         fprintf(stderr, "\n%s\n", description);
00517 
00518     bool need_a_comma = false;
00519     int  line_len = 0;
00520 
00521     bool legacy_parameters = false;
00522     bool legacy_count = 0;
00523 
00524     for (LongParameterList * ptr = list + 1; ptr->description != NULL; ptr++)
00525         if (ptr->type == LP_LEGACY_PARAMETERS)
00526             legacy_parameters = true;
00527         else if (legacy_parameters == false)
00528             Status(ptr, line_len, need_a_comma);
00529         else if (ptr->touched)
00530         {
00531             if (legacy_count == 0)
00532             {
00533                 fprintf(stderr, "\n\nAdditional Options:\n %*s ", group_len + 3, "");
00534                 line_len = group_len + 5;
00535                 need_a_comma = false;
00536             }
00537 
00538             Status(ptr, line_len, need_a_comma);
00539             legacy_count++;
00540         }
00541 
00542     fprintf(stderr, "\n");
00543 }
00544 
00545 void ParameterList::Add(Parameter * p)
00546 {
00547     if (count + 1 >= size)
00548         error("Parameter list size should be increased");
00549 
00550     p->SetWarningBuffer(warnings);
00551     pl[count++] = p;
00552 };
00553 
00554 void ParameterList::Read(int argc, char ** argv, int start)
00555 {
00556     MakeString(argc, argv, start);
00557     for (int i=start; i < argc; i++)
00558     {
00559         bool success = false;
00560 
00561         if (argv[i][0] == '-' && argv[i][1])
00562             for (int j=0; j<count; j++)
00563             {
00564                 success = tolower(argv[i][1]) == pl[j]->ch;
00565 
00566                 if (success)
00567                 {
00568                     if ((i+1 < argc) && pl[j]->TranslateExtras(argv[i]+2, argv[i+1]))
00569                         i++;
00570                     else if (argv[i][2] == 0 && (i+1 < argc) && (argv[i + 1][0] != '-'))
00571                         pl[j]->Translate(argv[++i]);
00572                     else
00573                         pl[j]->Translate(argv[i] + 2);
00574                     break;
00575                 }
00576             }
00577 
00578         if (!success)
00579         {
00580             String warning;
00581 
00582             warning.printf("Command line parameter %s (#%d) ignored\n", argv[i], i);
00583             warnings += warning;
00584         }
00585     }
00586 }
00587 
00588 int ParameterList::ReadWithTrailer(int argc, char ** argv, int start)
00589 {
00590     MakeString(argc, argv, start);
00591 
00592     int last_success = start - 1;
00593     bool split = false;
00594 
00595     for (int i=start; i < argc; i++)
00596     {
00597         bool success = false;
00598 
00599         if (argv[i][0] == '-' && argv[i][1])
00600             for (int j=0; j<count; j++)
00601             {
00602                 success = tolower(argv[i][1]) == pl[j]->ch;
00603 
00604                 if (success)
00605                 {
00606                     if ((i+1 < argc) && pl[j]->TranslateExtras(argv[i]+2, argv[i+1]))
00607                         split = true;
00608                     else if (argv[i][2] == 0 && (i+1 < argc) && (argv[i + 1][0] != '-'))
00609                         pl[j]->Translate(argv[i + 1]), split = true;
00610                     else
00611                         pl[j]->Translate(argv[i] + 2);
00612                     break;
00613                 }
00614             }
00615 
00616         if (success)
00617             for (last_success++; last_success < i; last_success++)
00618                 warnings.printf("Command line parameter %s (#%d) ignored\n",
00619                                 argv[last_success], last_success);
00620 
00621         if (split)
00622         {
00623             split = false;
00624             last_success++;
00625             i++;
00626         }
00627     }
00628 
00629     return last_success;
00630 };
00631 
00632 
00633 void ParameterList::Status()
00634 {
00635     fprintf(stderr, "\nThe following parameters are available.  Ones with \"[]\" are in effect:\n");
00636 
00637     for (int i=0; i<count; i++)
00638         pl[i]->Status();
00639 
00640     fprintf(stderr, "\n");
00641 
00642     if (warnings.Length())
00643     {
00644         ::warning("Problems encountered parsing command line:\n\n%s",
00645                   (const char *) warnings);
00646         warnings.Clear();
00647     }
00648 
00649     if (messages.Length())
00650         fprintf(stderr, "NOTES:\n%s\n", (const char *) messages);
00651 }
00652 
00653 void ParameterList::MakeString(int argc, char ** argv, int start)
00654 {
00655     int len = 0;
00656 
00657     for (int i=start; i<argc; i++)
00658         len += strlen(argv[i]) + 1;
00659 
00660     string = new char [len+1];
00661     string[0] = 0;
00662 
00663     for (int i=start; i<argc; i++)
00664     {
00665         strcat(string, argv[i]);
00666         strcat(string, " ");
00667     }
00668 }
00669 
00670 ParameterList::~ParameterList()
00671 {
00672     for (int i = 0; i < count; i++)
00673         delete pl[i];
00674     delete [] pl;
00675     delete [] string;
00676 };
00677 
00678 bool Parameter::CheckInteger(const char * value)
00679 {
00680     if (value[0] != '+' && value[0] != '-' &&
00681             (value[0] < '0' || value[0] > '9'))
00682         return false;
00683 
00684     int pos = 1;
00685     while (value[pos] != 0)
00686         if (value[pos] < '0' || value[pos] > '9')
00687             return false;
00688         else
00689             pos++;
00690 
00691     return true;
00692 }
00693 
00694 bool Parameter::CheckDouble(const char * value)
00695 {
00696     if (value[0] != '+' && value[0] != '-' && value[0] != '.' &&
00697             (value[0] < '0'  || value[0] > '9'))
00698     {
00699         return false;
00700     }
00701 
00702     bool decimal = value[0] == '.';
00703 
00704     for (int pos = 1; value[pos] != 0; pos++)
00705     {
00706         if (value[pos] < '0' || value[pos] > '9')
00707         {
00708             if (!decimal && value[pos] == '.')
00709             {
00710                 decimal = true;
00711             }
00712             else if (value[pos] == 'e' || value[pos] == 'E')
00713             {
00714                 return CheckInteger(value + pos + 1);
00715             }
00716         }
00717     }
00718 
00719     return true;
00720 }
00721 
00722 void ParameterList::Enforce(bool & var, bool value, const char * format, ...)
00723 {
00724     if (var == value)
00725         return;
00726 
00727     var = value;
00728 
00729     String buffer;
00730 
00731     va_list ap;
00732     va_start(ap, format);
00733     buffer.vprintf(format, ap);
00734     va_end(ap);
00735 
00736     messages += buffer;
00737 }
00738 
00739 void ParameterList::Enforce(int & var, int value, const char * format, ...)
00740 {
00741     if (var == value)
00742         return;
00743 
00744     var = value;
00745 
00746     String buffer;
00747 
00748     va_list ap;
00749     va_start(ap, format);
00750     buffer.vprintf(format, ap);
00751     va_end(ap);
00752 
00753     messages += buffer;
00754 }
00755 
00756 void ParameterList::Enforce(double & var, double value, const char * format, ...)
00757 {
00758     if (var == value)
00759         return;
00760 
00761     var = value;
00762 
00763     String buffer;
00764 
00765     va_list ap;
00766     va_start(ap, format);
00767     buffer.vprintf(format, ap);
00768     va_end(ap);
00769 
00770     messages += buffer;
00771 }
00772 
00773 void ParameterList::Enforce(String & var, const char * value, const char * format, ...)
00774 {
00775     if (var.SlowCompare(value) == 0)
00776         return;
00777 
00778     var = value;
00779 
00780     String buffer;
00781     va_list ap;
00782     va_start(ap, format);
00783     buffer.vprintf(format, ap);
00784     va_end(ap);
00785 
00786     messages += buffer;
00787 }
Generated on Tue Sep 6 17:52:00 2011 for libStatGen Software by  doxygen 1.6.3