Mutators for Catalog states.
[shapes.git] / source / main.cc
blob6f0cbb9061af52911bc3eac21c627a64d1f6a023
1 #include "Shapes_Kernel_decls.h"
3 #include "simplepdfi.h"
4 #include "simplepdfo.h"
5 #include "globals.h"
6 #include "consts.h"
7 #include "shapesexceptions.h"
8 #include "hottypes.h"
9 #include "multipage.h"
10 #include "continuations.h"
11 #include "charconverters.h"
12 #include "pagecontentstates.h"
13 #include "autoonoff.h"
14 #include "shapesscanner.h"
15 #include "texlabelmanager.h"
16 #include "debuglog.h"
18 #include <iostream>
19 #include <iomanip>
20 #include <fstream>
21 #include <string>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <list>
25 #include <time.h>
26 #include <errno.h>
27 #include <limits>
28 #include <iconv.h>
30 int shapesparse( );
31 extern int shapesdebug;
32 void printVersion( );
34 using namespace Shapes;
35 using namespace SimplePDF;
37 void argcAssertion( const char * optionSpecifier, int argc, int argcMin );
38 bool strprefixcmp( char * str, const char * prefix, char ** endp );
39 bool strtobool( const char * str, const char * containingString, const char * trueLabel = 0, const char * falseLabel = 0 );
40 std::string callDir;
41 std::string absoluteFilename( const char * filename );
42 std::string absoluteDirectory( const char * filename );
43 void ensureTmpDirectoryExists( const std::string & dirname, bool allowCreate );
44 RefCountPtr< std::ifstream > performIterativeStartup( const std::string & texJobName );
45 void abortProcedure( int exitCode );
46 void setupGlobals( );
47 enum XpdfAction{ XPDF_DEFAULT, XPDF_RAISE, XPDF_RELOAD, XPDF_QUIT, XPDF_NOSERVER };
48 void xpdfHelper( const std::string & filename, const std::string & server, const XpdfAction & action );
49 void openHelper( const std::string & filename, const char * application );
50 void addDefaultNeedPath( );
51 void addDefaultFontMetricsPath( );
52 void destroyGlobals( );
53 void escapeExtGlobChars( const std::string & str, std::ostream & dst );
55 namespace Shapes
57 namespace Interaction
59 void systemDebugMessage( const std::string & msg );
63 int
64 main( int argc, char ** argv )
66 srand( time(NULL) );
68 setupGlobals( );
70 bool iterativeMode = true;
71 bool useResources = true;
74 char * cwd = getcwd( 0, 0 );
75 callDir = cwd + std::string( "/" );
76 free( cwd );
79 std::string outDir;
80 std::string tmpDir;
81 bool allowCreateTmpDir = false;
82 std::string baseName;
83 std::string inputName;
84 std::string outputName;
85 std::string texJobName;
86 std::string labelDBName;
87 std::string fontmetricsOutputName;
89 enum FilenameRequests{ FILENAME_RESOURCE, FILENAME_IN, FILENAME_OUT, FILENAME_TMP, FILENAME_TEXJOB, FILENAME_LABELDB, FILENAME_AFM, FILENAME_TEXINPUTS, FILENAME_HTMLDOC };
91 std::list< int > filenameRequestList;
92 std::list< const char * > resourceRequestList;
94 bool evalTrace = false;
95 bool evalBackTrace = false;
96 bool cleanupMemory = true;
97 bool memoryStats = false;
98 bool launch_xpdf = false;
99 SimplePDF::PDF_Version::Version pdfVersion = SimplePDF::PDF_Version::VERSION_UNDEFINED;
100 SimplePDF::PDF_Version::Action pdfVersionAction = SimplePDF::PDF_Version::WARN;
101 XpdfAction xpdfAction = XPDF_DEFAULT;
102 std::string xpdfServer;
103 bool do_open = false;
104 const char * do_open_application = 0;
105 std::ostringstream prependStreamOut;
107 enum SplitMode{ SPLIT_NO, SPLIT_FLAT, SPLIT_DIR };
108 SplitMode splitMode = SPLIT_NO;
110 argc -= 1;
111 argv += 1;
112 while( argc > 0 )
114 char * optionSuffix;
115 if( strcmp( *argv, "--yydebug" ) == 0 )
117 shapesdebug = 1;
118 argv += 1;
119 argc -= 1;
121 else if( strcmp( *argv, "--shapes-debug" ) == 0 )
123 shapesdebug = 1;
124 argv += 1;
125 argc -= 1;
127 else if( strcmp( *argv, "--system-debug" ) == 0 )
129 Interaction::debugSystem = true;
130 argv += 1;
131 argc -= 1;
133 else if( strcmp( *argv, "--afm-debug" ) == 0 )
135 Interaction::fontMetricDebug = true;
136 argv += 1;
137 argc -= 1;
139 else if( strcmp( *argv, "--afm-messages" ) == 0 )
141 Interaction::fontMetricMessages = true;
142 argv += 1;
143 argc -= 1;
145 else if( strcmp( *argv, "--tex-debug" ) == 0 )
147 Interaction::pdfLaTeXInteractionTo_stderr = true;
148 argv += 1;
149 argc -= 1;
151 else if( strcmp( *argv, "--log-globals" ) == 0 )
153 Interaction::logGlobals = true;
154 argv += 1;
155 argc -= 1;
157 else if( strprefixcmp( *argv, "--bytecolumn=", & optionSuffix ) )
159 Interaction::characterColumnInBytes = strtobool( optionSuffix, *argv );
160 argv += 1;
161 argc -= 1;
163 else if( strprefixcmp( *argv, "--debugstep=", & optionSuffix ) )
165 char * endp;
166 int tmp = strtol( optionSuffix, & endp, 10 );
167 if( tmp < 0 )
169 std::cerr << "The --debugstep value must be nonnegative: " << optionSuffix << std::endl ;
170 exit( Interaction::EXIT_INVOCATION_ERROR );
172 Interaction::debugStep = static_cast< size_t >( tmp );
173 argv += 1;
174 argc -= 1;
176 else if( strcmp( *argv, "--debuglog" ) == 0 )
178 argcAssertion( *argv, argc, 2 );
181 Kernel::theDebugLog.setFilename( *( argv + 1 ) );
183 catch( const char * ball )
185 std::cerr << ball << std::endl ;
186 exit( Interaction::EXIT_INVOCATION_ERROR );
188 argv += 2;
189 argc -= 2;
191 else if( strcmp( *argv, "--debuglog-stderr" ) == 0 )
195 Kernel::theDebugLog.setStream( & std::cerr );
197 catch( const char * ball )
199 std::cerr << ball << std::endl ;
200 exit( Interaction::EXIT_INVOCATION_ERROR );
202 argv += 1;
203 argc -= 1;
205 else if( strcmp( *argv, "--debuglog-stdout" ) == 0 )
209 Kernel::theDebugLog.setStream( & std::cout );
211 catch( const char * ball )
213 std::cerr << ball << std::endl ;
214 exit( Interaction::EXIT_INVOCATION_ERROR );
216 argv += 1;
217 argc -= 1;
219 else if( strprefixcmp( *argv, "--dtminerror=", & optionSuffix ) )
221 Computation::dtMinIsError = strtobool( optionSuffix, *argv );
222 argv += 1;
223 argc -= 1;
225 else if( strprefixcmp( *argv, "--fmguesserror=", & optionSuffix ) )
227 Computation::fontMetricGuessIsError = strtobool( optionSuffix, *argv );
228 argv += 1;
229 argc -= 1;
231 else if( strcmp( *argv, "--evaltrace" ) == 0 )
233 evalTrace = true;
234 argv += 1;
235 argc -= 1;
237 else if( strcmp( *argv, "--evalbacktrace" ) == 0 )
239 evalTrace = true;
240 evalBackTrace = true;
241 argv += 1;
242 argc -= 1;
244 else if( strprefixcmp( *argv, "--backtrace=", & optionSuffix ) )
246 Interaction::debugBacktrace = strtobool( optionSuffix, *argv );
247 argv += 1;
248 argc -= 1;
250 else if( strprefixcmp( *argv, "--iteration=", & optionSuffix ) )
252 iterativeMode = strtobool( optionSuffix, *argv );
253 argv += 1;
254 argc -= 1;
256 else if( strprefixcmp( *argv, "--resources=", & optionSuffix ) )
258 useResources = strtobool( optionSuffix, *argv );
259 argv += 1;
260 argc -= 1;
262 else if( strprefixcmp( *argv, "--stats=", & optionSuffix ) )
264 memoryStats = strtobool( optionSuffix, *argv );
265 argv += 1;
266 argc -= 1;
268 else if( strprefixcmp( *argv, "--memclean=", & optionSuffix ) )
270 cleanupMemory = strtobool( optionSuffix, *argv );
271 argv += 1;
272 argc -= 1;
274 else if( strprefixcmp( *argv, "--showfiles=", & optionSuffix ) )
276 Ast::theShapesScanner.setShowFiles( strtobool( optionSuffix, *argv ) );
277 argv += 1;
278 argc -= 1;
280 else if( strprefixcmp( *argv, "--pdf-version=", & optionSuffix ) || /* Note that we use that || shortcuts! */
281 strprefixcmp( *argv, "-v", & optionSuffix ) )
283 if( pdfVersion != SimplePDF::PDF_Version::VERSION_UNDEFINED )
285 std::cerr << "Multiply defined pdf version." << std::endl ;
286 exit( Interaction::EXIT_INVOCATION_ERROR );
289 switch( *optionSuffix )
291 case 'e':
292 pdfVersionAction = SimplePDF::PDF_Version::ERROR;
293 break;
294 case 'w':
295 pdfVersionAction = SimplePDF::PDF_Version::WARN;
296 break;
297 case 's':
298 pdfVersionAction = SimplePDF::PDF_Version::SILENT;
299 break;
300 default:
301 std::cerr << "The only allowed action-characters in the pdf version specification are: \"e\" (error), \"w\" (warn), and \"s\" (silent). You said \"" << *optionSuffix << "\", being the first character in \"" << optionSuffix << "\"." << std::endl ;
302 exit( Interaction::EXIT_INVOCATION_ERROR );
304 ++optionSuffix;
305 if( strncmp( optionSuffix, "1.", 2 ) == 0 &&
306 '1' <= optionSuffix[2] && optionSuffix[2] <= '6' )
308 SimplePDF::PDF_Version::Version versions[] = { SimplePDF::PDF_Version::PDF_1_1, SimplePDF::PDF_Version::PDF_1_2, SimplePDF::PDF_Version::PDF_1_3, SimplePDF::PDF_Version::PDF_1_4, SimplePDF::PDF_Version::PDF_1_5, SimplePDF::PDF_Version::PDF_1_6 };
309 pdfVersion = versions[ optionSuffix[2] - '1' ];
311 else if( strcmp( optionSuffix, "X" ) == 0 )
313 std::cerr << "Restriction to PDF-X is not implemented, please try using a low version number, such as 1.1 instead." << std::endl ;
315 else
317 std::cerr << "Unsupported pdf version specification: " << optionSuffix << std::endl ;
318 exit( Interaction::EXIT_INVOCATION_ERROR );
320 argv += 1;
321 argc -= 1;
323 else if( strprefixcmp( *argv, "--tp=", & optionSuffix ) )
325 Kernel::allowTransparency = strtobool( optionSuffix, *argv );
326 argv += 1;
327 argc -= 1;
329 else if( strprefixcmp( *argv, "--spot-pair=", & optionSuffix ) )
331 Kernel::allowSingletonPaths = ! strtobool( optionSuffix, *argv );
332 argv += 1;
333 argc -= 1;
335 else if( strprefixcmp( *argv, "--unit=", & optionSuffix ) )
337 Interaction::displayUnitName = optionSuffix;
339 argv += 1;
340 argc -= 1;
342 else if( strprefixcmp( *argv, "--splicingtol=", & optionSuffix ) )
344 char * endp;
345 Computation::theTrixelizeSplicingTol = strtod( optionSuffix, &endp );
346 if( *endp != '\0' )
348 std::cerr << "Argument to --splicingtol= was not a float: " << optionSuffix << std::endl ;
349 exit( Interaction::EXIT_INVOCATION_ERROR );
351 if( Computation::theTrixelizeSplicingTol <= 0 )
353 std::cerr << "Argument to --splicingtol not positive: " << Computation::theTrixelizeSplicingTol.offtype< 1, 0 >( ) << std::endl ;
354 exit( Interaction::EXIT_INVOCATION_ERROR );
357 argv += 1;
358 argc -= 1;
360 else if( strprefixcmp( *argv, "--overlaptol=", & optionSuffix ) )
362 char * endp;
363 Computation::theTrixelizeOverlapTol = strtod( optionSuffix, &endp );
364 if( *endp != '\0' )
366 std::cerr << "Argument to --overlaptol was not a float: " << optionSuffix << std::endl ;
367 exit( Interaction::EXIT_INVOCATION_ERROR );
369 if( Computation::theTrixelizeOverlapTol <= 0 )
371 std::cerr << "Argument to --overlaptol not positive: " << Computation::theTrixelizeOverlapTol.offtype< 1, 0 >( ) << std::endl ;
372 exit( Interaction::EXIT_INVOCATION_ERROR );
375 argv += 1;
376 argc -= 1;
378 else if( strcmp( *argv, "--needpath" ) == 0 ||
379 strncmp( *argv, "-N", 2 ) == 0 )
381 bool longForm = strncmp( *argv, "--", 2 ) == 0;
383 const char * pth = 0;
384 if( longForm )
386 argcAssertion( *argv, argc, 2 );
387 pth = *( argv + 1 );
389 else
391 pth = (*argv) + 2;
394 if( strchr( pth, ':' ) != 0 )
396 const char * flag = 0;
397 const char * shortFlag = "-N";
398 if( longForm )
400 flag = *argv;
402 else
404 flag = shortFlag;
407 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
408 if( longForm )
410 std::cerr << " (or " << shortFlag << ")" ;
412 std::cerr <<"." << std::endl ;
413 exit( Interaction::EXIT_INVOCATION_ERROR );
416 Ast::theShapesScanner.push_backNeedPath( absoluteDirectory( pth ) );
418 if( longForm )
420 argv += 2;
421 argc -= 2;
423 else
425 argv += 1;
426 argc -= 1;
429 else if( strcmp( *argv, "--fontmetricspath" ) == 0 ||
430 strncmp( *argv, "-M", 2 ) == 0 )
432 bool longForm = strncmp( *argv, "--", 2 ) == 0;
434 const char * pth = 0;
435 if( longForm )
437 argcAssertion( *argv, argc, 2 );
438 pth = *( argv + 1 );
440 else
442 pth = (*argv) + 2;
445 if( strchr( pth, ':' ) != 0 )
447 const char * flag = 0;
448 const char * shortFlag = "-M";
449 if( longForm )
451 flag = *argv;
453 else
455 flag = shortFlag;
458 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
459 if( longForm )
461 std::cerr << " (or " << shortFlag << ")" ;
463 std::cerr <<"." << std::endl ;
464 exit( Interaction::EXIT_INVOCATION_ERROR );
467 Lang::Font::push_backFontMetricsPath( absoluteDirectory( pth ) );
469 if( longForm )
471 argv += 2;
472 argc -= 2;
474 else
476 argv += 1;
477 argc -= 1;
480 else if( strprefixcmp( *argv, "--seed=", & optionSuffix ) )
482 char * endp;
483 long s = strtol( optionSuffix, &endp, 10 );
484 if( *endp != '\0' )
486 std::cerr << "Argument to --seed= was not an integer: " << optionSuffix << std::endl ;
487 exit( Interaction::EXIT_INVOCATION_ERROR );
490 srand( s );
492 argv += 1;
493 argc -= 1;
495 else if( strprefixcmp( *argv, "--arcdelta=", & optionSuffix ) )
497 char * endp;
498 Computation::the_arcdelta = strtod( optionSuffix, &endp );
499 if( *endp != '\0' )
501 std::cerr << "Argument to --arcdelta= was not a float: " << optionSuffix << std::endl ;
502 exit( Interaction::EXIT_INVOCATION_ERROR );
504 if( Computation::the_arcdelta <= 0 )
506 std::cerr << "Argument to --arcdelta= not positive: " << Computation::the_arcdelta.offtype< 1, 0 >( ) << std::endl ;
507 exit( Interaction::EXIT_INVOCATION_ERROR );
510 argv += 1;
511 argc -= 1;
513 else if( strprefixcmp( *argv, "--dtmin=", & optionSuffix ) )
515 char * endp;
516 Computation::the_dtMin = strtod( optionSuffix, &endp );
517 if( *endp != '\0' )
519 std::cerr << "Argument to --dtmin= was not a float: " << optionSuffix << std::endl ;
520 exit( Interaction::EXIT_INVOCATION_ERROR );
522 if( Computation::the_dtMin <= 0 )
524 std::cerr << "Argument to --dtmin= not positive: " << Computation::the_dtMin << std::endl ;
525 exit( Interaction::EXIT_INVOCATION_ERROR );
528 argv += 1;
529 argc -= 1;
531 else if( strcmp( *argv, "--prepend" ) == 0 )
533 argcAssertion( *argv, argc, 2 );
534 prependStreamOut << *( argv + 1 ) << std::endl ;
535 argv += 2;
536 argc -= 2;
538 else if( strcmp( *argv, "--base" ) == 0 )
540 argcAssertion( *argv, argc, 2 );
541 if( baseName != "" )
543 std::cerr << "The name base is multiply specified." << std::endl ;
544 exit( Interaction::EXIT_INVOCATION_ERROR );
546 baseName = *( argv + 1 );
547 argv += 2;
548 argc -= 2;
550 else if( strcmp( *argv, "--which" ) == 0 )
552 argcAssertion( *argv, argc, 2 );
553 filenameRequestList.push_back( FILENAME_RESOURCE );
554 resourceRequestList.push_back( *( argv + 1 ) );
555 argv += 2;
556 argc -= 2;
558 else if( strcmp( *argv, "--in" ) == 0 )
560 argcAssertion( *argv, argc, 2 );
561 if( inputName != "" )
563 std::cerr << "The input file is multiply specified." << std::endl ;
564 exit( Interaction::EXIT_INVOCATION_ERROR );
566 inputName = absoluteFilename( *( argv + 1 ) );
567 argv += 2;
568 argc -= 2;
570 else if( strcmp( *argv, "--which-in" ) == 0 )
572 filenameRequestList.push_back( FILENAME_IN );
573 argv += 1;
574 argc -= 1;
576 else if( strcmp( *argv, "--out" ) == 0 )
578 argcAssertion( *argv, argc, 2 );
579 if( outputName != "" )
581 std::cerr << "The output file is multiply specified." << std::endl ;
582 exit( Interaction::EXIT_INVOCATION_ERROR );
584 outputName = absoluteFilename( *( argv + 1 ) );
585 argv += 2;
586 argc -= 2;
588 else if( strcmp( *argv, "--which-out" ) == 0 )
590 filenameRequestList.push_back( FILENAME_OUT );
591 argv += 1;
592 argc -= 1;
594 else if( strcmp( *argv, "--texjob" ) == 0 )
596 argcAssertion( *argv, argc, 2 );
597 if( texJobName != "" )
599 std::cerr << "The tex job name is multiply specified." << std::endl ;
600 exit( Interaction::EXIT_INVOCATION_ERROR );
602 texJobName = *( argv + 1 );
603 if( texJobName.find( '/' ) != std::string::npos )
605 std::cerr << "The tex job name may not include directory specification. Please use --tmpdir to set the directory where the tex job is carried out." << std::endl ;
606 exit( Interaction::EXIT_INVOCATION_ERROR );
608 argv += 2;
609 argc -= 2;
611 else if( strcmp( *argv, "--which-texjob" ) == 0 )
613 filenameRequestList.push_back( FILENAME_TEXJOB );
614 argv += 1;
615 argc -= 1;
617 else if( strcmp( *argv, "--labeldb" ) == 0 )
619 argcAssertion( *argv, argc, 2 );
620 if( labelDBName != "" )
622 std::cerr << "The label database file is multiply specified." << std::endl ;
623 exit( Interaction::EXIT_INVOCATION_ERROR );
625 labelDBName = absoluteFilename( *( argv + 1 ) );
626 argv += 2;
627 argc -= 2;
629 else if( strcmp( *argv, "--which-labeldb" ) == 0 )
631 filenameRequestList.push_back( FILENAME_LABELDB );
632 argv += 1;
633 argc -= 1;
635 else if( strcmp( *argv, "--afmout" ) == 0 )
637 argcAssertion( *argv, argc, 2 );
638 if( fontmetricsOutputName != "" )
640 std::cerr << "The font metrics output name is multiply specified." << std::endl ;
641 exit( Interaction::EXIT_INVOCATION_ERROR );
643 fontmetricsOutputName = absoluteFilename( *( argv + 1 ) );
644 argv += 2;
645 argc -= 2;
647 else if( strcmp( *argv, "--which-afmout" ) == 0 )
649 filenameRequestList.push_back( FILENAME_AFM );
650 argv += 1;
651 argc -= 1;
653 else if( strcmp( *argv, "--which-TEXINPUTS" ) == 0 )
655 filenameRequestList.push_back( FILENAME_TEXINPUTS );
656 argv += 1;
657 argc -= 1;
659 else if( strcmp( *argv, "--which-doc" ) == 0 )
661 filenameRequestList.push_back( FILENAME_HTMLDOC );
662 argv += 1;
663 argc -= 1;
665 else if( strcmp( *argv, "--outdir" ) == 0 )
667 argcAssertion( *argv, argc, 2 );
668 if( outDir != "" )
670 std::cerr << "The output directory is multiply specified." << std::endl ;
671 exit( Interaction::EXIT_INVOCATION_ERROR );
673 outDir = absoluteDirectory( *( argv + 1 ) );
674 argv += 2;
675 argc -= 2;
677 else if( strprefixcmp( *argv, "--tmp*=", & optionSuffix ) )
679 allowCreateTmpDir = strtobool( optionSuffix, *argv );
680 argv += 1;
681 argc -= 1;
683 else if( strcmp( *argv, "--tmpdir" ) == 0 )
685 argcAssertion( *argv, argc, 2 );
686 if( tmpDir != "" )
688 std::cerr << "The temporaries directory is multiply specified." << std::endl ;
689 exit( Interaction::EXIT_INVOCATION_ERROR );
691 tmpDir = absoluteDirectory( *( argv + 1 ) );
692 argv += 2;
693 argc -= 2;
695 else if( strcmp( *argv, "--which-tmp" ) == 0 )
697 filenameRequestList.push_back( FILENAME_TMP );
698 argv += 1;
699 argc -= 1;
701 else if( strprefixcmp( *argv, "--split=", & optionSuffix ) )
703 if( strcmp( optionSuffix, "no" ) == 0 )
705 splitMode = SPLIT_NO;
707 else if( strcmp( optionSuffix, "flat" ) == 0 )
709 splitMode = SPLIT_FLAT;
711 else if( strcmp( optionSuffix, "dir" ) == 0 )
713 splitMode = SPLIT_DIR;
715 else
717 std::cerr << "The string \"" << optionSuffix << "\" in the command line argument \"" << *argv << "\" was not any of { 'no', 'flat', 'dir' }." << std::endl ;
719 argv += 1;
720 argc -= 1;
722 else if( strcmp( *argv, "--xpdf" ) == 0 )
724 launch_xpdf = true;
725 argv += 1;
726 argc -= 1;
728 else if( strcmp( *argv, "--xpdf-remote" ) == 0 )
730 argcAssertion( *argv, argc, 2 );
731 if( xpdfServer != "" )
733 std::cerr << "The xpdf server is multiply specified." << std::endl ;
734 exit( Interaction::EXIT_INVOCATION_ERROR );
736 xpdfServer = *( argv + 1 );
737 argv += 2;
738 argc -= 2;
740 else if( strcmp( *argv, "--xpdf-no-server" ) == 0 )
742 if( xpdfAction != XPDF_DEFAULT )
744 std::cerr << "The xpdf action is multiply specified." << std::endl ;
745 exit( Interaction::EXIT_INVOCATION_ERROR );
747 xpdfAction = XPDF_NOSERVER;
748 argv += 1;
749 argc -= 1;
751 else if( strcmp( *argv, "--xpdf-reload" ) == 0 )
753 if( xpdfAction != XPDF_DEFAULT )
755 std::cerr << "The xpdf action is multiply specified." << std::endl ;
756 exit( Interaction::EXIT_INVOCATION_ERROR );
758 xpdfAction = XPDF_RELOAD;
759 argv += 1;
760 argc -= 1;
762 else if( strcmp( *argv, "--xpdf-quit" ) == 0 )
764 if( xpdfAction != XPDF_DEFAULT )
766 std::cerr << "The xpdf action is multiply specified." << std::endl ;
767 exit( Interaction::EXIT_INVOCATION_ERROR );
769 xpdfAction = XPDF_QUIT;
770 argv += 1;
771 argc -= 1;
773 else if( strcmp( *argv, "--open" ) == 0 )
775 do_open = true;
776 argv += 1;
777 argc -= 1;
779 else if( strcmp( *argv, "--open-a" ) == 0 )
781 argcAssertion( *argv, argc, 2 );
782 do_open = true;
783 do_open_application = *( argv + 1 );
784 argv += 2;
785 argc -= 2;
787 else if( strcmp( *argv, "--version" ) == 0 )
789 printVersion( );
790 exit( Interaction::EXIT_OK );
792 else if( argc == 1 )
794 if( baseName != "" )
796 std::cerr << "The name base is multiply specified." << std::endl ;
797 exit( Interaction::EXIT_INVOCATION_ERROR );
799 struct stat theStat;
800 if( stat( *argv, & theStat ) == 0 &&
801 ( theStat.st_mode & S_IFDIR ) == 0 ) /* We are not interested in directories here. */
803 inputName = *argv;
804 char * ext = *argv + strlen( *argv ) - 6;
805 if( ext <= *argv )
807 std::cerr << "The file name \"" << *argv << "\" is unexpectedly short (it should include the \".shape\" suffix)." << std::endl ;
808 exit( Interaction::EXIT_INVOCATION_ERROR );
810 if( strcmp( ext, ".shape" ) != 0 )
812 std::cerr << "Expected \".shape\" suffix in the file name \"" << *argv << "\"." << std::endl ;
813 exit( Interaction::EXIT_INVOCATION_ERROR );
816 else
818 if( (*argv)[ strlen( *argv ) - 1 ] == '.' )
820 inputName = std::string( *argv ) + "shape";
822 else
824 inputName = std::string( *argv ) + ".shape";
826 if( ! stat( inputName.c_str( ), & theStat ) == 0 )
828 /* It is not entirely clear what is the best error message here,
829 * as the source file may be specified in several different ways.
830 * This should cause the least confusion.
832 std::cerr << "Failed to locate input file: " << *argv << std::endl ;
833 exit( Interaction::EXIT_INPUT_FILE_ERROR );
837 const char * slash = strrchr( inputName.c_str( ), '/' );
838 if( slash == 0 )
840 slash = inputName.c_str( );
842 else
844 ++slash;
846 size_t skipCount = slash - inputName.c_str( );
847 baseName = inputName.substr( skipCount, inputName.length( ) - skipCount - 6 );
849 argv += 1;
850 argc -= 1;
852 else
854 std::cerr << "Illegal command line option: " << *argv << std::endl ;
855 exit( Interaction::EXIT_INVOCATION_ERROR );
859 if( outDir == "" )
861 outDir = absoluteDirectory( "" );
863 if( tmpDir == "" )
865 char * start = getenv( "SHAPESTMPDIR" );
866 if( start != 0 )
868 tmpDir = absoluteDirectory( start );
870 else
872 tmpDir = absoluteDirectory( "" );
875 ensureTmpDirectoryExists( tmpDir, allowCreateTmpDir );
877 if( baseName == "" )
879 if( texJobName == "" )
881 texJobName = "#shapes.labels";
884 else
886 if( inputName == "" )
888 inputName = absoluteFilename( ( baseName + ".shape" ).c_str( ) );
890 if( outputName == "" )
892 if( splitMode == SPLIT_NO )
894 outputName = outDir + baseName + ".pdf";
896 else
898 outputName = outDir + baseName;
901 if( texJobName == "" )
903 texJobName = baseName + ".labels";
905 if( labelDBName == "" )
907 labelDBName = outDir + baseName + ".labels.pdf";
909 if( fontmetricsOutputName == "" )
911 fontmetricsOutputName = outDir + baseName + ".afm";
915 if( outputName == "" )
917 if( ! filenameRequestList.empty( ) )
919 /* The output name will never really be used, so it's rather harmless to assign a dummy value.
921 outputName = "?.pdf" ;
923 else
925 std::cerr << "The output file is undetermined. Consider specifying it using \"--out <filename>\"." << std::endl ;
926 exit( Interaction::EXIT_INVOCATION_ERROR );
930 if( labelDBName == "" )
932 iterativeMode = false;
935 if( ! Kernel::theDebugLog.initialized( ) )
937 std::string::size_type suffixSep = outputName.rfind( '.' );
938 if( suffixSep != std::string::npos && outputName.find( '/', suffixSep ) == std::string::npos )
940 /* If there would have been a slash after the '.', the dot would have been part of a directory name.
941 * Otherwise, we conclude that we have found the extension of a filename, and replace that extension
942 * by ".log".
944 Kernel::theDebugLog.setFilename( outputName.substr( 0, suffixSep ) + ".log" );
946 else if( baseName != "" )
948 Kernel::theDebugLog.setFilename( outDir + baseName + ".log" );
950 else
952 Kernel::theDebugLog.setFilename( outDir + "#shapes.log" );
957 std::string inDir;
958 std::string inPath = inputName;
959 std::string::size_type slash = inPath.rfind( '/' );
960 if( slash == std::string::npos )
962 inDir = absoluteDirectory( "" );
964 else
966 inDir = absoluteDirectory( inPath.substr( 0, slash ).c_str( ) );
968 Ast::theShapesScanner.setSourceDir( inDir );
969 Kernel::theTeXLabelManager.setup( inDir, tmpDir, texJobName );
972 if( Computation::theTrixelizeSplicingTol >= Computation::theTrixelizeOverlapTol )
974 std::cerr << "The splicing tolerance (" << Concrete::Length::offtype( Computation::theTrixelizeSplicingTol ) << "bp) must be less than the overlap tolerance (" << Concrete::Length::offtype( Computation::theTrixelizeOverlapTol ) << "bp)." << std::endl ;
975 exit( Interaction::EXIT_INVOCATION_ERROR );
978 if( xpdfServer == "" )
980 xpdfServer = outputName;
983 if( xpdfAction == XPDF_DEFAULT )
985 xpdfAction = XPDF_RAISE;
988 addDefaultNeedPath( );
989 addDefaultFontMetricsPath( );
991 #ifdef RESOURCES_DIR
992 if( useResources )
994 Ast::theShapesScanner.push_backNeedPath( std::string( RESOURCES_DIR ) + "/extensions/" );
995 Lang::Font::push_backFontMetricsPath( std::string( RESOURCES_DIR ) + "/fontmetrics/" );
997 #endif
999 if( ! filenameRequestList.empty( ) )
1001 std::list< const char * >::const_iterator resource = resourceRequestList.begin( );
1002 for( std::list< int >::const_iterator i = filenameRequestList.begin( );
1003 i != filenameRequestList.end( );
1004 ++i )
1006 switch( *i )
1008 case FILENAME_IN:
1009 if( inputName == "" )
1011 std::cout << "<stdin>" ;
1013 else
1015 std::cout << inputName ;
1017 break;
1018 case FILENAME_OUT:
1019 std::cout << outputName ;
1020 break;
1021 case FILENAME_TMP:
1022 std::cout << tmpDir ;
1023 break;
1024 case FILENAME_TEXJOB:
1025 std::cout << tmpDir << texJobName ;
1026 break;
1027 case FILENAME_LABELDB:
1028 std::cout << labelDBName ;
1029 break;
1030 case FILENAME_AFM:
1031 std::cout << fontmetricsOutputName ;
1032 break;
1033 case FILENAME_TEXINPUTS:
1035 std::cout << getenv( "TEXINPUTS" ) ;
1037 break;
1038 case FILENAME_HTMLDOC:
1040 std::cout << HTMLDIR << "/index.html" ;
1042 break;
1043 case FILENAME_RESOURCE:
1047 std::cout << Ast::theShapesScanner.searchFile( *resource ) ;
1049 catch( const Exceptions::Exception & ball )
1051 std::cout.flush( );
1052 ball.display( std::cerr );
1053 exit( Interaction::EXIT_INVOCATION_ERROR );
1055 ++resource;
1057 break;
1058 default:
1059 std::cerr << "Internal error: filename request switch in main out of range." << std::endl ;
1060 exit( Interaction::EXIT_INTERNAL_ERROR );
1062 std::cout << std::endl ;
1064 exit( Interaction::EXIT_OK );
1067 if( pdfVersion == SimplePDF::PDF_Version::VERSION_UNDEFINED )
1069 pdfVersion = SimplePDF::PDF_Version::PDF_1_4;
1072 Kernel::the_PDF_version.setVersion( pdfVersion );
1073 Kernel::the_PDF_version.setAction( pdfVersionAction );
1076 std::ostringstream oss;
1077 time_t tmp;
1078 time( &tmp );
1079 struct tm * now( gmtime( &tmp ) );
1080 oss << "D:"
1081 << std::setfill( '0' )
1082 << std::setw(4) << 1900 + now->tm_year
1083 << std::setw(2) << 1 + now->tm_mon
1084 << std::setw(2) << now->tm_mday
1085 << std::setw(2) << now->tm_hour
1086 << std::setw(2) << now->tm_min
1087 << std::setw(2) << now->tm_sec
1088 << "+0000" ;
1089 Kernel::theDocInfo.addInfo( "CreationDate", SimplePDF::newString( oss.str( ).c_str( ) ) );
1092 std::ifstream iFile;
1093 if( inputName == "" )
1095 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( "<stdin>" ) );
1096 Ast::theShapesScanner.switch_streams( & std::cin, & std::cerr );
1097 Ast::theShapesScanner.setNameOf_yyin( "stdin" );
1099 else
1101 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( inputName.c_str( ) ) );
1102 iFile.open( inputName.c_str( ) );
1103 if( ! iFile.good( ) || ! iFile.is_open( ) )
1105 std::cerr << "Failed to open " << inputName << " for input." << std::endl ;
1106 exit( Interaction::EXIT_INPUT_FILE_ERROR );
1108 Ast::theShapesScanner.switch_streams( & iFile, & std::cerr );
1109 Ast::theShapesScanner.setNameOf_yyin( inputName.c_str( ) );
1112 std::istringstream prependStreamIn;
1113 if( ! prependStreamOut.str( ).empty( ) )
1115 prependStreamIn.str( prependStreamOut.str( ) );
1116 Ast::theShapesScanner.prependStream( & prependStreamIn );
1119 RefCountPtr< std::ifstream > labelDBFile = RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1120 Kernel::WarmCatalog::ShipoutList documents;
1124 shapesparse( );
1125 Kernel::theGlobalEnvironment = new Kernel::Environment( Kernel::theEnvironmentList );
1126 Kernel::registerGlobals( Kernel::theGlobalEnvironment );
1127 Kernel::registerDynamic( Kernel::theGlobalEnvironment );
1128 Kernel::registerHot( Kernel::theGlobalEnvironment );
1129 Kernel::registerClasses( Kernel::theGlobalEnvironment );
1130 Kernel::registerCore_elem( Kernel::theGlobalEnvironment );
1131 Kernel::registerCore_point( Kernel::theGlobalEnvironment );
1132 Kernel::registerCore_path( Kernel::theGlobalEnvironment );
1133 Kernel::registerCore_draw( Kernel::theGlobalEnvironment );
1134 Kernel::registerCore_construct( Kernel::theGlobalEnvironment );
1135 Kernel::registerCore_font( Kernel::theGlobalEnvironment );
1136 Kernel::registerCore_misc( Kernel::theGlobalEnvironment );
1137 Kernel::registerCore_state( Kernel::theGlobalEnvironment );
1138 Kernel::registerCore_annotation( Kernel::theGlobalEnvironment );
1139 Ast::theGlobalAnalysisEnvironment = Kernel::theGlobalEnvironment->newAnalysisEnvironment( );
1140 Ast::theProgram->analyze( 0, Ast::theGlobalAnalysisEnvironment );
1141 if( ! Ast::theAnalysisErrorsList.empty( ) )
1143 std::cout.flush( );
1144 typedef typeof Ast::theAnalysisErrorsList ListType;
1145 for( ListType::const_iterator i = Ast::theAnalysisErrorsList.begin( ); i != Ast::theAnalysisErrorsList.end( ); ++i )
1148 typedef const Exceptions::StaticInconsistency ErrorType;
1149 ErrorType * err = dynamic_cast< ErrorType * >( *i );
1150 if( err != 0 )
1152 std::cerr << err->loc( ) << ": " ;
1153 err->display( std::cerr );
1154 continue;
1157 std::cerr << "(Bad exception type)" << ": " ;
1158 (*i)->display( std::cerr );
1160 abortProcedure( Interaction::EXIT_USER_ERROR );
1163 // The display unit is looked up after the input is scanned, so the user may use her own units
1164 Interaction::displayUnitFactor = Ast::theShapesScanner.lookupUnitFactor( Interaction::displayUnitName );
1165 if( Interaction::displayUnitFactor <= 0 )
1167 std::cerr << "Invalid display unit: " << Interaction::displayUnitName << std::endl ;
1168 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
1170 labelDBFile = performIterativeStartup( labelDBName );
1171 RefCountPtr< const Kernel::GraphicsState > graphicsState( new Kernel::GraphicsState( true ) );
1172 Kernel::PassedDyn baseDyn( new Kernel::DynamicEnvironment( graphicsState ) );
1174 bool done = false;
1175 Kernel::EvalState evalState( Ast::theProgram,
1176 Kernel::theGlobalEnvironment,
1177 baseDyn,
1178 Kernel::ContRef( new Kernel::ExitVoidContinuation( & done, Ast::theProgram->loc( ) ) ) );
1181 while( ! done )
1183 if( evalTrace )
1185 if( evalBackTrace )
1187 evalState.cont_->backTrace( std::cerr );
1188 std::cerr << "--- Bottom of trace ---" << std::endl ;
1190 else
1192 std::cerr << "Eval trace: Cont: " << evalState.cont_->traceLoc( ) << " Expr: " << evalState.expr_->loc( ) << std::endl ;
1195 Ast::Expression * expr = evalState.expr_;
1196 expr->eval( & evalState );
1199 catch( const Exceptions::StaticInconsistency & ball )
1201 std::cout.flush( );
1202 std::cerr << ball.loc( ) << ": " ;
1203 ball.display( std::cerr );
1204 abortProcedure( ball.exitCode( ) );
1206 catch( Exceptions::Exception & ball )
1208 std::cout.flush( );
1209 if( Interaction::debugBacktrace )
1211 evalState.cont_->backTrace( std::cerr );
1214 std::cerr << evalState.cont_->traceLoc( ) << Exceptions::Exception::locsep ;
1215 ball.display( std::cerr );
1216 abortProcedure( ball.exitCode( ) );
1219 Kernel::WarmCatalog * catalog = dynamic_cast< Kernel::WarmCatalog * >( Kernel::theGlobalEnvironment->getStateHandle( Ast::theGlobalAnalysisEnvironment->findLocalStatePosition( Ast::THE_UNKNOWN_LOCATION, Lang::CATALOG_ID ) ) );
1220 RefCountPtr< const Lang::Group2D > finalPicture = dynamic_cast< Kernel::WarmGroup2D * >( Kernel::theGlobalEnvironment->getStateHandle( Ast::theGlobalAnalysisEnvironment->findLocalStatePosition( Ast::THE_UNKNOWN_LOCATION, Lang::CANVAS_ID ) ) )->getPile( );
1221 if( catalog->isEmpty( ) && finalPicture->isNull( ) )
1223 throw Exceptions::EmptyFinalPicture( );
1225 if( catalog->isEmpty( ) )
1227 catalog->tackOnPage( baseDyn, finalPicture, Ast::THE_UNKNOWN_LOCATION );
1230 catalog->shipout( splitMode != SPLIT_NO, & documents );
1232 if( cleanupMemory )
1234 Kernel::theGlobalEnvironment->clear( );
1237 delete Kernel::theGlobalEnvironment;
1238 delete Ast::theProgram;
1240 catch( const Exceptions::StaticInconsistency & ball )
1242 std::cout.flush( );
1243 std::cerr << ball.loc( ) << ": " ;
1244 ball.display( std::cerr );
1245 abortProcedure( ball.exitCode( ) );
1247 catch( const Exceptions::Exception & ball )
1249 std::cout.flush( );
1250 ball.display( std::cerr );
1251 abortProcedure( ball.exitCode( ) );
1253 catch( const NonLocalExit::DynamicBindingNotFound & ball )
1255 std::cerr << "Caught DynamicBindingNotFound at top level." << std::endl
1256 << "This should really not be possible; it is an internal error." << std::endl ;
1257 exit( Interaction::EXIT_INTERNAL_ERROR );
1259 catch( const NonLocalExit::NotThisType & ball )
1261 std::cerr << "Caught NotThisType at top level." << std::endl
1262 << "This should really not be possible; it is an internal error." << std::endl ;
1263 exit( Interaction::EXIT_INTERNAL_ERROR );
1265 catch( const NonLocalExit::NonLocalExitBase & ball )
1267 std::cerr << "Caught an unknown descendant to NonLocalExitBase at top level." << std::endl
1268 << "This should really not be possible; it is an internal error." << std::endl ;
1269 exit( Interaction::EXIT_INTERNAL_ERROR );
1271 catch( const char * ball )
1273 std::cerr << "Caught (char*) ball at top level:" << std::endl
1274 << " " << ball << std::endl ;
1275 exit( Interaction::EXIT_GENERIC_ERROR );
1277 catch( const std::string & ball )
1279 std::cerr << "Caught (string) ball at top level:" << std::endl
1280 << " " << ball << std::endl ;
1281 exit( Interaction::EXIT_GENERIC_ERROR );
1283 catch( ... )
1285 std::cerr << "Caught (...) ball at top level." << std::endl ;
1286 exit( Interaction::EXIT_GENERIC_ERROR );
1289 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1291 abortProcedure( Interaction::EXIT_USER_ERROR );
1294 if( memoryStats )
1296 std::cerr << "Summary:" << std::endl ;
1297 std::cerr << "Environments: alive: " << Kernel::Environment::liveCount << " of total: " << Kernel::Environment::createdCount
1298 << " (" << 100 * static_cast< double >( Kernel::Environment::liveCount ) / static_cast< double >( Kernel::Environment::createdCount ) << "%)" << std::endl ;
1301 switch( splitMode )
1303 case SPLIT_NO:
1305 if( documents.size( ) != 1 )
1307 std::cerr << "Internal error: Failed to produce exactly one document of output although --split=no." << std::endl ;
1309 std::ofstream oFile;
1310 oFile.open( outputName.c_str( ) );
1311 if( ! oFile.good( ) )
1313 /* If this is because the output directory does not exist, we shall inform the user about this. */
1314 std::string::size_type slash = outputName.rfind( '/' );
1315 if( slash != std::string::npos )
1317 std::string outputDir = outputName.substr( 0, slash );
1318 struct stat theStat;
1319 if( stat( outputDir.c_str( ), & theStat ) == 0 )
1321 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1323 std::cerr << "The prefix " << outputDir << " of the output name must be a directory." << std::endl ;
1324 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1326 /* In case we reach here, the directory exists, so we fall back on the generic error message below. */
1328 else
1330 std::cerr << "The file " << outputName << " cannot be opened for output since the directory " << outputDir << " does not exist." << std::endl ;
1331 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1334 std::cerr << "Failed to open " << outputName << " for output." << std::endl ;
1335 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1337 documents.front( ).writeFile( oFile, Kernel::the_PDF_version );
1339 break;
1340 case SPLIT_FLAT:
1342 std::ostringstream rmCommand;
1343 rmCommand << "sh -O extglob -c 'rm -f " ;
1344 escapeExtGlobChars( outputName, rmCommand );
1345 rmCommand << "-+([0-9]).pdf'" ;
1346 Interaction::systemDebugMessage( rmCommand.str( ) );
1347 if( system( rmCommand.str( ).c_str( ) ) != 0 )
1349 /* Never mind; we made a try, and this probably means that there were no files to remove. */
1351 size_t physicalPageNo = 1;
1352 for( Kernel::WarmCatalog::ShipoutList::iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1354 std::ostringstream tmpFilename;
1355 tmpFilename << outputName << "-" << physicalPageNo << ".pdf" ;
1356 std::ofstream oFile;
1357 oFile.open( tmpFilename.str( ).c_str( ) );
1358 if( ! oFile.good( ) )
1360 std::cerr << "Failed to open " << tmpFilename.str( ) << " for output." << std::endl ;
1361 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1363 i->writeFile( oFile, Kernel::the_PDF_version );
1366 break;
1367 case SPLIT_DIR:
1369 struct stat theStat;
1370 if( stat( outputName.c_str( ), & theStat ) == 0 )
1372 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1374 std::cerr << "The path " << outputName << " was expected to reference a directory." << std::endl ;
1375 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1378 else
1380 if( mkdir( outputName.c_str( ), S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
1382 std::cerr << "Failed to create directory for split document files (errno=" << errno << "): " << outputName << std::endl ;
1383 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1386 std::ostringstream rmCommand;
1387 rmCommand << "sh -O extglob -c 'rm -f " ;
1388 escapeExtGlobChars( outputName, rmCommand );
1389 rmCommand << "/+([0-9]).pdf'" ;
1390 Interaction::systemDebugMessage( rmCommand.str( ) );
1391 if( system( rmCommand.str( ).c_str( ) ) != 0 )
1393 /* Never mind; we made a try, and this probably means that there were no files to remove. */
1395 size_t physicalPageNo = 1;
1396 for( Kernel::WarmCatalog::ShipoutList::iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1398 std::ostringstream tmpFilename;
1399 tmpFilename << outputName << "/" << physicalPageNo << ".pdf" ;
1400 std::ofstream oFile;
1401 oFile.open( tmpFilename.str( ).c_str( ) );
1402 if( ! oFile.good( ) )
1404 std::cerr << "Failed to open " << tmpFilename.str( ) << " for output." << std::endl ;
1405 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1407 i->writeFile( oFile, Kernel::the_PDF_version );
1410 break;
1413 /* This must be done after the output has been written, and before iterativeFinish writes to the labels database file.
1415 Kernel::thePDFImporter.free( );
1417 if( labelDBFile != NullPtr< std::ifstream >( ) )
1419 if( labelDBFile->is_open( ) )
1421 labelDBFile->close( );
1423 labelDBFile = NullPtr< std::ifstream >( ); // Free the reference.
1426 if( iterativeMode )
1428 Kernel::theTeXLabelManager.iterativeFinish( labelDBName );
1431 if( launch_xpdf )
1433 if( splitMode != SPLIT_NO )
1435 std::cerr << "Warning: not launching viewer since the documet was split" << std::endl ;
1437 else
1439 xpdfHelper( outputName, xpdfServer, xpdfAction );
1443 if( do_open )
1445 if( splitMode != SPLIT_NO )
1447 std::cerr << "Warning: not launching viewer since the documet was split" << std::endl ;
1449 else
1451 openHelper( outputName, do_open_application );
1455 destroyGlobals( );
1457 return 0;
1461 void
1462 argcAssertion( const char * optionSpecifier, int argc, int argcMin )
1464 if( argc < argcMin )
1466 std::cerr << "The command line option " << optionSpecifier << " requires " << argcMin - 1 << " parameters." << std::endl ;
1467 exit( Interaction::EXIT_INVOCATION_ERROR );
1471 bool
1472 strprefixcmp( char * str, const char * prefix, char ** endp )
1474 int len = strlen( prefix );
1475 bool res = ( strncmp( str, prefix, len ) == 0 );
1476 *endp = str + len;
1477 return res;
1480 bool
1481 strtobool( const char * str, const char * containingString, const char * trueLabel, const char * falseLabel )
1483 if( trueLabel != 0 &&
1484 strcmp( str, trueLabel ) == 0 )
1486 return true;
1488 if( falseLabel != 0 &&
1489 strcmp( str, falseLabel ) == 0 )
1491 return false;
1493 if( strcmp( str, "yes" ) == 0 ||
1494 strcmp( str, "true" ) == 0 ||
1495 strcmp( str, "on" ) == 0 )
1497 return true;
1499 if( strcmp( str, "no" ) == 0 ||
1500 strcmp( str, "false" ) == 0 ||
1501 strcmp( str, "off" ) == 0)
1503 return false;
1505 std::cerr << "The string \"" << str << "\" in the command line argument \"" << containingString << "\" was not recognized as a boolean value." << std::endl ;
1506 exit( Interaction::EXIT_INVOCATION_ERROR );
1510 RefCountPtr< std::ifstream >
1511 performIterativeStartup( const std::string & labelDBName )
1514 struct stat theStat;
1515 if( stat( labelDBName.c_str( ), & theStat ) != 0 )
1517 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1520 // {
1521 // ostringstream mvCommand;
1522 // mvCommand << "cp '" << oldFilename.str( ) << "' '" << labelDBName.str( ) << "'" ;
1523 // Interaction::systemDebugMessage( mvCommand.str( ) );
1524 // if( system( mvCommand.str( ).c_str( ) ) != 0 )
1525 // {
1526 // return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1527 // }
1528 // }
1529 RefCountPtr< std::ifstream > labelsFile( new std::ifstream( labelDBName.c_str( ) ) );
1530 if( ! labelsFile->good( ) )
1532 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1536 Kernel::theTeXLabelManager.iterativeStartup( labelsFile );
1537 return labelsFile;
1539 catch( const char * ball )
1541 std::cerr << "Caught (char*) ball from iterative startup:" << std::endl
1542 << " " << ball << std::endl ;
1543 exit( Interaction::EXIT_GENERIC_ERROR );
1545 catch( const std::string & ball )
1547 std::cerr << "Caught (string) ball from iterative startup:" << std::endl
1548 << " " << ball << std::endl ;
1549 exit( Interaction::EXIT_GENERIC_ERROR );
1551 catch( const Exceptions::Exception & ball )
1553 ball.display( std::cerr );
1554 exit( ball.exitCode( ) );
1556 catch( ... )
1558 std::cerr << "Caught (...) ball from iterative startup." << std::endl ;
1559 exit( Interaction::EXIT_GENERIC_ERROR );
1564 void
1565 abortProcedure( int exitCode )
1567 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1569 std::cout.flush( );
1570 while( ! Kernel::thePostCheckErrorsList.empty( ) )
1572 Exceptions::Exception * e = Kernel::thePostCheckErrorsList.front( );
1573 Kernel::thePostCheckErrorsList.pop_front( );
1575 typedef const Exceptions::PostCondition ErrorType;
1576 ErrorType * err = dynamic_cast< ErrorType * >( e );
1577 if( err != 0 )
1579 std::cerr << err->loc( ) << ": " ;
1580 err->display( std::cerr );
1581 continue;
1585 typedef const Exceptions::RuntimeError ErrorType;
1586 ErrorType * err = dynamic_cast< ErrorType * >( e );
1587 if( err != 0 )
1589 std::cerr << err->getLoc( ) << " (runtime): " ;
1590 err->display( std::cerr );
1591 continue;
1594 std::cerr << "(Bad post-exception type)" << ": " ;
1595 e->display( std::cerr );
1598 std::cerr << "Aborting job. Output files are left unchanged." << std::endl ;
1599 exit( exitCode );
1602 namespace Shapes
1604 namespace Helpers
1606 void setSelfRef( RefCountPtr< const Lang::Class > cls )
1608 cls->setSelfRef( cls );
1610 void initMutators( RefCountPtr< const Lang::Class > cls )
1612 Lang::SystemFinalClass * typedPtr = dynamic_cast< Lang::SystemFinalClass * >( const_cast< Lang::Class * >( cls.getPtr( ) ) );
1613 if( typedPtr != 0 )
1615 typedPtr->initMutators( );
1617 else
1619 std::cerr << "Helpers::initMutators was called with object of bad type." << std::endl ;
1620 exit( Interaction::EXIT_INTERNAL_ERROR );
1626 void
1627 setupGlobals( )
1629 Lang::ElementaryPath2D * bbox = new Lang::ElementaryPath2D;
1630 bbox->push_back( new Concrete::PathPoint2D( 0, 0 ) );
1631 bbox->close( );
1632 Lang::THE_POINTPICTURE = RefCountPtr< Lang::Drawable2D >( new Lang::BBoxed2D( Lang::THE_NULL2D,
1633 RefCountPtr< Lang::ElementaryPath2D >( bbox ) ) );
1634 Helpers::setSelfRef( Lang::THE_OBJECT );
1636 Helpers::setSelfRef( Lang::Void::TypeID );
1637 Helpers::setSelfRef( Lang::Symbol::TypeID );
1638 Helpers::setSelfRef( Lang::Float::TypeID );
1639 Helpers::setSelfRef( Lang::Length::TypeID );
1640 Helpers::setSelfRef( Lang::Boolean::TypeID );
1641 Helpers::setSelfRef( Lang::String::TypeID );
1642 Helpers::setSelfRef( Lang::FloatPair::TypeID );
1643 Helpers::setSelfRef( Lang::FloatTriple::TypeID );
1644 Helpers::setSelfRef( Lang::Coords2D::TypeID );
1645 Helpers::setSelfRef( Lang::CornerCoords2D::TypeID );
1646 Helpers::setSelfRef( Lang::Coords3D::TypeID );
1648 Helpers::setSelfRef( Lang::Function::TypeID );
1649 Helpers::setSelfRef( Lang::Transform2D::TypeID );
1650 Helpers::setSelfRef( Lang::Transform3D::TypeID );
1652 Helpers::setSelfRef( Lang::Class::TypeID );
1654 Helpers::setSelfRef( Lang::TransformedInstance::TypeID );
1656 Helpers::initMutators( Kernel::WarmGroup2D::TypeID );
1657 Helpers::initMutators( Kernel::WarmGroup3D::TypeID );
1658 Helpers::initMutators( Kernel::WarmCatalog::TypeID );
1661 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric2D" ) );
1662 Lang::Geometric2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1663 Helpers::setSelfRef( Lang::Geometric2D::TypeID );
1664 /* Note that addVirtual must not be called before the selfRef is set!
1668 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric3D" ) );
1669 Lang::Geometric3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1670 Helpers::setSelfRef( Lang::Geometric3D::TypeID );
1671 /* Note that addVirtual must not be called before the selfRef is set!
1675 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable2D" ) );
1676 Lang::Drawable2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1677 Helpers::setSelfRef( Lang::Drawable2D::TypeID );
1678 /* Note that addVirtual must not be called before the selfRef is set!
1680 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1683 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable3D" ) );
1684 Lang::Drawable3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1685 Helpers::setSelfRef( Lang::Drawable3D::TypeID );
1686 /* Note that addVirtual must not be called before the selfRef is set!
1688 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1691 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Color" ) );
1692 Lang::Color::TypeID = RefCountPtr< const Lang::Class >( tmp );
1693 Helpers::setSelfRef( Lang::Color::TypeID );
1694 /* Note that addVirtual must not be called before the selfRef is set!
1696 tmp->addVirtual( "stroking" );
1697 tmp->addVirtual( "nonstroking" );
1701 void
1702 xpdfHelper( const std::string & filename, const std::string & server, const XpdfAction & action )
1704 pid_t xpdfProcess = fork( );
1705 if( xpdfProcess == -1 )
1707 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running xpdf." ) );
1710 if( xpdfProcess == 0 ) /* This is the child */
1712 /* The exec call below never returns, so the child process never leaves this if clause.
1713 * Hence, there is no need to create a special else clasuse below.
1715 switch( action )
1717 case XPDF_RAISE:
1718 execlp( "xpdf", "xpdf", "-remote", server.c_str( ), "-raise", filename.c_str( ), static_cast< const char * >( 0 ) );
1719 break;
1720 case XPDF_RELOAD:
1721 execlp( "xpdf", "xpdf", "-remote", server.c_str( ), "-reload", static_cast< const char * >( 0 ) );
1722 break;
1723 case XPDF_QUIT:
1724 execlp( "xpdf", "xpdf", "-remote", server.c_str( ), "-quit", static_cast< const char * >( 0 ) );
1725 break;
1726 case XPDF_NOSERVER:
1727 execlp( "xpdf", "xpdf", filename.c_str( ), static_cast< const char * >( 0 ) );
1728 break;
1729 default:
1730 std::cerr << "Internal error: XpdfAction switch out of range." << std::endl ;
1731 exit( Interaction::EXIT_INTERNAL_ERROR );
1733 if( errno != 0 )
1735 std::cerr << "Recieved errno = " << errno << " from execlp call to xpdf." << std::endl ;
1736 exit( Interaction::EXIT_EXTERNAL_ERROR );
1738 std::cerr << "execlp call to xpdf returned with errno == 0." << std::endl ;
1739 exit( Interaction::EXIT_INTERNAL_ERROR );
1744 void
1745 openHelper( const std::string & filename, const char * application )
1747 pid_t openProcess = fork( );
1748 if( openProcess == -1 )
1750 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running open." ) );
1753 if( openProcess == 0 ) /* This is the child */
1755 /* The exec call below never returns, so the child process never leaves this if clause.
1756 * Hence, there is no need to create a special else clasuse below.
1758 if( application != 0 )
1760 execlp( "open", "open", "-a", application, filename.c_str( ), static_cast< const char * >( 0 ) );
1762 else
1764 execlp( "open", "open", filename.c_str( ), static_cast< const char * >( 0 ) );
1766 if( errno != 0 )
1768 std::cerr << "Recieved errno = " << errno << " from execlp call to open." << std::endl ;
1769 exit( Interaction::EXIT_EXTERNAL_ERROR );
1771 std::cerr << "execlp call to open returned with errno == 0." << std::endl ;
1772 exit( Interaction::EXIT_INTERNAL_ERROR );
1777 void
1778 Interaction::systemDebugMessage( const std::string & msg )
1780 if( Interaction::debugSystem )
1782 std::cerr << "System command: " << msg << std::endl ;
1786 void
1787 addDefaultNeedPath( )
1789 char * start = getenv( "SHAPESINPUTS" );
1790 if( start == 0 )
1792 Ast::theShapesScanner.push_backNeedPath( "./" );
1793 return;
1795 char * tok = strsep( & start, ":" );
1796 while( tok != 0 )
1798 Ast::theShapesScanner.push_backNeedPath( tok );
1799 tok = strsep( & start, ":" );
1803 void
1804 addDefaultFontMetricsPath( )
1806 char * start = getenv( "SHAPESFONTMETRICS" );
1807 if( start == 0 )
1809 return;
1811 char * tok = strsep( & start, ":" );
1812 while( tok != 0 )
1814 Lang::Font::push_backFontMetricsPath( tok );
1815 tok = strsep( & start, ":" );
1819 void
1820 destroyGlobals( )
1822 Helpers::requireUTF8ToMacRomanConverter( true ); // true means "cleanup"
1823 Helpers::requireMacRomanToUTF8Converter( true ); // true means "cleanup"
1824 Helpers::requireUTF8ToWinANSIConverter( true ); // true means "cleanup"
1825 Helpers::requireUTF8ToASCIIConverter( true ); // true means "cleanup"
1826 Helpers::requireUTF8ToUCS4Converter( true ); // true means "cleanup"
1827 Helpers::requireUTF16BEToUCS4Converter( true ); // true means "cleanup"
1828 Helpers::requireGlyphList( true ); // true means "cleanup"
1829 Helpers::requireMacRomanEncoding( true ); // true means "cleanup"
1832 std::string
1833 absoluteFilename( const char * filename )
1835 if( *filename == '/' )
1837 return filename;
1839 return callDir + filename;
1842 std::string
1843 absoluteDirectory( const char * filename )
1845 if( *filename == '\0' )
1847 return callDir;
1849 if( filename[ strlen( filename ) - 1 ] != '/' )
1851 if( *filename == '/' )
1853 return filename + std::string( "/" );
1855 return callDir + filename + "/";
1857 if( *filename == '/' )
1859 return filename;
1861 return callDir + filename;
1864 #include <iomanip>
1866 void
1867 ensureTmpDirectoryExists( const std::string & dirname, bool allowCreate )
1869 struct stat theStat;
1870 if( stat( dirname.c_str( ), & theStat ) == 0 )
1872 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1874 std::cerr << "The path " << dirname << " was expected to reference a directory." << std::endl ;
1875 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1877 // if( ( theStat.st_mode & S_IWOTH ) == 0 )
1878 // {
1879 // std::cerr << "The directory " << dirname << " was expected have write permission for others." << std::endl ;
1880 // exit( Interaction::EXIT_FILE_PERMISSION_ERROR );
1881 // }
1882 return;
1885 if( ! allowCreate )
1887 std::cerr << "The directory for temporaries, " << dirname << ", does not exist and is not allowed to be created. Consider using --tmpdir+ instead of --tmpdir ." << std::endl ;
1888 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1891 size_t i2 = 0; /* We know there's a slash at the first position */
1892 i2 = dirname.find( '/', i2 + 1 );
1893 bool atRoot = true;
1894 while( stat( dirname.substr( 0, i2 ).c_str( ), & theStat ) == 0 )
1896 atRoot = false;
1897 i2 = dirname.find( '/', i2 + 1 );
1899 if( atRoot )
1901 std::cerr << "Shapes will not create directories for temporary files at the root: " << dirname << std::endl ;
1902 exit( Interaction::EXIT_INVOCATION_ERROR );
1905 mode_t oldUmask = umask( 0 ); /* We want to be able to create directories with any permissions. */
1906 while( i2 != std::string::npos )
1908 if( mkdir( dirname.substr( 0, i2 ).c_str( ), theStat.st_mode & ( S_IRWXU | S_IRWXG | S_IRWXO ) ) != 0 )
1910 std::cerr << "Failed to create directory for temporary files (errno=" << errno << "): " << dirname.substr( 0, i2 ) << std::endl ;
1911 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1913 i2 = dirname.find( '/', i2 + 1 );
1915 umask( oldUmask );
1918 void
1919 escapeExtGlobChars( const std::string & str, std::ostream & dst )
1921 const char * special = "*?[+@!";
1922 for( std::string::const_iterator i = str.begin( ); i != str.end( ); ++i )
1924 if( strchr( special, *i ) != 0 )
1926 dst << '\\' ;
1928 dst << *i ;