Updating the changelog in the VERSION file, and version_sync.
[shapes.git] / source / main.cc
blob66e7b789737607628e33ed2a17fb28f6d1d36f90
1 /* This file is part of Shapes.
3 * Shapes is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * any later version.
8 * Shapes is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with Shapes. If not, see <http://www.gnu.org/licenses/>.
16 * Copyright 2008 Henrik Tidefelt
19 #include "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 };
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, "--tp=", & optionPrefix, & optionSuffix ) )
348 Kernel::allowTransparency = strtobool( optionSuffix, *argv );
349 argv += 1;
350 argc -= 1;
352 else if( strprefixcmp( *argv, "--spot-pair=", & optionPrefix, & optionSuffix ) )
354 Kernel::allowSingletonPaths = ! strtobool( optionSuffix, *argv );
355 argv += 1;
356 argc -= 1;
358 else if( strprefixcmp( *argv, "--unit=", & optionPrefix, & optionSuffix ) )
360 Interaction::displayUnitName = optionSuffix;
362 argv += 1;
363 argc -= 1;
365 else if( strprefixcmp( *argv, "--splicingtol=", & optionPrefix, & optionSuffix ) )
369 Computation::theTrixelizeSplicingTol = Ast::theShapesScanner.strtoLength( optionSuffix );
371 catch( ... )
373 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
374 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
376 if( Computation::theTrixelizeSplicingTol <= 0 )
378 std::cerr << "Argument to " << optionPrefix << " not positive: " << Computation::theTrixelizeSplicingTol.offtype< 1, 0 >( ) << std::endl ;
379 exit( Interaction::EXIT_INVOCATION_ERROR );
382 argv += 1;
383 argc -= 1;
385 else if( strprefixcmp( *argv, "--overlaptol=", & optionPrefix, & optionSuffix ) )
389 Computation::theTrixelizeOverlapTol = Ast::theShapesScanner.strtoLength( optionSuffix );
391 catch( ... )
393 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
394 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
396 if( Computation::theTrixelizeOverlapTol <= 0 )
398 std::cerr << "Argument to " << optionPrefix << " not positive: " << Computation::theTrixelizeOverlapTol.offtype< 1, 0 >( ) << std::endl ;
399 exit( Interaction::EXIT_INVOCATION_ERROR );
402 argv += 1;
403 argc -= 1;
405 else if( strcmp( *argv, "--needpath" ) == 0 ||
406 strncmp( *argv, "-N", 2 ) == 0 )
408 bool longForm = strncmp( *argv, "--", 2 ) == 0;
410 const char * pth = 0;
411 if( longForm )
413 argcAssertion( *argv, argc, 2 );
414 pth = *( argv + 1 );
416 else
418 pth = (*argv) + 2;
421 if( strchr( pth, ':' ) != 0 )
423 const char * flag = 0;
424 const char * shortFlag = "-N";
425 if( longForm )
427 flag = *argv;
429 else
431 flag = shortFlag;
434 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
435 if( longForm )
437 std::cerr << " (or " << shortFlag << ")" ;
439 std::cerr <<"." << std::endl ;
440 exit( Interaction::EXIT_INVOCATION_ERROR );
443 Ast::theShapesScanner.push_backNeedPath( absoluteDirectory( pth ) );
445 if( longForm )
447 argv += 2;
448 argc -= 2;
450 else
452 argv += 1;
453 argc -= 1;
456 else if( strcmp( *argv, "--fontmetricspath" ) == 0 ||
457 strncmp( *argv, "-M", 2 ) == 0 )
459 bool longForm = strncmp( *argv, "--", 2 ) == 0;
461 const char * pth = 0;
462 if( longForm )
464 argcAssertion( *argv, argc, 2 );
465 pth = *( argv + 1 );
467 else
469 pth = (*argv) + 2;
472 if( strchr( pth, ':' ) != 0 )
474 const char * flag = 0;
475 const char * shortFlag = "-M";
476 if( longForm )
478 flag = *argv;
480 else
482 flag = shortFlag;
485 std::cerr << "The path separator ':' is not allowed in the " << flag << " argument. Consider repeating " << flag ;
486 if( longForm )
488 std::cerr << " (or " << shortFlag << ")" ;
490 std::cerr <<"." << std::endl ;
491 exit( Interaction::EXIT_INVOCATION_ERROR );
494 Lang::Font::push_backFontMetricsPath( absoluteDirectory( pth ) );
496 if( longForm )
498 argv += 2;
499 argc -= 2;
501 else
503 argv += 1;
504 argc -= 1;
507 else if( strprefixcmp( *argv, "--seed=", & optionPrefix, & optionSuffix ) )
509 char * endp;
510 long s = strtol( optionSuffix, &endp, 10 );
511 if( *endp != '\0' )
513 std::cerr << "Argument to " << optionPrefix << " was not an integer: " << optionSuffix << std::endl ;
514 exit( Interaction::EXIT_INVOCATION_ERROR );
517 srand( s );
519 argv += 1;
520 argc -= 1;
522 else if( strprefixcmp( *argv, "--arcdelta=", & optionPrefix, & optionSuffix ) )
526 Computation::the_arcdelta = Ast::theShapesScanner.strtoLength( optionSuffix );
528 catch( ... )
530 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
531 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
533 if( Computation::the_arcdelta <= 0 )
535 std::cerr << "Argument to " << optionPrefix << " not positive: " << optionSuffix << std::endl ;
536 exit( Interaction::EXIT_INVOCATION_ERROR );
539 argv += 1;
540 argc -= 1;
542 else if( strprefixcmp( *argv, "--dtmin=", & optionPrefix, & optionSuffix ) )
544 char * endp;
545 Computation::the_dtMin = strtod( optionSuffix, &endp );
546 if( *endp != '\0' )
548 std::cerr << "Argument to " << optionPrefix << " was not a float: " << optionSuffix << std::endl ;
549 exit( Interaction::EXIT_INVOCATION_ERROR );
551 if( Computation::the_dtMin <= 0 )
553 std::cerr << "Argument to " << optionPrefix << " not positive: " << Computation::the_dtMin << std::endl ;
554 exit( Interaction::EXIT_INVOCATION_ERROR );
557 argv += 1;
558 argc -= 1;
560 else if( strprefixcmp( *argv, "--disttol=", & optionPrefix, & optionSuffix ) )
564 Computation::theDistanceTol = Ast::theShapesScanner.strtoLength( optionSuffix );
566 catch( ... )
568 std::cerr << "Argument to " << optionPrefix << " was not recognized as a length: " << optionSuffix << std::endl ;
569 abortProcedure( Interaction::EXIT_INVOCATION_ERROR );
571 if( Computation::theDistanceTol <= 0 )
573 std::cerr << "Argument to " << optionPrefix << " not positive: " << optionSuffix << std::endl ;
574 exit( Interaction::EXIT_INVOCATION_ERROR );
577 argv += 1;
578 argc -= 1;
580 else if( strcmp( *argv, "--prepend" ) == 0 )
582 argcAssertion( *argv, argc, 2 );
583 prependStreamOut << *( argv + 1 ) << std::endl ;
584 argv += 2;
585 argc -= 2;
587 else if( strcmp( *argv, "--base" ) == 0 )
589 argcAssertion( *argv, argc, 2 );
590 if( baseName != "" )
592 std::cerr << "The name base is multiply specified." << std::endl ;
593 exit( Interaction::EXIT_INVOCATION_ERROR );
595 baseName = *( argv + 1 );
596 argv += 2;
597 argc -= 2;
599 else if( strcmp( *argv, "--which" ) == 0 )
601 argcAssertion( *argv, argc, 2 );
602 filenameRequestList.push_back( FILENAME_RESOURCE );
603 resourceRequestList.push_back( *( argv + 1 ) );
604 argv += 2;
605 argc -= 2;
607 else if( strcmp( *argv, "--in" ) == 0 )
609 argcAssertion( *argv, argc, 2 );
610 if( inputName != "" )
612 std::cerr << "The input file is multiply specified." << std::endl ;
613 exit( Interaction::EXIT_INVOCATION_ERROR );
615 inputName = absoluteFilename( *( argv + 1 ) );
616 argv += 2;
617 argc -= 2;
619 else if( strcmp( *argv, "--which-in" ) == 0 )
621 filenameRequestList.push_back( FILENAME_IN );
622 argv += 1;
623 argc -= 1;
625 else if( strcmp( *argv, "--out" ) == 0 )
627 argcAssertion( *argv, argc, 2 );
628 if( outputName != "" )
630 std::cerr << "The output file is multiply specified." << std::endl ;
631 exit( Interaction::EXIT_INVOCATION_ERROR );
633 outputName = absoluteFilename( *( argv + 1 ) );
634 argv += 2;
635 argc -= 2;
637 else if( strcmp( *argv, "--which-out" ) == 0 )
639 filenameRequestList.push_back( FILENAME_OUT );
640 argv += 1;
641 argc -= 1;
643 else if( strcmp( *argv, "--texjob" ) == 0 )
645 argcAssertion( *argv, argc, 2 );
646 if( texJobName != "" )
648 std::cerr << "The tex job name is multiply specified." << std::endl ;
649 exit( Interaction::EXIT_INVOCATION_ERROR );
651 texJobName = *( argv + 1 );
652 if( texJobName.find( '/' ) != std::string::npos )
654 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 ;
655 exit( Interaction::EXIT_INVOCATION_ERROR );
657 argv += 2;
658 argc -= 2;
660 else if( strcmp( *argv, "--which-texjob" ) == 0 )
662 filenameRequestList.push_back( FILENAME_TEXJOB );
663 argv += 1;
664 argc -= 1;
666 else if( strcmp( *argv, "--labeldb" ) == 0 )
668 argcAssertion( *argv, argc, 2 );
669 if( labelDBName != "" )
671 std::cerr << "The label database file is multiply specified." << std::endl ;
672 exit( Interaction::EXIT_INVOCATION_ERROR );
674 labelDBName = absoluteFilename( *( argv + 1 ) );
675 argv += 2;
676 argc -= 2;
678 else if( strcmp( *argv, "--which-labeldb" ) == 0 )
680 filenameRequestList.push_back( FILENAME_LABELDB );
681 argv += 1;
682 argc -= 1;
684 else if( strcmp( *argv, "--afmout" ) == 0 )
686 argcAssertion( *argv, argc, 2 );
687 if( fontmetricsOutputName != "" )
689 std::cerr << "The font metrics output name is multiply specified." << std::endl ;
690 exit( Interaction::EXIT_INVOCATION_ERROR );
692 fontmetricsOutputName = absoluteFilename( *( argv + 1 ) );
693 argv += 2;
694 argc -= 2;
696 else if( strcmp( *argv, "--which-afmout" ) == 0 )
698 filenameRequestList.push_back( FILENAME_AFM );
699 argv += 1;
700 argc -= 1;
702 else if( strcmp( *argv, "--which-TEXINPUTS" ) == 0 )
704 filenameRequestList.push_back( FILENAME_TEXINPUTS );
705 argv += 1;
706 argc -= 1;
708 else if( strcmp( *argv, "--which-doc" ) == 0 )
710 filenameRequestList.push_back( FILENAME_HTMLDOC );
711 argv += 1;
712 argc -= 1;
714 else if( strcmp( *argv, "--outdir" ) == 0 )
716 argcAssertion( *argv, argc, 2 );
717 if( outDir != "" )
719 std::cerr << "The output directory is multiply specified." << std::endl ;
720 exit( Interaction::EXIT_INVOCATION_ERROR );
722 outDir = absoluteDirectory( *( argv + 1 ) );
723 argv += 2;
724 argc -= 2;
726 else if( strprefixcmp( *argv, "--tmp*=", & optionPrefix, & optionSuffix ) )
728 allowCreateTmpDir = strtobool( optionSuffix, *argv );
729 argv += 1;
730 argc -= 1;
732 else if( strcmp( *argv, "--tmpdir" ) == 0 )
734 argcAssertion( *argv, argc, 2 );
735 if( tmpDir != "" )
737 std::cerr << "The temporaries directory is multiply specified." << std::endl ;
738 exit( Interaction::EXIT_INVOCATION_ERROR );
740 tmpDir = absoluteDirectory( *( argv + 1 ) );
741 argv += 2;
742 argc -= 2;
744 else if( strcmp( *argv, "--which-tmp" ) == 0 )
746 filenameRequestList.push_back( FILENAME_TMP );
747 argv += 1;
748 argc -= 1;
750 else if( strprefixcmp( *argv, "--split=", & optionPrefix, & optionSuffix ) )
752 if( strcmp( optionSuffix, "no" ) == 0 )
754 splitMode = SPLIT_NO;
756 else if( strcmp( optionSuffix, "flat" ) == 0 )
758 splitMode = SPLIT_FLAT;
760 else if( strcmp( optionSuffix, "dir" ) == 0 )
762 splitMode = SPLIT_DIR;
764 else
766 std::cerr << "The string \"" << optionSuffix << "\" in the command line argument \"" << *argv << "\" was not any of { 'no', 'flat', 'dir' }." << std::endl ;
768 argv += 1;
769 argc -= 1;
771 else if( strcmp( *argv, "--xpdf" ) == 0 )
773 previewOptions.launch_xpdf = true;
774 argv += 1;
775 argc -= 1;
777 else if( strcmp( *argv, "--xpdf-remote" ) == 0 )
779 argcAssertion( *argv, argc, 2 );
780 if( previewOptions.xpdfServer != "" )
782 std::cerr << "The xpdf server is multiply specified." << std::endl ;
783 exit( Interaction::EXIT_INVOCATION_ERROR );
785 previewOptions.xpdfServer = *( argv + 1 );
786 argv += 2;
787 argc -= 2;
789 else if( strcmp( *argv, "--xpdf-no-server" ) == 0 )
791 if( previewOptions.xpdfAction != Interaction::PreviewOptions::XPDF_DEFAULT )
793 std::cerr << "The xpdf action is multiply specified." << std::endl ;
794 exit( Interaction::EXIT_INVOCATION_ERROR );
796 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_NOSERVER;
797 argv += 1;
798 argc -= 1;
800 else if( strcmp( *argv, "--xpdf-reload" ) == 0 )
802 if( previewOptions.xpdfAction != Interaction::PreviewOptions::XPDF_DEFAULT )
804 std::cerr << "The xpdf action is multiply specified." << std::endl ;
805 exit( Interaction::EXIT_INVOCATION_ERROR );
807 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_RELOAD;
808 argv += 1;
809 argc -= 1;
811 else if( strcmp( *argv, "--xpdf-quit" ) == 0 )
813 if( previewOptions.xpdfAction != Interaction::PreviewOptions::XPDF_DEFAULT )
815 std::cerr << "The xpdf action is multiply specified." << std::endl ;
816 exit( Interaction::EXIT_INVOCATION_ERROR );
818 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_QUIT;
819 argv += 1;
820 argc -= 1;
822 else if( strcmp( *argv, "--open" ) == 0 )
824 previewOptions.do_open = true;
825 argv += 1;
826 argc -= 1;
828 else if( strcmp( *argv, "--open-a" ) == 0 )
830 argcAssertion( *argv, argc, 2 );
831 previewOptions.do_open = true;
832 previewOptions.do_open_application = *( argv + 1 );
833 argv += 2;
834 argc -= 2;
836 else if( strcmp( *argv, "--version" ) == 0 )
838 printVersion( );
839 exit( Interaction::EXIT_OK );
841 else if( argc == 1 )
843 if( baseName != "" )
845 std::cerr << "The name base is multiply specified." << std::endl ;
846 exit( Interaction::EXIT_INVOCATION_ERROR );
848 struct stat theStat;
849 if( stat( *argv, & theStat ) == 0 &&
850 ( theStat.st_mode & S_IFDIR ) == 0 ) /* We are not interested in directories here. */
852 inputName = *argv;
853 char * ext = *argv + strlen( *argv ) - 6;
854 if( ext <= *argv )
856 std::cerr << "The file name \"" << *argv << "\" is unexpectedly short (it should include the \".shape\" suffix)." << std::endl ;
857 exit( Interaction::EXIT_INVOCATION_ERROR );
859 if( strcmp( ext, ".shape" ) != 0 )
861 std::cerr << "Expected \".shape\" suffix in the file name \"" << *argv << "\"." << std::endl ;
862 exit( Interaction::EXIT_INVOCATION_ERROR );
865 else
867 if( (*argv)[ strlen( *argv ) - 1 ] == '.' )
869 inputName = std::string( *argv ) + "shape";
871 else
873 inputName = std::string( *argv ) + ".shape";
875 if( ! stat( inputName.c_str( ), & theStat ) == 0 )
877 /* It is not entirely clear what is the best error message here,
878 * as the source file may be specified in several different ways.
879 * This should cause the least confusion.
881 std::cerr << "Failed to locate input file: " << *argv << std::endl ;
882 exit( Interaction::EXIT_INPUT_FILE_ERROR );
886 const char * slash = strrchr( inputName.c_str( ), '/' );
887 if( slash == 0 )
889 slash = inputName.c_str( );
891 else
893 ++slash;
895 size_t skipCount = slash - inputName.c_str( );
896 baseName = inputName.substr( skipCount, inputName.length( ) - skipCount - 6 );
898 argv += 1;
899 argc -= 1;
901 else
903 std::cerr << "Illegal command line option: " << *argv << std::endl ;
904 exit( Interaction::EXIT_INVOCATION_ERROR );
908 if( tmpDir == "" )
910 char * start = getenv( "SHAPESTMPDIR" );
911 if( start != 0 )
913 tmpDir = absoluteDirectory( start );
915 else
917 tmpDir = absoluteDirectory( "" );
920 if( outDir == "" )
922 if( interactiveMode )
924 outDir = tmpDir;
926 else
928 outDir = absoluteDirectory( "" );
931 ensureTmpDirectoryExists( tmpDir, allowCreateTmpDir );
933 if( baseName == "" )
935 if( interactiveMode )
937 if( outputName == "" )
939 if( splitMode == SPLIT_NO )
941 outputName = outDir + "#shapes" + ".pdf";
943 else
945 outputName = outDir + "#shapes" ;
949 if( texJobName == "" )
951 texJobName = "#shapes.labels";
954 else
956 if( inputName == "" )
958 inputName = absoluteFilename( ( baseName + ".shape" ).c_str( ) );
960 if( outputName == "" )
962 if( splitMode == SPLIT_NO )
964 outputName = outDir + baseName + ".pdf";
966 else
968 outputName = outDir + baseName;
971 if( texJobName == "" )
973 texJobName = baseName + ".labels";
975 if( labelDBName == "" )
977 labelDBName = outDir + baseName + ".labels.pdf";
979 if( fontmetricsOutputName == "" )
981 fontmetricsOutputName = outDir + baseName + ".afm";
985 if( outputName == "" )
987 if( ! filenameRequestList.empty( ) )
989 /* The output name will never really be used, so it's rather harmless to assign a dummy value.
991 outputName = "?.pdf" ;
993 else
995 std::cerr << "The output file is undetermined. Consider specifying it using \"--out <filename>\"." << std::endl ;
996 exit( Interaction::EXIT_INVOCATION_ERROR );
1000 if( labelDBName == "" )
1002 iterativeMode = false;
1005 if( ! Kernel::theDebugLog.initialized( ) )
1007 std::string::size_type suffixSep = outputName.rfind( '.' );
1008 if( suffixSep != std::string::npos && outputName.find( '/', suffixSep ) == std::string::npos )
1010 /* If there would have been a slash after the '.', the dot would have been part of a directory name.
1011 * Otherwise, we conclude that we have found the extension of a filename, and replace that extension
1012 * by ".log".
1014 Kernel::theDebugLog.setFilename( outputName.substr( 0, suffixSep ) + ".log" );
1016 else if( baseName != "" )
1018 Kernel::theDebugLog.setFilename( outDir + baseName + ".log" );
1020 else
1022 Kernel::theDebugLog.setFilename( outDir + "#shapes.log" );
1027 std::string inDir;
1028 std::string inPath = inputName;
1029 std::string::size_type slash = inPath.rfind( '/' );
1030 if( slash == std::string::npos )
1032 inDir = absoluteDirectory( "" );
1034 else
1036 inDir = absoluteDirectory( inPath.substr( 0, slash ).c_str( ) );
1038 Ast::theShapesScanner.setSourceDir( inDir );
1039 Kernel::theTeXLabelManager.setup( inDir, tmpDir, texJobName );
1042 if( Computation::theTrixelizeSplicingTol >= Computation::theTrixelizeOverlapTol )
1044 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 ;
1045 exit( Interaction::EXIT_INVOCATION_ERROR );
1048 if( previewOptions.xpdfServer == "" )
1050 previewOptions.xpdfServer = outputName;
1053 if( previewOptions.xpdfAction == Interaction::PreviewOptions::XPDF_DEFAULT )
1055 previewOptions.xpdfAction = Interaction::PreviewOptions::XPDF_RAISE;
1058 addDefaultNeedPath( );
1059 addDefaultFontMetricsPath( );
1061 #ifdef RESOURCES_DIR
1062 if( useResources )
1064 Ast::theShapesScanner.push_backNeedPath( std::string( RESOURCES_DIR ) + "/extensions/" );
1065 Lang::Font::push_backFontMetricsPath( std::string( RESOURCES_DIR ) + "/fontmetrics/" );
1067 #endif
1069 if( ! filenameRequestList.empty( ) )
1071 std::list< const char * >::const_iterator resource = resourceRequestList.begin( );
1072 for( std::list< int >::const_iterator i = filenameRequestList.begin( );
1073 i != filenameRequestList.end( );
1074 ++i )
1076 switch( *i )
1078 case FILENAME_IN:
1079 if( inputName == "" )
1081 std::cout << "<stdin>" ;
1083 else
1085 std::cout << inputName ;
1087 break;
1088 case FILENAME_OUT:
1089 std::cout << outputName ;
1090 break;
1091 case FILENAME_TMP:
1092 std::cout << tmpDir ;
1093 break;
1094 case FILENAME_TEXJOB:
1095 std::cout << tmpDir << texJobName ;
1096 break;
1097 case FILENAME_LABELDB:
1098 std::cout << labelDBName ;
1099 break;
1100 case FILENAME_AFM:
1101 std::cout << fontmetricsOutputName ;
1102 break;
1103 case FILENAME_TEXINPUTS:
1105 std::cout << getenv( "TEXINPUTS" ) ;
1107 break;
1108 case FILENAME_HTMLDOC:
1110 std::cout << HTMLDIR << "/index.html" ;
1112 break;
1113 case FILENAME_RESOURCE:
1117 std::cout << Ast::theShapesScanner.searchFile( *resource ) ;
1119 catch( const Exceptions::Exception & ball )
1121 std::cout.flush( );
1122 ball.display( std::cerr );
1123 exit( Interaction::EXIT_INVOCATION_ERROR );
1125 ++resource;
1127 break;
1128 default:
1129 std::cerr << "Internal error: filename request switch in main out of range." << std::endl ;
1130 exit( Interaction::EXIT_INTERNAL_ERROR );
1132 std::cout << std::endl ;
1134 exit( Interaction::EXIT_OK );
1137 if( pdfVersion == SimplePDF::PDF_Version::VERSION_UNDEFINED )
1139 pdfVersion = SimplePDF::PDF_Version::PDF_1_4;
1142 Kernel::the_PDF_version.setVersion( pdfVersion );
1143 Kernel::the_PDF_version.setAction( pdfVersionAction );
1146 std::ostringstream oss;
1147 time_t tmp;
1148 time( &tmp );
1149 struct tm * now( gmtime( &tmp ) );
1150 oss << "D:"
1151 << std::setfill( '0' )
1152 << std::setw(4) << 1900 + now->tm_year
1153 << std::setw(2) << 1 + now->tm_mon
1154 << std::setw(2) << now->tm_mday
1155 << std::setw(2) << now->tm_hour
1156 << std::setw(2) << now->tm_min
1157 << std::setw(2) << now->tm_sec
1158 << "+0000" ;
1159 Kernel::theDocInfo.addInfo( "CreationDate", SimplePDF::newString( oss.str( ).c_str( ) ) );
1162 std::istringstream prependStreamIn;
1163 if( ! prependStreamOut.str( ).empty( ) )
1165 prependStreamIn.str( prependStreamOut.str( ) );
1166 Ast::theShapesScanner.queueStream( & prependStreamIn, Ast::FileID::build_special( "<--prepend>" ) );
1169 std::ifstream iFile;
1170 std::istream * iFilePtr = 0;
1171 if( inputName == "" )
1173 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( "<stdin>" ) );
1174 iFilePtr = & std::cin;
1175 if( interactiveMode )
1177 /* On some platforms, the lexer crashes if it is not given a new file at EOF,
1178 * so we need to put an empty file after the prelude to avoid that.
1180 * Note that we use the iFile object here, although this will not be pointed to
1181 * by iFilePtr, which points to std::cin instead.
1183 const char * nullName = "/dev/null";
1184 iFile.open( nullName );
1185 if( ! iFile.good( ) || ! iFile.is_open( ) )
1187 std::cerr << "Failed to open " << nullName << " to create empty input file." << std::endl ;
1188 exit( Interaction::EXIT_EXTERNAL_ERROR );
1190 Ast::theShapesScanner.queueStream( & iFile, Ast::FileID::build_special( nullName ) );
1192 else
1194 Ast::theShapesScanner.queueStream( iFilePtr, Ast::FileID::build_special( "<stdin>" ) );
1195 // Ast::theShapesScanner.setNameOf_yyin( "<stdin>" );
1198 else
1200 Kernel::theDocInfo.addInfo( "Title", SimplePDF::newString( inputName.c_str( ) ) );
1201 iFile.open( inputName.c_str( ) );
1202 if( ! iFile.good( ) || ! iFile.is_open( ) )
1204 std::cerr << "Failed to open " << inputName << " for input." << std::endl ;
1205 exit( Interaction::EXIT_INPUT_FILE_ERROR );
1207 iFilePtr = & iFile;
1208 if( ! interactiveMode )
1210 struct stat theStat;
1211 if( stat( inputName.c_str( ), & theStat ) != 0 )
1213 std::cerr << "Failed to stat " << inputName << " (but it could be opened for input...)." << std::endl ;
1214 exit( Interaction::EXIT_INPUT_FILE_ERROR );
1216 Ast::theShapesScanner.queueStream( iFilePtr, Ast::FileID::build_stat( theStat, inputName ) );
1220 /* This will initiate the reading of preambles, and when there are no more preambles, we turn to the queue set up by calling queueStream. */
1221 Ast::theShapesScanner.start( );
1223 RefCountPtr< std::ifstream > labelDBFile = RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1224 Kernel::WarmCatalog::ShipoutList documents;
1226 /* This is where all the evaluation takes place. It is written as a separate functions to keep the function main short.
1230 if( interactiveMode )
1232 interactiveEvaluation( *iFilePtr, std::cout, outputName, splitMode,
1233 evalTrace, evalBackTrace, cleanupMemory,
1234 labelDBFile, labelDBName );
1236 else
1238 nonInteractiveEvaluation( documents, splitMode,
1239 evalTrace, evalBackTrace, cleanupMemory,
1240 labelDBFile, labelDBName );
1243 catch( const NonLocalExit::DynamicBindingNotFound & ball )
1245 std::cerr << "Caught DynamicBindingNotFound at top level." << std::endl
1246 << "This should really not be possible; it is an internal error." << std::endl ;
1247 exit( Interaction::EXIT_INTERNAL_ERROR );
1249 catch( const NonLocalExit::NotThisType & ball )
1251 std::cerr << "Caught NotThisType at top level." << std::endl
1252 << "This should really not be possible; it is an internal error." << std::endl ;
1253 exit( Interaction::EXIT_INTERNAL_ERROR );
1255 catch( const NonLocalExit::NonLocalExitBase & ball )
1257 std::cerr << "Caught an unknown descendant to NonLocalExitBase at top level." << std::endl
1258 << "This should really not be possible; it is an internal error." << std::endl ;
1259 exit( Interaction::EXIT_INTERNAL_ERROR );
1261 catch( const char * ball )
1263 std::cerr << "Caught (char*) ball at top level:" << std::endl
1264 << " " << ball << std::endl ;
1265 exit( Interaction::EXIT_GENERIC_ERROR );
1267 catch( const std::string & ball )
1269 std::cerr << "Caught (string) ball at top level:" << std::endl
1270 << " " << ball << std::endl ;
1271 exit( Interaction::EXIT_GENERIC_ERROR );
1273 catch( ... )
1275 std::cerr << "Caught (...) ball at top level." << std::endl ;
1276 exit( Interaction::EXIT_GENERIC_ERROR );
1279 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1281 abortProcedure( Interaction::EXIT_USER_ERROR );
1284 if( memoryStats )
1286 std::cerr << "Summary:" << std::endl ;
1287 std::cerr << "Environments: alive: " << Kernel::Environment::liveCount << " of total: " << Kernel::Environment::createdCount
1288 << " (" << 100 * static_cast< double >( Kernel::Environment::liveCount ) / static_cast< double >( Kernel::Environment::createdCount ) << "%)" << std::endl ;
1291 switch( splitMode )
1293 case SPLIT_NO:
1295 if( documents.size( ) != 1 )
1297 std::cerr << "Internal error: Failed to produce exactly one document of output although --split=no." << std::endl ;
1299 std::ofstream oFile;
1300 noSplitOpen( & oFile, outputName );
1301 documents.front( ).writeFile( oFile, Kernel::the_PDF_version );
1303 break;
1304 case SPLIT_FLAT:
1306 rmSplitFiles( outputName, "-" );
1307 size_t physicalPageNo = 1;
1308 for( Kernel::WarmCatalog::ShipoutList::iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1310 std::ofstream oFile;
1311 splitOpen( & oFile, outputName, "-", physicalPageNo );
1312 i->writeFile( oFile, Kernel::the_PDF_version );
1315 break;
1316 case SPLIT_DIR:
1318 mkSplitDir( outputName );
1319 rmSplitFiles( outputName, "/" );
1320 size_t physicalPageNo = 1;
1321 for( Kernel::WarmCatalog::ShipoutList::iterator i = documents.begin( ); i != documents.end( ); ++i, ++physicalPageNo )
1323 std::ofstream oFile;
1324 splitOpen( & oFile, outputName, "/", physicalPageNo );
1325 i->writeFile( oFile, Kernel::the_PDF_version );
1328 break;
1331 /* This must be done after the output has been written, and before iterativeFinish writes to the labels database file.
1333 Kernel::thePDFImporter.free( );
1335 if( labelDBFile != NullPtr< std::ifstream >( ) )
1337 if( labelDBFile->is_open( ) )
1339 labelDBFile->close( );
1341 labelDBFile = NullPtr< std::ifstream >( ); // Free the reference.
1344 if( iterativeMode )
1346 Kernel::theTeXLabelManager.iterativeFinish( labelDBName );
1349 previewOptions.preview( outputName, splitMode != SPLIT_NO );
1351 destroyGlobals( );
1353 return 0;
1357 void
1358 argcAssertion( const char * optionSpecifier, int argc, int argcMin )
1360 if( argc < argcMin )
1362 std::cerr << "The command line option " << optionSpecifier << " requires " << argcMin - 1 << " parameters." << std::endl ;
1363 exit( Interaction::EXIT_INVOCATION_ERROR );
1367 bool
1368 strprefixcmp( char * str, const char * prefix, const char ** prefixDst, char ** endp )
1370 *prefixDst = prefix;
1371 int len = strlen( prefix );
1372 bool res = ( strncmp( str, prefix, len ) == 0 );
1373 *endp = str + len;
1374 return res;
1377 bool
1378 strtobool( const char * str, const char * containingString, const char * trueLabel, const char * falseLabel )
1380 if( trueLabel != 0 &&
1381 strcmp( str, trueLabel ) == 0 )
1383 return true;
1385 if( falseLabel != 0 &&
1386 strcmp( str, falseLabel ) == 0 )
1388 return false;
1390 if( strcmp( str, "yes" ) == 0 ||
1391 strcmp( str, "true" ) == 0 ||
1392 strcmp( str, "on" ) == 0 )
1394 return true;
1396 if( strcmp( str, "no" ) == 0 ||
1397 strcmp( str, "false" ) == 0 ||
1398 strcmp( str, "off" ) == 0)
1400 return false;
1402 std::cerr << "The string \"" << str << "\" in the command line argument \"" << containingString << "\" was not recognized as a boolean value." << std::endl ;
1403 exit( Interaction::EXIT_INVOCATION_ERROR );
1407 RefCountPtr< std::ifstream >
1408 performIterativeStartup( const std::string & labelDBName )
1411 struct stat theStat;
1412 if( stat( labelDBName.c_str( ), & theStat ) != 0 )
1414 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1417 // {
1418 // ostringstream mvCommand;
1419 // mvCommand << "cp '" << oldFilename.str( ) << "' '" << labelDBName.str( ) << "'" ;
1420 // Interaction::systemDebugMessage( mvCommand.str( ) );
1421 // if( system( mvCommand.str( ).c_str( ) ) != 0 )
1422 // {
1423 // return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1424 // }
1425 // }
1426 RefCountPtr< std::ifstream > labelsFile( new std::ifstream( labelDBName.c_str( ) ) );
1427 if( ! labelsFile->good( ) )
1429 return RefCountPtr< std::ifstream >( NullPtr< std::ifstream >( ) );
1433 Kernel::theTeXLabelManager.iterativeStartup( labelsFile );
1434 return labelsFile;
1436 catch( const char * ball )
1438 std::cerr << "Caught (char*) ball from iterative startup:" << std::endl
1439 << " " << ball << std::endl ;
1440 exit( Interaction::EXIT_GENERIC_ERROR );
1442 catch( const std::string & ball )
1444 std::cerr << "Caught (string) ball from iterative startup:" << std::endl
1445 << " " << ball << std::endl ;
1446 exit( Interaction::EXIT_GENERIC_ERROR );
1448 catch( const Exceptions::Exception & ball )
1450 ball.display( std::cerr );
1451 exit( ball.exitCode( ) );
1453 catch( ... )
1455 std::cerr << "Caught (...) ball from iterative startup." << std::endl ;
1456 exit( Interaction::EXIT_GENERIC_ERROR );
1461 void
1462 abortProcedure( int exitCode )
1464 if( ! Kernel::thePostCheckErrorsList.empty( ) )
1466 std::cout.flush( );
1467 while( ! Kernel::thePostCheckErrorsList.empty( ) )
1469 Exceptions::Exception * e = Kernel::thePostCheckErrorsList.front( );
1470 Kernel::thePostCheckErrorsList.pop_front( );
1472 typedef const Exceptions::PostCondition ErrorType;
1473 ErrorType * err = dynamic_cast< ErrorType * >( e );
1474 if( err != 0 )
1476 std::cerr << err->loc( ) << ": " ;
1477 err->display( std::cerr );
1478 continue;
1482 typedef const Exceptions::RuntimeError ErrorType;
1483 ErrorType * err = dynamic_cast< ErrorType * >( e );
1484 if( err != 0 )
1486 std::cerr << err->getLoc( ) << " (runtime): " ;
1487 err->display( std::cerr );
1488 continue;
1491 std::cerr << "(Bad post-exception type)" << ": " ;
1492 e->display( std::cerr );
1495 std::cerr << "Aborting job. Output files are left unchanged." << std::endl ;
1496 exit( exitCode );
1499 namespace Shapes
1501 namespace Helpers
1503 void setSelfRef( RefCountPtr< const Lang::Class > cls )
1505 cls->setSelfRef( cls );
1507 void initClass( RefCountPtr< const Lang::Class > cls )
1509 Lang::SystemFinalClass * typedPtr = dynamic_cast< Lang::SystemFinalClass * >( const_cast< Lang::Class * >( cls.getPtr( ) ) );
1510 if( typedPtr != 0 )
1512 typedPtr->init( );
1514 else
1516 std::cerr << "Helpers::initMutators was called with object of bad type." << std::endl ;
1517 exit( Interaction::EXIT_INTERNAL_ERROR );
1523 void
1524 setupGlobals( )
1526 Lang::ElementaryPath2D * bbox = new Lang::ElementaryPath2D;
1527 bbox->push_back( new Concrete::PathPoint2D( 0, 0 ) );
1528 bbox->close( );
1529 Lang::THE_POINTPICTURE = RefCountPtr< Lang::Drawable2D >( new Lang::BBoxed2D( Lang::THE_NULL2D,
1530 RefCountPtr< Lang::ElementaryPath2D >( bbox ) ) );
1531 Helpers::setSelfRef( Lang::THE_OBJECT );
1533 Helpers::setSelfRef( Lang::Void::TypeID );
1534 Helpers::setSelfRef( Lang::Symbol::TypeID );
1535 Helpers::setSelfRef( Lang::Float::TypeID );
1536 Helpers::setSelfRef( Lang::Length::TypeID );
1537 Helpers::setSelfRef( Lang::Integer::TypeID );
1538 Helpers::setSelfRef( Lang::Boolean::TypeID );
1539 Helpers::setSelfRef( Lang::String::TypeID );
1540 Helpers::setSelfRef( Lang::FloatPair::TypeID );
1541 Helpers::setSelfRef( Lang::FloatTriple::TypeID );
1542 Helpers::setSelfRef( Lang::Coords2D::TypeID );
1543 Helpers::setSelfRef( Lang::CornerCoords2D::TypeID );
1544 Helpers::setSelfRef( Lang::Coords3D::TypeID );
1546 Helpers::setSelfRef( Lang::Function::TypeID );
1547 Helpers::setSelfRef( Lang::Transform2D::TypeID );
1548 Helpers::setSelfRef( Lang::Transform3D::TypeID );
1550 Helpers::setSelfRef( Lang::Class::TypeID );
1552 Helpers::setSelfRef( Lang::TransformedInstance::TypeID );
1554 Helpers::initClass( Kernel::WarmGroup2D::TypeID );
1555 Helpers::initClass( Kernel::WarmGroup3D::TypeID );
1556 Helpers::initClass( Kernel::WarmCatalog::TypeID );
1557 Helpers::initClass( Kernel::WarmDebugger::TypeID );
1558 Helpers::initClass( Lang::SingleList::TypeID );
1559 Helpers::initClass( Lang::Transform2D::TypeID );
1560 Helpers::initClass( Lang::Transform3D::TypeID );
1563 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric2D" ) );
1564 Lang::Geometric2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1565 Helpers::setSelfRef( Lang::Geometric2D::TypeID );
1566 /* Note that addVirtual must not be called before the selfRef is set!
1570 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Geometric3D" ) );
1571 Lang::Geometric3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1572 Helpers::setSelfRef( Lang::Geometric3D::TypeID );
1573 /* Note that addVirtual must not be called before the selfRef is set!
1577 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable2D" ) );
1578 Lang::Drawable2D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1579 Helpers::setSelfRef( Lang::Drawable2D::TypeID );
1580 /* Note that addVirtual must not be called before the selfRef is set!
1582 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1585 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Drawable3D" ) );
1586 Lang::Drawable3D::TypeID = RefCountPtr< const Lang::Class >( tmp );
1587 Helpers::setSelfRef( Lang::Drawable3D::TypeID );
1588 /* Note that addVirtual must not be called before the selfRef is set!
1590 tmp->addVirtual( Lang::MESSAGE_DRAWABLE_DRAW_ID );
1593 Lang::SystemVirtualInterface * tmp = new Lang::SystemVirtualInterface( strrefdup( "Color" ) );
1594 Lang::Color::TypeID = RefCountPtr< const Lang::Class >( tmp );
1595 Helpers::setSelfRef( Lang::Color::TypeID );
1596 /* Note that addVirtual must not be called before the selfRef is set!
1598 tmp->addVirtual( "stroking" );
1599 tmp->addVirtual( "nonstroking" );
1603 void
1604 Interaction::PreviewOptions::preview( const std::string & filename, bool splitNoLaunch ) const
1606 if( launch_xpdf )
1608 if( splitNoLaunch )
1610 std::cerr << "Warning: Not launching viewer since the documet was split." << std::endl ;
1612 else
1614 xpdfHelper( filename );
1618 if( do_open )
1620 if( splitNoLaunch )
1622 std::cerr << "Warning: Not launching viewer since the documet was split." << std::endl ;
1624 else
1626 openHelper( filename );
1631 void
1632 Interaction::PreviewOptions::xpdfHelper( const std::string & filename ) const
1634 pid_t xpdfProcess = fork( );
1635 if( xpdfProcess == -1 )
1637 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running xpdf." ) );
1640 if( xpdfProcess == 0 ) /* This is the child */
1642 /* The exec call below never returns, so the child process never leaves this if clause.
1643 * Hence, there is no need to create a special else clasuse below.
1645 switch( xpdfAction )
1647 case XPDF_RAISE:
1648 execlp( "xpdf", "xpdf", "-remote", xpdfServer.c_str( ), "-raise", filename.c_str( ), static_cast< const char * >( 0 ) );
1649 break;
1650 case XPDF_RELOAD:
1651 execlp( "xpdf", "xpdf", "-remote", xpdfServer.c_str( ), "-reload", static_cast< const char * >( 0 ) );
1652 break;
1653 case XPDF_QUIT:
1654 execlp( "xpdf", "xpdf", "-remote", xpdfServer.c_str( ), "-quit", static_cast< const char * >( 0 ) );
1655 break;
1656 case XPDF_NOSERVER:
1657 execlp( "xpdf", "xpdf", filename.c_str( ), static_cast< const char * >( 0 ) );
1658 break;
1659 default:
1660 std::cerr << "Internal error: XpdfAction switch out of range." << std::endl ;
1661 exit( Interaction::EXIT_INTERNAL_ERROR );
1663 if( errno != 0 )
1665 std::cerr << "Recieved errno = " << errno << " from execlp call to xpdf." << std::endl ;
1666 exit( Interaction::EXIT_EXTERNAL_ERROR );
1668 std::cerr << "execlp call to xpdf returned with errno == 0." << std::endl ;
1669 exit( Interaction::EXIT_INTERNAL_ERROR );
1674 void
1675 Interaction::PreviewOptions::openHelper( const std::string & filename ) const
1677 pid_t openProcess = fork( );
1678 if( openProcess == -1 )
1680 throw Exceptions::InternalError( strrefdup( "Failed to fork a process for running open." ) );
1683 if( openProcess == 0 ) /* This is the child */
1685 /* The exec call below never returns, so the child process never leaves this if clause.
1686 * Hence, there is no need to create a special else clasuse below.
1688 if( do_open_application != 0 )
1690 execlp( "open", "open", "-a", do_open_application, filename.c_str( ), static_cast< const char * >( 0 ) );
1692 else
1694 execlp( "open", "open", filename.c_str( ), static_cast< const char * >( 0 ) );
1696 if( errno != 0 )
1698 std::cerr << "Recieved errno = " << errno << " from execlp call to open." << std::endl ;
1699 exit( Interaction::EXIT_EXTERNAL_ERROR );
1701 std::cerr << "execlp call to open returned with errno == 0." << std::endl ;
1702 exit( Interaction::EXIT_INTERNAL_ERROR );
1707 void
1708 Interaction::systemDebugMessage( const std::string & msg )
1710 if( Interaction::debugSystem )
1712 std::cerr << "System command: " << msg << std::endl ;
1716 void
1717 addDefaultNeedPath( )
1719 char * start = getenv( "SHAPESINPUTS" );
1720 if( start == 0 )
1722 Ast::theShapesScanner.push_backNeedPath( "./" );
1723 return;
1725 char * tok = strsep( & start, ":" );
1726 while( tok != 0 )
1728 Ast::theShapesScanner.push_backNeedPath( tok );
1729 tok = strsep( & start, ":" );
1733 void
1734 addDefaultFontMetricsPath( )
1736 char * start = getenv( "SHAPESFONTMETRICS" );
1737 if( start == 0 )
1739 return;
1741 char * tok = strsep( & start, ":" );
1742 while( tok != 0 )
1744 Lang::Font::push_backFontMetricsPath( tok );
1745 tok = strsep( & start, ":" );
1749 void
1750 destroyGlobals( )
1752 Helpers::requireUTF8ToMacRomanConverter( true ); // true means "cleanup"
1753 Helpers::requireMacRomanToUTF8Converter( true ); // true means "cleanup"
1754 Helpers::requireUTF8ToWinANSIConverter( true ); // true means "cleanup"
1755 Helpers::requireUTF8ToASCIIConverter( true ); // true means "cleanup"
1756 Helpers::requireUTF8ToUCS4Converter( true ); // true means "cleanup"
1757 Helpers::requireUTF16BEToUCS4Converter( true ); // true means "cleanup"
1758 Helpers::requireGlyphList( true ); // true means "cleanup"
1759 Helpers::requireMacRomanEncoding( true ); // true means "cleanup"
1762 std::string
1763 absoluteFilename( const char * filename )
1765 if( *filename == '/' )
1767 return filename;
1769 return callDir + filename;
1772 std::string
1773 absoluteDirectory( const char * filename )
1775 if( *filename == '\0' )
1777 return callDir;
1779 if( filename[ strlen( filename ) - 1 ] != '/' )
1781 if( *filename == '/' )
1783 return filename + std::string( "/" );
1785 return callDir + filename + "/";
1787 if( *filename == '/' )
1789 return filename;
1791 return callDir + filename;
1794 #include <iomanip>
1796 void
1797 ensureTmpDirectoryExists( const std::string & dirname, bool allowCreate )
1799 struct stat theStat;
1800 if( stat( dirname.c_str( ), & theStat ) == 0 )
1802 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1804 std::cerr << "The path " << dirname << " was expected to reference a directory." << std::endl ;
1805 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1807 // if( ( theStat.st_mode & S_IWOTH ) == 0 )
1808 // {
1809 // std::cerr << "The directory " << dirname << " was expected have write permission for others." << std::endl ;
1810 // exit( Interaction::EXIT_FILE_PERMISSION_ERROR );
1811 // }
1812 return;
1815 if( ! allowCreate )
1817 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 ;
1818 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1821 size_t i2 = 0; /* We know there's a slash at the first position */
1822 i2 = dirname.find( '/', i2 + 1 );
1823 bool atRoot = true;
1824 while( stat( dirname.substr( 0, i2 ).c_str( ), & theStat ) == 0 )
1826 atRoot = false;
1827 i2 = dirname.find( '/', i2 + 1 );
1829 if( atRoot )
1831 std::cerr << "Shapes will not create directories for temporary files at the root: " << dirname << std::endl ;
1832 exit( Interaction::EXIT_INVOCATION_ERROR );
1835 mode_t oldUmask = umask( 0 ); /* We want to be able to create directories with any permissions. */
1836 while( i2 != std::string::npos )
1838 if( mkdir( dirname.substr( 0, i2 ).c_str( ), theStat.st_mode & ( S_IRWXU | S_IRWXG | S_IRWXO ) ) != 0 )
1840 std::cerr << "Failed to create directory for temporary files (errno=" << errno << "): " << dirname.substr( 0, i2 ) << std::endl ;
1841 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1843 i2 = dirname.find( '/', i2 + 1 );
1845 umask( oldUmask );
1848 void
1849 escapeExtGlobChars( const std::string & str, std::ostream & dst )
1851 const char * special = "*?[+@!";
1852 for( std::string::const_iterator i = str.begin( ); i != str.end( ); ++i )
1854 if( strchr( special, *i ) != 0 )
1856 dst << '\\' ;
1858 dst << *i ;
1862 void
1863 rmSplitFiles( const std::string & outputName, const char * sep )
1865 std::ostringstream rmCommand;
1866 rmCommand << "sh -O extglob -c 'rm -f " ;
1867 escapeExtGlobChars( outputName, rmCommand );
1868 rmCommand << sep << "+([0-9]).pdf'" ;
1869 Interaction::systemDebugMessage( rmCommand.str( ) );
1870 if( system( rmCommand.str( ).c_str( ) ) != 0 )
1872 /* Never mind; we made a try, and this probably means that there were no files to remove. */
1876 void
1877 mkSplitDir( const std::string & outputName )
1879 struct stat theStat;
1880 if( stat( outputName.c_str( ), & theStat ) == 0 )
1882 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1884 std::cerr << "The path " << outputName << " was expected to reference a directory." << std::endl ;
1885 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1888 else
1890 if( mkdir( outputName.c_str( ), S_IRWXU | S_IRWXG | S_IRWXO ) != 0 )
1892 std::cerr << "Failed to create directory for split document files (errno=" << errno << "): " << outputName << std::endl ;
1893 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1898 void
1899 noSplitOpen( std::ofstream * oFile, const std::string & outputName )
1901 oFile->open( outputName.c_str( ) );
1902 if( ! oFile->good( ) )
1904 /* If this is because the output directory does not exist, we shall inform the user about this. */
1905 std::string::size_type slash = outputName.rfind( '/' );
1906 if( slash != std::string::npos )
1908 std::string outputDir = outputName.substr( 0, slash );
1909 struct stat theStat;
1910 if( stat( outputDir.c_str( ), & theStat ) == 0 )
1912 if( ( theStat.st_mode & S_IFDIR ) == 0 )
1914 std::cerr << "The prefix " << outputDir << " of the output name must be a directory." << std::endl ;
1915 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1917 /* In case we reach here, the directory exists, so we fall back on the generic error message below. */
1919 else
1921 std::cerr << "The file " << outputName << " cannot be opened for output since the directory " << outputDir << " does not exist." << std::endl ;
1922 exit( Interaction::EXIT_NO_DIRECTORY_ERROR );
1925 std::cerr << "Failed to open " << outputName << " for output." << std::endl ;
1926 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1930 std::string
1931 splitOpen( std::ofstream * oFile, const std::string outputName, const char * sep, size_t physicalPageNo )
1933 std::ostringstream oss;
1934 oss << outputName << sep << physicalPageNo << ".pdf" ;
1935 std::string filename( oss.str( ) );
1936 oFile->open( filename.c_str( ) );
1937 if( ! oFile->good( ) )
1939 std::cerr << "Failed to open " << filename << " for output." << std::endl ;
1940 exit( Interaction::EXIT_OUTPUT_FILE_ERROR );
1942 return filename;
1945 void
1946 printHelp( )
1948 std::string ind(" ");
1949 std::cout
1950 << "Usage:" << std::endl
1951 << ind << "shapes [OPTIONS] file[.[shape]]" << std::endl
1952 << "A subset of options is listed below, by cathegory. This message is just" << std::endl
1953 << "a reminder; legal option values are only listed in rare cases." << std::endl
1954 << "Files: --in --out --outdir --texjob --tmpdir --tmp*= --labeldb" << std::endl
1955 << "Paths: --needpath -N --fontmetricspath --resources=" << std::endl
1956 << "Multipage: --split=('no'|'flat'|'dir')" << std::endl
1957 << "PDF: (--pdf-version=|-v)(e|w|s)1.N --tp= --spot-pair=" << std::endl
1958 << "Tolerances: --arcdelta= --dtmin= --dtminerror= --splicingtol= --overlaptol=" << std::endl
1959 << "Log file: --debuglog --debuglog-stderr --debuglog-stdout" << std::endl
1960 << "Help: --help --which-doc --showfiles= --which --version" << std::endl
1961 << "Viewing: --xpdf --open --open-a" << std::endl
1962 << "Interactive: --interactive -i" << std::endl
1963 << "See the man page for a complete listing and details." << std::endl