1 % This file is part of MetaPost
;
2 % the MetaPost program is in the public domain.
3 % See the
<Show version...
> code below for more info.
5 \font\tenlogo
=logo10
% font used for the METAFONT logo
6 \def\MP
{{\tenlogo META
}\
-{\tenlogo POST
}}
8 \def\title
{MetaPost executable
}
14 Now that all of \MP\ is a library
, a separate program is needed to
15 have our customary command-line interface.
17 @ First
, here are the C includes. |avl.h| is needed because of an
18 |avl_allocator| that is defined in |mplib.h|
24 #include
<w2c
/config.h
>
28 #if defined
(HAVE_SYS_TIME_H
)
30 #elif defined
(HAVE_SYS_TIMEB_H
)
31 #include
<sys
/timeb.h
>
33 #include
<time.h
> /* For `struct tm'. Moved here for Visual Studio
2005.
*/
39 #include
<kpathsea
/kpathsea.h
>
40 @
= /*@@null@@
*/ @
> static char
*mpost_tex_program
= NULL;
41 static int debug
= 0; /* debugging for makempx
*/
42 static int nokpse
= 0;
43 static boolean recorder_enabled
= false
;
44 static string recorder_name
= NULL;
45 static
FILE *recorder_file
= NULL;
46 static char
*job_name
= NULL;
47 static char
*job_area
= NULL;
48 static int dvitomp_only
= 0;
49 static int ini_version_test
= false
;
50 string output_directory
; /* Defaults to
NULL.
*/
52 @
<getopt structures@
>;
55 @ Allocating a bit of memory
, with error detection
:
57 @d mpost_xfree
(A
) do
{ if
(A
!=NULL) free
(A
); A
=NULL; } while
(0)
60 @
= /*@@only@@
*/ /*@@out@@
*/ @
> static void
*mpost_xmalloc
(size_t bytes
) {
61 void
*w
= malloc
(bytes
);
63 fprintf
(stderr
,"Out of memory!\n");
68 @
= /*@@only@@
*/ @
> static char
*mpost_xstrdup
(const char
*s
) {
72 fprintf
(stderr
,"Out of memory!\n");
77 static char
*mpost_itoa
(int i
) {
80 unsigned v
= (unsigned
)abs
(i
);
81 memset
(res
,0,32*sizeof
(char
));
83 char d
= (char
)(v
% 10);
87 res
[idx--
] = (char
)v
+ '
0'
;
91 return mpost_xstrdup
(res
+idx
+1);
100 return
(c
== ' ' || c
== '\t'
);
103 static void mpost_run_editor
(MP mp
, char
*fname
, int fline
) {
104 char
*temp
, *command
, *fullcmd
, *edit_value
;
106 boolean sdone
, ddone
;
109 char
*fp
, *ffp
, *env
, editorname
[256], buffer
[256];
114 sdone
= ddone
= false
;
115 edit_value
= kpse_var_value
("MPEDIT");
116 if
(edit_value
== NULL)
117 edit_value
= getenv
("EDITOR");
118 if
(edit_value
== NULL) {
119 fprintf
(stderr
,"call_edit: can't find a suitable MPEDIT or EDITOR variable\n");
122 command
= (string
) mpost_xmalloc
(strlen
(edit_value
) + strlen
(fname
) + 11 + 3);
127 if
((isalpha
(*edit_value
) && *(edit_value + 1) == ':'
128 && IS_DIR_SEP (*(edit_value + 2)))
129 ||
(*edit_value
== '
"' && isalpha(*(edit_value + 1))
130 && *(edit_value + 2) == ':'
131 && IS_DIR_SEP (*(edit_value + 3)))
136 while ((c = *edit_value++) != (char)0) {
138 switch (c = *edit_value++) {
141 fprintf (stderr,"call_edit
: `
%%d' appears twice in editor command\n
");
144 char *s = mpost_itoa(fline);
156 fprintf (stderr,"call_edit
: `
%%s' appears twice in editor command\n
");
159 while (*fname != '\0')
169 /* Back up to the null to force termination. */
181 else { if(Isspace(c) && cnt == 0) {
186 } else if(!Isspace(c) && cnt == 0) {
200 if (dontchange == 0) {
201 if(editorname[0] == '.' ||
202 editorname[0] == '/' ||
203 editorname[0] == '\\') {
204 fprintf(stderr, "%s is not allowed to execute.\n
", editorname);
207 env = (char *)getenv("PATH
");
208 if(SearchPath(env, editorname, ".exe
", 256, buffer, &ffp)==0) {
209 if(SearchPath(env, editorname, ".bat
", 256, buffer, &ffp)==0) {
210 fprintf(stderr, "I cannot find
%s in the PATH.\n
", editorname);
214 fullcmd = mpost_xmalloc(strlen(buffer)+strlen(command)+5);
215 strcpy(fullcmd, "\
"");
216 strcat
(fullcmd
, buffer
);
217 strcat
(fullcmd
, "\"");
218 strcat(fullcmd, command);
223 if (system (fullcmd) != 0)
224 fprintf (stderr, "! Trouble executing `
%s'.\n
", command);
229 @<Register the callback routines@>=
230 options->run_editor = mpost_run_editor;
234 static string normalize_quotes (const char *name, const char *mesg) {
235 boolean quoted = false;
236 boolean must_quote = (strchr(name, ' ') != NULL);
237 /* Leave room for quotes and NUL. */
238 string ret = (string)mpost_xmalloc(strlen(name)+3);
244 for
(q
= name
; *q
!= '\
0'
; q
++) {
254 fprintf
(stderr
, "! Unbalanced quotes in %s %s\n", mesg
, name
);
260 @ Helpers for the filename recorder.
263 void recorder_start
(char
*jobname
);
266 void recorder_start
(char
*jobname
) {
269 recorder_name
= mpost_xstrdup
("mpout.fls");
271 recorder_name
= (string
)xmalloc
((unsigned int
)(strlen
(jobname
)+5));
272 strcpy
(recorder_name
, jobname
);
273 strcat
(recorder_name
, ".fls");
275 recorder_file
= xfopen
(recorder_name
, FOPEN_W_MODE
);
277 if
(getcwd
(cwd
,1020) != NULL) {
280 for
(p
= cwd
; *p
; p
++) {
283 else if
(IS_KANJI
(p
))
287 fprintf
(recorder_file
, "PWD %s\n", cwd
);
289 fprintf
(recorder_file
, "PWD <unknown>\n");
295 @
= /*@@null@@
*/ @
> static char
*makempx_find_file
(MPX mpx
, const char
*nam
,
296 const char
*mode
, int ftype
) {
300 if
((mode
[0]=='r'
&& !kpse_in_name_ok(nam)) ||
301 (mode
[0]=='w'
&& !kpse_out_name_ok(nam)))
302 return
NULL; /* disallowed filename
*/
303 if
(mode
[0] != 'r'
) {
306 req
= true
; fmt
= -1;
308 case mpx_tfm_format
: fmt
= kpse_tfm_format
; break
;
309 case mpx_vf_format
: fmt
= kpse_vf_format
; req
= false
; break
;
310 case mpx_trfontmap_format
: fmt
= kpse_mpsupport_format
; break
;
311 case mpx_trcharadj_format
: fmt
= kpse_mpsupport_format
; break
;
312 case mpx_desc_format
: fmt
= kpse_troff_font_format
; break
;
313 case mpx_fontdesc_format
: fmt
= kpse_troff_font_format
; break
;
314 case mpx_specchar_format
: fmt
= kpse_mpsupport_format
; break
;
316 if
(fmt
<0) return
NULL;
317 return kpse_find_file
(nam
, fmt
, req
);
320 @ Invoke
{\tt makempx
} (or
{\tt troffmpx
}) to make sure there is an
321 up-to-date
{\tt .mpx
} file for a given
{\tt .mp
} file.
(Original
322 from John Hobby
3/14/90)
324 @d default_args
" --parse-first-line --interaction=nonstopmode"
326 @d TROFF
"soelim | eqn -Tps -d$$ | troff -Tps"
330 #define MPXCOMMAND
"makempx"
332 static int mpost_run_make_mpx
(MP mp
, char
*mpname
, char
*mpxname
) {
334 char
*cnf_cmd
= kpse_var_value
("MPXCOMMAND");
335 if
(cnf_cmd
!= NULL && (strcmp (cnf_cmd, "0")==0)) {
336 /* If they turned off this feature
, just return success.
*/
339 /* We will invoke something. Compile-time default if nothing else.
*/
340 char
*cmd
, *tmp
, *qmpname
, *qmpxname
;
341 if
(job_area
!= NULL) {
342 char
*l
= mpost_xmalloc
(strlen
(mpname
)+strlen
(job_area
)+1);
345 tmp
= normalize_quotes
(l
, "mpname");
348 tmp
= normalize_quotes
(mpname
, "mpname");
350 if
(!kpse_in_name_ok
(tmp
))
351 return
0; /* disallowed filename
*/
352 qmpname
= kpse_find_file
(tmp
,kpse_mp_format
, true
);
354 if
(qmpname
!= NULL && job_area != NULL) {
355 /* if there is a usable mpx file in the source path already
,
356 simply use that and return true
*/
357 char
*l
= mpost_xmalloc
(strlen
(qmpname
)+2);
363 struct stat source_stat
, target_stat
;
365 if
((stat
(qmpxname
, &target_stat) >= 0) &&
366 (stat
(qmpname
, &source_stat) >= 0)) {
368 if
(source_stat.st_mtim.tv_sec
< target_stat.st_mtim.tv_sec ||
369 (source_stat.st_mtim.tv_sec
== target_stat.st_mtim.tv_sec
&&
370 source_stat.st_mtim.tv_nsec
< target_stat.st_mtim.tv_nsec
))
373 if
(source_stat.st_mtime
< target_stat.st_mtime
)
377 if
(nothingtodo
== 1)
378 return
1; /* success
! */
382 qmpxname
= normalize_quotes
(mpxname
, "mpxname");
383 if
(cnf_cmd
!=NULL && (strcmp (cnf_cmd, "1")!=0)) {
384 if
(mp_troff_mode
(mp
)!=0)
385 cmd
= concatn
(cnf_cmd
, " -troff ",
386 qmpname
, " ", qmpxname
, NULL);
387 else if
(mpost_tex_program
!=NULL && *mpost_tex_program != '\0')
388 cmd
= concatn
(cnf_cmd
, " -tex=", mpost_tex_program
, " ",
389 qmpname
, " ", qmpxname
, NULL);
391 cmd
= concatn
(cnf_cmd
, " -tex ", qmpname
, " ", qmpxname
, NULL);
396 mpost_xfree
(qmpname
);
397 mpost_xfree
(qmpxname
);
399 mpx_options
* mpxopt
;
401 char
*maincmd
= NULL;
402 int mpxmode
= mp_troff_mode
(mp
);
403 char
*mpversion
= mp_metapost_version
() ;
404 mpxopt
= mpost_xmalloc
(sizeof
(mpx_options
));
405 if
(mpost_tex_program
!= NULL && *mpost_tex_program != '\0') {
406 maincmd
= mpost_xstrdup
(mpost_tex_program
);
408 if
(mpxmode
== mpx_tex_mode
) {
409 s
= kpse_var_value
("TEX");
410 if
(s
==NULL) s
= kpse_var_value
("MPXMAINCMD");
411 if
(s
==NULL) s
= mpost_xstrdup
(TEX
);
412 maincmd
= (char
*)mpost_xmalloc
(strlen
(s
)+strlen
(default_args
)+1);
414 strcat
(maincmd
,default_args
);
417 s
= kpse_var_value
("TROFF");
418 if
(s
==NULL) s
= kpse_var_value
("MPXMAINCMD");
419 if
(s
==NULL) s
= mpost_xstrdup
(TROFF
);
423 mpxopt-
>mode
= mpxmode
;
424 mpxopt-
>cmd
= maincmd
;
425 mpxopt-
>mptexpre
= kpse_var_value
("MPTEXPRE");
426 mpxopt-
>debug
= debug
;
427 mpxopt-
>mpname
= qmpname
;
428 mpxopt-
>mpxname
= qmpxname
;
429 mpxopt-
>find_file
= makempx_find_file
;
431 const char
*banner
= "% Written by metapost version ";
432 mpxopt-
>banner
= mpost_xmalloc
(strlen
(mpversion
)+strlen
(banner
)+1);
433 strcpy
(mpxopt-
>banner
, banner
);
434 strcat
(mpxopt-
>banner
, mpversion
);
436 ret
= mpx_makempx
(mpxopt
);
437 mpost_xfree
(mpxopt-
>cmd
);
438 mpost_xfree
(mpxopt-
>mptexpre
);
439 mpost_xfree
(mpxopt-
>banner
);
440 mpost_xfree
(mpxopt-
>mpname
);
441 mpost_xfree
(mpxopt-
>mpxname
);
443 mpost_xfree
(mpversion
);
447 mpost_xfree
(cnf_cmd
);
448 return
(int
)(ret
== 0);
451 static int mpost_run_dvitomp
(char
*dviname
, char
*mpxname
) {
455 mpx_options
* mpxopt
;
456 char
*mpversion
= mp_metapost_version
() ;
457 mpxopt
= mpost_xmalloc
(sizeof
(mpx_options
));
458 memset
(mpxopt
,0,sizeof
(mpx_options
));
459 mpxopt-
>mode
= mpx_tex_mode
;
464 m
= mpost_xstrdup
(dviname
);
465 if
(i
>4 && *(m+i-4)=='.'
466 && *(m+i-3)=='d' && *(m+i-2)=='v' && *(m+i-1)=='i')
469 m
= mpost_xstrdup
(mpxname
);
471 d
= mpost_xstrdup
(dviname
);
472 if
(!(i
>4 && *(d+i-4)=='.'
473 && *(d+i-3)=='d' && *(d+i-2)=='v' && *(d+i-1)=='i')) {
474 char
*s
= malloc
(i
+5);
477 (void
)strcat
(s
+i-1
, ".dvi");
483 if
(i
>4 && *(m+i-4)=='.'
484 && *(m+i-3)=='m' && *(m+i-2)=='p' && *(m+i-1)=='x') {
486 char
*s
= malloc
(i
+5);
489 (void
)strcat
(s
+i-1
, ".mpx");
493 if
(!(kpse_in_name_ok
(d
) && kpse_out_name_ok(m)))
494 return EXIT_FAILURE
; /* disallowed filename
*/
498 mpxopt-
>find_file
= makempx_find_file
;
500 const char
*banner
= "% Written by dvitomp version ";
501 mpxopt-
>banner
= mpost_xmalloc
(strlen
(mpversion
)+strlen
(banner
)+1);
502 strcpy
(mpxopt-
>banner
, banner
);
503 strcat
(mpxopt-
>banner
, mpversion
);
505 ret
= mpx_run_dvitomp
(mpxopt
);
506 mpost_xfree
(mpxopt-
>banner
);
508 mpost_xfree
(mpversion
);
509 puts
(""); /* nicer in case of error
*/
515 @
<Register the callback routines@
>=
517 options-
>run_make_mpx
= mpost_run_make_mpx
;
521 static int get_random_seed
(void
) {
523 #if defined
(HAVE_GETTIMEOFDAY
)
525 gettimeofday
(&tv, NULL);
526 ret
= (int
)(tv.tv_usec
+ 1000000 * tv.tv_usec
);
527 #elif defined
(HAVE_FTIME
)
530 ret
= (tb.millitm
+ 1000 * tb.time
);
532 time_t clock
= time
((time_t
*)NULL);
533 struct tm
*tmptr
= localtime
(&clock);
535 ret
= (tmptr-
>tm_sec
+ 60*(tmptr-
>tm_min
+ 60*tmptr-
>tm_hour
));
540 @ @
<Register the callback routines@
>=
541 options-
>random_seed
= get_random_seed
();
544 @ Handle
-output-directory.
547 static char
*mpost_find_in_output_directory
(const char
*s
,const char
*fmode
)
549 if
(output_directory
&& !kpse_absolute_p(s, false)) {
550 char
*ftemp
= concat3
(output_directory
, DIR_SEP_STRING
, s
);
559 static char
*mpost_find_file
(MP mp
, const char
*fname
, const char
*fmode
, int ftype
) {
568 if
(fname
== NULL ||
(fmode
[0]=='r'
&& !kpse_in_name_ok(fname)) )
569 return
NULL; /* disallowed filename
*/
573 if
(output_directory
) {
574 ofname
= mpost_find_in_output_directory
(fname
,fmode
);
575 if
(ofname
== NULL ||
(fmode
[0]=='w'
&& !kpse_out_name_ok(ofname))) {
577 return
NULL; /* disallowed filename
*/
580 if
(!kpse_out_name_ok
(fname
))
581 return
NULL; /* disallowed filename
*/
587 if
((job_area
!= NULL) &&
588 (ftype
>=mp_filetype_text || ftype
==mp_filetype_program
)) {
589 char
*f
= mpost_xmalloc
(strlen
(job_area
)+strlen
(fname
)+1);
592 if
(ftype
>=mp_filetype_text
) {
593 s
= kpse_find_file
(f
, kpse_mp_format
, 0);
596 if
(l
>3 && strcmp(f+l-3,".mf")==0) {
597 s
= kpse_find_file
(f
,kpse_mf_format
, 0);
599 } else if
(l
>4 && strcmp(f+l-4,".mpx")==0) {
600 struct stat source_stat
, target_stat
;
601 char
*mpname
= mpost_xstrdup
(f
);
602 *(mpname
+ strlen
(mpname
) -1 ) = '\
0'
;
603 /* printf
("statting %s and %s\n", mpname
, f
); */
604 if
((stat
(f
, &target_stat) >= 0) &&
605 (stat
(mpname
, &source_stat) >= 0)) {
607 if
(source_stat.st_mtim.tv_sec
<= target_stat.st_mtim.tv_sec ||
608 (source_stat.st_mtim.tv_sec
== target_stat.st_mtim.tv_sec
&&
609 source_stat.st_mtim.tv_nsec
<= target_stat.st_mtim.tv_nsec
))
610 s
= mpost_xstrdup
(f
);
612 if
(source_stat.st_mtime
<= target_stat.st_mtime
)
613 s
= mpost_xstrdup
(f
);
619 s
= kpse_find_file
(f
,kpse_mp_format
, 0);
627 if
(ftype
>=mp_filetype_text
) {
628 s
= kpse_find_file
(fname
, kpse_mp_format
, 0);
631 case mp_filetype_program
:
633 if
(l
>3 && strcmp(fname+l-3,".mf")==0) {
634 s
= kpse_find_file
(fname
, kpse_mf_format
, 0);
636 s
= kpse_find_file
(fname
, kpse_mp_format
, 0);
639 case mp_filetype_memfile
:
640 s
= kpse_find_file
(fname
, kpse_mem_format
, 1);
642 case mp_filetype_metrics
:
643 s
= kpse_find_file
(fname
, kpse_tfm_format
, 0);
645 case mp_filetype_fontmap
:
646 s
= kpse_find_file
(fname
, kpse_fontmap_format
, 0);
648 case mp_filetype_font
:
649 s
= kpse_find_file
(fname
, kpse_type1_format
, 0);
651 case mp_filetype_encoding
:
652 s
= kpse_find_file
(fname
, kpse_enc_format
, 0);
659 s
= mpost_xstrdup
(ofname
);
662 s
= mpost_xstrdup
(fname
);
669 @ @
<Register the callback routines@
>=
671 options-
>find_file
= mpost_find_file
;
673 @ The |mpost| program supports setting of internal values
674 via a |
-s| commandline switch. Since this switch is repeatable
,
675 a structure is needed to store the found values in
, which is a
679 typedef struct set_list_item
{
683 struct set_list_item
*next
;
686 @ Here is the global value that is the head of the list of |
-s| options.
688 struct set_list_item
*set_list
= NULL;
690 @ And |internal_set_option| is the routine that fills in the linked
691 list. The argument it receives starts at the first letter of the
692 internal
, and should contain an internal name
, an equals sign
,
693 and the value
(possibly in quotes
) without any intervening spaces.
695 Double quotes around the right hand side are needed to make sure that
696 the right hand side is treated as a string assignment by MPlib later.
697 These outer double quote characters are stripped
, but no other string
698 processing takes place.
700 As a special hidden feature
, a missing right hand side is treated as if it
701 was the integer value |
1|.
704 void internal_set_option
(const char
*opt
);
707 void internal_set_option
(const char
*opt
) {
708 struct set_list_item
*itm
;
711 s
= mpost_xstrdup
(opt
) ;
716 *v
='\
0'
; /* terminates |s|
*/
721 *(v+strlen(v)-1)= '\0';
724 if (s && v && strlen(s)>0) {
725 if (set_list == NULL) {
726 set_list = xmalloc(sizeof(struct set_list_item));
730 while (itm->next != NULL)
732 itm->next = xmalloc(sizeof(struct set_list_item));
737 itm->isstring = isstring;
742 @ After the initialization stage is done, the next function
743 runs through the list of options and feeds them to the MPlib
744 function |mp_set_internal|.
747 void run_set_list (MP mp);
750 void run_set_list (MP mp) {
751 struct set_list_item *itm;
754 mp_set_internal(mp,itm->name,itm->value, itm->isstring);
762 static void *mpost_open_file(MP mp, const char *fname, const char *fmode, int ftype) {
765 if (ftype==mp_filetype_terminal) {
766 return (fmode[0] == 'r' ? stdin : stdout);
767 } else if (ftype==mp_filetype_error) {
770 s = mpost_find_file (mp, fname, fmode, ftype);
773 realmode[0] = *fmode;
776 ret = (void *)fopen(s,realmode);
777 if (recorder_enabled) {
779 recorder_start(job_name);
781 fprintf(recorder_file, "INPUT %s\n", s);
783 fprintf(recorder_file, "OUTPUT %s\n", s);
792 @ @<Register the callback routines@>=
794 options->open_file = mpost_open_file;
797 @<getopt structures@>=
798 #define ARGUMENT_IS(a) STREQ (mpost_options[optionid].name, a)
800 /* SunOS cc can't initialize automatic structs, so make this static. */
801 static struct option mpost_options[]
802 = { { "mem", 1, 0, 0 },
804 { "debug", 0, &debug, 1 },
805 { "no-kpathsea", 0, &nokpse, 1 },
806 { "dvitomp", 0, &dvitomp_only, 1 },
807 { "ini", 0, &ini_version_test, 1 },
808 { "interaction", 1, 0, 0 },
810 { "numbersystem", 1, 0, 0 },
811 { "halt-on-error", 0, 0, 0 },
812 { "kpathsea-debug", 1, 0, 0 },
813 { "progname", 1, 0, 0 },
814 { "version", 0, 0, 0 },
815 { "recorder", 0, &recorder_enabled, 1 },
816 { "file-line-error-style", 0, 0, 0 },
817 { "no-file-line-error-style", 0, 0, 0 },
818 { "file-line-error", 0, 0, 0 },
819 { "no-file-line-error", 0, 0, 0 },
820 { "jobname", 1, 0, 0 },
821 { "output-directory", 1, 0, 0 },
823 { "parse-first-line", 0, 0, 0 },
824 { "no-parse-first-line", 0, 0, 0 },
827 { "troff", 0, 0, 0 },
833 @ Parsing the commandline options.
835 @<Read and set command line options@>=
837 int g; /* `getopt' return code. */
840 g = getopt_long_only (argc, argv, "+", mpost_options, &optionid);
842 if (g == -1) /* End of arguments, exit the loop. */
845 if (g == '?') { /* Unknown option. */
849 if (ARGUMENT_IS ("kpathsea-debug")) {
850 kpathsea_debug |= (unsigned)atoi (optarg);
852 } else if (ARGUMENT_IS("jobname")) {
854 mpost_xfree(options->job_name);
855 options->job_name = mpost_xstrdup(optarg);
858 } else if (ARGUMENT_IS ("progname")) {
859 user_progname = optarg;
861 } else if (ARGUMENT_IS ("mem")) {
863 mpost_xfree(options->mem_name);
864 options->mem_name = mpost_xstrdup(optarg);
865 if (user_progname == NULL)
866 user_progname = optarg;
869 } else if (ARGUMENT_IS ("interaction")) {
870 if (STREQ (optarg, "batchmode")) {
871 options->interaction = mp_batch_mode;
872 } else if (STREQ (optarg, "nonstopmode")) {
873 options->interaction = mp_nonstop_mode;
874 } else if (STREQ (optarg, "scrollmode")) {
875 options->interaction = mp_scroll_mode;
876 } else if (STREQ (optarg, "errorstopmode")) {
877 options->interaction = mp_error_stop_mode;
879 fprintf(stdout,"Ignoring unknown argument `%s' to --interaction\n", optarg);
881 } else if (ARGUMENT_IS ("math") || ARGUMENT_IS ("numbersystem")) {
882 if (STREQ (optarg, "scaled")) {
883 options->math_mode = mp_math_scaled_mode;
884 internal_set_option("numbersystem=\"scaled\"");
885 } else if (STREQ (optarg, "double")) {
886 options->math_mode = mp_math_double_mode;
887 internal_set_option("numbersystem=\"double\"");
888 } else if (STREQ (optarg, "decimal")) {
889 options->math_mode = mp_math_decimal_mode;
890 internal_set_option("numbersystem=\"decimal\"");
891 } else if (STREQ (optarg, "binary")) {
892 options->math_mode = mp_math_binary_mode;
893 internal_set_option("numbersystem=\"binary\"");
895 fprintf(stdout,"Ignoring unknown argument `%s' to --numbersystem\n", optarg);
897 } else if (ARGUMENT_IS("troff") ||
899 options->troff_mode = (int)true;
900 } else if (ARGUMENT_IS ("tex")) {
901 mpost_tex_program = optarg;
902 } else if (ARGUMENT_IS("file-line-error") ||
903 ARGUMENT_IS("file-line-error-style")) {
904 options->file_line_error_style=true;
905 } else if (ARGUMENT_IS("no-file-line-error") ||
906 ARGUMENT_IS("no-file-line-error-style")) {
907 options->file_line_error_style=false;
908 } else if (ARGUMENT_IS("help")) {
910 @<Show short help and exit@>;
912 @<Show help and exit@>;
914 } else if (ARGUMENT_IS("version")) {
915 @<Show version and exit@>;
916 } else if (ARGUMENT_IS("s")) {
917 if (strchr(optarg,'=')==NULL) {
918 fprintf(stdout,"fatal error: %s: missing -s argument\n", argv[0]);
921 internal_set_option(optarg);
923 } else if (ARGUMENT_IS("halt-on-error")) {
924 options->halt_on_error = true;
925 } else if (ARGUMENT_IS("output-directory")) {
926 output_directory = optarg ;
927 } else if (ARGUMENT_IS("8bit") ||
928 ARGUMENT_IS("parse-first-line")) {
929 /* do nothing, these are always on */
930 } else if (ARGUMENT_IS("translate-file") ||
931 ARGUMENT_IS("no-parse-first-line")) {
932 fprintf(stdout,"warning: %s: unimplemented option %s\n", argv[0], argv[optind]);
935 options->ini_version = (int)ini_version_test;
939 @<getopt structures@>=
940 #define option_is(a) STREQ (dvitomp_options[optionid].name, a)
942 /* SunOS cc can't initialize automatic structs, so make this static. */
943 static struct option dvitomp_options[]
944 = { { "help", 0, 0, 0 },
945 { "no-kpathsea", 0, &nokpse, 1 },
946 { "kpathsea-debug", 1, 0, 0 },
947 { "progname", 1, 0, 0 },
948 { "version", 0, 0, 0 },
954 @<Read and set dvitomp command line options@>=
956 int g; /* `getopt' return code. */
959 g = getopt_long_only (argc, argv, "+", dvitomp_options, &optionid);
961 if (g == -1) /* End of arguments, exit the loop. */
964 if (g == '?') { /* Unknown option. */
965 fprintf(stdout,"fatal error: %s: unknown option %s\n", argv[0], argv[optind]);
968 if (option_is ("kpathsea-debug")) {
970 kpathsea_debug |= (unsigned)atoi (optarg);
971 } else if (option_is ("progname")) {
972 user_progname = optarg;
973 } else if (option_is("help")) {
974 @<Show short help and exit@>;
975 } else if (option_is("version")) {
976 @<Show version and exit@>;
984 char *s = mp_metapost_version();
986 fprintf(stdout, "This is dvitomp %s" WEB2CVERSION " (%s)\n", s, kpathsea_version_string);
988 fprintf(stdout, "This is MetaPost %s" WEB2CVERSION " (%s)\n", s, kpathsea_version_string);
992 "Usage: mpost [OPTION] [&MEMNAME] [MPNAME[.mp]] [COMMANDS]\n"
993 " mpost --dvitomp DVINAME[.dvi] [MPXNAME[.mpx]]\n"
995 " Run MetaPost on MPNAME, usually creating MPNAME.NNN (and perhaps\n"
996 " MPNAME.tfm), where NNN are the character numbers generated.\n"
997 " Any remaining COMMANDS are processed as MetaPost input,\n"
998 " after MPNAME is read.\n\n"
999 " With a --dvitomp argument, MetaPost acts as DVI-to-MPX converter only.\n"
1000 " Call MetaPost with --dvitomp --help for option explanations.\n\n");
1002 " -ini be inimpost, for dumping mem files\n"
1003 " -interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n"
1004 " scrollmode/errorstopmode)\n"
1005 " -numbersystem=STRING set number system mode (STRING=scaled/double/binary/decimal)\n"
1006 " -jobname=STRING set the job name to STRING\n"
1007 " -progname=STRING set program (and mem) name to STRING\n"
1008 " -tex=TEXPROGRAM use TEXPROGRAM for text labels\n"
1009 " [-no]-file-line-error disable/enable file:line:error style messages\n"
1012 " -debug print debugging info and leave temporary files in place\n"
1013 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
1014 " the bits of NUMBER\n"
1015 " -mem=MEMNAME or &MEMNAME use MEMNAME instead of program name or a %%& line\n"
1016 " -recorder enable filename recorder\n"
1017 " -troff set prologues:=1 and assume TEXPROGRAM is really troff\n"
1018 " -s INTERNAL=\"STRING\" set internal INTERNAL to the string value STRING\n"
1019 " -s INTERNAL=NUMBER set internal INTERNAL to the integer value NUMBER\n"
1020 " -help display this help and exit\n"
1021 " -version output version information and exit\n"
1023 "Email bug reports to mp-implementors@@tug.org.\n"
1029 @<Show short help...@>=
1031 char *s = mp_metapost_version();
1033 fprintf(stdout, "This is dvitomp %s" WEB2CVERSION " (%s)\n", s, kpathsea_version_string);
1035 fprintf(stdout, "This is MetaPost %s" WEB2CVERSION " (%s)\n", s, kpathsea_version_string);
1039 "Usage: dvitomp DVINAME[.dvi] [MPXNAME[.mpx]]\n"
1040 " mpost --dvitomp DVINAME[.dvi] [MPXNAME[.mpx]]\n"
1042 " Convert a TeX DVI file to a MetaPost MPX file.\n\n");
1044 " -progname=STRING set program name to STRING\n"
1045 " -kpathsea-debug=NUMBER set path searching debugging flags according to\n"
1046 " the bits of NUMBER\n"
1047 " -help display this help and exit\n"
1048 " -version output version information and exit\n"
1050 "Email bug reports to mp-implementors@@tug.org.\n"
1056 @<Show version...@>=
1058 char *s = mp_metapost_version();
1060 fprintf(stdout, "dvitomp (MetaPost) %s" WEB2CVERSION " (%s)\n", s, kpathsea_version_string);
1062 fprintf(stdout, "MetaPost %s" WEB2CVERSION " (%s)\n", s, kpathsea_version_string);
1064 "The MetaPost source code in the public domain.\n"
1065 "MetaPost also uses code available under the\n"
1066 "GNU Lesser General Public License (version 3 or later);\n"
1067 "therefore MetaPost executables are covered by the LGPL.\n"
1068 "There is NO warranty.\n"
1069 "For more information about these matters, see the file\n"
1070 "COPYING.LESSER or <http://gnu.org/licenses/lgpl.html>.\n"
1071 "Original author of MetaPost: John Hobby.\n"
1072 "Author of the CWEB MetaPost: Taco Hoekwater.\n"
1073 "Current maintainer of MetaPost: Luigi Scarso.\n\n"
1076 if (!dvitomp_only) {
1077 mp_show_library_versions();
1082 @ The final part of the command line, after option processing, is
1083 stored in the \MP\ instance, this will be taken as the first line of
1086 @d command_line_size 256
1088 @<Copy the rest of the command line@>=
1090 mpost_xfree(options->command_line);
1091 options->command_line = mpost_xmalloc(command_line_size);
1092 strcpy(options->command_line,"");
1095 for(;optind<argc;optind++) {
1096 char *c = argv[optind];
1097 while (*c != '\0') {
1098 if (k<(command_line_size-1)) {
1099 options->command_line[k++] = *c;
1103 options->command_line[k++] = ' ';
1106 if (options->command_line[(k-1)] == ' ')
1111 options->command_line[k] = '\0';
1115 @ A simple function to get numerical |texmf.cnf| values
1117 static int setup_var (int def, const char *var_name, boolean nokpse) {
1119 char * expansion = kpse_var_value (var_name);
1121 int conf_val = atoi (expansion);
1131 @ @<Set up the banner line@>=
1133 char * mpversion = mp_metapost_version () ;
1134 const char * banner = "This is MetaPost, version ";
1135 const char * kpsebanner_start = " (";
1136 const char * kpsebanner_stop = ")";
1137 mpost_xfree(options->banner);
1138 options->banner = mpost_xmalloc(strlen(banner)+
1140 strlen(WEB2CVERSION)+
1141 strlen(kpsebanner_start)+
1142 strlen(kpathsea_version_string)+
1143 strlen(kpsebanner_stop)+1);
1144 strcpy (options->banner, banner);
1145 strcat (options->banner, mpversion);
1146 strcat (options->banner, WEB2CVERSION);
1147 strcat (options->banner, kpsebanner_start);
1148 strcat (options->banner, kpathsea_version_string);
1149 strcat (options->banner, kpsebanner_stop);
1150 mpost_xfree(mpversion);
1153 @ Precedence order is:
1155 \item {} \.{-mem=MEMNAME} on the command line
1156 \item {} \.{\&MEMNAME} on the command line
1157 \item {} \.{\%\&MEM} as first line inside input file
1158 \item {} \.{argv[0]} if all else fails
1160 @<Discover the mem name@>=
1162 char *m = NULL; /* head of potential |mem_name| */
1163 char *n = NULL; /* a moving pointer */
1164 if (options->command_line != NULL && *(options->command_line) == '&'){
1165 m = mpost_xstrdup(options->command_line+1);
1167 while (*n != '\0' && *n != ' ') n++;
1168 while (*n == ' ') n++;
1169 if (*n != '\0') { /* more command line to follow */
1170 char *s = mpost_xstrdup(n);
1172 while (*n == ' ' && n>m) n--;
1174 *n ='\0'; /* this terminates |m| */
1175 mpost_xfree(options->command_line);
1176 options->command_line = s;
1177 } else { /* only \.{\&MEMNAME} on command line */
1179 while (*n == ' ' && n>m) n--;
1181 *n ='\0'; /* this terminates |m| */
1182 mpost_xfree(options->command_line);
1184 if ( options->mem_name == NULL && *m != '\0') {
1185 mpost_xfree(options->mem_name); /* for lint only */
1186 options->mem_name = m;
1192 if ( options->mem_name == NULL ) {
1193 char *m = NULL; /* head of potential |job_name| */
1194 char *n = NULL; /* a moving pointer */
1195 if (options->command_line != NULL && *(options->command_line) != '\\'){
1196 m = mpost_xstrdup(options->command_line);
1198 while (*n != '\0' && *n != ' ') n++;
1204 fname = kpse_find_file(m,kpse_mp_format,true);
1205 if (fname == NULL) {
1208 FILE *F = fopen(fname,"r");
1212 char *line = mpost_xmalloc(256);
1213 if (fgets(line,255,F) == NULL) {
1219 while (*line != '\0' && *line == ' ') line++;
1222 while (*n != '\0' && *n == ' ') n++;
1225 while (*n != '\0' && *n != ' ') n++;
1228 while (*n == ' ' && n>m) n--;
1229 *n ='\0'; /* this terminates |m| */
1230 options->mem_name = mpost_xstrdup(m);
1246 if ( options->mem_name == NULL )
1247 if (kpse_program_name!=NULL)
1248 options->mem_name = mpost_xstrdup(kpse_program_name);
1251 @ The job name needs to be known for the recorder to work,
1252 so we have to fix up |job_name| and |job_area|. If there
1253 was a \.{--jobname} on the command line, we have to reset
1254 the options structure as well.
1256 @<Discover the job name@>=
1258 char *tmp_job = NULL;
1259 if (options->job_name != NULL) {
1260 tmp_job = mpost_xstrdup(options->job_name);
1261 mpost_xfree(options->job_name);
1262 options->job_name = NULL;
1264 char *m = NULL; /* head of potential |job_name| */
1265 char *n = NULL; /* a moving pointer */
1266 if (options->command_line != NULL){
1267 m = mpost_xstrdup(options->command_line);
1269 if (*(options->command_line) != '\\') { /* this is the simple case */
1270 while (*n != '\0' && *n != ' ') n++;
1273 tmp_job = mpost_xstrdup(m);
1275 } else { /* this is still not perfect, but better */
1276 char *mm = strstr(m,"input ");
1280 while (*n != '\0' && *n != ' ' && *n!=';') n++;
1283 tmp_job = mpost_xstrdup(mm);
1289 if (tmp_job == NULL) {
1290 if (options->ini_version == 1 &&
1291 options->mem_name != NULL) {
1292 tmp_job = mpost_xstrdup(options->mem_name);
1295 if (tmp_job == NULL) {
1296 tmp_job = mpost_xstrdup("mpout");
1298 char *ext = strrchr(tmp_job,'.');
1303 /* now split |tmp_job| into |job_area| and |job_name| */
1305 char *s = tmp_job + strlen(tmp_job);
1306 if (!IS_DIR_SEP(*s)) { /* just in case */
1308 if (IS_DIR_SEP(*s)) {
1314 /* there was a directory part */
1316 job_name = mpost_xstrdup((s+1));
1322 /* |job_area| stays NULL */
1327 options->job_name = job_name;
1329 @ We |#define DLLPROC dllmpostmain| in order to build \MP\ as DLL for
1333 #define DLLPROC dllmpostmain
1334 #if defined(WIN32) && !defined(__MINGW32__) && defined(DLLPROC)
1335 extern __declspec(dllexport) int DLLPROC (int argc, char **argv);
1340 @ Now this is really it: \MP\ starts and ends here.
1343 static char *cleaned_invocation_name(char *arg)
1346 const char *start = xbasename(arg);
1347 ret = xstrdup(start);
1348 dot = strrchr(ret, '.');
1350 *dot = 0; /* chop */
1355 #if defined(DLLPROC)
1356 DLLPROC (int argc, char **argv)
1358 main (int argc, char **argv)
1360 { /* |start_here| */
1361 int k; /* index into buffer */
1362 int history; /* the exit status */
1363 MP mp; /* a metapost instance */
1364 struct MP_options * options; /* instance options */
1365 char *user_progname = NULL; /* If the user overrides |argv[0]| with {\tt -progname}. */
1366 options = mp_options();
1367 options->ini_version = (int)false;
1368 options->print_found_names = (int)true;
1370 const char *base = cleaned_invocation_name(argv[0]);
1371 if (FILESTRCASEEQ(base, "dvitomp"))
1375 @<Read and set dvitomp command line options@>;
1377 @<Read and set command line options@>;
1380 char *mpx = NULL, *dvi = NULL;
1384 dvi = argv[optind++];
1386 mpx = argv[optind++];
1390 @<Show short help and exit@>;
1393 kpse_set_program_name(argv[0],
1394 user_progname ? user_progname : "dvitomp");
1395 exit (mpost_run_dvitomp(dvi, mpx));
1399 @= /*@@-nullpass@@*/ @>
1401 kpse_set_program_enabled (kpse_mem_format, MAKE_TEX_FMT_BY_DEFAULT,
1403 kpse_set_program_name(argv[0], user_progname);
1405 @= /*@@=nullpass@@*/ @>
1406 if(putenv(xstrdup("engine=metapost")))
1407 fprintf(stdout,"warning: could not set up $engine\n");
1408 options->error_line = setup_var (79,"error_line",nokpse);
1409 options->half_error_line = setup_var (50,"half_error_line",nokpse);
1410 options->max_print_line = setup_var (100,"max_print_line",nokpse);
1411 @<Set up the banner line@>;
1412 @<Copy the rest of the command line@>;
1413 @<Discover the mem name@>;
1414 @<Discover the job name@>;
1415 @<Register the callback routines@>;
1416 mp = mp_initialize(options);
1417 mpost_xfree(options->command_line);
1418 mpost_xfree(options->mem_name);
1419 mpost_xfree(options->job_name);
1420 mpost_xfree(options->banner);
1424 history = mp_status(mp);
1425 if (history!=0 && history!=mp_warning_issued)
1427 if (set_list!=NULL) {
1430 history = mp_run(mp);
1431 (void)mp_finish(mp);
1432 if (history!=0 && history!=mp_warning_issued)