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 static const char *mpost_tex_program = "";
48 static int debug = 0; /* debugging for makempx */
50 @ Allocating a bit of memory, with error detection:
53 void *xmalloc (size_t bytes) {
54 void *w = malloc (bytes);
56 fprintf(stderr,"Out of memory!\n");
61 char *xstrdup(const char *s) {
63 if (s==NULL) return NULL;
66 fprintf(stderr,"Out of memory!\n");
74 void mpost_run_editor (MP mp, char *fname, int fline) {
75 char *temp, *command, *edit_value;
79 edit_value = kpse_var_value ("MPEDIT");
80 if (edit_value == NULL)
81 edit_value = getenv("EDITOR");
82 if (edit_value == NULL) {
83 fprintf (stderr,"call_edit: can't find a suitable MPEDIT or EDITOR variable\n");
86 command = (string) xmalloc (strlen (edit_value) + strlen(fname) + 11 + 3);
88 while ((c = *edit_value++) != 0) {
90 switch (c = *edit_value++) {
93 fprintf (stderr,"call_edit: `%%d' appears twice in editor command\n");
96 sprintf (temp, "%d", fline);
103 fprintf (stderr,"call_edit: `%%s' appears twice in editor command\n");
115 /* Back up to the null to force termination. */
128 if (system (command) != 0)
129 fprintf (stderr, "! Trouble executing `%s'.\n", command);
134 @<Register the callback routines@>=
135 options->run_editor = mpost_run_editor;
139 string normalize_quotes (const char *name, const char *mesg) {
141 int must_quote = (strchr(name, ' ') != NULL);
142 /* Leave room for quotes and NUL. */
143 string ret = (string)xmalloc(strlen(name)+3);
149 for (q = name; *q; q++) {
159 fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
166 static char *makempx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
169 if (mode[0] != 'r') {
174 case mpx_tfm_format: format = kpse_tfm_format; break;
175 case mpx_vf_format: format = kpse_vf_format; req = 0; break;
176 case mpx_trfontmap_format: format = kpse_mpsupport_format; break;
177 case mpx_trcharadj_format: format = kpse_mpsupport_format; break;
178 case mpx_desc_format: format = kpse_troff_font_format; break;
179 case mpx_fontdesc_format: format = kpse_troff_font_format; break;
180 case mpx_specchar_format: format = kpse_mpsupport_format; break;
181 default: return NULL; break;
183 return kpse_find_file (nam, format, req);
186 @ Invoke makempx (or troffmpx) to make sure there is an up-to-date
187 .mpx file for a given .mp file. (Original from John Hobby 3/14/90)
189 @d default_args " --parse-first-line --interaction=nonstopmode"
191 @d TROFF "soelim | eqn -Tps -d$$ | troff -Tps"
195 #define MPXCOMMAND "makempx"
197 int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) {
199 string cnf_cmd = kpse_var_value ("MPXCOMMAND");
201 if (cnf_cmd && (strcmp (cnf_cmd, "0")==0)) {
202 /* If they turned off this feature, just return success. */
206 /* We will invoke something. Compile-time default if nothing else. */
208 string qmpname = normalize_quotes(mpname, "mpname");
209 string qmpxname = normalize_quotes(mpxname, "mpxname");
211 if (mp_troff_mode(mp))
212 cmd = concatn (cnf_cmd, " -troff ",
213 qmpname, " ", qmpxname, NULL);
214 else if (mpost_tex_program && *mpost_tex_program)
215 cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ",
216 qmpname, " ", qmpxname, NULL);
218 cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
224 makempx_options * mpxopt;
225 const char *mpversion = mp_metapost_version () ;
226 mpxopt = xmalloc(sizeof(makempx_options));
228 char *maincmd = NULL;
229 int mpxmode = mp_troff_mode(mp);
230 if (mpost_tex_program && *mpost_tex_program) {
231 maincmd = xstrdup(mpost_tex_program);
233 if (mpxmode == mpx_tex_mode) {
234 s = kpse_var_value("TEX");
235 if (!s) s = kpse_var_value("MPXMAINCMD");
236 if (!s) s = xstrdup (TEX);
237 maincmd = (char *)xmalloc (strlen(s)+strlen(default_args)+1);
239 strcat(maincmd,default_args);
242 s = kpse_var_value("TROFF");
243 if (!s) s = kpse_var_value("MPXMAINCMD");
244 if (!s) s = xstrdup (TROFF);
248 mpxopt->mode = mpxmode;
249 mpxopt->cmd = maincmd;
250 mpxopt->mptexpre = kpse_var_value("MPTEXPRE");
251 mpxopt->mpname = qmpname;
252 mpxopt->mpxname = qmpxname;
253 mpxopt->debug = debug;
254 mpxopt->find_file = makempx_find_file;
256 char *banner = "% Written by metapost version ";
257 mpxopt->banner = xmalloc(strlen(mpversion)+strlen(banner)+1);
258 strcpy (mpxopt->banner, banner);
259 strcat (mpxopt->banner, mpversion);
261 ret = mp_makempx(mpxopt);
263 free(mpxopt->mptexpre);
275 @<Register the callback routines@>=
277 options->run_make_mpx = mpost_run_make_mpx;
281 static int get_random_seed (void) {
283 #if defined (HAVE_GETTIMEOFDAY)
285 gettimeofday(&tv, NULL);
286 ret = (tv.tv_usec + 1000000 * tv.tv_usec);
287 #elif defined (HAVE_FTIME)
290 ret = (tb.millitm + 1000 * tb.time);
292 time_t clock = time ((time_t*)NULL);
293 struct tm *tmptr = localtime(&clock);
294 ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
299 @ @<Register the callback routines@>=
300 options->random_seed = get_random_seed();
303 char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ftype) {
308 if (ftype>=mp_filetype_text) {
309 s = kpse_find_file (fname, kpse_mp_format, 0);
312 case mp_filetype_program:
314 if (l>3 && strcmp(fname+l-3,".mf")==0) {
315 s = kpse_find_file (fname, kpse_mf_format, 0);
317 s = kpse_find_file (fname, kpse_mp_format, 0);
320 case mp_filetype_memfile:
321 s = kpse_find_file (fname, kpse_mem_format, 0);
323 case mp_filetype_metrics:
324 s = kpse_find_file (fname, kpse_tfm_format, 0);
326 case mp_filetype_fontmap:
327 s = kpse_find_file (fname, kpse_fontmap_format, 0);
329 case mp_filetype_font:
330 s = kpse_find_file (fname, kpse_type1_format, 0);
332 case mp_filetype_encoding:
333 s = kpse_find_file (fname, kpse_enc_format, 0);
338 s = xstrdup(fname); /* when writing */
343 @ @<Register the callback routines@>=
345 options->find_file = mpost_find_file;
348 void *mpost_open_file(MP mp, const char *fname, const char *fmode, int ftype) {
351 if (ftype==mp_filetype_terminal) {
352 return (fmode[0] == 'r' ? stdin : stdout);
353 } else if (ftype==mp_filetype_error) {
356 s = mpost_find_file (mp, fname, fmode, ftype);
359 realmode[0] = *fmode;
362 ret = fopen(s,realmode);
370 @ @<Register the callback routines@>=
372 options->open_file = mpost_open_file;
375 @ At the moment, the command line is very simple.
377 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) ||
378 (strncmp(argv[a],"-" A, strlen(A)+1)==0))
379 @d option_arg(B) (optarg && strncmp(optarg,B, strlen(B))==0)
382 @<Read and set command line options@>=
385 boolean ini_version_test = false;
387 optarg = strstr(argv[a],"=") ;
390 if (!*optarg) optarg=NULL;
392 if (option_is("ini")) {
393 ini_version_test = true;
394 } else if (option_is("debug")) {
396 } else if (option_is ("kpathsea-debug")) {
397 kpathsea_debug |= atoi (optarg);
398 } else if (option_is("mem")) {
399 options->mem_name = xstrdup(optarg);
401 user_progname = optarg;
402 } else if (option_is("jobname")) {
403 options->job_name = xstrdup(optarg);
404 } else if (option_is ("progname")) {
405 user_progname = optarg;
406 } else if (option_is("troff")) {
407 options->troff_mode = true;
408 } else if (option_is ("tex")) {
409 mpost_tex_program = optarg;
410 } else if (option_is("interaction")) {
411 if (option_arg("batchmode")) {
412 options->interaction = mp_batch_mode;
413 } else if (option_arg("nonstopmode")) {
414 options->interaction = mp_nonstop_mode;
415 } else if (option_arg("scrollmode")) {
416 options->interaction = mp_scroll_mode;
417 } else if (option_arg("errorstopmode")) {
418 options->interaction = mp_error_stop_mode;
420 fprintf(stdout,"unknown option argument %s\n", argv[a]);
422 } else if (option_is("no-kpathsea")) {
424 } else if (option_is("help")) {
425 @<Show help and exit@>;
426 } else if (option_is("version")) {
427 @<Show version and exit@>;
428 } else if (option_is("")) {
429 continue; /* ignore unknown options */
434 options->ini_version = ini_version_test;
442 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
444 " Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
445 " MPNAME.tfm), where NNN are the character numbers generated.\n"
446 " Any remaining COMMANDS are processed as MetaPost input,\n"
447 " after MPNAME is read.\n\n");
449 " If no arguments or options are specified, prompt for input.\n"
451 " -ini be inimpost, for dumping mems\n"
452 " -interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n"
453 " scrollmode/errorstopmode)\n"
454 " -jobname=STRING set the job name to STRING\n"
455 " -progname=STRING set program (and mem) name to STRING\n");
457 " -tex=TEXPROGRAM use TEXPROGRAM for text labels\n"
458 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
459 " the bits of NUMBER\n"
460 " -mem=MEMNAME use MEMNAME instead of program name or a %%& line\n"
461 " -troff set the prologues variable, use `makempx -troff'\n"
462 " -help display this help and exit\n"
463 " -version output version information and exit\n"
465 "Email bug reports to mp-implementors@@tug.org.\n"
476 "Copyright 2008 AT&T Bell Laboratories.\n"
477 "There is NO warranty. Redistribution of this software is\n"
478 "covered by the terms of both the MetaPost copyright and\n"
479 "the Lesser GNU General Public License.\n"
480 "For more information about these matters, see the file\n"
481 "named COPYING and the MetaPost source.\n"
482 "Primary author of MetaPost: John Hobby.\n"
483 "Current maintainer of MetaPost: Taco Hoekwater.\n"
484 "\n", mp_metapost_version());
488 @ The final part of the command line, after option processing, is
489 stored in the \MP\ instance, this will be taken as the first line of
492 @d command_line_size 256
494 @<Copy the rest of the command line@>=
496 options->command_line = xmalloc(command_line_size);
497 strcpy(options->command_line,"");
503 if (k<(command_line_size-1)) {
504 options->command_line[k++] = *c;
508 options->command_line[k++] = ' ';
511 if (options->command_line[(k-1)] == ' ')
516 options->command_line[k] = 0;
520 @ A simple function to get numerical |texmf.cnf| values
522 int setup_var (int def, const char *var_name, int nokpse) {
524 char * expansion = kpse_var_value (var_name);
526 int conf_val = atoi (expansion);
537 @ Now this is really it: \MP\ starts and ends here.
539 @d xfree(A) if (A!=NULL) free(A)
542 int main (int argc, char **argv) { /* |start_here| */
543 int k; /* index into buffer */
544 int history; /* the exit status */
545 MP mp; /* a metapost instance */
546 struct MP_options * options; /* instance options */
547 int a=0; /* argc counter */
548 int nokpse = 0; /* switch to {\it not} enable kpse */
549 char *user_progname = NULL; /* If the user overrides argv[0] with -progname. */
550 options = mp_options();
551 options->ini_version = false;
552 options->print_found_names = true;
553 @<Read and set command line options@>;
555 kpse_set_program_name("mpost",user_progname);
556 if(putenv((char *)"engine=metapost"))
557 fprintf(stdout,"warning: could not set up $engine\n");
558 options->main_memory = setup_var (50000,"main_memory",nokpse);
559 options->hash_size = setup_var (16384,"hash_size",nokpse);
560 options->max_in_open = setup_var (25,"max_in_open",nokpse);
561 options->param_size = setup_var (1500,"param_size",nokpse);
562 options->error_line = setup_var (79,"error_line",nokpse);
563 options->half_error_line = setup_var (50,"half_error_line",nokpse);
564 options->max_print_line = setup_var (100,"max_print_line",nokpse);
565 @<Copy the rest of the command line@>;
566 @<Register the callback routines@>;
567 mp = mp_initialize(options);
568 xfree(options->command_line);
569 xfree(options->mem_name);
570 xfree(options->job_name);
574 history = mp_status(mp);
577 history = mp_run(mp);