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|
28 #define HAVE_PROTOTYPES 1
29 #include <kpathsea/progname.h>
30 #include <kpathsea/tex-file.h>
31 #include <kpathsea/variable.h>
32 extern unsigned kpathsea_debug;
33 #include <kpathsea/concatn.h>
34 static string mpost_tex_program = "";
36 @ Allocating a bit of memory, with error detection:
39 void *xmalloc (size_t bytes) {
40 void *w = malloc (bytes);
42 fprintf(stderr,"Out of memory!\n");
47 char *xstrdup(const char *s) {
49 if (s==NULL) return NULL;
52 fprintf(stderr,"Out of memory!\n");
61 void mpost_run_editor (MP mp, char *fname, int fline) {
63 fprintf(stdout,"Ok, bye (%s,%d)!",fname, fline);
68 @<Register the callback routines@>=
69 options->run_editor = mpost_run_editor;
73 string normalize_quotes (const char *name, const char *mesg) {
75 int must_quote = (strchr(name, ' ') != NULL);
76 /* Leave room for quotes and NUL. */
77 string ret = (string)xmalloc(strlen(name)+3);
83 for (q = name; *q; q++) {
93 fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
100 @ Invoke makempx (or troffmpx) to make sure there is an up-to-date
101 .mpx file for a given .mp file. (Original from John Hobby 3/14/90)
106 #define MPXCOMMAND "makempx"
108 int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) {
110 string cnf_cmd = kpse_var_value ("MPXCOMMAND");
112 if (cnf_cmd && (strcmp (cnf_cmd, "0")==0)) {
113 /* If they turned off this feature, just return success. */
117 /* We will invoke something. Compile-time default if nothing else. */
119 string qmpname = normalize_quotes(mpname, "mpname");
120 string qmpxname = normalize_quotes(mpxname, "mpxname");
122 cnf_cmd = xstrdup (MPXCOMMAND);
124 if (mp_troff_mode(mp))
125 cmd = concatn (cnf_cmd, " -troff ",
126 qmpname, " ", qmpxname, NULL);
127 else if (mpost_tex_program && *mpost_tex_program)
128 cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ",
129 qmpname, " ", qmpxname, NULL);
131 cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
145 @<Register the callback routines@>=
147 options->run_make_mpx = mpost_run_make_mpx;
150 @ @c int mpost_get_random_seed (MP mp) {
152 #if defined (HAVE_GETTIMEOFDAY)
154 gettimeofday(&tv, NULL);
155 ret = (tv.tv_usec + 1000000 * tv.tv_usec);
156 #elif defined (HAVE_FTIME)
159 ret = (tb.millitm + 1000 * tb.time);
161 time_t clock = time ((time_t*)NULL);
162 struct tm *tmptr = localtime(&clock);
163 ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
165 return (mp ? ret : ret); /* for -W */
168 @ @<Register the callback routines@>=
169 options->get_random_seed = mpost_get_random_seed;
172 char *mpost_find_file(char *fname, char *fmode, int ftype) {
176 if (ftype>=mp_filetype_text) {
177 s = kpse_find_file (fname, kpse_mp_format, 0);
180 case mp_filetype_program:
182 if (l>3 && strcmp(fname+l-3,".mf")==0) {
183 s = kpse_find_file (fname, kpse_mf_format, 0);
185 s = kpse_find_file (fname, kpse_mp_format, 0);
188 case mp_filetype_memfile:
189 s = kpse_find_file (fname, kpse_mem_format, 0);
191 case mp_filetype_metrics:
192 s = kpse_find_file (fname, kpse_tfm_format, 0);
194 case mp_filetype_fontmap:
195 s = kpse_find_file (fname, kpse_fontmap_format, 0);
197 case mp_filetype_font:
198 s = kpse_find_file (fname, kpse_type1_format, 0);
200 case mp_filetype_encoding:
201 s = kpse_find_file (fname, kpse_enc_format, 0);
206 s = xstrdup(fname); /* when writing */
211 @ @<Register the callback routines@>=
213 options->find_file = mpost_find_file;
216 void *mpost_open_file(char *fname, char *fmode, int ftype) {
218 if (ftype==mp_filetype_terminal) {
219 return (fmode[0] == 'r' ? stdin : stdout);
220 } else if (ftype==mp_filetype_error) {
223 s = mpost_find_file (fname, fmode, ftype);
225 return fopen(s,fmode);
231 @ @<Register the callback routines@>=
233 options->open_file = mpost_open_file;
236 @ At the moment, the command line is very simple.
238 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) ||
239 (strncmp(argv[a],"-" A, strlen(A)+1)==0))
240 @d option_arg(B) (optarg && strncmp(optarg,B, strlen(B))==0)
243 @<Read and set commmand line options@>=
247 optarg = strstr(argv[a],"=") ;
250 if (!*optarg) optarg=NULL;
252 if (option_is("ini")) {
253 options->ini_version = true;
254 } else if (option_is ("kpathsea-debug")) {
255 kpathsea_debug |= atoi (optarg);
256 } else if (option_is("mem")) {
257 options->mem_name = xstrdup(optarg);
259 user_progname = optarg;
260 } else if (option_is("jobname")) {
261 options->job_name = xstrdup(optarg);
262 } else if (option_is ("progname")) {
263 user_progname = optarg;
264 } else if (option_is("troff")) {
265 options->troff_mode = true;
266 } else if (option_is ("tex")) {
267 mpost_tex_program = optarg;
268 } else if (option_is("interaction")) {
269 if (option_arg("batchmode")) {
270 options->interaction = mp_batch_mode;
271 } else if (option_arg("nonstopmode")) {
272 options->interaction = mp_nonstop_mode;
273 } else if (option_arg("scrollmode")) {
274 options->interaction = mp_scroll_mode;
275 } else if (option_arg("errorstopmode")) {
276 options->interaction = mp_error_stop_mode;
278 fprintf(stdout,"unknown option argument %s\n", argv[a]);
280 } else if (option_is("no-kpathsea")) {
282 } else if (option_is("help")) {
283 @<Show help and exit@>;
284 } else if (option_is("version")) {
285 @<Show version and exit@>;
286 } else if (option_is("")) {
287 continue; /* ignore unknown options */
299 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
301 " Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
302 " MPNAME.tfm), where NNN are the character numbers generated.\n"
303 " Any remaining COMMANDS are processed as MetaPost input,\n"
304 " after MPNAME is read.\n"
306 " If no arguments or options are specified, prompt for input.\n"
308 " -ini be inimpost, for dumping mems\n"
309 " -interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n"
310 " scrollmode/errorstopmode)\n"
311 " -jobname=STRING set the job name to STRING\n"
312 " -progname=STRING set program (and mem) name to STRING\n"
313 " -tex=TEXPROGRAM use TEXPROGRAM for text labels\n"
314 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
315 " the bits of NUMBER\n"
316 " -mem=MEMNAME use MEMNAME instead of program name or a %%& line\n"
317 " -troff set the prologues variable, use `makempx -troff'\n"
318 " -help display this help and exit\n"
319 " -version output version information and exit\n"
321 "Email bug reports to mp-implementors@@tug.org.\n"
331 "MetaPost %s (CWeb version %s)\n"
332 "Copyright 2008 AT&T Bell Laboratories.\n"
333 "There is NO warranty. Redistribution of this software is\n"
334 "covered by the terms of both the MetaPost copyright and\n"
335 "the Lesser GNU General Public License.\n"
336 "For more information about these matters, see the file\n"
337 "named COPYING and the MetaPost source.\n"
338 "Primary author of MetaPost: John Hobby.\n"
339 "Current maintainer of MetaPost: Taco Hoekwater.\n"
340 "\n", mp_metapost_version(mp), mp_mplib_version(mp));
344 @ The final part of the command line, after option processing, is
345 stored in the \MP\ instance, this will be taken as the first line of
348 @d command_line_size 256
350 @<Copy the rest of the command line@>=
352 options->command_line = xmalloc(command_line_size);
353 strcpy(options->command_line,"");
359 if (k<(command_line_size-1)) {
360 options->command_line[k++] = *c;
364 options->command_line[k++] = ' ';
367 if (options->command_line[(k-1)] == ' ')
372 options->command_line[k] = 0;
376 @ A simple function to get numerical |texmf.cnf| values
378 int setup_var (int def, char *var_name, int nokpse) {
380 char * expansion = kpse_var_value (var_name);
382 int conf_val = atoi (expansion);
393 @ Now this is really it: \MP\ starts and ends here.
396 int main (int argc, char **argv) { /* |start_here| */
397 int a=0; /* argc counter */
398 int k; /* index into buffer */
399 int history; /* the exit status */
400 MP mp; /* a metapost instance */
401 struct MP_options * options; /* instance options */
402 int nokpse = 0; /* switch to {\it not} enable kpse */
403 char *user_progname = NULL; /* If the user overrides argv[0] with -progname. */
404 options = mp_options();
405 options->ini_version = false;
406 options->print_found_names = true;
407 @<Read and set commmand line options@>;
409 kpse_set_program_name("mpost",user_progname);
410 if(putenv("engine=newmetapost"))
411 fprintf(stdout,"warning: could not set up $engine\n");
412 options->main_memory = setup_var (50000,"main_memory",nokpse);
413 options->hash_size = setup_var (9500,"hash_size",nokpse);
414 options->hash_prime = 7919;
415 options->max_in_open = setup_var (25,"max_in_open",nokpse);
416 options->param_size = setup_var (1500,"param_size",nokpse);
417 options->error_line = setup_var (79,"error_line",nokpse);
418 options->half_error_line = setup_var (50,"half_error_line",nokpse);
419 options->max_print_line = setup_var (100,"max_print_line",nokpse);
420 @<Copy the rest of the command line@>;
421 @<Register the callback routines@>;
422 mp = mp_new(options);
423 free((void *)options);
426 history = mp_initialize(mp);
429 history = mp_run(mp);