1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 
30 #include <hash_map>
31 
32 #include "vcl/ppdparser.hxx"
33 #include "vcl/strhelper.hxx"
34 #include "vcl/helper.hxx"
35 #include "vcl/svapp.hxx"
36 #include "cupsmgr.hxx"
37 #include "tools/debug.hxx"
38 #include "tools/urlobj.hxx"
39 #include "tools/stream.hxx"
40 #include "tools/zcodec.hxx"
41 #include "osl/mutex.hxx"
42 #include "osl/file.hxx"
43 #include "osl/process.h"
44 #include "osl/thread.h"
45 #include "rtl/strbuf.hxx"
46 #include "rtl/ustrbuf.hxx"
47 
48 #include "com/sun/star/lang/Locale.hpp"
49 
50 namespace psp
51 {
52     class PPDTranslator
53     {
54         struct LocaleEqual
55         {
56             bool operator()(const com::sun::star::lang::Locale& i_rLeft,
57                             const com::sun::star::lang::Locale& i_rRight) const
58             {
59                 return i_rLeft.Language.equals( i_rRight.Language ) &&
60                 i_rLeft.Country.equals( i_rRight.Country ) &&
61                 i_rLeft.Variant.equals( i_rRight.Variant );
62             }
63         };
64 
65         struct LocaleHash
66         {
67             size_t operator()(const com::sun::star::lang::Locale& rLocale) const
68             { return
69                   (size_t)rLocale.Language.hashCode()
70                 ^ (size_t)rLocale.Country.hashCode()
71                 ^ (size_t)rLocale.Variant.hashCode()
72                 ;
73             }
74         };
75 
76         typedef std::hash_map< com::sun::star::lang::Locale, rtl::OUString, LocaleHash, LocaleEqual > translation_map;
77         typedef std::hash_map< rtl::OUString, translation_map, rtl::OUStringHash > key_translation_map;
78 
79         key_translation_map     m_aTranslations;
80         public:
81         PPDTranslator() {}
82         ~PPDTranslator() {}
83 
84 
85         void insertValue(
86             const rtl::OUString& i_rKey,
87             const rtl::OUString& i_rOption,
88             const rtl::OUString& i_rValue,
89             const rtl::OUString& i_rTranslation,
90             const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale()
91             );
92 
93         void insertOption( const rtl::OUString& i_rKey,
94                            const rtl::OUString& i_rOption,
95                            const rtl::OUString& i_rTranslation,
96                            const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() )
97         {
98             insertValue( i_rKey, i_rOption, rtl::OUString(), i_rTranslation, i_rLocale );
99         }
100 
101         void insertKey( const rtl::OUString& i_rKey,
102                         const rtl::OUString& i_rTranslation,
103                         const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() )
104         {
105             insertValue( i_rKey, rtl::OUString(), rtl::OUString(), i_rTranslation, i_rLocale );
106         }
107 
108         rtl::OUString translateValue(
109             const rtl::OUString& i_rKey,
110             const rtl::OUString& i_rOption,
111             const rtl::OUString& i_rValue,
112             const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale()
113             ) const;
114 
115         rtl::OUString translateOption( const rtl::OUString& i_rKey,
116                                        const rtl::OUString& i_rOption,
117                                        const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) const
118         {
119             return translateValue( i_rKey, i_rOption, rtl::OUString(), i_rLocale );
120         }
121 
122         rtl::OUString translateKey( const rtl::OUString& i_rKey,
123                                     const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) const
124         {
125             return translateValue( i_rKey, rtl::OUString(), rtl::OUString(), i_rLocale );
126         }
127     };
128 
129     static com::sun::star::lang::Locale normalizeInputLocale(
130         const com::sun::star::lang::Locale& i_rLocale,
131         bool bInsertDefault = false
132         )
133     {
134         com::sun::star::lang::Locale aLoc( i_rLocale );
135         if( bInsertDefault && aLoc.Language.getLength() == 0 )
136         {
137             // empty locale requested, fill in application UI locale
138             aLoc = Application::GetSettings().GetUILocale();
139 
140             #if OSL_DEBUG_LEVEL > 1
141             static const char* pEnvLocale = getenv( "SAL_PPDPARSER_LOCALE" );
142             if( pEnvLocale && *pEnvLocale )
143             {
144                 rtl::OString aStr( pEnvLocale );
145                 sal_Int32 nLen = aStr.getLength();
146                 aLoc.Language = rtl::OStringToOUString( aStr.copy( 0, nLen > 2 ? 2 : nLen ), RTL_TEXTENCODING_MS_1252 );
147                 if( nLen >=5 && aStr.getStr()[2] == '_' )
148                     aLoc.Country = rtl::OStringToOUString( aStr.copy( 3, 2 ), RTL_TEXTENCODING_MS_1252 );
149                 else
150                     aLoc.Country = rtl::OUString();
151                 aLoc.Variant = rtl::OUString();
152             }
153             #endif
154         }
155         aLoc.Language = aLoc.Language.toAsciiLowerCase();
156         aLoc.Country  = aLoc.Country.toAsciiUpperCase();
157         aLoc.Variant  = aLoc.Variant.toAsciiUpperCase();
158 
159         return aLoc;
160     }
161 
162     void PPDTranslator::insertValue(
163         const rtl::OUString& i_rKey,
164         const rtl::OUString& i_rOption,
165         const rtl::OUString& i_rValue,
166         const rtl::OUString& i_rTranslation,
167         const com::sun::star::lang::Locale& i_rLocale
168         )
169     {
170         rtl::OUStringBuffer aKey( i_rKey.getLength() + i_rOption.getLength() + i_rValue.getLength() + 2 );
171         aKey.append( i_rKey );
172         if( i_rOption.getLength() || i_rValue.getLength() )
173         {
174             aKey.append( sal_Unicode( ':' ) );
175             aKey.append( i_rOption );
176         }
177         if( i_rValue.getLength() )
178         {
179             aKey.append( sal_Unicode( ':' ) );
180             aKey.append( i_rValue );
181         }
182         if( aKey.getLength() && i_rTranslation.getLength() )
183         {
184             rtl::OUString aK( aKey.makeStringAndClear() );
185             com::sun::star::lang::Locale aLoc;
186             aLoc.Language = i_rLocale.Language.toAsciiLowerCase();
187             aLoc.Country  = i_rLocale.Country.toAsciiUpperCase();
188             aLoc.Variant  = i_rLocale.Variant.toAsciiUpperCase();
189             m_aTranslations[ aK ][ aLoc ] = i_rTranslation;
190         }
191     }
192 
193     rtl::OUString PPDTranslator::translateValue(
194         const rtl::OUString& i_rKey,
195         const rtl::OUString& i_rOption,
196         const rtl::OUString& i_rValue,
197         const com::sun::star::lang::Locale& i_rLocale
198         ) const
199     {
200         rtl::OUString aResult;
201 
202         rtl::OUStringBuffer aKey( i_rKey.getLength() + i_rOption.getLength() + i_rValue.getLength() + 2 );
203         aKey.append( i_rKey );
204         if( i_rOption.getLength() || i_rValue.getLength() )
205         {
206             aKey.append( sal_Unicode( ':' ) );
207             aKey.append( i_rOption );
208         }
209         if( i_rValue.getLength() )
210         {
211             aKey.append( sal_Unicode( ':' ) );
212             aKey.append( i_rValue );
213         }
214         if( aKey.getLength() )
215         {
216             rtl::OUString aK( aKey.makeStringAndClear() );
217             key_translation_map::const_iterator it = m_aTranslations.find( aK );
218             if( it != m_aTranslations.end() )
219             {
220                 const translation_map& rMap( it->second );
221 
222                 com::sun::star::lang::Locale aLoc( normalizeInputLocale( i_rLocale, true ) );
223                 for( int nTry = 0; nTry < 4; nTry++ )
224                 {
225                     translation_map::const_iterator tr = rMap.find( aLoc );
226                     if( tr != rMap.end() )
227                     {
228                         aResult = tr->second;
229                         break;
230                     }
231                     switch( nTry )
232                     {
233                     case 0: aLoc.Variant  = rtl::OUString();break;
234                     case 1: aLoc.Country  = rtl::OUString();break;
235                     case 2: aLoc.Language = rtl::OUString();break;
236                     }
237                 }
238             }
239         }
240         return aResult;
241     }
242 }
243 
244 using namespace psp;
245 using namespace rtl;
246 
247 #undef DBG_ASSERT
248 #if defined DBG_UTIL || (OSL_DEBUG_LEVEL > 1)
249 #define BSTRING(x) ByteString( x, osl_getThreadTextEncoding() )
250 #define DBG_ASSERT( x, y ) { if( ! (x) ) fprintf( stderr, (y) ); }
251 #else
252 #define DBG_ASSERT( x, y )
253 #endif
254 
255 std::list< PPDParser* > PPDParser::aAllParsers;
256 std::hash_map< OUString, OUString, OUStringHash >* PPDParser::pAllPPDFiles = NULL;
257 
258 class PPDDecompressStream
259 {
260     SvFileStream*       mpFileStream;
261     SvMemoryStream*     mpMemStream;
262     rtl::OUString       maFileName;
263 
264     // forbid copying
265     PPDDecompressStream( const PPDDecompressStream& );
266     PPDDecompressStream& operator=(const PPDDecompressStream& );
267 
268     public:
269     PPDDecompressStream( const rtl::OUString& rFile );
270     ~PPDDecompressStream();
271 
272     bool IsOpen() const;
273     bool IsEof() const;
274     void ReadLine( ByteString& o_rLine);
275     void Open( const rtl::OUString& i_rFile );
276     void Close();
277     const rtl::OUString& GetFileName() const { return maFileName; }
278 };
279 
280 PPDDecompressStream::PPDDecompressStream( const rtl::OUString& i_rFile ) :
281     mpFileStream( NULL ),
282     mpMemStream( NULL )
283 {
284     Open( i_rFile );
285 }
286 
287 PPDDecompressStream::~PPDDecompressStream()
288 {
289     Close();
290 }
291 
292 void PPDDecompressStream::Open( const rtl::OUString& i_rFile )
293 {
294     Close();
295 
296     mpFileStream = new SvFileStream( i_rFile, STREAM_READ );
297     maFileName = mpFileStream->GetFileName();
298 
299     if( ! mpFileStream->IsOpen() )
300     {
301         Close();
302         return;
303     }
304 
305     ByteString aLine;
306     mpFileStream->ReadLine( aLine );
307     mpFileStream->Seek( 0 );
308 
309     // check for compress'ed or gzip'ed file
310     sal_uLong nCompressMethod = 0;
311     if( aLine.Len() > 1 && static_cast<unsigned char>(aLine.GetChar( 0 )) == 0x1f )
312     {
313         if( static_cast<unsigned char>(aLine.GetChar( 1 )) == 0x8b ) // check for gzip
314             nCompressMethod = ZCODEC_DEFAULT | ZCODEC_GZ_LIB;
315     }
316 
317     if( nCompressMethod != 0 )
318     {
319         // so let's try to decompress the stream
320         mpMemStream = new SvMemoryStream( 4096, 4096 );
321         ZCodec aCodec;
322         aCodec.BeginCompression( nCompressMethod );
323         long nComp = aCodec.Decompress( *mpFileStream, *mpMemStream );
324         aCodec.EndCompression();
325         if( nComp < 0 )
326         {
327             // decompression failed, must be an uncompressed stream after all
328             delete mpMemStream, mpMemStream = NULL;
329             mpFileStream->Seek( 0 );
330         }
331         else
332         {
333             // compression successfull, can get rid of file stream
334             delete mpFileStream, mpFileStream = NULL;
335             mpMemStream->Seek( 0 );
336         }
337     }
338 }
339 
340 void PPDDecompressStream::Close()
341 {
342     delete mpMemStream, mpMemStream = NULL;
343     delete mpFileStream, mpFileStream = NULL;
344 }
345 
346 bool PPDDecompressStream::IsOpen() const
347 {
348     return (mpMemStream || (mpFileStream && mpFileStream->IsOpen()));
349 }
350 
351 bool PPDDecompressStream::IsEof() const
352 {
353     return ( mpMemStream ? mpMemStream->IsEof() : ( mpFileStream ? mpFileStream->IsEof() : true ) );
354 }
355 
356 void PPDDecompressStream::ReadLine( ByteString& o_rLine )
357 {
358     if( mpMemStream )
359         mpMemStream->ReadLine( o_rLine );
360     else if( mpFileStream )
361         mpFileStream->ReadLine( o_rLine );
362 }
363 
364 static osl::FileBase::RC resolveLink( const rtl::OUString& i_rURL, rtl::OUString& o_rResolvedURL, rtl::OUString& o_rBaseName, osl::FileStatus::Type& o_rType, int nLinkLevel = 10 )
365 {
366     osl::DirectoryItem aLinkItem;
367     osl::FileBase::RC aRet = osl::FileBase::E_None;
368 
369     if( ( aRet = osl::DirectoryItem::get( i_rURL, aLinkItem ) ) == osl::FileBase::E_None )
370     {
371         osl::FileStatus aStatus( FileStatusMask_FileName | FileStatusMask_Type | FileStatusMask_LinkTargetURL );
372         if( ( aRet = aLinkItem.getFileStatus( aStatus ) ) == osl::FileBase::E_None )
373         {
374             if( aStatus.getFileType() == osl::FileStatus::Link )
375             {
376                 if( nLinkLevel > 0 )
377                     aRet = resolveLink( aStatus.getLinkTargetURL(), o_rResolvedURL, o_rBaseName, o_rType, nLinkLevel-1 );
378                 else
379                     aRet = osl::FileBase::E_MULTIHOP;
380             }
381             else
382             {
383                 o_rResolvedURL = i_rURL;
384                 o_rBaseName = aStatus.getFileName();
385                 o_rType = aStatus.getFileType();
386             }
387         }
388     }
389     return aRet;
390 }
391 
392 void PPDParser::scanPPDDir( const String& rDir )
393 {
394     static struct suffix_t
395     {
396         const sal_Char* pSuffix;
397         const sal_Int32 nSuffixLen;
398     } const pSuffixes[] =
399     { { ".PS", 3 },  { ".PPD", 4 }, { ".PS.GZ", 6 }, { ".PPD.GZ", 7 } };
400 
401     const int nSuffixes = sizeof(pSuffixes)/sizeof(pSuffixes[0]);
402 
403     osl::Directory aDir( rDir );
404     if ( aDir.open() == osl::FileBase::E_None )
405     {
406         osl::DirectoryItem aItem;
407 
408         INetURLObject aPPDDir(rDir);
409         while( aDir.getNextItem( aItem ) == osl::FileBase::E_None )
410         {
411             osl::FileStatus aStatus( FileStatusMask_FileName );
412             if( aItem.getFileStatus( aStatus ) == osl::FileBase::E_None )
413             {
414                 rtl::OUStringBuffer aURLBuf( rDir.Len() + 64 );
415                 aURLBuf.append( rDir );
416                 aURLBuf.append( sal_Unicode( '/' ) );
417                 aURLBuf.append( aStatus.getFileName() );
418 
419                 rtl::OUString aFileURL, aFileName;
420                 osl::FileStatus::Type eType = osl::FileStatus::Unknown;
421 
422                 if( resolveLink( aURLBuf.makeStringAndClear(), aFileURL, aFileName, eType ) == osl::FileBase::E_None )
423                 {
424                     if( eType == osl::FileStatus::Regular )
425                     {
426                         INetURLObject aPPDFile = aPPDDir;
427                         aPPDFile.Append( aFileName );
428 
429                         // match extension
430                         for( int nSuffix = 0; nSuffix < nSuffixes; nSuffix++ )
431                         {
432                             if( aFileName.getLength() > pSuffixes[nSuffix].nSuffixLen )
433                             {
434                                 if( aFileName.endsWithIgnoreAsciiCaseAsciiL( pSuffixes[nSuffix].pSuffix, pSuffixes[nSuffix].nSuffixLen ) )
435                                 {
436                                     (*pAllPPDFiles)[ aFileName.copy( 0, aFileName.getLength() - pSuffixes[nSuffix].nSuffixLen ) ] = aPPDFile.PathToFileName();
437                                     break;
438                                 }
439                             }
440                         }
441                     }
442                     else if( eType == osl::FileStatus::Directory )
443                     {
444                         scanPPDDir( aFileURL );
445                     }
446                 }
447             }
448         }
449         aDir.close();
450     }
451 }
452 
453 void PPDParser::initPPDFiles()
454 {
455     if( pAllPPDFiles )
456         return;
457 
458     pAllPPDFiles = new std::hash_map< OUString, OUString, OUStringHash >();
459 
460     // check installation directories
461     std::list< OUString > aPathList;
462     psp::getPrinterPathList( aPathList, PRINTER_PPDDIR );
463     for( std::list< OUString >::const_iterator ppd_it = aPathList.begin(); ppd_it != aPathList.end(); ++ppd_it )
464     {
465         INetURLObject aPPDDir( *ppd_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
466         scanPPDDir( aPPDDir.GetMainURL( INetURLObject::NO_DECODE ) );
467     }
468     if( pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() )
469     {
470         // last try: search in directory of executable (mainly for setup)
471         OUString aExe;
472         if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None )
473         {
474             INetURLObject aDir( aExe );
475             aDir.removeSegment();
476 #ifdef DEBUG
477             fprintf( stderr, "scanning last chance dir: %s\n", OUStringToOString( aDir.GetMainURL( INetURLObject::NO_DECODE ), osl_getThreadTextEncoding() ).getStr() );
478 #endif
479             scanPPDDir( aDir.GetMainURL( INetURLObject::NO_DECODE ) );
480 #ifdef DEBUG
481             fprintf( stderr, "SGENPRT %s\n", pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() ? "not found" : "found" );
482 #endif
483         }
484     }
485 }
486 
487 void PPDParser::getKnownPPDDrivers( std::list< rtl::OUString >& o_rDrivers, bool bRefresh )
488 {
489     if( bRefresh )
490     {
491         delete pAllPPDFiles;
492         pAllPPDFiles = NULL;
493     }
494 
495     initPPDFiles();
496     o_rDrivers.clear();
497 
498     std::hash_map< OUString, OUString, OUStringHash >::const_iterator it;
499     for( it = pAllPPDFiles->begin(); it != pAllPPDFiles->end(); ++it )
500         o_rDrivers.push_back( it->first );
501 }
502 
503 String PPDParser::getPPDFile( const String& rFile )
504 {
505     INetURLObject aPPD( rFile, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
506     // someone might enter a full qualified name here
507     PPDDecompressStream aStream( aPPD.PathToFileName() );
508     if( ! aStream.IsOpen() )
509     {
510         std::hash_map< OUString, OUString, OUStringHash >::const_iterator it;
511 
512         bool bRetry = true;
513         do
514         {
515             initPPDFiles();
516             // some PPD files contain dots beside the extension, so try name first
517             // and cut of points after that
518             rtl::OUString aBase( rFile );
519             sal_Int32 nLastIndex = aBase.lastIndexOf( sal_Unicode( '/' ) );
520             if( nLastIndex >= 0 )
521                 aBase = aBase.copy( nLastIndex+1 );
522             do
523             {
524                 it = pAllPPDFiles->find( aBase );
525                 nLastIndex = aBase.lastIndexOf( sal_Unicode( '.' ) );
526                 if( nLastIndex > 0 )
527                     aBase = aBase.copy( 0, nLastIndex );
528             } while( it == pAllPPDFiles->end() && nLastIndex > 0 );
529 
530             if( it == pAllPPDFiles->end() && bRetry )
531             {
532                 // a new file ? rehash
533                 delete pAllPPDFiles; pAllPPDFiles = NULL;
534                 bRetry = false;
535                 // note this is optimized for office start where
536                 // no new files occur and initPPDFiles is called only once
537             }
538         } while( ! pAllPPDFiles );
539 
540         if( it != pAllPPDFiles->end() )
541             aStream.Open( it->second );
542     }
543 
544     String aRet;
545     if( aStream.IsOpen() )
546     {
547         ByteString aLine;
548         aStream.ReadLine( aLine );
549         if( aLine.Search( "*PPD-Adobe" ) == 0 )
550             aRet = aStream.GetFileName();
551         else
552         {
553             // our *Include hack does usually not begin
554             // with *PPD-Adobe, so try some lines for *Include
555             int nLines = 10;
556             while( aLine.Search( "*Include" ) != 0 && --nLines )
557                 aStream.ReadLine( aLine );
558             if( nLines )
559                 aRet = aStream.GetFileName();
560         }
561     }
562 
563     return aRet;
564 }
565 
566 String PPDParser::getPPDPrinterName( const String& rFile )
567 {
568     String aPath = getPPDFile( rFile );
569     String aName;
570 
571     // read in the file
572     PPDDecompressStream aStream( aPath );
573     if( aStream.IsOpen() )
574     {
575         String aCurLine;
576         while( ! aStream.IsEof() && aStream.IsOpen() )
577         {
578             ByteString aByteLine;
579             aStream.ReadLine( aByteLine );
580             aCurLine = String( aByteLine, RTL_TEXTENCODING_MS_1252 );
581             if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL )
582             {
583                 aCurLine.Erase( 0, 9 );
584                 aCurLine.EraseLeadingChars( ' ' );
585                 aCurLine.EraseTrailingChars( ' ' );
586                 aCurLine.EraseLeadingChars( '\t' );
587                 aCurLine.EraseTrailingChars( '\t' );
588                 aCurLine.EraseTrailingChars( '\r' );
589                 aCurLine.EraseTrailingChars( '\n' );
590                 aCurLine.EraseLeadingChars( '"' );
591                 aCurLine.EraseTrailingChars( '"' );
592                 aStream.Close();
593                 aStream.Open( getPPDFile( aCurLine ) );
594                 continue;
595             }
596             if( aCurLine.CompareToAscii( "*ModelName:", 11 ) == COMPARE_EQUAL )
597             {
598                 aName = aCurLine.GetToken( 1, '"' );
599                 break;
600             }
601             else if( aCurLine.CompareToAscii( "*NickName:", 10 ) == COMPARE_EQUAL )
602                 aName = aCurLine.GetToken( 1, '"' );
603         }
604     }
605     return aName;
606 }
607 
608 const PPDParser* PPDParser::getParser( const String& rFile )
609 {
610     static ::osl::Mutex aMutex;
611     ::osl::Guard< ::osl::Mutex > aGuard( aMutex );
612 
613     String aFile = rFile;
614     if( rFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL )
615         aFile = getPPDFile( rFile );
616     if( ! aFile.Len() )
617     {
618 #if OSL_DEBUG_LEVEL > 1
619         fprintf( stderr, "Could not get printer PPD file \"%s\" !\n", OUStringToOString( rFile, osl_getThreadTextEncoding() ).getStr() );
620 #endif
621         return NULL;
622     }
623 
624     for( ::std::list< PPDParser* >::const_iterator it = aAllParsers.begin(); it != aAllParsers.end(); ++it )
625         if( (*it)->m_aFile == aFile )
626             return *it;
627 
628     PPDParser* pNewParser = NULL;
629     if( aFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL )
630         pNewParser = new PPDParser( aFile );
631     else
632     {
633         PrinterInfoManager& rMgr = PrinterInfoManager::get();
634         if( rMgr.getType() == PrinterInfoManager::CUPS )
635         {
636             pNewParser = const_cast<PPDParser*>(static_cast<CUPSManager&>(rMgr).createCUPSParser( aFile ));
637         }
638     }
639     if( pNewParser )
640     {
641         // this may actually be the SGENPRT parser,
642         // so ensure uniquness here
643         aAllParsers.remove( pNewParser );
644         // insert new parser to list
645         aAllParsers.push_front( pNewParser );
646     }
647     return pNewParser;
648 }
649 
650 void PPDParser::freeAll()
651 {
652     while( aAllParsers.begin() != aAllParsers.end() )
653     {
654         delete aAllParsers.front();
655         aAllParsers.pop_front();
656     }
657     delete pAllPPDFiles;
658     pAllPPDFiles = NULL;
659 }
660 
661 PPDParser::PPDParser( const String& rFile ) :
662         m_aFile( rFile ),
663         m_bType42Capable( false ),
664         m_aFileEncoding( RTL_TEXTENCODING_MS_1252 ),
665         m_pDefaultImageableArea( NULL ),
666         m_pImageableAreas( NULL ),
667         m_pDefaultPaperDimension( NULL ),
668         m_pPaperDimensions( NULL ),
669         m_pDefaultInputSlot( NULL ),
670         m_pInputSlots( NULL ),
671         m_pDefaultResolution( NULL ),
672         m_pResolutions( NULL ),
673         m_pDefaultDuplexType( NULL ),
674         m_pDuplexTypes( NULL ),
675         m_pFontList( NULL ),
676         m_pTranslator( new PPDTranslator() )
677 {
678     // read in the file
679     std::list< ByteString > aLines;
680     PPDDecompressStream aStream( m_aFile );
681     bool bLanguageEncoding = false;
682     if( aStream.IsOpen() )
683     {
684         ByteString aCurLine;
685         while( ! aStream.IsEof() )
686         {
687             aStream.ReadLine( aCurLine );
688             if( aCurLine.GetChar( 0 ) == '*' )
689             {
690                 if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL )
691                 {
692                     aCurLine.Erase( 0, 9 );
693                     aCurLine.EraseLeadingChars( ' ' );
694                     aCurLine.EraseTrailingChars( ' ' );
695                     aCurLine.EraseLeadingChars( '\t' );
696                     aCurLine.EraseTrailingChars( '\t' );
697                     aCurLine.EraseTrailingChars( '\r' );
698                     aCurLine.EraseTrailingChars( '\n' );
699                     aCurLine.EraseLeadingChars( '"' );
700                     aCurLine.EraseTrailingChars( '"' );
701                     aStream.Close();
702                     aStream.Open( getPPDFile( String( aCurLine, m_aFileEncoding ) ) );
703                     continue;
704                 }
705                 else if( ! bLanguageEncoding &&
706                          aCurLine.CompareIgnoreCaseToAscii( "*languageencoding", 17 ) == COMPARE_EQUAL )
707                 {
708                     bLanguageEncoding = true; // generally only the first one counts
709                     ByteString aLower = aCurLine;
710                     aLower.ToLowerAscii();
711                     if( aLower.Search( "isolatin1", 17 ) != STRING_NOTFOUND ||
712                         aLower.Search( "windowsansi", 17 ) != STRING_NOTFOUND )
713                         m_aFileEncoding = RTL_TEXTENCODING_MS_1252;
714                     else if( aLower.Search( "isolatin2", 17 ) != STRING_NOTFOUND )
715                         m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_2;
716                     else if( aLower.Search( "isolatin5", 17 ) != STRING_NOTFOUND )
717                         m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_5;
718                     else if( aLower.Search( "jis83-rksj", 17 ) != STRING_NOTFOUND )
719                         m_aFileEncoding = RTL_TEXTENCODING_SHIFT_JIS;
720                     else if( aLower.Search( "macstandard", 17 ) != STRING_NOTFOUND )
721                         m_aFileEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
722                     else if( aLower.Search( "utf-8", 17 ) != STRING_NOTFOUND )
723                         m_aFileEncoding = RTL_TEXTENCODING_UTF8;
724                 }
725             }
726             aLines.push_back( aCurLine );
727         }
728     }
729     aStream.Close();
730 
731     // now get the Values
732     parse( aLines );
733 #if OSL_DEBUG_LEVEL > 2
734     fprintf( stderr, "acquired %d Keys from PPD %s:\n", m_aKeys.size(), BSTRING( m_aFile ).GetBuffer() );
735     for( PPDParser::hash_type::const_iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it )
736     {
737         const PPDKey* pKey = it->second;
738         char* pSetupType = "<unknown>";
739         switch( pKey->m_eSetupType )
740         {
741             case PPDKey::ExitServer:        pSetupType = "ExitServer";break;
742             case PPDKey::Prolog:            pSetupType = "Prolog";break;
743             case PPDKey::DocumentSetup: pSetupType = "DocumentSetup";break;
744             case PPDKey::PageSetup:     pSetupType = "PageSetup";break;
745             case PPDKey::JCLSetup:          pSetupType = "JCLSetup";break;
746             case PPDKey::AnySetup:          pSetupType = "AnySetup";break;
747             default: break;
748         };
749         fprintf( stderr, "\t\"%s\" (\"%s\") (%d values) OrderDependency: %d %s\n",
750                  BSTRING( pKey->getKey() ).GetBuffer(),
751                  BSTRING( pKey->m_aUITranslation ).GetBuffer(),
752                  pKey->countValues(),
753                  pKey->m_nOrderDependency,
754                  pSetupType );
755         for( int j = 0; j < pKey->countValues(); j++ )
756         {
757             fprintf( stderr, "\t\t" );
758             const PPDValue* pValue = pKey->getValue( j );
759             if( pValue == pKey->m_pDefaultValue )
760                 fprintf( stderr, "(Default:) " );
761             char* pVType = "<unknown>";
762             switch( pValue->m_eType )
763             {
764                 case eInvocation:       pVType = "invocation";break;
765                 case eQuoted:           pVType = "quoted";break;
766                 case eString:           pVType = "string";break;
767                 case eSymbol:           pVType = "symbol";break;
768                 case eNo:               pVType = "no";break;
769                 default: break;
770             };
771             fprintf( stderr, "option: \"%s\" (\"%s\"), value: type %s \"%s\" (\"%s\")\n",
772                      BSTRING( pValue->m_aOption ).GetBuffer(),
773                      BSTRING( pValue->m_aOptionTranslation ).GetBuffer(),
774                      pVType,
775                      BSTRING( pValue->m_aValue ).GetBuffer(),
776                      BSTRING( pValue->m_aValueTranslation ).GetBuffer() );
777         }
778     }
779     fprintf( stderr, "constraints: (%d found)\n", m_aConstraints.size() );
780     for( std::list< PPDConstraint >::const_iterator cit = m_aConstraints.begin(); cit != m_aConstraints.end(); ++cit )
781     {
782         fprintf( stderr, "*\"%s\" \"%s\" *\"%s\" \"%s\"\n",
783                  BSTRING( cit->m_pKey1->getKey() ).GetBuffer(),
784                  cit->m_pOption1 ? BSTRING( cit->m_pOption1->m_aOption ).GetBuffer() : "<nil>",
785                  BSTRING( cit->m_pKey2->getKey() ).GetBuffer(),
786                  cit->m_pOption2 ? BSTRING( cit->m_pOption2->m_aOption ).GetBuffer() : "<nil>"
787                  );
788     }
789 #endif
790 
791     // fill in shortcuts
792     const PPDKey* pKey;
793 
794     m_pImageableAreas = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ImageableArea" ) ) );
795     if( m_pImageableAreas )
796         m_pDefaultImageableArea = m_pImageableAreas->getDefaultValue();
797     DBG_ASSERT( m_pImageableAreas, "Warning: no ImageableArea in PPD\n" );
798     DBG_ASSERT( m_pDefaultImageableArea, "Warning: no DefaultImageableArea in PPD\n" );
799 
800     m_pPaperDimensions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PaperDimension" ) ) );
801     if( m_pPaperDimensions )
802         m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue();
803     DBG_ASSERT( m_pPaperDimensions, "Warning: no PaperDimension in PPD\n" );
804     DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultPaperDimension in PPD\n" );
805 
806     m_pResolutions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) );
807     if( m_pResolutions )
808         m_pDefaultResolution = m_pResolutions->getDefaultValue();
809     DBG_ASSERT( m_pResolutions, "Warning: no Resolution in PPD\n" );
810     DBG_ASSERT( m_pDefaultResolution, "Warning: no DefaultResolution in PPD\n" );
811 
812     m_pInputSlots = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
813     if( m_pInputSlots )
814         m_pDefaultInputSlot = m_pInputSlots->getDefaultValue();
815     DBG_ASSERT( m_pPaperDimensions, "Warning: no InputSlot in PPD\n" );
816     DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultInputSlot in PPD\n" );
817 
818     m_pDuplexTypes = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
819     if( m_pDuplexTypes )
820         m_pDefaultDuplexType = m_pDuplexTypes->getDefaultValue();
821 
822     m_pFontList = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Font" ) ) );
823     DBG_ASSERT( m_pFontList, "Warning: no Font in PPD\n" );
824 
825     // fill in direct values
826     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ModelName" ) ) )) )
827         m_aPrinterName = pKey->getValue( 0 )->m_aValue;
828     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "NickName" ) ) )) )
829         m_aNickName = pKey->getValue( 0 )->m_aValue;
830     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ColorDevice" ) ) )) )
831         m_bColorDevice = pKey->getValue( 0 )->m_aValue.CompareIgnoreCaseToAscii( "true", 4 ) == COMPARE_EQUAL ? true : false;
832 
833     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "LanguageLevel" ) ) )) )
834         m_nLanguageLevel = pKey->getValue( 0 )->m_aValue.ToInt32();
835     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "TTRasterizer" ) ) )) )
836         m_bType42Capable = pKey->getValue( 0 )->m_aValue.EqualsIgnoreCaseAscii( "Type42" ) ? true : false;
837 }
838 
839 PPDParser::~PPDParser()
840 {
841     for( PPDParser::hash_type::iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it )
842         delete it->second;
843     delete m_pTranslator;
844 }
845 
846 void PPDParser::insertKey( const String& rKey, PPDKey* pKey )
847 {
848     m_aKeys[ rKey ] = pKey;
849     m_aOrderedKeys.push_back( pKey );
850 }
851 
852 const PPDKey* PPDParser::getKey( int n ) const
853 {
854     return ((unsigned int)n < m_aOrderedKeys.size() && n >= 0) ? m_aOrderedKeys[n] : NULL;
855 }
856 
857 const PPDKey* PPDParser::getKey( const String& rKey ) const
858 {
859     PPDParser::hash_type::const_iterator it = m_aKeys.find( rKey );
860     return it != m_aKeys.end() ? it->second : NULL;
861 }
862 
863 bool PPDParser::hasKey( const PPDKey* pKey ) const
864 {
865     return
866         pKey ?
867         ( m_aKeys.find( pKey->getKey() ) != m_aKeys.end() ? true : false ) :
868         false;
869 }
870 
871 static sal_uInt8 getNibble( sal_Char cChar )
872 {
873     sal_uInt8 nRet = 0;
874     if( cChar >= '0' && cChar <= '9' )
875         nRet = sal_uInt8( cChar - '0' );
876     else if( cChar >= 'A' && cChar <= 'F' )
877         nRet = 10 + sal_uInt8( cChar - 'A' );
878     else if( cChar >= 'a' && cChar <= 'f' )
879         nRet = 10 + sal_uInt8( cChar - 'a' );
880     return nRet;
881 }
882 
883 String PPDParser::handleTranslation( const ByteString& i_rString, bool bIsGlobalized )
884 {
885     int nOrigLen = i_rString.Len();
886     OStringBuffer aTrans( nOrigLen );
887     const sal_Char* pStr = i_rString.GetBuffer();
888     const sal_Char* pEnd = pStr + nOrigLen;
889     while( pStr < pEnd )
890     {
891         if( *pStr == '<' )
892         {
893             pStr++;
894             sal_Char cChar;
895             while( *pStr != '>' && pStr < pEnd-1 )
896             {
897                 cChar = getNibble( *pStr++ ) << 4;
898                 cChar |= getNibble( *pStr++ );
899                 aTrans.append( cChar );
900             }
901             pStr++;
902         }
903         else
904             aTrans.append( *pStr++ );
905     }
906     return OStringToOUString( aTrans.makeStringAndClear(), bIsGlobalized ? RTL_TEXTENCODING_UTF8 : m_aFileEncoding );
907 }
908 
909 void PPDParser::parse( ::std::list< ByteString >& rLines )
910 {
911     std::list< ByteString >::iterator line = rLines.begin();
912     PPDParser::hash_type::const_iterator keyit;
913     while( line != rLines.end() )
914     {
915         ByteString aCurrentLine( *line );
916         ++line;
917         if( aCurrentLine.GetChar(0) != '*' )
918             continue;
919         if( aCurrentLine.GetChar(1) == '%' )
920             continue;
921 
922         ByteString aKey = GetCommandLineToken( 0, aCurrentLine.GetToken( 0, ':' ) );
923         int nPos = aKey.Search( '/' );
924         if( nPos != STRING_NOTFOUND )
925             aKey.Erase( nPos );
926         aKey.Erase( 0, 1 ); // remove the '*'
927 
928         if( aKey.Equals( "CloseUI" ) || aKey.Equals( "OpenGroup" ) || aKey.Equals( "CloseGroup" ) || aKey.Equals( "End" ) || aKey.Equals( "OpenSubGroup" ) || aKey.Equals( "CloseSubGroup" ) )
929             continue;
930 
931         if( aKey.Equals( "OpenUI" ) )
932         {
933             parseOpenUI( aCurrentLine );
934             continue;
935         }
936         else if( aKey.Equals( "OrderDependency" ) )
937         {
938             parseOrderDependency( aCurrentLine );
939             continue;
940         }
941         else if( aKey.Equals( "UIConstraints" ) || aKey.Equals( "NonUIConstraints" ) )
942             continue; // parsed in pass 2
943         else if( aKey.Equals( "CustomPageSize" ) ) // currently not handled
944             continue;
945 
946         // default values are parsed in pass 2
947         if( aKey.CompareTo( "Default", 7 ) == COMPARE_EQUAL )
948             continue;
949 
950         bool bQuery     = false;
951         if( aKey.GetChar( 0 ) == '?' )
952         {
953             aKey.Erase( 0, 1 );
954             bQuery = true;
955         }
956 
957         String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 );
958         // handle CUPS extension for globalized PPDs
959         bool bIsGlobalizedLine = false;
960         com::sun::star::lang::Locale aTransLocale;
961         if( ( aUniKey.Len() > 3 && aUniKey.GetChar( 2 ) == '.' ) ||
962             ( aUniKey.Len() > 5 && aUniKey.GetChar( 2 ) == '_' && aUniKey.GetChar( 5 ) == '.' ) )
963         {
964             if( aUniKey.GetChar( 2 ) == '.' )
965             {
966                 aTransLocale.Language = aUniKey.Copy( 0, 2 );
967                 aUniKey = aUniKey.Copy( 3 );
968             }
969             else
970             {
971                 aTransLocale.Language = aUniKey.Copy( 0, 2 );
972                 aTransLocale.Country = aUniKey.Copy( 3, 2 );
973                 aUniKey = aUniKey.Copy( 6 );
974             }
975             bIsGlobalizedLine = true;
976         }
977 
978         String aOption;
979         nPos = aCurrentLine.Search( ':' );
980         if( nPos != STRING_NOTFOUND )
981         {
982             aOption = String( aCurrentLine.Copy( 1, nPos-1 ), RTL_TEXTENCODING_MS_1252 );
983             aOption = GetCommandLineToken( 1, aOption );
984             int nTransPos = aOption.Search( '/' );
985             if( nTransPos != STRING_NOTFOUND )
986                 aOption.Erase( nTransPos );
987         }
988 
989         PPDValueType eType = eNo;
990         String aValue;
991         rtl::OUString aOptionTranslation;
992         rtl::OUString aValueTranslation;
993         if( nPos != STRING_NOTFOUND )
994         {
995             // found a colon, there may be an option
996             ByteString aLine = aCurrentLine.Copy( 1, nPos-1 );
997             aLine = WhitespaceToSpace( aLine );
998             int nTransPos = aLine.Search( '/' );
999             if( nTransPos != STRING_NOTFOUND )
1000                 aOptionTranslation = handleTranslation( aLine.Copy( nTransPos+1 ), bIsGlobalizedLine );
1001 
1002             // read in more lines if necessary for multiline values
1003             aLine = aCurrentLine.Copy( nPos+1 );
1004             if( aLine.Len() )
1005             {
1006                 while( ! ( aLine.GetTokenCount( '"' ) & 1 ) &&
1007                        line != rLines.end() )
1008                     // while there is an even number of tokens; that means
1009                     // an odd number of doubleqoutes
1010                 {
1011                     // copy the newlines also
1012                     aLine += '\n';
1013                     aLine += *line;
1014                     ++line;
1015                 }
1016             }
1017             aLine = WhitespaceToSpace( aLine );
1018 
1019             // #i100644# handle a missing value (actually a broken PPD)
1020             if( ! aLine.Len() )
1021             {
1022                 if( aOption.Len() &&
1023                     aUniKey.CompareToAscii( "JCL", 3 ) != COMPARE_EQUAL )
1024                     eType = eInvocation;
1025                 else
1026                     eType = eQuoted;
1027             }
1028             // check for invocation or quoted value
1029             else if( aLine.GetChar(0) == '"' )
1030             {
1031                 aLine.Erase( 0, 1 );
1032                 nTransPos = aLine.Search( '"' );
1033                 aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 );
1034                 // after the second doublequote can follow a / and a translation
1035                 aValueTranslation = handleTranslation( aLine.Copy( nTransPos+2 ), bIsGlobalizedLine );
1036                 // check for quoted value
1037                 if( aOption.Len() &&
1038                     aUniKey.CompareToAscii( "JCL", 3 ) != COMPARE_EQUAL )
1039                     eType = eInvocation;
1040                 else
1041                     eType = eQuoted;
1042             }
1043             // check for symbol value
1044             else if( aLine.GetChar(0) == '^' )
1045             {
1046                 aLine.Erase( 0, 1 );
1047                 aValue = String( aLine, RTL_TEXTENCODING_MS_1252 );
1048                 eType = eSymbol;
1049             }
1050             else
1051             {
1052                 // must be a string value then
1053                 // strictly this is false because string values
1054                 // can contain any whitespace which is reduced
1055                 // to one space by now
1056                 // who cares ...
1057                 nTransPos = aLine.Search( '/' );
1058                 if( nTransPos == STRING_NOTFOUND )
1059                     nTransPos = aLine.Len();
1060                 aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 );
1061                 aValueTranslation = handleTranslation( aLine.Copy( nTransPos+1 ), bIsGlobalizedLine );
1062                 eType = eString;
1063             }
1064         }
1065 
1066         // handle globalized PPD entries
1067         if( bIsGlobalizedLine )
1068         {
1069             // handle main key translations of form:
1070             // *ll_CC.Translation MainKeyword/translated text: ""
1071             if( aUniKey.EqualsAscii( "Translation" ) )
1072             {
1073                 m_pTranslator->insertKey( aOption, aOptionTranslation, aTransLocale );
1074             }
1075             // handle options translations of for:
1076             // *ll_CC.MainKeyword OptionKeyword/translated text: ""
1077             else
1078             {
1079                 m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale );
1080             }
1081             continue;
1082         }
1083 
1084         PPDKey* pKey = NULL;
1085         keyit = m_aKeys.find( aUniKey );
1086         if( keyit == m_aKeys.end() )
1087         {
1088             pKey = new PPDKey( aUniKey );
1089             insertKey( aUniKey, pKey );
1090         }
1091         else
1092             pKey = keyit->second;
1093 
1094         if( eType == eNo && bQuery )
1095             continue;
1096 
1097         PPDValue* pValue = pKey->insertValue( aOption );
1098         if( ! pValue )
1099             continue;
1100         pValue->m_eType = eType;
1101         pValue->m_aValue = aValue;
1102 
1103         if( aOptionTranslation.getLength() )
1104             m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale );
1105         if( aValueTranslation.getLength() )
1106             m_pTranslator->insertValue( aUniKey, aOption, aValue, aValueTranslation, aTransLocale );
1107 
1108         // eventually update query and remove from option list
1109         if( bQuery && pKey->m_bQueryValue == sal_False )
1110         {
1111             pKey->m_aQueryValue = *pValue;
1112             pKey->m_bQueryValue = true;
1113             pKey->eraseValue( pValue->m_aOption );
1114         }
1115     }
1116 
1117     // second pass: fill in defaults
1118     for( line = rLines.begin(); line != rLines.end(); ++line )
1119     {
1120         ByteString aLine( *line );
1121         if( aLine.CompareTo( "*Default", 8 ) == COMPARE_EQUAL )
1122         {
1123             String aKey( aLine.Copy( 8 ), RTL_TEXTENCODING_MS_1252 );
1124             sal_uInt16 nPos = aKey.Search( ':' );
1125             if( nPos != STRING_NOTFOUND )
1126             {
1127                 aKey.Erase( nPos );
1128                 String aOption( WhitespaceToSpace( aLine.Copy( nPos+9 ) ), RTL_TEXTENCODING_MS_1252 );
1129                 keyit = m_aKeys.find( aKey );
1130                 if( keyit != m_aKeys.end() )
1131                 {
1132                     PPDKey* pKey = keyit->second;
1133                     const PPDValue* pDefValue = pKey->getValue( aOption );
1134                     if( pKey->m_pDefaultValue == NULL )
1135                         pKey->m_pDefaultValue = pDefValue;
1136                 }
1137                 else
1138                 {
1139                     // some PPDs contain defaults for keys that
1140                     // do not exist otherwise
1141                     // (example: DefaultResolution)
1142                     // so invent that key here and have a default value
1143                     PPDKey* pKey = new PPDKey( aKey );
1144                     PPDValue* pNewValue = pKey->insertValue( aOption );
1145                     pNewValue->m_eType = eInvocation; // or what ?
1146                     insertKey( aKey, pKey );
1147                 }
1148             }
1149         }
1150         else if( aLine.CompareTo( "*UIConstraints", 14 ) == COMPARE_EQUAL  ||
1151                  aLine.CompareTo( "*NonUIConstraints", 17 ) == COMPARE_EQUAL )
1152             parseConstraint( aLine );
1153 
1154     }
1155 }
1156 
1157 void PPDParser::parseOpenUI( const ByteString& rLine )
1158 {
1159     String aTranslation;
1160     ByteString aKey = rLine;
1161 
1162     int nPos = aKey.Search( ':' );
1163     if( nPos != STRING_NOTFOUND )
1164         aKey.Erase( nPos );
1165     nPos = aKey.Search( '/' );
1166     if( nPos != STRING_NOTFOUND )
1167     {
1168         aTranslation = handleTranslation( aKey.Copy( nPos + 1 ), false );
1169         aKey.Erase( nPos );
1170     }
1171     aKey = GetCommandLineToken( 1, aKey );
1172     aKey.Erase( 0, 1 );
1173 
1174     String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 );
1175     PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aUniKey );
1176     PPDKey* pKey;
1177     if( keyit == m_aKeys.end() )
1178     {
1179         pKey = new PPDKey( aUniKey );
1180         insertKey( aUniKey, pKey );
1181     }
1182     else
1183         pKey = keyit->second;
1184 
1185     pKey->m_bUIOption = true;
1186     m_pTranslator->insertKey( pKey->getKey(), aTranslation );
1187 
1188     ByteString aValue = WhitespaceToSpace( rLine.GetToken( 1, ':' ) );
1189     if( aValue.CompareIgnoreCaseToAscii( "boolean" ) == COMPARE_EQUAL )
1190         pKey->m_eUIType = PPDKey::Boolean;
1191     else if( aValue.CompareIgnoreCaseToAscii( "pickmany" ) == COMPARE_EQUAL )
1192         pKey->m_eUIType = PPDKey::PickMany;
1193     else
1194         pKey->m_eUIType = PPDKey::PickOne;
1195 }
1196 
1197 void PPDParser::parseOrderDependency( const ByteString& rLine )
1198 {
1199     ByteString aLine( rLine );
1200     int nPos = aLine.Search( ':' );
1201     if( nPos != STRING_NOTFOUND )
1202         aLine.Erase( 0, nPos+1 );
1203 
1204     int nOrder = GetCommandLineToken( 0, aLine ).ToInt32();
1205     ByteString aSetup = GetCommandLineToken( 1, aLine );
1206     String aKey( GetCommandLineToken( 2, aLine ), RTL_TEXTENCODING_MS_1252 );
1207     if( aKey.GetChar( 0 ) != '*' )
1208         return; // invalid order depency
1209     aKey.Erase( 0, 1 );
1210 
1211     PPDKey* pKey;
1212     PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aKey );
1213     if( keyit == m_aKeys.end() )
1214     {
1215         pKey = new PPDKey( aKey );
1216         insertKey( aKey, pKey );
1217     }
1218     else
1219         pKey = keyit->second;
1220 
1221     pKey->m_nOrderDependency = nOrder;
1222     if( aSetup.Equals( "ExitServer" ) )
1223         pKey->m_eSetupType = PPDKey::ExitServer;
1224     else if( aSetup.Equals( "Prolog" ) )
1225         pKey->m_eSetupType = PPDKey::Prolog;
1226     else if( aSetup.Equals( "DocumentSetup" ) )
1227         pKey->m_eSetupType = PPDKey::DocumentSetup;
1228     else if( aSetup.Equals( "PageSetup" ) )
1229         pKey->m_eSetupType = PPDKey::PageSetup;
1230     else if( aSetup.Equals( "JCLSetup" ) )
1231         pKey->m_eSetupType = PPDKey::JCLSetup;
1232     else
1233         pKey->m_eSetupType = PPDKey::AnySetup;
1234 }
1235 
1236 void PPDParser::parseConstraint( const ByteString& rLine )
1237 {
1238     bool bFailed = false;
1239 
1240     String aLine( rLine, RTL_TEXTENCODING_MS_1252 );
1241     aLine.Erase( 0, rLine.Search( ':' )+1 );
1242     PPDConstraint aConstraint;
1243     int nTokens = GetCommandLineTokenCount( aLine );
1244     for( int i = 0; i < nTokens; i++ )
1245     {
1246         String aToken = GetCommandLineToken( i, aLine );
1247         if( aToken.GetChar( 0 ) == '*' )
1248         {
1249             aToken.Erase( 0, 1 );
1250             if( aConstraint.m_pKey1 )
1251                 aConstraint.m_pKey2 = getKey( aToken );
1252             else
1253                 aConstraint.m_pKey1 = getKey( aToken );
1254         }
1255         else
1256         {
1257             if( aConstraint.m_pKey2 )
1258             {
1259                 if( ! ( aConstraint.m_pOption2 = aConstraint.m_pKey2->getValue( aToken ) ) )
1260                     bFailed = true;
1261             }
1262             else if( aConstraint.m_pKey1 )
1263             {
1264                 if( ! ( aConstraint.m_pOption1 = aConstraint.m_pKey1->getValue( aToken ) ) )
1265                     bFailed = true;
1266             }
1267             else
1268                 // constraint for nonexistent keys; this happens
1269                 // e.g. in HP4PLUS3 (#75636#)
1270                 bFailed = true;
1271         }
1272     }
1273     // there must be two keywords
1274     if( ! aConstraint.m_pKey1 || ! aConstraint.m_pKey2 || bFailed )
1275     {
1276 #ifdef __DEBUG
1277         fprintf( stderr, "Warning: constraint \"%s\" is invalid\n", rLine.GetStr() );
1278 #endif
1279     }
1280     else
1281         m_aConstraints.push_back( aConstraint );
1282 }
1283 
1284 String PPDParser::getDefaultPaperDimension() const
1285 {
1286     if( m_pDefaultPaperDimension )
1287         return m_pDefaultPaperDimension->m_aOption;
1288 
1289     return String();
1290 }
1291 
1292 bool PPDParser::getMargins(
1293                            const String& rPaperName,
1294                            int& rLeft, int& rRight,
1295                            int& rUpper, int& rLower ) const
1296 {
1297     if( ! m_pImageableAreas || ! m_pPaperDimensions )
1298         return false;
1299 
1300     int nPDim=-1, nImArea=-1, i;
1301     for( i = 0; i < m_pImageableAreas->countValues(); i++ )
1302         if( rPaperName == m_pImageableAreas->getValue( i )->m_aOption )
1303             nImArea = i;
1304     for( i = 0; i < m_pPaperDimensions->countValues(); i++ )
1305         if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1306             nPDim = i;
1307     if( nPDim == -1 || nImArea == -1 )
1308         return false;
1309 
1310     double ImLLx, ImLLy, ImURx, ImURy;
1311     double PDWidth, PDHeight;
1312     String aArea = m_pImageableAreas->getValue( nImArea )->m_aValue;
1313     ImLLx = StringToDouble( GetCommandLineToken( 0, aArea ) );
1314     ImLLy = StringToDouble( GetCommandLineToken( 1, aArea ) );
1315     ImURx = StringToDouble( GetCommandLineToken( 2, aArea ) );
1316     ImURy = StringToDouble( GetCommandLineToken( 3, aArea ) );
1317 //  sscanf( m_pImageableAreas->getValue( nImArea )->m_aValue.GetStr(),
1318 //          "%lg%lg%lg%lg", &ImLLx, &ImLLy, &ImURx, &ImURy );
1319     aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1320     PDWidth     = StringToDouble( GetCommandLineToken( 0, aArea ) );
1321     PDHeight    = StringToDouble( GetCommandLineToken( 1, aArea ) );
1322 //  sscanf( m_pPaperDimensions->getValue( nPDim )->m_aValue.GetStr(),
1323 //          "%lg%lg", &PDWidth, &PDHeight );
1324     rLeft  = (int)(ImLLx + 0.5);
1325     rLower = (int)(ImLLy + 0.5);
1326     rUpper = (int)(PDHeight - ImURy + 0.5);
1327     rRight = (int)(PDWidth - ImURx + 0.5);
1328 
1329     return true;
1330 }
1331 
1332 bool PPDParser::getPaperDimension(
1333                                   const String& rPaperName,
1334                                   int& rWidth, int& rHeight ) const
1335 {
1336     if( ! m_pPaperDimensions )
1337         return false;
1338 
1339     int nPDim=-1;
1340     for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1341         if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1342             nPDim = i;
1343     if( nPDim == -1 )
1344         return false;
1345 
1346     double PDWidth, PDHeight;
1347     String aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1348     PDWidth     = StringToDouble( GetCommandLineToken( 0, aArea ) );
1349     PDHeight    = StringToDouble( GetCommandLineToken( 1, aArea ) );
1350     rHeight = (int)(PDHeight + 0.5);
1351     rWidth  = (int)(PDWidth + 0.5);
1352 
1353     return true;
1354 }
1355 
1356 String PPDParser::matchPaper( int nWidth, int nHeight ) const
1357 {
1358     if( ! m_pPaperDimensions )
1359         return String();
1360 
1361     int nPDim = -1;
1362     double PDWidth, PDHeight;
1363     double fSort = 2e36, fNewSort;
1364 
1365     for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1366     {
1367         String aArea =  m_pPaperDimensions->getValue( i )->m_aValue;
1368         PDWidth     = StringToDouble( GetCommandLineToken( 0, aArea ) );
1369         PDHeight    = StringToDouble( GetCommandLineToken( 1, aArea ) );
1370         PDWidth     /= (double)nWidth;
1371         PDHeight    /= (double)nHeight;
1372         if( PDWidth >= 0.9      &&  PDWidth <= 1.1      &&
1373             PDHeight >= 0.9     &&  PDHeight <= 1.1         )
1374         {
1375             fNewSort =
1376                 (1.0-PDWidth)*(1.0-PDWidth) + (1.0-PDHeight)*(1.0-PDHeight);
1377             if( fNewSort == 0.0 ) // perfect match
1378                 return m_pPaperDimensions->getValue( i )->m_aOption;
1379 
1380             if( fNewSort < fSort )
1381             {
1382                 fSort = fNewSort;
1383                 nPDim = i;
1384             }
1385         }
1386     }
1387 
1388     static bool bDontSwap = false;
1389     if( nPDim == -1 && ! bDontSwap )
1390     {
1391         // swap portrait/landscape and try again
1392         bDontSwap = true;
1393         String rRet = matchPaper( nHeight, nWidth );
1394         bDontSwap = false;
1395         return rRet;
1396     }
1397 
1398     return nPDim != -1 ? m_pPaperDimensions->getValue( nPDim )->m_aOption : String();
1399 }
1400 
1401 String PPDParser::getDefaultInputSlot() const
1402 {
1403     if( m_pDefaultInputSlot )
1404         return m_pDefaultInputSlot->m_aValue;
1405     return String();
1406 }
1407 
1408 String PPDParser::getSlot( int nSlot ) const
1409 {
1410     if( ! m_pInputSlots )
1411         return String();
1412 
1413     if( nSlot > 0 && nSlot < m_pInputSlots->countValues() )
1414         return m_pInputSlots->getValue( nSlot )->m_aOption;
1415     else if( m_pInputSlots->countValues() > 0 )
1416         return m_pInputSlots->getValue( (sal_uLong)0 )->m_aOption;
1417 
1418     return String();
1419 }
1420 
1421 String PPDParser::getSlotCommand( int nSlot ) const
1422 {
1423     if( ! m_pInputSlots )
1424         return String();
1425 
1426     if( nSlot > 0 && nSlot < m_pInputSlots->countValues() )
1427         return m_pInputSlots->getValue( nSlot )->m_aValue;
1428     else if( m_pInputSlots->countValues() > 0 )
1429         return m_pInputSlots->getValue( (sal_uLong)0 )->m_aValue;
1430 
1431     return String();
1432 }
1433 
1434 String PPDParser::getSlotCommand( const String& rSlot ) const
1435 {
1436     if( ! m_pInputSlots )
1437         return String();
1438 
1439     for( int i=0; i < m_pInputSlots->countValues(); i++ )
1440     {
1441         const PPDValue* pValue = m_pInputSlots->getValue( i );
1442         if( pValue->m_aOption == rSlot )
1443             return pValue->m_aValue;
1444     }
1445     return String();
1446 }
1447 
1448 String PPDParser::getPaperDimension( int nPaperDimension ) const
1449 {
1450     if( ! m_pPaperDimensions )
1451         return String();
1452 
1453     if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() )
1454         return m_pPaperDimensions->getValue( nPaperDimension )->m_aOption;
1455     else if( m_pPaperDimensions->countValues() > 0 )
1456         return m_pPaperDimensions->getValue( (sal_uLong)0 )->m_aOption;
1457 
1458     return String();
1459 }
1460 
1461 String PPDParser::getPaperDimensionCommand( int nPaperDimension ) const
1462 {
1463     if( ! m_pPaperDimensions )
1464         return String();
1465 
1466     if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() )
1467         return m_pPaperDimensions->getValue( nPaperDimension )->m_aValue;
1468     else if( m_pPaperDimensions->countValues() > 0 )
1469         return m_pPaperDimensions->getValue( (sal_uLong)0 )->m_aValue;
1470 
1471     return String();
1472 }
1473 
1474 String PPDParser::getPaperDimensionCommand( const String& rPaperDimension ) const
1475 {
1476     if( ! m_pPaperDimensions )
1477         return String();
1478 
1479     for( int i=0; i < m_pPaperDimensions->countValues(); i++ )
1480     {
1481         const PPDValue* pValue = m_pPaperDimensions->getValue( i );
1482         if( pValue->m_aOption == rPaperDimension )
1483             return pValue->m_aValue;
1484     }
1485     return String();
1486 }
1487 
1488 void PPDParser::getResolutionFromString(
1489                                         const String& rString,
1490                                         int& rXRes, int& rYRes ) const
1491 {
1492     int nPos = 0, nDPIPos;
1493 
1494     rXRes = rYRes = 300;
1495 
1496     nDPIPos = rString.SearchAscii( "dpi" );
1497     if( nDPIPos != STRING_NOTFOUND )
1498     {
1499         if( ( nPos = rString.Search( 'x' ) ) != STRING_NOTFOUND )
1500         {
1501             rXRes = rString.Copy( 0, nPos ).ToInt32();
1502             rYRes = rString.GetToken( 1, 'x' ).Erase( nDPIPos - nPos - 1 ).ToInt32();
1503         }
1504         else
1505             rXRes = rYRes = rString.Copy( 0, nDPIPos ).ToInt32();
1506     }
1507 }
1508 
1509 void PPDParser::getDefaultResolution( int& rXRes, int& rYRes ) const
1510 {
1511     if( m_pDefaultResolution )
1512     {
1513         getResolutionFromString( m_pDefaultResolution->m_aValue, rXRes, rYRes );
1514         return;
1515     }
1516 
1517     rXRes = 300;
1518     rYRes = 300;
1519 }
1520 
1521 int PPDParser::getResolutions() const
1522 {
1523     if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) &&
1524         m_pDefaultResolution )
1525         return 1;
1526     return m_pResolutions ? m_pResolutions->countValues() : 0;
1527 }
1528 
1529 void PPDParser::getResolution( int nNr, int& rXRes, int& rYRes ) const
1530 {
1531     if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution && nNr == 0 )
1532     {
1533         getDefaultResolution( rXRes, rYRes );
1534         return;
1535     }
1536     if( ! m_pResolutions )
1537         return;
1538 
1539     getResolutionFromString( m_pResolutions->getValue( nNr )->m_aOption,
1540                              rXRes, rYRes );
1541 }
1542 
1543 String PPDParser::getResolutionCommand( int nXRes, int nYRes ) const
1544 {
1545     if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution )
1546         return m_pDefaultResolution->m_aValue;
1547 
1548     if( ! m_pResolutions )
1549         return String();
1550 
1551     int nX, nY;
1552     for( int i = 0; i < m_pResolutions->countValues(); i++ )
1553     {
1554         getResolutionFromString( m_pResolutions->getValue( i )->m_aOption,
1555                                  nX, nY );
1556         if( nX == nXRes && nY == nYRes )
1557             return m_pResolutions->getValue( i )->m_aValue;
1558     }
1559     return String();
1560 }
1561 
1562 String PPDParser::getDefaultDuplexType() const
1563 {
1564     if( m_pDefaultDuplexType )
1565         return m_pDefaultDuplexType->m_aValue;
1566     return String();
1567 }
1568 
1569 String PPDParser::getDuplex( int nDuplex ) const
1570 {
1571     if( ! m_pDuplexTypes )
1572         return String();
1573 
1574     if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() )
1575         return m_pDuplexTypes->getValue( nDuplex )->m_aOption;
1576     else if( m_pDuplexTypes->countValues() > 0 )
1577         return m_pDuplexTypes->getValue( (sal_uLong)0 )->m_aOption;
1578 
1579     return String();
1580 }
1581 
1582 String PPDParser::getDuplexCommand( int nDuplex ) const
1583 {
1584     if( ! m_pDuplexTypes )
1585         return String();
1586 
1587     if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() )
1588         return m_pDuplexTypes->getValue( nDuplex )->m_aValue;
1589     else if( m_pDuplexTypes->countValues() > 0 )
1590         return m_pDuplexTypes->getValue( (sal_uLong)0 )->m_aValue;
1591 
1592     return String();
1593 }
1594 
1595 String PPDParser::getDuplexCommand( const String& rDuplex ) const
1596 {
1597     if( ! m_pDuplexTypes )
1598         return String();
1599 
1600     for( int i=0; i < m_pDuplexTypes->countValues(); i++ )
1601     {
1602         const PPDValue* pValue = m_pDuplexTypes->getValue( i );
1603         if( pValue->m_aOption == rDuplex )
1604             return pValue->m_aValue;
1605     }
1606     return String();
1607 }
1608 
1609 void PPDParser::getFontAttributes(
1610                                   int nFont,
1611                                   String& rEncoding,
1612                                   String& rCharset ) const
1613 {
1614     if( m_pFontList && nFont >= 0 && nFont < m_pFontList->countValues() )
1615     {
1616         String aAttribs =
1617             WhitespaceToSpace( m_pFontList->getValue( nFont )->m_aValue );
1618         rEncoding   = GetCommandLineToken( 0, aAttribs );
1619         rCharset    = GetCommandLineToken( 2, aAttribs );
1620     }
1621 }
1622 
1623 void PPDParser::getFontAttributes(
1624                                   const String& rFont,
1625                                   String& rEncoding,
1626                                   String& rCharset ) const
1627 {
1628     if( m_pFontList )
1629     {
1630         for( int i = 0; i < m_pFontList->countValues(); i++ )
1631             if( m_pFontList->getValue( i )->m_aOption == rFont )
1632                 getFontAttributes( i, rEncoding, rCharset );
1633     }
1634 }
1635 
1636 String PPDParser::getFont( int nFont ) const
1637 {
1638     if( ! m_pFontList )
1639         return String();
1640 
1641     if( nFont >=0 && nFont < m_pFontList->countValues() )
1642         return m_pFontList->getValue( nFont )->m_aOption;
1643     return String();
1644 }
1645 
1646 rtl::OUString PPDParser::translateKey( const rtl::OUString& i_rKey,
1647                                        const com::sun::star::lang::Locale& i_rLocale ) const
1648 {
1649     rtl::OUString aResult( m_pTranslator->translateKey( i_rKey, i_rLocale ) );
1650     if( aResult.getLength() == 0 )
1651         aResult = i_rKey;
1652     return aResult;
1653 }
1654 
1655 rtl::OUString PPDParser::translateOption( const rtl::OUString& i_rKey,
1656                                           const rtl::OUString& i_rOption,
1657                                           const com::sun::star::lang::Locale& i_rLocale ) const
1658 {
1659     rtl::OUString aResult( m_pTranslator->translateOption( i_rKey, i_rOption, i_rLocale ) );
1660     if( aResult.getLength() == 0 )
1661         aResult = i_rOption;
1662     return aResult;
1663 }
1664 
1665 rtl::OUString PPDParser::translateValue( const rtl::OUString& i_rKey,
1666                                          const rtl::OUString& i_rOption,
1667                                          const rtl::OUString& i_rValue,
1668                                          const com::sun::star::lang::Locale& i_rLocale ) const
1669 {
1670     rtl::OUString aResult( m_pTranslator->translateValue( i_rKey, i_rOption, i_rValue, i_rLocale ) );
1671     if( aResult.getLength() == 0 )
1672         aResult = i_rValue;
1673     return aResult;
1674 }
1675 
1676 /*
1677  *  PPDKey
1678  */
1679 
1680 PPDKey::PPDKey( const String& rKey ) :
1681         m_aKey( rKey ),
1682         m_pDefaultValue( NULL ),
1683         m_bQueryValue( false ),
1684         m_bUIOption( false ),
1685         m_eUIType( PickOne ),
1686         m_nOrderDependency( 100 ),
1687         m_eSetupType( AnySetup )
1688 {
1689 }
1690 
1691 // -------------------------------------------------------------------
1692 
1693 PPDKey::~PPDKey()
1694 {
1695 }
1696 
1697 // -------------------------------------------------------------------
1698 
1699 const PPDValue* PPDKey::getValue( int n ) const
1700 {
1701     return ((unsigned int)n < m_aOrderedValues.size() && n >= 0) ? m_aOrderedValues[n] : NULL;
1702 }
1703 
1704 // -------------------------------------------------------------------
1705 
1706 const PPDValue* PPDKey::getValue( const String& rOption ) const
1707 {
1708     PPDKey::hash_type::const_iterator it = m_aValues.find( rOption );
1709     return it != m_aValues.end() ? &it->second : NULL;
1710 }
1711 
1712 // -------------------------------------------------------------------
1713 
1714 const PPDValue* PPDKey::getValueCaseInsensitive( const String& rOption ) const
1715 {
1716     const PPDValue* pValue = getValue( rOption );
1717     if( ! pValue )
1718     {
1719         for( size_t n = 0; n < m_aOrderedValues.size() && ! pValue; n++ )
1720             if( m_aOrderedValues[n]->m_aOption.EqualsIgnoreCaseAscii( rOption ) )
1721                 pValue = m_aOrderedValues[n];
1722     }
1723 
1724     return pValue;
1725 }
1726 
1727 // -------------------------------------------------------------------
1728 
1729 void PPDKey::eraseValue( const String& rOption )
1730 {
1731     PPDKey::hash_type::iterator it = m_aValues.find( rOption );
1732     if( it == m_aValues.end() )
1733         return;
1734 
1735     for( PPDKey::value_type::iterator vit = m_aOrderedValues.begin(); vit != m_aOrderedValues.end(); ++vit )
1736     {
1737         if( *vit == &(it->second ) )
1738         {
1739             m_aOrderedValues.erase( vit );
1740             break;
1741         }
1742     }
1743     m_aValues.erase( it );
1744 }
1745 
1746 // -------------------------------------------------------------------
1747 
1748 PPDValue* PPDKey::insertValue( const String& rOption )
1749 {
1750     if( m_aValues.find( rOption ) != m_aValues.end() )
1751         return NULL;
1752 
1753     PPDValue aValue;
1754     aValue.m_aOption = rOption;
1755     m_aValues[ rOption ] = aValue;
1756     PPDValue* pValue = &m_aValues[rOption];
1757     m_aOrderedValues.push_back( pValue );
1758     return pValue;
1759 }
1760 
1761 // -------------------------------------------------------------------
1762 
1763 /*
1764  * PPDContext
1765  */
1766 
1767 PPDContext::PPDContext( const PPDParser* pParser ) :
1768         m_pParser( pParser )
1769 {
1770 }
1771 
1772 // -------------------------------------------------------------------
1773 
1774 PPDContext& PPDContext::operator=( const PPDContext& rCopy )
1775 {
1776     m_pParser           = rCopy.m_pParser;
1777     m_aCurrentValues    = rCopy.m_aCurrentValues;
1778     return *this;
1779 }
1780 
1781 // -------------------------------------------------------------------
1782 
1783 PPDContext::~PPDContext()
1784 {
1785 }
1786 
1787 // -------------------------------------------------------------------
1788 
1789 const PPDKey* PPDContext::getModifiedKey( int n ) const
1790 {
1791     hash_type::const_iterator it;
1792     for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end() && n--; ++it )
1793         ;
1794     return it != m_aCurrentValues.end() ? it->first : NULL;
1795 }
1796 
1797 // -------------------------------------------------------------------
1798 
1799 void PPDContext::setParser( const PPDParser* pParser )
1800 {
1801     if( pParser != m_pParser )
1802     {
1803         m_aCurrentValues.clear();
1804         m_pParser = pParser;
1805     }
1806 }
1807 
1808 // -------------------------------------------------------------------
1809 
1810 const PPDValue* PPDContext::getValue( const PPDKey* pKey ) const
1811 {
1812     if( ! m_pParser )
1813         return NULL;
1814 
1815     hash_type::const_iterator it;
1816     it = m_aCurrentValues.find( pKey );
1817     if( it != m_aCurrentValues.end() )
1818         return it->second;
1819 
1820     if( ! m_pParser->hasKey( pKey ) )
1821         return NULL;
1822 
1823     const PPDValue* pValue = pKey->getDefaultValue();
1824     if( ! pValue )
1825         pValue = pKey->getValue( 0 );
1826 
1827     return pValue;
1828 }
1829 
1830 // -------------------------------------------------------------------
1831 
1832 const PPDValue* PPDContext::setValue( const PPDKey* pKey, const PPDValue* pValue, bool bDontCareForConstraints )
1833 {
1834     if( ! m_pParser || ! pKey )
1835         return NULL;
1836 
1837     // pValue can be NULL - it means ignore this option
1838 
1839     if( ! m_pParser->hasKey( pKey ) )
1840         return NULL;
1841 
1842     // check constraints
1843     if( pValue )
1844     {
1845         if( bDontCareForConstraints )
1846         {
1847             m_aCurrentValues[ pKey ] = pValue;
1848         }
1849         else if( checkConstraints( pKey, pValue, true ) )
1850         {
1851             m_aCurrentValues[ pKey ] = pValue;
1852 
1853             // after setting this value, check all constraints !
1854             hash_type::iterator it = m_aCurrentValues.begin();
1855             while(  it != m_aCurrentValues.end() )
1856             {
1857                 if( it->first != pKey &&
1858                     ! checkConstraints( it->first, it->second, false ) )
1859                 {
1860 #ifdef __DEBUG
1861                     fprintf( stderr, "PPDContext::setValue: option %s (%s) is constrained after setting %s to %s\n",
1862                              it->first->getKey().GetStr(),
1863                              it->second->m_aOption.GetStr(),
1864                              pKey->getKey().GetStr(),
1865                              pValue->m_aOption.GetStr() );
1866 #endif
1867                     resetValue( it->first, true );
1868                     it = m_aCurrentValues.begin();
1869                 }
1870                 else
1871                     ++it;
1872             }
1873         }
1874     }
1875     else
1876         m_aCurrentValues[ pKey ] = NULL;
1877 
1878     return pValue;
1879 }
1880 
1881 // -------------------------------------------------------------------
1882 
1883 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pValue )
1884 {
1885     if( ! m_pParser || ! pKey || ! pValue )
1886         return false;
1887 
1888     // ensure that this key is already in the list if it exists at all
1889     if( m_aCurrentValues.find( pKey ) != m_aCurrentValues.end() )
1890         return checkConstraints( pKey, pValue, false );
1891 
1892     // it is not in the list, insert it temporarily
1893     bool bRet = false;
1894     if( m_pParser->hasKey( pKey ) )
1895     {
1896         const PPDValue* pDefValue = pKey->getDefaultValue();
1897         m_aCurrentValues[ pKey ] = pDefValue;
1898         bRet = checkConstraints( pKey, pValue, false );
1899         m_aCurrentValues.erase( pKey );
1900     }
1901 
1902     return bRet;
1903 }
1904 
1905 // -------------------------------------------------------------------
1906 
1907 bool PPDContext::resetValue( const PPDKey* pKey, bool bDefaultable )
1908 {
1909     if( ! pKey || ! m_pParser || ! m_pParser->hasKey( pKey ) )
1910         return false;
1911 
1912     const PPDValue* pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) );
1913     if( ! pResetValue )
1914         pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "False" ) ) );
1915     if( ! pResetValue && bDefaultable )
1916         pResetValue = pKey->getDefaultValue();
1917 
1918     bool bRet = pResetValue ? ( setValue( pKey, pResetValue ) == pResetValue ? true : false ) : false;
1919 
1920     return bRet;
1921 }
1922 
1923 // -------------------------------------------------------------------
1924 
1925 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pNewValue, bool bDoReset )
1926 {
1927     if( ! pNewValue )
1928         return true;
1929 
1930     // sanity checks
1931     if( ! m_pParser )
1932         return false;
1933 
1934     if( pKey->getValue( pNewValue->m_aOption ) != pNewValue )
1935         return false;
1936 
1937     // None / False and the default can always be set, but be careful !
1938     // setting them might influence constrained values
1939     if( pNewValue->m_aOption.EqualsAscii( "None" ) || pNewValue->m_aOption.EqualsAscii( "False" ) ||
1940         pNewValue == pKey->getDefaultValue() )
1941         return true;
1942 
1943     const ::std::list< PPDParser::PPDConstraint >& rConstraints( m_pParser->getConstraints() );
1944     for( ::std::list< PPDParser::PPDConstraint >::const_iterator it = rConstraints.begin(); it != rConstraints.end(); ++it )
1945     {
1946         const PPDKey* pLeft     = it->m_pKey1;
1947         const PPDKey* pRight    = it->m_pKey2;
1948         if( ! pLeft || ! pRight || ( pKey != pLeft && pKey != pRight ) )
1949             continue;
1950 
1951         const PPDKey* pOtherKey = pKey == pLeft ? pRight : pLeft;
1952         const PPDValue* pOtherKeyOption = pKey == pLeft ? it->m_pOption2 : it->m_pOption1;
1953         const PPDValue* pKeyOption = pKey == pLeft ? it->m_pOption1 : it->m_pOption2;
1954 
1955         // syntax *Key1 option1 *Key2 option2
1956         if( pKeyOption && pOtherKeyOption )
1957         {
1958             if( pNewValue != pKeyOption )
1959                 continue;
1960             if( pOtherKeyOption == getValue( pOtherKey ) )
1961             {
1962                 return false;
1963             }
1964         }
1965         // syntax *Key1 option *Key2  or  *Key1 *Key2 option
1966         else if( pOtherKeyOption || pKeyOption )
1967         {
1968             if( pKeyOption )
1969             {
1970                 if( ! ( pOtherKeyOption = getValue( pOtherKey ) ) )
1971                     continue; // this should not happen, PPD broken
1972 
1973                 if( pKeyOption == pNewValue &&
1974                     ! pOtherKeyOption->m_aOption.EqualsAscii( "None" ) &&
1975                     ! pOtherKeyOption->m_aOption.EqualsAscii( "False" ) )
1976                 {
1977                     // check if the other value can be reset and
1978                     // do so if possible
1979                     if( bDoReset && resetValue( pOtherKey ) )
1980                         continue;
1981 
1982                     return false;
1983                 }
1984             }
1985             else if( pOtherKeyOption )
1986             {
1987                 if( getValue( pOtherKey ) == pOtherKeyOption &&
1988                     ! pNewValue->m_aOption.EqualsAscii( "None" ) &&
1989                     ! pNewValue->m_aOption.EqualsAscii( "False" ) )
1990                     return false;
1991             }
1992             else
1993             {
1994                 // this should not happen, PPD is broken
1995             }
1996         }
1997         // syntax *Key1 *Key2
1998         else
1999         {
2000             const PPDValue* pOtherValue = getValue( pOtherKey );
2001             if( ! pOtherValue->m_aOption.EqualsAscii( "None" )  &&
2002                 ! pOtherValue->m_aOption.EqualsAscii( "False" )     &&
2003                 ! pNewValue->m_aOption.EqualsAscii( "None" )        &&
2004                 ! pNewValue->m_aOption.EqualsAscii( "False" ) )
2005                 return false;
2006         }
2007     }
2008     return true;
2009 }
2010 
2011 // -------------------------------------------------------------------
2012 
2013 void PPDContext::getUnconstrainedValues( const PPDKey* pKey, ::std::list< const PPDValue* >& rValues )
2014 {
2015     rValues.clear();
2016 
2017     if( ! m_pParser || ! pKey || ! m_pParser->hasKey( pKey ) )
2018         return;
2019 
2020     int nValues = pKey->countValues();
2021     for( int i = 0; i < nValues; i++ )
2022     {
2023         const PPDValue* pValue = pKey->getValue( i );
2024         if( checkConstraints( pKey, pValue ) )
2025             rValues.push_back( pValue );
2026     }
2027 }
2028 
2029 
2030 // -------------------------------------------------------------------
2031 
2032 void* PPDContext::getStreamableBuffer( sal_uLong& rBytes ) const
2033 {
2034     rBytes = 0;
2035     if( ! m_aCurrentValues.size() )
2036         return NULL;
2037     hash_type::const_iterator it;
2038     for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it )
2039     {
2040         ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 );
2041         rBytes += aCopy.Len();
2042         rBytes += 1; // for ':'
2043         if( it->second )
2044         {
2045             aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 );
2046             rBytes += aCopy.Len();
2047         }
2048         else
2049             rBytes += 4;
2050         rBytes += 1; // for '\0'
2051     }
2052     rBytes += 1;
2053     void* pBuffer = new char[ rBytes ];
2054     memset( pBuffer, 0, rBytes );
2055     char* pRun = (char*)pBuffer;
2056     for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it )
2057     {
2058         ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 );
2059         int nBytes = aCopy.Len();
2060         memcpy( pRun, aCopy.GetBuffer(), nBytes );
2061         pRun += nBytes;
2062         *pRun++ = ':';
2063         if( it->second )
2064             aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 );
2065         else
2066             aCopy = "*nil";
2067         nBytes = aCopy.Len();
2068         memcpy( pRun, aCopy.GetBuffer(), nBytes );
2069         pRun += nBytes;
2070 
2071         *pRun++ = 0;
2072     }
2073     return pBuffer;
2074 }
2075 
2076 // -------------------------------------------------------------------
2077 
2078 void PPDContext::rebuildFromStreamBuffer( void* pBuffer, sal_uLong nBytes )
2079 {
2080     if( ! m_pParser )
2081         return;
2082 
2083     m_aCurrentValues.clear();
2084 
2085     char* pRun = (char*)pBuffer;
2086     while( nBytes && *pRun )
2087     {
2088         ByteString aLine( pRun );
2089         int nPos = aLine.Search( ':' );
2090         if( nPos != STRING_NOTFOUND )
2091         {
2092             const PPDKey* pKey = m_pParser->getKey( String( aLine.Copy( 0, nPos ), RTL_TEXTENCODING_MS_1252 ) );
2093             if( pKey )
2094             {
2095                 const PPDValue* pValue = NULL;
2096                 String aOption( aLine.Copy( nPos+1 ), RTL_TEXTENCODING_MS_1252 );
2097                 if( ! aOption.EqualsAscii( "*nil" ) )
2098                     pValue = pKey->getValue( aOption );
2099                 m_aCurrentValues[ pKey ] = pValue;
2100 #ifdef __DEBUG
2101                 fprintf( stderr, "PPDContext::rebuildFromStreamBuffer: read PPDKeyValue { %s, %s }\n", pKV->m_pKey->getKey().GetStr(), pKV->m_pCurrentValue ? pKV->m_pCurrentValue->m_aOption.GetStr() : "<nil>" );
2102 #endif
2103             }
2104         }
2105         nBytes -= aLine.Len()+1;
2106         pRun += aLine.Len()+1;
2107     }
2108 }
2109 
2110 // -------------------------------------------------------------------
2111 
2112 int PPDContext::getRenderResolution() const
2113 {
2114     // initialize to reasonable default, if parser is not set
2115     int nDPI = 300;
2116     if( m_pParser )
2117     {
2118         int nDPIx = 300, nDPIy = 300;
2119         const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) );
2120         if( pKey )
2121         {
2122             const PPDValue* pValue = getValue( pKey );
2123             if( pValue )
2124                 m_pParser->getResolutionFromString( pValue->m_aOption, nDPIx, nDPIy );
2125             else
2126                 m_pParser->getDefaultResolution( nDPIx, nDPIy );
2127         }
2128         else
2129             m_pParser->getDefaultResolution( nDPIx, nDPIy );
2130 
2131         nDPI = (nDPIx > nDPIy) ? nDPIx : nDPIy;
2132     }
2133     return  nDPI;
2134 }
2135 
2136 // -------------------------------------------------------------------
2137 
2138 void PPDContext::getPageSize( String& rPaper, int& rWidth, int& rHeight ) const
2139 {
2140     // initialize to reasonable default, if parser is not set
2141     rPaper  = String( RTL_CONSTASCII_USTRINGPARAM( "A4" ) );
2142     rWidth  = 595;
2143     rHeight = 842;
2144     if( m_pParser )
2145     {
2146         const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
2147         if( pKey )
2148         {
2149             const PPDValue* pValue = getValue( pKey );
2150             if( pValue )
2151             {
2152                 rPaper = pValue->m_aOption;
2153                 m_pParser->getPaperDimension( rPaper, rWidth, rHeight );
2154             }
2155             else
2156             {
2157                 rPaper = m_pParser->getDefaultPaperDimension();
2158                 m_pParser->getDefaultPaperDimension( rWidth, rHeight );
2159             }
2160         }
2161     }
2162 }
2163