Edinburgh Speech Tools  2.4-release
 All Classes Functions Variables Typedefs Enumerations Enumerator Friends Pages
cmd_line.cc
1 /*************************************************************************/
2 /* */
3 /* Centre for Speech Technology Research */
4 /* University of Edinburgh, UK */
5 /* Copyright (c) 1994,1995,1996 */
6 /* All Rights Reserved. */
7 /* */
8 /* Permission is hereby granted, free of charge, to use and distribute */
9 /* this software and its documentation without restriction, including */
10 /* without limitation the rights to use, copy, modify, merge, publish, */
11 /* distribute, sublicense, and/or sell copies of this work, and to */
12 /* permit persons to whom this work is furnished to do so, subject to */
13 /* the following conditions: */
14 /* 1. The code must retain the above copyright notice, this list of */
15 /* conditions and the following disclaimer. */
16 /* 2. Any modifications must be clearly marked as such. */
17 /* 3. Original authors' names are not deleted. */
18 /* 4. The authors' names are not used to endorse or promote products */
19 /* derived from this software without specific prior written */
20 /* permission. */
21 /* */
22 /* THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK */
23 /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
24 /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
25 /* SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE */
26 /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
27 /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
28 /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
29 /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
30 /* THIS SOFTWARE. */
31 /* */
32 /*************************************************************************/
33 /* Author : Paul Taylor */
34 /* Date : October 1994 */
35 /*-----------------------------------------------------------------------*/
36 /* Command Line Utilities */
37 /* */
38 /* awb merged the help usage and argument definitions April 97 */
39 /* */
40 /*=======================================================================*/
41 #include <cstdlib>
42 #include "EST_unix.h"
43 #include "EST_String.h"
44 #include "EST_io_aux.h"
45 #include "EST_Token.h"
46 #include "EST_cutils.h"
47 #include "EST_TList.h"
48 #include "EST_string_aux.h"
49 #include "EST_cmd_line.h"
50 #include "EST_Pathname.h"
51 #include "EST_Features.h"
52 
53 // This is reset by the command line options functions to argv[0]
54 EST_String est_progname = "ESTtools";
55 
56 static int valid_option(const EST_Option &option,const char *arg,
57  EST_String &sarg);
58 static void standard_options(int argc, char **argv, const EST_String &usage);
59 static void arg_error(const EST_String &progname, const EST_String &message);
60 static void parse_usage(const EST_String &progname, const EST_String &usage,
61  EST_Option &options, EST_Option &al);
62 static void output_man_options(const EST_String &usage);
63 static void output_sgml_options(const EST_String &usage);
64 static void output_sgml_synopsis(char **argv, const EST_String &usage);
65 
66 int init_lib_ops(EST_Option &al, EST_Option &op)
67 {
68  char *envname;
69 
70  // read environment variable operations file if specified
71  if ((al.val("-N", 0) != "true") &&
72  ((envname = getenv("IA_OP_FILE")) != 0))
73  if (op.load(getenv("IA_OP_FILE")) != read_ok)
74  exit (1);
75 
76  // read command line operations file if specified
77  if (al.val("-c", 0) != "")
78  if (op.load(al.val("-c")) != read_ok)
79  exit (1);
80 
81  // override operations with command line options
82  override_lib_ops(op, al);
83 
84  if (al.val("-ops", 0) == "true") // print options if required
85  cout << op;
86 
87  return 0;
88 }
89 
90 // An attempt to integrate help, usage and argument definitions in
91 // one string (seems to work)
92 // Still to do:
93 // * adding arbitrary "-" at end of files (do we need that?)
94 // dealing with environment var specification of extra options
95 // override options function (maybe no longer needed)
96 // list of named values for argument
97 // Way to identify mandatory arguments
98 int parse_command_line(int argc,
99  char *argv[],
100  const EST_String &usage,
101  EST_StrList &files,
102  EST_Option &al, int make_stdio)
103 {
104  // Parse the command line arguments returning them in a normalised
105  // form in al, and files in files.
106  int i;
107  EST_Option options;
108  EST_String arg;
109  (void)make_stdio;
110 
111  // help, version, man_options are always supported
112  standard_options(argc,argv,usage);
113 
114  // Find valid options, arguments and defaults
115  // sets defaults in al
116  est_progname = argv[0];
117  parse_usage(argv[0],usage,options,al);
118 
119  for (i=1; i < argc; i++)
120  {
121  if (!EST_String(argv[i]).contains("-",0)) // its a filename
122  files.append(argv[i]);
123  else if (streq(argv[i],"-")) // single "-" denotes stdin/out
124  files.append("-");
125  else if (!valid_option(options,argv[i],arg))
126  {
127  arg_error(argv[0],
128  EST_String(": Unknown option \"")+argv[i]+"\"\n");
129  }
130  else // valid option, check args
131  {
132  if (options.val(arg) == "true") // no argument required
133  al.add_item(arg, "true");
134  else if (options.val(arg) == "<int>")
135  {
136  if (i+1 == argc)
137  arg_error(argv[0],
138  EST_String(": missing int argument for \"")+
139  arg+"\"\n");
140  i++;
141  if (!EST_String(argv[i]).matches(RXint))
142  arg_error(argv[0],
143  EST_String(": argument for \"")+
144  arg+"\" not an int\n");
145  al.add_item(arg,argv[i]);
146  }
147  else if ((options.val(arg) == "<float>") ||
148  (options.val(arg) == "<double>"))
149  {
150  if (i+1 == argc)
151  arg_error(argv[0],
152  EST_String(": missing float argument for \"")+
153  arg+"\"\n");
154  i++;
155  if (!EST_String(argv[i]).matches(RXdouble))
156  arg_error(argv[0],
157  EST_String(": argument for \"")+
158  arg+"\" not a float\n");
159  al.add_item(arg,argv[i]);
160  }
161  else if (options.val(arg) == "<string>")
162  {
163  if (i+1 == argc)
164  arg_error(argv[0],
165  EST_String(": missing string argument for \"")+
166  arg+"\"\n");
167  i++;
168  al.add_item(arg,argv[i]);
169  }
170  else if (options.val(arg) == "<ofile>")
171  {
172  if (i+1 == argc)
173  arg_error(argv[0],
174  EST_String(": missing ifile argument for \"")+
175  arg+"\"\n");
176  i++;
177  if (writable_file(argv[i]) == TRUE)
178  al.add_item(arg,argv[i]);
179  else
180  arg_error(argv[0],
181  EST_String(": output file not accessible \"")+
182  argv[i]+"\"\n");
183  }
184  else if (options.val(arg) == "<ifile>")
185  {
186  if (i+1 == argc)
187  arg_error(argv[0],
188  EST_String(": missing ifile argument for \"")+
189  arg+"\"\n");
190  i++;
191  if (readable_file(argv[i]) == TRUE)
192  al.add_item(arg,argv[i]);
193  else
194  arg_error(argv[0],
195  EST_String(": input file not accessible \"")+
196  argv[i]+"\"\n");
197 
198  }
199  else if (options.val(arg) == "<star>")
200  {
201  al.add_item(arg,EST_String(argv[i]).after(arg));
202  }
203  // else string list
204  else
205  {
206  arg_error(argv[0],
207  EST_String(": unknown argument type \"")+
208  options.val(argv[i])+"\" (misparsed usage string)\n");
209  }
210  }
211  }
212 
213  if (files.length() == 0)
214  files.append("-");
215 
216  return 0;
217 }
218 
219 static int valid_option(const EST_Option &options,const char *arg,
220  EST_String &sarg)
221 {
222  // Checks to see arg is declared as an option.
223  // This would be trivial were it not for options containing *
224  // The actual arg name is put in sarg
225  EST_Litem *p;
226 
227  for (p = options.list.head(); p != 0; p = p->next())
228  {
229  if (options.key(p) == arg)
230  {
231  sarg = arg;
232  return TRUE;
233  }
234  else if ((options.val(p) == "<star>") &&
235  (EST_String(arg).contains(options.key(p), 0)))
236  {
237  sarg = options.key(p);
238  return TRUE;
239  }
240  }
241 
242  return FALSE;
243 }
244 
245 static void parse_usage(const EST_String &progname, const EST_String &usage,
246  EST_Option &options, EST_Option &al)
247 {
248  // Extract option definitions from usage and put them in options
249  // If defaults are specified add them al
250  EST_TokenStream ts;
251  EST_Token t;
252 
253  ts.open_string(usage);
254  ts.set_SingleCharSymbols("{}[]|");
255  ts.set_PunctuationSymbols("");
257 
258  while (!ts.eof())
259  {
260  t = ts.get();
261  if ((t.string().contains("-",0)) &&
262  (t.whitespace().contains("\n")))
263  { // An argument
264  if ((ts.peek().string() == "<string>") ||
265  (ts.peek().string() == "<float>") ||
266  (ts.peek().string() == "<double>") ||
267  (ts.peek().string() == "<ifile>") ||
268  (ts.peek().string() == "<ofile>") ||
269  (ts.peek().string() == "<int>"))
270  {
271  options.add_item(t.string(),ts.get().string());
272  if (ts.peek().string() == "{") // a default is given
273  {
274  ts.get();
275  al.add_item(t.string(),ts.get().string());
276  if (ts.get() != "}")
277  arg_error(progname,
278  EST_String(": malformed default value for \"")+
279  t.string()+"\" (missing closing brace)\n");
280  }
281  }
282  else if (t.string().contains("*"))
283  options.add_item(t.string().before("*"),"<star>");
284  // else check for explicit list of names
285  else
286  options.add_item(t.string(),"true"); // simple argument
287  }
288  }
289 }
290 
291 static void arg_error(const EST_String &progname, const EST_String &message)
292 {
293  // Output message and pointer to more help then exit
294  cerr << progname << message;
295  cerr << "Type -h for help on options.\n";
296  exit(-1);
297 }
298 
299 static void standard_options(int argc, char **argv, const EST_String &usage)
300 {
301  // A number of options are always supported
302  int i;
303 
304  for (i=1; i < argc; i++)
305  {
306  if (streq(argv[i],"-man_options"))
307  {
308  output_man_options(usage);
309  exit(0);
310  }
311  if (streq(argv[i],"-sgml_options"))
312  {
313  output_sgml_options(usage);
314  exit(0);
315  }
316  if (streq(argv[i],"-sgml_synopsis"))
317  {
318  output_sgml_synopsis(argv, usage);
319  exit(0);
320  }
321  if ((streq(argv[i],"-h")) ||
322  (streq(argv[i],"-help")) ||
323  (streq(argv[i],"-?")) ||
324  (streq(argv[i],"--help")))
325  {
326  EST_Pathname full(argv[0]);
327  cout << "Usage: " << full.filename() << " " << usage << endl;
328  exit(0);
329  }
330  if (((streq(argv[i],"-version")) ||
331  (streq(argv[i],"--version")))&&
332  (!usage.contains("\n-v")))
333  {
334  cout << argv[0] << ": " << est_name << " v" << est_tools_version << endl;
335  exit(0);
336  }
337  }
338 
339  return;
340 }
341 
342 static void output_man_options(const EST_String &usage)
343 {
344  EST_TokenStream ts;
345  EST_Token t;
346  int in_options = FALSE;
347 
348  ts.open_string(usage);
349  ts.set_SingleCharSymbols("{}[]|");
350  ts.set_PunctuationSymbols("");
352 
353  while (!ts.eof())
354  {
355  t = ts.get();
356  if ((t.string().contains("-",0)) &&
357  (t.whitespace().contains("\n")))
358  { // An argument
359  fprintf(stdout,"\n.TP 8\n.BI \"%s \" ",(const char *)t.string());
360  if ((ts.peek().string() == "<string>") ||
361  (ts.peek().string() == "<float>") ||
362  (ts.peek().string() == "<double>") ||
363  (ts.peek().string() == "<int>"))
364  fprintf(stdout,"%s",(const char *)ts.get().string());
365  if ((ts.peek().string() == "{"))
366  { // a default value
367  ts.get();
368  fprintf(stdout," \" {%s}\"",(const char *)ts.get().string());
369  ts.get();
370  }
371  if (!ts.peek().whitespace().contains("\n"))
372  fprintf(stdout,"\n");
373  in_options = TRUE;
374  }
375  else if (in_options)
376  {
377  if (t.whitespace().contains("\n"))
378  fprintf(stdout,"\n");
379  fprintf(stdout,"%s ",(const char *)t.string());
380  }
381  }
382  if (in_options)
383  fprintf(stdout,"\n");
384 
385 
386 }
387 
388 static void output_sgml_options(const EST_String &usage)
389 {
390  EST_TokenStream ts;
391  EST_Token t;
392  EST_String atype;
393  int in_options = FALSE;
394 
395  ts.open_string(usage);
396  ts.set_SingleCharSymbols("{}[]|");
397  ts.set_PunctuationSymbols("");
399 
400  fprintf(stdout,"<variablelist>\n");
401 
402  while (!ts.eof())
403  {
404  t = ts.get();
405  if ((t.string().contains("-",0)) &&
406  (t.whitespace().contains("\n")))
407  { // An argument
408  if (in_options)
409  fprintf(stdout,"\n</PARA></LISTITEM>\n</varlistentry>\n\n");
410  fprintf(stdout,"<varlistentry><term>%s</term>\n<LISTITEM><PARA>\n", (const char *)t.string());
411  if ((ts.peek().string() == "<string>") ||
412  (ts.peek().string() == "<float>") ||
413  (ts.peek().string() == "<ifile>") ||
414  (ts.peek().string() == "<ofile>") ||
415  (ts.peek().string() == "<double>") ||
416  (ts.peek().string() == "<int>"))
417  { // must strip of the angle brackets to make SGML
418  atype = ts.get().string();
419  atype.gsub("<","");
420  atype.gsub(">","");
421  fprintf(stdout,"<replaceable>%s</replaceable>\n",
422  (const char *) atype);
423  }
424  if ((ts.peek().string() == "{"))
425  { // a default value
426  ts.get();
427  fprintf(stdout," \" {%s}\"",(const char *)ts.get().string());
428  ts.get();
429  }
430  if (!ts.peek().whitespace().contains("\n"))
431  fprintf(stdout,"\n");
432  in_options = TRUE;
433  }
434  else if (in_options)
435  {
436  if (t.whitespace().contains("\n"))
437  fprintf(stdout,"\n");
438  fprintf(stdout,"%s ",(const char *)t.string());
439  }
440  }
441  if (in_options)
442  fprintf(stdout,"</PARA></LISTITEM>\n</varlistentry>\n</variablelist>\n");
443 
444 }
445 
446 static void output_sgml_synopsis(char **argv, const EST_String &usage)
447 {
448  EST_TokenStream ts;
449  EST_Token t;
450  EST_String atype;
451  int in_options = FALSE;
452 
453  ts.open_string(usage);
454  ts.set_SingleCharSymbols("{}[]|");
455  ts.set_PunctuationSymbols("");
457 
458  EST_Pathname full(argv[0]);
459 
460  fprintf(stdout,"<cmdsynopsis><command>%s</command>",
461  (const char *)full.filename());
462 
463  fprintf(stdout,"%s",(const char *)ts.get_upto_eoln().string());
464 
465  while (!ts.eof())
466  {
467  t = ts.get();
468  if ((t.string().contains("-",0)) &&
469  (t.whitespace().contains("\n")))
470  { // An argument
471  if (in_options)
472  fprintf(stdout,"</arg>\n");
473  fprintf(stdout,"<arg>%s ", (const char *)t.string());
474  if ((ts.peek().string() == "<string>") ||
475  (ts.peek().string() == "<float>") ||
476  (ts.peek().string() == "<ifile>") ||
477  (ts.peek().string() == "<ofile>") ||
478  (ts.peek().string() == "<double>") ||
479  (ts.peek().string() == "<int>"))
480  { // must strip of the angle brackets to make SGML
481  atype = ts.get().string();
482  atype.gsub("<","");
483  atype.gsub(">","");
484  fprintf(stdout,"<replaceable>%s</replaceable>",
485  (const char *) atype);
486  }
487  if ((ts.peek().string() == "{"))
488  { // a default value
489  ts.get();
490  fprintf(stdout," \" {%s}\"",(const char *)ts.get().string());
491  ts.get();
492  }
493  in_options = TRUE;
494  }
495  }
496  fprintf(stdout,"</arg>\n</cmdsynopsis>\n");
497 }
498 
499 EST_String options_general(void)
500 {
501  // The standard waveform input options
502  return
503  EST_String("")+
504  "-o <ofile> output file" +
505  "-otype <string> output file type\n";
506 }
507 
508 void option_override(EST_Features &op, EST_Option al,
509  const EST_String &option, const EST_String &arg)
510 {
511  if (al.present(arg))
512  op.set(option, al.val(arg));
513 }
514