3 % Copyright 2008 Taco Hoekwater.
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.
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.
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/>.
18 \font\tenlogo=logo10 % font used for the METAFONT logo
19 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
21 \def\title{MetaPost executable}
25 @* \[1] Metapost executable.
27 Now that all of \MP\ is a library, a separate program is needed to
28 have our customary command-line interface.
30 @ First, here are the C includes. |avl.h| is needed because of an
31 |avl_allocator| that is defined in |mplib.h|
46 #include <kpathsea/kpathsea.h>
47 extern char *kpathsea_version_string;
48 static const char *mpost_tex_program = "";
49 static int debug = 0; /* debugging for makempx */
51 @ Allocating a bit of memory, with error detection:
54 static void *mpost_xmalloc (size_t bytes) {
55 void *w = malloc (bytes);
57 fprintf(stderr,"Out of memory!\n");
62 static char *mpost_xstrdup(const char *s) {
64 if (s==NULL) return NULL;
67 fprintf(stderr,"Out of memory!\n");
75 void mpost_run_editor (MP mp, char *fname, int fline) {
76 char *temp, *command, *edit_value;
80 edit_value = kpse_var_value ("MPEDIT");
81 if (edit_value == NULL)
82 edit_value = getenv("EDITOR");
83 if (edit_value == NULL) {
84 fprintf (stderr,"call_edit: can't find a suitable MPEDIT or EDITOR variable\n");
87 command = (string) mpost_xmalloc (strlen (edit_value) + strlen(fname) + 11 + 3);
89 while ((c = *edit_value++) != (char)0) {
91 switch (c = *edit_value++) {
94 fprintf (stderr,"call_edit: `%%d' appears twice in editor command\n");
97 sprintf (temp, "%d", fline);
104 fprintf (stderr,"call_edit: `%%s' appears twice in editor command\n");
107 while (*fname != '\0')
116 /* Back up to the null to force termination. */
129 if (system (command) != 0)
130 fprintf (stderr, "! Trouble executing `%s'.\n", command);
135 @<Register the callback routines@>=
136 options->run_editor = mpost_run_editor;
140 string normalize_quotes (const char *name, const char *mesg) {
142 int must_quote = (strchr(name, ' ') != NULL);
143 /* Leave room for quotes and NUL. */
144 string ret = (string)mpost_xmalloc(strlen(name)+3);
150 for (q = name; *q != '\0'; q++) {
160 fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
167 static char *makempx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
170 if (mode[0] != 'r') {
175 case mpx_tfm_format: fmt = kpse_tfm_format; break;
176 case mpx_vf_format: fmt = kpse_vf_format; req = 0; break;
177 case mpx_trfontmap_format: fmt = kpse_mpsupport_format; break;
178 case mpx_trcharadj_format: fmt = kpse_mpsupport_format; break;
179 case mpx_desc_format: fmt = kpse_troff_font_format; break;
180 case mpx_fontdesc_format: fmt = kpse_troff_font_format; break;
181 case mpx_specchar_format: fmt = kpse_mpsupport_format; break;
183 if (fmt<0) return NULL;
184 return kpse_find_file (nam, fmt, req);
187 @ Invoke makempx (or troffmpx) to make sure there is an up-to-date
188 .mpx file for a given .mp file. (Original from John Hobby 3/14/90)
190 @d default_args " --parse-first-line --interaction=nonstopmode"
192 @d TROFF "soelim | eqn -Tps -d$$ | troff -Tps"
196 #define MPXCOMMAND "makempx"
198 int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) {
200 string cnf_cmd = kpse_var_value ("MPXCOMMAND");
202 if (cnf_cmd != NULL && (strcmp (cnf_cmd, "0")==0)) {
203 /* If they turned off this feature, just return success. */
207 /* We will invoke something. Compile-time default if nothing else. */
209 string qmpname = kpse_find_file (normalize_quotes(mpname, "mpname"),kpse_mp_format, 1);
210 string qmpxname = normalize_quotes(mpxname, "mpxname");
212 if (mp_troff_mode(mp))
213 cmd = concatn (cnf_cmd, " -troff ",
214 qmpname, " ", qmpxname, NULL);
215 else if (mpost_tex_program!=NULL && *mpost_tex_program != '\0')
216 cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ",
217 qmpname, " ", qmpxname, NULL);
219 cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
225 makempx_options * mpxopt;
227 char *maincmd = NULL;
228 int mpxmode = mp_troff_mode(mp);
229 const char *mpversion = mp_metapost_version () ;
230 mpxopt = mpost_xmalloc(sizeof(makempx_options));
231 if (mpost_tex_program && *mpost_tex_program) {
232 maincmd = mpost_xstrdup(mpost_tex_program);
234 if (mpxmode == mpx_tex_mode) {
235 s = kpse_var_value("TEX");
236 if (!s) s = kpse_var_value("MPXMAINCMD");
237 if (!s) s = mpost_xstrdup (TEX);
238 maincmd = (char *)mpost_xmalloc (strlen(s)+strlen(default_args)+1);
240 strcat(maincmd,default_args);
243 s = kpse_var_value("TROFF");
244 if (!s) s = kpse_var_value("MPXMAINCMD");
245 if (!s) s = mpost_xstrdup (TROFF);
249 mpxopt->mode = mpxmode;
250 mpxopt->cmd = maincmd;
251 mpxopt->mptexpre = kpse_var_value("MPTEXPRE");
252 mpxopt->mpname = qmpname;
253 mpxopt->mpxname = qmpxname;
254 mpxopt->debug = debug;
255 mpxopt->find_file = makempx_find_file;
257 char *banner = "% Written by metapost version ";
258 mpxopt->banner = mpost_xmalloc(strlen(mpversion)+strlen(banner)+1);
259 strcpy (mpxopt->banner, banner);
260 strcat (mpxopt->banner, mpversion);
262 ret = mp_makempx(mpxopt);
264 free(mpxopt->mptexpre);
276 @<Register the callback routines@>=
278 options->run_make_mpx = mpost_run_make_mpx;
282 static int get_random_seed (void) {
284 #if defined (HAVE_GETTIMEOFDAY)
286 gettimeofday(&tv, NULL);
287 ret = (tv.tv_usec + 1000000 * tv.tv_usec);
288 #elif defined (HAVE_FTIME)
291 ret = (tb.millitm + 1000 * tb.time);
293 time_t clock = time ((time_t*)NULL);
294 struct tm *tmptr = localtime(&clock);
295 ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
300 @ @<Register the callback routines@>=
301 options->random_seed = get_random_seed();
304 char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ftype) {
309 if (ftype>=mp_filetype_text) {
310 s = kpse_find_file (fname, kpse_mp_format, 0);
313 case mp_filetype_program:
315 if (l>3 && strcmp(fname+l-3,".mf")==0) {
316 s = kpse_find_file (fname, kpse_mf_format, 0);
318 s = kpse_find_file (fname, kpse_mp_format, 0);
321 case mp_filetype_memfile:
322 s = kpse_find_file (fname, kpse_mem_format, 0);
324 case mp_filetype_metrics:
325 s = kpse_find_file (fname, kpse_tfm_format, 0);
327 case mp_filetype_fontmap:
328 s = kpse_find_file (fname, kpse_fontmap_format, 0);
330 case mp_filetype_font:
331 s = kpse_find_file (fname, kpse_type1_format, 0);
333 case mp_filetype_encoding:
334 s = kpse_find_file (fname, kpse_enc_format, 0);
339 s = mpost_xstrdup(fname); /* when writing */
344 @ @<Register the callback routines@>=
346 options->find_file = mpost_find_file;
349 void *mpost_open_file(MP mp, const char *fname, const char *fmode, int ftype) {
352 if (ftype==mp_filetype_terminal) {
353 return (fmode[0] == 'r' ? stdin : stdout);
354 } else if (ftype==mp_filetype_error) {
357 s = mpost_find_file (mp, fname, fmode, ftype);
360 realmode[0] = *fmode;
363 ret = fopen(s,realmode);
371 @ @<Register the callback routines@>=
373 options->open_file = mpost_open_file;
376 @ At the moment, the command line is very simple.
378 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) ||
379 (strncmp(argv[a],"-" A, strlen(A)+1)==0))
380 @d option_arg(B) (optarg && strncmp(optarg,B, strlen(B))==0)
383 @<Read and set command line options@>=
386 boolean ini_version_test = false;
388 optarg = strstr(argv[a],"=") ;
391 if (!*optarg) optarg=NULL;
393 if (option_is("ini")) {
394 ini_version_test = true;
395 } else if (option_is("debug")) {
397 } else if (option_is ("kpathsea-debug")) {
398 kpathsea_debug |= atoi (optarg);
399 } else if (option_is("mem")) {
400 options->mem_name = mpost_xstrdup(optarg);
402 user_progname = optarg;
403 } else if (option_is("jobname")) {
404 options->job_name = mpost_xstrdup(optarg);
405 } else if (option_is ("progname")) {
406 user_progname = optarg;
407 } else if (option_is("troff")) {
408 options->troff_mode = true;
409 } else if (option_is ("tex")) {
410 mpost_tex_program = optarg;
411 } else if (option_is("interaction")) {
412 if (option_arg("batchmode")) {
413 options->interaction = mp_batch_mode;
414 } else if (option_arg("nonstopmode")) {
415 options->interaction = mp_nonstop_mode;
416 } else if (option_arg("scrollmode")) {
417 options->interaction = mp_scroll_mode;
418 } else if (option_arg("errorstopmode")) {
419 options->interaction = mp_error_stop_mode;
421 fprintf(stdout,"unknown option argument %s\n", argv[a]);
423 } else if (option_is("no-kpathsea")) {
425 } else if (option_is("help")) {
426 @<Show help and exit@>;
427 } else if (option_is("version")) {
428 @<Show version and exit@>;
429 } else if (option_is("")) {
430 continue; /* ignore unknown options */
435 options->ini_version = ini_version_test;
443 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
445 " Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
446 " MPNAME.tfm), where NNN are the character numbers generated.\n"
447 " Any remaining COMMANDS are processed as MetaPost input,\n"
448 " after MPNAME is read.\n\n");
450 " If no arguments or options are specified, prompt for input.\n"
452 " -ini be inimpost, for dumping mems\n"
453 " -interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n"
454 " scrollmode/errorstopmode)\n"
455 " -jobname=STRING set the job name to STRING\n"
456 " -progname=STRING set program (and mem) name to STRING\n");
458 " -tex=TEXPROGRAM use TEXPROGRAM for text labels\n"
459 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
460 " the bits of NUMBER\n"
461 " -mem=MEMNAME use MEMNAME instead of program name or a %%& line\n"
462 " -troff set the prologues variable, use `makempx -troff'\n"
463 " -help display this help and exit\n"
464 " -version output version information and exit\n"
466 "Email bug reports to mp-implementors@@tug.org.\n"
477 "Copyright 2008 AT&T Bell Laboratories.\n"
478 "There is NO warranty. Redistribution of this software is\n"
479 "covered by the terms of both the MetaPost copyright and\n"
480 "the Lesser GNU General Public License.\n"
481 "For more information about these matters, see the file\n"
482 "named COPYING and the MetaPost source.\n"
483 "Primary author of MetaPost: John Hobby.\n"
484 "Current maintainer of MetaPost: Taco Hoekwater.\n"
485 "\n", mp_metapost_version());
489 @ The final part of the command line, after option processing, is
490 stored in the \MP\ instance, this will be taken as the first line of
493 @d command_line_size 256
495 @<Copy the rest of the command line@>=
497 options->command_line = mpost_xmalloc(command_line_size);
498 strcpy(options->command_line,"");
504 if (k<(command_line_size-1)) {
505 options->command_line[k++] = *c;
509 options->command_line[k++] = ' ';
512 if (options->command_line[(k-1)] == ' ')
517 options->command_line[k] = '\0';
521 @ A simple function to get numerical |texmf.cnf| values
523 int setup_var (int def, const char *var_name, int nokpse) {
525 char * expansion = kpse_var_value (var_name);
527 int conf_val = atoi (expansion);
537 @ @<Set up the banner line@>=
539 const char *mpversion = mp_metapost_version () ;
540 const char * banner = "This is MetaPost, version ";
541 const char * kpsebanner_start = " (";
542 const char * kpsebanner_stop = ")";
543 options->banner = mpost_xmalloc(strlen(banner)+
545 strlen(kpsebanner_start)+
546 strlen(kpathsea_version_string)+
547 strlen(kpsebanner_stop)+1);
548 strcpy (options->banner, banner);
549 strcat (options->banner, mpversion);
550 strcat (options->banner, kpsebanner_start);
551 strcat (options->banner, kpathsea_version_string);
552 strcat (options->banner, kpsebanner_stop);
556 @ Now this is really it: \MP\ starts and ends here.
558 @d xfree(A) if (A!=NULL) free(A)
561 int main (int argc, char **argv) { /* |start_here| */
562 int k; /* index into buffer */
563 int history; /* the exit status */
564 MP mp; /* a metapost instance */
565 struct MP_options * options; /* instance options */
566 int a=0; /* argc counter */
567 int nokpse = 0; /* switch to {\it not} enable kpse */
568 char *user_progname = NULL; /* If the user overrides argv[0] with -progname. */
569 options = mp_options();
570 options->ini_version = false;
571 options->print_found_names = true;
572 @<Read and set command line options@>;
574 kpse_set_program_name("mpost",user_progname);
575 if(putenv((char *)"engine=metapost"))
576 fprintf(stdout,"warning: could not set up $engine\n");
577 options->main_memory = setup_var (50000,"main_memory",nokpse);
578 options->hash_size = setup_var (16384,"hash_size",nokpse);
579 options->max_in_open = setup_var (25,"max_in_open",nokpse);
580 options->param_size = setup_var (1500,"param_size",nokpse);
581 options->error_line = setup_var (79,"error_line",nokpse);
582 options->half_error_line = setup_var (50,"half_error_line",nokpse);
583 options->max_print_line = setup_var (100,"max_print_line",nokpse);
584 @<Set up the banner line@>;
585 @<Copy the rest of the command line@>;
586 @<Register the callback routines@>;
587 mp = mp_initialize(options);
588 xfree(options->command_line);
589 xfree(options->mem_name);
590 xfree(options->job_name);
591 xfree(options->banner);
595 history = mp_status(mp);
598 history = mp_run(mp);