Merge branch 'ht/mutator'
[shapes.git] / source / main.cc
blob67aac87fe9c1498a7c0285f40891dd92f4a6831e
1 /* This file is part of Shapes.
3 * Shapes is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * any later version.
8 * Shapes is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with Shapes. If not, see <http://www.gnu.org/licenses/>.
16 * Copyright 2008 Henrik Tidefelt
19 #include "Shapes_Kernel_decls.h"
21 #include "simplepdfi.h"
22 #include "simplepdfo.h"
23 #include "globals.h"
24 #include "consts.h"
25 #include "shapesexceptions.h"
26 #include "hottypes.h"
27 #include "multipage.h"
28 #include "continuations.h"
29 #include "charconverters.h"
30 #include "pagecontentstates.h"
31 #include "autoonoff.h"
32 #include "shapesscanner.h"
33 #include "texlabelmanager.h"
34 #include "debuglog.h"
36 #include <iostream>
37 #include <iomanip>
38 #include <fstream>
39 #include <string>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <list>
43 #include <time.h>
44 #include <errno.h>
45 #include <limits>
46 #include <iconv.h>
48 int shapesparse( );
49 extern int shapesdebug;
50 void printVersion( );
52 using namespace Shapes;
53 using namespace SimplePDF;
55 void argcAssertion( const char * optionSpecifier, int argc, int argcMin );
56 bool strprefixcmp( char * str, const char * prefix, char ** endp );
57 bool strtobool( const char * str, const char * containingString, const char * trueLabel = 0, const char * falseLabel = 0 );
58 std::string callDir;
59 std::string absoluteFilename( const char * filename );
60 std::string absoluteDirectory( const char * filename );
61 void ensureTmpDirectoryExists( const std::string & dirname, bool allowCreate );
62 RefCountPtr< std::ifstream > performIterativeStartup( const std::string & texJobName );
63 void abortProcedure( int exitCode );
64 void setupGlobals( );
65 enum XpdfAction{ XPDF_DEFAULT, XPDF_RAISE, XPDF_RELOAD, XPDF_QUIT, XPDF_NOSERVER };
66 void xpdfHelper( const std::string & filename, const std::string & server, const XpdfAction & action );
67 void openHelper( const std::string & filename, const char * application );
68 void addDefaultNeedPath( );
69 void addDefaultFontMetricsPath( );
70 void destroyGlobals( );
71 void escapeExtGlobChars( const std::string & str, std::ostream & dst );
73 namespace Shapes
75 namespace Interaction
77 void systemDebugMessage( const std::string & msg );
81 int
82 main( int argc, char ** argv )
84 srand( time(NULL) );
86 setupGlobals( );
88 bool iterativeMode = true;
89 bool useResources = true;
92 char * cwd = getcwd( 0, 0 );
93 callDir = cwd + std::string( "/" );
94 free( cwd );
97 std::string outDir;
98 std::string tmpDir;
99 bool allowCreateTmpDir = false;
100 std::string baseName;
101 std::string inputName;
102 std::string outputName;
103 std::string texJobName;
104 std::string labelDBName;
105 std::string fontmetricsOutputName;
107 enum FilenameRequests{ FILENAME_RESOURCE, FILENAME_IN, FILENAME_OUT, FILENAME_TMP, FILENAME_TEXJOB, FILENAME_LABELDB, FILENAME_AFM, FILENAME_TEXINPUTS, FILENAME_HTMLDOC };
109 std::list< int > filenameRequestList;
110 std::list< const char * > resourceRequestList;
112 bool evalTrace = false;
113 bool evalBackTrace = false;
114 bool cleanupMemory = true;
115 bool memoryStats = false;
116 bool launch_xpdf = false;
117 SimplePDF::PDF_Version::Version pdfVersion = SimplePDF::PDF_Version::VERSION_UNDEFINED;
118 SimplePDF::PDF_Version::Action pdfVersionAction = SimplePDF::PDF_Version::WARN;
119 XpdfAction xpdfAction = XPDF_DEFAULT;
120 std::string xpdfServer;
121 bool do_open = false;
122 const char * do_open_application = 0;
123 std::ostringstream prependStreamOut;
125 enum SplitMode{ SPLIT_NO, SPLIT_FLAT, SPLIT_DIR };
126 SplitMode splitMode = SPLIT_NO;
128 argc -= 1;
129 argv += 1;
130 while( argc > 0 )
132 char * optionSuffix;
133 if( strcmp( *argv, "--yydebug" ) == 0 )
135 shapesdebug = 1;
136 argv += 1;
137 argc -= 1;
139 else if( strcmp( *argv, "--shapes-debug" ) == 0 )
141 shapesdebug = 1;
142 argv += 1;
143 argc -= 1;
145 else if( strcmp( *argv, "--system-debug" ) == 0 )
147 Interaction::debugSystem = true;
148 argv += 1;
149 argc -= 1;
151 else if( strcmp( *argv, "--afm-debug" ) == 0 )
153 Interaction::fontMetricDebug = true;
154 argv += 1;
155 argc -= 1;
157 else if( strcmp( *argv, "--afm-messages" ) == 0 )
159 Interaction::fontMetricMessages = true;
160 argv += 1;
161 argc -= 1;
163 else if( strcmp( *argv, "--tex-debug" ) == 0 )
165 Interaction::pdfLaTeXInteractionTo_stderr = true;
166 argv += 1;
167 argc -= 1;
169 else if( strcmp( *argv, "--log-globals" ) == 0 )
171 Interaction::logGlobals = true;
172 argv += 1;
173 argc -= 1;
175 else if( strprefixcmp( *argv, "--bytecolumn=", & optionSuffix ) )
177 Interaction::characterColumnInBytes = strtobool( optionSuffix, *argv );
178 argv += 1;
179 argc -= 1;
181 else if( strprefixcmp( *argv, "--debugstep=", & optionSuffix ) )
183 char * endp;
184 int tmp = strtol( optionSuffix, & endp, 10 );
185 if( tmp < 0 )
187 std::cerr << "The --debugstep value must be nonnegative: " << optionSuffix << std::endl ;
188 exit( Interaction::EXIT_INVOCATION_ERROR );
190 Interaction::debugStep = static_cast< size_t >( tmp );
191 argv += 1;
192 argc -= 1;
194 else if( strcmp( *argv, "--debuglog" ) == 0 )
196 argcAssertion( *argv, argc, 2 );
199 Kernel::theDebugLog.setFilename( *( argv + 1 ) );
201 catch( const char * ball )
203 std::cerr << ball << std::endl ;
204 exit( Interaction::EXIT_INVOCATION_ERROR );
206 argv += 2;
207 argc -= 2;
209 else if( strcmp( *argv, "--debuglog-stderr" ) == 0 )
213 Kernel::theDebugLog.setStream( & std::cerr );
215 catch( const char * ball )
217 std::cerr << ball << std::endl ;
218 exit( Interaction::EXIT_INVOCATION_ERROR );
220 argv += 1;
221 argc -= 1;
223 else if( strcmp( *argv, "--debuglog-stdout" ) == 0 )
227 Kernel::theDebugLog.setStream( & std::cout );
229 catch( const char * ball )
231 std::cerr << ball << std::endl ;
232 exit( Interaction::EXIT_INVOCATION_ERROR );
234 argv += 1;
235 argc -= 1;
237 else if( strprefixcmp( *argv, "--dtminerror=", & optionSuffix ) )
239 Computation::dtMinIsError = strtobool( optionSuffix, *argv );
240 argv += 1;
241 argc -= 1;
243 else if( strprefixcmp( *argv, "--fmguesserror=", & optionSuffix ) )
245 Computation::fontMetricGuessIsError = strtobool( optionSuffix, *argv );
246 argv += 1;
247 argc -= 1;
249 else if( strcmp( *argv, "--evaltrace" ) == 0 )
251 evalTrace = true;
252 argv += 1;
253 argc -= 1;
255 else if( strcmp( *argv, "--evalbacktrace" ) == 0 )
257 evalTrace = true;
258 evalBackTrace = true;
259 argv += 1;
260 argc -= 1;
262 else if( strprefixcmp( *argv, "--backtrace=", & optionSuffix ) )
264 Interaction::debugBacktrace = strtobool( optionSuffix, *argv );
265 argv += 1;
266 argc -= 1;
268 else if( strprefixcmp( *argv, "--iteration=", & optionSuffix ) )
270 iterativeMode = strtobool( optionSuffix, *argv );
271 argv += 1;
272 argc -= 1;
274 else if( strprefixcmp( *argv, "--resources=", & optionSuffix ) )
276 useResources = strtobool( optionSuffix, *argv );
277 argv += 1;
278 argc -= 1;
280 else if( strprefixcmp( *argv, "--stats=", & optionSuffix ) )
282 memoryStats = strtobool( optionSuffix, *argv );
283 argv += 1;
284 argc -= 1;
286 else if( strprefixcmp( *argv, "--memclean=", & optionSuffix ) )
288 cleanupMemory = strtobool( optionSuffix, *argv );
289 argv += 1;
290 argc -= 1;
292 else if( strprefixcmp( *argv, "--showfiles=", & optionSuffix ) )
294 Ast::theShapesScanner.setShowFiles( strtobool( optionSuffix, *argv ) );
295 argv += 1;
296 argc -= 1;
298 else if( strprefixcmp( *argv, "--pdf-version=", & optionSuffix ) || /* Note that we use that || shortcuts! */
299 strprefixcmp( *argv, "-v", & optionSuffix ) )
301 if( pdfVersion != SimplePDF::PDF_Version::VERSION_UNDEFINED )
303 std::cerr << "Multiply defined pdf version." << std::endl ;
304 exit( Interaction::EXIT_INVOCATION_ERROR );
307 switch( *optionSuffix )
309 case 'e':
310 pdfVersionAction = SimplePDF::PDF_Version::ERROR;
311 break;
312 case 'w':
313 pdfVersionAction = SimplePDF::PDF_Version::WARN;
314 break;
315 case 's':
316 pdfVersionAction = SimplePDF::PDF_Version::SILENT;
317 break;
318 default:
319 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 ;
320 exit( Interaction::EXIT_INVOCATION_ERROR );
322 ++optionSuffix;
323 if( strncmp( optionSuffix, "1.", 2 ) == 0 &&
324 '1' <= optionSuffix[2] && optionSuffix[2] <= '6' )
326 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 };
327 pdfVersion = versions[ optionSuffix[2] - '1' ];
329 else if( strcmp( optionSuffix, "X" ) == 0 )
331 std::cerr << "Restriction to PDF-X is not implemented, please try using a low version number, such as 1.1 instead." << std::endl ;
333 else
335 std::cerr << "Unsupported pdf version specification: " << optionSuffix << std::endl ;
336 exit( Interaction::EXIT_INVOCATION_ERROR );
338 argv += 1;
339 argc -= 1;
341 else if( strprefixcmp( *argv, "--tp=", & optionSuffix ) )
343 Kernel::allowTransparency = strtobool( optionSuffix, *argv );
344 argv += 1;
345 argc -= 1;
347 else if( strprefixcmp( *argv, "--spot-pair=", & optionSuffix ) )
349 Kernel::allowSingletonPaths = ! strtobool( optionSuffix, *argv );
350 argv += 1;
351 argc -= 1;
353 else if( strprefixcmp( *argv, "--unit=", & optionSuffix ) )
355 Interaction::displayUnitName = optionSuffix;
357 argv += 1;
358 argc -= 1;
360 else if( strprefixcmp( *argv, "--splicingtol=", & optionSuffix ) )
364 Computation::theTrixelizeSplicingTol = Ast::theShapesScanner.strtoLength( optionSuffix );
366 catch( ... )
368 std::cerr << "Argument to --splicing= was not recognized as a length: " << optionSuffix << std::endl ;
369 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
371 if( Computation::theTrixelizeSplicingTol <= 0 )
373 std::cerr << "Argument to --splicingtol not positive: " << Computation::theTrixelizeSplicingTol.offtype< 1, 0 >( ) << std::endl ;
374 exit( Interaction::EXIT_INVOCATION_ERROR );
377 argv += 1;
378 argc -= 1;
380 else if( strprefixcmp( *argv, "--overlaptol=", & optionSuffix ) )
384 Computation::theTrixelizeOverlapTol = Ast::theShapesScanner.strtoLength( optionSuffix );
386 catch( ... )
388 std::cerr << "Argument to --overlaptol= was not recognized as a length: " << optionSuffix << std::endl ;
389 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
391 if( Computation::theTrixelizeOverlapTol <= 0 )
393 std::cerr << "Argument to --overlaptol not positive: " << Computation::theTrixelizeOverlapTol.offtype< 1, 0 >( ) << std::endl ;
394 exit( Interaction::EXIT_INVOCATION_ERROR );
397 argv += 1;
398 argc -= 1;
400 else if( strcmp( *argv, "--needpath" ) == 0 ||
401 strncmp( *argv, "-N", 2 ) == 0 )
403 bool longForm = strncmp( *argv, "--", 2 ) == 0;
405 const char * pth = 0;
406 if( longForm )
408 argcAssertion( *argv, argc, 2 );
409 pth = *( argv + 1 );
411 else
413 pth = (*argv) + 2;
416 if( strchr( pth, ':' ) != 0 )
418 const char * flag = 0;
419 const char * shortFlag = "-N";
420 if( longForm )
422 flag = *argv;
424 else
426 flag = shortFlag;
429 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
430 if( longForm )
432 std::cerr << " (or " << shortFlag << ")" ;
434 std::cerr <<"." << std::endl ;
435 exit( Interaction::EXIT_INVOCATION_ERROR );
438 Ast::theShapesScanner.push_backNeedPath( absoluteDirectory( pth ) );
440 if( longForm )
442 argv += 2;
443 argc -= 2;
445 else
447 argv += 1;
448 argc -= 1;
451 else if( strcmp( *argv, "--fontmetricspath" ) == 0 ||
452 strncmp( *argv, "-M", 2 ) == 0 )
454 bool longForm = strncmp( *argv, "--", 2 ) == 0;
456 const char * pth = 0;
457 if( longForm )
459 argcAssertion( *argv, argc, 2 );
460 pth = *( argv + 1 );
462 else
464 pth = (*argv) + 2;
467 if( strchr( pth, ':' ) != 0 )
469 const char * flag = 0;
470 const char * shortFlag = "-M";
471 if( longForm )
473 flag = *argv;
475 else
477 flag = shortFlag;
480 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
481 if( longForm )
483 std::cerr << " (or " << shortFlag << ")" ;
485 std::cerr <<"." << std::endl ;
486 exit( Interaction::EXIT_INVOCATION_ERROR );
489 Lang::Font::push_backFontMetricsPath( absoluteDirectory( pth ) );
491 if( longForm )
493 argv += 2;
494 argc -= 2;
496 else
498 argv += 1;
499 argc -= 1;
502 else if( strprefixcmp( *argv, "--seed=", & optionSuffix ) )
504 char * endp;
505 long s = strtol( optionSuffix, &endp, 10 );
506 if( *endp != '\0' )
508 std::cerr << "Argument to --seed= was not an integer: " << optionSuffix << std::endl ;
509 exit( Interaction::EXIT_INVOCATION_ERROR );
512 srand( s );
514 argv += 1;
515 argc -= 1;
517 else if( strprefixcmp( *argv, "--arcdelta=", & optionSuffix ) )
521 Computation::the_arcdelta = Ast::theShapesScanner.strtoLength( optionSuffix );
523 catch( ... )
525 std::cerr << "Argument to --arcdelta= was not recognized as a length: " << optionSuffix << std::endl ;
526 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
528 if( Computation::the_arcdelta <= 0 )
530 std::cerr << "Argument to --arcdelta= not positive: " << optionSuffix << std::endl ;
531 exit( Interaction::EXIT_INVOCATION_ERROR );
534 argv += 1;
535 argc -= 1;
537 else if( strprefixcmp( *argv, "--dtmin=", & optionSuffix ) )
539 char * endp;
540 Computation::the_dtMin = strtod( optionSuffix, &endp );
541 if( *endp != '\0' )
543 std::cerr << "Argument to --dtmin= was not a float: " << optionSuffix << std::endl ;
544 exit( Interaction::EXIT_INVOCATION_ERROR );
546 if( Computation::the_dtMin <= 0 )
548 std::cerr << "Argument to --dtmin= not positive: " << Computation::the_dtMin << std::endl ;
549 exit( Interaction::EXIT_INVOCATION_ERROR );
552 argv += 1;
553 argc -= 1;
555 else if( strcmp( *argv, "--prepend" ) == 0 )
557 argcAssertion( *argv, argc, 2 );
558 prependStreamOut << *( argv + 1 ) << std::endl ;
559 argv += 2;
560 argc -= 2;
562 else if( strcmp( *argv, "--base" ) == 0 )
564 argcAssertion( *argv, argc, 2 );
565 if( baseName != "" )
567 std::cerr << "The name base is multiply specified." << std::endl ;
568 exit( Interaction::EXIT_INVOCATION_ERROR );
570 baseName = *( argv + 1 );
571 argv += 2;
572 argc -= 2;
574 else if( strcmp( *argv, "--which" ) == 0 )
576 argcAssertion( *argv, argc, 2 );
577 filenameRequestList.push_back( FILENAME_RESOURCE );
578 resourceRequestList.push_back( *( argv + 1 ) );
579 argv += 2;
580 argc -= 2;
582 else if( strcmp( *argv, "--in" ) == 0 )
584 argcAssertion( *argv, argc, 2 );
585 if( inputName != "" )
587 std::cerr << "The input file is multiply specified." << std::endl ;
588 exit( Interaction::EXIT_INVOCATION_ERROR );
590 inputName = absoluteFilename( *( argv + 1 ) );
591 argv += 2;
592 argc -= 2;
594 else if( strcmp( *argv, "--which-in" ) == 0 )
596 filenameRequestList.push_back( FILENAME_IN );
597 argv += 1;
598 argc -= 1;
600 else if( strcmp( *argv, "--out" ) == 0 )
602 argcAssertion( *argv, argc, 2 );
603 if( outputName != "" )
605 std::cerr << "The output file is multiply specified." << std::endl ;
606 exit( Interaction::EXIT_INVOCATION_ERROR );
608 outputName = absoluteFilename( *( argv + 1 ) );
609 argv += 2;
610 argc -= 2;
612 else if( strcmp( *argv, "--which-out" ) == 0 )
614 filenameRequestList.push_back( FILENAME_OUT );
615 argv += 1;
616 argc -= 1;
618 else if( strcmp( *argv, "--texjob" ) == 0 )
620 argcAssertion( *argv, argc, 2 );
621 if( texJobName != "" )
623 std::cerr << "The tex job name is multiply specified." << std::endl ;
624 exit( Interaction::EXIT_INVOCATION_ERROR );
626 texJobName = *( argv + 1 );
627 if( texJobName.find( '/' ) != std::string::npos )
629 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 ;
630 exit( Interaction::EXIT_INVOCATION_ERROR );
632 argv += 2;
633 argc -= 2;
635 else if( strcmp( *argv, "--which-texjob" ) == 0 )
637 filenameRequestList.push_back( FILENAME_TEXJOB );
638 argv += 1;
639 argc -= 1;
641 else if( strcmp( *argv, "--labeldb" ) == 0 )
643 argcAssertion( *argv, argc, 2 );
644 if( labelDBName != "" )
646 std::cerr << "The label database file is multiply specified." << std::endl ;
647 exit( Interaction::EXIT_INVOCATION_ERROR );
649 labelDBName = absoluteFilename( *( argv + 1 ) );
650 argv += 2;
651 argc -= 2;
653 else if( strcmp( *argv, "--which-labeldb" ) == 0 )
655 filenameRequestList.push_back( FILENAME_LABELDB );
656 argv += 1;
657 argc -= 1;
659 else if( strcmp( *argv, "--afmout" ) == 0 )
661 argcAssertion( *argv, argc, 2 );
662 if( fontmetricsOutputName != "" )
664 std::cerr << "The font metrics output name is multiply specified." << std::endl ;
665 exit( Interaction::EXIT_INVOCATION_ERROR );
667 fontmetricsOutputName = absoluteFilename( *( argv + 1 ) );
668 argv += 2;
669 argc -= 2;
671 else if( strcmp( *argv, "--which-afmout" ) == 0 )
673 filenameRequestList.push_back( FILENAME_AFM );
674 argv += 1;
675 argc -= 1;
677 else if( strcmp( *argv, "--which-TEXINPUTS" ) == 0 )
679 filenameRequestList.push_back( FILENAME_TEXINPUTS );
680 argv += 1;
681 argc -= 1;
683 else if( strcmp( *argv, "--which-doc" ) == 0 )
685 filenameRequestList.push_back( FILENAME_HTMLDOC );
686 argv += 1;
687 argc -= 1;
689 else if( strcmp( *argv, "--outdir" ) == 0 )
691 argcAssertion( *argv, argc, 2 );
692 if( outDir != "" )
694 std::cerr << "The output directory is multiply specified." << std::endl ;
695 exit( Interaction::EXIT_INVOCATION_ERROR );
697 outDir = absoluteDirectory( *( argv + 1 ) );
698 argv += 2;
699 argc -= 2;
701 else if( strprefixcmp( *argv, "--tmp*=", & optionSuffix ) )
703 allowCreateTmpDir = strtobool( optionSuffix, *argv );
704 argv += 1;
705 argc -= 1;
707 else if( strcmp( *argv, "--tmpdir" ) == 0 )
709 argcAssertion( *argv, argc, 2 );
710 if( tmpDir != "" )
712 std::cerr << "The temporaries directory is multiply specified." << std::endl ;
713 exit( Interaction::EXIT_INVOCATION_ERROR );
715 tmpDir = absoluteDirectory( *( argv + 1 ) );
716 argv += 2;
717 argc -= 2;
719 else if( strcmp( *argv, "--which-tmp" ) == 0 )
721 filenameRequestList.push_back( FILENAME_TMP );
722 argv += 1;
723 argc -= 1;
725 else if( strprefixcmp( *argv, "--split=", & optionSuffix ) )
727 if( strcmp( optionSuffix, "no" ) == 0 )
729 splitMode = SPLIT_NO;
731 else if( strcmp( optionSuffix, "flat" ) == 0 )
733 splitMode = SPLIT_FLAT;
735 else if( strcmp( optionSuffix, "dir" ) == 0 )
737 splitMode = SPLIT_DIR;
739 else
741 std::cerr << "The string \"" << optionSuffix << "\" in the command line argument \"" << *argv << "\" was not any of { 'no', 'flat', 'dir' }." << std::endl ;
743 argv += 1;
744 argc -= 1;
746 else if( strcmp( *argv, "--xpdf" ) == 0 )
748 launch_xpdf = true;
749 argv += 1;
750 argc -= 1;
752 else if( strcmp( *argv, "--xpdf-remote" ) == 0 )
754 argcAssertion( *argv, argc, 2 );
755 if( xpdfServer != "" )
757 std::cerr << "The xpdf server is multiply specified." << std::endl ;
758 exit( Interaction::EXIT_INVOCATION_ERROR );
760 xpdfServer = *( argv + 1 );
761 argv += 2;
762 argc -= 2;
764 else if( strcmp( *argv, "--xpdf-no-server" ) == 0 )
766 if( xpdfAction != XPDF_DEFAULT )
768 std::cerr << "The xpdf action is multiply specified." << std::endl ;
769 exit( Interaction::EXIT_INVOCATION_ERROR );
771 xpdfAction = XPDF_NOSERVER;
772 argv += 1;
773 argc -= 1;
775 else if( strcmp( *argv, "--xpdf-reload" ) == 0 )
777 if( xpdfAction != XPDF_DEFAULT )
779 std::cerr << "The xpdf action is multiply specified." << std::endl ;
780 exit( Interaction::EXIT_INVOCATION_ERROR );
782 xpdfAction = XPDF_RELOAD;
783 argv += 1;
784 argc -= 1;
786 else if( strcmp( *argv, "--xpdf-quit" ) == 0 )
788 if( xpdfAction != XPDF_DEFAULT )
790 std::cerr << "The xpdf action is multiply specified." << std::endl ;
791 exit( Interaction::EXIT_INVOCATION_ERROR );
793 xpdfAction = XPDF_QUIT;
794 argv += 1;
795 argc -= 1;
797 else if( strcmp( *argv, "--open" ) == 0 )
799 do_open = true;
800 argv += 1;
801 argc -= 1;
803 else if( strcmp( *argv, "--open-a" ) == 0 )
805 argcAssertion( *argv, argc, 2 );
806 do_open = true;
807 do_open_application = *( argv + 1 );
808 argv += 2;
809 argc -= 2;
811 else if( strcmp( *argv, "--version" ) == 0 )
813 printVersion( );
814 exit( Interaction::EXIT_OK );
816 else if( argc == 1 )
818 if( baseName != "" )
820 std::cerr << "The name base is multiply specified." << std::endl ;
821 exit( Interaction::EXIT_INVOCATION_ERROR );
823 struct stat theStat;
824 if( stat( *argv, & theStat ) == 0 &&
825 ( theStat.st_mode & S_IFDIR ) == 0 ) /* We are not interested in directories here. */
827 inputName = *argv;
828 char * ext = *argv + strlen( *argv ) - 6;
829 if( ext <= *argv )
831 std::cerr << "The file name \"" << *argv << "\" is unexpectedly short (it should include the \".shape\" suffix)." << std::endl ;
832 exit( Interaction::EXIT_INVOCATION_ERROR );
834 if( strcmp( ext, ".shape" ) != 0 )
836 std::cerr << "Expected \".shape\" suffix in the file name \"" << *argv << "\"." << std::endl ;
837 exit( Interaction::EXIT_INVOCATION_ERROR );
840 else
842 if( (*argv)[ strlen( *argv ) - 1 ] == '.' )
844 inputName = std::string( *argv ) + "shape";
846 else
848 inputName = std::string( *argv ) + ".shape";
850 if( ! stat( inputName.c_str( ), & theStat ) == 0 )
852 /* It is not entirely clear what is the best error message here,
853 * as the source file may be specified in several different ways.
854 * This should cause the least confusion.
856 std::cerr << "Failed to locate input file: " << *argv << std::endl ;
857 exit( Interaction::EXIT_INPUT_FILE_ERROR );
861 const char * slash = strrchr( inputName.c_str( ), '/' );
862 if( slash == 0 )
864 slash = inputName.c_str( );
866 else
868 ++slash;
870 size_t skipCount = slash - inputName.c_str( );
871 baseName = inputName.substr( skipCount, inputName.length( ) - skipCount - 6 );
873 argv += 1;
874 argc -= 1;
876 else
878 std::cerr << "Illegal command line option: " << *argv << std::endl ;
879 exit( Interaction::EXIT_INVOCATION_ERROR );
883 if( outDir == "" )
885 outDir = absoluteDirectory( "" );
887 if( tmpDir == "" )
889 char * start = getenv( "SHAPESTMPDIR" );
890 if( start != 0 )
892 tmpDir = absoluteDirectory( start );
894 else
896 tmpDir = absoluteDirectory( "" );
899 ensureTmpDirectoryExists( tmpDir, allowCreateTmpDir );
901 if( baseName == "" )
903 if( texJobName == "" )
905 texJobName = "#shapes.labels";
908 else
910 if( inputName == "" )
912 inputName = absoluteFilename( ( baseName + ".shape" ).c_str( ) );
914 if( outputName == "" )
916 if( splitMode == SPLIT_NO )
918 outputName = outDir + baseName + ".pdf";
920 else
922 outputName = outDir + baseName;
925 if( texJobName == "" )
927 texJobName = baseName + ".labels";
929 if( labelDBName == "" )
931 labelDBName = outDir + baseName + ".labels.pdf";
933 if( fontmetricsOutputName == "" )
935 fontmetricsOutputName = outDir + baseName + ".afm";
939 if( outputName == "" )
941 if( ! filenameRequestList.empty( ) )
943 /* The output name will never really be used, so it's rather harmless to assign a dummy value.
945 outputName = "?.pdf" ;
947 else
949 std::cerr << "The output file is undetermined. Consider specifying it using \"--out <filename>\"." << std::endl ;
950 exit( Interaction::EXIT_INVOCATION_ERROR );
954 if( labelDBName == "" )
956 iterativeMode = false;
959 if( ! Kernel::theDebugLog.initialized( ) )
961 std::string::size_type suffixSep = outputName.rfind( '.' );
962 if( suffixSep != std::string::npos && outputName.find( '/', suffixSep ) == std::string::npos )
964 /* If there would have been a slash after the '.', the dot would have been part of a directory name.
965 * Otherwise, we conclude that we have found the extension of a filename, and replace that extension
966 * by ".log".
968 Kernel::theDebugLog.setFilename( outputName.substr( 0, suffixSep ) + ".log" );
970 else if( baseName != "" )
972 Kernel::theDebugLog.setFilename( outDir + baseName + ".log" );
974 else
976 Kernel::theDebugLog.setFilename( outDir + "#shapes.log" );
981 std::string inDir;
982 std::string inPath = inputName;
983 std::string::size_type slash = inPath.rfind( '/' );
984 if( slash == std::string::npos )
986 inDir = absoluteDirectory( "" );
988 else
990 inDir = absoluteDirectory( inPath.substr( 0, slash ).c_str( ) );
992 Ast::theShapesScanner.setSourceDir( inDir );
993 Kernel::theTeXLabelManager.setup( inDir, tmpDir, texJobName );
996 if( Computation::theTrixelizeSplicingTol >= Computation::theTrixelizeOverlapTol )
998 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 ;
999 exit( Interaction::EXIT_INVOCATION_ERROR );
1002 if( xpdfServer == "" )
1004 xpdfServer = outputName;
1007 if( xpdfAction == XPDF_DEFAULT )
1009 xpdfAction = XPDF_RAISE;
1012 addDefaultNeedPath( );
1013 addDefaultFontMetricsPath( );
1015 #ifdef RESOURCES_DIR
1016 if( useResources )
1018 Ast::theShapesScanner.push_backNeedPath( std::string( RESOURCES_DIR ) + "/extensions/" );
1019 Lang::Font::push_backFontMetricsPath( std::string( RESOURCES_DIR ) + "/fontmetrics/" );
1021 #endif
1023 if( ! filenameRequestList.empty( ) )
1025 std::list< const char * >::const_iterator resource = resourceRequestList.begin( );
1026 for( std::list< int >::const_iterator i = filenameRequestList.begin( );
1027 i != filenameRequestList.end( );
1028 ++i )
1030 switch( *i )
1032 case FILENAME_IN:
1033 if( inputName == "" )
1035 std::cout << "<stdin>" ;
1037 else
1039 std::cout << inputName ;
1041 break;
1042 case FILENAME_OUT:
1043 std::cout << outputName ;
1044 break;
1045 case FILENAME_TMP:
1046 std::cout << tmpDir ;
1047 break;
1048 case FILENAME_TEXJOB:
1049 std::cout << tmpDir << texJobName ;
1050 break;
1051 case FILENAME_LABELDB:
1052 std::cout << labelDBName ;
1053 break;
1054 case FILENAME_AFM:
1055 std::cout << fontmetricsOutputName ;
1056 break;
1057 case FILENAME_TEXINPUTS:
1059 std::cout << getenv( "TEXINPUTS" ) ;
1061 break;
1062 case FILENAME_HTMLDOC:
1064 std::cout << HTMLDIR << "/index.html" ;
1066 break;
1067 case FILENAME_RESOURCE:
1071 std::cout << Ast::theShapesScanner.searchFile( *resource ) ;
1073 catch( const Exceptions::Exception & ball )
1075 std::cout.flush( );
1076 ball.display( std::cerr );
1077 exit( Interaction::EXIT_INVOCATION_ERROR );
1079 ++resource;
1081 break;
1082 default:
1083 std::cerr << "Internal error: filename request switch in main out of range." << std::endl ;
1084 exit( Interaction::EXIT_INTERNAL_ERROR );
1086 std::cout << std::endl ;
1088 exit( Interaction::EXIT_OK );
1091 if( pdfVersion == SimplePDF::PDF_Version::VERSION_UNDEFINED )
1093 pdfVersion = SimplePDF::PDF_Version::PDF_1_4;
1096 Kernel::the_PDF_version.setVersion( pdfVersion );
1097 Kernel::the_PDF_version.setAction( pdfVersionAction );
1100 std::ostringstream oss;
1101 time_t tmp;
1102 time( &tmp );
1103 struct tm * now( gmtime( &tmp ) );
1104 oss << "D:"
1105 << std::setfill( '0' )
1106 << std::setw(4) << 1900 + now->tm_year
1107 << std::setw(2) << 1 + now->tm_mon
1108 << std::setw(2) << now->tm_mday
1109 << std::setw(2) << now->tm_hour
1110 << std::setw(2) << now->tm_min
1111 << std::setw(2) << now->tm_sec
1112 << "+0000" ;
1113 Kernel::theDocInfo.addInfo( "CreationDate", SimplePDF::newString( oss.str( ).c_str( ) ) );
1116 std::ifstream iFile;
1117 if( inputName == "" )
1119 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( "<stdin>" ) );
1120 Ast::theShapesScanner.switch_streams( & std::cin, & std::cerr );
1121 Ast::theShapesScanner.setNameOf_yyin( "stdin" );
1123 else
1125 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( inputName.c_str( ) ) );
1126 iFile.open( inputName.c_str( ) );
1127 if( ! iFile.good( ) || ! iFile.is_open( ) )
1129 std::cerr << "Failed to open " << inputName << " for input." << std::endl ;
1130 exit( Interaction::EXIT_INPUT_FILE_ERROR );
1132 Ast::theShapesScanner.switch_streams( & iFile, & std::cerr );
1133 Ast::theShapesScanner.setNameOf_yyin( inputName.c_str( ) );
1136 std::istringstream prependStreamIn;
1137 if( ! prependStreamOut.str( ).empty( ) )
1139 prependStreamIn.str( prependStreamOut.str( ) );
1140 Ast::theShapesScanner.prependStream( & prependStreamIn );
1143 RefCountPtr< std::ifstream > labelDBFile = RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1144 Kernel::WarmCatalog::ShipoutList documents;
1148 shapesparse( );
1149 Kernel::theGlobalEnvironment = new Kernel::Environment( Kernel::theEnvironmentList );
1150 Kernel::registerGlobals( Kernel::theGlobalEnvironment );
1151 Kernel::registerDynamic( Kernel::theGlobalEnvironment );
1152 Kernel::registerHot( Kernel::theGlobalEnvironment );
1153 Kernel::registerClasses( Kernel::theGlobalEnvironment );
1154 Kernel::registerCore_elem( Kernel::theGlobalEnvironment );
1155 Kernel::registerCore_point( Kernel::theGlobalEnvironment );
1156 Kernel::registerCore_path( Kernel::theGlobalEnvironment );
1157 Kernel::registerCore_draw( Kernel::theGlobalEnvironment );
1158 Kernel::registerCore_construct( Kernel::theGlobalEnvironment );
1159 Kernel::registerCore_font( Kernel::theGlobalEnvironment );
1160 Kernel::registerCore_misc( Kernel::theGlobalEnvironment );
1161 Kernel::registerCore_state( Kernel::theGlobalEnvironment );
1162 Kernel::registerCore_annotation( Kernel::theGlobalEnvironment );
1163 Kernel::registerCore_decomp( Kernel::theGlobalEnvironment );
1164 Ast::theGlobalAnalysisEnvironment = Kernel::theGlobalEnvironment->newAnalysisEnvironment( );
1165 Ast::theProgram->analyze( 0, Ast::theGlobalAnalysisEnvironment );
1166 if( ! Ast::theAnalysisErrorsList.empty( ) )
1168 std::cout.flush( );
1169 typedef typeof Ast::theAnalysisErrorsList ListType;
1170 for( ListType::const_iterator i = Ast::theAnalysisErrorsList.begin( ); i != Ast::theAnalysisErrorsList.end( ); ++i )
1173 typedef const Exceptions::StaticInconsistency ErrorType;
1174 ErrorType * err = dynamic_cast< ErrorType * >( *i );
1175 if( err != 0 )
1177 std::cerr << err->loc( ) << ": " ;
1178 err->display( std::cerr );
1179 continue;
1182 std::cerr << "(Bad exception type)" << ": " ;
1183 (*i)->display( std::cerr );
1185 abortProcedure( Interaction::EXIT_USER_ERROR );
1188 // The display unit is looked up after the input is scanned, so the user may use her own units
1189 Interaction::displayUnitFactor = Ast::theShapesScanner.lookupUnitFactor( Interaction::displayUnitName );
1190 if( Interaction::displayUnitFactor <= 0 )
1192 std::cerr << "Invalid display unit: " << Interaction::displayUnitName << std::endl ;
1193 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
1195 labelDBFile = performIterativeStartup( labelDBName );
1196 RefCountPtr< const Kernel::GraphicsState > graphicsState( new Kernel::GraphicsState( true ) );
1197 Kernel::PassedDyn baseDyn( new Kernel::DynamicEnvironment( graphicsState ) );
1199 bool done = false;
1200 Kernel::EvalState evalState( Ast::theProgram,
1201 Kernel::theGlobalEnvironment,
1202 baseDyn,
1203 Kernel::ContRef( new Kernel::ExitVoidContinuation( & done, Ast::theProgram->loc( ) ) ) );
1206 while( ! done )
1208 if( evalTrace )
1210 if( evalBackTrace )
1212 evalState.cont_->backTrace( std::cerr );
1213 std::cerr << "--- Bottom of trace ---" << std::endl ;
1215 else
1217 std::cerr << "Eval trace: Cont: " << evalState.cont_->traceLoc( ) << " Expr: " << evalState.expr_->loc( ) << std::endl ;
1220 Ast::Expression * expr = evalState.expr_;
1221 expr->eval( & evalState );
1224 catch( const Exceptions::StaticInconsistency & ball )
1226 std::cout.flush( );
1227 std::cerr << ball.loc( ) << ": " ;
1228 ball.display( std::cerr );
1229 abortProcedure( ball.exitCode( ) );
1231 catch( Exceptions::Exception & ball )
1233 std::cout.flush( );
1234 if( Interaction::debugBacktrace )
1236 evalState.cont_->backTrace( std::cerr );
1239 std::cerr << evalState.cont_->traceLoc( ) << Exceptions::Exception::locsep ;
1240 ball.display( std::cerr );
1241 abortProcedure( ball.exitCode( ) );
1244 Kernel::WarmCatalog * catalog = dynamic_cast< Kernel::WarmCatalog * >( Kernel::theGlobalEnvironment->getStateHandle( Ast::theGlobalAnalysisEnvironment->findLocalStatePosition( Ast::THE_UNKNOWN_LOCATION, Lang::CATALOG_ID ) ) );
1245 RefCountPtr< const Lang::Group2D > finalPicture = dynamic_cast< Kernel::WarmGroup2D * >( Kernel::theGlobalEnvironment->getStateHandle( Ast::theGlobalAnalysisEnvironment->findLocalStatePosition( Ast::THE_UNKNOWN_LOCATION, Lang::CANVAS_ID ) ) )->getPile( );
1246 if( catalog->isEmpty( ) && finalPicture->isNull( ) )
1248 throw Exceptions::EmptyFinalPicture( );
1250 if( catalog->isEmpty( ) )
1252 catalog->tackOnPage( baseDyn, finalPicture, Ast::THE_UNKNOWN_LOCATION );
1255 catalog->shipout( splitMode != SPLIT_NO, & documents );
1257 if( cleanupMemory )
1259 Kernel::theGlobalEnvironment->clear( );
1262 delete Kernel::theGlobalEnvironment;
1263 delete Ast::theProgram;
1265 catch( const Exceptions::StaticInconsistency & ball )
1267 std::cout.flush( );
1268 std::cerr << ball.loc( ) << ": " ;
1269 ball.display( std::cerr );
1270 abortProcedure( ball.exitCode( ) );
1272 catch( const Exceptions::Exception & ball )
1274 std::cout.flush( );
1275 ball.display( std::cerr );
1276 abortProcedure( ball.exitCode( ) );
1278 catch( const NonLocalExit::DynamicBindingNotFound & ball )
1280 std::cerr << "Caught DynamicBindingNotFound at top level." << std::endl
1281 << "This should really not be possible; it is an internal error." << std::endl ;
1282 exit( Interaction::EXIT_INTERNAL_ERROR );
1284 catch( const NonLocalExit::NotThisType & ball )
1286 std::cerr << "Caught NotThisType at top level." << std::endl
1287 << "This should really not be possible; it is an internal error." << std::endl ;
1288 exit( Interaction::EXIT_INTERNAL_ERROR );
1290 catch( const NonLocalExit::NonLocalExitBase & ball )
1292 std::cerr << "Caught an unknown descendant to NonLocalExitBase at top level." << std::endl
1293 << "This should really not be possible; it is an internal error." << std::endl ;
1294 exit( Interaction::EXIT_INTERNAL_ERROR );
1296 catch( const char * ball )
1298 std::cerr << "Caught (char*) ball at top level:" << std::endl
1299 << " " << ball << std::endl ;
1300 exit( Interaction::EXIT_GENERIC_ERROR );
1302 catch( const std::string & ball )
1304 std::cerr << "Caught (string) ball at top level:" << std::endl
1305 << " " << ball << std::endl ;
1306 exit( Interaction::EXIT_GENERIC_ERROR );
1308 catch( ... )
1310 std::cerr << "Caught (...) ball at top level." << std::endl ;
1311 exit( Interaction::EXIT_GENERIC_ERROR );
1314 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1316 abortProcedure( Interaction::EXIT_USER_ERROR );
1319 if( memoryStats )
1321 std::cerr << "Summary:" << std::endl ;
1322 std::cerr << "Environments: alive: " << Kernel::Environment::liveCount << " of total: " << Kernel::Environment::createdCount
1323 << " (" << 100 * static_cast< double >( Kernel::Environment::liveCount ) / static_cast< double >( Kernel::Environment::createdCount ) << "%)" << std::endl ;
1326 switch( splitMode )
1328 case SPLIT_NO:
1330 if( documents.size( ) != 1 )
1332 std::cerr << "Internal error: Failed to produce exactly one document of output although --split=no." << std::endl ;
1334 std::ofstream oFile;
1335 oFile.open( outputName.c_str( ) );
1336 if( ! oFile.good( ) )
1338 /* If this is because the output directory does not exist, we shall inform the user about this. */
1339 std::string::size_type slash = outputName.rfind( '/' );
1340 if( slash != std::string::npos )
1342 std::string outputDir = outputName.substr( 0, slash );
1343 struct stat theStat;
1344 if( stat( outputDir.c_str( ), & theStat ) == 0 )
1346 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1348 std::cerr << "The prefix " << outputDir << " of the output name must be a directory." << std::endl ;
1349 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1351 /* In case we reach here, the directory exists, so we fall back on the generic error message below. */
1353 else
1355 std::cerr << "The file " << outputName << " cannot be opened for output since the directory " << outputDir << " does not exist." << std::endl ;
1356 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1359 std::cerr << "Failed to open " << outputName << " for output." << std::endl ;
1360 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1362 documents.front( ).writeFile( oFile, Kernel::the_PDF_version );
1364 break;
1365 case SPLIT_FLAT:
1367 std::ostringstream rmCommand;
1368 rmCommand << "sh -O extglob -c 'rm -f " ;
1369 escapeExtGlobChars( outputName, rmCommand );
1370 rmCommand << "-+([0-9]).pdf'" ;
1371 Interaction::systemDebugMessage( rmCommand.str( ) );
1372 if( system( rmCommand.str( ).c_str( ) ) != 0 )
1374 /* Never mind; we made a try, and this probably means that there were no files to remove. */
1376 size_t physicalPageNo = 1;
1377 for( Kernel::WarmCatalog::ShipoutList::iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1379 std::ostringstream tmpFilename;
1380 tmpFilename << outputName << "-" << physicalPageNo << ".pdf" ;
1381 std::ofstream oFile;
1382 oFile.open( tmpFilename.str( ).c_str( ) );
1383 if( ! oFile.good( ) )
1385 std::cerr << "Failed to open " << tmpFilename.str( ) << " for output." << std::endl ;
1386 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1388 i->writeFile( oFile, Kernel::the_PDF_version );
1391 break;
1392 case SPLIT_DIR:
1394 struct stat theStat;
1395 if( stat( outputName.c_str( ), & theStat ) == 0 )
1397 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1399 std::cerr << "The path " << outputName << " was expected to reference a directory." << std::endl ;
1400 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1403 else
1405 if( mkdir( outputName.c_str( ), S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
1407 std::cerr << "Failed to create directory for split document files (errno=" << errno << "): " << outputName << std::endl ;
1408 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1411 std::ostringstream rmCommand;
1412 rmCommand << "sh -O extglob -c 'rm -f " ;
1413 escapeExtGlobChars( outputName, rmCommand );
1414 rmCommand << "/+([0-9]).pdf'" ;
1415 Interaction::systemDebugMessage( rmCommand.str( ) );
1416 if( system( rmCommand.str( ).c_str( ) ) != 0 )
1418 /* Never mind; we made a try, and this probably means that there were no files to remove. */
1420 size_t physicalPageNo = 1;
1421 for( Kernel::WarmCatalog::ShipoutList::iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1423 std::ostringstream tmpFilename;
1424 tmpFilename << outputName << "/" << physicalPageNo << ".pdf" ;
1425 std::ofstream oFile;
1426 oFile.open( tmpFilename.str( ).c_str( ) );
1427 if( ! oFile.good( ) )
1429 std::cerr << "Failed to open " << tmpFilename.str( ) << " for output." << std::endl ;
1430 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1432 i->writeFile( oFile, Kernel::the_PDF_version );
1435 break;
1438 /* This must be done after the output has been written, and before iterativeFinish writes to the labels database file.
1440 Kernel::thePDFImporter.free( );
1442 if( labelDBFile != NullPtr< std::ifstream >( ) )
1444 if( labelDBFile->is_open( ) )
1446 labelDBFile->close( );
1448 labelDBFile = NullPtr< std::ifstream >( ); // Free the reference.
1451 if( iterativeMode )
1453 Kernel::theTeXLabelManager.iterativeFinish( labelDBName );
1456 if( launch_xpdf )
1458 if( splitMode != SPLIT_NO )
1460 std::cerr << "Warning: not launching viewer since the documet was split" << std::endl ;
1462 else
1464 xpdfHelper( outputName, xpdfServer, xpdfAction );
1468 if( do_open )
1470 if( splitMode != SPLIT_NO )
1472 std::cerr << "Warning: not launching viewer since the documet was split" << std::endl ;
1474 else
1476 openHelper( outputName, do_open_application );
1480 destroyGlobals( );
1482 return 0;
1486 void
1487 argcAssertion( const char * optionSpecifier, int argc, int argcMin )
1489 if( argc < argcMin )
1491 std::cerr << "The command line option " << optionSpecifier << " requires " << argcMin - 1 << " parameters." << std::endl ;
1492 exit( Interaction::EXIT_INVOCATION_ERROR );
1496 bool
1497 strprefixcmp( char * str, const char * prefix, char ** endp )
1499 int len = strlen( prefix );
1500 bool res = ( strncmp( str, prefix, len ) == 0 );
1501 *endp = str + len;
1502 return res;
1505 bool
1506 strtobool( const char * str, const char * containingString, const char * trueLabel, const char * falseLabel )
1508 if( trueLabel != 0 &&
1509 strcmp( str, trueLabel ) == 0 )
1511 return true;
1513 if( falseLabel != 0 &&
1514 strcmp( str, falseLabel ) == 0 )
1516 return false;
1518 if( strcmp( str, "yes" ) == 0 ||
1519 strcmp( str, "true" ) == 0 ||
1520 strcmp( str, "on" ) == 0 )
1522 return true;
1524 if( strcmp( str, "no" ) == 0 ||
1525 strcmp( str, "false" ) == 0 ||
1526 strcmp( str, "off" ) == 0)
1528 return false;
1530 std::cerr << "The string \"" << str << "\" in the command line argument \"" << containingString << "\" was not recognized as a boolean value." << std::endl ;
1531 exit( Interaction::EXIT_INVOCATION_ERROR );
1535 RefCountPtr< std::ifstream >
1536 performIterativeStartup( const std::string & labelDBName )
1539 struct stat theStat;
1540 if( stat( labelDBName.c_str( ), & theStat ) != 0 )
1542 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1545 // {
1546 // ostringstream mvCommand;
1547 // mvCommand << "cp '" << oldFilename.str( ) << "' '" << labelDBName.str( ) << "'" ;
1548 // Interaction::systemDebugMessage( mvCommand.str( ) );
1549 // if( system( mvCommand.str( ).c_str( ) ) != 0 )
1550 // {
1551 // return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1552 // }
1553 // }
1554 RefCountPtr< std::ifstream > labelsFile( new std::ifstream( labelDBName.c_str( ) ) );
1555 if( ! labelsFile->good( ) )
1557 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1561 Kernel::theTeXLabelManager.iterativeStartup( labelsFile );
1562 return labelsFile;
1564 catch( const char * ball )
1566 std::cerr << "Caught (char*) ball from iterative startup:" << std::endl
1567 << " " << ball << std::endl ;
1568 exit( Interaction::EXIT_GENERIC_ERROR );
1570 catch( const std::string & ball )
1572 std::cerr << "Caught (string) ball from iterative startup:" << std::endl
1573 << " " << ball << std::endl ;
1574 exit( Interaction::EXIT_GENERIC_ERROR );
1576 catch( const Exceptions::Exception & ball )
1578 ball.display( std::cerr );
1579 exit( ball.exitCode( ) );
1581 catch( ... )
1583 std::cerr << "Caught (...) ball from iterative startup." << std::endl ;
1584 exit( Interaction::EXIT_GENERIC_ERROR );
1589 void
1590 abortProcedure( int exitCode )
1592 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1594 std::cout.flush( );
1595 while( ! Kernel::thePostCheckErrorsList.empty( ) )
1597 Exceptions::Exception * e = Kernel::thePostCheckErrorsList.front( );
1598 Kernel::thePostCheckErrorsList.pop_front( );
1600 typedef const Exceptions::PostCondition ErrorType;
1601 ErrorType * err = dynamic_cast< ErrorType * >( e );
1602 if( err != 0 )
1604 std::cerr << err->loc( ) << ": " ;
1605 err->display( std::cerr );
1606 continue;
1610 typedef const Exceptions::RuntimeError ErrorType;
1611 ErrorType * err = dynamic_cast< ErrorType * >( e );
1612 if( err != 0 )
1614 std::cerr << err->getLoc( ) << " (runtime): " ;
1615 err->display( std::cerr );
1616 continue;
1619 std::cerr << "(Bad post-exception type)" << ": " ;
1620 e->display( std::cerr );
1623 std::cerr << "Aborting job. Output files are left unchanged." << std::endl ;
1624 exit( exitCode );
1627 namespace Shapes
1629 namespace Helpers
1631 void setSelfRef( RefCountPtr< const Lang::Class > cls )
1633 cls->setSelfRef( cls );
1635 void initMutators( RefCountPtr< const Lang::Class > cls )
1637 Lang::SystemFinalClass * typedPtr = dynamic_cast< Lang::SystemFinalClass * >( const_cast< Lang::Class * >( cls.getPtr( ) ) );
1638 if( typedPtr != 0 )
1640 typedPtr->initMutators( );
1642 else
1644 std::cerr << "Helpers::initMutators was called with object of bad type." << std::endl ;
1645 exit( Interaction::EXIT_INTERNAL_ERROR );
1651 void
1652 setupGlobals( )
1654 Lang::ElementaryPath2D * bbox = new Lang::ElementaryPath2D;
1655 bbox->push_back( new Concrete::PathPoint2D( 0, 0 ) );
1656 bbox->close( );
1657 Lang::THE_POINTPICTURE = RefCountPtr< Lang::Drawable2D >( new Lang::BBoxed2D( Lang::THE_NULL2D,
1658 RefCountPtr< Lang::ElementaryPath2D >( bbox ) ) );
1659 Helpers::setSelfRef( Lang::THE_OBJECT );
1661 Helpers::setSelfRef( Lang::Void::TypeID );
1662 Helpers::setSelfRef( Lang::Symbol::TypeID );
1663 Helpers::setSelfRef( Lang::Float::TypeID );
1664 Helpers::setSelfRef( Lang::Length::TypeID );
1665 Helpers::setSelfRef( Lang::Boolean::TypeID );
1666 Helpers::setSelfRef( Lang::String::TypeID );
1667 Helpers::setSelfRef( Lang::FloatPair::TypeID );
1668 Helpers::setSelfRef( Lang::FloatTriple::TypeID );
1669 Helpers::setSelfRef( Lang::Coords2D::TypeID );
1670 Helpers::setSelfRef( Lang::CornerCoords2D::TypeID );
1671 Helpers::setSelfRef( Lang::Coords3D::TypeID );
1673 Helpers::setSelfRef( Lang::Function::TypeID );
1674 Helpers::setSelfRef( Lang::Transform2D::TypeID );
1675 Helpers::setSelfRef( Lang::Transform3D::TypeID );
1677 Helpers::setSelfRef( Lang::Class::TypeID );
1679 Helpers::setSelfRef( Lang::TransformedInstance::TypeID );
1681 Helpers::initMutators( Kernel::WarmGroup2D::TypeID );
1682 Helpers::initMutators( Kernel::WarmGroup3D::TypeID );
1683 Helpers::initMutators( Kernel::WarmCatalog::TypeID );
1686 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric2D" ) );
1687 Lang::Geometric2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1688 Helpers::setSelfRef( Lang::Geometric2D::TypeID );
1689 /* Note that addVirtual must not be called before the selfRef is set!
1693 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric3D" ) );
1694 Lang::Geometric3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1695 Helpers::setSelfRef( Lang::Geometric3D::TypeID );
1696 /* Note that addVirtual must not be called before the selfRef is set!
1700 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable2D" ) );
1701 Lang::Drawable2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1702 Helpers::setSelfRef( Lang::Drawable2D::TypeID );
1703 /* Note that addVirtual must not be called before the selfRef is set!
1705 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1708 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable3D" ) );
1709 Lang::Drawable3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1710 Helpers::setSelfRef( Lang::Drawable3D::TypeID );
1711 /* Note that addVirtual must not be called before the selfRef is set!
1713 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1716 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Color" ) );
1717 Lang::Color::TypeID = RefCountPtr< const Lang::Class >( tmp );
1718 Helpers::setSelfRef( Lang::Color::TypeID );
1719 /* Note that addVirtual must not be called before the selfRef is set!
1721 tmp->addVirtual( "stroking" );
1722 tmp->addVirtual( "nonstroking" );
1726 void
1727 xpdfHelper( const std::string & filename, const std::string & server, const XpdfAction & action )
1729 pid_t xpdfProcess = fork( );
1730 if( xpdfProcess == -1 )
1732 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running xpdf." ) );
1735 if( xpdfProcess == 0 ) /* This is the child */
1737 /* The exec call below never returns, so the child process never leaves this if clause.
1738 * Hence, there is no need to create a special else clasuse below.
1740 switch( action )
1742 case XPDF_RAISE:
1743 execlp( "xpdf", "xpdf", "-remote", server.c_str( ), "-raise", filename.c_str( ), static_cast< const char * >( 0 ) );
1744 break;
1745 case XPDF_RELOAD:
1746 execlp( "xpdf", "xpdf", "-remote", server.c_str( ), "-reload", static_cast< const char * >( 0 ) );
1747 break;
1748 case XPDF_QUIT:
1749 execlp( "xpdf", "xpdf", "-remote", server.c_str( ), "-quit", static_cast< const char * >( 0 ) );
1750 break;
1751 case XPDF_NOSERVER:
1752 execlp( "xpdf", "xpdf", filename.c_str( ), static_cast< const char * >( 0 ) );
1753 break;
1754 default:
1755 std::cerr << "Internal error: XpdfAction switch out of range." << std::endl ;
1756 exit( Interaction::EXIT_INTERNAL_ERROR );
1758 if( errno != 0 )
1760 std::cerr << "Recieved errno = " << errno << " from execlp call to xpdf." << std::endl ;
1761 exit( Interaction::EXIT_EXTERNAL_ERROR );
1763 std::cerr << "execlp call to xpdf returned with errno == 0." << std::endl ;
1764 exit( Interaction::EXIT_INTERNAL_ERROR );
1769 void
1770 openHelper( const std::string & filename, const char * application )
1772 pid_t openProcess = fork( );
1773 if( openProcess == -1 )
1775 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running open." ) );
1778 if( openProcess == 0 ) /* This is the child */
1780 /* The exec call below never returns, so the child process never leaves this if clause.
1781 * Hence, there is no need to create a special else clasuse below.
1783 if( application != 0 )
1785 execlp( "open", "open", "-a", application, filename.c_str( ), static_cast< const char * >( 0 ) );
1787 else
1789 execlp( "open", "open", filename.c_str( ), static_cast< const char * >( 0 ) );
1791 if( errno != 0 )
1793 std::cerr << "Recieved errno = " << errno << " from execlp call to open." << std::endl ;
1794 exit( Interaction::EXIT_EXTERNAL_ERROR );
1796 std::cerr << "execlp call to open returned with errno == 0." << std::endl ;
1797 exit( Interaction::EXIT_INTERNAL_ERROR );
1802 void
1803 Interaction::systemDebugMessage( const std::string & msg )
1805 if( Interaction::debugSystem )
1807 std::cerr << "System command: " << msg << std::endl ;
1811 void
1812 addDefaultNeedPath( )
1814 char * start = getenv( "SHAPESINPUTS" );
1815 if( start == 0 )
1817 Ast::theShapesScanner.push_backNeedPath( "./" );
1818 return;
1820 char * tok = strsep( & start, ":" );
1821 while( tok != 0 )
1823 Ast::theShapesScanner.push_backNeedPath( tok );
1824 tok = strsep( & start, ":" );
1828 void
1829 addDefaultFontMetricsPath( )
1831 char * start = getenv( "SHAPESFONTMETRICS" );
1832 if( start == 0 )
1834 return;
1836 char * tok = strsep( & start, ":" );
1837 while( tok != 0 )
1839 Lang::Font::push_backFontMetricsPath( tok );
1840 tok = strsep( & start, ":" );
1844 void
1845 destroyGlobals( )
1847 Helpers::requireUTF8ToMacRomanConverter( true ); // true means "cleanup"
1848 Helpers::requireMacRomanToUTF8Converter( true ); // true means "cleanup"
1849 Helpers::requireUTF8ToWinANSIConverter( true ); // true means "cleanup"
1850 Helpers::requireUTF8ToASCIIConverter( true ); // true means "cleanup"
1851 Helpers::requireUTF8ToUCS4Converter( true ); // true means "cleanup"
1852 Helpers::requireUTF16BEToUCS4Converter( true ); // true means "cleanup"
1853 Helpers::requireGlyphList( true ); // true means "cleanup"
1854 Helpers::requireMacRomanEncoding( true ); // true means "cleanup"
1857 std::string
1858 absoluteFilename( const char * filename )
1860 if( *filename == '/' )
1862 return filename;
1864 return callDir + filename;
1867 std::string
1868 absoluteDirectory( const char * filename )
1870 if( *filename == '\0' )
1872 return callDir;
1874 if( filename[ strlen( filename ) - 1 ] != '/' )
1876 if( *filename == '/' )
1878 return filename + std::string( "/" );
1880 return callDir + filename + "/";
1882 if( *filename == '/' )
1884 return filename;
1886 return callDir + filename;
1889 #include <iomanip>
1891 void
1892 ensureTmpDirectoryExists( const std::string & dirname, bool allowCreate )
1894 struct stat theStat;
1895 if( stat( dirname.c_str( ), & theStat ) == 0 )
1897 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1899 std::cerr << "The path " << dirname << " was expected to reference a directory." << std::endl ;
1900 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1902 // if( ( theStat.st_mode & S_IWOTH ) == 0 )
1903 // {
1904 // std::cerr << "The directory " << dirname << " was expected have write permission for others." << std::endl ;
1905 // exit( Interaction::EXIT_FILE_PERMISSION_ERROR );
1906 // }
1907 return;
1910 if( ! allowCreate )
1912 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 ;
1913 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1916 size_t i2 = 0; /* We know there's a slash at the first position */
1917 i2 = dirname.find( '/', i2 + 1 );
1918 bool atRoot = true;
1919 while( stat( dirname.substr( 0, i2 ).c_str( ), & theStat ) == 0 )
1921 atRoot = false;
1922 i2 = dirname.find( '/', i2 + 1 );
1924 if( atRoot )
1926 std::cerr << "Shapes will not create directories for temporary files at the root: " << dirname << std::endl ;
1927 exit( Interaction::EXIT_INVOCATION_ERROR );
1930 mode_t oldUmask = umask( 0 ); /* We want to be able to create directories with any permissions. */
1931 while( i2 != std::string::npos )
1933 if( mkdir( dirname.substr( 0, i2 ).c_str( ), theStat.st_mode & ( S_IRWXU | S_IRWXG | S_IRWXO ) ) != 0 )
1935 std::cerr << "Failed to create directory for temporary files (errno=" << errno << "): " << dirname.substr( 0, i2 ) << std::endl ;
1936 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1938 i2 = dirname.find( '/', i2 + 1 );
1940 umask( oldUmask );
1943 void
1944 escapeExtGlobChars( const std::string & str, std::ostream & dst )
1946 const char * special = "*?[+@!";
1947 for( std::string::const_iterator i = str.begin( ); i != str.end( ); ++i )
1949 if( strchr( special, *i ) != 0 )
1951 dst << '\\' ;
1953 dst << *i ;