Adjust to updated function signature in libpng.
[shapes.git] / source / main.cc
blob811b9086d033835be7e737739c34c8a4f90087b5
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, 2010 Henrik Tidefelt
19 #include "main.h"
21 using namespace Shapes;
22 using namespace SimplePDF;
24 namespace Shapes
26 namespace Interaction
28 void systemDebugMessage( const std::string & msg );
32 Interaction::PreviewOptions::PreviewOptions( )
33 : launch_xpdf( false ),
34 xpdfAction( XPDF_DEFAULT ),
35 do_open( false ),
36 do_open_application( 0 )
37 { }
39 std::string callDir;
40 Shapes::Interaction::PreviewOptions previewOptions;
41 Shapes::Interaction::InteractionFormats interactionFormats;
43 int
44 main( int argc, char ** argv )
46 srand( time(NULL) );
48 setupGlobals( );
50 bool iterativeMode = true;
51 bool useResources = true;
52 bool interactiveMode = false;
55 char * cwd = getcwd( 0, 0 );
56 callDir = cwd + std::string( "/" );
57 free( cwd );
60 std::string outDir;
61 std::string tmpDir;
62 bool allowCreateTmpDir = false;
63 std::string baseName;
64 std::string inputName;
65 std::string outputName;
66 std::string texJobName;
67 std::string labelDBName;
68 std::string fontmetricsOutputName;
70 enum FilenameRequests{ FILENAME_RESOURCE, FILENAME_IN, FILENAME_OUT, FILENAME_TMP, FILENAME_TEXJOB, FILENAME_LABELDB, FILENAME_AFM, FILENAME_TEXINPUTS, FILENAME_HTMLDOC, FILENAME_SHARE };
72 std::list< int > filenameRequestList;
73 std::list< const char * > resourceRequestList;
75 bool evalTrace = false;
76 bool evalBackTrace = false;
77 bool cleanupMemory = true;
78 bool memoryStats = false;
79 SimplePDF::PDF_Version::Version pdfVersion = SimplePDF::PDF_Version::VERSION_UNDEFINED;
80 SimplePDF::PDF_Version::Action pdfVersionAction = SimplePDF::PDF_Version::WARN;
81 std::ostringstream prependStreamOut;
83 SplitMode splitMode = SPLIT_NO;
85 argc -= 1;
86 argv += 1;
88 if( argc > 0 && strcmp( *argv, "--help" ) == 0 )
90 if( argc > 1 )
92 std::cerr << "The --help command line switch must appear alone." << std::endl ;
93 exit( Interaction::EXIT_INVOCATION_ERROR );
95 printHelp( );
96 exit( 0 );
99 while( argc > 0 )
101 const char * optionPrefix;
102 char * optionSuffix;
103 if( strcmp( *argv, "--yydebug" ) == 0 )
105 shapesdebug = 1;
106 argv += 1;
107 argc -= 1;
109 else if( strcmp( *argv, "--shapes-debug" ) == 0 )
111 shapesdebug = 1;
112 argv += 1;
113 argc -= 1;
115 else if( strcmp( *argv, "--system-debug" ) == 0 )
117 Interaction::debugSystem = true;
118 argv += 1;
119 argc -= 1;
121 else if( strcmp( *argv, "--afm-debug" ) == 0 )
123 Interaction::fontMetricDebug = true;
124 argv += 1;
125 argc -= 1;
127 else if( strcmp( *argv, "--afm-messages" ) == 0 )
129 Interaction::fontMetricMessages = true;
130 argv += 1;
131 argc -= 1;
133 else if( strcmp( *argv, "--tex-debug" ) == 0 )
135 Interaction::pdfLaTeXInteractionTo_stderr = true;
136 argv += 1;
137 argc -= 1;
139 else if( strcmp( *argv, "--log-globals" ) == 0 )
141 Interaction::logGlobals = true;
142 argv += 1;
143 argc -= 1;
145 else if( strprefixcmp( *argv, "--bytecolumn=", & optionPrefix, & optionSuffix ) )
147 Interaction::characterColumnInBytes = strtobool( optionSuffix, *argv );
148 argv += 1;
149 argc -= 1;
151 else if( strprefixcmp( *argv, "--debugstep=", & optionPrefix, & optionSuffix ) )
153 char * endp;
154 int tmp = strtol( optionSuffix, & endp, 10 );
155 if( tmp < 0 )
157 std::cerr << "The --debugstep value must be nonnegative: " << optionSuffix << std::endl ;
158 exit( Interaction::EXIT_INVOCATION_ERROR );
160 Interaction::debugStep = static_cast< size_t >( tmp );
161 argv += 1;
162 argc -= 1;
164 else if( strcmp( *argv, "--debuglog" ) == 0 )
166 argcAssertion( *argv, argc, 2 );
169 Kernel::theDebugLog.setFilename( *( argv + 1 ) );
171 catch( const char * ball )
173 std::cerr << ball << std::endl ;
174 exit( Interaction::EXIT_INVOCATION_ERROR );
176 argv += 2;
177 argc -= 2;
179 else if( strcmp( *argv, "--debuglog-stderr" ) == 0 )
183 Kernel::theDebugLog.setStream( & std::cerr );
185 catch( const char * ball )
187 std::cerr << ball << std::endl ;
188 exit( Interaction::EXIT_INVOCATION_ERROR );
190 argv += 1;
191 argc -= 1;
193 else if( strcmp( *argv, "--debuglog-stdout" ) == 0 )
197 Kernel::theDebugLog.setStream( & std::cout );
199 catch( const char * ball )
201 std::cerr << ball << std::endl ;
202 exit( Interaction::EXIT_INVOCATION_ERROR );
204 argv += 1;
205 argc -= 1;
207 else if( strprefixcmp( *argv, "--dtminerror=", & optionPrefix, & optionSuffix ) )
209 Computation::dtMinIsError = strtobool( optionSuffix, *argv );
210 argv += 1;
211 argc -= 1;
213 else if( strprefixcmp( *argv, "--fmguesserror=", & optionPrefix, & optionSuffix ) )
215 Computation::fontMetricGuessIsError = strtobool( optionSuffix, *argv );
216 argv += 1;
217 argc -= 1;
219 else if( strcmp( *argv, "--interactive") == 0 ||
220 strcmp( *argv, "-i") == 0 )
222 interactiveMode = true;
223 argv += 1;
224 argc -= 1;
226 else if( strprefixcmp( *argv, "--i-format-prompt=", & optionPrefix, & optionSuffix ) ||
227 strprefixcmp( *argv, "-?P", & optionPrefix, & optionSuffix ) )
229 interactionFormats.setPrompt( strdup( optionSuffix ) );
230 argv += 1;
231 argc -= 1;
233 else if( strprefixcmp( *argv, "--i-format-show=", & optionPrefix, & optionSuffix ) ||
234 strprefixcmp( *argv, "-?S", & optionPrefix, & optionSuffix ) )
236 interactionFormats.setShow( strdup( optionSuffix ) );
237 argv += 1;
238 argc -= 1;
240 else if( strprefixcmp( *argv, "--i-format-file=", & optionPrefix, & optionSuffix ) ||
241 strprefixcmp( *argv, "-?F", & optionPrefix, & optionSuffix ) )
243 interactionFormats.setFile( strdup( optionSuffix ) );
244 argv += 1;
245 argc -= 1;
247 else if( strprefixcmp( *argv, "--i-format-eof=", & optionPrefix, & optionSuffix ) ||
248 strprefixcmp( *argv, "-?E", & optionPrefix, & optionSuffix ) )
250 interactionFormats.setBye( strdup( optionSuffix ) );
251 argv += 1;
252 argc -= 1;
254 else if( strcmp( *argv, "--evaltrace" ) == 0 )
256 evalTrace = true;
257 argv += 1;
258 argc -= 1;
260 else if( strcmp( *argv, "--evalbacktrace" ) == 0 )
262 evalTrace = true;
263 evalBackTrace = true;
264 argv += 1;
265 argc -= 1;
267 else if( strprefixcmp( *argv, "--backtrace=", & optionPrefix, & optionSuffix ) )
269 Interaction::debugBacktrace = strtobool( optionSuffix, *argv );
270 argv += 1;
271 argc -= 1;
273 else if( strprefixcmp( *argv, "--iteration=", & optionPrefix, & optionSuffix ) )
275 iterativeMode = strtobool( optionSuffix, *argv );
276 argv += 1;
277 argc -= 1;
279 else if( strprefixcmp( *argv, "--resources=", & optionPrefix, & optionSuffix ) )
281 useResources = strtobool( optionSuffix, *argv );
282 argv += 1;
283 argc -= 1;
285 else if( strprefixcmp( *argv, "--stats=", & optionPrefix, & optionSuffix ) )
287 memoryStats = strtobool( optionSuffix, *argv );
288 argv += 1;
289 argc -= 1;
291 else if( strprefixcmp( *argv, "--memclean=", & optionPrefix, & optionSuffix ) )
293 cleanupMemory = strtobool( optionSuffix, *argv );
294 argv += 1;
295 argc -= 1;
297 else if( strprefixcmp( *argv, "--showfiles=", & optionPrefix, & optionSuffix ) )
299 Ast::theShapesScanner.setShowFiles( strtobool( optionSuffix, *argv ) );
300 argv += 1;
301 argc -= 1;
303 else if( strprefixcmp( *argv, "--pdf-version=", & optionPrefix, & optionSuffix ) || /* Note that we use that || shortcuts! */
304 strprefixcmp( *argv, "-v", & optionPrefix, & optionSuffix ) )
306 if( pdfVersion != SimplePDF::PDF_Version::VERSION_UNDEFINED )
308 std::cerr << "Multiply defined pdf version." << std::endl ;
309 exit( Interaction::EXIT_INVOCATION_ERROR );
312 switch( *optionSuffix )
314 case 'e':
315 pdfVersionAction = SimplePDF::PDF_Version::ERROR;
316 break;
317 case 'w':
318 pdfVersionAction = SimplePDF::PDF_Version::WARN;
319 break;
320 case 's':
321 pdfVersionAction = SimplePDF::PDF_Version::SILENT;
322 break;
323 default:
324 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 ;
325 exit( Interaction::EXIT_INVOCATION_ERROR );
327 ++optionSuffix;
328 if( strncmp( optionSuffix, "1.", 2 ) == 0 &&
329 '1' <= optionSuffix[2] && optionSuffix[2] <= '6' )
331 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 };
332 pdfVersion = versions[ optionSuffix[2] - '1' ];
334 else if( strcmp( optionSuffix, "X" ) == 0 )
336 std::cerr << "Restriction to PDF-X is not implemented, please try using a low version number, such as 1.1 instead." << std::endl ;
338 else
340 std::cerr << "Unsupported pdf version specification: " << optionSuffix << std::endl ;
341 exit( Interaction::EXIT_INVOCATION_ERROR );
343 argv += 1;
344 argc -= 1;
346 else if( strprefixcmp( *argv, "--warn=", & optionPrefix, & optionSuffix ) ||
347 strprefixcmp( *argv, "-W", & optionPrefix, & optionSuffix ) )
349 if( strcmp( optionSuffix, "display" ) == 0 ||
350 strcmp( optionSuffix, "d" ) == 0 )
352 Interaction::warningAction = Interaction::WARNING_DISPLAY;
354 else if( strcmp( optionSuffix, "error" ) == 0 ||
355 strcmp( optionSuffix, "e" ) == 0 )
357 Interaction::warningAction = Interaction::WARNING_ERROR;
359 else if( strcmp( optionSuffix, "ignore" ) == 0 ||
360 strcmp( optionSuffix, "i" ) == 0 )
362 Interaction::warningAction = Interaction::WARNING_IGNORE;
364 else
366 std::cerr << "Unsupported warning action: " << optionSuffix << std::endl ;
367 exit( Interaction::EXIT_INVOCATION_ERROR );
369 argv += 1;
370 argc -= 1;
372 else if( strprefixcmp( *argv, "--tp=", & optionPrefix, & optionSuffix ) )
374 Kernel::allowTransparency = strtobool( optionSuffix, *argv );
375 argv += 1;
376 argc -= 1;
378 else if( strprefixcmp( *argv, "--spot-pair=", & optionPrefix, & optionSuffix ) )
380 Kernel::allowSingletonPaths = ! strtobool( optionSuffix, *argv );
381 argv += 1;
382 argc -= 1;
384 else if( strprefixcmp( *argv, "--unit=", & optionPrefix, & optionSuffix ) )
386 Interaction::displayUnitName = optionSuffix;
388 argv += 1;
389 argc -= 1;
391 else if( strprefixcmp( *argv, "--splicingtol=", & optionPrefix, & optionSuffix ) )
395 Computation::theTrixelizeSplicingTol = Ast::theShapesScanner.strtoLength( optionSuffix );
397 catch( ... )
399 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
400 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
402 if( Computation::theTrixelizeSplicingTol <= 0 )
404 std::cerr << "Argument to " << optionPrefix << " not positive: " << Computation::theTrixelizeSplicingTol.offtype< 1, 0 >( ) << std::endl ;
405 exit( Interaction::EXIT_INVOCATION_ERROR );
408 argv += 1;
409 argc -= 1;
411 else if( strprefixcmp( *argv, "--overlaptol=", & optionPrefix, & optionSuffix ) )
415 Computation::theTrixelizeOverlapTol = Ast::theShapesScanner.strtoLength( optionSuffix );
417 catch( ... )
419 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
420 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
422 if( Computation::theTrixelizeOverlapTol <= 0 )
424 std::cerr << "Argument to " << optionPrefix << " not positive: " << Computation::theTrixelizeOverlapTol.offtype< 1, 0 >( ) << std::endl ;
425 exit( Interaction::EXIT_INVOCATION_ERROR );
428 argv += 1;
429 argc -= 1;
431 else if( strcmp( *argv, "--needpath" ) == 0 ||
432 strncmp( *argv, "-N", 2 ) == 0 )
434 bool longForm = strncmp( *argv, "--", 2 ) == 0;
436 const char * pth = 0;
437 if( longForm )
439 argcAssertion( *argv, argc, 2 );
440 pth = *( argv + 1 );
442 else
444 pth = (*argv) + 2;
447 if( strchr( pth, ':' ) != 0 )
449 const char * flag = 0;
450 const char * shortFlag = "-N";
451 if( longForm )
453 flag = *argv;
455 else
457 flag = shortFlag;
460 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
461 if( longForm )
463 std::cerr << " (or " << shortFlag << ")" ;
465 std::cerr <<"." << std::endl ;
466 exit( Interaction::EXIT_INVOCATION_ERROR );
469 Ast::theShapesScanner.push_backNeedPath( absoluteDirectory( pth ) );
471 if( longForm )
473 argv += 2;
474 argc -= 2;
476 else
478 argv += 1;
479 argc -= 1;
482 else if( strcmp( *argv, "--fontmetricspath" ) == 0 ||
483 strncmp( *argv, "-M", 2 ) == 0 )
485 bool longForm = strncmp( *argv, "--", 2 ) == 0;
487 const char * pth = 0;
488 if( longForm )
490 argcAssertion( *argv, argc, 2 );
491 pth = *( argv + 1 );
493 else
495 pth = (*argv) + 2;
498 if( strchr( pth, ':' ) != 0 )
500 const char * flag = 0;
501 const char * shortFlag = "-M";
502 if( longForm )
504 flag = *argv;
506 else
508 flag = shortFlag;
511 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
512 if( longForm )
514 std::cerr << " (or " << shortFlag << ")" ;
516 std::cerr <<"." << std::endl ;
517 exit( Interaction::EXIT_INVOCATION_ERROR );
520 Lang::Font::push_backFontMetricsPath( absoluteDirectory( pth ) );
522 if( longForm )
524 argv += 2;
525 argc -= 2;
527 else
529 argv += 1;
530 argc -= 1;
533 else if( strprefixcmp( *argv, "--seed=", & optionPrefix, & optionSuffix ) )
535 char * endp;
536 long s = strtol( optionSuffix, &endp, 10 );
537 if( *endp != '\0' )
539 std::cerr << "Argument to " << optionPrefix << " was not an integer: " << optionSuffix << std::endl ;
540 exit( Interaction::EXIT_INVOCATION_ERROR );
543 srand( s );
545 argv += 1;
546 argc -= 1;
548 else if( strprefixcmp( *argv, "--arcdelta=", & optionPrefix, & optionSuffix ) )
552 Computation::the_arcdelta = Ast::theShapesScanner.strtoLength( optionSuffix );
554 catch( ... )
556 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
557 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
559 if( Computation::the_arcdelta <= 0 )
561 std::cerr << "Argument to " << optionPrefix << " not positive: " << optionSuffix << std::endl ;
562 exit( Interaction::EXIT_INVOCATION_ERROR );
565 argv += 1;
566 argc -= 1;
568 else if( strprefixcmp( *argv, "--dtmin=", & optionPrefix, & optionSuffix ) )
570 char * endp;
571 Computation::the_dtMin = strtod( optionSuffix, &endp );
572 if( *endp != '\0' )
574 std::cerr << "Argument to " << optionPrefix << " was not a float: " << optionSuffix << std::endl ;
575 exit( Interaction::EXIT_INVOCATION_ERROR );
577 if( Computation::the_dtMin <= 0 )
579 std::cerr << "Argument to " << optionPrefix << " not positive: " << Computation::the_dtMin << std::endl ;
580 exit( Interaction::EXIT_INVOCATION_ERROR );
583 argv += 1;
584 argc -= 1;
586 else if( strprefixcmp( *argv, "--disttol=", & optionPrefix, & optionSuffix ) )
590 Computation::theDistanceTol = Ast::theShapesScanner.strtoLength( optionSuffix );
592 catch( ... )
594 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
595 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
597 if( Computation::theDistanceTol <= 0 )
599 std::cerr << "Argument to " << optionPrefix << " not positive: " << optionSuffix << std::endl ;
600 exit( Interaction::EXIT_INVOCATION_ERROR );
603 argv += 1;
604 argc -= 1;
606 else if( strcmp( *argv, "--prepend" ) == 0 )
608 argcAssertion( *argv, argc, 2 );
609 prependStreamOut << *( argv + 1 ) << std::endl ;
610 argv += 2;
611 argc -= 2;
613 else if( strcmp( *argv, "--base" ) == 0 )
615 argcAssertion( *argv, argc, 2 );
616 if( baseName != "" )
618 std::cerr << "The name base is multiply specified." << std::endl ;
619 exit( Interaction::EXIT_INVOCATION_ERROR );
621 baseName = *( argv + 1 );
622 argv += 2;
623 argc -= 2;
625 else if( strcmp( *argv, "--which" ) == 0 )
627 argcAssertion( *argv, argc, 2 );
628 filenameRequestList.push_back( FILENAME_RESOURCE );
629 resourceRequestList.push_back( *( argv + 1 ) );
630 argv += 2;
631 argc -= 2;
633 else if( strcmp( *argv, "--in" ) == 0 )
635 argcAssertion( *argv, argc, 2 );
636 if( inputName != "" )
638 std::cerr << "The input file is multiply specified." << std::endl ;
639 exit( Interaction::EXIT_INVOCATION_ERROR );
641 inputName = absoluteFilename( *( argv + 1 ) );
642 argv += 2;
643 argc -= 2;
645 else if( strcmp( *argv, "--which-in" ) == 0 )
647 filenameRequestList.push_back( FILENAME_IN );
648 argv += 1;
649 argc -= 1;
651 else if( strcmp( *argv, "--out" ) == 0 )
653 argcAssertion( *argv, argc, 2 );
654 if( outputName != "" )
656 std::cerr << "The output file is multiply specified." << std::endl ;
657 exit( Interaction::EXIT_INVOCATION_ERROR );
659 outputName = absoluteFilename( *( argv + 1 ) );
660 argv += 2;
661 argc -= 2;
663 else if( strcmp( *argv, "--which-out" ) == 0 )
665 filenameRequestList.push_back( FILENAME_OUT );
666 argv += 1;
667 argc -= 1;
669 else if( strcmp( *argv, "--texjob" ) == 0 )
671 argcAssertion( *argv, argc, 2 );
672 if( texJobName != "" )
674 std::cerr << "The tex job name is multiply specified." << std::endl ;
675 exit( Interaction::EXIT_INVOCATION_ERROR );
677 texJobName = *( argv + 1 );
678 if( texJobName.find( '/' ) != std::string::npos )
680 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 ;
681 exit( Interaction::EXIT_INVOCATION_ERROR );
683 argv += 2;
684 argc -= 2;
686 else if( strcmp( *argv, "--which-texjob" ) == 0 )
688 filenameRequestList.push_back( FILENAME_TEXJOB );
689 argv += 1;
690 argc -= 1;
692 else if( strcmp( *argv, "--labeldb" ) == 0 )
694 argcAssertion( *argv, argc, 2 );
695 if( labelDBName != "" )
697 std::cerr << "The label database file is multiply specified." << std::endl ;
698 exit( Interaction::EXIT_INVOCATION_ERROR );
700 labelDBName = absoluteFilename( *( argv + 1 ) );
701 argv += 2;
702 argc -= 2;
704 else if( strcmp( *argv, "--which-labeldb" ) == 0 )
706 filenameRequestList.push_back( FILENAME_LABELDB );
707 argv += 1;
708 argc -= 1;
710 else if( strcmp( *argv, "--afmout" ) == 0 )
712 argcAssertion( *argv, argc, 2 );
713 if( fontmetricsOutputName != "" )
715 std::cerr << "The font metrics output name is multiply specified." << std::endl ;
716 exit( Interaction::EXIT_INVOCATION_ERROR );
718 fontmetricsOutputName = absoluteFilename( *( argv + 1 ) );
719 argv += 2;
720 argc -= 2;
722 else if( strcmp( *argv, "--which-afmout" ) == 0 )
724 filenameRequestList.push_back( FILENAME_AFM );
725 argv += 1;
726 argc -= 1;
728 else if( strcmp( *argv, "--which-TEXINPUTS" ) == 0 )
730 filenameRequestList.push_back( FILENAME_TEXINPUTS );
731 argv += 1;
732 argc -= 1;
734 else if( strcmp( *argv, "--which-doc" ) == 0 )
736 filenameRequestList.push_back( FILENAME_HTMLDOC );
737 argv += 1;
738 argc -= 1;
740 else if( strcmp( *argv, "--which-share" ) == 0 )
742 filenameRequestList.push_back( FILENAME_SHARE );
743 argv += 1;
744 argc -= 1;
746 else if( strcmp( *argv, "--outdir" ) == 0 )
748 argcAssertion( *argv, argc, 2 );
749 if( outDir != "" )
751 std::cerr << "The output directory is multiply specified." << std::endl ;
752 exit( Interaction::EXIT_INVOCATION_ERROR );
754 outDir = absoluteDirectory( *( argv + 1 ) );
755 argv += 2;
756 argc -= 2;
758 else if( strprefixcmp( *argv, "--tmp*=", & optionPrefix, & optionSuffix ) )
760 allowCreateTmpDir = strtobool( optionSuffix, *argv );
761 argv += 1;
762 argc -= 1;
764 else if( strcmp( *argv, "--tmpdir" ) == 0 )
766 argcAssertion( *argv, argc, 2 );
767 if( tmpDir != "" )
769 std::cerr << "The temporaries directory is multiply specified." << std::endl ;
770 exit( Interaction::EXIT_INVOCATION_ERROR );
772 tmpDir = absoluteDirectory( *( argv + 1 ) );
773 argv += 2;
774 argc -= 2;
776 else if( strcmp( *argv, "--which-tmp" ) == 0 )
778 filenameRequestList.push_back( FILENAME_TMP );
779 argv += 1;
780 argc -= 1;
782 else if( strprefixcmp( *argv, "--split=", & optionPrefix, & optionSuffix ) )
784 if( strcmp( optionSuffix, "no" ) == 0 )
786 splitMode = SPLIT_NO;
788 else if( strcmp( optionSuffix, "flat" ) == 0 )
790 splitMode = SPLIT_FLAT;
792 else if( strcmp( optionSuffix, "dir" ) == 0 )
794 splitMode = SPLIT_DIR;
796 else
798 std::cerr << "The string \"" << optionSuffix << "\" in the command line argument \"" << *argv << "\" was not any of { 'no', 'flat', 'dir' }." << std::endl ;
800 argv += 1;
801 argc -= 1;
803 else if( strcmp( *argv, "--xpdf" ) == 0 )
805 previewOptions.launch_xpdf = true;
806 argv += 1;
807 argc -= 1;
809 else if( strcmp( *argv, "--xpdf-remote" ) == 0 )
811 argcAssertion( *argv, argc, 2 );
812 if( previewOptions.xpdfServer != "" )
814 std::cerr << "The xpdf server is multiply specified." << std::endl ;
815 exit( Interaction::EXIT_INVOCATION_ERROR );
817 previewOptions.xpdfServer = *( argv + 1 );
818 argv += 2;
819 argc -= 2;
821 else if( strcmp( *argv, "--xpdf-no-server" ) == 0 )
823 if( previewOptions.xpdfAction != Interaction::PreviewOptions::XPDF_DEFAULT )
825 std::cerr << "The xpdf action is multiply specified." << std::endl ;
826 exit( Interaction::EXIT_INVOCATION_ERROR );
828 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_NOSERVER;
829 argv += 1;
830 argc -= 1;
832 else if( strcmp( *argv, "--xpdf-reload" ) == 0 )
834 if( previewOptions.xpdfAction != Interaction::PreviewOptions::XPDF_DEFAULT )
836 std::cerr << "The xpdf action is multiply specified." << std::endl ;
837 exit( Interaction::EXIT_INVOCATION_ERROR );
839 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_RELOAD;
840 argv += 1;
841 argc -= 1;
843 else if( strcmp( *argv, "--xpdf-quit" ) == 0 )
845 if( previewOptions.xpdfAction != Interaction::PreviewOptions::XPDF_DEFAULT )
847 std::cerr << "The xpdf action is multiply specified." << std::endl ;
848 exit( Interaction::EXIT_INVOCATION_ERROR );
850 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_QUIT;
851 argv += 1;
852 argc -= 1;
854 else if( strcmp( *argv, "--open" ) == 0 )
856 previewOptions.do_open = true;
857 argv += 1;
858 argc -= 1;
860 else if( strcmp( *argv, "--open-a" ) == 0 )
862 argcAssertion( *argv, argc, 2 );
863 previewOptions.do_open = true;
864 previewOptions.do_open_application = *( argv + 1 );
865 argv += 2;
866 argc -= 2;
868 else if( strcmp( *argv, "--version" ) == 0 )
870 printVersion( );
871 exit( Interaction::EXIT_OK );
873 else if( argc == 1 )
875 if( baseName != "" )
877 std::cerr << "The name base is multiply specified." << std::endl ;
878 exit( Interaction::EXIT_INVOCATION_ERROR );
880 struct stat theStat;
881 if( stat( *argv, & theStat ) == 0 &&
882 ( theStat.st_mode & S_IFDIR ) == 0 ) /* We are not interested in directories here. */
884 inputName = *argv;
885 char * ext = *argv + strlen( *argv ) - 6;
886 if( ext <= *argv )
888 std::cerr << "The file name \"" << *argv << "\" is unexpectedly short (it should include the \".shape\" suffix)." << std::endl ;
889 exit( Interaction::EXIT_INVOCATION_ERROR );
891 if( strcmp( ext, ".shape" ) != 0 )
893 std::cerr << "Expected \".shape\" suffix in the file name \"" << *argv << "\"." << std::endl ;
894 exit( Interaction::EXIT_INVOCATION_ERROR );
897 else
899 if( (*argv)[ strlen( *argv ) - 1 ] == '.' )
901 inputName = std::string( *argv ) + "shape";
903 else
905 inputName = std::string( *argv ) + ".shape";
907 if( ! stat( inputName.c_str( ), & theStat ) == 0 )
909 /* It is not entirely clear what is the best error message here,
910 * as the source file may be specified in several different ways.
911 * This should cause the least confusion.
913 std::cerr << "Failed to locate input file: " << *argv << std::endl ;
914 exit( Interaction::EXIT_INPUT_FILE_ERROR );
918 const char * slash = strrchr( inputName.c_str( ), '/' );
919 if( slash == 0 )
921 slash = inputName.c_str( );
923 else
925 ++slash;
927 size_t skipCount = slash - inputName.c_str( );
928 baseName = inputName.substr( skipCount, inputName.length( ) - skipCount - 6 );
930 argv += 1;
931 argc -= 1;
933 else
935 std::cerr << "Illegal command line option: " << *argv << std::endl ;
936 exit( Interaction::EXIT_INVOCATION_ERROR );
940 if( tmpDir == "" )
942 char * start = getenv( "SHAPESTMPDIR" );
943 if( start != 0 )
945 tmpDir = absoluteDirectory( start );
947 else
949 tmpDir = absoluteDirectory( "" );
952 if( outDir == "" )
954 if( interactiveMode )
956 outDir = tmpDir;
958 else
960 outDir = absoluteDirectory( "" );
963 ensureTmpDirectoryExists( tmpDir, allowCreateTmpDir );
965 if( baseName == "" )
967 if( interactiveMode )
969 if( outputName == "" )
971 if( splitMode == SPLIT_NO )
973 outputName = outDir + "#shapes" + ".pdf";
975 else
977 outputName = outDir + "#shapes" ;
981 if( texJobName == "" )
983 texJobName = "#shapes.labels";
986 else
988 if( inputName == "" )
990 inputName = absoluteFilename( ( baseName + ".shape" ).c_str( ) );
992 if( outputName == "" )
994 if( splitMode == SPLIT_NO )
996 outputName = outDir + baseName + ".pdf";
998 else
1000 outputName = outDir + baseName;
1003 if( texJobName == "" )
1005 texJobName = baseName + ".labels";
1007 if( labelDBName == "" )
1009 labelDBName = outDir + baseName + ".labels.pdf";
1011 if( fontmetricsOutputName == "" )
1013 fontmetricsOutputName = outDir + baseName + ".afm";
1017 if( outputName == "" )
1019 if( ! filenameRequestList.empty( ) )
1021 /* The output name will never really be used, so it's rather harmless to assign a dummy value.
1023 outputName = "?.pdf" ;
1025 else
1027 std::cerr << "The output file is undetermined. Consider specifying it using \"--out <filename>\"." << std::endl ;
1028 exit( Interaction::EXIT_INVOCATION_ERROR );
1032 if( labelDBName == "" )
1034 iterativeMode = false;
1036 if( iterativeMode == false )
1038 /* If the iterative mode has explicitly been turned off, we prevent any old labels from being reused. */
1039 labelDBName = "";
1042 if( ! Kernel::theDebugLog.initialized( ) )
1044 std::string::size_type suffixSep = outputName.rfind( '.' );
1045 if( suffixSep != std::string::npos && outputName.find( '/', suffixSep ) == std::string::npos )
1047 /* If there would have been a slash after the '.', the dot would have been part of a directory name.
1048 * Otherwise, we conclude that we have found the extension of a filename, and replace that extension
1049 * by ".log".
1051 Kernel::theDebugLog.setFilename( outputName.substr( 0, suffixSep ) + ".log" );
1053 else if( baseName != "" )
1055 Kernel::theDebugLog.setFilename( outDir + baseName + ".log" );
1057 else
1059 Kernel::theDebugLog.setFilename( outDir + "#shapes.log" );
1064 std::string inDir;
1065 std::string inPath = inputName;
1066 std::string::size_type slash = inPath.rfind( '/' );
1067 if( slash == std::string::npos )
1069 inDir = absoluteDirectory( "" );
1071 else
1073 inDir = absoluteDirectory( inPath.substr( 0, slash ).c_str( ) );
1075 Ast::theShapesScanner.setSourceDir( inDir );
1076 Kernel::theTeXLabelManager.setup( inDir, tmpDir, texJobName );
1079 if( Computation::theTrixelizeSplicingTol >= Computation::theTrixelizeOverlapTol )
1081 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 ;
1082 exit( Interaction::EXIT_INVOCATION_ERROR );
1085 if( previewOptions.xpdfServer == "" )
1087 previewOptions.xpdfServer = outputName;
1090 if( previewOptions.xpdfAction == Interaction::PreviewOptions::XPDF_DEFAULT )
1092 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_RAISE;
1095 addDefaultNeedPath( );
1096 addDefaultFontMetricsPath( );
1098 if( useResources )
1100 Ast::theShapesScanner.push_backNeedPath( std::string( Kernel::RESOURCES_DIR ) + "/extensions/" );
1101 Lang::Font::push_backFontMetricsPath( std::string( Kernel::RESOURCES_DIR ) + "/fontmetrics/" );
1104 if( ! filenameRequestList.empty( ) )
1106 std::list< const char * >::const_iterator resource = resourceRequestList.begin( );
1107 for( std::list< int >::const_iterator i = filenameRequestList.begin( );
1108 i != filenameRequestList.end( );
1109 ++i )
1111 switch( *i )
1113 case FILENAME_IN:
1114 if( inputName == "" )
1116 std::cout << "<stdin>" ;
1118 else
1120 std::cout << inputName ;
1122 break;
1123 case FILENAME_OUT:
1124 std::cout << outputName ;
1125 break;
1126 case FILENAME_TMP:
1127 std::cout << tmpDir ;
1128 break;
1129 case FILENAME_TEXJOB:
1130 std::cout << tmpDir << texJobName ;
1131 break;
1132 case FILENAME_LABELDB:
1133 std::cout << labelDBName ;
1134 break;
1135 case FILENAME_AFM:
1136 std::cout << fontmetricsOutputName ;
1137 break;
1138 case FILENAME_TEXINPUTS:
1140 std::cout << getenv( "TEXINPUTS" ) ;
1142 break;
1143 case FILENAME_HTMLDOC:
1145 std::cout << Kernel::HTML_DIR << "/index.html" ;
1147 break;
1148 case FILENAME_SHARE:
1150 std::cout << Kernel::RESOURCES_DIR ;
1152 break;
1153 case FILENAME_RESOURCE:
1157 std::cout << Ast::theShapesScanner.searchFile( *resource ) ;
1159 catch( const Exceptions::Exception & ball )
1161 std::cout.flush( );
1162 ball.display( std::cerr );
1163 exit( Interaction::EXIT_INVOCATION_ERROR );
1165 ++resource;
1167 break;
1168 default:
1169 std::cerr << "Internal error: filename request switch in main out of range." << std::endl ;
1170 exit( Interaction::EXIT_INTERNAL_ERROR );
1172 std::cout << std::endl ;
1174 exit( Interaction::EXIT_OK );
1177 if( pdfVersion == SimplePDF::PDF_Version::VERSION_UNDEFINED )
1179 pdfVersion = SimplePDF::PDF_Version::PDF_1_4;
1182 Kernel::the_PDF_version.setVersion( pdfVersion );
1183 Kernel::the_PDF_version.setAction( pdfVersionAction );
1186 std::ostringstream oss;
1187 time_t tmp;
1188 time( &tmp );
1189 struct tm * now( gmtime( &tmp ) );
1190 oss << "D:"
1191 << std::setfill( '0' )
1192 << std::setw(4) << 1900 + now->tm_year
1193 << std::setw(2) << 1 + now->tm_mon
1194 << std::setw(2) << now->tm_mday
1195 << std::setw(2) << now->tm_hour
1196 << std::setw(2) << now->tm_min
1197 << std::setw(2) << now->tm_sec
1198 << "+0000" ;
1199 Kernel::theDocInfo.addInfo( "CreationDate", SimplePDF::newString( oss.str( ).c_str( ) ) );
1202 std::istringstream prependStreamIn;
1203 if( ! prependStreamOut.str( ).empty( ) )
1205 prependStreamIn.str( prependStreamOut.str( ) );
1206 Ast::theShapesScanner.queueStream( & prependStreamIn, Ast::FileID::build_special( "<--prepend>" ) );
1209 std::ifstream iFile;
1210 std::istream * iFilePtr = 0;
1211 if( inputName == "" )
1213 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( "<stdin>" ) );
1214 iFilePtr = & std::cin;
1215 if( interactiveMode )
1217 /* On some platforms, the lexer crashes if it is not given a new file at EOF,
1218 * so we need to put an empty file after the prelude to avoid that.
1220 * Note that we use the iFile object here, although this will not be pointed to
1221 * by iFilePtr, which points to std::cin instead.
1223 const char * nullName = "/dev/null";
1224 iFile.open( nullName );
1225 if( ! iFile.good( ) || ! iFile.is_open( ) )
1227 std::cerr << "Failed to open " << nullName << " to create empty input file." << std::endl ;
1228 exit( Interaction::EXIT_EXTERNAL_ERROR );
1230 Ast::theShapesScanner.queueStream( & iFile, Ast::FileID::build_special( nullName ) );
1232 else
1234 Ast::theShapesScanner.queueStream( iFilePtr, Ast::FileID::build_special( "<stdin>" ) );
1235 // Ast::theShapesScanner.setNameOf_yyin( "<stdin>" );
1238 else
1240 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( inputName.c_str( ) ) );
1241 iFile.open( inputName.c_str( ) );
1242 if( ! iFile.good( ) || ! iFile.is_open( ) )
1244 std::cerr << "Failed to open " << inputName << " for input." << std::endl ;
1245 exit( Interaction::EXIT_INPUT_FILE_ERROR );
1247 iFilePtr = & iFile;
1248 if( ! interactiveMode )
1250 struct stat theStat;
1251 if( stat( inputName.c_str( ), & theStat ) != 0 )
1253 std::cerr << "Failed to stat " << inputName << " (but it could be opened for input...)." << std::endl ;
1254 exit( Interaction::EXIT_INPUT_FILE_ERROR );
1256 Ast::theShapesScanner.queueStream( iFilePtr, Ast::FileID::build_stat( theStat, inputName ) );
1260 /* This will initiate the reading of preambles, and when there are no more preambles, we turn to the queue set up by calling queueStream. */
1261 Ast::theShapesScanner.start( );
1263 RefCountPtr< std::ifstream > labelDBFile = RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1264 Kernel::WarmCatalog::ShipoutList documents;
1266 /* This is where all the evaluation takes place. It is written as a separate functions to keep the function main short.
1270 if( interactiveMode )
1272 interactiveEvaluation( *iFilePtr, std::cout, outputName, splitMode,
1273 evalTrace, evalBackTrace, cleanupMemory,
1274 labelDBFile, labelDBName );
1276 else
1278 nonInteractiveEvaluation( documents, splitMode,
1279 evalTrace, evalBackTrace, cleanupMemory,
1280 labelDBFile, labelDBName );
1283 catch( const NonLocalExit::DynamicBindingNotFound & ball )
1285 std::cerr << "Caught DynamicBindingNotFound at top level." << std::endl
1286 << "This should really not be possible; it is an internal error." << std::endl ;
1287 exit( Interaction::EXIT_INTERNAL_ERROR );
1289 catch( const NonLocalExit::NotThisType & ball )
1291 std::cerr << "Caught NotThisType at top level." << std::endl
1292 << "This should really not be possible; it is an internal error." << std::endl ;
1293 exit( Interaction::EXIT_INTERNAL_ERROR );
1295 catch( const NonLocalExit::NonLocalExitBase & ball )
1297 std::cerr << "Caught an unknown descendant to NonLocalExitBase at top level." << std::endl
1298 << "This should really not be possible; it is an internal error." << std::endl ;
1299 exit( Interaction::EXIT_INTERNAL_ERROR );
1301 catch( const char * ball )
1303 std::cerr << "Caught (char*) ball at top level:" << std::endl
1304 << " " << ball << std::endl ;
1305 exit( Interaction::EXIT_GENERIC_ERROR );
1307 catch( const std::string & ball )
1309 std::cerr << "Caught (string) ball at top level:" << std::endl
1310 << " " << ball << std::endl ;
1311 exit( Interaction::EXIT_GENERIC_ERROR );
1313 catch( const std::exception & ball )
1315 std::cerr << "Caught std::exception at top level:" << std::endl
1316 << " " << ball.what( ) << std::endl ;
1317 exit( Interaction::EXIT_GENERIC_ERROR );
1319 catch( ... )
1321 std::cerr << "Caught (...) ball at top level." << std::endl ;
1322 exit( Interaction::EXIT_GENERIC_ERROR );
1325 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1327 abortProcedure( Interaction::EXIT_USER_ERROR );
1330 if( memoryStats )
1332 std::cerr << "Summary:" << std::endl ;
1333 std::cerr << "Environments: alive: " << Kernel::Environment::liveCount << " of total: " << Kernel::Environment::createdCount
1334 << " (" << 100 * static_cast< double >( Kernel::Environment::liveCount ) / static_cast< double >( Kernel::Environment::createdCount ) << "%)" << std::endl ;
1337 switch( splitMode )
1339 case SPLIT_NO:
1341 if( documents.size( ) != 1 )
1343 std::cerr << "Internal error: Failed to produce exactly one document of output although --split=no." << std::endl ;
1345 std::ofstream oFile;
1346 noSplitOpen( & oFile, outputName );
1347 documents.front( ).writeFile( oFile, Kernel::the_PDF_version );
1349 break;
1350 case SPLIT_FLAT:
1352 rmSplitFiles( outputName, "-" );
1353 size_t physicalPageNo = 1;
1354 for( Kernel::WarmCatalog::ShipoutList::iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1356 std::ofstream oFile;
1357 splitOpen( & oFile, outputName, "-", physicalPageNo );
1358 i->writeFile( oFile, Kernel::the_PDF_version );
1361 break;
1362 case SPLIT_DIR:
1364 mkSplitDir( outputName );
1365 rmSplitFiles( outputName, "/" );
1366 size_t physicalPageNo = 1;
1367 for( Kernel::WarmCatalog::ShipoutList::iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1369 std::ofstream oFile;
1370 splitOpen( & oFile, outputName, "/", physicalPageNo );
1371 i->writeFile( oFile, Kernel::the_PDF_version );
1374 break;
1377 /* This must be done after the output has been written, and before iterativeFinish writes to the labels database file.
1379 Kernel::thePDFImporter.free( );
1381 if( labelDBFile != NullPtr< std::ifstream >( ) )
1383 if( labelDBFile->is_open( ) )
1385 labelDBFile->close( );
1387 labelDBFile = NullPtr< std::ifstream >( ); // Free the reference.
1390 if( iterativeMode )
1392 Kernel::theTeXLabelManager.iterativeFinish( labelDBName );
1395 previewOptions.preview( outputName, splitMode != SPLIT_NO );
1397 destroyGlobals( );
1399 return 0;
1403 void
1404 argcAssertion( const char * optionSpecifier, int argc, int argcMin )
1406 if( argc < argcMin )
1408 std::cerr << "The command line option " << optionSpecifier << " requires " << argcMin - 1 << " parameters." << std::endl ;
1409 exit( Interaction::EXIT_INVOCATION_ERROR );
1413 bool
1414 strprefixcmp( char * str, const char * prefix, const char ** prefixDst, char ** endp )
1416 *prefixDst = prefix;
1417 int len = strlen( prefix );
1418 bool res = ( strncmp( str, prefix, len ) == 0 );
1419 *endp = str + len;
1420 return res;
1423 bool
1424 strtobool( const char * str, const char * containingString, const char * trueLabel, const char * falseLabel )
1426 if( trueLabel != 0 &&
1427 strcmp( str, trueLabel ) == 0 )
1429 return true;
1431 if( falseLabel != 0 &&
1432 strcmp( str, falseLabel ) == 0 )
1434 return false;
1436 if( strcmp( str, "yes" ) == 0 ||
1437 strcmp( str, "true" ) == 0 ||
1438 strcmp( str, "on" ) == 0 )
1440 return true;
1442 if( strcmp( str, "no" ) == 0 ||
1443 strcmp( str, "false" ) == 0 ||
1444 strcmp( str, "off" ) == 0)
1446 return false;
1448 std::cerr << "The string \"" << str << "\" in the command line argument \"" << containingString << "\" was not recognized as a boolean value." << std::endl ;
1449 exit( Interaction::EXIT_INVOCATION_ERROR );
1453 RefCountPtr< std::ifstream >
1454 performIterativeStartup( const std::string & labelDBName )
1457 struct stat theStat;
1458 if( stat( labelDBName.c_str( ), & theStat ) != 0 )
1460 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1463 // {
1464 // ostringstream mvCommand;
1465 // mvCommand << "cp '" << oldFilename.str( ) << "' '" << labelDBName.str( ) << "'" ;
1466 // Interaction::systemDebugMessage( mvCommand.str( ) );
1467 // if( system( mvCommand.str( ).c_str( ) ) != 0 )
1468 // {
1469 // return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1470 // }
1471 // }
1472 RefCountPtr< std::ifstream > labelsFile( new std::ifstream( labelDBName.c_str( ) ) );
1473 if( ! labelsFile->good( ) )
1475 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1479 Kernel::theTeXLabelManager.iterativeStartup( labelsFile );
1480 return labelsFile;
1482 catch( const char * ball )
1484 std::cerr << "Caught (char*) ball from iterative startup:" << std::endl
1485 << " " << ball << std::endl ;
1486 exit( Interaction::EXIT_GENERIC_ERROR );
1488 catch( const std::string & ball )
1490 std::cerr << "Caught (string) ball from iterative startup:" << std::endl
1491 << " " << ball << std::endl ;
1492 exit( Interaction::EXIT_GENERIC_ERROR );
1494 catch( const Exceptions::Exception & ball )
1496 ball.display( std::cerr );
1497 exit( ball.exitCode( ) );
1499 catch( ... )
1501 std::cerr << "Caught (...) ball from iterative startup." << std::endl ;
1502 exit( Interaction::EXIT_GENERIC_ERROR );
1507 void
1508 abortProcedure( int exitCode )
1510 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1512 std::cout.flush( );
1513 while( ! Kernel::thePostCheckErrorsList.empty( ) )
1515 Exceptions::Exception * e = Kernel::thePostCheckErrorsList.front( );
1516 Kernel::thePostCheckErrorsList.pop_front( );
1518 typedef const Exceptions::PostCondition ErrorType;
1519 ErrorType * err = dynamic_cast< ErrorType * >( e );
1520 if( err != 0 )
1522 std::cerr << err->loc( ) << ": " ;
1523 err->display( std::cerr );
1524 continue;
1528 typedef const Exceptions::RuntimeError ErrorType;
1529 ErrorType * err = dynamic_cast< ErrorType * >( e );
1530 if( err != 0 )
1532 std::cerr << err->getLoc( ) << " (runtime): " ;
1533 err->display( std::cerr );
1534 continue;
1537 std::cerr << "(Bad post-exception type)" << ": " ;
1538 e->display( std::cerr );
1541 std::cerr << "Aborting job. Output files are left unchanged." << std::endl ;
1542 exit( exitCode );
1545 namespace Shapes
1547 namespace Helpers
1549 void setSelfRef( RefCountPtr< const Lang::Class > cls )
1551 cls->setSelfRef( cls );
1553 void initClass( RefCountPtr< const Lang::Class > cls )
1555 Lang::SystemFinalClass * typedPtr = dynamic_cast< Lang::SystemFinalClass * >( const_cast< Lang::Class * >( cls.getPtr( ) ) );
1556 if( typedPtr != 0 )
1558 typedPtr->init( );
1560 else
1562 std::cerr << "Helpers::initMutators was called with object of bad type." << std::endl ;
1563 exit( Interaction::EXIT_INTERNAL_ERROR );
1569 void
1570 setupGlobals( )
1572 Lang::ElementaryPath2D * bbox = new Lang::ElementaryPath2D;
1573 bbox->push_back( new Concrete::PathPoint2D( 0, 0 ) );
1574 bbox->close( );
1575 Lang::THE_POINTPICTURE = RefCountPtr< Lang::Drawable2D >( new Lang::BBoxed2D( Lang::THE_NULL2D,
1576 RefCountPtr< Lang::ElementaryPath2D >( bbox ) ) );
1577 Helpers::setSelfRef( Lang::THE_OBJECT );
1579 Helpers::setSelfRef( Lang::Void::TypeID );
1580 Helpers::setSelfRef( Lang::Symbol::TypeID );
1581 Helpers::setSelfRef( Lang::Float::TypeID );
1582 Helpers::setSelfRef( Lang::Length::TypeID );
1583 Helpers::setSelfRef( Lang::Integer::TypeID );
1584 Helpers::setSelfRef( Lang::Boolean::TypeID );
1585 Helpers::setSelfRef( Lang::String::TypeID );
1586 Helpers::setSelfRef( Lang::FloatPair::TypeID );
1587 Helpers::setSelfRef( Lang::FloatTriple::TypeID );
1588 Helpers::setSelfRef( Lang::Coords2D::TypeID );
1589 Helpers::setSelfRef( Lang::CornerCoords2D::TypeID );
1590 Helpers::setSelfRef( Lang::Coords3D::TypeID );
1592 Helpers::setSelfRef( Lang::Function::TypeID );
1593 Helpers::setSelfRef( Lang::Transform2D::TypeID );
1594 Helpers::setSelfRef( Lang::Transform3D::TypeID );
1596 Helpers::setSelfRef( Lang::Class::TypeID );
1598 Helpers::setSelfRef( Lang::TransformedInstance::TypeID );
1600 Helpers::initClass( Lang::String::TypeID );
1601 Helpers::initClass( Kernel::WarmGroup2D::TypeID );
1602 Helpers::initClass( Kernel::WarmGroup3D::TypeID );
1603 Helpers::initClass( Kernel::WarmCatalog::TypeID );
1604 Helpers::initClass( Kernel::WarmDebugger::TypeID );
1605 Helpers::initClass( Kernel::Warm_vector::TypeID );
1606 Helpers::initClass( Lang::Font::TypeID );
1607 Helpers::initClass( Lang::SingleList::TypeID );
1608 Helpers::initClass( Lang::ConsPair::TypeID );
1609 Helpers::initClass( Lang::VectorFunction::TypeID );
1610 Helpers::initClass( Lang::Transform2D::TypeID );
1611 Helpers::initClass( Lang::Transform3D::TypeID );
1614 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric2D" ) );
1615 Lang::Geometric2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1616 Helpers::setSelfRef( Lang::Geometric2D::TypeID );
1617 /* Note that addVirtual must not be called before the selfRef is set!
1621 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric3D" ) );
1622 Lang::Geometric3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1623 Helpers::setSelfRef( Lang::Geometric3D::TypeID );
1624 /* Note that addVirtual must not be called before the selfRef is set!
1628 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable2D" ) );
1629 Lang::Drawable2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1630 Helpers::setSelfRef( Lang::Drawable2D::TypeID );
1631 /* Note that addVirtual must not be called before the selfRef is set!
1633 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1636 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable3D" ) );
1637 Lang::Drawable3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1638 Helpers::setSelfRef( Lang::Drawable3D::TypeID );
1639 /* Note that addVirtual must not be called before the selfRef is set!
1641 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1644 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Color" ) );
1645 Lang::Color::TypeID = RefCountPtr< const Lang::Class >( tmp );
1646 Helpers::setSelfRef( Lang::Color::TypeID );
1647 /* Note that addVirtual must not be called before the selfRef is set!
1649 tmp->addVirtual( "stroking" );
1650 tmp->addVirtual( "nonstroking" );
1653 #ifdef HAVE_FT2
1655 FT_Error error = FT_Init_FreeType( & Kernel::theFreeType );
1656 if( error != 0 )
1658 std::cerr << "Failed to initialize FreeType library object: " << Kernel::FreeTypeErrorMessage( error ) << std::endl ;
1659 exit( Interaction::EXIT_EXTERNAL_ERROR );
1662 #endif
1663 #ifdef HAVE_FONTCONFIG
1664 if( FcInit( ) != FcTrue )
1666 std::cerr << "Failed to initialize FontConfig library." << std::endl ;
1667 exit( Interaction::EXIT_EXTERNAL_ERROR );
1669 #endif
1672 void
1673 Interaction::PreviewOptions::preview( const std::string & filename, bool splitNoLaunch ) const
1675 if( launch_xpdf )
1677 if( splitNoLaunch )
1679 std::cerr << "Warning: Not launching viewer since the documet was split." << std::endl ;
1681 else
1683 xpdfHelper( filename );
1687 if( do_open )
1689 if( splitNoLaunch )
1691 std::cerr << "Warning: Not launching viewer since the documet was split." << std::endl ;
1693 else
1695 openHelper( filename );
1700 void
1701 Interaction::PreviewOptions::xpdfHelper( const std::string & filename ) const
1703 pid_t xpdfProcess = fork( );
1704 if( xpdfProcess == -1 )
1706 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running xpdf." ) );
1709 if( xpdfProcess == 0 ) /* This is the child */
1711 /* The exec call below never returns, so the child process never leaves this if clause.
1712 * Hence, there is no need to create a special else clasuse below.
1714 switch( xpdfAction )
1716 case XPDF_RAISE:
1717 execlp( "xpdf", "xpdf", "-remote", xpdfServer.c_str( ), "-raise", filename.c_str( ), static_cast< const char * >( 0 ) );
1718 break;
1719 case XPDF_RELOAD:
1720 execlp( "xpdf", "xpdf", "-remote", xpdfServer.c_str( ), "-reload", static_cast< const char * >( 0 ) );
1721 break;
1722 case XPDF_QUIT:
1723 execlp( "xpdf", "xpdf", "-remote", xpdfServer.c_str( ), "-quit", static_cast< const char * >( 0 ) );
1724 break;
1725 case XPDF_NOSERVER:
1726 execlp( "xpdf", "xpdf", filename.c_str( ), static_cast< const char * >( 0 ) );
1727 break;
1728 default:
1729 std::cerr << "Internal error: XpdfAction switch out of range." << std::endl ;
1730 exit( Interaction::EXIT_INTERNAL_ERROR );
1732 if( errno != 0 )
1734 std::cerr << "Recieved errno = " << errno << " from execlp call to xpdf." << std::endl ;
1735 exit( Interaction::EXIT_EXTERNAL_ERROR );
1737 std::cerr << "execlp call to xpdf returned with errno == 0." << std::endl ;
1738 exit( Interaction::EXIT_INTERNAL_ERROR );
1743 void
1744 Interaction::PreviewOptions::openHelper( const std::string & filename ) const
1746 pid_t openProcess = fork( );
1747 if( openProcess == -1 )
1749 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running open." ) );
1752 if( openProcess == 0 ) /* This is the child */
1754 /* The exec call below never returns, so the child process never leaves this if clause.
1755 * Hence, there is no need to create a special else clasuse below.
1757 if( do_open_application != 0 )
1759 execlp( "open", "open", "-a", do_open_application, filename.c_str( ), static_cast< const char * >( 0 ) );
1761 else
1763 execlp( "open", "open", filename.c_str( ), static_cast< const char * >( 0 ) );
1765 if( errno != 0 )
1767 std::cerr << "Recieved errno = " << errno << " from execlp call to open." << std::endl ;
1768 exit( Interaction::EXIT_EXTERNAL_ERROR );
1770 std::cerr << "execlp call to open returned with errno == 0." << std::endl ;
1771 exit( Interaction::EXIT_INTERNAL_ERROR );
1776 void
1777 Interaction::systemDebugMessage( const std::string & msg )
1779 if( Interaction::debugSystem )
1781 std::cerr << "System command: " << msg << std::endl ;
1785 void
1786 Interaction::warn_or_push( Exceptions::Exception * message, PtrOwner_back_Access< std::list< Exceptions::Exception * > > * errorsList )
1788 switch( Interaction::warningAction )
1790 case Interaction::WARNING_DISPLAY:
1791 Interaction::displayWarning( *message );
1792 delete message;
1793 break;
1794 case Interaction::WARNING_ERROR:
1795 errorsList->push_back( message );
1796 break;
1797 case Interaction::WARNING_IGNORE:
1798 delete message;
1799 break;
1803 void
1804 Interaction::displayWarning( const Exceptions::Exception & ball )
1806 std::cerr << "***Warning*** " ;
1807 ball.display( std::cerr );
1810 void
1811 addDefaultNeedPath( )
1813 char * start = getenv( "SHAPESINPUTS" );
1814 if( start == 0 )
1816 Ast::theShapesScanner.push_backNeedPath( "./" );
1817 return;
1819 char * tok = strsep( & start, ":" );
1820 while( tok != 0 )
1822 Ast::theShapesScanner.push_backNeedPath( tok );
1823 tok = strsep( & start, ":" );
1827 void
1828 addDefaultFontMetricsPath( )
1830 char * start = getenv( "SHAPESFONTMETRICS" );
1831 if( start == 0 )
1833 return;
1835 char * tok = strsep( & start, ":" );
1836 while( tok != 0 )
1838 Lang::Font::push_backFontMetricsPath( tok );
1839 tok = strsep( & start, ":" );
1843 void
1844 destroyGlobals( )
1846 Helpers::requireUTF8ToMacRomanConverter( true ); // true means "cleanup"
1847 Helpers::requireMacRomanToUTF8Converter( true ); // true means "cleanup"
1848 Helpers::requireUTF8ToWinANSIConverter( true ); // true means "cleanup"
1849 Helpers::requireUTF8ToUTF16BEConverter( 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"
1855 #ifdef HAVE_FONTCONFIG
1856 FcFini( );
1857 #endif
1858 #ifdef HAVE_FT2
1860 FT_Error error = FT_Done_FreeType( Kernel::theFreeType );
1861 if( error != 0 )
1863 std::cerr << "Failed to \"destroy\" FreeType library object: " << Kernel::FreeTypeErrorMessage( error ) << std::endl ;
1864 exit( Interaction::EXIT_EXTERNAL_ERROR );
1867 #endif
1870 std::string
1871 absoluteFilename( const char * filename )
1873 if( *filename == '/' )
1875 return filename;
1877 return callDir + filename;
1880 std::string
1881 absoluteDirectory( const char * filename )
1883 if( *filename == '\0' )
1885 return callDir;
1887 if( filename[ strlen( filename ) - 1 ] != '/' )
1889 if( *filename == '/' )
1891 return filename + std::string( "/" );
1893 return callDir + filename + "/";
1895 if( *filename == '/' )
1897 return filename;
1899 return callDir + filename;
1902 #include <iomanip>
1904 void
1905 ensureTmpDirectoryExists( const std::string & dirname, bool allowCreate )
1907 struct stat theStat;
1908 if( stat( dirname.c_str( ), & theStat ) == 0 )
1910 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1912 std::cerr << "The path " << dirname << " was expected to reference a directory." << std::endl ;
1913 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1915 // if( ( theStat.st_mode & S_IWOTH ) == 0 )
1916 // {
1917 // std::cerr << "The directory " << dirname << " was expected have write permission for others." << std::endl ;
1918 // exit( Interaction::EXIT_FILE_PERMISSION_ERROR );
1919 // }
1920 return;
1923 if( ! allowCreate )
1925 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 ;
1926 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1929 size_t i2 = 0; /* We know there's a slash at the first position */
1930 i2 = dirname.find( '/', i2 + 1 );
1931 bool atRoot = true;
1932 while( stat( dirname.substr( 0, i2 ).c_str( ), & theStat ) == 0 )
1934 atRoot = false;
1935 i2 = dirname.find( '/', i2 + 1 );
1937 if( atRoot )
1939 std::cerr << "Shapes will not create directories for temporary files at the root: " << dirname << std::endl ;
1940 exit( Interaction::EXIT_INVOCATION_ERROR );
1943 mode_t oldUmask = umask( 0 ); /* We want to be able to create directories with any permissions. */
1944 while( i2 != std::string::npos )
1946 if( mkdir( dirname.substr( 0, i2 ).c_str( ), theStat.st_mode & ( S_IRWXU | S_IRWXG | S_IRWXO ) ) != 0 )
1948 std::cerr << "Failed to create directory for temporary files (errno=" << errno << "): " << dirname.substr( 0, i2 ) << std::endl ;
1949 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1951 i2 = dirname.find( '/', i2 + 1 );
1953 umask( oldUmask );
1956 void
1957 escapeExtGlobChars( const std::string & str, std::ostream & dst )
1959 const char * special = "*?[+@!";
1960 for( std::string::const_iterator i = str.begin( ); i != str.end( ); ++i )
1962 if( strchr( special, *i ) != 0 )
1964 dst << '\\' ;
1966 dst << *i ;
1970 void
1971 rmSplitFiles( const std::string & outputName, const char * sep )
1973 std::ostringstream rmCommand;
1974 rmCommand << "sh -O extglob -c 'rm -f " ;
1975 escapeExtGlobChars( outputName, rmCommand );
1976 rmCommand << sep << "+([0-9]).pdf'" ;
1977 Interaction::systemDebugMessage( rmCommand.str( ) );
1978 if( system( rmCommand.str( ).c_str( ) ) != 0 )
1980 /* Never mind; we made a try, and this probably means that there were no files to remove. */
1984 void
1985 mkSplitDir( const std::string & outputName )
1987 struct stat theStat;
1988 if( stat( outputName.c_str( ), & theStat ) == 0 )
1990 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1992 std::cerr << "The path " << outputName << " was expected to reference a directory." << std::endl ;
1993 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1996 else
1998 if( mkdir( outputName.c_str( ), S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
2000 std::cerr << "Failed to create directory for split document files (errno=" << errno << "): " << outputName << std::endl ;
2001 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
2006 void
2007 noSplitOpen( std::ofstream * oFile, const std::string & outputName )
2009 oFile->open( outputName.c_str( ) );
2010 if( ! oFile->good( ) )
2012 /* If this is because the output directory does not exist, we shall inform the user about this. */
2013 std::string::size_type slash = outputName.rfind( '/' );
2014 if( slash != std::string::npos )
2016 std::string outputDir = outputName.substr( 0, slash );
2017 struct stat theStat;
2018 if( stat( outputDir.c_str( ), & theStat ) == 0 )
2020 if( ( theStat.st_mode & S_IFDIR ) == 0 )
2022 std::cerr << "The prefix " << outputDir << " of the output name must be a directory." << std::endl ;
2023 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
2025 /* In case we reach here, the directory exists, so we fall back on the generic error message below. */
2027 else
2029 std::cerr << "The file " << outputName << " cannot be opened for output since the directory " << outputDir << " does not exist." << std::endl ;
2030 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
2033 std::cerr << "Failed to open " << outputName << " for output." << std::endl ;
2034 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
2038 std::string
2039 splitOpen( std::ofstream * oFile, const std::string outputName, const char * sep, size_t physicalPageNo )
2041 std::ostringstream oss;
2042 oss << outputName << sep << physicalPageNo << ".pdf" ;
2043 std::string filename( oss.str( ) );
2044 oFile->open( filename.c_str( ) );
2045 if( ! oFile->good( ) )
2047 std::cerr << "Failed to open " << filename << " for output." << std::endl ;
2048 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
2050 return filename;
2053 void
2054 printHelp( )
2056 std::string ind(" ");
2057 std::cout
2058 << "Usage:" << std::endl
2059 << ind << "shapes [OPTIONS] file[.[shape]]" << std::endl
2060 << "A subset of options is listed below, by cathegory. This message is just" << std::endl
2061 << "a reminder; legal option values are only listed in rare cases." << std::endl
2062 << "Files: --in --out --outdir --texjob --tmpdir --tmp*= --labeldb" << std::endl
2063 << "Paths: --needpath -N --fontmetricspath --resources=" << std::endl
2064 << "Multipage: --split=('no'|'flat'|'dir')" << std::endl
2065 << "PDF: (--pdf-version=|-v)(e|w|s)1.N --tp= --spot-pair=" << std::endl
2066 << "Tolerances: --arcdelta= --dtmin= --dtminerror= --splicingtol= --overlaptol=" << std::endl
2067 << "Log file: --debuglog --debuglog-stderr --debuglog-stdout" << std::endl
2068 << "Help: --help --which-doc --showfiles= --which --version" << std::endl
2069 << "Viewing: --xpdf --open --open-a" << std::endl
2070 << "Interactive: --interactive -i" << std::endl
2071 << "See the man page for a complete listing and details." << std::endl