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;
151 static int get_random_seed (void) {
153 #if defined (HAVE_GETTIMEOFDAY)
155 gettimeofday(&tv, NULL);
156 ret = (tv.tv_usec + 1000000 * tv.tv_usec);
157 #elif defined (HAVE_FTIME)
160 ret = (tb.millitm + 1000 * tb.time);
162 time_t clock = time ((time_t*)NULL);
163 struct tm *tmptr = localtime(&clock);
164 ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
169 @ @<Register the callback routines@>=
170 options->random_seed = get_random_seed();
173 char *mpost_find_file(char *fname, char *fmode, int ftype) {
177 if (ftype>=mp_filetype_text) {
178 s = kpse_find_file (fname, kpse_mp_format, 0);
181 case mp_filetype_program:
183 if (l>3 && strcmp(fname+l-3,".mf")==0) {
184 s = kpse_find_file (fname, kpse_mf_format, 0);
186 s = kpse_find_file (fname, kpse_mp_format, 0);
189 case mp_filetype_memfile:
190 s = kpse_find_file (fname, kpse_mem_format, 0);
192 case mp_filetype_metrics:
193 s = kpse_find_file (fname, kpse_tfm_format, 0);
195 case mp_filetype_fontmap:
196 s = kpse_find_file (fname, kpse_fontmap_format, 0);
198 case mp_filetype_font:
199 s = kpse_find_file (fname, kpse_type1_format, 0);
201 case mp_filetype_encoding:
202 s = kpse_find_file (fname, kpse_enc_format, 0);
207 s = xstrdup(fname); /* when writing */
212 @ @<Register the callback routines@>=
214 options->find_file = mpost_find_file;
217 void *mpost_open_file(char *fname, char *fmode, int ftype) {
219 if (ftype==mp_filetype_terminal) {
220 return (fmode[0] == 'r' ? stdin : stdout);
221 } else if (ftype==mp_filetype_error) {
224 s = mpost_find_file (fname, fmode, ftype);
226 return fopen(s,fmode);
232 @ @<Register the callback routines@>=
234 options->open_file = mpost_open_file;
237 @ At the moment, the command line is very simple.
239 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) ||
240 (strncmp(argv[a],"-" A, strlen(A)+1)==0))
241 @d option_arg(B) (optarg && strncmp(optarg,B, strlen(B))==0)
244 @<Read and set commmand line options@>=
248 optarg = strstr(argv[a],"=") ;
251 if (!*optarg) optarg=NULL;
253 if (option_is("ini")) {
254 options->ini_version = true;
255 } else if (option_is ("kpathsea-debug")) {
256 kpathsea_debug |= atoi (optarg);
257 } else if (option_is("mem")) {
258 options->mem_name = xstrdup(optarg);
260 user_progname = optarg;
261 } else if (option_is("jobname")) {
262 options->job_name = xstrdup(optarg);
263 } else if (option_is ("progname")) {
264 user_progname = optarg;
265 } else if (option_is("troff")) {
266 options->troff_mode = true;
267 } else if (option_is ("tex")) {
268 mpost_tex_program = optarg;
269 } else if (option_is("interaction")) {
270 if (option_arg("batchmode")) {
271 options->interaction = mp_batch_mode;
272 } else if (option_arg("nonstopmode")) {
273 options->interaction = mp_nonstop_mode;
274 } else if (option_arg("scrollmode")) {
275 options->interaction = mp_scroll_mode;
276 } else if (option_arg("errorstopmode")) {
277 options->interaction = mp_error_stop_mode;
279 fprintf(stdout,"unknown option argument %s\n", argv[a]);
281 } else if (option_is("no-kpathsea")) {
283 } else if (option_is("help")) {
284 @<Show help and exit@>;
285 } else if (option_is("version")) {
286 @<Show version and exit@>;
287 } else if (option_is("")) {
288 continue; /* ignore unknown options */
300 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
302 " Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
303 " MPNAME.tfm), where NNN are the character numbers generated.\n"
304 " Any remaining COMMANDS are processed as MetaPost input,\n"
305 " after MPNAME is read.\n"
307 " If no arguments or options are specified, prompt for input.\n"
309 " -ini be inimpost, for dumping mems\n"
310 " -interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n"
311 " scrollmode/errorstopmode)\n"
312 " -jobname=STRING set the job name to STRING\n"
313 " -progname=STRING set program (and mem) name to STRING\n"
314 " -tex=TEXPROGRAM use TEXPROGRAM for text labels\n"
315 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
316 " the bits of NUMBER\n"
317 " -mem=MEMNAME use MEMNAME instead of program name or a %%& line\n"
318 " -troff set the prologues variable, use `makempx -troff'\n"
319 " -help display this help and exit\n"
320 " -version output version information and exit\n"
322 "Email bug reports to mp-implementors@@tug.org.\n"
332 "MetaPost %s (CWeb version %s)\n"
333 "Copyright 2008 AT&T Bell Laboratories.\n"
334 "There is NO warranty. Redistribution of this software is\n"
335 "covered by the terms of both the MetaPost copyright and\n"
336 "the Lesser GNU General Public License.\n"
337 "For more information about these matters, see the file\n"
338 "named COPYING and the MetaPost source.\n"
339 "Primary author of MetaPost: John Hobby.\n"
340 "Current maintainer of MetaPost: Taco Hoekwater.\n"
341 "\n", mp_metapost_version(mp), mp_mplib_version(mp));
345 @ The final part of the command line, after option processing, is
346 stored in the \MP\ instance, this will be taken as the first line of
349 @d command_line_size 256
351 @<Copy the rest of the command line@>=
353 options->command_line = xmalloc(command_line_size);
354 strcpy(options->command_line,"");
360 if (k<(command_line_size-1)) {
361 options->command_line[k++] = *c;
365 options->command_line[k++] = ' ';
368 if (options->command_line[(k-1)] == ' ')
373 options->command_line[k] = 0;
377 @ A simple function to get numerical |texmf.cnf| values
379 int setup_var (int def, char *var_name, int nokpse) {
381 char * expansion = kpse_var_value (var_name);
383 int conf_val = atoi (expansion);
394 @ Now this is really it: \MP\ starts and ends here.
396 @d xfree(A) if (A!=NULL) free(A)
399 int main (int argc, char **argv) { /* |start_here| */
400 int a=0; /* argc counter */
401 int k; /* index into buffer */
402 int history; /* the exit status */
403 MP mp; /* a metapost instance */
404 struct MP_options * options; /* instance options */
405 int nokpse = 0; /* switch to {\it not} enable kpse */
406 char *user_progname = NULL; /* If the user overrides argv[0] with -progname. */
407 options = mp_options();
408 options->ini_version = false;
409 options->print_found_names = true;
410 @<Read and set commmand line options@>;
412 kpse_set_program_name("mpost",user_progname);
413 if(putenv("engine=newmetapost"))
414 fprintf(stdout,"warning: could not set up $engine\n");
415 options->main_memory = setup_var (50000,"main_memory",nokpse);
416 options->hash_size = setup_var (9500,"hash_size",nokpse);
417 options->hash_prime = 7919;
418 options->max_in_open = setup_var (25,"max_in_open",nokpse);
419 options->param_size = setup_var (1500,"param_size",nokpse);
420 options->error_line = setup_var (79,"error_line",nokpse);
421 options->half_error_line = setup_var (50,"half_error_line",nokpse);
422 options->max_print_line = setup_var (100,"max_print_line",nokpse);
423 @<Copy the rest of the command line@>;
424 @<Register the callback routines@>;
425 mp = mp_new(options);
426 xfree(options->command_line);
427 xfree(options->mem_name);
428 xfree(options->job_name);
432 history = mp_initialize(mp);
435 history = mp_run(mp);