root/trunk/asymptote/settings.cc

Revision 8, 20.1 kB (checked in by marius, 5 years ago)

Added files

Line 
1/*****
2 * settings.cc
3 * Andy Hammerlindl 2004/05/10
4 *
5 * Declares a list of global variables that act as settings in the system.
6 *****/
7
8#include <iostream>
9#include <iomanip>
10#include <cstdlib>
11#include <cerrno>
12#include <vector>
13#include <sys/stat.h>
14
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#if HAVE_GNU_GETOPT_H
20#include <getopt.h>
21#else
22#include "getopt.h"
23#endif
24
25#include "util.h"
26#include "settings.h"
27#include "pen.h"
28#include "interact.h"
29#include "locate.h"
30#include "lexical.h"
31#include "memory.h"
32#include "record.h"
33#include "env.h"
34#include "item.h"
35#include "refaccess.h"
36
37using std::vector;
38using vm::item;
39
40using trans::itemRefAccess;
41using trans::refAccess;
42using trans::varEntry;
43
44namespace loop {
45  void doConfig(string filename);
46}
47
48namespace settings {
49 
50#ifdef MSDOS
51const bool msdos=true;
52const char pathSeparator=';';
53const string defaultPSViewer=
54  "'c:\\Program Files\\Ghostgum\\gsview\\gsview32.exe'";
55const string defaultPDFViewer=
56  "'c:\\Program Files\\Adobe\\Acrobat 7.0\\Reader\\AcroRd32.exe'";
57const string defaultGhostscript=
58  "'c:\\Program Files\\gs\\gs8.51\\bin\\gswin32.exe'";
59const string defaultPython="'c:\\Python24\\python.exe'";
60const string defaultDisplay="imdisplay";
61#undef ASYMPTOTE_SYSDIR
62#define ASYMPTOTE_SYSDIR "c:\\Program Files\\Asymptote"
63const string docdir=".";
64#else 
65const bool msdos=false;
66const char pathSeparator=':';
67const string defaultPSViewer="gv";
68const string defaultPDFViewer="acroread";
69const string defaultGhostscript="gs";
70const string defaultDisplay="display";
71const string defaultPython="";
72const string docdir=ASYMPTOTE_DOCDIR;
73#endif 
74
75const char PROGRAM[]=PACKAGE_NAME;
76const char VERSION[]=PACKAGE_VERSION;
77const char BUGREPORT[]=PACKAGE_BUGREPORT;
78
79// The name of the program (as called).  Used when displaying help info.
80char *argv0;
81
82typedef ::option c_option;
83
84types::dummyRecord *settingsModule;
85
86types::record *getSettingsModule() {
87  return settingsModule;
88}
89
90// The dictionaries of long options and short options.
91class option;
92typedef mem::map<CONST mem::string, option *> optionsMap_t;
93optionsMap_t optionsMap;
94typedef mem::map<CONST char, option *> codeMap_t;
95codeMap_t codeMap;
96 
97struct option : public gc {
98  mem::string name;
99  char code;      // Command line option, i.e. 'V' for -V.
100  bool argument;  // If it takes an argument on the command line.  This is set
101                  // based on whether argname is empty.
102 
103  mem::string argname; // The argument name for printing the description.
104  mem::string desc; // One line description of what the option does.
105
106  option(mem::string name, char code, mem::string argname, mem::string desc)
107    : name(name), code(code), argument(!argname.empty()),
108      argname(argname), desc(desc) {}
109
110  virtual ~option() {}
111
112  // Builds this option's contribution to the optstring argument of get_opt().
113  virtual mem::string optstring() {
114    if (code) {
115      mem::string base;
116      base.push_back(code);
117      return argument ? base+":" : base;
118    }
119    else return "";
120  }
121
122  // Sets the contribution to the longopt array.
123  virtual void longopt(c_option &o) {
124    o.name=name.c_str();
125    o.has_arg=argument ? 1 : 0;
126    o.flag=0;
127    o.val=0;
128  }
129
130  // Add to the dictionaries of options.
131  virtual void add() {
132    optionsMap[name]=this;
133    if (code)
134      codeMap[code]=this;
135  }
136
137  // Set the option from the command-line argument.  Return true if the option
138  // was correctly parsed.
139  virtual bool getOption() = 0;
140
141  void error(string msg) {
142    cerr << endl << argv0 << ": ";
143    if (code)
144      cerr << "-" << code << " ";
145    cerr << "(-" << name << ") " << msg << endl;
146  }
147
148  // The "-f,-outformat format" part of the option.
149  virtual mem::string describeStart() {
150    ostringstream ss;
151    if (code)
152      ss << "-" << code << ",";
153    ss << "-" << name;
154    if (argument)
155      ss << " " << argname;
156    return ss.str();
157  }
158
159  // Outputs description of the command for the -help option.
160  virtual void describe() {
161    // Don't show the option if it has no desciption.
162    if (!desc.empty()) {
163      const unsigned int WIDTH=22;
164      mem::string start=describeStart();
165      cerr << std::left << std::setw(WIDTH) << start;
166      if (start.size() >= WIDTH) {
167        cerr << endl;
168        cerr << std::left << std::setw(WIDTH) << "";
169      }
170      cerr << desc << endl;
171    }
172  }
173};
174
175const mem::string noarg;
176
177struct setting : public option {
178  types::ty *t;
179
180  setting(mem::string name, char code, mem::string argname, mem::string desc,
181          types::ty *t)
182      : option(name, code, argname, desc), t(t) {}
183
184  virtual void reset() = 0;
185
186  virtual trans::access *buildAccess() = 0;
187
188  // Add to the dictionaries of options and to the settings module.
189  virtual void add() {
190    option::add();
191
192    settingsModule->e.addVar(symbol::trans(name), buildVarEntry());
193  }
194
195  varEntry *buildVarEntry() {
196    return new varEntry(t, buildAccess());
197  }
198};
199
200struct itemSetting : public setting {
201  item defaultValue;
202  item value;
203
204  itemSetting(mem::string name, char code,
205              mem::string argname, mem::string desc,
206              types::ty *t, item defaultValue)
207      : setting(name, code, argname, desc, t), defaultValue(defaultValue) {
208    reset();
209  }
210
211  void reset() {
212    value=defaultValue;
213  }
214
215  trans::access *buildAccess() {
216    return new itemRefAccess(&(value));
217  }
218};
219
220item& Setting(string name) {
221  itemSetting *s=dynamic_cast<itemSetting *>(optionsMap[name]);
222  assert(s);
223  return s->value;
224}
225 
226struct boolSetting : public itemSetting {
227  boolSetting(mem::string name, char code, mem::string desc,
228              bool defaultValue=false)
229    : itemSetting(name, code, noarg, desc,
230                  types::primBoolean(), (item)defaultValue) {}
231
232  bool getOption() {
233    value=(item)true;
234    return true;
235  }
236
237  option *negation(mem::string name) {
238    struct negOption : public option {
239      boolSetting &base;
240
241      negOption(boolSetting &base, mem::string name)
242        : option(name, 0, noarg, ""), base(base) {}
243
244      bool getOption() {
245        base.value=(item)false;
246        return true;
247      }
248    };
249    return new negOption(*this, name);
250  }
251
252  void add() {
253    setting::add();
254    negation("no"+name)->add();
255    if (code) {
256      mem::string nocode="no"; nocode.push_back(code);
257      negation(nocode)->add();
258    }
259  }
260
261  // Set several related boolean options at once.  Used for view and trap which
262  // have batch and interactive settings.
263  struct multiOption : public option {
264    typedef mem::list<boolSetting *> setlist;
265    setlist set;
266    multiOption(mem::string name, char code, mem::string desc)
267      : option(name, code, noarg, desc) {}
268
269    void add(boolSetting *s) {
270      set.push_back(s);
271    }
272
273    void setValue(bool value) {
274      for (setlist::iterator s=set.begin(); s!=set.end(); ++s)
275        (*s)->value=(item)value;
276    }
277
278    bool getOption() {
279      setValue(true);
280      return true;
281    }
282
283    option *negation(mem::string name) {
284      struct negOption : public option {
285        multiOption &base;
286
287        negOption(multiOption &base, mem::string name)
288          : option(name, 0, noarg, ""), base(base) {}
289
290        bool getOption() {
291          base.setValue(false);
292          return true;
293        }
294      };
295      return new negOption(*this, name);
296    }
297
298    void add() {
299      option::add();
300      negation("no"+name)->add();
301      if (code) {
302        mem::string nocode="no"; nocode.push_back(code);
303        negation(nocode)->add();
304      }
305
306      for (multiOption::setlist::iterator s=set.begin(); s!=set.end(); ++s)
307        (*s)->add();
308    }
309  };
310};
311
312typedef boolSetting::multiOption multiOption;
313
314struct argumentSetting : public itemSetting {
315  argumentSetting(mem::string name, char code,
316                  mem::string argname, mem::string desc,
317                  types::ty *t, item defaultValue)
318    : itemSetting(name, code, argname, desc, t, defaultValue) 
319  {
320    assert(!argname.empty());
321  }
322};
323
324struct stringSetting : public argumentSetting {
325  stringSetting(mem::string name, char code,
326                mem::string argname, mem::string desc,
327                mem::string defaultValue)
328    : argumentSetting(name, code, argname, desc,
329              types::primString(), (item)defaultValue) {}
330
331  bool getOption() {
332    value=(item)(mem::string)optarg;
333    return true;
334  }
335};
336
337mem::string GetEnv(mem::string s, mem::string Default) {
338  transform(s.begin(), s.end(), s.begin(), toupper);       
339  string t=Getenv(("ASYMPTOTE_"+s).c_str());
340  return t != "" ? mem::string(t) : Default;
341}
342 
343struct envSetting : public stringSetting {
344  envSetting(mem::string name, mem::string Default)
345    : stringSetting(name, 0, " ", "", GetEnv(name,Default)) {}
346};
347
348struct realSetting : public argumentSetting {
349  realSetting(mem::string name, char code,
350              mem::string argname, mem::string desc,
351              double defaultValue)
352    : argumentSetting(name, code, argname, desc,
353                  types::primReal(), (item)defaultValue) {}
354
355  bool getOption() {
356    try {
357      value=(item)lexical::cast<double>(optarg);
358    } catch (lexical::bad_cast&) {
359      error("option requires a real number as an argument");
360      return false;
361    }
362    return true;
363  }
364};
365
366struct pairSetting : public argumentSetting {
367  pairSetting(mem::string name, char code,
368              mem::string argname, mem::string desc,
369              camp::pair defaultValue=0.0)
370    : argumentSetting(name, code, argname, desc,
371                  types::primPair(), (item)defaultValue) {}
372
373  bool getOption() {
374    try {
375      value=(item)lexical::cast<camp::pair>(optarg);
376    } catch (lexical::bad_cast&) {
377      error("option requires a pair as an argument");
378      return false;
379    }
380    return true;
381  }
382};
383
384// For setting the alignment of a figure on the page.
385struct alignSetting : public argumentSetting {
386  alignSetting(mem::string name, char code,
387                  mem::string argname, mem::string desc,
388                  int defaultValue=CENTER)
389    : argumentSetting(name, code, argname, desc,
390                  types::primInt(), (item)defaultValue) {}
391
392  bool getOption() {
393    mem::string str=optarg;
394    if (str=="C")
395      value=(int)CENTER;
396    else if (str=="T")
397      value=(int)TOP;
398    else if (str=="B")
399      value=(int)BOTTOM;
400    else if (str=="Z") {
401      value=(int)ZERO;
402      Setting("tex")=false;
403    }
404    else {
405      error("invalid argument for option");
406      return false;
407    }
408    return true;
409  }
410};
411
412template <class T>
413struct refSetting : public setting {
414  T *ref;
415  T defaultValue;
416
417  refSetting(mem::string name, char code, mem::string argname,
418             mem::string desc, types::ty *t, T *ref, T defaultValue)
419      : setting(name, code, argname, desc, t),
420        ref(ref), defaultValue(defaultValue) {
421    reset();
422  }
423
424  virtual void reset() {
425    *ref=defaultValue;
426  }
427
428  trans::access *buildAccess() {
429    return new refAccess<T>(ref);
430  }
431};
432
433struct incrementSetting : public refSetting<int> {
434  incrementSetting(mem::string name, char code, mem::string desc, int *ref)
435    : refSetting<int>(name, code, noarg, desc,
436              types::primInt(), ref, 0) {}
437
438  bool getOption() {
439    // Increment the value.
440    ++(*ref);
441    return true;
442  }
443 
444  option *negation(mem::string name) {
445    struct negOption : public option {
446      incrementSetting &base;
447
448      negOption(incrementSetting &base, mem::string name)
449        : option(name, 0, noarg, ""), base(base) {}
450
451      bool getOption() {
452        if(*base.ref) --(*base.ref);
453        return true;
454      }
455    };
456    return new negOption(*this, name);
457  }
458
459  void add() {
460    setting::add();
461    negation("no"+name)->add();
462    if (code) {
463      mem::string nocode="no"; nocode.push_back(code);
464      negation(nocode)->add();
465    }
466  }
467};
468
469void addOption(option *o) {
470  o->add();
471}
472
473void usage(const char *program)
474{
475  cerr << PROGRAM << " version " << VERSION
476       << " [(C) 2004 Andy Hammerlindl, John C. Bowman, Tom Prince]" 
477       << endl
478       << "\t\t\t" << "http://asymptote.sourceforge.net/"
479       << endl
480       << "Usage: " << program << " [options] [file ...]"
481       << endl;
482}
483
484void reportSyntax() {
485  cerr << endl;
486  usage(argv0);
487  cerr << endl << "Type '" << argv0
488    << " -h' for a description of options." << endl;
489  exit(1);
490}
491
492void displayOptions()
493{
494  cerr << endl;
495  cerr << "Options: " << endl;
496  for (optionsMap_t::iterator opt=optionsMap.begin();
497       opt!=optionsMap.end();
498       ++opt)
499    opt->second->describe();
500}
501
502struct helpOption : public option {
503  helpOption(mem::string name, char code, mem::string desc)
504    : option(name, code, noarg, desc) {}
505
506  bool getOption() {
507    usage(argv0);
508    displayOptions();
509    cerr << endl;
510    exit(0);
511
512    // Unreachable code.
513    return true;
514  }
515};
516
517// For security reasons, safe isn't a field of the settings module.
518struct safeOption : public option {
519  bool value;
520
521  safeOption(mem::string name, char code, mem::string desc, bool value)
522    : option(name, code, noarg, desc), value(value) {}
523
524  bool getOption() {
525    safe=value;
526    return true;
527  }
528};
529
530
531mem::string build_optstring() {
532  mem::string s;
533  for (codeMap_t::iterator p=codeMap.begin(); p !=codeMap.end(); ++p)
534    s +=p->second->optstring();
535
536  return s;
537}
538
539c_option *build_longopts() {
540  size_t n=optionsMap.size();
541
542#ifdef USEGC
543  c_option *longopts=new (GC) c_option[n];
544#else
545  c_option *longopts=new c_option[n];
546#endif
547
548  int i=0;
549  for (optionsMap_t::iterator p=optionsMap.begin();
550       p !=optionsMap.end();
551       ++p, ++i)
552    p->second->longopt(longopts[i]);
553
554  return longopts;
555}
556
557void resetOptions() {
558  verbose=0;
559}
560 
561void getOptions(int argc, char *argv[])
562{
563  bool syntax=false;
564  optind=0;
565
566  mem::string optstring=build_optstring();
567  //cerr << "optstring: " << optstring << endl;
568  c_option *longopts=build_longopts();
569  int long_index = 0;
570
571  errno=0;
572  for(;;) {
573    int c = getopt_long_only(argc,argv,
574                             optstring.c_str(), longopts, &long_index);
575    if (c == -1)
576      break;
577
578    if (c == 0) {
579      const char *name=longopts[long_index].name;
580      //cerr << "long option: " << name << endl;
581      if (!optionsMap[name]->getOption())
582        syntax=true;
583    }
584    else if (codeMap.find((char)c) != codeMap.end()) {
585      //cerr << "char option: " << (char)c << endl;
586      if (!codeMap[(char)c]->getOption())
587        syntax=true;
588    }
589    else {
590      syntax=true;
591    }
592
593    errno=0;
594  }
595 
596  if (syntax)
597    reportSyntax();
598}
599
600// The verbosity setting, a global variable.
601int verbose;
602
603void initSettings() {
604  settingsModule=new types::dummyRecord(symbol::trans("settings"));
605 
606  multiOption *view=new multiOption("View", 'V', "View output files");
607  view->add(new boolSetting("batchView", 0,
608                            "View output files in batch mode", false));
609  view->add(new boolSetting("interactiveView", 0,
610                            "View output files in interactive mode", true));
611  view->add(new boolSetting("oneFileView", 0, "", msdos));
612  addOption(view);
613
614  addOption(new realSetting("deconstruct", 'x', "X",
615                     "Deconstruct into transparent GIF objects magnified by X",
616                            0.0));
617  addOption(new boolSetting("clearGUI", 'c', "Clear GUI operations"));
618  addOption(new boolSetting("ignoreGUI", 'i', "Ignore GUI operations"));
619  addOption(new stringSetting("outformat", 'f', "format",
620                              "Convert each output file to specified format",
621                              "eps"));
622  addOption(new stringSetting("outname", 'o', "name",
623                              "(First) output file name", ""));
624  addOption(new helpOption("help", 'h', "Show summary of options"));
625
626  addOption(new pairSetting("offset", 'O', "pair", "PostScript offset"));
627  addOption(new alignSetting("align", 'a', "C|B|T|Z",
628                "Center, Bottom, Top or Zero page alignment; Z => -notex"));
629 
630  addOption(new boolSetting("debug", 'd', "Enable debugging messages"));
631  addOption(new incrementSetting("verbose", 'v',
632                                 "Increase verbosity level", &verbose));
633  addOption(new boolSetting("keep", 'k', "Keep intermediate files"));
634  addOption(new boolSetting("tex", 0,
635                            "Enable LaTeX label postprocessing (default)",
636                            true));
637  addOption(new boolSetting("inlinetex", 0, ""));
638  addOption(new boolSetting("parseonly", 'p', "Parse test"));
639  addOption(new boolSetting("translate", 's', "Translate test"));
640  addOption(new boolSetting("listvariables", 'l',
641                            "List available global functions and variables"));
642 
643  multiOption *mask=new multiOption("mask", 'm',
644                                    "Mask fpu exceptions");
645  mask->add(new boolSetting("batchMask", 0,
646                            "Mask fpu exceptions in batch mode", false));
647  mask->add(new boolSetting("interactiveMask", 0,
648                            "Mask fpu exceptions in interactive mode", true));
649  addOption(mask);
650
651  addOption(new boolSetting("bw", 0,
652                            "Convert all colors to black and white"));
653  addOption(new boolSetting("gray", 0, "Convert all colors to grayscale"));
654  addOption(new boolSetting("rgb", 0, "Convert cmyk colors to rgb"));
655  addOption(new boolSetting("cmyk", 0, "Convert rgb colors to cmyk"));
656
657  addOption(new safeOption("safe", 0,
658                           "Disable system call (default)", true));
659  addOption(new safeOption("unsafe", 0,
660                           "Enable system call", false));
661
662  addOption(new boolSetting("localhistory", 0, 
663                            "Use a local interactive history file"));
664  addOption(new boolSetting("autoplain", 0,
665                            "Enable automatic importing of plain (default)",
666                            true));
667 
668  addOption(new envSetting("config",initdir+"/config.asy"));
669  addOption(new envSetting("pdfviewer", defaultPDFViewer));
670  addOption(new envSetting("psviewer", defaultPSViewer));
671  addOption(new envSetting("gs", defaultGhostscript));
672  addOption(new envSetting("latex", "latex"));
673  addOption(new envSetting("dvips", "dvips"));
674  addOption(new envSetting("convert", "convert"));
675  addOption(new envSetting("display", defaultDisplay));
676  addOption(new envSetting("animate", "animate"));
677  addOption(new envSetting("python", defaultPython));
678  addOption(new envSetting("xasy", "xasy"));
679  addOption(new envSetting("papertype", "letter"));
680  addOption(new envSetting("dir", ""));
681 
682}
683
684int safe=1;
685int ShipoutNumber=0;
686int scrollLines=0;
687 
688const string suffix="asy";
689const string guisuffix="gui";
690 
691bool TeXinitialized=false;
692string initdir;
693
694camp::pen defaultpen=camp::pen::startupdefaultpen();
695 
696// Local versions of the argument list.
697int argCount = 0;
698char **argList = 0;
699 
700// Access the arguments once options have been parsed.
701int numArgs() { return argCount; }
702char *getArg(int n) { return argList[n]; }
703
704void setInteractive() {
705  if(numArgs() == 0 && !getSetting<bool>("listvariables"))
706    interact::interactive=true;
707}
708
709bool view() {
710  if (interact::interactive)
711    return getSetting<bool>("interactiveView");
712  else
713    return getSetting<bool>("batchView") ||
714           (numArgs()==1 && getSetting<bool>("oneFileView"));
715}
716
717bool trap() {
718  if (interact::interactive)
719    return !getSetting<bool>("interactiveMask");
720  else
721    return !getSetting<bool>("batchMask");
722}
723
724void initDir() {
725  initdir=Getenv("HOME",false)+"/.asy";
726  mkdir(initdir.c_str(),0xFFFF);
727}
728 
729void setPath() {
730  searchPath.push_back(".");
731  string asydir=getSetting<mem::string>("dir");
732  if(asydir != "") {
733    size_t p,i=0;
734    while((p=asydir.find(pathSeparator,i)) < string::npos) {
735      if(p > i) searchPath.push_back(asydir.substr(i,p-i));
736      i=p+1;
737    }
738    if(i < asydir.length()) searchPath.push_back(asydir.substr(i));
739  }
740#ifdef ASYMPTOTE_SYSDIR
741  searchPath.push_back(ASYMPTOTE_SYSDIR);
742#endif
743}
744
745void GetPageDimensions(double& pageWidth, double& pageHeight) {
746  string paperType=getSetting<mem::string>("papertype");
747
748  if(paperType == "letter") {
749    pageWidth=72.0*8.5;
750    pageHeight=72.0*11.0;
751  } else {
752    pageWidth=72.0*21.0/2.54;
753    pageHeight=72.0*29.7/2.54;
754    if(paperType != "a4") {
755      cerr << "Unknown paper size \'" << paperType << "\'; assuming a4." 
756           << endl;
757      Setting("papertype")=mem::string("a4");
758    }
759  }
760}
761
762void setOptions(int argc, char *argv[])
763{
764  argv0=argv[0];
765
766  // Make configuration and history directory
767  initDir();
768 
769  // Build settings module.
770  initSettings();
771 
772  // Read command-line options initially to obtain CONFIG and DIR.
773  getOptions(argc,argv);
774  resetOptions();
775 
776  // Read user configuration file.
777  setPath();
778  loop::doConfig(getSetting<mem::string>("config"));
779
780  // Read command-line options again to override configuration file defaults.
781  getOptions(argc,argv);
782 
783  // Set variables for the normal arguments.
784  argCount = argc - optind;
785  argList = argv + optind;
786
787  setInteractive();
788}
789
790}
Note: See TracBrowser for help on using the browser.