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");
72 static char *mpost_itoa (int i) {
75 unsigned v = (unsigned)abs(i);
76 memset(res,0,32*sizeof(char));
78 char d = (char)(v % 10);
86 return mpost_xstrdup(res+idx);
91 static void mpost_run_editor (MP mp, char *fname, int fline) {
92 char *temp, *command, *edit_value;
96 edit_value = kpse_var_value ("MPEDIT");
97 if (edit_value == NULL)
98 edit_value = getenv("EDITOR");
99 if (edit_value == NULL) {
100 fprintf (stderr,"call_edit: can't find a suitable MPEDIT or EDITOR variable\n");
103 command = (string) mpost_xmalloc (strlen (edit_value) + strlen(fname) + 11 + 3);
105 while ((c = *edit_value++) != (char)0) {
107 switch (c = *edit_value++) {
110 fprintf (stderr,"call_edit: `%%d' appears twice in editor command\n");
113 char *s = mpost_itoa(fline);
121 fprintf (stderr,"call_edit: `%%s' appears twice in editor command\n");
124 while (*fname != '\0')
134 /* Back up to the null to force termination. */
147 if (system (command) != 0)
148 fprintf (stderr, "! Trouble executing `%s'.\n", command);
153 @<Register the callback routines@>=
154 options->run_editor = mpost_run_editor;
158 static string normalize_quotes (const char *name, const char *mesg) {
160 int must_quote = (strchr(name, ' ') != NULL);
161 /* Leave room for quotes and NUL. */
162 string ret = (string)mpost_xmalloc(strlen(name)+3);
168 for (q = name; *q != '\0'; q++) {
178 fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
185 static char *makempx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
188 if (mode[0] != 'r') {
193 case mpx_tfm_format: fmt = kpse_tfm_format; break;
194 case mpx_vf_format: fmt = kpse_vf_format; req = 0; break;
195 case mpx_trfontmap_format: fmt = kpse_mpsupport_format; break;
196 case mpx_trcharadj_format: fmt = kpse_mpsupport_format; break;
197 case mpx_desc_format: fmt = kpse_troff_font_format; break;
198 case mpx_fontdesc_format: fmt = kpse_troff_font_format; break;
199 case mpx_specchar_format: fmt = kpse_mpsupport_format; break;
201 if (fmt<0) return NULL;
202 return kpse_find_file (nam, fmt, req);
205 @ Invoke makempx (or troffmpx) to make sure there is an up-to-date
206 .mpx file for a given .mp file. (Original from John Hobby 3/14/90)
208 @d default_args " --parse-first-line --interaction=nonstopmode"
210 @d TROFF "soelim | eqn -Tps -d$$ | troff -Tps"
214 #define MPXCOMMAND "makempx"
216 static int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) {
218 string cnf_cmd = kpse_var_value ("MPXCOMMAND");
220 if (cnf_cmd != NULL && (strcmp (cnf_cmd, "0")==0)) {
221 /* If they turned off this feature, just return success. */
225 /* We will invoke something. Compile-time default if nothing else. */
227 string qmpname = kpse_find_file (normalize_quotes(mpname, "mpname"),kpse_mp_format, 1);
228 string qmpxname = normalize_quotes(mpxname, "mpxname");
230 if (mp_troff_mode(mp))
231 cmd = concatn (cnf_cmd, " -troff ",
232 qmpname, " ", qmpxname, NULL);
233 else if (mpost_tex_program!=NULL && *mpost_tex_program != '\0')
234 cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ",
235 qmpname, " ", qmpxname, NULL);
237 cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
243 makempx_options * mpxopt;
245 char *maincmd = NULL;
246 int mpxmode = mp_troff_mode(mp);
247 const char *mpversion = mp_metapost_version () ;
248 mpxopt = mpost_xmalloc(sizeof(makempx_options));
249 if (mpost_tex_program && *mpost_tex_program) {
250 maincmd = mpost_xstrdup(mpost_tex_program);
252 if (mpxmode == mpx_tex_mode) {
253 s = kpse_var_value("TEX");
254 if (!s) s = kpse_var_value("MPXMAINCMD");
255 if (!s) s = mpost_xstrdup (TEX);
256 maincmd = (char *)mpost_xmalloc (strlen(s)+strlen(default_args)+1);
258 strcat(maincmd,default_args);
261 s = kpse_var_value("TROFF");
262 if (!s) s = kpse_var_value("MPXMAINCMD");
263 if (!s) s = mpost_xstrdup (TROFF);
267 mpxopt->mode = mpxmode;
268 mpxopt->cmd = maincmd;
269 mpxopt->mptexpre = kpse_var_value("MPTEXPRE");
270 mpxopt->mpname = qmpname;
271 mpxopt->mpxname = qmpxname;
272 mpxopt->debug = debug;
273 mpxopt->find_file = makempx_find_file;
275 char *banner = "% Written by metapost version ";
276 mpxopt->banner = mpost_xmalloc(strlen(mpversion)+strlen(banner)+1);
277 strcpy (mpxopt->banner, banner);
278 strcat (mpxopt->banner, mpversion);
280 ret = mp_makempx(mpxopt);
282 free(mpxopt->mptexpre);
294 @<Register the callback routines@>=
296 options->run_make_mpx = mpost_run_make_mpx;
300 static int get_random_seed (void) {
302 #if defined (HAVE_GETTIMEOFDAY)
304 gettimeofday(&tv, NULL);
305 ret = (tv.tv_usec + 1000000 * tv.tv_usec);
306 #elif defined (HAVE_FTIME)
309 ret = (tb.millitm + 1000 * tb.time);
311 time_t clock = time ((time_t*)NULL);
312 struct tm *tmptr = localtime(&clock);
313 ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
318 @ @<Register the callback routines@>=
319 options->random_seed = get_random_seed();
322 static char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ftype) {
327 if (ftype>=mp_filetype_text) {
328 s = kpse_find_file (fname, kpse_mp_format, 0);
331 case mp_filetype_program:
333 if (l>3 && strcmp(fname+l-3,".mf")==0) {
334 s = kpse_find_file (fname, kpse_mf_format, 0);
336 s = kpse_find_file (fname, kpse_mp_format, 0);
339 case mp_filetype_memfile:
340 s = kpse_find_file (fname, kpse_mem_format, 0);
342 case mp_filetype_metrics:
343 s = kpse_find_file (fname, kpse_tfm_format, 0);
345 case mp_filetype_fontmap:
346 s = kpse_find_file (fname, kpse_fontmap_format, 0);
348 case mp_filetype_font:
349 s = kpse_find_file (fname, kpse_type1_format, 0);
351 case mp_filetype_encoding:
352 s = kpse_find_file (fname, kpse_enc_format, 0);
357 s = mpost_xstrdup(fname); /* when writing */
362 @ @<Register the callback routines@>=
364 options->find_file = mpost_find_file;
367 static void *mpost_open_file(MP mp, const char *fname, const char *fmode, int ftype) {
370 if (ftype==mp_filetype_terminal) {
371 return (fmode[0] == 'r' ? stdin : stdout);
372 } else if (ftype==mp_filetype_error) {
375 s = mpost_find_file (mp, fname, fmode, ftype);
378 realmode[0] = *fmode;
381 ret = fopen(s,realmode);
389 @ @<Register the callback routines@>=
391 options->open_file = mpost_open_file;
394 @ At the moment, the command line is very simple.
396 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) ||
397 (strncmp(argv[a],"-" A, strlen(A)+1)==0))
398 @d option_arg(B) (optarg && strncmp(optarg,B, strlen(B))==0)
401 @<Read and set command line options@>=
404 boolean ini_version_test = false;
406 optarg = strstr(argv[a],"=") ;
409 if (!*optarg) optarg=NULL;
411 if (option_is("ini")) {
412 ini_version_test = true;
413 } else if (option_is("debug")) {
415 } else if (option_is ("kpathsea-debug")) {
416 kpathsea_debug |= atoi (optarg);
417 } else if (option_is("mem")) {
418 options->mem_name = mpost_xstrdup(optarg);
420 user_progname = optarg;
421 } else if (option_is("jobname")) {
422 options->job_name = mpost_xstrdup(optarg);
423 } else if (option_is ("progname")) {
424 user_progname = optarg;
425 } else if (option_is("troff")) {
426 options->troff_mode = true;
427 } else if (option_is ("tex")) {
428 mpost_tex_program = optarg;
429 } else if (option_is("interaction")) {
430 if (option_arg("batchmode")) {
431 options->interaction = mp_batch_mode;
432 } else if (option_arg("nonstopmode")) {
433 options->interaction = mp_nonstop_mode;
434 } else if (option_arg("scrollmode")) {
435 options->interaction = mp_scroll_mode;
436 } else if (option_arg("errorstopmode")) {
437 options->interaction = mp_error_stop_mode;
439 fprintf(stdout,"unknown option argument %s\n", argv[a]);
441 } else if (option_is("no-kpathsea")) {
443 } else if (option_is("help")) {
444 @<Show help and exit@>;
445 } else if (option_is("version")) {
446 @<Show version and exit@>;
447 } else if (option_is("")) {
448 continue; /* ignore unknown options */
453 options->ini_version = ini_version_test;
461 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
463 " Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
464 " MPNAME.tfm), where NNN are the character numbers generated.\n"
465 " Any remaining COMMANDS are processed as MetaPost input,\n"
466 " after MPNAME is read.\n\n");
468 " If no arguments or options are specified, prompt for input.\n"
470 " -ini be inimpost, for dumping mems\n"
471 " -interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n"
472 " scrollmode/errorstopmode)\n"
473 " -jobname=STRING set the job name to STRING\n"
474 " -progname=STRING set program (and mem) name to STRING\n");
476 " -tex=TEXPROGRAM use TEXPROGRAM for text labels\n"
477 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
478 " the bits of NUMBER\n"
479 " -mem=MEMNAME use MEMNAME instead of program name or a %%& line\n"
480 " -troff set the prologues variable and assume TEXPROGRAM is really troff\n"
481 " -help display this help and exit\n"
482 " -version output version information and exit\n"
484 "Email bug reports to mp-implementors@@tug.org.\n"
495 "Copyright 2008 AT&T Bell Laboratories.\n"
496 "There is NO warranty. Redistribution of this software is\n"
497 "covered by the terms of both the MetaPost copyright and\n"
498 "the Lesser GNU General Public License.\n"
499 "For more information about these matters, see the file\n"
500 "named COPYING and the MetaPost source.\n"
501 "Primary author of MetaPost: John Hobby.\n"
502 "Current maintainer of MetaPost: Taco Hoekwater.\n"
503 "\n", mp_metapost_version());
507 @ The final part of the command line, after option processing, is
508 stored in the \MP\ instance, this will be taken as the first line of
511 @d command_line_size 256
513 @<Copy the rest of the command line@>=
515 options->command_line = mpost_xmalloc(command_line_size);
516 strcpy(options->command_line,"");
522 if (k<(command_line_size-1)) {
523 options->command_line[k++] = *c;
527 options->command_line[k++] = ' ';
530 if (options->command_line[(k-1)] == ' ')
535 options->command_line[k] = '\0';
539 @ A simple function to get numerical |texmf.cnf| values
541 static int setup_var (int def, const char *var_name, int nokpse) {
543 char * expansion = kpse_var_value (var_name);
545 int conf_val = atoi (expansion);
555 @ @<Set up the banner line@>=
557 const char *mpversion = mp_metapost_version () ;
558 const char * banner = "This is MetaPost, version ";
559 const char * kpsebanner_start = " (";
560 const char * kpsebanner_stop = ")";
561 options->banner = mpost_xmalloc(strlen(banner)+
563 strlen(kpsebanner_start)+
564 strlen(kpathsea_version_string)+
565 strlen(kpsebanner_stop)+1);
566 strcpy (options->banner, banner);
567 strcat (options->banner, mpversion);
568 strcat (options->banner, kpsebanner_start);
569 strcat (options->banner, kpathsea_version_string);
570 strcat (options->banner, kpsebanner_stop);
574 @ Now this is really it: \MP\ starts and ends here.
576 @d xfree(A) if (A!=NULL) free(A)
579 int main (int argc, char **argv) { /* |start_here| */
580 int k; /* index into buffer */
581 int history; /* the exit status */
582 MP mp; /* a metapost instance */
583 struct MP_options * options; /* instance options */
584 int a=0; /* argc counter */
585 int nokpse = 0; /* switch to {\it not} enable kpse */
586 char *user_progname = NULL; /* If the user overrides argv[0] with -progname. */
587 options = mp_options();
588 options->ini_version = false;
589 options->print_found_names = true;
590 @<Read and set command line options@>;
592 kpse_set_program_name("mpost",user_progname);
593 if(putenv((char *)"engine=metapost"))
594 fprintf(stdout,"warning: could not set up $engine\n");
595 options->main_memory = setup_var (50000,"main_memory",nokpse);
596 options->hash_size = (unsigned)setup_var (16384,"hash_size",nokpse);
597 options->max_in_open = setup_var (25,"max_in_open",nokpse);
598 options->param_size = setup_var (1500,"param_size",nokpse);
599 options->error_line = setup_var (79,"error_line",nokpse);
600 options->half_error_line = setup_var (50,"half_error_line",nokpse);
601 options->max_print_line = setup_var (100,"max_print_line",nokpse);
602 @<Set up the banner line@>;
603 @<Copy the rest of the command line@>;
604 @<Register the callback routines@>;
605 mp = mp_initialize(options);
606 xfree(options->command_line);
607 xfree(options->mem_name);
608 xfree(options->job_name);
609 xfree(options->banner);
613 history = mp_status(mp);
616 history = mp_run(mp);