1/* $Id: CoinParamUtils.cpp 1469 2011-09-03 18:49:33Z stefan $ */
2// Copyright (C) 2007, International Business Machines
3// Corporation and others. All Rights Reserved.
4// This code is licensed under the terms of the Eclipse Public License (EPL).
5
6#include <cassert>
7#include <cerrno>
8#include <iostream>
9
10#include "CoinUtilsConfig.h"
11#include "CoinParam.hpp"
12#include <cstdlib>
13#include <cstring>
14#include <cstdio>
15
16#ifdef COIN_HAS_READLINE
17#include <readline/readline.h>
18#include <readline/history.h>
19#endif
20
21/* Unnamed local namespace */
22namespace
23{
24
25/*
26 cmdField: The index of the current command line field. Forced to -1 when
27 accepting commands from stdin (interactive) or a command file.
28 readSrc: Current input source.
29
30 pendingVal: When the form param=value is encountered, both keyword and value
31 form one command line field. We need to return `param' as the
32 field and somehow keep the value around for the upcoming call
33 that'll request it. That's the purpose of pendingVal.
34*/
35
36int cmdField = 1 ;
37FILE *readSrc = stdin ;
38std::string pendingVal = "" ;
39
40
41/*
42 Get next command or field in command. When in interactive mode, prompt the
43 user and read the resulting line of input.
44*/
45std::string nextField (const char *prompt)
46{
47 static char line[1000] ;
48 static char *where = NULL ;
49 std::string field ;
50 const char *dflt_prompt = "Eh? " ;
51
52 if (prompt == 0)
53 { prompt = dflt_prompt ; }
54/*
55 Do we have a line at the moment? If not, acquire one. When we're done,
56 line holds the input line and where points to the start of the line. If we're
57 using the readline library, add non-empty lines to the history list.
58*/
59 if (!where) {
60#ifdef COIN_HAS_READLINE
61 if (readSrc == stdin)
62 { where = readline(prompt) ;
63 if (where)
64 { if (*where)
65 add_history (where) ;
66 strcpy(line,where) ;
67 free(where) ;
68 where = line ; } }
69 else
70 { where = fgets(line,1000,readSrc) ; }
71#else
72 if (readSrc == stdin)
73 { fprintf(stdout,"%s",prompt) ;
74 fflush(stdout) ; }
75 where = fgets(line,1000,readSrc) ;
76#endif
77/*
78 If where is NULL, we have EOF. Return a null string.
79*/
80 if (!where)
81 return field ;
82/*
83 Clean the image. Trailing junk first. The line will be cut off at the last
84 non-whitespace character, but we need to scan until we find the end of the
85 string or some other non-printing character to make sure we don't miss a
86 printing character after whitespace.
87*/
88 char *lastNonBlank = line-1 ;
89 for (where = line ; *where != '\0' ; where++)
90 { if (*where != '\t' && *where < ' ')
91 { break ; }
92 if (*where != '\t' && *where != ' ')
93 { lastNonBlank = where ; } }
94 *(lastNonBlank+1) = '\0' ;
95 where = line ; }
96/*
97 Munch through leading white space.
98*/
99 while (*where == ' ' || *where == '\t')
100 where++ ;
101/*
102 See if we can separate a field; if so, copy it over into field for return.
103 If we're out of line, return the string "EOL".
104*/
105 char *saveWhere = where ;
106 while (*where != ' ' && *where != '\t' && *where!='\0')
107 where++ ;
108 if (where != saveWhere)
109 { char save = *where ;
110 *where = '\0' ;
111 field = saveWhere ;
112 *where = save ; }
113 else
114 { where = NULL ;
115 field = "EOL" ; }
116
117 return (field) ; }
118
119}
120
121
122/* Visible functions */
123
124namespace CoinParamUtils
125{
126
127/*
128 As mentioned above, cmdField set to -1 is the indication that we're reading
129 from stdin or a file.
130*/
131void setInputSrc (FILE *src)
132
133{ if (src != 0)
134 { cmdField = -1 ;
135 readSrc = src ; } }
136
137/*
138 A utility to allow clients to determine if we're processing parameters from
139 the comand line or otherwise.
140*/
141bool isCommandLine ()
142
143{ assert(cmdField != 0) ;
144
145 if (cmdField > 0)
146 { return (true) ; }
147 else
148 { return (false) ; } }
149
150/*
151 A utility to allow clients to determine if we're accepting parameters
152 interactively.
153*/
154bool isInteractive ()
155
156{ assert(cmdField != 0) ;
157
158 if (cmdField < 0 && readSrc == stdin)
159 { return (true) ; }
160 else
161 { return (false) ; } }
162
163/*
164 Utility functions for acquiring input.
165*/
166
167
168/*
169 Return the next field (word) from the current command line. Generally, this
170 is expected to be of the form `-param' or `--param', with special cases as
171 set out below.
172
173 If we're in interactive mode (cmdField == -1), nextField does all the work
174 to prompt the user and return the next field from the resulting input. It is
175 assumed that the user knows not to use `-' or `--' prefixes in interactive
176 mode.
177
178 If we're in command line mode (cmdField > 0), cmdField indicates the
179 current command line word. The order of processing goes like this:
180 * A stand-alone `-' is converted to `stdin'
181 * A stand-alone '--' is returned as a word; interpretation is up to the
182 client.
183 * A prefix of '-' or '--' is stripped from the field.
184 If the result is `stdin', it's assumed we're switching to interactive mode
185 and the user is prompted for another command.
186
187 Whatever results from the above sequence is returned to the client as the
188 next field. An empty string indicates end of input.
189
190 Prompt will be used by nextField if it's necessary to prompt the user for
191 a command (only when reading from stdin).
192
193 If provided, pfx is set to the prefix ("-", "--", or "") stripped from the
194 field. Lack of prefix is not necessarily an error because of the following
195 scenario: To read a file, the verbose command might be "foo -import
196 myfile". But we might want to allow a short form, "foo myfile". And we'd
197 like "foo import" to be interpreted as "foo -import import" (i.e., import the
198 file named `import').
199*/
200
201std::string getCommand (int argc, const char *argv[],
202 const std::string prompt, std::string *pfx)
203
204{ std::string field = "EOL" ;
205 pendingVal = "" ;
206 int pfxlen ;
207
208 if (pfx != 0)
209 { (*pfx) = "" ; }
210/*
211 Acquire the next field, and convert as outlined above if we're processing
212 command line parameters.
213*/
214 while (field == "EOL")
215 { pfxlen = 0 ;
216 if (cmdField > 0)
217 { if (cmdField < argc)
218 { field = argv[cmdField++] ;
219 if (field == "-")
220 { field = "stdin" ; }
221 else
222 if (field == "--")
223 { /* Prevent `--' from being eaten by next case. */ }
224 else
225 { if (field[0] == '-')
226 { pfxlen = 1 ;
227 if (field[1] == '-')
228 pfxlen = 2 ;
229 if (pfx != 0)
230 (*pfx) = field.substr(0,pfxlen) ;
231 field = field.substr(pfxlen) ; } } }
232 else
233 { field = "" ; } }
234 else
235 { field = nextField(prompt.c_str()) ; }
236 if (field == "stdin")
237 { std::cout << "Switching to line mode" << std::endl ;
238 cmdField = -1 ;
239 field = nextField(prompt.c_str()) ; } }
240/*
241 Are we left with something of the form param=value? If so, separate the
242 pieces, returning `param' and saving `value' for later use as per comments
243 at the head of the file.
244*/
245 std::string::size_type found = field.find('=');
246 if (found != std::string::npos)
247 { pendingVal = field.substr(found+1) ;
248 field = field.substr(0,found) ; }
249
250 return (field) ; }
251
252
253/*
254 Function to look up a parameter keyword (name) in the parameter vector and
255 deal with the result. The keyword may end in one or more `?' characters;
256 this is a query for information about matching parameters.
257
258 If we have a single match satisfying the minimal match requirements, and
259 there's no query, we simply return the index of the matching parameter in
260 the parameter vector. If there are no matches, and no query, the return
261 value will be -3. No matches on a query returns -1.
262
263 A single short match, or a single match of any length with a query, will
264 result in a short help message
265
266 If present, these values are set as follows:
267 * matchCntp is set to the number of parameters that matched.
268 * shortCntp is set to the number of matches that failed to meet the minimum
269 match requirement.
270 * queryCntp is set to the number of trailing `?' characters at the end
271 of name.
272
273 Return values:
274 >0: index of the single unique match for the name
275 -1: query present
276 -2: no query, one or more short matches
277 -3: no query, no match
278 -4: multiple full matches (indicates configuration error)
279
280 The final three parameters (matchCnt, shortCnt, queryCnt) are optional and
281 default to null. Use them if you want more detail on the match.
282*/
283
284int lookupParam (std::string name, CoinParamVec &paramVec,
285 int *matchCntp, int *shortCntp, int *queryCntp)
286
287{
288 int retval = -3 ;
289
290 if (matchCntp != 0)
291 { *matchCntp = 0 ; }
292 if (shortCntp != 0)
293 { *shortCntp = 0 ; }
294 if (queryCntp != 0)
295 { *queryCntp = 0 ; }
296/*
297 Is there anything here at all?
298*/
299 if (name.length() == 0)
300 { return (retval) ; }
301/*
302 Scan the parameter name to see if it ends in one or more `?' characters. If
303 so, take it as a request to return a list of parameters that match name up
304 to the first `?'. The strings '?' and '???' are considered to be valid
305 parameter names (short and long help, respectively) and are handled as
306 special cases: If the whole string is `?'s, one and three are commands as
307 is, while 2 and 4 or more are queries about `?' or `???'.
308*/
309 int numQuery = 0 ;
310 { int length = static_cast<int>(name.length()) ;
311 int i ;
312 for (i = length-1 ; i >= 0 && name[i] == '?' ; i--)
313 { numQuery++ ; }
314 if (numQuery == length)
315 { switch (length)
316 { case 1:
317 case 3:
318 { numQuery = 0 ;
319 break ; }
320 case 2:
321 { numQuery -= 1 ;
322 break ; }
323 default:
324 { numQuery -= 3 ;
325 break ; } } }
326 name = name.substr(0,length-numQuery) ;
327 if (queryCntp != 0)
328 { *queryCntp = numQuery ; } }
329/*
330 See if we can match the parameter name. On return, matchNdx is set to the
331 last match satisfying the minimal match criteria, or -1 if there's no
332 match. matchCnt is the number of matches satisfying the minimum match
333 length, and shortCnt is possible matches that were short of the minimum
334 match length,
335*/
336 int matchNdx = -1 ;
337 int shortCnt = 0 ;
338 int matchCnt = CoinParamUtils::matchParam(paramVec,name,matchNdx,shortCnt) ;
339/*
340 Set up return values before we get into further processing.
341*/
342 if (matchCntp != 0)
343 { *matchCntp = matchCnt ; }
344 if (shortCntp != 0)
345 { *shortCntp = shortCnt ; }
346 if (numQuery > 0)
347 { retval = -1 ; }
348 else
349 { if (matchCnt+shortCnt == 0)
350 { retval = -3 ; }
351 else
352 if (matchCnt > 1)
353 { retval = -4 ; }
354 else
355 { retval = -2 ; } }
356/*
357 No matches? Nothing more to be done here.
358*/
359 if (matchCnt+shortCnt == 0)
360 { return (retval) ; }
361/*
362 A unique match and no `?' in the name says we have our parameter. Return
363 the result.
364*/
365 if (matchCnt == 1 && shortCnt == 0 && numQuery == 0)
366 { assert (matchNdx >= 0 && matchNdx < static_cast<int>(paramVec.size())) ;
367 return (matchNdx) ; }
368/*
369 A single match? There are two possibilities:
370 * The string specified is shorter than the match length requested by the
371 parameter. (Useful for avoiding inadvertent execution of commands that
372 the client might regret.)
373 * The string specified contained a `?', in which case we print the help.
374 The match may or may not be short.
375*/
376 if (matchCnt+shortCnt == 1)
377 { CoinParamUtils::shortOrHelpOne(paramVec,matchNdx,name,numQuery) ;
378 return (retval) ; }
379/*
380 The final case: multiple matches. Most commonly this will be multiple short
381 matches. If we have multiple matches satisfying the minimal length
382 criteria, we have a configuration problem. The other question is whether
383 the user wanted help information. Two question marks gets short help.
384*/
385 if (matchCnt > 1)
386 { std::cout
387 << "Configuration error! `" << name
388 <<"' was fully matched " << matchCnt << " times!"
389 << std::endl ; }
390 std::cout
391 << "Multiple matches for `" << name << "'; possible completions:"
392 << std::endl ;
393 CoinParamUtils::shortOrHelpMany(paramVec,name,numQuery) ;
394
395 return (retval) ; }
396
397
398/*
399 Utility functions to acquire parameter values from the command line. For
400 all of these, a pendingVal is consumed if it exists.
401*/
402
403
404/*
405 Read a string and return a pointer to the string. Set valid to indicate the
406 result of parsing: 0: okay, 1: <unused>, 2: not present.
407*/
408
409std::string getStringField (int argc, const char *argv[], int *valid)
410
411{ std::string field ;
412
413 if (pendingVal != "")
414 { field = pendingVal ;
415 pendingVal = "" ; }
416 else
417 { field = "EOL" ;
418 if (cmdField > 0)
419 { if (cmdField < argc)
420 { field = argv[cmdField++] ; } }
421 else
422 { field = nextField(0) ; } }
423
424 if (valid != 0)
425 { if (field != "EOL")
426 { *valid = 0 ; }
427 else
428 { *valid = 2 ; } }
429
430 return (field) ; }
431
432/*
433 Read an int and return the value. Set valid to indicate the result of
434 parsing: 0: okay, 1: parse error, 2: not present.
435*/
436
437int getIntField (int argc, const char *argv[], int *valid)
438
439{ std::string field ;
440
441 if (pendingVal != "")
442 { field = pendingVal ;
443 pendingVal = "" ; }
444 else
445 { field = "EOL" ;
446 if (cmdField > 0)
447 { if (cmdField < argc)
448 { field = argv[cmdField++] ; } }
449 else
450 { field = nextField(0) ; } }
451/*
452 The only way to check for parse error here is to set the system variable
453 errno to 0 and then see if it's nonzero after we try to convert the string
454 to integer.
455*/
456 int value = 0 ;
457 errno = 0 ;
458 if (field != "EOL")
459 { value = atoi(field.c_str()) ; }
460
461 if (valid != 0)
462 { if (field != "EOL")
463 { if (errno == 0)
464 { *valid = 0 ; }
465 else
466 { *valid = 1 ; } }
467 else
468 { *valid = 2 ; } }
469
470 return (value) ; }
471
472
473/*
474 Read a double and return the value. Set valid to indicate the result of
475 parsing: 0: okay, 1: bad parse, 2: not present. But we'll never return
476 valid == 1 because atof gives us no way to tell.)
477*/
478
479double getDoubleField (int argc, const char *argv[], int *valid)
480
481{ std::string field ;
482
483 if (pendingVal != "")
484 { field = pendingVal ;
485 pendingVal = "" ; }
486 else
487 { field = "EOL" ;
488 if (cmdField > 0)
489 { if (cmdField < argc)
490 { field = argv[cmdField++] ; } }
491 else
492 { field = nextField(0) ; } }
493/*
494 The only way to check for parse error here is to set the system variable
495 errno to 0 and then see if it's nonzero after we try to convert the string
496 to integer.
497*/
498 double value = 0.0 ;
499 errno = 0 ;
500 if (field != "EOL")
501 { value = atof(field.c_str()) ; }
502
503 if (valid != 0)
504 { if (field != "EOL")
505 { if (errno == 0)
506 { *valid = 0 ; }
507 else
508 { *valid = 1 ; } }
509 else
510 { *valid = 2 ; } }
511
512 return (value) ; }
513
514
515/*
516 Utility function to scan a parameter vector for matches. Sets matchNdx to
517 the index of the last parameter that meets the minimal match criteria (but
518 note there should be at most one such parameter if the parameter vector is
519 properly configured). Sets shortCnt to the number of short matches (should
520 be zero in a properly configured vector if a minimal match is found).
521 Returns the number of matches satisfying the minimal match requirement
522 (should be 0 or 1 in a properly configured vector).
523
524 The routine allows for the possibility of null entries in the parameter
525 vector.
526
527 In order to handle `?' and `???', there's nothing to it but to force a
528 unique match if we match `?' exactly. (This is another quirk of clp/cbc
529 parameter parsing, which we need to match for historical reasons.)
530*/
531
532int matchParam (const CoinParamVec &paramVec, std::string name,
533 int &matchNdx, int &shortCnt)
534
535{
536 int vecLen = static_cast<int>(paramVec.size()) ;
537 int matchCnt = 0 ;
538
539 matchNdx = -1 ;
540 shortCnt = 0 ;
541
542 for (int i = 0 ; i < vecLen ; i++)
543 { CoinParam *param = paramVec[i] ;
544 if (param == 0) continue ;
545 int match = paramVec[i]->matches(name) ;
546 if (match == 1)
547 { matchNdx = i ;
548 matchCnt++ ;
549 if (name == "?")
550 { matchCnt = 1 ;
551 break ; } }
552 else
553 { shortCnt += match>>1 ; } }
554
555 return (matchCnt) ;
556}
557
558/*
559 Now a bunch of routines that are useful in the context of generating help
560 messages.
561*/
562
563/*
564 Simple formatting routine for long messages. Used to print long help for
565 parameters. Lines are broken at the first white space after 65 characters,
566 or when an explicit return (`\n') character is scanned. Leading spaces are
567 suppressed.
568*/
569
570void printIt (const char *msg)
571
572{ int length = static_cast<int>(strlen(msg)) ;
573 char temp[101] ;
574 int i ;
575 int n = 0 ;
576 for (i = 0 ; i < length ; i++)
577 { if (msg[i] == '\n' ||
578 (n >= 65 && (msg[i] == ' ' || msg[i] == '\t')))
579 { temp[n] = '\0' ;
580 std::cout << temp << std::endl ;
581 n = 0 ; }
582 else
583 if (n || msg[i] != ' ')
584 { temp[n++] = msg[i] ; } }
585 if (n > 0)
586 { temp[n] = '\0' ;
587 std::cout << temp << std::endl ; }
588
589 return ; }
590
591
592/*
593 Utility function for the case where a name matches a single parameter, but
594 either it's short, or the user wanted help, or both.
595
596 The routine allows for the possibility that there are null entries in the
597 parameter vector, but matchNdx should point to a valid entry if it's >= 0.
598*/
599
600void shortOrHelpOne (CoinParamVec &paramVec,
601 int matchNdx, std::string name, int numQuery)
602
603{ int i ;
604 int numParams = static_cast<int>(paramVec.size()) ;
605 int lclNdx = -1 ;
606/*
607 For a short match, we need to look up the parameter again. This should find
608 a short match, given the conditions where this routine is called. But be
609 prepared to find a full match.
610
611 If matchNdx >= 0, just use the index we're handed.
612*/
613 if (matchNdx < 0)
614 { int match = 0 ;
615 for (i = 0 ; i < numParams ; i++)
616 { CoinParam *param = paramVec[i] ;
617 if (param == 0) continue ;
618 int match = param->matches(name) ;
619 if (match != 0)
620 { lclNdx = i ;
621 break ; } }
622
623 assert (lclNdx >= 0) ;
624
625 if (match == 1)
626 { std::cout
627 << "Match for '" << name << "': "
628 << paramVec[matchNdx]->matchName() << "." ; }
629 else
630 { std::cout
631 << "Short match for '" << name << "'; possible completion: "
632 << paramVec[lclNdx]->matchName() << "." ; } }
633 else
634 { assert(matchNdx >= 0 && matchNdx < static_cast<int>(paramVec.size())) ;
635 std::cout << "Match for `" << name << "': "
636 << paramVec[matchNdx]->matchName() ;
637 lclNdx = matchNdx ; }
638/*
639 Print some help, if there was a `?' in the name. `??' gets the long help.
640*/
641 if (numQuery > 0)
642 { std::cout << std::endl ;
643 if (numQuery == 1)
644 { std::cout << paramVec[lclNdx]->shortHelp() ; }
645 else
646 { paramVec[lclNdx]->printLongHelp() ; } }
647 std::cout << std::endl ;
648
649 return ; }
650
651/*
652 Utility function for the case where a name matches multiple parameters.
653 Zero or one `?' gets just the matching names, while `??' gets short help
654 with each match.
655
656 The routine allows for the possibility that there are null entries in the
657 parameter vector.
658*/
659
660void shortOrHelpMany (CoinParamVec &paramVec, std::string name, int numQuery)
661
662{ int numParams = static_cast<int>(paramVec.size()) ;
663/*
664 Scan the parameter list. For each match, print just the name, or the name
665 and short help.
666*/
667 int lineLen = 0 ;
668 bool printed = false ;
669 for (int i = 0 ; i < numParams ; i++)
670 { CoinParam *param = paramVec[i] ;
671 if (param == 0) continue ;
672 int match = param->matches(name) ;
673 if (match > 0)
674 { std::string nme = param->matchName() ;
675 int len = static_cast<int>(nme.length()) ;
676 if (numQuery >= 2)
677 { std::cout << nme << " : " << param->shortHelp() ;
678 std::cout << std::endl ; }
679 else
680 { lineLen += 2+len ;
681 if (lineLen > 80)
682 { std::cout << std::endl ;
683 lineLen = 2+len ; }
684 std::cout << " " << nme ;
685 printed = true ; } } }
686
687 if (printed)
688 { std::cout << std::endl ; }
689
690 return ; }
691
692
693/*
694 A generic help message that explains the basic operation of parameter
695 parsing.
696*/
697
698void printGenericHelp ()
699
700{ std::cout << std::endl ;
701 std::cout
702 << "For command line arguments, keywords have a leading `-' or '--'; "
703 << std::endl ;
704 std::cout
705 << "-stdin or just - switches to stdin with a prompt."
706 << std::endl ;
707 std::cout
708 << "When prompted, one command per line, without the leading `-'."
709 << std::endl ;
710 std::cout
711 << "abcd value sets abcd to value."
712 << std::endl ;
713 std::cout
714 << "abcd without a value (where one is expected) gives the current value."
715 << std::endl ;
716 std::cout
717 << "abcd? gives a list of possible matches; if there's only one, a short"
718 << std::endl ;
719 std::cout
720 << "help message is printed."
721 << std::endl ;
722 std::cout
723 << "abcd?? prints the short help for all matches; if there's only one"
724 << std::endl ;
725 std::cout
726 << "match, a longer help message and current value are printed."
727 << std::endl ;
728
729 return ; }
730
731
732/*
733 Utility function for various levels of `help' command. The entries between
734 paramVec[firstParam] and paramVec[lastParam], inclusive, will be printed.
735 If shortHelp is true, the short help message will be printed for each
736 parameter. If longHelp is true, the long help message will be printed for
737 each parameter. If hidden is true, even parameters with display = false
738 will be printed. Each line is prefaced with the specified prefix.
739
740 The routine allows for the possibility that there are null entries in the
741 parameter vector.
742*/
743
744void printHelp (CoinParamVec &paramVec, int firstParam, int lastParam,
745 std::string prefix,
746 bool shortHelp, bool longHelp, bool hidden)
747
748{ bool noHelp = !(shortHelp || longHelp) ;
749 int i ;
750 int pfxLen = static_cast<int>(prefix.length()) ;
751 bool printed = false ;
752
753 if (noHelp)
754 { int lineLen = 0 ;
755 for (i = firstParam ; i <= lastParam ; i++)
756 { CoinParam *param = paramVec[i] ;
757 if (param == 0) continue ;
758 if (param->display() || hidden)
759 { std::string nme = param->matchName() ;
760 int len = static_cast<int>(nme.length()) ;
761 if (!printed)
762 { std::cout << std::endl << prefix ;
763 lineLen += pfxLen ;
764 printed = true ; }
765 lineLen += 2+len ;
766 if (lineLen > 80)
767 { std::cout << std::endl << prefix ;
768 lineLen = pfxLen+2+len ; }
769 std::cout << " " << nme ; } }
770 if (printed)
771 { std::cout << std::endl ; } }
772 else
773 if (shortHelp)
774 { for (i = firstParam ; i <= lastParam ; i++)
775 { CoinParam *param = paramVec[i] ;
776 if (param == 0) continue ;
777 if (param->display() || hidden)
778 { std::cout << std::endl << prefix ;
779 std::cout << param->matchName() ;
780 std::cout << ": " ;
781 std::cout << param->shortHelp() ; } }
782 std::cout << std::endl ; }
783 else
784 if (longHelp)
785 { for (i = firstParam ; i <= lastParam ; i++)
786 { CoinParam *param = paramVec[i] ;
787 if (param == 0) continue ;
788 if (param->display() || hidden)
789 { std::cout << std::endl << prefix ;
790 std::cout << "Command: " << param->matchName() ;
791 std::cout << std::endl << prefix ;
792 std::cout << "---- description" << std::endl ;
793 printIt(param->longHelp().c_str()) ;
794 std::cout << prefix << "----" << std::endl ; } } }
795
796 std::cout << std::endl ;
797
798 return ; }
799
800} // end namespace CoinParamUtils
801