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 = "";
37 @ Allocating a bit of memory, with error detection:
40 void *xmalloc (size_t bytes) {
41 void *w = malloc (bytes);
43 fprintf(stderr,"Out of memory!\n");
48 char *xstrdup(const char *s) {
50 if (s==NULL) return NULL;
53 fprintf(stderr,"Out of memory!\n");
62 void mpost_run_editor (MP mp, char *fname, int fline) {
64 fprintf(stdout,"Ok, bye (%s,%d)!",fname, fline);
69 @<Register the callback routines@>=
70 options->run_editor = mpost_run_editor;
74 string normalize_quotes (const char *name, const char *mesg) {
76 int must_quote = (strchr(name, ' ') != NULL);
77 /* Leave room for quotes and NUL. */
78 string ret = (string)xmalloc(strlen(name)+3);
84 for (q = name; *q; q++) {
94 fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
101 @ Invoke makempx (or troffmpx) to make sure there is an up-to-date
102 .mpx file for a given .mp file. (Original from John Hobby 3/14/90)
107 #define MPXCOMMAND "makempx"
109 int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) {
111 string cnf_cmd = kpse_var_value ("MPXCOMMAND");
113 if (cnf_cmd && (strcmp (cnf_cmd, "0")==0)) {
114 /* If they turned off this feature, just return success. */
118 /* We will invoke something. Compile-time default if nothing else. */
120 string qmpname = normalize_quotes(mpname, "mpname");
121 string qmpxname = normalize_quotes(mpxname, "mpxname");
123 if (mp_troff_mode(mp))
124 cmd = concatn (cnf_cmd, " -troff ",
125 qmpname, " ", qmpxname, NULL);
126 else if (mpost_tex_program && *mpost_tex_program)
127 cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ",
128 qmpname, " ", qmpxname, NULL);
130 cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
136 mp_makempx(mp_troff_mode(mp),NULL, qmpname, qmpxname,0);
147 @<Register the callback routines@>=
149 options->run_make_mpx = mpost_run_make_mpx;
153 static int get_random_seed (void) {
155 #if defined (HAVE_GETTIMEOFDAY)
157 gettimeofday(&tv, NULL);
158 ret = (tv.tv_usec + 1000000 * tv.tv_usec);
159 #elif defined (HAVE_FTIME)
162 ret = (tb.millitm + 1000 * tb.time);
164 time_t clock = time ((time_t*)NULL);
165 struct tm *tmptr = localtime(&clock);
166 ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
171 @ @<Register the callback routines@>=
172 options->random_seed = get_random_seed();
175 char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ftype) {
180 if (ftype>=mp_filetype_text) {
181 s = kpse_find_file (fname, kpse_mp_format, 0);
184 case mp_filetype_program:
186 if (l>3 && strcmp(fname+l-3,".mf")==0) {
187 s = kpse_find_file (fname, kpse_mf_format, 0);
189 s = kpse_find_file (fname, kpse_mp_format, 0);
192 case mp_filetype_memfile:
193 s = kpse_find_file (fname, kpse_mem_format, 0);
195 case mp_filetype_metrics:
196 s = kpse_find_file (fname, kpse_tfm_format, 0);
198 case mp_filetype_fontmap:
199 s = kpse_find_file (fname, kpse_fontmap_format, 0);
201 case mp_filetype_font:
202 s = kpse_find_file (fname, kpse_type1_format, 0);
204 case mp_filetype_encoding:
205 s = kpse_find_file (fname, kpse_enc_format, 0);
210 s = xstrdup(fname); /* when writing */
215 @ @<Register the callback routines@>=
217 options->find_file = mpost_find_file;
220 void *mpost_open_file(MP mp, const char *fname, const char *fmode, int ftype) {
223 if (ftype==mp_filetype_terminal) {
224 return (fmode[0] == 'r' ? stdin : stdout);
225 } else if (ftype==mp_filetype_error) {
228 s = mpost_find_file (mp, fname, fmode, ftype);
230 realmode[0] = *fmode;
233 return fopen(s,realmode);
239 @ @<Register the callback routines@>=
241 options->open_file = mpost_open_file;
244 @ At the moment, the command line is very simple.
246 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) ||
247 (strncmp(argv[a],"-" A, strlen(A)+1)==0))
248 @d option_arg(B) (optarg && strncmp(optarg,B, strlen(B))==0)
251 @<Read and set commmand line options@>=
255 optarg = strstr(argv[a],"=") ;
258 if (!*optarg) optarg=NULL;
260 if (option_is("ini")) {
261 options->ini_version = true;
262 } else if (option_is ("kpathsea-debug")) {
263 kpathsea_debug |= atoi (optarg);
264 } else if (option_is("mem")) {
265 options->mem_name = xstrdup(optarg);
267 user_progname = optarg;
268 } else if (option_is("jobname")) {
269 options->job_name = xstrdup(optarg);
270 } else if (option_is ("progname")) {
271 user_progname = optarg;
272 } else if (option_is("troff")) {
273 options->troff_mode = true;
274 } else if (option_is ("tex")) {
275 mpost_tex_program = optarg;
276 } else if (option_is("interaction")) {
277 if (option_arg("batchmode")) {
278 options->interaction = mp_batch_mode;
279 } else if (option_arg("nonstopmode")) {
280 options->interaction = mp_nonstop_mode;
281 } else if (option_arg("scrollmode")) {
282 options->interaction = mp_scroll_mode;
283 } else if (option_arg("errorstopmode")) {
284 options->interaction = mp_error_stop_mode;
286 fprintf(stdout,"unknown option argument %s\n", argv[a]);
288 } else if (option_is("no-kpathsea")) {
290 } else if (option_is("help")) {
291 @<Show help and exit@>;
292 } else if (option_is("version")) {
293 mp = mp_new(mp_options());
294 @<Show version and exit@>;
295 } else if (option_is("")) {
296 continue; /* ignore unknown options */
308 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
310 " Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
311 " MPNAME.tfm), where NNN are the character numbers generated.\n"
312 " Any remaining COMMANDS are processed as MetaPost input,\n"
313 " after MPNAME is read.\n\n");
315 " If no arguments or options are specified, prompt for input.\n"
317 " -ini be inimpost, for dumping mems\n"
318 " -interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n"
319 " scrollmode/errorstopmode)\n"
320 " -jobname=STRING set the job name to STRING\n"
321 " -progname=STRING set program (and mem) name to STRING\n");
323 " -tex=TEXPROGRAM use TEXPROGRAM for text labels\n"
324 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
325 " the bits of NUMBER\n"
326 " -mem=MEMNAME use MEMNAME instead of program name or a %%& line\n"
327 " -troff set the prologues variable, use `makempx -troff'\n"
328 " -help display this help and exit\n"
329 " -version output version information and exit\n"
331 "Email bug reports to mp-implementors@@tug.org.\n"
341 "MetaPost %s (CWeb version %s)\n"
342 "Copyright 2008 AT&T Bell Laboratories.\n"
343 "There is NO warranty. Redistribution of this software is\n"
344 "covered by the terms of both the MetaPost copyright and\n"
345 "the Lesser GNU General Public License.\n"
346 "For more information about these matters, see the file\n"
347 "named COPYING and the MetaPost source.\n"
348 "Primary author of MetaPost: John Hobby.\n"
349 "Current maintainer of MetaPost: Taco Hoekwater.\n"
350 "\n", mp_metapost_version(mp), mp_mplib_version(mp));
354 @ The final part of the command line, after option processing, is
355 stored in the \MP\ instance, this will be taken as the first line of
358 @d command_line_size 256
360 @<Copy the rest of the command line@>=
362 options->command_line = xmalloc(command_line_size);
363 strcpy(options->command_line,"");
369 if (k<(command_line_size-1)) {
370 options->command_line[k++] = *c;
374 options->command_line[k++] = ' ';
377 if (options->command_line[(k-1)] == ' ')
382 options->command_line[k] = 0;
386 @ A simple function to get numerical |texmf.cnf| values
388 int setup_var (int def, const char *var_name, int nokpse) {
390 char * expansion = kpse_var_value (var_name);
392 int conf_val = atoi (expansion);
403 @ Now this is really it: \MP\ starts and ends here.
405 @d xfree(A) if (A!=NULL) free(A)
408 int main (int argc, char **argv) { /* |start_here| */
409 int a=0; /* argc counter */
410 int k; /* index into buffer */
411 int history; /* the exit status */
412 MP mp; /* a metapost instance */
413 struct MP_options * options; /* instance options */
414 int nokpse = 0; /* switch to {\it not} enable kpse */
415 char *user_progname = NULL; /* If the user overrides argv[0] with -progname. */
416 options = mp_options();
417 options->ini_version = false;
418 options->print_found_names = true;
419 @<Read and set commmand line options@>;
421 kpse_set_program_name("mpost",user_progname);
422 if(putenv((char *)"engine=newmetapost"))
423 fprintf(stdout,"warning: could not set up $engine\n");
424 options->main_memory = setup_var (50000,"main_memory",nokpse);
425 options->hash_size = setup_var (9500,"hash_size",nokpse);
426 options->hash_prime = 7919;
427 options->max_in_open = setup_var (25,"max_in_open",nokpse);
428 options->param_size = setup_var (1500,"param_size",nokpse);
429 options->error_line = setup_var (79,"error_line",nokpse);
430 options->half_error_line = setup_var (50,"half_error_line",nokpse);
431 options->max_print_line = setup_var (100,"max_print_line",nokpse);
432 @<Copy the rest of the command line@>;
433 @<Register the callback routines@>;
434 mp = mp_new(options);
435 xfree(options->command_line);
436 xfree(options->mem_name);
437 xfree(options->job_name);
441 history = mp_initialize(mp);
444 history = mp_run(mp);