1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <errno.h>
5 
6 #include "commonlib.h"
7 #include "lp_lib.h"
8 #include "lp_report.h"
9 #include "ini.h"
10 
11 typedef int (__WINAPI fn_int_get_function)(lprec *lp);
12 typedef long (__WINAPI fn_long_get_function)(lprec *lp);
13 typedef MYBOOL (__WINAPI fn_MYBOOL_get_function)(lprec *lp);
14 typedef REAL (__WINAPI fn_REAL_get_function)(lprec *lp);
15 typedef void (__WINAPI fn_int_set_function)(lprec *lp, int value);
16 typedef void (__WINAPI fn_long_set_function)(lprec *lp, long value);
17 typedef void (__WINAPI fn_MYBOOL_set_function)(lprec *lp, MYBOOL value);
18 typedef void (__WINAPI fn_REAL_set_function)(lprec *lp, REAL value);
19 
20 #define intfunction    1
21 #define longfunction   2
22 #define MYBOOLfunction 3
23 #define REALfunction   4
24 
25 #define setvalues(values, basemask) values, sizeof(values) / sizeof(*values), basemask
26 #define setNULLvalues NULL, 0, 0
27 #define setvalue(value) value, #value
28 #define setintfunction(get_function, set_function) { get_function }, { set_function }, intfunction
29 #define setlongfunction(get_function, set_function) { (fn_int_get_function *) get_function }, {(fn_int_set_function *) set_function }, longfunction
30 #define setMYBOOLfunction(get_function, set_function) { (fn_int_get_function *) get_function }, { (fn_int_set_function *) set_function }, MYBOOLfunction
31 #define setREALfunction(get_function, set_function) {(fn_int_get_function *) get_function }, { (fn_int_set_function *) set_function }, REALfunction
32 
33 #define WRITE_COMMENTED 0
34 #define WRITE_ACTIVE    1
35 
36 struct _values {
37   int value;
38   char *svalue;
39 };
40 
41 struct _functions {
42   char *par;                                    /* name of parameter in ini file */
43   union {
44     fn_int_get_function *int_get_function;         /* set via setintfunction */
45     fn_long_get_function *long_get_function;       /* set via setlongfunction */
46     fn_MYBOOL_get_function *MYBOOL_get_function;   /* set via setMYBOOLfunction */
47     fn_REAL_get_function *REAL_get_function;       /* set via setREALfunction */
48   } get_function;
49   union {
50     fn_int_set_function *int_set_function;         /* set via setintfunction */
51     fn_long_set_function *long_set_function;       /* set via setlongfunction */
52     fn_MYBOOL_set_function *MYBOOL_set_function;   /* set via setMYBOOLfunction */
53     fn_REAL_set_function *REAL_set_function;       /* set via setREALfunction */
54   } set_function;
55   int type;                                     /* set via set*function */
56   struct _values *values;                       /* set via setvalues to a structure of _values */
57   int elements;                                 /*  or via setNULLvalues if the value is shown as is */
58   unsigned int basemask;
59   int mask;                                     /* WRITE_ACTIVE or WRITE_COMMENTED */
60 };
61 
62 static struct _values anti_degen[] =
63 {
64   { setvalue(ANTIDEGEN_NONE) },
65   { setvalue(ANTIDEGEN_FIXEDVARS) },
66   { setvalue(ANTIDEGEN_COLUMNCHECK) },
67   { setvalue(ANTIDEGEN_STALLING) },
68   { setvalue(ANTIDEGEN_NUMFAILURE) },
69   { setvalue(ANTIDEGEN_LOSTFEAS) },
70   { setvalue(ANTIDEGEN_INFEASIBLE) },
71   { setvalue(ANTIDEGEN_DYNAMIC) },
72   { setvalue(ANTIDEGEN_DURINGBB) },
73   { setvalue(ANTIDEGEN_RHSPERTURB) },
74   { setvalue(ANTIDEGEN_BOUNDFLIP) },
75 };
76 
77 static struct _values basiscrash[] =
78 {
79   { setvalue(CRASH_NONE) },
80   /* { setvalue(CRASH_NONBASICBOUNDS) }, */ /* not yet implemented */
81   { setvalue(CRASH_MOSTFEASIBLE) },
82   { setvalue(CRASH_LEASTDEGENERATE) },
83 };
84 
85 static struct _values bb_floorfirst[] =
86 {
87   { setvalue(BRANCH_CEILING) },
88   { setvalue(BRANCH_FLOOR) },
89   { setvalue(BRANCH_AUTOMATIC) },
90 };
91 
92 static struct _values bb_rule[] =
93 {
94   { setvalue(NODE_FIRSTSELECT) },
95   { setvalue(NODE_GAPSELECT) },
96   { setvalue(NODE_RANGESELECT) },
97   { setvalue(NODE_FRACTIONSELECT) },
98   { setvalue(NODE_PSEUDOCOSTSELECT) },
99   { setvalue(NODE_PSEUDONONINTSELECT) },
100   { setvalue(NODE_PSEUDORATIOSELECT) },
101   { setvalue(NODE_USERSELECT) },
102   { setvalue(NODE_WEIGHTREVERSEMODE) },
103   { setvalue(NODE_BRANCHREVERSEMODE) },
104   { setvalue(NODE_GREEDYMODE) },
105   { setvalue(NODE_PSEUDOCOSTMODE) },
106   { setvalue(NODE_DEPTHFIRSTMODE) },
107   { setvalue(NODE_RANDOMIZEMODE) },
108   { setvalue(NODE_GUBMODE) },
109   { setvalue(NODE_DYNAMICMODE) },
110   { setvalue(NODE_RESTARTMODE) },
111   { setvalue(NODE_BREADTHFIRSTMODE) },
112   { setvalue(NODE_AUTOORDER) },
113   { setvalue(NODE_RCOSTFIXING) },
114   { setvalue(NODE_STRONGINIT) },
115 };
116 
117 static struct _values improve[] =
118 {
119   { setvalue(IMPROVE_NONE) },
120   { setvalue(IMPROVE_SOLUTION) },
121   { setvalue(IMPROVE_DUALFEAS) },
122   { setvalue(IMPROVE_THETAGAP) },
123   { setvalue(IMPROVE_BBSIMPLEX) },
124 };
125 
get_mip_gap_abs(lprec * lp)126 static REAL __WINAPI get_mip_gap_abs(lprec *lp)
127 {
128   return(get_mip_gap(lp, TRUE));
129 }
130 
get_mip_gap_rel(lprec * lp)131 static REAL __WINAPI get_mip_gap_rel(lprec *lp)
132 {
133   return(get_mip_gap(lp, FALSE));
134 }
135 
set_mip_gap_abs(lprec * lp,REAL mip_gap)136 static void __WINAPI set_mip_gap_abs(lprec *lp, REAL mip_gap)
137 {
138   set_mip_gap(lp, TRUE, mip_gap);
139 }
140 
set_mip_gap_rel(lprec * lp,REAL mip_gap)141 static void __WINAPI set_mip_gap_rel(lprec *lp, REAL mip_gap)
142 {
143   set_mip_gap(lp, FALSE, mip_gap);
144 }
145 
146 static struct _values pivoting[] =
147 {
148   { setvalue(PRICER_FIRSTINDEX) },
149   { setvalue(PRICER_DANTZIG) },
150   { setvalue(PRICER_DEVEX) },
151   { setvalue(PRICER_STEEPESTEDGE) },
152   { setvalue(PRICE_PRIMALFALLBACK) },
153   { setvalue(PRICE_MULTIPLE) },
154   { setvalue(PRICE_PARTIAL) },
155   { setvalue(PRICE_ADAPTIVE) },
156   { setvalue(PRICE_RANDOMIZE) },
157   { setvalue(PRICE_AUTOPARTIAL) },
158   { setvalue(PRICE_LOOPLEFT) },
159   { setvalue(PRICE_LOOPALTERNATE) },
160   { setvalue(PRICE_HARRISTWOPASS) },
161   { setvalue(PRICE_TRUENORMINIT) },
162 };
163 
164 static struct _values presolving[] =
165 {
166   { setvalue(PRESOLVE_NONE) },
167   { setvalue(PRESOLVE_ROWS) },
168   { setvalue(PRESOLVE_COLS) },
169   { setvalue(PRESOLVE_LINDEP) },
170   { setvalue(PRESOLVE_AGGREGATE) },
171   { setvalue(PRESOLVE_SPARSER) },
172   { setvalue(PRESOLVE_SOS) },
173   { setvalue(PRESOLVE_REDUCEMIP) },
174   { setvalue(PRESOLVE_KNAPSACK) },
175   { setvalue(PRESOLVE_ELIMEQ2) },
176   { setvalue(PRESOLVE_IMPLIEDFREE) },
177   { setvalue(PRESOLVE_REDUCEGCD) },
178   { setvalue(PRESOLVE_PROBEFIX) },
179   { setvalue(PRESOLVE_PROBEREDUCE) },
180   { setvalue(PRESOLVE_ROWDOMINATE) },
181   { setvalue(PRESOLVE_COLDOMINATE) },
182   { setvalue(PRESOLVE_MERGEROWS) },
183   { setvalue(PRESOLVE_IMPLIEDSLK) },
184   { setvalue(PRESOLVE_COLFIXDUAL) },
185   { setvalue(PRESOLVE_BOUNDS) },
186   { setvalue(PRESOLVE_DUALS) },
187   { setvalue(PRESOLVE_SENSDUALS) },
188 };
189 
STRLWR(char * str)190 static char *STRLWR(char *str)
191 {
192   char *ptr;
193 
194   for(ptr = str; *ptr; ptr++)
195     *ptr = (char) tolower((unsigned char) *ptr);
196 
197   return(str);
198 }
199 
STRUPR(char * str)200 static char *STRUPR(char *str)
201 {
202   char *ptr;
203 
204   for(ptr = str; *ptr; ptr++)
205     *ptr = (char) toupper((unsigned char) *ptr);
206 
207   return(str);
208 }
209 
set_presolve1(lprec * lp,int do_presolve)210 static void __WINAPI set_presolve1(lprec *lp, int do_presolve)
211 {
212   set_presolve(lp, do_presolve, get_presolveloops(lp));
213 }
214 
set_presolve2(lprec * lp,int maxloops)215 static void __WINAPI set_presolve2(lprec *lp, int maxloops)
216 {
217   set_presolve(lp, get_presolve(lp), maxloops);
218 }
219 
220 static struct _values print_sol[] =
221 {
222   { FALSE, "0" },
223   { TRUE,  "1" },
224   { setvalue(AUTOMATIC) },
225 };
226 
227 static struct _values scaling[] =
228 {
229   { setvalue(SCALE_NONE) },
230   { setvalue(SCALE_EXTREME) },
231   { setvalue(SCALE_RANGE) },
232   { setvalue(SCALE_MEAN) },
233   { setvalue(SCALE_GEOMETRIC) },
234   { setvalue(SCALE_CURTISREID) },
235   { setvalue(SCALE_QUADRATIC) },
236   { setvalue(SCALE_LOGARITHMIC) },
237   { setvalue(SCALE_USERWEIGHT) },
238   { setvalue(SCALE_POWER2) },
239   { setvalue(SCALE_EQUILIBRATE) },
240   { setvalue(SCALE_INTEGERS) },
241   { setvalue(SCALE_DYNUPDATE) },
242   { setvalue(SCALE_ROWSONLY) },
243   { setvalue(SCALE_COLSONLY) },
244 };
245 
246 static struct _values simplextype[] =
247 {
248   { setvalue(SIMPLEX_PRIMAL_PRIMAL) },
249   { setvalue(SIMPLEX_DUAL_PRIMAL) },
250   { setvalue(SIMPLEX_PRIMAL_DUAL) },
251   { setvalue(SIMPLEX_DUAL_DUAL) },
252 };
253 
254 static struct _values verbose[] =
255 {
256   { setvalue(NEUTRAL) },
257   { setvalue(CRITICAL) },
258   { setvalue(SEVERE) },
259   { setvalue(IMPORTANT) },
260   { setvalue(NORMAL) },
261   { setvalue(DETAILED) },
262   { setvalue(FULL) },
263 };
264 
265 static struct _functions functions[] =
266 {
267   /* solve options */
268   { "ANTI_DEGEN", setintfunction(get_anti_degen, set_anti_degen), setvalues(anti_degen, ~0), WRITE_ACTIVE },
269   { "BASISCRASH", setintfunction(get_basiscrash, set_basiscrash), setvalues(basiscrash, ~0), WRITE_ACTIVE },
270   { "IMPROVE", setintfunction(get_improve, set_improve), setvalues(improve, ~0), WRITE_ACTIVE },
271   { "MAXPIVOT", setintfunction(get_maxpivot, set_maxpivot), setNULLvalues, WRITE_ACTIVE },
272   { "NEGRANGE", setREALfunction(get_negrange, set_negrange), setNULLvalues, WRITE_ACTIVE },
273   { "PIVOTING", setintfunction(get_pivoting, set_pivoting), setvalues(pivoting, PRICER_LASTOPTION), WRITE_ACTIVE },
274   { "PRESOLVE", setintfunction(get_presolve, set_presolve1), setvalues(presolving, ~0), WRITE_ACTIVE },
275   { "PRESOLVELOOPS", setintfunction(get_presolveloops, set_presolve2), setNULLvalues, WRITE_ACTIVE },
276   { "SCALELIMIT", setREALfunction(get_scalelimit, set_scalelimit), setNULLvalues, WRITE_ACTIVE },
277   { "SCALING", setintfunction(get_scaling, set_scaling), setvalues(scaling, SCALE_CURTISREID), WRITE_ACTIVE },
278   { "SIMPLEXTYPE", setintfunction(get_simplextype, set_simplextype), setvalues(simplextype, ~0), WRITE_ACTIVE },
279   { "OBJ_IN_BASIS", setMYBOOLfunction(is_obj_in_basis, set_obj_in_basis), setNULLvalues, WRITE_COMMENTED },
280 
281   /* B&B options */
282   { "BB_DEPTHLIMIT", setintfunction(get_bb_depthlimit, set_bb_depthlimit), setNULLvalues, WRITE_ACTIVE },
283   { "BB_FLOORFIRST", setintfunction(get_bb_floorfirst, set_bb_floorfirst), setvalues(bb_floorfirst, ~0), WRITE_ACTIVE },
284   { "BB_RULE", setintfunction(get_bb_rule, set_bb_rule), setvalues(bb_rule, NODE_STRATEGYMASK), WRITE_ACTIVE },
285   { "BREAK_AT_FIRST", setMYBOOLfunction(is_break_at_first, set_break_at_first), setNULLvalues, WRITE_COMMENTED },
286   { "BREAK_AT_VALUE", setREALfunction(get_break_at_value, set_break_at_value), setNULLvalues, WRITE_COMMENTED },
287   { "MIP_GAP_ABS", setREALfunction(get_mip_gap_abs, set_mip_gap_abs), setNULLvalues, WRITE_ACTIVE },
288   { "MIP_GAP_REL", setREALfunction(get_mip_gap_rel, set_mip_gap_rel), setNULLvalues, WRITE_ACTIVE },
289   { "EPSINT", setREALfunction(get_epsint, set_epsint), setNULLvalues, WRITE_ACTIVE },
290 
291   /* tolerances, values */
292   { "EPSB", setREALfunction(get_epsb, set_epsb), setNULLvalues, WRITE_ACTIVE },
293   { "EPSD", setREALfunction(get_epsd, set_epsd), setNULLvalues, WRITE_ACTIVE },
294   { "EPSEL", setREALfunction(get_epsel, set_epsel), setNULLvalues, WRITE_ACTIVE },
295   { "EPSPERTURB", setREALfunction(get_epsperturb, set_epsperturb), setNULLvalues, WRITE_ACTIVE },
296   { "EPSPIVOT", setREALfunction(get_epspivot, set_epspivot), setNULLvalues, WRITE_ACTIVE },
297   { "INFINITE", setREALfunction(get_infinite, set_infinite), setNULLvalues, WRITE_ACTIVE },
298   { "BREAK_NUMERIC_ACCURACY", setREALfunction(get_break_numeric_accuracy, set_break_numeric_accuracy), setNULLvalues, WRITE_ACTIVE },
299 
300   /* read-only options */
301   { "DEBUG", setMYBOOLfunction(is_debug, set_debug), setNULLvalues, WRITE_COMMENTED },
302   { "OBJ_BOUND", setREALfunction(get_obj_bound, set_obj_bound), setNULLvalues, WRITE_COMMENTED },
303   { "PRINT_SOL", setintfunction(get_print_sol, set_print_sol), setvalues(print_sol, ~0), WRITE_COMMENTED },
304   { "TIMEOUT", setlongfunction(get_timeout, set_timeout), setNULLvalues, WRITE_COMMENTED },
305   { "TRACE", setMYBOOLfunction(is_trace, set_trace), setNULLvalues, WRITE_COMMENTED },
306   { "VERBOSE", setintfunction(get_verbose, set_verbose), setvalues(verbose, ~0), WRITE_COMMENTED },
307 };
308 
write_params1(lprec * lp,FILE * fp,char * header,int newline)309 static void write_params1(lprec *lp, FILE *fp, char *header, int newline)
310 {
311   int ret = 0, ret2, i, j, k, value, value2, elements, majorversion, minorversion, release, build;
312   unsigned int basemask;
313   REAL a = 0;
314   char buf[4096], par[30];
315 
316   ini_writeheader(fp, header, newline);
317   lp_solve_version(&majorversion, &minorversion, &release, &build);
318   sprintf(buf, "lp_solve version %d.%d settings\n", majorversion, minorversion);
319   ini_writecomment(fp, buf);
320   for(i = 0; i < sizeof(functions) / sizeof(*functions); i++) {
321     switch(functions[i].type) {
322     case intfunction:
323       if(functions[i].get_function.int_get_function == NULL)
324         continue;
325       ret = functions[i].get_function.int_get_function(lp);
326       break;
327     case longfunction:
328       if(functions[i].get_function.long_get_function == NULL)
329         continue;
330       ret = functions[i].get_function.long_get_function(lp);
331       break;
332     case MYBOOLfunction:
333       if(functions[i].get_function.MYBOOL_get_function == NULL)
334         continue;
335       ret = (int) functions[i].get_function.MYBOOL_get_function(lp);
336       break;
337     case REALfunction:
338       if(functions[i].get_function.REAL_get_function == NULL)
339         continue;
340       a = functions[i].get_function.REAL_get_function(lp);
341       break;
342     }
343     buf[0] = 0;
344     if(functions[i].values == NULL) {
345       switch(functions[i].type) {
346       case intfunction:
347       case longfunction:
348       case MYBOOLfunction:
349         sprintf(buf, "%d", ret);
350         break;
351       case REALfunction:
352         sprintf(buf, "%g", a);
353         break;
354       }
355     }
356     else {
357       elements = functions[i].elements;
358       basemask = functions[i].basemask;
359       for(j = 0; j < elements; j++) {
360         value = functions[i].values[j].value;
361         ret2 = ret;
362         if(((unsigned int) value) < basemask)
363           ret2 &= basemask;
364         if(value == 0) {
365           if(ret2 == 0) {
366             if(*buf)
367               strcat(buf, " + ");
368             strcat(buf, functions[i].values[j].svalue);
369           }
370         }
371         else if((ret2 & value) == value) {
372           for(k = 0; k < elements; k++) {
373             value2 = functions[i].values[k].value;
374             if((k != j) && (value2 > value) && ((value2 & value) == value) && ((ret2 & value2) == value2))
375               break;
376           }
377           if(k == elements) {
378             if(*buf)
379               strcat(buf, " + ");
380             strcat(buf, functions[i].values[j].svalue);
381           }
382         }
383       }
384     }
385     if(functions[i].mask & WRITE_ACTIVE)
386       par[0] = 0;
387     else
388       strcpy(par, ";");
389     strcat(par, functions[i].par);
390     ini_writedata(fp, STRLWR(par), buf);
391   }
392 }
393 
readoptions(char * options,char ** header)394 static void readoptions(char *options, char **header)
395 {
396   char *ptr1, *ptr2;
397 
398   if(options != NULL) {
399     ptr1 = options;
400     while(*ptr1) {
401       ptr2 = strchr(ptr1, '-');
402       if(ptr2 == NULL)
403         break;
404       ptr2++;
405       if(tolower((unsigned char) *ptr2) == 'h') {
406         for(++ptr2; (*ptr2) && (isspace(*ptr2)); ptr2++);
407         for(ptr1 = ptr2; (*ptr1) && (!isspace(*ptr1)); ptr1++);
408         *header = (char *) calloc(1 + (int) (ptr1 - ptr2), 1);
409         memcpy(*header, ptr2, (int) (ptr1 - ptr2));
410       }
411     }
412   }
413 
414   if(*header == NULL)
415     *header = strdup("Default");
416 }
417 
write_params(lprec * lp,char * filename,char * options)418 MYBOOL __WINAPI write_params(lprec *lp, char *filename, char *options)
419 {
420   int k, ret, params_written;
421   FILE *fp, *fp0;
422   int state = 0, looping, newline;
423   char buf[4096], *filename0, *ptr1, *ptr2, *header = NULL;
424 
425   readoptions(options, &header);
426 
427   k = (int) strlen(filename);
428   filename0 = (char *) malloc(k + 1 + 1);
429   strcpy(filename0, filename);
430   ptr1 = strrchr(filename0, '.');
431   ptr2 = strrchr(filename0, '\\');
432   if((ptr1 == NULL) || ((ptr2 != NULL) && (ptr1 < ptr2)))
433     ptr1 = filename0 + k;
434   memmove(ptr1 + 1, ptr1, k + 1 - (int) (ptr1 - filename0));
435   ptr1[0] = '_';
436   if(rename(filename, filename0)) {
437     switch(errno) {
438     case ENOENT: /* File or path specified by oldname not found */
439       FREE(filename0);
440       filename0 = NULL;
441       break;
442     case EACCES: /* File or directory specified by newname already exists or could not be created (invalid path); or oldname is a directory and newname specifies a different path. */
443       FREE(filename0);
444       FREE(header);
445       return(FALSE);
446       break;
447     }
448   }
449 
450   if((fp = ini_create(filename)) == NULL)
451     ret = FALSE;
452   else {
453     params_written = FALSE;
454     newline = TRUE;
455     if(filename0 != NULL) {
456       fp0 = ini_open(filename0);
457       if(fp0 == NULL) {
458         rename(filename0, filename);
459         FREE(filename0);
460         FREE(header);
461         return(FALSE);
462       }
463       looping = TRUE;
464       while(looping) {
465         switch(ini_readdata(fp0, buf, sizeof(buf), TRUE)) {
466         case 0: /* End of file */
467           looping = FALSE;
468           break;
469         case 1: /* header */
470           ptr1 = strdup(buf);
471           STRUPR(buf);
472           ptr2 = strdup(header);
473           STRUPR(ptr2);
474           if(strcmp(buf, ptr2) == 0) {
475             write_params1(lp, fp, ptr1, newline);
476             params_written = TRUE;
477             newline = TRUE;
478             state = 1;
479           }
480           else {
481             state = 0;
482             ini_writeheader(fp, ptr1, newline);
483             newline = TRUE;
484           }
485           FREE(ptr2);
486           FREE(ptr1);
487           break;
488         case 2: /* data */
489           if(state == 0) {
490             ini_writedata(fp, NULL, buf);
491             newline = (*buf != 0);
492           }
493           break;
494         }
495       }
496       ini_close(fp0);
497     }
498 
499     if(!params_written)
500       write_params1(lp, fp, header, newline);
501 
502     ini_close(fp);
503     ret = TRUE;
504   }
505 
506   if(filename0 != NULL) {
507     remove(filename0);
508     FREE(filename0);
509   }
510 
511   FREE(header);
512 
513   return( (MYBOOL) ret );
514 }
515 
516 
read_params(lprec * lp,char * filename,char * options)517 MYBOOL __WINAPI read_params(lprec *lp, char *filename, char *options)
518 {
519   int ret, looping, line;
520   FILE *fp;
521   hashtable *hashfunctions, *hashparameters;
522   hashelem *hp;
523   int i, j, elements, n, intvalue, state = 0;
524   REAL REALvalue;
525   char buf[4096], *header = NULL, *ptr, *ptr1, *ptr2;
526 
527   if((fp = ini_open(filename)) == NULL)
528     ret = FALSE;
529   else {
530     /* create hashtable of all callable commands to find them quickly */
531     hashfunctions = create_hash_table(sizeof(functions) / sizeof(*functions), 0);
532     for (n = 0, i = 0; i < (int) (sizeof(functions)/sizeof(*functions)); i++) {
533       puthash(functions[i].par, i, NULL, hashfunctions);
534       if(functions[i].values != NULL)
535         n += functions[i].elements;
536     }
537     /* create hashtable of all arguments to find them quickly */
538     hashparameters = create_hash_table(n, 0);
539     for (n = 0, i = 0; i < (int) (sizeof(functions)/sizeof(*functions)); i++) {
540       if(functions[i].values != NULL) {
541         elements = functions[i].elements;
542         for(j = 0; j < elements; j++)
543           if((strcmp(functions[i].values[j].svalue, "0") != 0) &&
544              (strcmp(functions[i].values[j].svalue, "1") != 0))
545             puthash(functions[i].values[j].svalue, j, NULL, hashparameters);
546       }
547     }
548 
549     readoptions(options, &header);
550 
551     STRUPR(header);
552     ret = looping = TRUE;
553     line = 0;
554     while((ret) && (looping)) {
555       line++;
556       switch(ini_readdata(fp, buf, sizeof(buf), FALSE)) {
557         case 0: /* End of file */
558           looping = FALSE;
559           break;
560         case 1: /* header */
561           switch(state) {
562             case 0:
563               STRUPR(buf);
564               if(strcmp(buf, header) == 0)
565                 state = 1;
566               break;
567             case 1:
568               looping = FALSE;
569               break;
570           }
571           break;
572         case 2: /* data */
573           if(state == 1) {
574             for(ptr = buf; (*ptr) && (isspace(*ptr)); ptr++);
575           }
576           else
577             ptr = NULL;
578           if((ptr != NULL) && (*ptr)) {
579             STRUPR(buf);
580             ptr = strchr(buf, '=');
581             if(ptr == NULL) {
582               report(lp, IMPORTANT, "read_params: No equal sign on line %d\n", line);
583               ret = FALSE;
584             }
585             else {
586               *ptr = 0;
587               for(ptr1 = buf; isspace(*ptr1); ptr1++);
588               for(ptr2 = ptr - 1; (ptr2 >= ptr1) && (isspace(*ptr2)); ptr2--);
589               if(ptr2 <= ptr1) {
590                 report(lp, IMPORTANT, "read_params: No parameter name before equal sign on line %d\n", line);
591                 ret = FALSE;
592               }
593               else {
594                 ptr2[1] = 0;
595                 hp = findhash(ptr1, hashfunctions);
596                 if(hp == NULL) {
597                   report(lp, IMPORTANT, "read_params: Unknown parameter name (%s) before equal sign on line %d\n", ptr1, line);
598                   ret = FALSE;
599                 }
600                 else {
601                   i = hp->index;
602                   ptr1 = ++ptr;
603                   intvalue = 0;
604                   REALvalue = 0;
605                   if(functions[i].values == NULL) {
606                     switch(functions[i].type) {
607                       case intfunction:
608                       case longfunction:
609                       case MYBOOLfunction:
610                         intvalue = strtol(ptr1, &ptr2, 10);
611                         while((*ptr2) && (isspace(*ptr2)))
612                           ptr2++;
613                         if(*ptr2) {
614                           report(lp, IMPORTANT, "read_params: Invalid integer value on line %d\n", line);
615                           ret = FALSE;
616                         }
617                         break;
618                       case REALfunction:
619                         REALvalue = strtod(ptr1, &ptr2);
620                         while((*ptr2) && (isspace(*ptr2)))
621                           ptr2++;
622                         if(*ptr2) {
623                           report(lp, IMPORTANT, "read_params: Invalid real value on line %d\n", line);
624                           ret = FALSE;
625                         }
626                         break;
627                     }
628                   }
629                   else {
630                     while(ret) {
631                       ptr = strchr(ptr1, '+');
632                       if(ptr == NULL)
633                         ptr = ptr1 + strlen(ptr1);
634                       for(; isspace(*ptr1); ptr1++);
635                       for(ptr2 = ptr - 1; (ptr2 >= ptr1) && (isspace(*ptr2)); ptr2--);
636                       if(ptr2 <= ptr1)
637                         break;
638                       else {
639                         ptr2[1] = 0;
640                         hp = findhash(ptr1, hashparameters);
641                         if (hp == NULL) {
642                           report(lp, IMPORTANT, "read_params: Invalid parameter name (%s) on line %d\n", ptr1, line);
643                           ret = FALSE;
644                         }
645                         else {
646                           j = hp->index;
647                           if((j >= functions[i].elements) ||
648                              (strcmp(functions[i].values[j].svalue, ptr1))) {
649                             report(lp, IMPORTANT, "read_params: Inappropriate parameter name (%s) on line %d\n", ptr1, line);
650                             ret = FALSE;
651                           }
652                           else {
653                             intvalue += functions[i].values[j].value;
654                           }
655                         }
656                         ptr1 = ptr + 1;
657                       }
658                     }
659                   }
660                   if(ret) {
661                     switch(functions[i].type) {
662                       case intfunction:
663                         functions[i].set_function.int_set_function(lp, intvalue);
664                         break;
665                       case longfunction:
666                         functions[i].set_function.long_set_function(lp, intvalue);
667                         break;
668                       case MYBOOLfunction:
669                         functions[i].set_function.MYBOOL_set_function(lp, (MYBOOL) intvalue);
670                         break;
671                       case REALfunction:
672                         functions[i].set_function.REAL_set_function(lp, REALvalue);
673                         break;
674                     }
675                   }
676                 }
677               }
678             }
679           }
680           break;
681       }
682     }
683 
684     FREE(header);
685     free_hash_table(hashfunctions);
686     free_hash_table(hashparameters);
687 
688     ini_close(fp);
689   }
690 
691   return( (MYBOOL) ret );
692 }
693