2 % MetaPost command-line program, by Taco Hoekwater. Public domain.
4 \font\tenlogo=logo10 % font used for the METAFONT logo
5 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
7 \def\title{MetaPost executable}
11 @* \[1] Metapost executable.
13 Now that all of \MP\ is a library, a separate program is needed to
14 have our customary command-line interface.
16 @ First, here are the C includes. |avl.h| is needed because of an
17 |avl_allocator| that is defined in |mplib.h|
29 #define HAVE_PROTOTYPES 1
30 #include <kpathsea/progname.h>
31 #include <kpathsea/tex-file.h>
32 #include <kpathsea/variable.h>
33 extern unsigned kpathsea_debug;
34 #include <kpathsea/concatn.h>
35 static const char *mpost_tex_program = "";
36 static int debug = 0; /* debugging for makempx */
38 @ Allocating a bit of memory, with error detection:
41 void *xmalloc (size_t bytes) {
42 void *w = malloc (bytes);
44 fprintf(stderr,"Out of memory!\n");
49 char *xstrdup(const char *s) {
51 if (s==NULL) return NULL;
54 fprintf(stderr,"Out of memory!\n");
63 void mpost_run_editor (MP mp, char *fname, int fline) {
65 fprintf(stdout,"Ok, bye (%s,%d)!",fname, fline);
70 @<Register the callback routines@>=
71 options->run_editor = mpost_run_editor;
75 string normalize_quotes (const char *name, const char *mesg) {
77 int must_quote = (strchr(name, ' ') != NULL);
78 /* Leave room for quotes and NUL. */
79 string ret = (string)xmalloc(strlen(name)+3);
85 for (q = name; *q; q++) {
95 fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
102 static char *makempx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
105 if (mode[0] != 'r') {
110 case mpx_tfm_format: format = kpse_tfm_format; break;
111 case mpx_vf_format: format = kpse_vf_format; req = 0; break;
112 case mpx_trfontmap_format: format = kpse_mpsupport_format; break;
113 case mpx_trcharadj_format: format = kpse_mpsupport_format; break;
114 case mpx_desc_format: format = kpse_troff_font_format; break;
115 case mpx_fontdesc_format: format = kpse_troff_font_format; break;
116 case mpx_specchar_format: format = kpse_mpsupport_format; break;
117 default: return NULL; break;
119 return kpse_find_file (nam, format, req);
122 @ Invoke makempx (or troffmpx) to make sure there is an up-to-date
123 .mpx file for a given .mp file. (Original from John Hobby 3/14/90)
125 @d default_args " --parse-first-line --interaction=nonstopmode"
127 @d TROFF "soelim | eqn -Tps -d$$ | troff -Tps"
131 #define MPXCOMMAND "makempx"
133 int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) {
135 string cnf_cmd = kpse_var_value ("MPXCOMMAND");
137 if (cnf_cmd && (strcmp (cnf_cmd, "0")==0)) {
138 /* If they turned off this feature, just return success. */
142 /* We will invoke something. Compile-time default if nothing else. */
144 string qmpname = normalize_quotes(mpname, "mpname");
145 string qmpxname = normalize_quotes(mpxname, "mpxname");
147 if (mp_troff_mode(mp))
148 cmd = concatn (cnf_cmd, " -troff ",
149 qmpname, " ", qmpxname, NULL);
150 else if (mpost_tex_program && *mpost_tex_program)
151 cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ",
152 qmpname, " ", qmpxname, NULL);
154 cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
160 makempx_options * mpxopt;
161 const char *mpversion = mp_metapost_version (mp) ;
162 mpxopt = xmalloc(sizeof(makempx_options));
164 char *maincmd = NULL;
165 int mpxmode = mp_troff_mode(mp);
166 if (mpost_tex_program && *mpost_tex_program) {
167 maincmd = xstrdup(mpost_tex_program);
169 if (mpxmode == mpx_tex_mode) {
170 s = kpse_var_value("TEX");
171 if (!s) s = kpse_var_value("MPXMAINCMD");
172 if (!s) s = xstrdup (TEX);
173 maincmd = (char *)xmalloc (strlen(s)+strlen(default_args)+1);
175 strcat(maincmd,default_args);
178 s = kpse_var_value("TROFF");
179 if (!s) s = kpse_var_value("MPXMAINCMD");
180 if (!s) s = xstrdup (TROFF);
184 mpxopt->mode = mpxmode;
185 mpxopt->cmd = maincmd;
186 mpxopt->mptexpre = kpse_var_value("MPTEXPRE");
187 mpxopt->mpname = qmpname;
188 mpxopt->mpxname = qmpxname;
189 mpxopt->debug = debug;
190 mpxopt->find_file = makempx_find_file;
192 char *banner = "% Written by metapost version ";
193 mpxopt->banner = xmalloc(strlen(mpversion)+strlen(banner)+1);
194 strcpy (mpxopt->banner, banner);
195 strcat (mpxopt->banner, mpversion);
197 ret = mp_makempx(mpxopt);
199 free(mpxopt->mptexpre);
211 @<Register the callback routines@>=
213 options->run_make_mpx = mpost_run_make_mpx;
217 static int get_random_seed (void) {
219 #if defined (HAVE_GETTIMEOFDAY)
221 gettimeofday(&tv, NULL);
222 ret = (tv.tv_usec + 1000000 * tv.tv_usec);
223 #elif defined (HAVE_FTIME)
226 ret = (tb.millitm + 1000 * tb.time);
228 time_t clock = time ((time_t*)NULL);
229 struct tm *tmptr = localtime(&clock);
230 ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
235 @ @<Register the callback routines@>=
236 options->random_seed = get_random_seed();
239 char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ftype) {
244 if (ftype>=mp_filetype_text) {
245 s = kpse_find_file (fname, kpse_mp_format, 0);
248 case mp_filetype_program:
250 if (l>3 && strcmp(fname+l-3,".mf")==0) {
251 s = kpse_find_file (fname, kpse_mf_format, 0);
253 s = kpse_find_file (fname, kpse_mp_format, 0);
256 case mp_filetype_memfile:
257 s = kpse_find_file (fname, kpse_mem_format, 0);
259 case mp_filetype_metrics:
260 s = kpse_find_file (fname, kpse_tfm_format, 0);
262 case mp_filetype_fontmap:
263 s = kpse_find_file (fname, kpse_fontmap_format, 0);
265 case mp_filetype_font:
266 s = kpse_find_file (fname, kpse_type1_format, 0);
268 case mp_filetype_encoding:
269 s = kpse_find_file (fname, kpse_enc_format, 0);
274 s = xstrdup(fname); /* when writing */
279 @ @<Register the callback routines@>=
281 options->find_file = mpost_find_file;
284 void *mpost_open_file(MP mp, const char *fname, const char *fmode, int ftype) {
287 if (ftype==mp_filetype_terminal) {
288 return (fmode[0] == 'r' ? stdin : stdout);
289 } else if (ftype==mp_filetype_error) {
292 s = mpost_find_file (mp, fname, fmode, ftype);
295 realmode[0] = *fmode;
298 ret = fopen(s,realmode);
306 @ @<Register the callback routines@>=
308 options->open_file = mpost_open_file;
311 @ At the moment, the command line is very simple.
313 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) ||
314 (strncmp(argv[a],"-" A, strlen(A)+1)==0))
315 @d option_arg(B) (optarg && strncmp(optarg,B, strlen(B))==0)
318 @<Read and set command line options@>=
322 optarg = strstr(argv[a],"=") ;
325 if (!*optarg) optarg=NULL;
327 if (option_is("ini")) {
328 options->ini_version = true;
329 } else if (option_is("debug")) {
331 } else if (option_is ("kpathsea-debug")) {
332 kpathsea_debug |= atoi (optarg);
333 } else if (option_is("mem")) {
334 options->mem_name = xstrdup(optarg);
336 user_progname = optarg;
337 } else if (option_is("jobname")) {
338 options->job_name = xstrdup(optarg);
339 } else if (option_is ("progname")) {
340 user_progname = optarg;
341 } else if (option_is("troff")) {
342 options->troff_mode = true;
343 } else if (option_is ("tex")) {
344 mpost_tex_program = optarg;
345 } else if (option_is("interaction")) {
346 if (option_arg("batchmode")) {
347 options->interaction = mp_batch_mode;
348 } else if (option_arg("nonstopmode")) {
349 options->interaction = mp_nonstop_mode;
350 } else if (option_arg("scrollmode")) {
351 options->interaction = mp_scroll_mode;
352 } else if (option_arg("errorstopmode")) {
353 options->interaction = mp_error_stop_mode;
355 fprintf(stdout,"unknown option argument %s\n", argv[a]);
357 } else if (option_is("no-kpathsea")) {
359 } else if (option_is("help")) {
360 @<Show help and exit@>;
361 } else if (option_is("version")) {
362 mp = mp_new(mp_options());
363 @<Show version and exit@>;
364 } else if (option_is("")) {
365 continue; /* ignore unknown options */
377 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
379 " Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
380 " MPNAME.tfm), where NNN are the character numbers generated.\n"
381 " Any remaining COMMANDS are processed as MetaPost input,\n"
382 " after MPNAME is read.\n\n");
384 " If no arguments or options are specified, prompt for input.\n"
386 " -ini be inimpost, for dumping mems\n"
387 " -interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n"
388 " scrollmode/errorstopmode)\n"
389 " -jobname=STRING set the job name to STRING\n"
390 " -progname=STRING set program (and mem) name to STRING\n");
392 " -tex=TEXPROGRAM use TEXPROGRAM for text labels\n"
393 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
394 " the bits of NUMBER\n"
395 " -mem=MEMNAME use MEMNAME instead of program name or a %%& line\n"
396 " -troff set the prologues variable, use `makempx -troff'\n"
397 " -help display this help and exit\n"
398 " -version output version information and exit\n"
400 "Email bug reports to mp-implementors@@tug.org.\n"
410 "MetaPost %s (CWeb version %s)\n"
411 "Copyright 2008 AT&T Bell Laboratories.\n"
412 "There is NO warranty. Redistribution of this software is\n"
413 "covered by the terms of both the MetaPost copyright and\n"
414 "the Lesser GNU General Public License.\n"
415 "For more information about these matters, see the file\n"
416 "named COPYING and the MetaPost source.\n"
417 "Primary author of MetaPost: John Hobby.\n"
418 "Current maintainer of MetaPost: Taco Hoekwater.\n"
419 "\n", mp_metapost_version(mp), mp_mplib_version(mp));
423 @ The final part of the command line, after option processing, is
424 stored in the \MP\ instance, this will be taken as the first line of
427 @d command_line_size 256
429 @<Copy the rest of the command line@>=
431 options->command_line = xmalloc(command_line_size);
432 strcpy(options->command_line,"");
438 if (k<(command_line_size-1)) {
439 options->command_line[k++] = *c;
443 options->command_line[k++] = ' ';
446 if (options->command_line[(k-1)] == ' ')
451 options->command_line[k] = 0;
455 @ A simple function to get numerical |texmf.cnf| values
457 int setup_var (int def, const char *var_name, int nokpse) {
459 char * expansion = kpse_var_value (var_name);
461 int conf_val = atoi (expansion);
472 @ Now this is really it: \MP\ starts and ends here.
474 @d xfree(A) if (A!=NULL) free(A)
477 int main (int argc, char **argv) { /* |start_here| */
478 int k; /* index into buffer */
479 int history; /* the exit status */
480 MP mp; /* a metapost instance */
481 struct MP_options * options; /* instance options */
482 int a=0; /* argc counter */
483 int nokpse = 0; /* switch to {\it not} enable kpse */
484 char *user_progname = NULL; /* If the user overrides argv[0] with -progname. */
485 options = mp_options();
486 options->ini_version = false;
487 options->print_found_names = true;
488 @<Read and set command line options@>;
490 kpse_set_program_name("mpost",user_progname);
491 if(putenv((char *)"engine=metapost"))
492 fprintf(stdout,"warning: could not set up $engine\n");
493 options->main_memory = setup_var (50000,"main_memory",nokpse);
494 options->hash_size = setup_var (9500,"hash_size",nokpse);
495 options->hash_prime = 7919;
496 options->max_in_open = setup_var (25,"max_in_open",nokpse);
497 options->param_size = setup_var (1500,"param_size",nokpse);
498 options->error_line = setup_var (79,"error_line",nokpse);
499 options->half_error_line = setup_var (50,"half_error_line",nokpse);
500 options->max_print_line = setup_var (100,"max_print_line",nokpse);
501 @<Copy the rest of the command line@>;
502 @<Register the callback routines@>;
503 mp = mp_new(options);
504 xfree(options->command_line);
505 xfree(options->mem_name);
506 xfree(options->job_name);
510 history = mp_initialize(mp);
513 history = mp_run(mp);