fix copyright statements
[mplib] / src / texk / web2c / mpdir / mpost.w
1 % $Id$
2 %
3 % Copyright 2008 Taco Hoekwater.
4 %
5 % This program is free software: you can redistribute it and/or modify
6 % it under the terms of the GNU General Public License as published by
7 % the Free Software Foundation, either version 2 of the License, or
8 % (at your option) any later version.
9 %
10 % This program is distributed in the hope that it will be useful,
11 % but WITHOUT ANY WARRANTY; without even the implied warranty of
12 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 % GNU General Public License for more details.
14 %
15 % You should have received a copy of the GNU General Public License
16 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 \font\tenlogo=logo10 % font used for the METAFONT logo
19 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
20
21 \def\title{MetaPost executable}
22 \def\[#1]{#1.}
23 \pdfoutput=1
24
25 @* \[1] Metapost executable.
26
27 Now that all of \MP\ is a library, a separate program is needed to 
28 have our customary command-line interface. 
29
30 @ First, here are the C includes. |avl.h| is needed because of an 
31 |avl_allocator| that is defined in |mplib.h|
32
33 @d true 1
34 @d false 0
35  
36 @c
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41 #include <mplib.h>
42 #include <mpxout.h>
43 #ifdef WIN32
44 #include <process.h>
45 #endif
46 #include <kpathsea/kpathsea.h>
47 static const char *mpost_tex_program = "";
48 static int debug = 0; /* debugging for makempx */
49
50 @ Allocating a bit of memory, with error detection:
51
52 @c
53 void  *xmalloc (size_t bytes) {
54   void *w = malloc (bytes);
55   if (w==NULL) {
56     fprintf(stderr,"Out of memory!\n");
57     exit(EXIT_FAILURE);
58   }
59   return w;
60 }
61 char *xstrdup(const char *s) {
62   char *w; 
63   if (s==NULL) return NULL;
64   w = strdup(s);
65   if (w==NULL) {
66     fprintf(stderr,"Out of memory!\n");
67     exit(EXIT_FAILURE);
68   }
69   return w;
70 }
71
72
73 @ @c
74 void mpost_run_editor (MP mp, char *fname, int fline) {
75   char *temp, *command, *edit_value;
76   char c;
77   int sdone, ddone;
78   sdone = ddone = 0;
79   edit_value = kpse_var_value ("MPEDIT");
80   if (edit_value == NULL)
81     edit_value = getenv("EDITOR");
82   if (edit_value == NULL) {
83     fprintf (stderr,"call_edit: can't find a suitable MPEDIT or EDITOR variable\n");
84     exit(mp_status(mp));    
85   }
86   command = (string) xmalloc (strlen (edit_value) + strlen(fname) + 11 + 3);
87   temp = command;
88   while ((c = *edit_value++) != 0) {
89       if (c == '%')   {
90         switch (c = *edit_value++) {
91             case 'd':
92               if (ddone) {
93             fprintf (stderr,"call_edit: `%%d' appears twice in editor command\n");
94             exit(EXIT_FAILURE);  
95           }
96           sprintf (temp, "%d", fline);
97           while (*temp != '\0')
98             temp++;
99           ddone = 1;
100           break;
101             case 's':
102           if (sdone) {
103             fprintf (stderr,"call_edit: `%%s' appears twice in editor command\n");
104             exit(EXIT_FAILURE);
105           }
106           while (*fname)
107                     *temp++ = *fname++;
108           *temp++ = '.';
109                   *temp++ = 'm';
110                   *temp++ = 'p';
111           sdone = 1;
112           break;
113             case '\0':
114           *temp++ = '%';
115           /* Back up to the null to force termination.  */
116               edit_value--;
117               break;
118             default:
119               *temp++ = '%';
120               *temp++ = c;
121               break;
122             }
123          } else {
124         *temp++ = c;
125      }
126    }
127   *temp = 0;
128   if (system (command) != 0)
129     fprintf (stderr, "! Trouble executing `%s'.\n", command);
130   exit(EXIT_FAILURE);
131 }
132
133
134 @<Register the callback routines@>=
135 options->run_editor = mpost_run_editor;
136
137 @
138 @c 
139 string normalize_quotes (const char *name, const char *mesg) {
140     int quoted = false;
141     int must_quote = (strchr(name, ' ') != NULL);
142     /* Leave room for quotes and NUL. */
143     string ret = (string)xmalloc(strlen(name)+3);
144     string p;
145     const_string q;
146     p = ret;
147     if (must_quote)
148         *p++ = '"';
149     for (q = name; *q; q++) {
150         if (*q == '"')
151             quoted = !quoted;
152         else
153             *p++ = *q;
154     }
155     if (must_quote)
156         *p++ = '"';
157     *p = '\0';
158     if (quoted) {
159         fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
160         exit(EXIT_FAILURE);
161     }
162     return ret;
163 }
164
165 @ @c 
166 static char *makempx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
167   (void) mpx;
168   int format, req;
169   if (mode[0] != 'r') { 
170      return strdup(nam);
171   }
172   req = 1;
173   switch(ftype) {
174   case mpx_tfm_format:       format = kpse_tfm_format; break;
175   case mpx_vf_format:        format = kpse_vf_format; req = 0; break;
176   case mpx_trfontmap_format: format = kpse_mpsupport_format; break;
177   case mpx_trcharadj_format: format = kpse_mpsupport_format; break;
178   case mpx_desc_format:      format = kpse_troff_font_format; break;
179   case mpx_fontdesc_format:  format =  kpse_troff_font_format; break;
180   case mpx_specchar_format:  format =  kpse_mpsupport_format; break;
181   default:                   return NULL;  break;
182   }
183   return  kpse_find_file (nam, format, req);
184 }
185
186 @ Invoke makempx (or troffmpx) to make sure there is an up-to-date
187    .mpx file for a given .mp file.  (Original from John Hobby 3/14/90) 
188
189 @d default_args " --parse-first-line --interaction=nonstopmode"
190 @d TEX     "tex"
191 @d TROFF   "soelim | eqn -Tps -d$$ | troff -Tps"
192
193 @c
194 #ifndef MPXCOMMAND
195 #define MPXCOMMAND "makempx"
196 #endif
197 int mpost_run_make_mpx (MP mp, char *mpname, char *mpxname) {
198   int ret;
199   string cnf_cmd = kpse_var_value ("MPXCOMMAND");
200   
201   if (cnf_cmd && (strcmp (cnf_cmd, "0")==0)) {
202     /* If they turned off this feature, just return success.  */
203     ret = 0;
204
205   } else {
206     /* We will invoke something. Compile-time default if nothing else.  */
207     string cmd;
208     string qmpname = normalize_quotes(mpname, "mpname");
209     string qmpxname = normalize_quotes(mpxname, "mpxname");
210     if (cnf_cmd) {
211       if (mp_troff_mode(mp))
212         cmd = concatn (cnf_cmd, " -troff ",
213                      qmpname, " ", qmpxname, NULL);
214       else if (mpost_tex_program && *mpost_tex_program)
215         cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ",
216                      qmpname, " ", qmpxname, NULL);
217       else
218         cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
219   
220       /* Run it.  */
221       ret = system (cmd);
222       free (cmd);
223     } else {
224       makempx_options * mpxopt;
225       const char *mpversion = mp_metapost_version () ;
226       mpxopt = xmalloc(sizeof(makempx_options));
227       char *s = NULL;
228       char *maincmd = NULL;
229       int mpxmode = mp_troff_mode(mp);
230       if (mpost_tex_program && *mpost_tex_program) {
231         maincmd = xstrdup(mpost_tex_program);
232       } else {
233         if (mpxmode == mpx_tex_mode) {
234           s = kpse_var_value("TEX");
235           if (!s) s = kpse_var_value("MPXMAINCMD");
236           if (!s) s = xstrdup (TEX);
237           maincmd = (char *)xmalloc (strlen(s)+strlen(default_args)+1);
238           strcpy(maincmd,s);
239           strcat(maincmd,default_args);
240           free(s);
241         } else {
242           s = kpse_var_value("TROFF");
243           if (!s) s = kpse_var_value("MPXMAINCMD");
244           if (!s) s = xstrdup (TROFF);
245           maincmd = s;
246         }
247       }
248       mpxopt->mode = mpxmode;
249       mpxopt->cmd  = maincmd;
250       mpxopt->mptexpre = kpse_var_value("MPTEXPRE");
251       mpxopt->mpname = qmpname;
252       mpxopt->mpxname = qmpxname;
253       mpxopt->debug = debug;
254       mpxopt->find_file = makempx_find_file;
255       {
256         char *banner = "% Written by metapost version ";
257         mpxopt->banner = xmalloc(strlen(mpversion)+strlen(banner)+1);
258         strcpy (mpxopt->banner, banner);
259         strcat (mpxopt->banner, mpversion);
260       }
261       ret = mp_makempx(mpxopt);
262       free(mpxopt->cmd);
263       free(mpxopt->mptexpre);
264       free(mpxopt);
265     }
266     free (qmpname);
267     free (qmpxname);
268   }
269
270   free (cnf_cmd);
271   return ret == 0;
272 }
273
274
275 @<Register the callback routines@>=
276 if (!nokpse)
277   options->run_make_mpx = mpost_run_make_mpx;
278
279
280 @ @c 
281 static int get_random_seed (void) {
282   int ret ;
283 #if defined (HAVE_GETTIMEOFDAY)
284   struct timeval tv;
285   gettimeofday(&tv, NULL);
286   ret = (tv.tv_usec + 1000000 * tv.tv_usec);
287 #elif defined (HAVE_FTIME)
288   struct timeb tb;
289   ftime(&tb);
290   ret = (tb.millitm + 1000 * tb.time);
291 #else
292   time_t clock = time ((time_t*)NULL);
293   struct tm *tmptr = localtime(&clock);
294   ret = (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
295 #endif
296   return ret;
297 }
298
299 @ @<Register the callback routines@>=
300 options->random_seed = get_random_seed();
301
302 @ @c 
303 char *mpost_find_file(MP mp, const char *fname, const char *fmode, int ftype)  {
304   int l ;
305   char *s = NULL;
306   (void)mp;
307   if (fmode[0]=='r') {
308         if (ftype>=mp_filetype_text) {
309       s = kpse_find_file (fname, kpse_mp_format, 0); 
310     } else {
311     switch(ftype) {
312     case mp_filetype_program: 
313       l = strlen(fname);
314           if (l>3 && strcmp(fname+l-3,".mf")==0) {
315             s = kpse_find_file (fname, kpse_mf_format, 0); 
316       } else {
317             s = kpse_find_file (fname, kpse_mp_format, 0); 
318       }
319       break;
320     case mp_filetype_memfile: 
321       s = kpse_find_file (fname, kpse_mem_format, 0); 
322       break;
323     case mp_filetype_metrics: 
324       s = kpse_find_file (fname, kpse_tfm_format, 0); 
325       break;
326     case mp_filetype_fontmap: 
327       s = kpse_find_file (fname, kpse_fontmap_format, 0); 
328       break;
329     case mp_filetype_font: 
330       s = kpse_find_file (fname, kpse_type1_format, 0); 
331       break;
332     case mp_filetype_encoding: 
333       s = kpse_find_file (fname, kpse_enc_format, 0); 
334       break;
335     }
336     }
337   } else {
338     s = xstrdup(fname); /* when writing */
339   }
340   return s;
341 }
342
343 @  @<Register the callback routines@>=
344 if (!nokpse)
345   options->find_file = mpost_find_file;
346
347 @ @c 
348 void *mpost_open_file(MP mp, const char *fname, const char *fmode, int ftype)  {
349   char realmode[3];
350   char *s;
351   if (ftype==mp_filetype_terminal) {
352     return (fmode[0] == 'r' ? stdin : stdout);
353   } else if (ftype==mp_filetype_error) {
354     return stderr;
355   } else { 
356     s = mpost_find_file (mp, fname, fmode, ftype);
357     if (s!=NULL) {
358       void *ret = NULL;
359       realmode[0] = *fmode;
360           realmode[1] = 'b';
361           realmode[2] = 0;
362       ret = fopen(s,realmode);
363       free(s);
364       return ret;
365     }
366   }
367   return NULL;
368 }
369
370 @  @<Register the callback routines@>=
371 if (!nokpse)
372   options->open_file = mpost_open_file;
373
374
375 @ At the moment, the command line is very simple.
376
377 @d option_is(A) ((strncmp(argv[a],"--" A, strlen(A)+2)==0) || 
378        (strncmp(argv[a],"-" A, strlen(A)+1)==0))
379 @d option_arg(B) (optarg && strncmp(optarg,B, strlen(B))==0)
380
381
382 @<Read and set command line options@>=
383 {
384   char *optarg;
385   boolean ini_version_test = false;
386   while (++a<argc) {
387     optarg = strstr(argv[a],"=") ;
388     if (optarg!=NULL) {
389       optarg++;
390       if (!*optarg)  optarg=NULL;
391     }
392     if (option_is("ini")) {
393       ini_version_test = true;
394     } else if (option_is("debug")) {
395       debug = 1;
396     } else if (option_is ("kpathsea-debug")) {
397       kpathsea_debug |= atoi (optarg);
398     } else if (option_is("mem")) {
399       options->mem_name = xstrdup(optarg);
400       if (!user_progname) 
401             user_progname = optarg;
402     } else if (option_is("jobname")) {
403       options->job_name = xstrdup(optarg);
404     } else if (option_is ("progname")) {
405       user_progname = optarg;
406     } else if (option_is("troff")) {
407       options->troff_mode = true;
408     } else if (option_is ("tex")) {
409       mpost_tex_program = optarg;
410     } else if (option_is("interaction")) {
411       if (option_arg("batchmode")) {
412         options->interaction = mp_batch_mode;
413       } else if (option_arg("nonstopmode")) {
414         options->interaction = mp_nonstop_mode;
415       } else if (option_arg("scrollmode")) {
416         options->interaction = mp_scroll_mode;
417       } else if (option_arg("errorstopmode")) {
418         options->interaction = mp_error_stop_mode;
419       } else {
420         fprintf(stdout,"unknown option argument %s\n", argv[a]);
421       }
422     } else if (option_is("no-kpathsea")) {
423       nokpse=1;
424     } else if (option_is("help")) {
425       @<Show help and exit@>;
426     } else if (option_is("version")) {
427       @<Show version and exit@>;
428     } else if (option_is("")) {
429       continue; /* ignore unknown options */
430     } else {
431       break;
432     }
433   }
434   options->ini_version = ini_version_test;
435 }
436
437
438 @<Show help...@>=
439 {
440 fprintf(stdout,
441 "\n"
442 "Usage: mpost [OPTION] [MPNAME[.mp]] [COMMANDS]\n"
443 "\n"
444 "  Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
445 "  MPNAME.tfm), where NNN are the character numbers generated.\n"
446 "  Any remaining COMMANDS are processed as MetaPost input,\n"
447 "  after MPNAME is read.\n\n");
448 fprintf(stdout,
449 "  If no arguments or options are specified, prompt for input.\n"
450 "\n"
451 "  -ini                    be inimpost, for dumping mems\n"
452 "  -interaction=STRING     set interaction mode (STRING=batchmode/nonstopmode/\n"
453 "                          scrollmode/errorstopmode)\n"
454 "  -jobname=STRING         set the job name to STRING\n"
455 "  -progname=STRING        set program (and mem) name to STRING\n");
456 fprintf(stdout,
457 "  -tex=TEXPROGRAM         use TEXPROGRAM for text labels\n"
458 "  -kpathsea-debug=NUMBER  set path searching debugging flags according to\n"
459 "                          the bits of NUMBER\n"
460 "  -mem=MEMNAME            use MEMNAME instead of program name or a %%& line\n"
461 "  -troff                  set the prologues variable, use `makempx -troff'\n"
462 "  -help                   display this help and exit\n"
463 "  -version                output version information and exit\n"
464 "\n"
465 "Email bug reports to mp-implementors@@tug.org.\n"
466 "\n");
467   exit(EXIT_SUCCESS);
468 }
469
470
471 @<Show version...@>=
472 {
473 fprintf(stdout, 
474 "\n"
475 "MetaPost %s\n"
476 "Copyright 2008 AT&T Bell Laboratories.\n"
477 "There is NO warranty.  Redistribution of this software is\n"
478 "covered by the terms of both the MetaPost copyright and\n"
479 "the Lesser GNU General Public License.\n"
480 "For more information about these matters, see the file\n"
481 "named COPYING and the MetaPost source.\n"
482 "Primary author of MetaPost: John Hobby.\n"
483 "Current maintainer of MetaPost: Taco Hoekwater.\n"
484 "\n", mp_metapost_version());
485   exit(EXIT_SUCCESS);
486 }
487
488 @ The final part of the command line, after option processing, is
489 stored in the \MP\ instance, this will be taken as the first line of
490 input.
491
492 @d command_line_size 256
493
494 @<Copy the rest of the command line@>=
495 {
496   options->command_line = xmalloc(command_line_size);
497   strcpy(options->command_line,"");
498   if (a<argc) {
499     k=0;
500     for(;a<argc;a++) {
501       char *c = argv[a];
502       while (*c) {
503             if (k<(command_line_size-1)) {
504           options->command_line[k++] = *c;
505         }
506         c++;
507       }
508       options->command_line[k++] = ' ';
509     }
510         while (k>0) {
511       if (options->command_line[(k-1)] == ' ') 
512         k--; 
513       else 
514         break;
515     }
516     options->command_line[k] = 0;
517   }
518 }
519
520 @ A simple function to get numerical |texmf.cnf| values
521 @c
522 int setup_var (int def, const char *var_name, int nokpse) {
523   if (!nokpse) {
524     char * expansion = kpse_var_value (var_name);
525     if (expansion) {
526       int conf_val = atoi (expansion);
527       free (expansion);
528       if (conf_val > 0) {
529         return conf_val;
530       }
531     }
532   }
533   return def;
534 }
535
536
537 @ Now this is really it: \MP\ starts and ends here.
538
539 @d xfree(A) if (A!=NULL) free(A)
540
541 @c 
542 int main (int argc, char **argv) { /* |start_here| */
543   int k; /* index into buffer */
544   int history; /* the exit status */
545   MP mp; /* a metapost instance */
546   struct MP_options * options; /* instance options */
547   int a=0; /* argc counter */
548   int nokpse = 0; /* switch to {\it not} enable kpse */
549   char *user_progname = NULL; /* If the user overrides argv[0] with -progname.  */
550   options = mp_options();
551   options->ini_version       = false;
552   options->print_found_names = true;
553   @<Read and set command line options@>;
554   if (!nokpse)
555     kpse_set_program_name("mpost",user_progname);  
556   if(putenv((char *)"engine=metapost"))
557     fprintf(stdout,"warning: could not set up $engine\n");
558   options->main_memory       = setup_var (50000,"main_memory",nokpse);
559   options->hash_size         = setup_var (16384,"hash_size",nokpse);
560   options->max_in_open       = setup_var (25,"max_in_open",nokpse);
561   options->param_size        = setup_var (1500,"param_size",nokpse);
562   options->error_line        = setup_var (79,"error_line",nokpse);
563   options->half_error_line   = setup_var (50,"half_error_line",nokpse);
564   options->max_print_line    = setup_var (100,"max_print_line",nokpse);
565   @<Copy the rest of the command line@>;
566   @<Register the callback routines@>;
567   mp = mp_initialize(options);
568   xfree(options->command_line);
569   xfree(options->mem_name);
570   xfree(options->job_name);
571   free(options);
572   if (mp==NULL)
573         exit(EXIT_FAILURE);
574   history = mp_status(mp);
575   if (history)
576         exit(history);
577   history = mp_run(mp);
578   (void)mp_finish(mp);
579   exit(history);
580 }
581