cleanup the fixed line length
[mplib] / src / texk / web2c / mpdir / mpost.w
1 % $Id: mpost.w $
2 % MetaPost command-line program, by Taco Hoekwater.  Public domain.
3
4 \font\tenlogo=logo10 % font used for the METAFONT logo
5 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
6
7 \def\title{MetaPost executable}
8 \def\[#1]{#1.}
9 \pdfoutput=1
10
11 @* \[1] Metapost executable.
12
13 Now that all of \MP\ is a library, a separate program is needed to 
14 have our customary command-line interface. 
15
16 @ First, here are the C includes. |avl.h| is needed because of an 
17 |avl_allocator| that is defined in |mplib.h|
18
19 @d true 1
20 @d false 0
21  
22 @c
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <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 = "";
35
36 @ Allocating a bit of memory, with error detection:
37
38 @c
39 void  *xmalloc (size_t bytes) {
40   void *w = malloc (bytes);
41   if (w==NULL) {
42     fprintf(stderr,"Out of memory!\n");
43     exit(EXIT_FAILURE);
44   }
45   return w;
46 }
47 char *xstrdup(const char *s) {
48   char *w; 
49   if (s==NULL) return NULL;
50   w = strdup(s);
51   if (w==NULL) {
52     fprintf(stderr,"Out of memory!\n");
53     exit(EXIT_FAILURE);
54   }
55   return w;
56 }
57
58
59
60 @c
61 void mpost_run_editor (MP mp, char *fname, int fline) {
62   if (mp)
63     fprintf(stdout,"Ok, bye (%s,%d)!",fname, fline);
64   exit(EXIT_SUCCESS);
65 }
66
67
68 @<Register the callback routines@>=
69 options->run_editor = mpost_run_editor;
70
71 @
72 @c 
73 string normalize_quotes (const char *name, const char *mesg) {
74     int quoted = false;
75     int must_quote = (strchr(name, ' ') != NULL);
76     /* Leave room for quotes and NUL. */
77     string ret = (string)xmalloc(strlen(name)+3);
78     string p;
79     const_string q;
80     p = ret;
81     if (must_quote)
82         *p++ = '"';
83     for (q = name; *q; q++) {
84         if (*q == '"')
85             quoted = !quoted;
86         else
87             *p++ = *q;
88     }
89     if (must_quote)
90         *p++ = '"';
91     *p = '\0';
92     if (quoted) {
93         fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
94         exit(EXIT_FAILURE);
95     }
96     return ret;
97 }
98
99
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) 
102
103 @c
104
105 #ifndef MPXCOMMAND
106 #define MPXCOMMAND "makempx"
107 #endif
108 int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) {
109   int ret;
110   string cnf_cmd = kpse_var_value ("MPXCOMMAND");
111   
112   if (cnf_cmd && (strcmp (cnf_cmd, "0")==0)) {
113     /* If they turned off this feature, just return success.  */
114     ret = 0;
115
116   } else {
117     /* We will invoke something. Compile-time default if nothing else.  */
118     string cmd;
119     string qmpname = normalize_quotes(mpname, "mpname");
120     string qmpxname = normalize_quotes(mpxname, "mpxname");
121     if (!cnf_cmd)
122       cnf_cmd = xstrdup (MPXCOMMAND);
123
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);
130     else
131       cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
132
133     /* Run it.  */
134     ret = system (cmd);
135     free (cmd);
136     free (qmpname);
137     free (qmpxname);
138   }
139
140   free (cnf_cmd);
141   return ret == 0;
142 }
143
144
145 @<Register the callback routines@>=
146 if (!nokpse)
147   options->run_make_mpx = mpost_run_make_mpx;
148
149
150 @ @c int mpost_get_random_seed (MP mp) {
151   int ret ;
152 #if defined (HAVE_GETTIMEOFDAY)
153   struct timeval tv;
154   gettimeofday(&tv, NULL);
155   ret = (tv.tv_usec + 1000000 * tv.tv_usec);
156 #elif defined (HAVE_FTIME)
157   struct timeb tb;
158   ftime(&tb);
159   ret = (tb.millitm + 1000 * tb.time);
160 #else
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));
164 #endif
165   return (mp ? ret : ret); /* for -W */
166 }
167
168 @ @<Register the callback routines@>=
169 options->get_random_seed = mpost_get_random_seed;
170
171 @ @c 
172 char *mpost_find_file(char *fname, char *fmode, int ftype)  {
173   char *s;
174   int l ;
175   if (fmode[0]=='r') {
176         if (ftype>=mp_filetype_text) {
177       s = kpse_find_file (fname, kpse_mp_format, 0); 
178     } else {
179     switch(ftype) {
180     case mp_filetype_program: 
181       l = strlen(fname);
182           if (l>3 && strcmp(fname+l-3,".mf")==0) {
183             s = kpse_find_file (fname, kpse_mf_format, 0); 
184       } else {
185             s = kpse_find_file (fname, kpse_mp_format, 0); 
186       }
187       break;
188     case mp_filetype_memfile: 
189       s = kpse_find_file (fname, kpse_mem_format, 0); 
190       break;
191     case mp_filetype_metrics: 
192       s = kpse_find_file (fname, kpse_tfm_format, 0); 
193       break;
194     case mp_filetype_fontmap: 
195       s = kpse_find_file (fname, kpse_fontmap_format, 0); 
196       break;
197     case mp_filetype_font: 
198       s = kpse_find_file (fname, kpse_type1_format, 0); 
199       break;
200     case mp_filetype_encoding: 
201       s = kpse_find_file (fname, kpse_enc_format, 0); 
202       break;
203     }
204     }
205   } else {
206     s = xstrdup(fname); /* when writing */
207   }
208   return s;
209 }
210
211 @  @<Register the callback routines@>=
212 if (!nokpse)
213   options->find_file = mpost_find_file;
214
215 @ @c 
216 void *mpost_open_file(char *fname, char *fmode, int ftype)  {
217   char *s;
218   if (ftype==mp_filetype_terminal) {
219     return (fmode[0] == 'r' ? stdin : stdout);
220   } else if (ftype==mp_filetype_error) {
221     return stderr;
222   } else { 
223     s = mpost_find_file (fname, fmode, ftype);
224     if (s!=NULL) {
225       return fopen(s,fmode);
226     }
227   }
228   return NULL;
229 }
230
231 @  @<Register the callback routines@>=
232 if (!nokpse)
233   options->open_file = mpost_open_file;
234
235
236 @ At the moment, the command line is very simple.
237
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)
241
242
243 @<Read and set commmand line options@>=
244 {
245   char *optarg;
246   while (++a<argc) {
247     optarg = strstr(argv[a],"=") ;
248     if (optarg!=NULL) {
249       optarg++;
250       if (!*optarg)  optarg=NULL;
251     }
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);
258       if (!user_progname) 
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;
277       } else {
278         fprintf(stdout,"unknown option argument %s\n", argv[a]);
279       }
280     } else if (option_is("no-kpathsea")) {
281       nokpse=1;
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 */
288     } else {
289       break;
290     }
291   }
292 }
293
294
295 @<Show help...@>=
296 {
297 fprintf(stdout,
298 "\n"
299 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
300 "\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"
305 "\n"
306 "  If no arguments or options are specified, prompt for input.\n"
307 "\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"
320 "\n"
321 "Email bug reports to mp-implementors@@tug.org.\n"
322 "\n");
323   exit(EXIT_SUCCESS);
324 }
325
326
327 @<Show version...@>=
328 {
329 fprintf(stdout,
330 "\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));
341   exit(EXIT_SUCCESS);
342 }
343
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
346 input.
347
348 @d command_line_size 256
349
350 @<Copy the rest of the command line@>=
351 {
352   options->command_line = xmalloc(command_line_size);
353   strcpy(options->command_line,"");
354   if (a<argc) {
355     k=0;
356     for(;a<argc;a++) {
357       char *c = argv[a];
358       while (*c) {
359             if (k<(command_line_size-1)) {
360           options->command_line[k++] = *c;
361         }
362         c++;
363       }
364       options->command_line[k++] = ' ';
365     }
366         while (k>0) {
367       if (options->command_line[(k-1)] == ' ') 
368         k--; 
369       else 
370         break;
371     }
372     options->command_line[k] = 0;
373   }
374 }
375
376 @ A simple function to get numerical |texmf.cnf| values
377 @c
378 int setup_var (int def, char *var_name, int nokpse) {
379   if (!nokpse) {
380     char * expansion = kpse_var_value (var_name);
381     if (expansion) {
382       int conf_val = atoi (expansion);
383       free (expansion);
384       if (conf_val > 0) {
385         return conf_val;
386       }
387     }
388   }
389   return def;
390 }
391
392
393 @ Now this is really it: \MP\ starts and ends here.
394
395 @c 
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@>;
408   if (!nokpse)
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);
424   if (mp==NULL)
425         exit(EXIT_FAILURE);
426   history = mp_initialize(mp);
427   if (history) 
428     exit(history);
429   history = mp_run(mp);
430   mp_free(mp);
431   exit(history);
432 }
433