dvitomp fix from Akira
[mplib] / src / texk / web2c / mpdir / mpost.w
1 % $Id$
2 %
3 % Copyright 2008 Taco Hoekwater.
4 %
5 % This program is free software: you can redistribute it and/or modify
6 % it under the terms of the GNU General Public License as published by
7 % the Free Software Foundation, either version 2 of the License, or
8 % (at your option) any later version.
9 %
10 % This program is distributed in the hope that it will be useful,
11 % but WITHOUT ANY WARRANTY; without even the implied warranty of
12 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 % GNU General Public License for more details.
14 %
15 % You should have received a copy of the GNU General Public License
16 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 \font\tenlogo=logo10 % font used for the METAFONT logo
19 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
20
21 \def\title{MetaPost executable}
22 \def\[#1]{#1.}
23 \pdfoutput=1
24
25 @* \[1] Metapost executable.
26
27 Now that all of \MP\ is a library, a separate program is needed to 
28 have our customary command-line interface. 
29
30 @ First, here are the C includes. |avl.h| is needed because of an 
31 |avl_allocator| that is defined in |mplib.h|
32
33 @d true 1
34 @d false 0
35  
36 @c
37 #include "config.h"
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #if defined (HAVE_SYS_TIME_H)
43 #include <sys/time.h>
44 #elif defined (HAVE_SYS_TIMEB_H)
45 #include <sys/timeb.h>
46 #endif
47 #include <mplib.h>
48 #include <mpxout.h>
49 #ifdef WIN32
50 #include <process.h>
51 #endif
52 #include <kpathsea/kpathsea.h>
53 extern char *kpathsea_version_string;
54 extern string kpse_program_name;
55 @= /*@@null@@*/ @> static char *mpost_tex_program = NULL;
56 static int debug = 0; /* debugging for makempx */
57
58 @ Allocating a bit of memory, with error detection:
59
60 @d mpost_xfree(A) do { if (A!=NULL) free(A); A=NULL; } while (0)
61
62 @c
63 @= /*@@only@@*/ /*@@out@@*/ @> static void  *mpost_xmalloc (size_t bytes) {
64   void *w = malloc (bytes); 
65   if (w==NULL) {
66     fprintf(stderr,"Out of memory!\n");
67     exit(EXIT_FAILURE);
68   }
69   return w;
70 }
71 @= /*@@only@@*/ @> static char *mpost_xstrdup(const char *s) {
72   char *w; 
73   w = strdup(s);
74   if (w==NULL) {
75     fprintf(stderr,"Out of memory!\n");
76     exit(EXIT_FAILURE);
77   }
78   return w;
79 }
80 static char *mpost_itoa (int i) {
81   char res[32] ;
82   unsigned idx = 30;
83   unsigned v = (unsigned)abs(i);
84   memset(res,0,32*sizeof(char));
85   while (v>=10) {
86     char d = (char)(v % 10);
87     v = v / 10;
88     res[idx--] = d;
89   }
90   res[idx--] = (char)v;
91   if (i<0) {
92       res[idx--] = '-';
93   }
94   return mpost_xstrdup(res+idx);
95 }
96
97
98 @ @c
99 static void mpost_run_editor (MP mp, char *fname, int fline) {
100   char *temp, *command, *edit_value;
101   char c;
102   boolean sdone, ddone;
103   sdone = ddone = false;
104   edit_value = kpse_var_value ("MPEDIT");
105   if (edit_value == NULL)
106     edit_value = getenv("EDITOR");
107   if (edit_value == NULL) {
108     fprintf (stderr,"call_edit: can't find a suitable MPEDIT or EDITOR variable\n");
109     exit(mp_status(mp));    
110   }
111   command = (string) mpost_xmalloc (strlen (edit_value) + strlen(fname) + 11 + 3);
112   temp = command;
113   while ((c = *edit_value++) != (char)0) {
114       if (c == '%')   {
115         switch (c = *edit_value++) {
116           case 'd':
117             if (ddone) {
118               fprintf (stderr,"call_edit: `%%d' appears twice in editor command\n");
119               exit(EXIT_FAILURE);  
120             } else {
121               char *s = mpost_itoa(fline);
122               if (s != NULL) {
123                 while (*s != '\0')
124                   *temp++ = *s++;
125                 free(s);
126               }
127               ddone = true;
128             }
129             break;
130           case 's':
131             if (sdone) {
132               fprintf (stderr,"call_edit: `%%s' appears twice in editor command\n");
133               exit(EXIT_FAILURE);
134             } else {
135               while (*fname != '\0')
136                 *temp++ = *fname++;
137               *temp++ = '.';
138               *temp++ = 'm';
139               *temp++ = 'p';
140               sdone = true;
141             }
142             break;
143           case '\0':
144             *temp++ = '%';
145             /* Back up to the null to force termination.  */
146             edit_value--;
147             break;
148           default:
149             *temp++ = '%';
150             *temp++ = c;
151             break;
152           }
153      } else {
154         *temp++ = c;
155      }
156    }
157   *temp = '\0';
158   if (system (command) != 0)
159     fprintf (stderr, "! Trouble executing `%s'.\n", command);
160   exit(EXIT_FAILURE);
161 }
162
163
164 @<Register the callback routines@>=
165 options->run_editor = mpost_run_editor;
166
167 @
168 @c 
169 static string normalize_quotes (const char *name, const char *mesg) {
170     boolean quoted = false;
171     boolean must_quote = (strchr(name, ' ') != NULL);
172     /* Leave room for quotes and NUL. */
173     string ret = (string)mpost_xmalloc(strlen(name)+3);
174     string p;
175     const_string q;
176     p = ret;
177     if (must_quote)
178         *p++ = '"';
179     for (q = name; *q != '\0'; q++) {
180         if (*q == '"')
181             quoted = !quoted;
182         else
183             *p++ = *q;
184     }
185     if (must_quote)
186         *p++ = '"';
187     *p = '\0';
188     if (quoted) {
189         fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
190         exit(EXIT_FAILURE);
191     }
192     return ret;
193 }
194
195 @ @c 
196 @= /*@@null@@*/ @> static char *makempx_find_file (MPX mpx, const char *nam, 
197                                                    const char *mode, int ftype) {
198   int fmt;
199   boolean req;
200   (void) mpx;
201   if (mode[0] != 'r') { 
202      return strdup(nam);
203   }
204   req = true; fmt = -1;
205   switch(ftype) {
206   case mpx_tfm_format:       fmt = kpse_tfm_format; break;
207   case mpx_vf_format:        fmt = kpse_vf_format; req = false; break;
208   case mpx_trfontmap_format: fmt = kpse_mpsupport_format; break;
209   case mpx_trcharadj_format: fmt = kpse_mpsupport_format; break;
210   case mpx_desc_format:      fmt = kpse_troff_font_format; break;
211   case mpx_fontdesc_format:  fmt = kpse_troff_font_format; break;
212   case mpx_specchar_format:  fmt = kpse_mpsupport_format; break;
213   }
214   if (fmt<0) return NULL;
215   return  kpse_find_file (nam, fmt, req);
216 }
217
218 @ Invoke makempx (or troffmpx) to make sure there is an up-to-date
219    .mpx file for a given .mp file.  (Original from John Hobby 3/14/90) 
220
221 @d default_args " --parse-first-line --interaction=nonstopmode"
222 @d TEX     "tex"
223 @d TROFF   "soelim | eqn -Tps -d$$ | troff -Tps"
224
225 @c
226 #ifndef MPXCOMMAND
227 #define MPXCOMMAND "makempx"
228 #endif
229 static int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) {
230   int ret;
231   char *cnf_cmd = kpse_var_value ("MPXCOMMAND");
232   
233   if (cnf_cmd != NULL && (strcmp (cnf_cmd, "0")==0)) {
234     /* If they turned off this feature, just return success.  */
235     ret = 0;
236
237   } else {
238     /* We will invoke something. Compile-time default if nothing else.  */
239     char *cmd;
240     char *tmp = normalize_quotes(mpname, "mpname");
241     char *qmpname = kpse_find_file (tmp,kpse_mp_format, true);
242     char *qmpxname = normalize_quotes(mpxname, "mpxname");
243     mpost_xfree(tmp);
244     if (cnf_cmd!=NULL) {
245       if (mp_troff_mode(mp)!=0)
246         cmd = concatn (cnf_cmd, " -troff ",
247                      qmpname, " ", qmpxname, NULL);
248       else if (mpost_tex_program!=NULL && *mpost_tex_program != '\0')
249         cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ",
250                      qmpname, " ", qmpxname, NULL);
251       else
252         cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
253   
254       /* Run it.  */
255       ret = system (cmd);
256       free (cmd);
257       mpost_xfree(qmpname);
258       mpost_xfree(qmpxname);
259     } else {
260       mpx_options * mpxopt;
261       char *s = NULL;
262       char *maincmd = NULL;
263       int mpxmode = mp_troff_mode(mp);
264       char *mpversion = mp_metapost_version () ;
265       mpxopt = mpost_xmalloc(sizeof(mpx_options));
266       if (mpost_tex_program != NULL && *mpost_tex_program != '\0') {
267         maincmd = mpost_xstrdup(mpost_tex_program);
268       } else {
269         if (mpxmode == mpx_tex_mode) {
270           s = kpse_var_value("TEX");
271           if (s==NULL) s = kpse_var_value("MPXMAINCMD");
272           if (s==NULL) s = mpost_xstrdup (TEX);
273           maincmd = (char *)mpost_xmalloc (strlen(s)+strlen(default_args)+1);
274           strcpy(maincmd,s);
275           strcat(maincmd,default_args);
276           free(s);
277         } else {
278           s = kpse_var_value("TROFF");
279           if (s==NULL) s = kpse_var_value("MPXMAINCMD");
280           if (s==NULL) s = mpost_xstrdup (TROFF);
281           maincmd = s;
282         }
283       }
284       mpxopt->mode = mpxmode;
285       mpxopt->cmd  = maincmd;
286       mpxopt->mptexpre = kpse_var_value("MPTEXPRE");
287       mpxopt->debug = debug;
288       mpxopt->mpname = qmpname;
289       mpxopt->mpxname = qmpxname;
290       mpxopt->find_file = makempx_find_file;
291       {
292         char *banner = "% Written by metapost version ";
293         mpxopt->banner = mpost_xmalloc(strlen(mpversion)+strlen(banner)+1);
294         strcpy (mpxopt->banner, banner);
295         strcat (mpxopt->banner, mpversion);
296       }
297       ret = mpx_makempx(mpxopt);
298       mpost_xfree(mpxopt->cmd);
299       mpost_xfree(mpxopt->mptexpre);
300       mpost_xfree(mpxopt->banner);
301       mpost_xfree(mpxopt->mpname);
302       mpost_xfree(mpxopt->mpxname);
303       mpost_xfree(mpxopt);
304       mpost_xfree(mpversion);
305     }
306   }
307
308   mpost_xfree (cnf_cmd);
309   return (int)(ret == 0);
310 }
311
312
313 @<Register the callback routines@>=
314 if (!nokpse)
315   options->run_make_mpx = mpost_run_make_mpx;
316
317
318 @ @c 
319 static int get_random_seed (void) {
320   int ret = 0;
321 #if defined (HAVE_GETTIMEOFDAY)
322   struct timeval tv;
323   gettimeofday(&tv, NULL);
324   ret = (tv.tv_usec + 1000000 * tv.tv_usec);
325 #elif defined (HAVE_FTIME)
326   struct timeb tb;
327   ftime(&tb);
328   ret = (tb.millitm + 1000 * tb.time);
329 #else
330   time_t clock = time ((time_t*)NULL);
331   struct tm *tmptr = localtime(&clock);
332   if (tmptr!=NULL)
333     ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
334 #endif
335   return ret;
336 }
337
338 @ @<Register the callback routines@>=
339 options->random_seed = get_random_seed();
340
341 @ @c 
342 static char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ftype)  {
343   size_t l ;
344   char *s;
345   (void)mp;
346   s = NULL;
347   if (fmode[0]=='r') {
348         if (ftype>=mp_filetype_text) {
349       s = kpse_find_file (fname, kpse_mp_format, 0); 
350     } else {
351     switch(ftype) {
352     case mp_filetype_program: 
353       l = strlen(fname);
354           if (l>3 && strcmp(fname+l-3,".mf")==0) {
355             s = kpse_find_file (fname, kpse_mf_format, 0); 
356       } else {
357             s = kpse_find_file (fname, kpse_mp_format, 0); 
358       }
359       break;
360     case mp_filetype_memfile: 
361       s = kpse_find_file (fname, kpse_mem_format, 0); 
362       break;
363     case mp_filetype_metrics: 
364       s = kpse_find_file (fname, kpse_tfm_format, 0); 
365       break;
366     case mp_filetype_fontmap: 
367       s = kpse_find_file (fname, kpse_fontmap_format, 0); 
368       break;
369     case mp_filetype_font: 
370       s = kpse_find_file (fname, kpse_type1_format, 0); 
371       break;
372     case mp_filetype_encoding: 
373       s = kpse_find_file (fname, kpse_enc_format, 0); 
374       break;
375     }
376     }
377   } else {
378     if (fname!=NULL)
379       s = mpost_xstrdup(fname); /* when writing */
380   }
381   return s;
382 }
383
384 @  @<Register the callback routines@>=
385 if (!nokpse)
386   options->find_file = mpost_find_file;
387
388 @ @c 
389 static void *mpost_open_file(MP mp, const char *fname, const char *fmode, int ftype)  {
390   char realmode[3];
391   char *s;
392   if (ftype==mp_filetype_terminal) {
393     return (fmode[0] == 'r' ? stdin : stdout);
394   } else if (ftype==mp_filetype_error) {
395     return stderr;
396   } else { 
397     s = mpost_find_file (mp, fname, fmode, ftype);
398     if (s!=NULL) {
399       void *ret = NULL;
400       realmode[0] = *fmode;
401           realmode[1] = 'b';
402           realmode[2] = '\0';
403       ret = (void *)fopen(s,realmode);
404       free(s);
405       return ret;
406     }
407   }
408   return NULL;
409 }
410
411 @  @<Register the callback routines@>=
412 if (!nokpse)
413   options->open_file = mpost_open_file;
414
415
416 @ At the moment, the command line is very simple.
417
418 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) || 
419        (strncmp(argv[a],"-" A, strlen(A)+1)==0))
420 @d option_arg(B) (optarg != NULL && strncmp(optarg,B, strlen(B))==0)
421
422
423 @<Read and set command line options@>=
424 {
425   char *mpost_optarg;
426   boolean ini_version_test = false;
427   while (++a<argc) {
428     mpost_optarg = strstr(argv[a],"=") ;
429     if (mpost_optarg!=NULL) {
430       mpost_optarg++;
431       if (*mpost_optarg == '\0')  mpost_optarg=NULL;
432     }
433     if (option_is("ini")) {
434       ini_version_test = true;
435     } else if (option_is("debug")) {
436       debug = 1;
437     } else if (option_is ("kpathsea-debug")) {
438       if (mpost_optarg!=NULL)
439         kpathsea_debug |= atoi (mpost_optarg);
440     } else if (option_is("mem")) {
441       if (mpost_optarg!=NULL) {
442         mpost_xfree(options->mem_name);
443         options->mem_name = mpost_xstrdup(mpost_optarg);
444         if (user_progname == NULL) 
445             user_progname = mpost_optarg;
446       }
447     } else if (option_is("jobname")) {
448       if (mpost_optarg!=NULL) {
449         mpost_xfree(options->job_name);
450         options->job_name = mpost_xstrdup(mpost_optarg);
451       }
452     } else if (option_is ("progname")) {
453       user_progname = mpost_optarg;
454     } else if (option_is("troff")) {
455       options->troff_mode = (int)true;
456     } else if (option_is ("tex")) {
457       mpost_tex_program = mpost_optarg;
458     } else if (option_is("interaction")) {
459       if (option_arg("batchmode")) {
460         options->interaction = mp_batch_mode;
461       } else if (option_arg("nonstopmode")) {
462         options->interaction = mp_nonstop_mode;
463       } else if (option_arg("scrollmode")) {
464         options->interaction = mp_scroll_mode;
465       } else if (option_arg("errorstopmode")) {
466         options->interaction = mp_error_stop_mode;
467       } else {
468         fprintf(stdout,"unknown option argument %s\n", argv[a]);
469       }
470     } else if (option_is("no-kpathsea")) {
471       nokpse=true;
472     } else if (option_is("help")) {
473       @<Show help and exit@>;
474     } else if (option_is("version")) {
475       @<Show version and exit@>;
476     } else if (option_is("")) {
477       continue; /* ignore unknown options */
478     } else {
479       break;
480     }
481   }
482   options->ini_version = (int)ini_version_test;
483 }
484
485
486 @<Show help...@>=
487 {
488 fprintf(stdout,
489 "\n"
490 "Usage: mpost [OPTION] [&MEMNAME] [MPNAME[.mp]] [COMMANDS]\n"
491 "\n"
492 "  Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
493 "  MPNAME.tfm), where NNN are the character numbers generated.\n"
494 "  Any remaining COMMANDS are processed as MetaPost input,\n"
495 "  after MPNAME is read.\n\n");
496 fprintf(stdout,
497 "  If no arguments or options are specified, prompt for input.\n"
498 "\n"
499 "  -ini                      be inimpost, for dumping mem files\n"
500 "  -interaction=STRING       set interaction mode (STRING=batchmode/nonstopmode/\n"
501 "                            scrollmode/errorstopmode)\n"
502 "  -jobname=STRING           set the job name to STRING\n"
503 "  -progname=STRING          set program (and mem) name to STRING\n"
504 "  -tex=TEXPROGRAM           use TEXPROGRAM for text labels\n");
505 fprintf(stdout,
506 "  -kpathsea-debug=NUMBER    set path searching debugging flags according to\n"
507 "                            the bits of NUMBER\n"
508 "  -mem=MEMNAME or &MEMNAME  use MEMNAME instead of program name or a %%& line\n"
509 "  -troff                    set prologues:=1 and assume TEXPROGRAM is really troff\n"
510 "  -help                     display this help and exit\n"
511 "  -version                  output version information and exit\n"
512 "\n"
513 "Email bug reports to mp-implementors@@tug.org.\n"
514 "\n");
515   exit(EXIT_SUCCESS);
516 }
517
518
519 @<Show version...@>=
520 {
521   char *s = mp_metapost_version();
522 fprintf(stdout, 
523 "\n"
524 "MetaPost %s\n"
525 "Copyright 2008 AT&T Bell Laboratories.\n"
526 "There is NO warranty.  Redistribution of this software is\n"
527 "covered by the terms of both the MetaPost copyright and\n"
528 "the Lesser GNU General Public License.\n"
529 "For more information about these matters, see the file\n"
530 "named COPYING and the MetaPost source.\n"
531 "Primary author of MetaPost: John Hobby.\n"
532 "Current maintainer of MetaPost: Taco Hoekwater.\n"
533 "\n", s);
534   mpost_xfree(s);
535   exit(EXIT_SUCCESS);
536 }
537
538 @ The final part of the command line, after option processing, is
539 stored in the \MP\ instance, this will be taken as the first line of
540 input.
541
542 @d command_line_size 256
543
544 @<Copy the rest of the command line@>=
545 {
546   mpost_xfree(options->command_line);
547   options->command_line = mpost_xmalloc(command_line_size);
548   strcpy(options->command_line,"");
549   if (a<argc) {
550     k=0;
551     for(;a<argc;a++) {
552       char *c = argv[a];
553       while (*c != '\0') {
554             if (k<(command_line_size-1)) {
555           options->command_line[k++] = *c;
556         }
557         c++;
558       }
559       options->command_line[k++] = ' ';
560     }
561         while (k>0) {
562       if (options->command_line[(k-1)] == ' ') 
563         k--; 
564       else 
565         break;
566     }
567     options->command_line[k] = '\0';
568   }
569 }
570
571 @ A simple function to get numerical |texmf.cnf| values
572 @c
573 static int setup_var (int def, const char *var_name, boolean nokpse) {
574   if (!nokpse) {
575     char * expansion = kpse_var_value (var_name);
576     if (expansion) {
577       int conf_val = atoi (expansion);
578       free (expansion);
579       if (conf_val > 0) {
580         return conf_val;
581       }
582     }
583   }
584   return def;
585 }
586
587 @ @<Set up the banner line@>=
588 {
589   char * mpversion = mp_metapost_version () ;
590   const char * banner = "This is MetaPost, version ";
591   const char * kpsebanner_start = " (";
592   const char * kpsebanner_stop = ")";
593   mpost_xfree(options->banner);
594   options->banner = mpost_xmalloc(strlen(banner)+
595                             strlen(mpversion)+
596                             strlen(kpsebanner_start)+
597                             strlen(kpathsea_version_string)+
598                             strlen(kpsebanner_stop)+1);
599   strcpy (options->banner, banner);
600   strcat (options->banner, mpversion);
601   strcat (options->banner, kpsebanner_start);
602   strcat (options->banner, kpathsea_version_string);
603   strcat (options->banner, kpsebanner_stop);
604   mpost_xfree(mpversion);
605 }
606
607 @ Precedence order is:
608
609 \item {} \.{-mem=MEMNAME} on the command line 
610 \item {} \.{\&MEMNAME} on the command line 
611 \item {} \.{\%\&MEM} as first line inside input file
612 \item {} \.{argv[0]} if all else fails
613
614 @<Discover the mem name@>=
615 {
616   char *m = NULL; /* head of potential |mem_name| */
617   char *n = NULL; /* a moving pointer */
618   if (options->command_line != NULL && *(options->command_line) == '&'){
619     m = mpost_xstrdup(options->command_line+1);
620     n = m;
621     while (*n != '\0' && *n != ' ') n++;
622     while (*n == ' ') n++;
623     if (*n != '\0') { /* more command line to follow */
624       char *s = mpost_xstrdup(n);
625       if (n>m) n--;
626       while (*n == ' ' && n>m) n--;
627       n++;
628       *n ='\0'; /* this terminates |m| */
629       mpost_xfree(options->command_line);
630       options->command_line = s;
631     } else { /* only \.{\&MEMNAME} on command line */
632       if (n>m) n--;
633       while (*n == ' ' && n>m) n--;
634       n++;
635       *n ='\0'; /* this terminates |m| */
636       mpost_xfree(options->command_line);
637     }
638     if ( options->mem_name == NULL && *m != '\0') {
639       mpost_xfree(options->mem_name); /* for lint only */
640       options->mem_name = m;
641     } else {
642       mpost_xfree(m);
643     }
644   }
645 }
646 if ( options->mem_name == NULL ) {
647   char *m = NULL; /* head of potential |job_name| */
648   char *n = NULL; /* a moving pointer */
649   if (options->command_line != NULL && *(options->command_line) != '\\'){
650     m = mpost_xstrdup(options->command_line);
651     n = m;
652     while (*n != '\0' && *n != ' ') n++;
653     if (n>m) {
654       char *fname;
655       *n='\0';
656       fname = m;
657       if (!nokpse)
658         fname = kpse_find_file(m,kpse_mp_format,true);
659       if (fname == NULL) {
660         mpost_xfree(m);
661       } else {
662         FILE *F = fopen(fname,"r");
663         if (F==NULL) {
664           mpost_xfree(fname);
665         } else {
666           char *line = mpost_xmalloc(256);
667           if (fgets(line,255,F) == NULL) {
668             (void)fclose(F);
669             mpost_xfree(fname);
670             mpost_xfree(line);
671           } else {
672             (void)fclose(F);
673             while (*line != '\0' && *line == ' ') line++;
674             if (*line == '%') {
675               n = m = line+1;
676               while (*n != '\0' && *n == ' ') n++;
677               if (*n == '&') {
678                 m = n+1;
679                 while (*n != '\0' && *n != ' ') n++;
680                 if (n>(m+1)) {
681                   n--;
682                   while (*n == ' ' && n>m) n--;
683                   *n ='\0'; /* this terminates |m| */
684                   options->mem_name = mpost_xstrdup(m);
685                   mpost_xfree(fname);
686                 } else {
687                   mpost_xfree(fname);
688                   mpost_xfree(line);    
689                 }
690               }
691             }
692           }
693         }
694       }
695     } else {
696       mpost_xfree(m);
697     }
698   }
699 }
700 if ( options->mem_name == NULL )
701   if (kpse_program_name!=NULL)
702     options->mem_name = mpost_xstrdup(kpse_program_name);
703
704
705 @ Now this is really it: \MP\ starts and ends here.
706
707 @c 
708 int main (int argc, char **argv) { /* |start_here| */
709   int k; /* index into buffer */
710   int history; /* the exit status */
711   MP mp; /* a metapost instance */
712   struct MP_options * options; /* instance options */
713   int a=0; /* argc counter */
714   boolean nokpse = false; /* switch to {\it not} enable kpse */
715   char *user_progname = NULL; /* If the user overrides argv[0] with -progname.  */
716   options = mp_options();
717   options->ini_version       = (int)false;
718   options->print_found_names = (int)true;
719   @<Read and set command line options@>;
720   @= /*@@-nullpass@@*/ @> 
721   if (!nokpse)
722     kpse_set_program_name("mpost", user_progname);  
723   @= /*@@=nullpass@@*/ @> 
724   if(putenv((char *)"engine=metapost"))
725     fprintf(stdout,"warning: could not set up $engine\n");
726   options->main_memory       = setup_var (50000,"main_memory",nokpse);
727   options->hash_size         = (unsigned)setup_var (16384,"hash_size",nokpse);
728   options->max_in_open       = setup_var (25,"max_in_open",nokpse);
729   options->param_size        = setup_var (1500,"param_size",nokpse);
730   options->error_line        = setup_var (79,"error_line",nokpse);
731   options->half_error_line   = setup_var (50,"half_error_line",nokpse);
732   options->max_print_line    = setup_var (100,"max_print_line",nokpse);
733   @<Set up the banner line@>;
734   @<Copy the rest of the command line@>;
735   if (options->ini_version!=(int)true) {
736     @<Discover the mem name@>;
737   }
738   @<Register the callback routines@>;
739   mp = mp_initialize(options);
740   mpost_xfree(options->command_line);
741   mpost_xfree(options->mem_name);
742   mpost_xfree(options->job_name);
743   mpost_xfree(options->banner);
744   free(options);
745   if (mp==NULL)
746         exit(EXIT_FAILURE);
747   history = mp_status(mp);
748   if (history!=0)
749         exit(history);
750   history = mp_run(mp);
751   (void)mp_finish(mp);
752   exit(history);
753 }
754