Update procedures
[shapes.git] / source / main.cc
blob919662979050b1c055b48f34756bef81bd032b70
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, 2013, 2014 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;
53 bool forceBlankMode = false;
54 bool forceShapeMode = false;
57 char * cwd = getcwd( 0, 0 );
58 callDir = cwd + std::string( "/" );
59 free( cwd );
62 std::string outDir;
63 std::string tmpDir;
64 bool allowCreateTmpDir = false;
65 std::string baseName;
66 std::string inputName;
67 std::string outputName;
68 std::string texJobName;
69 std::string labelDBName;
70 std::string fontmetricsOutputName;
72 enum FilenameRequests{ FILENAME_RESOURCE, FILENAME_IN, FILENAME_OUT, FILENAME_TMP, FILENAME_TEXJOB, FILENAME_LABELDB, FILENAME_AFM, FILENAME_TEXINPUTS, FILENAME_HTMLDOC, FILENAME_SHARE };
74 std::list< int > filenameRequestList;
75 std::list< const char * > resourceRequestList;
77 bool evalTrace = false;
78 bool evalBackTrace = false;
79 bool cleanupMemory = true;
80 bool memoryStats = false;
81 SimplePDF::PDF_Version::Version pdfVersion = SimplePDF::PDF_Version::VERSION_UNDEFINED;
82 SimplePDF::PDF_Version::Action pdfVersionAction = SimplePDF::PDF_Version::WARN;
83 std::ostringstream prependStreamOut;
85 SplitMode splitMode = SPLIT_NO;
87 argc -= 1;
88 argv += 1;
90 if( argc > 0 && strcmp( *argv, "--help" ) == 0 )
92 if( argc > 1 )
94 std::cerr << "The --help command line switch must appear alone." << std::endl ;
95 exit( Interaction::EXIT_INVOCATION_ERROR );
97 printHelp( );
98 exit( 0 );
101 while( argc > 0 )
103 const char * optionPrefix;
104 char * optionSuffix;
105 if( strcmp( *argv, "--yydebug" ) == 0 )
107 shapesdebug = 1;
108 argv += 1;
109 argc -= 1;
111 else if( strcmp( *argv, "--shapes-debug" ) == 0 )
113 shapesdebug = 1;
114 argv += 1;
115 argc -= 1;
117 else if( strcmp( *argv, "--system-debug" ) == 0 )
119 Interaction::debugSystem = true;
120 argv += 1;
121 argc -= 1;
123 else if( strcmp( *argv, "--afm-debug" ) == 0 )
125 Interaction::fontMetricDebug = true;
126 argv += 1;
127 argc -= 1;
129 else if( strcmp( *argv, "--afm-messages" ) == 0 )
131 Interaction::fontMetricMessages = true;
132 argv += 1;
133 argc -= 1;
135 else if( strcmp( *argv, "--tex-debug" ) == 0 )
137 Interaction::pdfLaTeXInteractionTo_stderr = true;
138 argv += 1;
139 argc -= 1;
141 else if( strcmp( *argv, "--log-globals" ) == 0 )
143 Interaction::logGlobals = true;
144 argv += 1;
145 argc -= 1;
147 else if( strprefixcmp( *argv, "--bytecolumn=", & optionPrefix, & optionSuffix ) )
149 Interaction::characterColumnInBytes = strtobool( optionSuffix, *argv );
150 argv += 1;
151 argc -= 1;
153 else if( strprefixcmp( *argv, "--debugstep=", & optionPrefix, & optionSuffix ) )
155 char * endp;
156 int tmp = strtol( optionSuffix, & endp, 10 );
157 if( tmp < 0 )
159 std::cerr << "The --debugstep value must be nonnegative: " << optionSuffix << std::endl ;
160 exit( Interaction::EXIT_INVOCATION_ERROR );
162 Interaction::debugStep = static_cast< size_t >( tmp );
163 argv += 1;
164 argc -= 1;
166 else if( strcmp( *argv, "--debuglog" ) == 0 )
168 argcAssertion( *argv, argc, 2 );
171 Kernel::theDebugLog.setFilename( *( argv + 1 ) );
173 catch( const char * ball )
175 std::cerr << ball << std::endl ;
176 exit( Interaction::EXIT_INVOCATION_ERROR );
178 argv += 2;
179 argc -= 2;
181 else if( strcmp( *argv, "--debuglog-stderr" ) == 0 )
185 Kernel::theDebugLog.setStream( & std::cerr );
187 catch( const char * ball )
189 std::cerr << ball << std::endl ;
190 exit( Interaction::EXIT_INVOCATION_ERROR );
192 argv += 1;
193 argc -= 1;
195 else if( strcmp( *argv, "--debuglog-stdout" ) == 0 )
199 Kernel::theDebugLog.setStream( & std::cout );
201 catch( const char * ball )
203 std::cerr << ball << std::endl ;
204 exit( Interaction::EXIT_INVOCATION_ERROR );
206 argv += 1;
207 argc -= 1;
209 else if( strprefixcmp( *argv, "--dtminerror=", & optionPrefix, & optionSuffix ) )
211 Computation::dtMinIsError = strtobool( optionSuffix, *argv );
212 argv += 1;
213 argc -= 1;
215 else if( strprefixcmp( *argv, "--fmguesserror=", & optionPrefix, & optionSuffix ) )
217 Computation::fontMetricGuessIsError = strtobool( optionSuffix, *argv );
218 argv += 1;
219 argc -= 1;
221 else if( strcmp( *argv, "--interactive") == 0 ||
222 strcmp( *argv, "-i") == 0 )
224 interactiveMode = true;
225 argv += 1;
226 argc -= 1;
228 else if( strcmp( *argv, "--blank") == 0 )
230 if( forceShapeMode ){
231 std::cerr << "Conflicting forcing of both blank mode and shape mode." << std::endl ;
232 exit( Interaction::EXIT_INVOCATION_ERROR );
234 forceBlankMode = true;
235 argv += 1;
236 argc -= 1;
238 else if( strcmp( *argv, "--shape") == 0 )
240 if( forceBlankMode ){
241 std::cerr << "Conflicting forcing of both blank mode and shape mode." << std::endl ;
242 exit( Interaction::EXIT_INVOCATION_ERROR );
244 forceShapeMode = true;
245 argv += 1;
246 argc -= 1;
248 else if( strprefixcmp( *argv, "--i-format-prompt=", & optionPrefix, & optionSuffix ) ||
249 strprefixcmp( *argv, "-?P", & optionPrefix, & optionSuffix ) )
251 interactionFormats.setPrompt( strdup( optionSuffix ) );
252 argv += 1;
253 argc -= 1;
255 else if( strprefixcmp( *argv, "--i-format-show=", & optionPrefix, & optionSuffix ) ||
256 strprefixcmp( *argv, "-?S", & optionPrefix, & optionSuffix ) )
258 interactionFormats.setShow( strdup( optionSuffix ) );
259 argv += 1;
260 argc -= 1;
262 else if( strprefixcmp( *argv, "--i-format-file=", & optionPrefix, & optionSuffix ) ||
263 strprefixcmp( *argv, "-?F", & optionPrefix, & optionSuffix ) )
265 interactionFormats.setFile( strdup( optionSuffix ) );
266 argv += 1;
267 argc -= 1;
269 else if( strprefixcmp( *argv, "--i-format-eof=", & optionPrefix, & optionSuffix ) ||
270 strprefixcmp( *argv, "-?E", & optionPrefix, & optionSuffix ) )
272 interactionFormats.setBye( strdup( optionSuffix ) );
273 argv += 1;
274 argc -= 1;
276 else if( strcmp( *argv, "--evaltrace" ) == 0 )
278 evalTrace = true;
279 argv += 1;
280 argc -= 1;
282 else if( strcmp( *argv, "--evalbacktrace" ) == 0 )
284 evalTrace = true;
285 evalBackTrace = true;
286 argv += 1;
287 argc -= 1;
289 else if( strprefixcmp( *argv, "--backtrace=", & optionPrefix, & optionSuffix ) )
291 Interaction::debugBacktrace = strtobool( optionSuffix, *argv );
292 argv += 1;
293 argc -= 1;
295 else if( strprefixcmp( *argv, "--iteration=", & optionPrefix, & optionSuffix ) )
297 iterativeMode = strtobool( optionSuffix, *argv );
298 argv += 1;
299 argc -= 1;
301 else if( strprefixcmp( *argv, "--resources=", & optionPrefix, & optionSuffix ) )
303 useResources = strtobool( optionSuffix, *argv );
304 argv += 1;
305 argc -= 1;
307 else if( strprefixcmp( *argv, "--stats=", & optionPrefix, & optionSuffix ) )
309 memoryStats = strtobool( optionSuffix, *argv );
310 argv += 1;
311 argc -= 1;
313 else if( strprefixcmp( *argv, "--memclean=", & optionPrefix, & optionSuffix ) )
315 cleanupMemory = strtobool( optionSuffix, *argv );
316 argv += 1;
317 argc -= 1;
319 else if( strprefixcmp( *argv, "--showfiles=", & optionPrefix, & optionSuffix ) )
321 Ast::theShapesScanner.setShowFiles( strtobool( optionSuffix, *argv ) );
322 argv += 1;
323 argc -= 1;
325 else if( strprefixcmp( *argv, "--pdf-version=", & optionPrefix, & optionSuffix ) || /* Note that we use that || shortcuts! */
326 strprefixcmp( *argv, "-v", & optionPrefix, & optionSuffix ) )
328 if( pdfVersion != SimplePDF::PDF_Version::VERSION_UNDEFINED )
330 std::cerr << "Multiply defined pdf version." << std::endl ;
331 exit( Interaction::EXIT_INVOCATION_ERROR );
334 switch( *optionSuffix )
336 case 'e':
337 pdfVersionAction = SimplePDF::PDF_Version::ERROR;
338 break;
339 case 'w':
340 pdfVersionAction = SimplePDF::PDF_Version::WARN;
341 break;
342 case 's':
343 pdfVersionAction = SimplePDF::PDF_Version::SILENT;
344 break;
345 default:
346 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 ;
347 exit( Interaction::EXIT_INVOCATION_ERROR );
349 ++optionSuffix;
350 if( strncmp( optionSuffix, "1.", 2 ) == 0 &&
351 '1' <= optionSuffix[2] && optionSuffix[2] <= '6' )
353 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 };
354 pdfVersion = versions[ optionSuffix[2] - '1' ];
356 else if( strcmp( optionSuffix, "X" ) == 0 )
358 std::cerr << "Restriction to PDF-X is not implemented, please try using a low version number, such as 1.1 instead." << std::endl ;
360 else
362 std::cerr << "Unsupported pdf version specification: " << optionSuffix << std::endl ;
363 exit( Interaction::EXIT_INVOCATION_ERROR );
365 argv += 1;
366 argc -= 1;
368 else if( strprefixcmp( *argv, "--warn=", & optionPrefix, & optionSuffix ) ||
369 strprefixcmp( *argv, "-W", & optionPrefix, & optionSuffix ) )
371 if( strcmp( optionSuffix, "display" ) == 0 ||
372 strcmp( optionSuffix, "d" ) == 0 )
374 Interaction::warningAction = Interaction::WARNING_DISPLAY;
376 else if( strcmp( optionSuffix, "error" ) == 0 ||
377 strcmp( optionSuffix, "e" ) == 0 )
379 Interaction::warningAction = Interaction::WARNING_ERROR;
381 else if( strcmp( optionSuffix, "ignore" ) == 0 ||
382 strcmp( optionSuffix, "i" ) == 0 )
384 Interaction::warningAction = Interaction::WARNING_IGNORE;
386 else
388 std::cerr << "Unsupported warning action: " << optionSuffix << std::endl ;
389 exit( Interaction::EXIT_INVOCATION_ERROR );
391 argv += 1;
392 argc -= 1;
394 else if( strprefixcmp( *argv, "--tp=", & optionPrefix, & optionSuffix ) )
396 Kernel::allowTransparency = strtobool( optionSuffix, *argv );
397 argv += 1;
398 argc -= 1;
400 else if( strprefixcmp( *argv, "--spot-pair=", & optionPrefix, & optionSuffix ) )
402 Kernel::allowSingletonPaths = ! strtobool( optionSuffix, *argv );
403 argv += 1;
404 argc -= 1;
406 else if( strprefixcmp( *argv, "--unit=", & optionPrefix, & optionSuffix ) )
408 Interaction::displayUnitName = optionSuffix;
410 argv += 1;
411 argc -= 1;
413 else if( strprefixcmp( *argv, "--splicingtol=", & optionPrefix, & optionSuffix ) )
417 Computation::theTrixelizeSplicingTol = Ast::theShapesScanner.strtoLength( optionSuffix );
419 catch( ... )
421 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
422 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
424 if( Computation::theTrixelizeSplicingTol <= 0 )
426 std::cerr << "Argument to " << optionPrefix << " not positive: " << Computation::theTrixelizeSplicingTol.offtype< 1, 0 >( ) << std::endl ;
427 exit( Interaction::EXIT_INVOCATION_ERROR );
430 argv += 1;
431 argc -= 1;
433 else if( strprefixcmp( *argv, "--overlaptol=", & optionPrefix, & optionSuffix ) )
437 Computation::theTrixelizeOverlapTol = Ast::theShapesScanner.strtoLength( optionSuffix );
439 catch( ... )
441 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
442 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
444 if( Computation::theTrixelizeOverlapTol <= 0 )
446 std::cerr << "Argument to " << optionPrefix << " not positive: " << Computation::theTrixelizeOverlapTol.offtype< 1, 0 >( ) << std::endl ;
447 exit( Interaction::EXIT_INVOCATION_ERROR );
450 argv += 1;
451 argc -= 1;
453 else if( strcmp( *argv, "--needpath" ) == 0 ||
454 strncmp( *argv, "-N", 2 ) == 0 )
456 bool longForm = strncmp( *argv, "--", 2 ) == 0;
458 const char * pth = 0;
459 if( longForm )
461 argcAssertion( *argv, argc, 2 );
462 pth = *( argv + 1 );
464 else
466 pth = (*argv) + 2;
469 if( strchr( pth, ':' ) != 0 )
471 const char * flag = 0;
472 const char * shortFlag = "-N";
473 if( longForm )
475 flag = *argv;
477 else
479 flag = shortFlag;
482 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
483 if( longForm )
485 std::cerr << " (or " << shortFlag << ")" ;
487 std::cerr <<"." << std::endl ;
488 exit( Interaction::EXIT_INVOCATION_ERROR );
491 Ast::theShapesScanner.push_backNeedPath( absoluteDirectory( pth ) );
493 if( longForm )
495 argv += 2;
496 argc -= 2;
498 else
500 argv += 1;
501 argc -= 1;
504 else if( strcmp( *argv, "--fontmetricspath" ) == 0 ||
505 strncmp( *argv, "-M", 2 ) == 0 )
507 bool longForm = strncmp( *argv, "--", 2 ) == 0;
509 const char * pth = 0;
510 if( longForm )
512 argcAssertion( *argv, argc, 2 );
513 pth = *( argv + 1 );
515 else
517 pth = (*argv) + 2;
520 if( strchr( pth, ':' ) != 0 )
522 const char * flag = 0;
523 const char * shortFlag = "-M";
524 if( longForm )
526 flag = *argv;
528 else
530 flag = shortFlag;
533 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
534 if( longForm )
536 std::cerr << " (or " << shortFlag << ")" ;
538 std::cerr <<"." << std::endl ;
539 exit( Interaction::EXIT_INVOCATION_ERROR );
542 Lang::Font::push_backFontMetricsPath( absoluteDirectory( pth ) );
544 if( longForm )
546 argv += 2;
547 argc -= 2;
549 else
551 argv += 1;
552 argc -= 1;
555 else if( strprefixcmp( *argv, "--seed=", & optionPrefix, & optionSuffix ) )
557 char * endp;
558 long s = strtol( optionSuffix, &endp, 10 );
559 if( *endp != '\0' )
561 std::cerr << "Argument to " << optionPrefix << " was not an integer: " << optionSuffix << std::endl ;
562 exit( Interaction::EXIT_INVOCATION_ERROR );
565 srand( s );
567 argv += 1;
568 argc -= 1;
570 else if( strprefixcmp( *argv, "--arcdelta=", & optionPrefix, & optionSuffix ) )
574 Computation::the_arcdelta = Ast::theShapesScanner.strtoLength( optionSuffix );
576 catch( ... )
578 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
579 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
581 if( Computation::the_arcdelta <= 0 )
583 std::cerr << "Argument to " << optionPrefix << " not positive: " << optionSuffix << std::endl ;
584 exit( Interaction::EXIT_INVOCATION_ERROR );
587 argv += 1;
588 argc -= 1;
590 else if( strprefixcmp( *argv, "--dtmin=", & optionPrefix, & optionSuffix ) )
592 char * endp;
593 Computation::the_dtMin = strtod( optionSuffix, &endp );
594 if( *endp != '\0' )
596 std::cerr << "Argument to " << optionPrefix << " was not a float: " << optionSuffix << std::endl ;
597 exit( Interaction::EXIT_INVOCATION_ERROR );
599 if( Computation::the_dtMin <= 0 )
601 std::cerr << "Argument to " << optionPrefix << " not positive: " << Computation::the_dtMin << std::endl ;
602 exit( Interaction::EXIT_INVOCATION_ERROR );
605 argv += 1;
606 argc -= 1;
608 else if( strprefixcmp( *argv, "--disttol=", & optionPrefix, & optionSuffix ) )
612 Computation::theDistanceTol = Ast::theShapesScanner.strtoLength( optionSuffix );
614 catch( ... )
616 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
617 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
619 if( Computation::theDistanceTol <= 0 )
621 std::cerr << "Argument to " << optionPrefix << " not positive: " << optionSuffix << std::endl ;
622 exit( Interaction::EXIT_INVOCATION_ERROR );
625 argv += 1;
626 argc -= 1;
628 else if( strcmp( *argv, "--prepend" ) == 0 )
630 argcAssertion( *argv, argc, 2 );
631 prependStreamOut << *( argv + 1 ) << std::endl ;
632 argv += 2;
633 argc -= 2;
635 else if( strcmp( *argv, "--base" ) == 0 )
637 argcAssertion( *argv, argc, 2 );
638 if( baseName != "" )
640 std::cerr << "The name base is multiply specified." << std::endl ;
641 exit( Interaction::EXIT_INVOCATION_ERROR );
643 baseName = *( argv + 1 );
644 argv += 2;
645 argc -= 2;
647 else if( strcmp( *argv, "--which" ) == 0 )
649 argcAssertion( *argv, argc, 2 );
650 filenameRequestList.push_back( FILENAME_RESOURCE );
651 resourceRequestList.push_back( *( argv + 1 ) );
652 argv += 2;
653 argc -= 2;
655 else if( strcmp( *argv, "--in" ) == 0 )
657 argcAssertion( *argv, argc, 2 );
658 if( inputName != "" )
660 std::cerr << "The input file is multiply specified." << std::endl ;
661 exit( Interaction::EXIT_INVOCATION_ERROR );
663 inputName = absoluteFilename( *( argv + 1 ) );
664 argv += 2;
665 argc -= 2;
667 else if( strcmp( *argv, "--which-in" ) == 0 )
669 filenameRequestList.push_back( FILENAME_IN );
670 argv += 1;
671 argc -= 1;
673 else if( strcmp( *argv, "--out" ) == 0 )
675 argcAssertion( *argv, argc, 2 );
676 if( outputName != "" )
678 std::cerr << "The output file is multiply specified." << std::endl ;
679 exit( Interaction::EXIT_INVOCATION_ERROR );
681 outputName = absoluteFilename( *( argv + 1 ) );
682 argv += 2;
683 argc -= 2;
685 else if( strcmp( *argv, "--which-out" ) == 0 )
687 filenameRequestList.push_back( FILENAME_OUT );
688 argv += 1;
689 argc -= 1;
691 else if( strcmp( *argv, "--texjob" ) == 0 )
693 argcAssertion( *argv, argc, 2 );
694 if( texJobName != "" )
696 std::cerr << "The tex job name is multiply specified." << std::endl ;
697 exit( Interaction::EXIT_INVOCATION_ERROR );
699 texJobName = *( argv + 1 );
700 if( texJobName.find( '/' ) != std::string::npos )
702 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 ;
703 exit( Interaction::EXIT_INVOCATION_ERROR );
705 argv += 2;
706 argc -= 2;
708 else if( strcmp( *argv, "--which-texjob" ) == 0 )
710 filenameRequestList.push_back( FILENAME_TEXJOB );
711 argv += 1;
712 argc -= 1;
714 else if( strcmp( *argv, "--labeldb" ) == 0 )
716 argcAssertion( *argv, argc, 2 );
717 if( labelDBName != "" )
719 std::cerr << "The label database file is multiply specified." << std::endl ;
720 exit( Interaction::EXIT_INVOCATION_ERROR );
722 labelDBName = absoluteFilename( *( argv + 1 ) );
723 argv += 2;
724 argc -= 2;
726 else if( strcmp( *argv, "--which-labeldb" ) == 0 )
728 filenameRequestList.push_back( FILENAME_LABELDB );
729 argv += 1;
730 argc -= 1;
732 else if( strcmp( *argv, "--afmout" ) == 0 )
734 argcAssertion( *argv, argc, 2 );
735 if( fontmetricsOutputName != "" )
737 std::cerr << "The font metrics output name is multiply specified." << std::endl ;
738 exit( Interaction::EXIT_INVOCATION_ERROR );
740 fontmetricsOutputName = absoluteFilename( *( argv + 1 ) );
741 argv += 2;
742 argc -= 2;
744 else if( strcmp( *argv, "--which-afmout" ) == 0 )
746 filenameRequestList.push_back( FILENAME_AFM );
747 argv += 1;
748 argc -= 1;
750 else if( strcmp( *argv, "--which-TEXINPUTS" ) == 0 )
752 filenameRequestList.push_back( FILENAME_TEXINPUTS );
753 argv += 1;
754 argc -= 1;
756 else if( strcmp( *argv, "--which-doc" ) == 0 )
758 filenameRequestList.push_back( FILENAME_HTMLDOC );
759 argv += 1;
760 argc -= 1;
762 else if( strcmp( *argv, "--which-share" ) == 0 )
764 filenameRequestList.push_back( FILENAME_SHARE );
765 argv += 1;
766 argc -= 1;
768 else if( strcmp( *argv, "--outdir" ) == 0 )
770 argcAssertion( *argv, argc, 2 );
771 if( outDir != "" )
773 std::cerr << "The output directory is multiply specified." << std::endl ;
774 exit( Interaction::EXIT_INVOCATION_ERROR );
776 outDir = absoluteDirectory( *( argv + 1 ) );
777 argv += 2;
778 argc -= 2;
780 else if( strprefixcmp( *argv, "--tmp*=", & optionPrefix, & optionSuffix ) )
782 allowCreateTmpDir = strtobool( optionSuffix, *argv );
783 argv += 1;
784 argc -= 1;
786 else if( strcmp( *argv, "--tmpdir" ) == 0 )
788 argcAssertion( *argv, argc, 2 );
789 if( tmpDir != "" )
791 std::cerr << "The temporaries directory is multiply specified." << std::endl ;
792 exit( Interaction::EXIT_INVOCATION_ERROR );
794 tmpDir = absoluteDirectory( *( argv + 1 ) );
795 argv += 2;
796 argc -= 2;
798 else if( strcmp( *argv, "--which-tmp" ) == 0 )
800 filenameRequestList.push_back( FILENAME_TMP );
801 argv += 1;
802 argc -= 1;
804 else if( strprefixcmp( *argv, "--split=", & optionPrefix, & optionSuffix ) )
806 if( strcmp( optionSuffix, "no" ) == 0 )
808 splitMode = SPLIT_NO;
810 else if( strcmp( optionSuffix, "flat" ) == 0 )
812 splitMode = SPLIT_FLAT;
814 else if( strcmp( optionSuffix, "dir" ) == 0 )
816 splitMode = SPLIT_DIR;
818 else
820 std::cerr << "The string \"" << optionSuffix << "\" in the command line argument \"" << *argv << "\" was not any of { 'no', 'flat', 'dir' }." << std::endl ;
822 argv += 1;
823 argc -= 1;
825 else if( strcmp( *argv, "--xpdf" ) == 0 )
827 previewOptions.launch_xpdf = true;
828 argv += 1;
829 argc -= 1;
831 else if( strcmp( *argv, "--xpdf-remote" ) == 0 )
833 argcAssertion( *argv, argc, 2 );
834 if( previewOptions.xpdfServer != "" )
836 std::cerr << "The xpdf server is multiply specified." << std::endl ;
837 exit( Interaction::EXIT_INVOCATION_ERROR );
839 previewOptions.xpdfServer = *( argv + 1 );
840 argv += 2;
841 argc -= 2;
843 else if( strcmp( *argv, "--xpdf-no-server" ) == 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_NOSERVER;
851 argv += 1;
852 argc -= 1;
854 else if( strcmp( *argv, "--xpdf-reload" ) == 0 )
856 if( previewOptions.xpdfAction != Interaction::PreviewOptions::XPDF_DEFAULT )
858 std::cerr << "The xpdf action is multiply specified." << std::endl ;
859 exit( Interaction::EXIT_INVOCATION_ERROR );
861 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_RELOAD;
862 argv += 1;
863 argc -= 1;
865 else if( strcmp( *argv, "--xpdf-quit" ) == 0 )
867 if( previewOptions.xpdfAction != Interaction::PreviewOptions::XPDF_DEFAULT )
869 std::cerr << "The xpdf action is multiply specified." << std::endl ;
870 exit( Interaction::EXIT_INVOCATION_ERROR );
872 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_QUIT;
873 argv += 1;
874 argc -= 1;
876 else if( strcmp( *argv, "--open" ) == 0 )
878 previewOptions.do_open = true;
879 argv += 1;
880 argc -= 1;
882 else if( strcmp( *argv, "--open-a" ) == 0 )
884 argcAssertion( *argv, argc, 2 );
885 previewOptions.do_open = true;
886 previewOptions.do_open_application = *( argv + 1 );
887 argv += 2;
888 argc -= 2;
890 else if( strcmp( *argv, "--version" ) == 0 )
892 printVersion( );
893 exit( Interaction::EXIT_OK );
895 else if( argc == 1 )
897 if( baseName != "" )
899 std::cerr << "The name base is multiply specified." << std::endl ;
900 exit( Interaction::EXIT_INVOCATION_ERROR );
902 typedef std::list< const char * > SuffixesType;
903 SuffixesType suffixes;
904 if( ! forceBlankMode ){
905 suffixes.push_back( "shape" );
907 if( ! forceShapeMode ){
908 suffixes.push_back( "blank" );
910 struct stat theStat;
911 if( stat( *argv, & theStat ) == 0 &&
912 ( theStat.st_mode & S_IFDIR ) == 0 ) /* We are not interested in directories here. */
914 inputName = *argv;
915 char * ext = *argv + strlen( *argv ) - 6;
916 if( ext <= *argv )
918 std::cerr << "The file name \"" << *argv << "\" is unexpectedly short (it should include the" ;
919 for( SuffixesType::const_iterator i = suffixes.begin( ); i != suffixes.end( ); ++i ){
920 if( i != suffixes.begin( ) )
921 std::cerr << " or" ;
922 std::cerr << " \"." << *i << "\"" ;
924 std::cerr << " suffix)." << std::endl ;
925 exit( Interaction::EXIT_INVOCATION_ERROR );
927 SuffixesType::const_iterator i = suffixes.begin( );
928 for( ; i != suffixes.end( ); ++i ){
929 if( ext[0] == '.' && strcmp( ext + 1, *i ) == 0 )
930 break; /* Found, leave iterator before end. */
932 if( i == suffixes.end( ) ){
933 std::cerr << "Expected" ;
934 for( SuffixesType::const_iterator i = suffixes.begin( ); i != suffixes.end( ); ++i ){
935 if( i != suffixes.begin( ) )
936 std::cerr << " or" ;
937 std::cerr << " \"." << *i << "\"" ;
939 std::cerr << " suffix in the file name \"" << *argv << "\"." << std::endl ;
940 exit( Interaction::EXIT_INVOCATION_ERROR );
943 else
945 SuffixesType::const_iterator i = suffixes.begin( );
946 for( ; i != suffixes.end( ); ++i ){
947 if( (*argv)[ strlen( *argv ) - 1 ] == '.' ){
948 inputName = std::string( *argv ) + *i;
949 }else{
950 inputName = std::string( *argv ) + "." + *i;
952 struct stat theStat;
953 if( stat( inputName.c_str( ), & theStat ) == 0 )
954 break; /* Found, leave iterator before end. */
956 if( i == suffixes.end( ) ){
957 /* No matching suffix was found.
958 * It is not entirely clear what is the best error message here,
959 * as the source file may be specified in several different ways.
960 * This should cause the least confusion.
962 std::cerr << "Failed to locate input file: " << *argv << std::endl ;
963 exit( Interaction::EXIT_INPUT_FILE_ERROR );
967 const char * slash = strrchr( inputName.c_str( ), '/' );
968 if( slash == 0 )
970 slash = inputName.c_str( );
972 else
974 ++slash;
976 size_t skipCount = slash - inputName.c_str( );
977 baseName = inputName.substr( skipCount, inputName.length( ) - skipCount - 6 );
979 argv += 1;
980 argc -= 1;
982 else
984 std::cerr << "Illegal command line option: " << *argv << std::endl ;
985 exit( Interaction::EXIT_INVOCATION_ERROR );
989 if( tmpDir == "" )
991 char * start = getenv( "SHAPESTMPDIR" );
992 if( start != 0 )
994 tmpDir = absoluteDirectory( start );
996 else
998 tmpDir = absoluteDirectory( "" );
1001 if( outDir == "" )
1003 if( interactiveMode )
1005 outDir = tmpDir;
1007 else
1009 outDir = absoluteDirectory( "" );
1012 ensureTmpDirectoryExists( tmpDir, allowCreateTmpDir );
1014 if( baseName == "" )
1016 if( interactiveMode )
1018 if( outputName == "" )
1020 if( splitMode == SPLIT_NO )
1022 outputName = outDir + "#shapes" + ".pdf";
1024 else
1026 outputName = outDir + "#shapes" ;
1030 if( texJobName == "" )
1032 texJobName = "#shapes.labels";
1035 else
1037 if( inputName == "" )
1039 typedef std::list< const char * > SuffixesType;
1040 SuffixesType suffixes;
1041 if( ! forceBlankMode ){
1042 suffixes.push_back( "shape" );
1044 if( ! forceShapeMode ){
1045 suffixes.push_back( "blank" );
1047 SuffixesType::const_iterator i = suffixes.begin( );
1048 for( ; i != suffixes.end( ); ++i ){
1049 struct stat theStat;
1050 inputName = absoluteFilename( ( baseName + "." + *i ).c_str( ) );
1051 if( stat( inputName.c_str( ), & theStat ) == 0 )
1052 break; /* Found, leave iterator before end. */
1054 if( i == suffixes.end( ) ){
1055 inputName = absoluteFilename( ( baseName + "." + suffixes.front( ) ).c_str( ) );
1058 if( outputName == "" )
1060 if( splitMode == SPLIT_NO )
1062 outputName = outDir + baseName + ".pdf";
1064 else
1066 outputName = outDir + baseName;
1069 if( texJobName == "" )
1071 texJobName = baseName + ".labels";
1073 if( labelDBName == "" )
1075 labelDBName = outDir + baseName + ".labels.pdf";
1077 if( fontmetricsOutputName == "" )
1079 fontmetricsOutputName = outDir + baseName + ".afm";
1083 if( forceBlankMode ) {
1084 Interaction::blankMode = true;
1085 } else if( forceShapeMode ){
1086 Interaction::blankMode = false;
1087 } else {
1088 Interaction::blankMode = ( inputName.size() >= 6 && inputName.compare(inputName.size() - 6, std::string::npos, ".blank") == 0 );
1091 if( outputName == "" )
1093 if( ! filenameRequestList.empty( ) || Interaction::blankMode )
1095 /* The output name will never really be used, so it's rather harmless to assign a dummy value.
1097 outputName = "?.pdf" ;
1099 else
1101 std::cerr << "The output file is undetermined. Consider specifying it using \"--out <filename>\"." << std::endl ;
1102 exit( Interaction::EXIT_INVOCATION_ERROR );
1106 if( labelDBName == "" )
1108 iterativeMode = false;
1110 if( iterativeMode == false )
1112 /* If the iterative mode has explicitly been turned off, we prevent any old labels from being reused. */
1113 labelDBName = "";
1116 if( ! Kernel::theDebugLog.initialized( ) )
1118 std::string::size_type suffixSep = outputName.rfind( '.' );
1119 if( suffixSep != std::string::npos && outputName.find( '/', suffixSep ) == std::string::npos )
1121 /* If there would have been a slash after the '.', the dot would have been part of a directory name.
1122 * Otherwise, we conclude that we have found the extension of a filename, and replace that extension
1123 * by ".log".
1125 Kernel::theDebugLog.setFilename( outputName.substr( 0, suffixSep ) + ".log" );
1127 else if( baseName != "" )
1129 Kernel::theDebugLog.setFilename( outDir + baseName + ".log" );
1131 else
1133 Kernel::theDebugLog.setFilename( outDir + "#shapes.log" );
1138 std::string inDir;
1139 std::string inPath = inputName;
1140 std::string::size_type slash = inPath.rfind( '/' );
1141 if( slash == std::string::npos )
1143 inDir = absoluteDirectory( "" );
1145 else
1147 inDir = absoluteDirectory( inPath.substr( 0, slash ).c_str( ) );
1149 Ast::theShapesScanner.setSourceDir( inDir );
1150 Kernel::theTeXLabelManager.setup( inDir, tmpDir, texJobName );
1153 if( Computation::theTrixelizeSplicingTol >= Computation::theTrixelizeOverlapTol )
1155 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 ;
1156 exit( Interaction::EXIT_INVOCATION_ERROR );
1159 if( previewOptions.xpdfServer == "" )
1161 previewOptions.xpdfServer = outputName;
1164 if( previewOptions.xpdfAction == Interaction::PreviewOptions::XPDF_DEFAULT )
1166 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_RAISE;
1169 addDefaultNeedPath( );
1170 addDefaultFontMetricsPath( );
1172 if( useResources )
1174 Ast::theShapesScanner.push_backNeedPath( std::string( Kernel::RESOURCES_DIR ) + "/extensions/" );
1175 Lang::Font::push_backFontMetricsPath( std::string( Kernel::RESOURCES_DIR ) + "/fontmetrics/" );
1178 if( ! filenameRequestList.empty( ) )
1180 std::list< const char * >::const_iterator resource = resourceRequestList.begin( );
1181 for( std::list< int >::const_iterator i = filenameRequestList.begin( );
1182 i != filenameRequestList.end( );
1183 ++i )
1185 switch( *i )
1187 case FILENAME_IN:
1188 if( inputName == "" )
1190 std::cout << "<stdin>" ;
1192 else
1194 std::cout << inputName ;
1196 break;
1197 case FILENAME_OUT:
1198 std::cout << outputName ;
1199 break;
1200 case FILENAME_TMP:
1201 std::cout << tmpDir ;
1202 break;
1203 case FILENAME_TEXJOB:
1204 std::cout << tmpDir << texJobName ;
1205 break;
1206 case FILENAME_LABELDB:
1207 std::cout << labelDBName ;
1208 break;
1209 case FILENAME_AFM:
1210 std::cout << fontmetricsOutputName ;
1211 break;
1212 case FILENAME_TEXINPUTS:
1214 std::cout << getenv( "TEXINPUTS" ) ;
1216 break;
1217 case FILENAME_HTMLDOC:
1219 std::cout << Kernel::HTML_DIR << "/index.html" ;
1221 break;
1222 case FILENAME_SHARE:
1224 std::cout << Kernel::RESOURCES_DIR ;
1226 break;
1227 case FILENAME_RESOURCE:
1231 std::cout << Ast::theShapesScanner.searchFile( *resource ) ;
1233 catch( const Exceptions::Exception & ball )
1235 std::cout.flush( );
1236 ball.display( std::cerr );
1237 exit( Interaction::EXIT_INVOCATION_ERROR );
1239 ++resource;
1241 break;
1242 default:
1243 std::cerr << "Internal error: filename request switch in main out of range." << std::endl ;
1244 exit( Interaction::EXIT_INTERNAL_ERROR );
1246 std::cout << std::endl ;
1248 exit( Interaction::EXIT_OK );
1251 if( pdfVersion == SimplePDF::PDF_Version::VERSION_UNDEFINED )
1253 pdfVersion = SimplePDF::PDF_Version::PDF_1_4;
1256 Kernel::the_PDF_version.setVersion( pdfVersion );
1257 Kernel::the_PDF_version.setAction( pdfVersionAction );
1260 std::ostringstream oss;
1261 time_t tmp;
1262 time( &tmp );
1263 struct tm * now( gmtime( &tmp ) );
1264 oss << "D:"
1265 << std::setfill( '0' )
1266 << std::setw(4) << 1900 + now->tm_year
1267 << std::setw(2) << 1 + now->tm_mon
1268 << std::setw(2) << now->tm_mday
1269 << std::setw(2) << now->tm_hour
1270 << std::setw(2) << now->tm_min
1271 << std::setw(2) << now->tm_sec
1272 << "+0000" ;
1273 Kernel::theDocInfo.addInfo( "CreationDate", SimplePDF::newString( oss.str( ).c_str( ) ) );
1276 std::istringstream prependStreamIn;
1277 if( ! prependStreamOut.str( ).empty( ) )
1279 prependStreamIn.str( prependStreamOut.str( ) );
1280 Ast::theShapesScanner.queueStream( & prependStreamIn, Ast::FileID::build_special( "<--prepend>" ) );
1283 std::ifstream iFile;
1284 std::istream * iFilePtr = 0;
1285 if( inputName == "" )
1287 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( "<stdin>" ) );
1288 iFilePtr = & std::cin;
1289 if( interactiveMode )
1291 /* On some platforms, the lexer crashes if it is not given a new file at EOF,
1292 * so we need to put an empty file after the prelude to avoid that.
1294 * Note that we use the iFile object here, although this will not be pointed to
1295 * by iFilePtr, which points to std::cin instead.
1297 const char * nullName = "/dev/null";
1298 iFile.open( nullName );
1299 if( ! iFile.good( ) || ! iFile.is_open( ) )
1301 std::cerr << "Failed to open " << nullName << " to create empty input file." << std::endl ;
1302 exit( Interaction::EXIT_EXTERNAL_ERROR );
1304 Ast::theShapesScanner.queueStream( & iFile, Ast::FileID::build_special( nullName ) );
1306 else
1308 Ast::theShapesScanner.queueStream( iFilePtr, Ast::FileID::build_special( "<stdin>" ) );
1309 // Ast::theShapesScanner.setNameOf_yyin( "<stdin>" );
1312 else
1314 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( inputName.c_str( ) ) );
1315 iFile.open( inputName.c_str( ) );
1316 if( ! iFile.good( ) || ! iFile.is_open( ) )
1318 std::cerr << "Failed to open " << inputName << " for input." << std::endl ;
1319 exit( Interaction::EXIT_INPUT_FILE_ERROR );
1321 iFilePtr = & iFile;
1322 if( ! interactiveMode )
1324 struct stat theStat;
1325 if( stat( inputName.c_str( ), & theStat ) != 0 )
1327 std::cerr << "Failed to stat " << inputName << " (but it could be opened for input...)." << std::endl ;
1328 exit( Interaction::EXIT_INPUT_FILE_ERROR );
1330 Ast::theShapesScanner.queueStream( iFilePtr, Ast::FileID::build_stat( theStat, inputName ) );
1336 /* This will initiate the reading of preludes, and when there are no more preludes, we turn to the queue set up by calling queueStream. */
1337 Ast::theShapesScanner.start( );
1339 catch( const Exceptions::StaticInconsistency & ball )
1341 std::cout.flush( );
1342 std::cerr << ball.loc( ) << ": " ;
1343 ball.display( std::cerr );
1344 std::cerr << "There were errors in the namespace directory hierarchy." << std::endl ;
1345 abortProcedure( ball.exitCode( ) );
1347 catch( const Exceptions::Exception & ball )
1349 std::cout.flush( );
1350 ball.display( std::cerr );
1351 std::cerr << "There were errors in the namespace directory hierarchy." << std::endl ;
1352 abortProcedure( ball.exitCode( ) );
1355 RefCountPtr< std::ifstream > labelDBFile = RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1356 Kernel::WarmCatalog::ShipoutList documents;
1358 /* This is where all the evaluation takes place. It is written as a separate functions to keep the function main short.
1362 if( interactiveMode )
1364 interactiveEvaluation( *iFilePtr, std::cout, outputName, splitMode,
1365 evalTrace, evalBackTrace, cleanupMemory,
1366 labelDBFile, labelDBName );
1368 else
1370 nonInteractiveEvaluation( documents, splitMode,
1371 evalTrace, evalBackTrace, cleanupMemory,
1372 labelDBFile, labelDBName );
1375 catch( const NonLocalExit::DynamicBindingNotFound & ball )
1377 std::cerr << "Caught DynamicBindingNotFound at top level." << std::endl
1378 << "This should really not be possible; it is an internal error." << std::endl ;
1379 exit( Interaction::EXIT_INTERNAL_ERROR );
1381 catch( const NonLocalExit::NotThisType & ball )
1383 std::cerr << "Caught NotThisType at top level." << std::endl
1384 << "This should really not be possible; it is an internal error." << std::endl ;
1385 exit( Interaction::EXIT_INTERNAL_ERROR );
1387 catch( const NonLocalExit::NonLocalExitBase & ball )
1389 std::cerr << "Caught an unknown descendant to NonLocalExitBase at top level." << std::endl
1390 << "This should really not be possible; it is an internal error." << std::endl ;
1391 exit( Interaction::EXIT_INTERNAL_ERROR );
1393 catch( const char * ball )
1395 std::cerr << "Caught (char*) ball at top level:" << std::endl
1396 << " " << ball << std::endl ;
1397 exit( Interaction::EXIT_GENERIC_ERROR );
1399 catch( const std::string & ball )
1401 std::cerr << "Caught (string) ball at top level:" << std::endl
1402 << " " << ball << std::endl ;
1403 exit( Interaction::EXIT_GENERIC_ERROR );
1405 catch( const std::exception & ball )
1407 std::cerr << "Caught std::exception at top level:" << std::endl
1408 << " " << ball.what( ) << std::endl ;
1409 exit( Interaction::EXIT_GENERIC_ERROR );
1411 catch( ... )
1413 std::cerr << "Caught (...) ball at top level." << std::endl ;
1414 exit( Interaction::EXIT_GENERIC_ERROR );
1417 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1419 abortProcedure( Interaction::EXIT_USER_ERROR );
1422 if( memoryStats )
1424 std::cerr << "Summary:" << std::endl ;
1425 std::cerr << "Environments: alive: " << Kernel::Environment::liveCount << " of total: " << Kernel::Environment::createdCount
1426 << " (" << 100 * static_cast< double >( Kernel::Environment::liveCount ) / static_cast< double >( Kernel::Environment::createdCount ) << "%)" << std::endl ;
1429 if( ! Interaction::blankMode )
1430 writeOutput( outputName, documents, splitMode );
1432 /* This must be done after the output has been written, and before iterativeFinish writes to the labels database file.
1434 Kernel::thePDFImporter.free( );
1436 if( labelDBFile != NullPtr< std::ifstream >( ) )
1438 if( labelDBFile->is_open( ) )
1440 labelDBFile->close( );
1442 labelDBFile = NullPtr< std::ifstream >( ); // Free the reference.
1445 if( iterativeMode )
1447 Kernel::theTeXLabelManager.iterativeFinish( labelDBName );
1450 if( ! Interaction::blankMode )
1451 previewOptions.preview( outputName, splitMode != SPLIT_NO );
1453 destroyGlobals( );
1455 return 0;
1459 void
1460 argcAssertion( const char * optionSpecifier, int argc, int argcMin )
1462 if( argc < argcMin )
1464 std::cerr << "The command line option " << optionSpecifier << " requires " << argcMin - 1 << " parameters." << std::endl ;
1465 exit( Interaction::EXIT_INVOCATION_ERROR );
1469 bool
1470 strprefixcmp( char * str, const char * prefix, const char ** prefixDst, char ** endp )
1472 *prefixDst = prefix;
1473 int len = strlen( prefix );
1474 bool res = ( strncmp( str, prefix, len ) == 0 );
1475 *endp = str + len;
1476 return res;
1479 bool
1480 strtobool( const char * str, const char * containingString, const char * trueLabel, const char * falseLabel )
1482 if( trueLabel != 0 &&
1483 strcmp( str, trueLabel ) == 0 )
1485 return true;
1487 if( falseLabel != 0 &&
1488 strcmp( str, falseLabel ) == 0 )
1490 return false;
1492 if( strcmp( str, "yes" ) == 0 ||
1493 strcmp( str, "true" ) == 0 ||
1494 strcmp( str, "on" ) == 0 )
1496 return true;
1498 if( strcmp( str, "no" ) == 0 ||
1499 strcmp( str, "false" ) == 0 ||
1500 strcmp( str, "off" ) == 0)
1502 return false;
1504 std::cerr << "The string \"" << str << "\" in the command line argument \"" << containingString << "\" was not recognized as a boolean value." << std::endl ;
1505 exit( Interaction::EXIT_INVOCATION_ERROR );
1509 RefCountPtr< std::ifstream >
1510 performIterativeStartup( const std::string & labelDBName )
1513 struct stat theStat;
1514 if( stat( labelDBName.c_str( ), & theStat ) != 0 )
1516 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1519 // {
1520 // ostringstream mvCommand;
1521 // mvCommand << "cp '" << oldFilename.str( ) << "' '" << labelDBName.str( ) << "'" ;
1522 // Interaction::systemDebugMessage( mvCommand.str( ) );
1523 // if( system( mvCommand.str( ).c_str( ) ) != 0 )
1524 // {
1525 // return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1526 // }
1527 // }
1528 RefCountPtr< std::ifstream > labelsFile( new std::ifstream( labelDBName.c_str( ) ) );
1529 if( ! labelsFile->good( ) )
1531 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1535 Kernel::theTeXLabelManager.iterativeStartup( labelsFile );
1536 return labelsFile;
1538 catch( const char * ball )
1540 std::cerr << "Caught (char*) ball from iterative startup:" << std::endl
1541 << " " << ball << std::endl ;
1542 exit( Interaction::EXIT_GENERIC_ERROR );
1544 catch( const std::string & ball )
1546 std::cerr << "Caught (string) ball from iterative startup:" << std::endl
1547 << " " << ball << std::endl ;
1548 exit( Interaction::EXIT_GENERIC_ERROR );
1550 catch( const Exceptions::Exception & ball )
1552 ball.display( std::cerr );
1553 exit( ball.exitCode( ) );
1555 catch( ... )
1557 std::cerr << "Caught (...) ball from iterative startup." << std::endl ;
1558 exit( Interaction::EXIT_GENERIC_ERROR );
1563 void
1564 abortProcedure( int exitCode )
1566 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1568 std::cout.flush( );
1569 while( ! Kernel::thePostCheckErrorsList.empty( ) )
1571 Exceptions::Exception * e = Kernel::thePostCheckErrorsList.front( );
1572 Kernel::thePostCheckErrorsList.pop_front( );
1574 typedef const Exceptions::PostCondition ErrorType;
1575 ErrorType * err = dynamic_cast< ErrorType * >( e );
1576 if( err != 0 )
1578 std::cerr << err->loc( ) << ": " ;
1579 err->display( std::cerr );
1580 continue;
1584 typedef const Exceptions::RuntimeError ErrorType;
1585 ErrorType * err = dynamic_cast< ErrorType * >( e );
1586 if( err != 0 )
1588 std::cerr << err->getLoc( ) << " (runtime): " ;
1589 err->display( std::cerr );
1590 continue;
1593 std::cerr << "(Bad post-exception type)" << ": " ;
1594 e->display( std::cerr );
1597 std::cerr << "Aborting job. Output files are left unchanged." << std::endl ;
1598 exit( exitCode );
1601 namespace Shapes
1603 namespace Helpers
1605 void setSelfRef( RefCountPtr< const Lang::Class > cls )
1607 cls->setSelfRef( cls );
1609 void initClass( RefCountPtr< const Lang::Class > cls )
1611 Lang::SystemFinalClass * typedPtr = dynamic_cast< Lang::SystemFinalClass * >( const_cast< Lang::Class * >( cls.getPtr( ) ) );
1612 if( typedPtr != 0 )
1614 typedPtr->init( );
1616 else
1618 std::cerr << "Helpers::initMutators was called with object of bad type." << std::endl ;
1619 exit( Interaction::EXIT_INTERNAL_ERROR );
1625 void
1626 writeOutput( std::string & outputName, const Kernel::WarmCatalog::ShipoutList & documents, SplitMode splitMode )
1630 switch( splitMode )
1632 case SPLIT_NO:
1634 if( documents.size( ) != 1 )
1636 std::cerr << "Internal error: Failed to produce exactly one document of output although --split=no." << std::endl ;
1638 std::ofstream oFile;
1639 noSplitOpen( & oFile, outputName );
1640 documents.front( ).writeFile( oFile, Kernel::the_PDF_version );
1642 break;
1643 case SPLIT_FLAT:
1645 rmSplitFiles( outputName, "-" );
1646 size_t physicalPageNo = 1;
1647 for( Kernel::WarmCatalog::ShipoutList::const_iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1649 std::ofstream oFile;
1650 splitOpen( & oFile, outputName, "-", physicalPageNo );
1651 i->writeFile( oFile, Kernel::the_PDF_version );
1654 break;
1655 case SPLIT_DIR:
1657 mkSplitDir( outputName );
1658 rmSplitFiles( outputName, "/" );
1659 size_t physicalPageNo = 1;
1660 for( Kernel::WarmCatalog::ShipoutList::const_iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1662 std::ofstream oFile;
1663 splitOpen( & oFile, outputName, "/", physicalPageNo );
1664 i->writeFile( oFile, Kernel::the_PDF_version );
1667 break;
1670 catch( const char * ball )
1672 std::cerr << "Writing output file failed:" << std::endl
1673 << " " << ball << std::endl ;
1674 exit( Interaction::EXIT_GENERIC_ERROR );
1676 catch( const std::string & ball )
1678 std::cerr << "Writing output file failed:" << std::endl
1679 << " " << ball << std::endl ;
1680 exit( Interaction::EXIT_GENERIC_ERROR );
1684 void
1685 setupGlobals( )
1687 Lang::ElementaryPath2D * bbox = new Lang::ElementaryPath2D;
1688 bbox->push_back( new Concrete::PathPoint2D( 0, 0 ) );
1689 bbox->close( );
1690 Lang::THE_POINTPICTURE = RefCountPtr< Lang::Drawable2D >( new Lang::BBoxed2D( Lang::THE_NULL2D,
1691 RefCountPtr< Lang::ElementaryPath2D >( bbox ) ) );
1692 Helpers::setSelfRef( Lang::THE_OBJECT );
1694 Helpers::setSelfRef( Lang::Void::TypeID );
1695 Helpers::setSelfRef( Lang::Symbol::TypeID );
1696 Helpers::setSelfRef( Lang::Float::TypeID );
1697 Helpers::setSelfRef( Lang::Length::TypeID );
1698 Helpers::setSelfRef( Lang::Integer::TypeID );
1699 Helpers::setSelfRef( Lang::Boolean::TypeID );
1700 Helpers::setSelfRef( Lang::String::TypeID );
1701 Helpers::setSelfRef( Lang::FloatPair::TypeID );
1702 Helpers::setSelfRef( Lang::FloatTriple::TypeID );
1703 Helpers::setSelfRef( Lang::Coords2D::TypeID );
1704 Helpers::setSelfRef( Lang::CornerCoords2D::TypeID );
1705 Helpers::setSelfRef( Lang::Coords3D::TypeID );
1707 Helpers::setSelfRef( Lang::Function::TypeID );
1708 Helpers::setSelfRef( Lang::Transform2D::TypeID );
1709 Helpers::setSelfRef( Lang::Transform3D::TypeID );
1711 Helpers::setSelfRef( Lang::Class::TypeID );
1713 Helpers::setSelfRef( Lang::TransformedInstance::TypeID );
1715 Helpers::initClass( Lang::String::TypeID );
1716 Helpers::initClass( Kernel::WarmGroup2D::TypeID );
1717 Helpers::initClass( Kernel::WarmGroup3D::TypeID );
1718 Helpers::initClass( Kernel::WarmCatalog::TypeID );
1719 Helpers::initClass( Kernel::WarmDebugger::TypeID );
1720 Helpers::initClass( Kernel::Warm_vector::TypeID );
1721 Helpers::initClass( Kernel::WarmGraph::TypeID );
1722 Helpers::initClass( Lang::Font::TypeID );
1723 Helpers::initClass( Lang::SingleList::TypeID );
1724 Helpers::initClass( Lang::SingleListNull::TypeID );
1725 Helpers::initClass( Lang::ConsPair::TypeID );
1726 Helpers::initClass( Lang::VectorFunction::TypeID );
1727 Helpers::initClass( Lang::Transform2D::TypeID );
1728 Helpers::initClass( Lang::Transform3D::TypeID );
1729 Helpers::initClass( Lang::Graph::TypeID );
1730 Helpers::initClass( Lang::Node::TypeID );
1731 Helpers::initClass( Lang::Edge::TypeID );
1734 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric2D" ) );
1735 Lang::Geometric2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1736 Helpers::setSelfRef( Lang::Geometric2D::TypeID );
1737 /* Note that addVirtual must not be called before the selfRef is set!
1741 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric3D" ) );
1742 Lang::Geometric3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1743 Helpers::setSelfRef( Lang::Geometric3D::TypeID );
1744 /* Note that addVirtual must not be called before the selfRef is set!
1748 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable2D" ) );
1749 Lang::Drawable2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1750 Helpers::setSelfRef( Lang::Drawable2D::TypeID );
1751 /* Note that addVirtual must not be called before the selfRef is set!
1753 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1756 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable3D" ) );
1757 Lang::Drawable3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1758 Helpers::setSelfRef( Lang::Drawable3D::TypeID );
1759 /* Note that addVirtual must not be called before the selfRef is set!
1761 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1764 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Color" ) );
1765 Lang::Color::TypeID = RefCountPtr< const Lang::Class >( tmp );
1766 Helpers::setSelfRef( Lang::Color::TypeID );
1767 /* Note that addVirtual must not be called before the selfRef is set!
1769 tmp->addVirtual( "stroking" );
1770 tmp->addVirtual( "nonstroking" );
1773 #ifdef HAVE_FT2
1775 FT_Error error = FT_Init_FreeType( & Kernel::theFreeType );
1776 if( error != 0 )
1778 std::cerr << "Failed to initialize FreeType library object: " << Kernel::FreeTypeErrorMessage( error ) << std::endl ;
1779 exit( Interaction::EXIT_EXTERNAL_ERROR );
1782 #endif
1783 #ifdef HAVE_FONTCONFIG
1784 if( FcInit( ) != FcTrue )
1786 std::cerr << "Failed to initialize FontConfig library." << std::endl ;
1787 exit( Interaction::EXIT_EXTERNAL_ERROR );
1789 #endif
1792 void
1793 Interaction::PreviewOptions::preview( const std::string & filename, bool splitNoLaunch ) const
1795 if( launch_xpdf )
1797 if( splitNoLaunch )
1799 std::cerr << "Warning: Not launching viewer since the documet was split." << std::endl ;
1801 else
1803 xpdfHelper( filename );
1807 if( do_open )
1809 if( splitNoLaunch )
1811 std::cerr << "Warning: Not launching viewer since the documet was split." << std::endl ;
1813 else
1815 openHelper( filename );
1820 void
1821 Interaction::PreviewOptions::xpdfHelper( const std::string & filename ) const
1823 pid_t xpdfProcess = fork( );
1824 if( xpdfProcess == -1 )
1826 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running xpdf." ) );
1829 if( xpdfProcess == 0 ) /* This is the child */
1831 /* The exec call below never returns, so the child process never leaves this if clause.
1832 * Hence, there is no need to create a special else clasuse below.
1834 switch( xpdfAction )
1836 case XPDF_RAISE:
1837 execlp( "xpdf", "xpdf", "-remote", xpdfServer.c_str( ), "-raise", filename.c_str( ), static_cast< const char * >( 0 ) );
1838 break;
1839 case XPDF_RELOAD:
1840 execlp( "xpdf", "xpdf", "-remote", xpdfServer.c_str( ), "-reload", static_cast< const char * >( 0 ) );
1841 break;
1842 case XPDF_QUIT:
1843 execlp( "xpdf", "xpdf", "-remote", xpdfServer.c_str( ), "-quit", static_cast< const char * >( 0 ) );
1844 break;
1845 case XPDF_NOSERVER:
1846 execlp( "xpdf", "xpdf", filename.c_str( ), static_cast< const char * >( 0 ) );
1847 break;
1848 default:
1849 std::cerr << "Internal error: XpdfAction switch out of range." << std::endl ;
1850 exit( Interaction::EXIT_INTERNAL_ERROR );
1852 if( errno != 0 )
1854 std::cerr << "Recieved errno = " << errno << " from execlp call to xpdf." << std::endl ;
1855 exit( Interaction::EXIT_EXTERNAL_ERROR );
1857 std::cerr << "execlp call to xpdf returned with errno == 0." << std::endl ;
1858 exit( Interaction::EXIT_INTERNAL_ERROR );
1863 void
1864 Interaction::PreviewOptions::openHelper( const std::string & filename ) const
1866 pid_t openProcess = fork( );
1867 if( openProcess == -1 )
1869 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running open." ) );
1872 if( openProcess == 0 ) /* This is the child */
1874 /* The exec call below never returns, so the child process never leaves this if clause.
1875 * Hence, there is no need to create a special else clasuse below.
1877 if( do_open_application != 0 )
1879 execlp( "open", "open", "-a", do_open_application, filename.c_str( ), static_cast< const char * >( 0 ) );
1881 else
1883 execlp( "open", "open", filename.c_str( ), static_cast< const char * >( 0 ) );
1885 if( errno != 0 )
1887 std::cerr << "Recieved errno = " << errno << " from execlp call to open." << std::endl ;
1888 exit( Interaction::EXIT_EXTERNAL_ERROR );
1890 std::cerr << "execlp call to open returned with errno == 0." << std::endl ;
1891 exit( Interaction::EXIT_INTERNAL_ERROR );
1896 void
1897 Interaction::systemDebugMessage( const std::string & msg )
1899 if( Interaction::debugSystem )
1901 std::cerr << "System command: " << msg << std::endl ;
1905 void
1906 Interaction::warn_or_push( Exceptions::Exception * message, PtrOwner_back_Access< std::list< Exceptions::Exception * > > * errorsList )
1908 switch( Interaction::warningAction )
1910 case Interaction::WARNING_DISPLAY:
1911 Interaction::displayWarning( *message );
1912 delete message;
1913 break;
1914 case Interaction::WARNING_ERROR:
1915 errorsList->push_back( message );
1916 break;
1917 case Interaction::WARNING_IGNORE:
1918 delete message;
1919 break;
1923 void
1924 Interaction::displayWarning( const Exceptions::Exception & ball )
1926 std::cerr << "***Warning*** " ;
1927 ball.display( std::cerr );
1930 void
1931 addDefaultNeedPath( )
1933 char * start = getenv( "SHAPESINPUTS" );
1934 if( start == 0 )
1936 Ast::theShapesScanner.push_backNeedPath( "./" );
1937 return;
1939 char * tok = strsep( & start, ":" );
1940 while( tok != 0 )
1942 Ast::theShapesScanner.push_backNeedPath( tok );
1943 tok = strsep( & start, ":" );
1947 void
1948 addDefaultFontMetricsPath( )
1950 char * start = getenv( "SHAPESFONTMETRICS" );
1951 if( start == 0 )
1953 return;
1955 char * tok = strsep( & start, ":" );
1956 while( tok != 0 )
1958 Lang::Font::push_backFontMetricsPath( tok );
1959 tok = strsep( & start, ":" );
1963 void
1964 destroyGlobals( )
1966 Helpers::requireUTF8ToMacRomanConverter( true ); // true means "cleanup"
1967 Helpers::requireMacRomanToUTF8Converter( true ); // true means "cleanup"
1968 Helpers::requireUTF8ToWinANSIConverter( true ); // true means "cleanup"
1969 Helpers::requireUTF8ToUTF16BEConverter( true ); // true means "cleanup"
1970 Helpers::requireUTF8ToASCIIConverter( true ); // true means "cleanup"
1971 Helpers::requireUTF8ToUCS4Converter( true ); // true means "cleanup"
1972 Helpers::requireUTF16BEToUCS4Converter( true ); // true means "cleanup"
1973 Helpers::requireGlyphList( true ); // true means "cleanup"
1974 Helpers::requireMacRomanEncoding( true ); // true means "cleanup"
1975 #ifdef HAVE_FONTCONFIG
1976 FcFini( );
1977 #endif
1978 #ifdef HAVE_FT2
1980 FT_Error error = FT_Done_FreeType( Kernel::theFreeType );
1981 if( error != 0 )
1983 std::cerr << "Failed to \"destroy\" FreeType library object: " << Kernel::FreeTypeErrorMessage( error ) << std::endl ;
1984 exit( Interaction::EXIT_EXTERNAL_ERROR );
1987 #endif
1990 std::string
1991 absoluteFilename( const char * filename )
1993 if( *filename == '/' )
1995 return filename;
1997 return callDir + filename;
2000 std::string
2001 absoluteDirectory( const char * filename )
2003 if( *filename == '\0' )
2005 return callDir;
2007 if( filename[ strlen( filename ) - 1 ] != '/' )
2009 if( *filename == '/' )
2011 return filename + std::string( "/" );
2013 return callDir + filename + "/";
2015 if( *filename == '/' )
2017 return filename;
2019 return callDir + filename;
2022 #include <iomanip>
2024 void
2025 ensureTmpDirectoryExists( const std::string & dirname, bool allowCreate )
2027 struct stat theStat;
2028 if( stat( dirname.c_str( ), & theStat ) == 0 )
2030 if( ( theStat.st_mode & S_IFDIR ) == 0 )
2032 std::cerr << "The path " << dirname << " was expected to reference a directory." << std::endl ;
2033 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
2035 // if( ( theStat.st_mode & S_IWOTH ) == 0 )
2036 // {
2037 // std::cerr << "The directory " << dirname << " was expected have write permission for others." << std::endl ;
2038 // exit( Interaction::EXIT_FILE_PERMISSION_ERROR );
2039 // }
2040 return;
2043 if( ! allowCreate )
2045 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 ;
2046 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
2049 size_t i2 = 0; /* We know there's a slash at the first position */
2050 i2 = dirname.find( '/', i2 + 1 );
2051 bool atRoot = true;
2052 while( stat( dirname.substr( 0, i2 ).c_str( ), & theStat ) == 0 )
2054 atRoot = false;
2055 i2 = dirname.find( '/', i2 + 1 );
2057 if( atRoot )
2059 std::cerr << "Shapes will not create directories for temporary files at the root: " << dirname << std::endl ;
2060 exit( Interaction::EXIT_INVOCATION_ERROR );
2063 mode_t oldUmask = umask( 0 ); /* We want to be able to create directories with any permissions. */
2064 while( i2 != std::string::npos )
2066 if( mkdir( dirname.substr( 0, i2 ).c_str( ), theStat.st_mode & ( S_IRWXU | S_IRWXG | S_IRWXO ) ) != 0 )
2068 std::cerr << "Failed to create directory for temporary files (errno=" << errno << "): " << dirname.substr( 0, i2 ) << std::endl ;
2069 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
2071 i2 = dirname.find( '/', i2 + 1 );
2073 umask( oldUmask );
2076 void
2077 escapeExtGlobChars( const std::string & str, std::ostream & dst )
2079 const char * special = "*?[+@!";
2080 for( std::string::const_iterator i = str.begin( ); i != str.end( ); ++i )
2082 if( strchr( special, *i ) != 0 )
2084 dst << '\\' ;
2086 dst << *i ;
2090 void
2091 rmSplitFiles( const std::string & outputName, const char * sep )
2093 std::ostringstream rmCommand;
2094 rmCommand << "sh -O extglob -c 'rm -f " ;
2095 escapeExtGlobChars( outputName, rmCommand );
2096 rmCommand << sep << "+([0-9]).pdf'" ;
2097 Interaction::systemDebugMessage( rmCommand.str( ) );
2098 if( system( rmCommand.str( ).c_str( ) ) != 0 )
2100 /* Never mind; we made a try, and this probably means that there were no files to remove. */
2104 void
2105 mkSplitDir( const std::string & outputName )
2107 struct stat theStat;
2108 if( stat( outputName.c_str( ), & theStat ) == 0 )
2110 if( ( theStat.st_mode & S_IFDIR ) == 0 )
2112 std::cerr << "The path " << outputName << " was expected to reference a directory." << std::endl ;
2113 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
2116 else
2118 if( mkdir( outputName.c_str( ), S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
2120 std::cerr << "Failed to create directory for split document files (errno=" << errno << "): " << outputName << std::endl ;
2121 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
2126 void
2127 noSplitOpen( std::ofstream * oFile, const std::string & outputName )
2129 oFile->open( outputName.c_str( ) );
2130 if( ! oFile->good( ) )
2132 /* If this is because the output directory does not exist, we shall inform the user about this. */
2133 std::string::size_type slash = outputName.rfind( '/' );
2134 if( slash != std::string::npos )
2136 std::string outputDir = outputName.substr( 0, slash );
2137 struct stat theStat;
2138 if( stat( outputDir.c_str( ), & theStat ) == 0 )
2140 if( ( theStat.st_mode & S_IFDIR ) == 0 )
2142 std::cerr << "The prefix " << outputDir << " of the output name must be a directory." << std::endl ;
2143 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
2145 /* In case we reach here, the directory exists, so we fall back on the generic error message below. */
2147 else
2149 std::cerr << "The file " << outputName << " cannot be opened for output since the directory " << outputDir << " does not exist." << std::endl ;
2150 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
2153 std::cerr << "Failed to open " << outputName << " for output." << std::endl ;
2154 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
2158 std::string
2159 splitOpen( std::ofstream * oFile, const std::string outputName, const char * sep, size_t physicalPageNo )
2161 std::ostringstream oss;
2162 oss << outputName << sep << physicalPageNo << ".pdf" ;
2163 std::string filename( oss.str( ) );
2164 oFile->open( filename.c_str( ) );
2165 if( ! oFile->good( ) )
2167 std::cerr << "Failed to open " << filename << " for output." << std::endl ;
2168 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
2170 return filename;
2173 void
2174 printHelp( )
2176 std::string ind(" ");
2177 std::cout
2178 << "Usage:" << std::endl
2179 << ind << "shapes [OPTIONS] file[.[shape|blank]]" << std::endl
2180 << "A subset of options is listed below, by cathegory. This message is just" << std::endl
2181 << "a reminder; legal option values are only listed in rare cases." << std::endl
2182 << "Files: --in --out --outdir --texjob --tmpdir --tmp*= --labeldb" << std::endl
2183 << "Paths: --needpath -N --fontmetricspath --resources=" << std::endl
2184 << "Multipage: --split=('no'|'flat'|'dir')" << std::endl
2185 << "PDF: (--pdf-version=|-v)(e|w|s)1.N --tp= --spot-pair=" << std::endl
2186 << "Tolerances: --arcdelta= --dtmin= --dtminerror= --splicingtol= --overlaptol=" << std::endl
2187 << "Log file: --debuglog --debuglog-stderr --debuglog-stdout" << std::endl
2188 << "Help: --help --which-doc --showfiles= --which --version" << std::endl
2189 << "Viewing: --xpdf --open --open-a" << std::endl
2190 << "Interactive: --interactive -i" << std::endl
2191 << "See the man page for a complete listing and details." << std::endl