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 mpxopt = xmalloc(sizeof(makempx_options));
163 char *maincmd = NULL;
164 int mpxmode = mp_troff_mode(mp);
165 if (mpost_tex_program && *mpost_tex_program) {
166 maincmd = xstrdup(mpost_tex_program);
168 if (mpxmode == mpx_tex_mode) {
169 s = kpse_var_value("TEX");
170 if (!s) s = kpse_var_value("MPXMAINCMD");
171 if (!s) s = xstrdup (TEX);
172 maincmd = (char *)xmalloc (strlen(s)+strlen(default_args)+1);
174 strcat(maincmd,default_args);
177 s = kpse_var_value("TROFF");
178 if (!s) s = kpse_var_value("MPXMAINCMD");
179 if (!s) s = xstrdup (TROFF);
183 mpxopt->mode = mpxmode;
184 mpxopt->cmd = maincmd;
185 mpxopt->mptexpre = kpse_var_value("MPTEXPRE");
186 mpxopt->mpname = qmpname;
187 mpxopt->mpxname = qmpxname;
188 mpxopt->debug = debug;
189 mpxopt->find_file = makempx_find_file;
190 ret = mp_makempx(mpxopt);
192 free(mpxopt->mptexpre);
204 @<Register the callback routines@>=
206 options->run_make_mpx = mpost_run_make_mpx;
210 static int get_random_seed (void) {
212 #if defined (HAVE_GETTIMEOFDAY)
214 gettimeofday(&tv, NULL);
215 ret = (tv.tv_usec + 1000000 * tv.tv_usec);
216 #elif defined (HAVE_FTIME)
219 ret = (tb.millitm + 1000 * tb.time);
221 time_t clock = time ((time_t*)NULL);
222 struct tm *tmptr = localtime(&clock);
223 ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
228 @ @<Register the callback routines@>=
229 options->random_seed = get_random_seed();
232 char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ftype) {
237 if (ftype>=mp_filetype_text) {
238 s = kpse_find_file (fname, kpse_mp_format, 0);
241 case mp_filetype_program:
243 if (l>3 && strcmp(fname+l-3,".mf")==0) {
244 s = kpse_find_file (fname, kpse_mf_format, 0);
246 s = kpse_find_file (fname, kpse_mp_format, 0);
249 case mp_filetype_memfile:
250 s = kpse_find_file (fname, kpse_mem_format, 0);
252 case mp_filetype_metrics:
253 s = kpse_find_file (fname, kpse_tfm_format, 0);
255 case mp_filetype_fontmap:
256 s = kpse_find_file (fname, kpse_fontmap_format, 0);
258 case mp_filetype_font:
259 s = kpse_find_file (fname, kpse_type1_format, 0);
261 case mp_filetype_encoding:
262 s = kpse_find_file (fname, kpse_enc_format, 0);
267 s = xstrdup(fname); /* when writing */
272 @ @<Register the callback routines@>=
274 options->find_file = mpost_find_file;
277 void *mpost_open_file(MP mp, const char *fname, const char *fmode, int ftype) {
280 if (ftype==mp_filetype_terminal) {
281 return (fmode[0] == 'r' ? stdin : stdout);
282 } else if (ftype==mp_filetype_error) {
285 s = mpost_find_file (mp, fname, fmode, ftype);
288 realmode[0] = *fmode;
291 ret = fopen(s,realmode);
299 @ @<Register the callback routines@>=
301 options->open_file = mpost_open_file;
304 @ At the moment, the command line is very simple.
306 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) ||
307 (strncmp(argv[a],"-" A, strlen(A)+1)==0))
308 @d option_arg(B) (optarg && strncmp(optarg,B, strlen(B))==0)
311 @<Read and set command line options@>=
315 optarg = strstr(argv[a],"=") ;
318 if (!*optarg) optarg=NULL;
320 if (option_is("ini")) {
321 options->ini_version = true;
322 } else if (option_is("debug")) {
324 } else if (option_is ("kpathsea-debug")) {
325 kpathsea_debug |= atoi (optarg);
326 } else if (option_is("mem")) {
327 options->mem_name = xstrdup(optarg);
329 user_progname = optarg;
330 } else if (option_is("jobname")) {
331 options->job_name = xstrdup(optarg);
332 } else if (option_is ("progname")) {
333 user_progname = optarg;
334 } else if (option_is("troff")) {
335 options->troff_mode = true;
336 } else if (option_is ("tex")) {
337 mpost_tex_program = optarg;
338 } else if (option_is("interaction")) {
339 if (option_arg("batchmode")) {
340 options->interaction = mp_batch_mode;
341 } else if (option_arg("nonstopmode")) {
342 options->interaction = mp_nonstop_mode;
343 } else if (option_arg("scrollmode")) {
344 options->interaction = mp_scroll_mode;
345 } else if (option_arg("errorstopmode")) {
346 options->interaction = mp_error_stop_mode;
348 fprintf(stdout,"unknown option argument %s\n", argv[a]);
350 } else if (option_is("no-kpathsea")) {
352 } else if (option_is("help")) {
353 @<Show help and exit@>;
354 } else if (option_is("version")) {
355 mp = mp_new(mp_options());
356 @<Show version and exit@>;
357 } else if (option_is("")) {
358 continue; /* ignore unknown options */
370 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
372 " Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
373 " MPNAME.tfm), where NNN are the character numbers generated.\n"
374 " Any remaining COMMANDS are processed as MetaPost input,\n"
375 " after MPNAME is read.\n\n");
377 " If no arguments or options are specified, prompt for input.\n"
379 " -ini be inimpost, for dumping mems\n"
380 " -interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n"
381 " scrollmode/errorstopmode)\n"
382 " -jobname=STRING set the job name to STRING\n"
383 " -progname=STRING set program (and mem) name to STRING\n");
385 " -tex=TEXPROGRAM use TEXPROGRAM for text labels\n"
386 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
387 " the bits of NUMBER\n"
388 " -mem=MEMNAME use MEMNAME instead of program name or a %%& line\n"
389 " -troff set the prologues variable, use `makempx -troff'\n"
390 " -help display this help and exit\n"
391 " -version output version information and exit\n"
393 "Email bug reports to mp-implementors@@tug.org.\n"
403 "MetaPost %s (CWeb version %s)\n"
404 "Copyright 2008 AT&T Bell Laboratories.\n"
405 "There is NO warranty. Redistribution of this software is\n"
406 "covered by the terms of both the MetaPost copyright and\n"
407 "the Lesser GNU General Public License.\n"
408 "For more information about these matters, see the file\n"
409 "named COPYING and the MetaPost source.\n"
410 "Primary author of MetaPost: John Hobby.\n"
411 "Current maintainer of MetaPost: Taco Hoekwater.\n"
412 "\n", mp_metapost_version(mp), mp_mplib_version(mp));
416 @ The final part of the command line, after option processing, is
417 stored in the \MP\ instance, this will be taken as the first line of
420 @d command_line_size 256
422 @<Copy the rest of the command line@>=
424 options->command_line = xmalloc(command_line_size);
425 strcpy(options->command_line,"");
431 if (k<(command_line_size-1)) {
432 options->command_line[k++] = *c;
436 options->command_line[k++] = ' ';
439 if (options->command_line[(k-1)] == ' ')
444 options->command_line[k] = 0;
448 @ A simple function to get numerical |texmf.cnf| values
450 int setup_var (int def, const char *var_name, int nokpse) {
452 char * expansion = kpse_var_value (var_name);
454 int conf_val = atoi (expansion);
465 @ Now this is really it: \MP\ starts and ends here.
467 @d xfree(A) if (A!=NULL) free(A)
470 int main (int argc, char **argv) { /* |start_here| */
471 int k; /* index into buffer */
472 int history; /* the exit status */
473 MP mp; /* a metapost instance */
474 struct MP_options * options; /* instance options */
475 int a=0; /* argc counter */
476 int nokpse = 0; /* switch to {\it not} enable kpse */
477 char *user_progname = NULL; /* If the user overrides argv[0] with -progname. */
478 options = mp_options();
479 options->ini_version = false;
480 options->print_found_names = true;
481 @<Read and set command line options@>;
483 kpse_set_program_name("mpost",user_progname);
484 if(putenv((char *)"engine=newmetapost"))
485 fprintf(stdout,"warning: could not set up $engine\n");
486 options->main_memory = setup_var (50000,"main_memory",nokpse);
487 options->hash_size = setup_var (9500,"hash_size",nokpse);
488 options->hash_prime = 7919;
489 options->max_in_open = setup_var (25,"max_in_open",nokpse);
490 options->param_size = setup_var (1500,"param_size",nokpse);
491 options->error_line = setup_var (79,"error_line",nokpse);
492 options->half_error_line = setup_var (50,"half_error_line",nokpse);
493 options->max_print_line = setup_var (100,"max_print_line",nokpse);
494 @<Copy the rest of the command line@>;
495 @<Register the callback routines@>;
496 mp = mp_new(options);
497 xfree(options->command_line);
498 xfree(options->mem_name);
499 xfree(options->job_name);
503 history = mp_initialize(mp);
506 history = mp_run(mp);