Replace 'i < len-1 && func(i+1)' by 'i+1 < len && func(i+1)'
[profile/ivi/qtbase.git] / src / gui / painting / qprinterinfo_unix.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qprinterinfo.h"
43 #include "qprinterinfo_p.h"
44
45 #include <qfile.h>
46 #include <qfileinfo.h>
47 #include <qdir.h>
48 #include <qprintdialog.h>
49 #include <qlibrary.h>
50 #include <qtextstream.h>
51
52 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
53 #  include <private/qcups_p.h>
54 #  include <cups/cups.h>
55 #  include <private/qpdf_p.h>
56 #endif
57
58 #include <private/qprinterinfo_unix_p.h>
59
60 QT_BEGIN_NAMESPACE
61
62 #ifndef QT_NO_PRINTER
63
64 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
65 // preserver names in ascending order for the binary search
66 static const struct NamedPaperSize {
67     const char *const name;
68     QPrinter::PaperSize size;
69 } named_sizes_map[QPrinter::NPageSize] = {
70     { "A0", QPrinter::A0 },
71     { "A1", QPrinter::A1 },
72     { "A2", QPrinter::A2 },
73     { "A3", QPrinter::A3 },
74     { "A4", QPrinter::A4 },
75     { "A5", QPrinter::A5 },
76     { "A6", QPrinter::A6 },
77     { "A7", QPrinter::A7 },
78     { "A8", QPrinter::A8 },
79     { "A9", QPrinter::A9 },
80     { "B0", QPrinter::B0 },
81     { "B1", QPrinter::B1 },
82     { "B10", QPrinter::B10 },
83     { "B2", QPrinter::B2 },
84     { "B4", QPrinter::B4 },
85     { "B5", QPrinter::B5 },
86     { "B6", QPrinter::B6 },
87     { "B7", QPrinter::B7 },
88     { "B8", QPrinter::B8 },
89     { "B9", QPrinter::B9 },
90     { "C5E", QPrinter::C5E },
91     { "Comm10E", QPrinter::Comm10E },
92     { "Custom", QPrinter::Custom },
93     { "DLE", QPrinter::DLE },
94     { "Executive", QPrinter::Executive },
95     { "Folio", QPrinter::Folio },
96     { "Ledger", QPrinter::Ledger },
97     { "Legal", QPrinter::Legal },
98     { "Letter", QPrinter::Letter },
99     { "Tabloid", QPrinter::Tabloid }
100 };
101
102 inline bool operator<(const char *name, const NamedPaperSize &data)
103 { return qstrcmp(name, data.name) < 0; }
104 inline bool operator<(const NamedPaperSize &data, const char *name)
105 { return qstrcmp(data.name, name) < 0; }
106
107 static inline QPrinter::PaperSize string2PaperSize(const char *name)
108 {
109     const NamedPaperSize *r = qBinaryFind(named_sizes_map, named_sizes_map + QPrinter::NPageSize, name);
110     if (r - named_sizes_map != QPrinter::NPageSize)
111         return r->size;
112     return QPrinter::Custom;
113 }
114
115 static inline const char *paperSize2String(QPrinter::PaperSize size)
116 {
117     for (int i = 0; i < QPrinter::NPageSize; ++i) {
118         if (size == named_sizes_map[i].size)
119             return named_sizes_map[i].name;
120     }
121     return 0;
122 }
123 #endif
124
125 void qt_perhapsAddPrinter(QList<QPrinterDescription> *printers, const QString &name,
126                                QString host, QString comment,
127                                QStringList aliases)
128 {
129     for (int i = 0; i < printers->size(); ++i)
130         if (printers->at(i).samePrinter(name))
131             return;
132
133 #ifndef QT_NO_PRINTDIALOG
134     if (host.isEmpty())
135         host = QPrintDialog::tr("locally connected");
136 #endif
137     printers->append(QPrinterDescription(name.simplified(), host.simplified(), comment.simplified(), aliases));
138 }
139
140 void qt_parsePrinterDesc(QString printerDesc, QList<QPrinterDescription> *printers)
141 {
142     if (printerDesc.length() < 1)
143         return;
144
145     printerDesc = printerDesc.simplified();
146     int i = printerDesc.indexOf(QLatin1Char(':'));
147     QString printerName, printerComment, printerHost;
148     QStringList aliases;
149
150     if (i >= 0) {
151         // have ':' want '|'
152         int j = printerDesc.indexOf(QLatin1Char('|'));
153         if (j > 0 && j < i) {
154             printerName = printerDesc.left(j);
155             aliases = printerDesc.mid(j + 1, i - j - 1).split(QLatin1Char('|'));
156 #ifndef QT_NO_PRINTDIALOG
157             // try extracting a comment from the aliases
158             printerComment = QPrintDialog::tr("Aliases: %1")
159                              .arg(aliases.join(QLatin1String(", ")));
160 #endif
161         } else {
162             printerName = printerDesc.left(i);
163         }
164         // look for lprng pseudo all printers entry
165         i = printerDesc.indexOf(QRegExp(QLatin1String(": *all *=")));
166         if (i >= 0)
167             printerName = QString();
168         // look for signs of this being a remote printer
169         i = printerDesc.indexOf(QRegExp(QLatin1String(": *rm *=")));
170         if (i >= 0) {
171             // point k at the end of remote host name
172             while (printerDesc[i] != QLatin1Char('='))
173                 i++;
174             while (printerDesc[i] == QLatin1Char('=') || printerDesc[i].isSpace())
175                 i++;
176             j = i;
177             while (j < (int)printerDesc.length() && printerDesc[j] != QLatin1Char(':'))
178                 j++;
179
180             // and stuff that into the string
181             printerHost = printerDesc.mid(i, j - i);
182         }
183     }
184     if (printerName.length())
185         qt_perhapsAddPrinter(printers, printerName, printerHost, printerComment,
186                              aliases);
187 }
188
189 int qt_parsePrintcap(QList<QPrinterDescription> *printers, const QString& fileName)
190 {
191     QFile printcap(fileName);
192     if (!printcap.open(QIODevice::ReadOnly))
193         return NotFound;
194
195     char *line_ascii = new char[1025];
196     line_ascii[1024] = '\0';
197
198     QString printerDesc;
199     bool atEnd = false;
200
201     while (!atEnd) {
202         if (printcap.atEnd() || printcap.readLine(line_ascii, 1024) <= 0)
203             atEnd = true;
204         QString line = QString::fromLocal8Bit(line_ascii);
205         line = line.trimmed();
206         if (line.length() >= 1 && line[int(line.length()) - 1] == QLatin1Char('\\'))
207             line.chop(1);
208         if (line[0] == QLatin1Char('#')) {
209             if (!atEnd)
210                 continue;
211         } else if (line[0] == QLatin1Char('|') || line[0] == QLatin1Char(':')
212                 || line.isEmpty()) {
213             printerDesc += line;
214             if (!atEnd)
215                 continue;
216         }
217
218         qt_parsePrinterDesc(printerDesc, printers);
219
220         // add the first line of the new printer definition
221         printerDesc = line;
222     }
223     delete[] line_ascii;
224     return Success;
225 }
226
227 /*!
228   \internal
229
230   Checks $HOME/.printers for a line matching '_default <name>' (where
231   <name> does not contain any white space). The first such match
232   results in <name> being returned.
233   If no lines match then an empty string is returned.
234 */
235 QString qt_getDefaultFromHomePrinters()
236 {
237     QFile file(QDir::homePath() + QLatin1String("/.printers"));
238     if (!file.open(QIODevice::ReadOnly))
239         return QString();
240     QString all(QLatin1String(file.readAll()));
241     QStringList words = all.split(QRegExp(QLatin1String("\\W+")), QString::SkipEmptyParts);
242     const int i = words.indexOf(QLatin1String("_default"));
243     if (i != -1 && i < words.size() - 1)
244         return words.at(i + 1);
245     return QString();
246 }
247
248 // solaris, not 2.6
249 void qt_parseEtcLpPrinters(QList<QPrinterDescription> *printers)
250 {
251     QDir lp(QLatin1String("/etc/lp/printers"));
252     QFileInfoList dirs = lp.entryInfoList();
253     if (dirs.isEmpty())
254         return;
255
256     QString tmp;
257     for (int i = 0; i < dirs.size(); ++i) {
258         QFileInfo printer = dirs.at(i);
259         if (printer.isDir()) {
260             tmp.sprintf("/etc/lp/printers/%s/configuration",
261                          printer.fileName().toAscii().data());
262             QFile configuration(tmp);
263             char *line = new char[1025];
264             QString remote(QLatin1String("Remote:"));
265             QString contentType(QLatin1String("Content types:"));
266             QString printerHost;
267             bool canPrintPostscript = false;
268             if (configuration.open(QIODevice::ReadOnly)) {
269                 while (!configuration.atEnd() &&
270                         configuration.readLine(line, 1024) > 0) {
271                     if (QString::fromLatin1(line).startsWith(remote)) {
272                         const char *p = line;
273                         while (*p != ':')
274                             p++;
275                         p++;
276                         while (isspace((uchar) *p))
277                             p++;
278                         printerHost = QString::fromLocal8Bit(p);
279                         printerHost = printerHost.simplified();
280                     } else if (QString::fromLatin1(line).startsWith(contentType)) {
281                         char *p = line;
282                         while (*p != ':')
283                             p++;
284                         p++;
285                         char *e;
286                         while (*p) {
287                             while (isspace((uchar) *p))
288                                 p++;
289                             if (*p) {
290                                 char s;
291                                 e = p;
292                                 while (isalnum((uchar) *e))
293                                     e++;
294                                 s = *e;
295                                 *e = '\0';
296                                 if (!qstrcmp(p, "postscript") ||
297                                      !qstrcmp(p, "any"))
298                                     canPrintPostscript = true;
299                                 *e = s;
300                                 if (s == ',')
301                                     e++;
302                                 p = e;
303                             }
304                         }
305                     }
306                 }
307                 if (canPrintPostscript)
308                     qt_perhapsAddPrinter(printers, printer.fileName(),
309                                          printerHost, QLatin1String(""));
310             }
311             delete[] line;
312         }
313     }
314 }
315
316 // solaris 2.6
317 char *qt_parsePrintersConf(QList<QPrinterDescription> *printers, bool *found)
318 {
319     QFile pc(QLatin1String("/etc/printers.conf"));
320     if (!pc.open(QIODevice::ReadOnly)) {
321         if (found)
322             *found = false;
323         return 0;
324     }
325     if (found)
326         *found = true;
327
328     char *line = new char[1025];
329     line[1024] = '\0';
330
331     QString printerDesc;
332     int lineLength = 0;
333
334     char *defaultPrinter = 0;
335
336     while (!pc.atEnd() &&
337             (lineLength=pc.readLine(line, 1024)) > 0) {
338         if (*line == '#') {
339             *line = '\0';
340             lineLength = 0;
341         }
342         if (lineLength >= 2 && line[lineLength-2] == '\\') {
343             line[lineLength-2] = '\0';
344             printerDesc += QString::fromLocal8Bit(line);
345         } else {
346             printerDesc += QString::fromLocal8Bit(line);
347             printerDesc = printerDesc.simplified();
348             int i = printerDesc.indexOf(QLatin1Char(':'));
349             QString printerName, printerHost, printerComment;
350             QStringList aliases;
351             if (i >= 0) {
352                 // have : want |
353                 int j = printerDesc.indexOf(QLatin1Char('|'));
354                 if (j >= i)
355                     j = -1;
356                 printerName = printerDesc.mid(0, j < 0 ? i : j);
357                 if (printerName == QLatin1String("_default")) {
358                     i = printerDesc.indexOf(
359                         QRegExp(QLatin1String(": *use *=")));
360                     while (printerDesc[i] != QLatin1Char('='))
361                         i++;
362                     while (printerDesc[i] == QLatin1Char('=') || printerDesc[i].isSpace())
363                         i++;
364                     j = i;
365                     while (j < (int)printerDesc.length() &&
366                             printerDesc[j] != QLatin1Char(':') && printerDesc[j] != QLatin1Char(','))
367                         j++;
368                     // that's our default printer
369                     defaultPrinter =
370                         qstrdup(printerDesc.mid(i, j-i).toAscii().data());
371                     printerName = QString();
372                     printerDesc = QString();
373                 } else if (printerName == QLatin1String("_all")) {
374                     // skip it.. any other cases we want to skip?
375                     printerName = QString();
376                     printerDesc = QString();
377                 }
378
379                 if (j > 0) {
380                     // try extracting a comment from the aliases
381                     aliases = printerDesc.mid(j + 1, i - j - 1).split(QLatin1Char('|'));
382 #ifndef QT_NO_PRINTDIALOG
383                     printerComment = QPrintDialog::tr("Aliases: %1")
384                                      .arg(aliases.join(QLatin1String(", ")));
385 #endif
386                 }
387                 // look for signs of this being a remote printer
388                 i = printerDesc.indexOf(
389                     QRegExp(QLatin1String(": *bsdaddr *=")));
390                 if (i >= 0) {
391                     // point k at the end of remote host name
392                     while (printerDesc[i] != QLatin1Char('='))
393                         i++;
394                     while (printerDesc[i] == QLatin1Char('=') || printerDesc[i].isSpace())
395                         i++;
396                     j = i;
397                     while (j < (int)printerDesc.length() &&
398                             printerDesc[j] != QLatin1Char(':') && printerDesc[j] != QLatin1Char(','))
399                         j++;
400                     // and stuff that into the string
401                     printerHost = printerDesc.mid(i, j-i);
402                     // maybe stick the remote printer name into the comment
403                     if (printerDesc[j] == QLatin1Char(',')) {
404                         i = ++j;
405                         while (printerDesc[i].isSpace())
406                             i++;
407                         j = i;
408                         while (j < (int)printerDesc.length() &&
409                                 printerDesc[j] != QLatin1Char(':') && printerDesc[j] != QLatin1Char(','))
410                             j++;
411                         if (printerName != printerDesc.mid(i, j-i)) {
412                             printerComment =
413                                 QLatin1String("Remote name: ");
414                             printerComment += printerDesc.mid(i, j-i);
415                         }
416                     }
417                 }
418             }
419             if (printerComment == QLatin1String(":"))
420                 printerComment = QString(); // for cups
421             if (printerName.length())
422                 qt_perhapsAddPrinter(printers, printerName, printerHost,
423                                      printerComment, aliases);
424             // chop away the line, for processing the next one
425             printerDesc = QString();
426         }
427     }
428     delete[] line;
429     return defaultPrinter;
430 }
431
432 #ifndef QT_NO_NIS
433
434 #if defined(Q_C_CALLBACKS)
435 extern "C" {
436 #endif
437
438 int qt_pd_foreach(int /*status */, char * /*key */, int /*keyLen */,
439                     char *val, int valLen, char *data)
440 {
441     qt_parsePrinterDesc(QString::fromLatin1(val, valLen), (QList<QPrinterDescription> *)data);
442     return 0;
443 }
444
445 #if defined(Q_C_CALLBACKS)
446 }
447 #endif
448
449 int qt_retrieveNisPrinters(QList<QPrinterDescription> *printers)
450 {
451 #ifndef QT_NO_LIBRARY
452     typedef int (*WildCast)(int, char *, int, char *, int, char *);
453     char printersConfByname[] = "printers.conf.byname";
454     char *domain;
455     int err;
456
457     QLibrary lib(QLatin1String("nsl"));
458     typedef int (*ypGetDefaultDomain)(char **);
459     ypGetDefaultDomain _ypGetDefaultDomain = (ypGetDefaultDomain)lib.resolve("yp_get_default_domain");
460     typedef int (*ypAll)(const char *, const char *, const struct ypall_callback *);
461     ypAll _ypAll = (ypAll)lib.resolve("yp_all");
462
463     if (_ypGetDefaultDomain && _ypAll) {
464         err = _ypGetDefaultDomain(&domain);
465         if (err == 0) {
466             ypall_callback cb;
467             // wild cast to support K&R-style system headers
468             (WildCast &) cb.foreach = (WildCast) qt_pd_foreach;
469             cb.data = (char *) printers;
470             err = _ypAll(domain, printersConfByname, &cb);
471         }
472         if (!err)
473             return Success;
474     }
475 #endif //QT_NO_LIBRARY
476     return Unavail;
477 }
478
479 #endif // QT_NO_NIS
480
481 char *qt_parseNsswitchPrintersEntry(QList<QPrinterDescription> *printers, char *line)
482 {
483 #define skipSpaces() \
484     while (line[k] != '\0' && isspace((uchar) line[k])) \
485         k++
486
487     char *defaultPrinter = 0;
488     bool stop = false;
489     int lastStatus = NotFound;
490
491     int k = 8;
492     skipSpaces();
493     if (line[k] != ':')
494         return 0;
495     k++;
496
497     char *cp = strchr(line, '#');
498     if (cp != 0)
499         *cp = '\0';
500
501     while (line[k] != '\0') {
502         if (isspace((uchar) line[k])) {
503             k++;
504         } else if (line[k] == '[') {
505             k++;
506             skipSpaces();
507             while (line[k] != '\0') {
508                 char status = tolower(line[k]);
509                 char action = '?';
510
511                 while (line[k] != '=' && line[k] != ']' && line[k] != '\0')
512                     k++;
513                 if (line[k] == '=') {
514                     k++;
515                     skipSpaces();
516                     action = tolower(line[k]);
517                     while (line[k] != '\0' && !isspace((uchar) line[k]) && line[k] != ']')
518                         k++;
519                 } else if (line[k] == ']') {
520                     k++;
521                     break;
522                 }
523                 skipSpaces();
524
525                 if (lastStatus == status)
526                     stop = (action == (char) Return);
527             }
528         } else {
529             if (stop)
530                 break;
531
532             QByteArray source;
533             while (line[k] != '\0' && !isspace((uchar) line[k]) && line[k] != '[') {
534                 source += line[k];
535                 k++;
536             }
537
538             if (source == "user") {
539                 lastStatus = qt_parsePrintcap(printers,
540                         QDir::homePath() + QLatin1String("/.printers"));
541             } else if (source == "files") {
542                 bool found;
543                 defaultPrinter = qt_parsePrintersConf(printers, &found);
544                 if (found)
545                     lastStatus = Success;
546 #ifndef QT_NO_NIS
547             } else if (source == "nis") {
548                 lastStatus = qt_retrieveNisPrinters(printers);
549 #endif
550             } else {
551                 // nisplus, dns, etc., are not implemented yet
552                 lastStatus = NotFound;
553             }
554             stop = (lastStatus == Success);
555         }
556     }
557     return defaultPrinter;
558 }
559
560 char *qt_parseNsswitchConf(QList<QPrinterDescription> *printers)
561 {
562     QFile nc(QLatin1String("/etc/nsswitch.conf"));
563     if (!nc.open(QIODevice::ReadOnly))
564         return 0;
565
566     char *defaultPrinter = 0;
567
568     char *line = new char[1025];
569     line[1024] = '\0';
570
571     while (!nc.atEnd() &&
572             nc.readLine(line, 1024) > 0) {
573         if (qstrncmp(line, "printers", 8) == 0) {
574             defaultPrinter = qt_parseNsswitchPrintersEntry(printers, line);
575             delete[] line;
576             return defaultPrinter;
577         }
578     }
579
580     strcpy(line, "printers: user files nis nisplus xfn");
581     defaultPrinter = qt_parseNsswitchPrintersEntry(printers, line);
582     delete[] line;
583     return defaultPrinter;
584 }
585
586 // HP-UX
587 void qt_parseEtcLpMember(QList<QPrinterDescription> *printers)
588 {
589     QDir lp(QLatin1String("/etc/lp/member"));
590     if (!lp.exists())
591         return;
592     QFileInfoList dirs = lp.entryInfoList();
593     if (dirs.isEmpty())
594         return;
595
596 #ifdef QT_NO_PRINTDIALOG
597     Q_UNUSED(printers);
598 #else
599     QString tmp;
600     for (int i = 0; i < dirs.size(); ++i) {
601         QFileInfo printer = dirs.at(i);
602         // I haven't found any real documentation, so I'm guessing that
603         // since lpstat uses /etc/lp/member rather than one of the
604         // other directories, it's the one to use.  I did not find a
605         // decent way to locate aliases and remote printers.
606         if (printer.isFile())
607             qt_perhapsAddPrinter(printers, printer.fileName(),
608                                  QPrintDialog::tr("unknown"),
609                                  QLatin1String(""));
610     }
611 #endif
612 }
613
614 // IRIX 6.x
615 void qt_parseSpoolInterface(QList<QPrinterDescription> *printers)
616 {
617     QDir lp(QLatin1String("/usr/spool/lp/interface"));
618     if (!lp.exists())
619         return;
620     QFileInfoList files = lp.entryInfoList();
621     if(files.isEmpty())
622         return;
623
624     for (int i = 0; i < files.size(); ++i) {
625         QFileInfo printer = files.at(i);
626
627         if (!printer.isFile())
628             continue;
629
630         // parse out some information
631         QFile configFile(printer.filePath());
632         if (!configFile.open(QIODevice::ReadOnly))
633             continue;
634
635         QByteArray line;
636         line.resize(1025);
637         QString namePrinter;
638         QString hostName;
639         QString hostPrinter;
640         QString printerType;
641
642         QString nameKey(QLatin1String("NAME="));
643         QString typeKey(QLatin1String("TYPE="));
644         QString hostKey(QLatin1String("HOSTNAME="));
645         QString hostPrinterKey(QLatin1String("HOSTPRINTER="));
646
647         while (!configFile.atEnd() &&
648                 (configFile.readLine(line.data(), 1024)) > 0) {
649             QString uline = QString::fromLocal8Bit(line);
650             if (uline.startsWith(typeKey) ) {
651                 printerType = uline.mid(nameKey.length());
652                 printerType = printerType.simplified();
653             } else if (uline.startsWith(hostKey)) {
654                 hostName = uline.mid(hostKey.length());
655                 hostName = hostName.simplified();
656             } else if (uline.startsWith(hostPrinterKey)) {
657                 hostPrinter = uline.mid(hostPrinterKey.length());
658                 hostPrinter = hostPrinter.simplified();
659             } else if (uline.startsWith(nameKey)) {
660                 namePrinter = uline.mid(nameKey.length());
661                 namePrinter = namePrinter.simplified();
662             }
663         }
664         configFile.close();
665
666         printerType = printerType.trimmed();
667         if (printerType.indexOf(QLatin1String("postscript"), 0, Qt::CaseInsensitive) < 0)
668             continue;
669
670         int ii = 0;
671         while ((ii = namePrinter.indexOf(QLatin1Char('"'), ii)) >= 0)
672             namePrinter.remove(ii, 1);
673
674         if (hostName.isEmpty() || hostPrinter.isEmpty()) {
675             qt_perhapsAddPrinter(printers, printer.fileName(),
676                                  QLatin1String(""), namePrinter);
677         } else {
678             QString comment;
679             comment = namePrinter;
680             comment += QLatin1String(" (");
681             comment += hostPrinter;
682             comment += QLatin1Char(')');
683             qt_perhapsAddPrinter(printers, printer.fileName(),
684                                  hostName, comment);
685         }
686     }
687 }
688
689
690 // Every unix must have its own.  It's a standard.  Here is AIX.
691 void qt_parseQconfig(QList<QPrinterDescription> *printers)
692 {
693     QFile qconfig(QLatin1String("/etc/qconfig"));
694     if (!qconfig.open(QIODevice::ReadOnly))
695         return;
696
697     QTextStream ts(&qconfig);
698     QString line;
699
700     QString stanzaName; // either a queue or a device name
701     bool up = true; // queue up?  default true, can be false
702     QString remoteHost; // null if local
703     QString deviceName; // null if remote
704
705     QRegExp newStanza(QLatin1String("^[0-z\\-]*:$"));
706
707     // our basic strategy here is to process each line, detecting new
708     // stanzas.  each time we see a new stanza, we check if the
709     // previous stanza was a valid queue for a) a remote printer or b)
710     // a local printer.  if it wasn't, we assume that what we see is
711     // the start of the first stanza, or that the previous stanza was
712     // a device stanza, or that there is some syntax error (we don't
713     // report those).
714
715     do {
716         line = ts.readLine();
717         bool indented = line[0].isSpace();
718         line = line.simplified();
719
720         int i = line.indexOf(QLatin1Char('='));
721         if (indented && i != -1) { // line in stanza
722             QString variable = line.left(i).simplified();
723             QString value=line.mid(i+1, line.length()).simplified();
724             if (variable == QLatin1String("device"))
725                 deviceName = value;
726             else if (variable == QLatin1String("host"))
727                 remoteHost = value;
728             else if (variable == QLatin1String("up"))
729                 up = !(value.toLower() == QLatin1String("false"));
730         } else if (line[0] == QLatin1Char('*')) { // comment
731             // nothing to do
732         } else if (ts.atEnd() || // end of file, or beginning of new stanza
733                     (!indented && line.contains(newStanza))) {
734             if (up && stanzaName.length() > 0 && stanzaName.length() < 21) {
735                 if (remoteHost.length()) // remote printer
736                     qt_perhapsAddPrinter(printers, stanzaName, remoteHost,
737                                          QString());
738                 else if (deviceName.length()) // local printer
739                     qt_perhapsAddPrinter(printers, stanzaName, QString(),
740                                          QString());
741             }
742             line.chop(1);
743             if (line.length() >= 1 && line.length() <= 20)
744                 stanzaName = line;
745             up = true;
746             remoteHost.clear();
747             deviceName.clear();
748         } else {
749             // syntax error?  ignore.
750         }
751     } while (!ts.atEnd());
752 }
753
754 int qt_getLprPrinters(QList<QPrinterDescription>& printers)
755 {
756     QByteArray etcLpDefault;
757     qt_parsePrintcap(&printers, QLatin1String("/etc/printcap"));
758     qt_parseEtcLpMember(&printers);
759     qt_parseSpoolInterface(&printers);
760     qt_parseQconfig(&printers);
761
762     QFileInfo f;
763     f.setFile(QLatin1String("/etc/lp/printers"));
764     if (f.isDir()) {
765         qt_parseEtcLpPrinters(&printers);
766         QFile def(QLatin1String("/etc/lp/default"));
767         if (def.open(QIODevice::ReadOnly)) {
768             etcLpDefault.resize(1025);
769             if (def.readLine(etcLpDefault.data(), 1024) > 0) {
770                 QRegExp rx(QLatin1String("^(\\S+)"));
771                 if (rx.indexIn(QString::fromLatin1(etcLpDefault)) != -1)
772                     etcLpDefault = rx.cap(1).toAscii();
773             }
774         }
775     }
776
777     char *def = 0;
778     f.setFile(QLatin1String("/etc/nsswitch.conf"));
779     if (f.isFile()) {
780         def = qt_parseNsswitchConf(&printers);
781     } else {
782         f.setFile(QLatin1String("/etc/printers.conf"));
783         if (f.isFile())
784             def = qt_parsePrintersConf(&printers);
785     }
786
787     if (def) {
788         etcLpDefault = def;
789         delete [] def;
790     }
791
792     QString homePrintersDefault = qt_getDefaultFromHomePrinters();
793
794     // all printers hopefully known.  try to find a good default
795     QString dollarPrinter;
796     {
797         dollarPrinter = QString::fromLocal8Bit(qgetenv("PRINTER"));
798         if (dollarPrinter.isEmpty())
799             dollarPrinter = QString::fromLocal8Bit(qgetenv("LPDEST"));
800         if (dollarPrinter.isEmpty())
801             dollarPrinter = QString::fromLocal8Bit(qgetenv("NPRINTER"));
802         if (dollarPrinter.isEmpty())
803             dollarPrinter = QString::fromLocal8Bit(qgetenv("NGPRINTER"));
804 #ifndef QT_NO_PRINTDIALOG
805         if (!dollarPrinter.isEmpty())
806             qt_perhapsAddPrinter(&printers, dollarPrinter,
807                                  QPrintDialog::tr("unknown"),
808                                  QLatin1String(""));
809 #endif
810     }
811
812     QRegExp ps(QLatin1String("[^a-z]ps(?:[^a-z]|$)"));
813     QRegExp lp(QLatin1String("[^a-z]lp(?:[^a-z]|$)"));
814
815     int quality = 0;
816     int best = 0;
817     for (int i = 0; i < printers.size(); ++i) {
818         QString name = printers.at(i).name;
819         QString comment = printers.at(i).comment;
820         if (quality < 5 && name == dollarPrinter) {
821             best = i;
822             quality = 5;
823         } else if (quality < 4 && !homePrintersDefault.isEmpty() &&
824                    name == homePrintersDefault) {
825             best = i;
826             quality = 4;
827         } else if (quality < 3 && !etcLpDefault.isEmpty() &&
828                     name == QLatin1String(etcLpDefault)) {
829             best = i;
830             quality = 3;
831         } else if (quality < 2 &&
832                     (name == QLatin1String("ps") ||
833                      ps.indexIn(comment) != -1)) {
834             best = i;
835             quality = 2;
836         } else if (quality < 1 &&
837                     (name == QLatin1String("lp") ||
838                      lp.indexIn(comment) > -1)) {
839             best = i;
840             quality = 1;
841         }
842     }
843
844     return best;
845 }
846
847 /////////////////////////////////////////////////////////////////////////////
848 /////////////////////////////////////////////////////////////////////////////
849
850 QList<QPrinterInfo> QPrinterInfo::availablePrinters()
851 {
852     QList<QPrinterInfo> printers;
853
854 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
855     if (QCUPSSupport::isAvailable()) {
856         QCUPSSupport cups;
857         int cupsPrinterCount = cups.availablePrintersCount();
858         const cups_dest_t* cupsPrinters = cups.availablePrinters();
859         for (int i = 0; i < cupsPrinterCount; ++i) {
860             QString printerName(QString::fromLocal8Bit(cupsPrinters[i].name));
861             if (cupsPrinters[i].instance)
862                 printerName += QLatin1Char('/') + QString::fromLocal8Bit(cupsPrinters[i].instance);
863
864             QPrinterInfo printerInfo(printerName);
865             if (cupsPrinters[i].is_default)
866                 printerInfo.d_ptr->isDefault = true;
867             printerInfo.d_ptr->cupsPrinterIndex = i;
868             printers.append(printerInfo);
869         }
870     } else
871 #endif
872            {
873         QList<QPrinterDescription> lprPrinters;
874         int defprn = qt_getLprPrinters(lprPrinters);
875         // populating printer combo
876         foreach (const QPrinterDescription &description, lprPrinters)
877             printers.append(QPrinterInfo(description.name));
878         if (defprn >= 0 && defprn < printers.size())
879             printers[defprn].d_ptr->isDefault = true;
880     }
881
882     return printers;
883 }
884
885 QPrinterInfo QPrinterInfo::defaultPrinter()
886 {
887     QList<QPrinterInfo> printers = availablePrinters();
888     foreach (const QPrinterInfo &printerInfo, printers) {
889         if (printerInfo.isDefault())
890             return printerInfo;
891     }
892
893     return printers.value(0);
894 }
895
896 QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
897 {
898 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
899     const Q_D(QPrinterInfo);
900
901     if (isNull())
902         return d->paperSizes;
903
904     if (!d->hasPaperSizes) {
905         d->hasPaperSizes = true;
906
907         if (QCUPSSupport::isAvailable()) {
908             // Find paper sizes from CUPS.
909             QCUPSSupport cups;
910             cups.setCurrentPrinter(d->cupsPrinterIndex);
911             const ppd_option_t* sizes = cups.pageSizes();
912             if (sizes) {
913                 for (int j = 0; j < sizes->num_choices; ++j)
914                     d->paperSizes.append(string2PaperSize(sizes->choices[j].choice));
915             }
916         }
917     }
918
919     return d->paperSizes;
920 #else
921     return QList<QPrinter::PaperSize>();
922 #endif
923 }
924
925 #endif // QT_NO_PRINTER
926
927 QT_END_NAMESPACE