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