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 
00129 void DoubleParameter::Translate(const char * value)
00130 {
00131     if (value[0])
00132         *(double *) var = atof(value);
00133     else
00134         *(double *) var = _NAN_;
00135 }
00136 
00137 bool DoubleParameter::TranslateExtras(const char * value, const char * extras)
00138 {
00139     if (value[0] != 0 || !CheckDouble(extras))
00140         return false;
00141 
00142     Translate(extras);
00143 
00144     return true;
00145 }
00146 
00147 void DoubleParameter::Status()
00148 {
00149     double absolute_value = fabs(* (double *) var);
00150 
00151     if (*(double *) var == _NAN_)
00152         fprintf(stderr, "%*s : %*s (-%c99.999)\n", nameCol, description,
00153                statusCol, "NAN", ch);
00154     else if (absolute_value >= 0.00095)
00155         fprintf(stderr, "%*s : % *.3f (-%c99.999)\n", nameCol, description,
00156                statusCol, * (double *) var, ch);
00157     else if (absolute_value <= 1e-15)
00158         fprintf(stderr, "%*s : % *.0f (-%c99.999)\n", nameCol, description,
00159                statusCol, * (double *) var, ch);
00160     else
00161         fprintf(stderr, "%*s : %*.0e (-%c99.999)\n", nameCol, description,
00162                statusCol, *(double *) var, ch);
00163 }
00164 
00165 void StringParameter::Translate(const char * value)
00166 {
00167     String * s = (String *) var;
00168 
00169     *s = value;
00170 }
00171 
00172 bool StringParameter::TranslateExtras(const char * value, const char * extras)
00173 {
00174     if ((value[0] != 0) || ((!required) && (extras[0] == '-')))
00175         return false;
00176 
00177     String * s = (String *) var;
00178 
00179     *s = extras;
00180 
00181     return true;
00182 }
00183 
00184 void StringParameter::Status()
00185 {
00186     fprintf(stderr, "%*s : %*s (-%cname)\n", nameCol, description,
00187            statusCol, (const char *)(*(String *) var), ch);
00188 }
00189 
00190 void ListParameter::Status()
00191 {
00192     OptionList * l;
00193 
00194     for (l = options; l->ch != 0; l++)
00195         if (l->code == *((int *)var))
00196             break;
00197 
00198     fprintf(stderr, "%*s : %*s (-%c[%s])\n", nameCol, description,
00199            statusCol, l->description, ch, (const char *) key);
00200 }
00201 
00202 void ListParameter::Translate(const char * value)
00203 {
00204     OptionList * l;
00205 
00206     for (l = options; l->ch != 0; l++)
00207         if (tolower(l->ch) == tolower(value[0]))
00208             break;
00209 
00210     if (l->ch == 0 && tolower(value[0]) != 0)
00211         warning("Command line parameter -%c%s: the option '%c' has no meaning\n",
00212                 ch, value, value[0], (const char *) key);
00213 
00214     *((int*) var) = l->code;
00215 }
00216 
00217 ListParameter::ListParameter(char c, const char * desc, int & v, OptionList * opt)
00218         : Parameter(c, desc, &v)
00219 {
00220     options = opt;
00221 
00222     for (OptionList * l = options; l->ch != 0; l++)
00223     {
00224         key += l->ch;
00225         key += '|';
00226     }
00227 
00228     key.SetLength(key.Length() - 1);
00229 }
00230 
00231 SetParameter::SetParameter(char c, const char * desc, int & v, OptionList * opt)
00232         : Parameter(c, desc, &v)
00233 {
00234     options = opt;
00235 
00236     for (OptionList * l = options; l->ch != 0; l++)
00237     {
00238         key += l->ch;
00239         key += '|';
00240     }
00241     key.SetLength(key.Length() - 1);
00242 }
00243 
00244 void SetParameter::Status()
00245 {
00246     bool first = 0;
00247     int  temp = * (int *) var;
00248 
00249     for (OptionList * l = options; l->ch != 0; l++)
00250         if ((l->code & temp) || (l->code == *(int *) var))
00251         {
00252             if (!first)
00253                 fprintf(stderr, "%*s : %*s (-%c{%s})\n", nameCol, description,
00254                        statusCol, l->description, ch, (const char *) key);
00255             else
00256                 fprintf(stderr, "%*s & %*s\n", nameCol, "",
00257                        statusCol, l->description);
00258             first = true;
00259             temp &= ~l->code;
00260         }
00261 }
00262 
00263 void SetParameter::Translate(const char * value)
00264 {
00265     *(int*)var = 0;
00266 
00267     for (const char * chr = value; *chr != 0; chr++)
00268     {
00269         int valid = false;
00270 
00271         for (OptionList * l = options; l->ch != 0; l++)
00272             if (tolower(l->ch) == tolower(*chr))
00273             {
00274                 *((int*) var) |= l->code;
00275                 valid = true;
00276             }
00277 
00278         if (!valid)
00279             warning("Command line parameter -%c%s: the option '%c' has no meaning\n",
00280                     ch, value, *chr);
00281     }
00282 }
00283 
00284 LongParameters::LongParameters(const char * desc, LongParameterList * lst)
00285         : Parameter('-', desc, NULL)
00286 {
00287     list = lst;
00288 
00289     index.Clear();
00290     legacyIndex.Clear();
00291     group_len = 0;
00292 
00293     LongParameterList * ptr = list + 1;
00294 
00295     while (ptr->description != NULL)
00296     {
00297         if (ptr->type == LP_LEGACY_PARAMETERS)
00298             break;
00299 
00300         if (ptr->value != NULL)
00301             index.Add(ptr->description, ptr);
00302         else
00303             group_len = max(strlen(ptr->description), group_len);
00304 
00305         ptr++;
00306     }
00307 
00308     while (ptr->description != NULL)
00309     {
00310         if (ptr->value != NULL)
00311             legacyIndex.Add(ptr->description, ptr);
00312 
00313         ptr++;
00314     }
00315 }
00316 
00317 void LongParameters::ExplainAmbiguity(const char * cstr)
00318 {
00319     String value(cstr);
00320 
00321     int p = value.FastFindChar(':');
00322     String stem = p == -1 ? value : value.Left(p);
00323     String matches;
00324 
00325     for (int i = 0; i < index.Length(); i++)
00326         if (index[i].SlowCompareToStem(stem) == 0)
00327         {
00328             if (matches.Length() + index[i].Length() > 50)
00329             {
00330                 matches += " ...";
00331                 break;
00332             }
00333 
00334             matches.catprintf(" --%s", (const char *) index[i]);
00335         }
00336 
00337     warning("Ambiguous --%s matches%s\n",
00338             (const char *) value, (const char *) matches);
00339 }
00340 
00341 void LongParameters::Translate(const char * cstr)
00342 {
00343     String value(cstr);
00344 
00345     int p = value.FastFindChar(':');
00346     int option = p == -1 ? index.FindStem(value) : index.FindStem(value.Left(p));
00347 
00348     if (option == -2)
00349     {
00350         ExplainAmbiguity(cstr);
00351         return;
00352     }
00353 
00354     LongParameterList * ptr;
00355 
00356     if (option >= 0)
00357         ptr = (LongParameterList *) index.Object(option);
00358     else
00359     {
00360         int alternate = p == -1 ? legacyIndex.FindFirstStem(value) :
00361                         legacyIndex.FindFirstStem(value.Left(p));
00362 
00363         if (alternate < 0)
00364         {
00365             warning("Command line parameter --%s is undefined\n", (const char *) value);
00366             return;
00367         }
00368 
00369         ptr = (LongParameterList *) legacyIndex.Object(alternate);
00370         ptr->touched = true;
00371     }
00372 
00373     if (ptr->type == LP_BOOL_PARAMETER)
00374     {
00375         if (p == -1)
00376             * (bool *) ptr->value ^= true;
00377         else
00378             *(bool *) ptr->value = value.SubStr(p + 1).SlowCompare("ON") == 0;
00379 
00380         // In exclusive groups, only one option may be selected
00381         if (ptr->exclusive)
00382         {
00383             for (int i = -1; ptr[i].exclusive; i--) *(bool *)ptr[i].value = false;
00384             for (int i =  1; ptr[i].exclusive; i++) *(bool *)ptr[i].value = false;
00385         }
00386     }
00387     else if (ptr->type == LP_INT_PARAMETER)
00388         if (p == -1)
00389             * (int *) ptr->value = * (int *) ptr->value ? 0 : 1;
00390         else
00391             *(int *) ptr->value = value.SubStr(p + 1).SlowCompare("ON") == 0 ?
00392                                   1 : value.SubStr(p + 1).AsInteger();
00393     else if (ptr->type == LP_DOUBLE_PARAMETER)
00394     {
00395         if (p != -1)
00396             * (double *) ptr->value = value.SubStr(p + 1).AsDouble();
00397     }
00398     else if (ptr->type == LP_STRING_PARAMETER)
00399     {
00400         if (p != -1)
00401             * (String *) ptr->value = value.SubStr(p + 1);
00402     }
00403 }
00404 
00405 bool LongParameters::TranslateExtras(const char * cstr, const char * extras)
00406 {
00407     if (strchr(cstr, ':') != NULL)
00408         return false;
00409 
00410     int option = index.FindStem(cstr);
00411 
00412     if (option == -2)
00413     {
00414         // No need to explain ambiguity here ... will be handle by later call
00415         // to Translate()
00416         // ExplainAmbiguity(cstr);
00417         return false;
00418     }
00419 
00420     LongParameterList * ptr;
00421 
00422     if (option >= 0)
00423         ptr = (LongParameterList *) index.Object(option);
00424     else
00425     {
00426         option = legacyIndex.FindFirstStem(cstr);
00427 
00428         if (option < 0)
00429             return false;
00430 
00431         ptr = (LongParameterList *) legacyIndex.Object(option);
00432         ptr->touched = true;
00433     }
00434 
00435     if (ptr->type == LP_INT_PARAMETER && CheckInteger(extras))
00436     {
00437         *(int *) ptr->value = atoi(extras);
00438         return true;
00439     }
00440     else if (ptr->type == LP_DOUBLE_PARAMETER && CheckDouble(extras))
00441     {
00442         *(double *) ptr->value = atof(extras);
00443         return true;
00444     }
00445     else if (ptr->type == LP_STRING_PARAMETER)
00446     {
00447         *(String *) ptr->value = extras;
00448         return true;
00449     }
00450 
00451     return false;
00452 }
00453 
00454 void LongParameters::Status(LongParameterList * ptr, int & line_len, bool & need_a_comma)
00455 {
00456     String state;
00457     int line_start = group_len ? group_len + 5 : 0;
00458 
00459     if (ptr->value == NULL)
00460     {
00461         fprintf(stderr, "%s %*s :", need_a_comma ? "\n" : "", group_len + 2, ptr->description);
00462         need_a_comma = false;
00463         line_len = line_start;
00464     }
00465     else
00466     {
00467         if (ptr->type == LP_BOOL_PARAMETER)
00468             state = * (bool *) ptr->value ? " [ON]" : "";
00469         else if (ptr->type == LP_INT_PARAMETER)
00470             if (((* (int *) ptr->value == 1) && (ptr->exclusive)) || (* (int *) ptr->value == 0))
00471                 state = * (int *) ptr->value ? " [ON]" : "";
00472             else
00473                 state = " [", state += * (int *) ptr->value, state += ']';
00474         else if (ptr->type == LP_DOUBLE_PARAMETER)
00475             if (* (double *) ptr->value != _NAN_)
00476             {
00477                 double value = * (double *) ptr->value;
00478 
00479                 state = " [";
00480                 if (value == 0.0 || value >= 0.01)
00481                     state.catprintf("%.2f", value);
00482                 else
00483                     state.catprintf("%.1e", value);
00484                 state += ']';
00485             }
00486             else
00487                 state = "";
00488         else if (ptr->type == LP_STRING_PARAMETER)
00489             state = " [" + * (String *) ptr->value + "]";
00490 
00491         int item_len = 3 + strlen(ptr->description) + need_a_comma + state.Length();
00492 
00493         if (item_len + line_len > 78 && line_len > line_start)
00494         {
00495             line_len = line_start;
00496             fprintf(stderr, "%s\n%*s", need_a_comma ? "," : "", line_len,  "");
00497             need_a_comma = 0;
00498             item_len -= 1;
00499         }
00500 
00501         fprintf(stderr, "%s --%s%s", need_a_comma ? "," : (need_a_comma = true, ""),
00502                ptr->description, (const char *) state);
00503 
00504         need_a_comma = true;
00505         line_len += item_len;
00506     }
00507 }
00508 
00509 void LongParameters::Status()
00510 {
00511     if (description != NULL && description[0] != 0)
00512         fprintf(stderr, "\n%s\n", description);
00513 
00514     bool need_a_comma = false;
00515     int  line_len = 0;
00516 
00517     bool legacy_parameters = false;
00518     bool legacy_count = 0;
00519 
00520     for (LongParameterList * ptr = list + 1; ptr->description != NULL; ptr++)
00521         if (ptr->type == LP_LEGACY_PARAMETERS)
00522             legacy_parameters = true;
00523         else if (legacy_parameters == false)
00524             Status(ptr, line_len, need_a_comma);
00525         else if (ptr->touched)
00526         {
00527             if (legacy_count == 0)
00528             {
00529                 fprintf(stderr, "\n\nAdditional Options:\n %*s ", group_len + 3, "");
00530                 line_len = group_len + 5;
00531                 need_a_comma = false;
00532             }
00533 
00534             Status(ptr, line_len, need_a_comma);
00535             legacy_count++;
00536         }
00537 
00538     fprintf(stderr, "\n");
00539 }
00540 
00541 void ParameterList::Add(Parameter * p)
00542 {
00543     if (count + 1 >= size)
00544         error("Parameter list size should be increased");
00545 
00546     p->SetWarningBuffer(warnings);
00547     pl[count++] = p;
00548 };
00549 
00550 void ParameterList::Read(int argc, char ** argv, int start)
00551 {
00552     MakeString(argc, argv, start);
00553     for (int i=start; i < argc; i++)
00554     {
00555         bool success = false;
00556 
00557         if (argv[i][0] == '-' && argv[i][1])
00558             for (int j=0; j<count; j++)
00559             {
00560                 success = tolower(argv[i][1]) == pl[j]->ch;
00561 
00562                 if (success)
00563                 {
00564                     if ((i+1 < argc) && pl[j]->TranslateExtras(argv[i]+2, argv[i+1]))
00565                         i++;
00566                     else if (argv[i][2] == 0 && (i+1 < argc) && (argv[i + 1][0] != '-'))
00567                         pl[j]->Translate(argv[++i]);
00568                     else
00569                         pl[j]->Translate(argv[i] + 2);
00570                     break;
00571                 }
00572             }
00573 
00574         if (!success)
00575         {
00576             String warning;
00577 
00578             warning.printf("Command line parameter %s (#%d) ignored\n", argv[i], i);
00579             warnings += warning;
00580         }
00581     }
00582 }
00583 
00584 int ParameterList::ReadWithTrailer(int argc, char ** argv, int start)
00585 {
00586     MakeString(argc, argv, start);
00587 
00588     int last_success = start - 1;
00589     bool split = false;
00590 
00591     for (int i=start; i < argc; i++)
00592     {
00593         bool success = false;
00594 
00595         if (argv[i][0] == '-' && argv[i][1])
00596             for (int j=0; j<count; j++)
00597             {
00598                 success = tolower(argv[i][1]) == pl[j]->ch;
00599 
00600                 if (success)
00601                 {
00602                     if ((i+1 < argc) && pl[j]->TranslateExtras(argv[i]+2, argv[i+1]))
00603                         split = true;
00604                     else if (argv[i][2] == 0 && (i+1 < argc) && (argv[i + 1][0] != '-'))
00605                         pl[j]->Translate(argv[i + 1]), split = true;
00606                     else
00607                         pl[j]->Translate(argv[i] + 2);
00608                     break;
00609                 }
00610             }
00611 
00612         if (success)
00613             for (last_success++; last_success < i; last_success++)
00614                 warnings.printf("Command line parameter %s (#%d) ignored\n",
00615                                 argv[last_success], last_success);
00616 
00617         if (split)
00618         {
00619             split = false;
00620             last_success++;
00621             i++;
00622         }
00623     }
00624 
00625     return last_success;
00626 };
00627 
00628 
00629 void ParameterList::Status()
00630 {
00631     fprintf(stderr, "\nThe following parameters are available.  Ones with \"[]\" are in effect:\n");
00632 
00633     for (int i=0; i<count; i++)
00634         pl[i]->Status();
00635 
00636     fprintf(stderr, "\n");
00637 
00638     if (warnings.Length())
00639     {
00640         ::warning("Problems encountered parsing command line:\n\n%s",
00641                   (const char *) warnings);
00642         warnings.Clear();
00643     }
00644 
00645     if (messages.Length())
00646         fprintf(stderr, "NOTES:\n%s\n", (const char *) messages);
00647 }
00648 
00649 void ParameterList::MakeString(int argc, char ** argv, int start)
00650 {
00651     int len = 0;
00652 
00653     for (int i=start; i<argc; i++)
00654         len += strlen(argv[i]) + 1;
00655 
00656     string = new char [len+1];
00657     string[0] = 0;
00658 
00659     for (int i=start; i<argc; i++)
00660     {
00661         strcat(string, argv[i]);
00662         strcat(string, " ");
00663     }
00664 }
00665 
00666 ParameterList::~ParameterList()
00667 {
00668     for (int i = 0; i < count; i++)
00669         delete pl[i];
00670     delete [] pl;
00671     delete [] string;
00672 };
00673 
00674 bool Parameter::CheckInteger(const char * value)
00675 {
00676     if (value[0] != '+' && value[0] != '-' &&
00677             (value[0] < '0' || value[0] > '9'))
00678         return false;
00679 
00680     int pos = 1;
00681     while (value[pos] != 0)
00682         if (value[pos] < '0' || value[pos] > '9')
00683             return false;
00684         else
00685             pos++;
00686 
00687     return true;
00688 }
00689 
00690 bool Parameter::CheckDouble(const char * value)
00691 {
00692     if (value[0] != '+' && value[0] != '-' && value[0] != '.' &&
00693             (value[0] < '0'  || value[0] > '9'))
00694     {
00695         return false;
00696     }
00697 
00698     bool decimal = value[0] == '.';
00699 
00700     for (int pos = 1; value[pos] != 0; pos++)
00701     {
00702         if (value[pos] < '0' || value[pos] > '9')
00703         {
00704             if (!decimal && value[pos] == '.')
00705             {
00706                 decimal = true;
00707             }
00708             else if (value[pos] == 'e' || value[pos] == 'E')
00709             {
00710                 return CheckInteger(value + pos + 1);
00711             }
00712         }
00713     }
00714 
00715     return true;
00716 }
00717 
00718 void ParameterList::Enforce(bool & var, bool value, const char * format, ...)
00719 {
00720     if (var == value)
00721         return;
00722 
00723     var = value;
00724 
00725     String buffer;
00726 
00727     va_list ap;
00728     va_start(ap, format);
00729     buffer.vprintf(format, ap);
00730     va_end(ap);
00731 
00732     messages += buffer;
00733 }
00734 
00735 void ParameterList::Enforce(int & var, int value, const char * format, ...)
00736 {
00737     if (var == value)
00738         return;
00739 
00740     var = value;
00741 
00742     String buffer;
00743 
00744     va_list ap;
00745     va_start(ap, format);
00746     buffer.vprintf(format, ap);
00747     va_end(ap);
00748 
00749     messages += buffer;
00750 }
00751 
00752 void ParameterList::Enforce(double & var, double value, const char * format, ...)
00753 {
00754     if (var == value)
00755         return;
00756 
00757     var = value;
00758 
00759     String buffer;
00760 
00761     va_list ap;
00762     va_start(ap, format);
00763     buffer.vprintf(format, ap);
00764     va_end(ap);
00765 
00766     messages += buffer;
00767 }
00768 
00769 void ParameterList::Enforce(String & var, const char * value, const char * format, ...)
00770 {
00771     if (var.SlowCompare(value) == 0)
00772         return;
00773 
00774     var = value;
00775 
00776     String buffer;
00777     va_list ap;
00778     va_start(ap, format);
00779     buffer.vprintf(format, ap);
00780     va_end(ap);
00781 
00782     messages += buffer;
00783 }
Generated on Wed Nov 17 15:38:29 2010 for StatGen Software by  doxygen 1.6.3