File indexing completed on 2025-03-09 05:06:33

0001 /*
0002     SPDX-FileCopyrightText: 2010-2018 Daniel Nicoletti <dantti12@gmail.com>
0003 
0004     The save PPD snippet is from CUPS
0005     SPDX-FileCopyrightText: 2007-2009 Apple Inc.
0006     SPDX-FileCopyrightText: 1997-2007 Easy Software Products.
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009     This is converted from LGPL 2 in accordance with section 3
0010     See https://www.cups.org/doc/license.html
0011 */
0012 
0013 #include "PrinterOptions.h"
0014 
0015 #include "ui_PrinterOptions.h"
0016 
0017 #include "Debug.h"
0018 
0019 #include <KCupsRequest.h>
0020 #include <NoSelectionRectDelegate.h>
0021 
0022 #include <QButtonGroup>
0023 #include <QComboBox>
0024 #include <QFormLayout>
0025 #include <QGroupBox>
0026 #include <QListView>
0027 #include <QPointer>
0028 #include <QRadioButton>
0029 #include <QStandardItemModel>
0030 #include <QTextCodec>
0031 
0032 #include <ctype.h>
0033 
0034 #define DEFAULT_CHOICE "defaultChoice"
0035 
0036 PrinterOptions::PrinterOptions(const QString &destName, bool isClass, bool isRemote, QWidget *parent)
0037     : PrinterPage(parent)
0038     , ui(new Ui::PrinterOptions)
0039     , m_destName(destName)
0040     , m_isClass(isClass)
0041     , m_isRemote(isRemote)
0042 {
0043     ui->setupUi(this);
0044     connect(ui->autoConfigurePB, &QPushButton::clicked, this, &PrinterOptions::autoConfigureClicked);
0045 
0046     reloadPPD();
0047 }
0048 
0049 void PrinterOptions::autoConfigureClicked()
0050 {
0051     QPointer<KCupsRequest> request = new KCupsRequest;
0052     request->printCommand(m_destName, QLatin1String("AutoConfigure"), i18n("Set Default Options"));
0053     request->waitTillFinished();
0054     if (request) {
0055         request->deleteLater();
0056     }
0057 }
0058 
0059 void PrinterOptions::reloadPPD()
0060 {
0061     //     The caller "owns" the file that is created and must unlink the returned filename.
0062     if (!m_filename.isEmpty()) {
0063         unlink(qUtf8Printable(m_filename));
0064     }
0065 
0066     // remove all the options
0067     while (ui->verticalLayout->count()) {
0068         qCDebug(PM_CONFIGURE_PRINTER) << "removing" << ui->verticalLayout->count();
0069         QLayoutItem *item = ui->verticalLayout->itemAt(0);
0070         ui->verticalLayout->removeItem(item);
0071         if (item->widget()) {
0072             item->widget()->deleteLater();
0073             delete item;
0074         } else if (item->layout()) {
0075             qCDebug(PM_CONFIGURE_PRINTER) << "removing layout" << ui->verticalLayout->count();
0076 
0077             //            item->layout()->deleteLater();
0078         } else if (item->spacerItem()) {
0079             delete item->spacerItem();
0080         }
0081     }
0082     m_changes = 0;
0083     m_customValues.clear();
0084     Q_EMIT changed(false);
0085 
0086     QPointer<KCupsRequest> request = new KCupsRequest;
0087     request->getPrinterPPD(m_destName);
0088     request->waitTillFinished();
0089     if (!request) {
0090         return;
0091     }
0092     m_filename = request->printerPPD();
0093     m_ppd = ppdOpenFile(qUtf8Printable(m_filename));
0094     request->deleteLater();
0095     if (m_ppd == nullptr) {
0096         qCWarning(PM_CONFIGURE_PRINTER) << "Could not open ppd file:" << m_filename << request->errorMsg();
0097         m_filename.clear();
0098         return;
0099     }
0100 
0101     ppdLocalize(m_ppd);
0102     // select the default options on the ppd file
0103     ppdMarkDefaults(m_ppd);
0104 
0105     // TODO try to use QTextCodec aliases
0106     const char *lang_encoding;
0107     lang_encoding = m_ppd->lang_encoding;
0108     if (lang_encoding && !strcasecmp(lang_encoding, "ISOLatin1")) {
0109         m_codec = QTextCodec::codecForName("ISO-8859-1");
0110     } else if (lang_encoding && !strcasecmp(lang_encoding, "ISOLatin2")) {
0111         m_codec = QTextCodec::codecForName("ISO-8859-2");
0112     } else if (lang_encoding && !strcasecmp(lang_encoding, "ISOLatin5")) {
0113         m_codec = QTextCodec::codecForName("ISO-8859-5");
0114     } else if (lang_encoding && !strcasecmp(lang_encoding, "JIS83-RKSJ")) {
0115         m_codec = QTextCodec::codecForName("SHIFT-JIS");
0116     } else if (lang_encoding && !strcasecmp(lang_encoding, "MacStandard")) {
0117         m_codec = QTextCodec::codecForName("MACINTOSH");
0118     } else if (lang_encoding && !strcasecmp(lang_encoding, "WindowsANSI")) {
0119         m_codec = QTextCodec::codecForName("WINDOWS-1252");
0120     } else {
0121         // Guess
0122         m_codec = QTextCodec::codecForName(lang_encoding);
0123     }
0124     if (m_codec == nullptr) {
0125         m_codec = QTextCodec::codecForName("UTF-8");
0126     }
0127 
0128     if (m_ppd->manufacturer) {
0129         m_make = m_codec->toUnicode(m_ppd->manufacturer);
0130     }
0131 
0132     if (m_ppd->nickname) {
0133         m_makeAndModel = m_codec->toUnicode(m_ppd->nickname);
0134     }
0135 
0136     ui->autoConfigurePB->hide();
0137     ppd_attr_t *ppdattr;
0138     if (m_ppd->num_filters == 0
0139         || ((ppdattr = ppdFindAttr(m_ppd, "cupsCommands", nullptr)) != nullptr && ppdattr->value && strstr(ppdattr->value, "AutoConfigure"))) {
0140         ui->autoConfigurePB->show();
0141     } else {
0142         for (int i = 0; i < m_ppd->num_filters; i++) {
0143             if (!strncmp(m_ppd->filters[i], "application/vnd.cups-postscript", 31)) {
0144                 ui->autoConfigurePB->show();
0145                 break;
0146             }
0147         }
0148     }
0149 
0150     createGroups();
0151 }
0152 
0153 void PrinterOptions::createGroups()
0154 {
0155     int i;
0156     ppd_group_t *group;
0157     // Iterate over the groups
0158     for (i = 0, group = m_ppd->groups; i < m_ppd->num_groups; i++, group++) {
0159         // The name of the group
0160         QString name = m_codec->toUnicode(group->name);
0161 
0162         // The human name of the group
0163         QString text = m_codec->toUnicode(group->text);
0164 
0165         // The group box were the options will be laid out
0166         auto groupBox = new QGroupBox(text, ui->scrollArea);
0167 
0168         // Create the form layout to put options in
0169         auto gFormLayout = new QFormLayout(groupBox);
0170         gFormLayout->setFormAlignment(Qt::AlignCenter);
0171         groupBox->setLayout(gFormLayout);
0172         ui->verticalLayout->addWidget(groupBox);
0173 
0174         int j;
0175         ppd_option_t *option;
0176         // Iterate over the options in the group
0177         for (j = 0, option = group->options; j < group->num_options; j++, option++) {
0178             QString oKeyword = m_codec->toUnicode(option->keyword);
0179             QString oText = m_codec->toUnicode(option->text);
0180             QString oDefChoice = m_codec->toUnicode(option->defchoice);
0181             // The python system-config-printer skips this one
0182             // which has the same data as "PageSize", let's hope
0183             // they did the right thing
0184             if (oKeyword == QLatin1String("PageRegion")) {
0185                 continue;
0186             }
0187 
0188             QWidget *optionW = nullptr;
0189             switch (option->ui) {
0190             case PPD_UI_BOOLEAN:
0191                 optionW = pickBoolean(option, oKeyword, ui->scrollAreaWidgetContents);
0192                 break;
0193             case PPD_UI_PICKMANY:
0194                 optionW = pickMany(option, oKeyword, ui->scrollAreaWidgetContents);
0195                 break;
0196             case PPD_UI_PICKONE:
0197                 optionW = pickOne(option, oKeyword, ui->scrollAreaWidgetContents);
0198                 break;
0199             default:
0200                 qCWarning(PM_CONFIGURE_PRINTER) << "Option type not recognized: " << option->ui;
0201                 // let's use the most common
0202                 optionW = pickOne(option, oKeyword, ui->scrollAreaWidgetContents);
0203                 break;
0204             }
0205 
0206             if (optionW) {
0207                 // insert the option widget
0208                 gFormLayout->addRow(oText, optionW);
0209             }
0210         }
0211     }
0212     ui->verticalLayout->addStretch();
0213 }
0214 
0215 QWidget *PrinterOptions::pickBoolean(ppd_option_t *option, const QString &keyword, QWidget *parent) const
0216 {
0217     Q_UNUSED(keyword)
0218     auto widget = new QWidget(parent);
0219     auto layout = new QHBoxLayout(widget);
0220     auto radioGroup = new QButtonGroup(widget);
0221     widget->setLayout(layout);
0222 
0223     int i;
0224     ppd_choice_t *choice;
0225     QString defChoice = m_codec->toUnicode(option->defchoice);
0226     // Iterate over the choices in the option
0227     for (i = 0, choice = option->choices; i < option->num_choices; ++i, ++choice) {
0228         const QString choiceName = m_codec->toUnicode(choice->choice);
0229         const QString cText = m_codec->toUnicode(choice->text);
0230 
0231         auto button = new QRadioButton(cText, widget);
0232         button->setChecked(defChoice == choiceName);
0233         button->setProperty("choice", choiceName);
0234         // if we are in looking at a remote printer we can't save it
0235         button->setEnabled(!m_isRemote);
0236         layout->addWidget(button);
0237         radioGroup->addButton(button);
0238     }
0239 
0240     // store the default choice
0241     radioGroup->setProperty(DEFAULT_CHOICE, defChoice);
0242     radioGroup->setProperty("Keyword", keyword);
0243     connect(radioGroup, static_cast<void (QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked), this, &PrinterOptions::radioBtClicked);
0244     return widget;
0245 }
0246 
0247 void PrinterOptions::radioBtClicked(QAbstractButton *button)
0248 {
0249     QObject *radioGroup = sender();
0250     bool isDifferent = radioGroup->property(DEFAULT_CHOICE).toString() != button->property("choice");
0251 
0252     if (isDifferent != radioGroup->property("different").toBool()) {
0253         // it's different from the last time so add or remove changes
0254         isDifferent ? m_changes++ : m_changes--;
0255 
0256         radioGroup->setProperty("different", isDifferent);
0257         Q_EMIT changed(m_changes);
0258     }
0259 
0260     QString keyword = radioGroup->property("Keyword").toString();
0261     QString choice = button->property("choice").toString();
0262     radioGroup->setProperty("currentChoice", choice);
0263 
0264     // TODO warning about conflicts
0265     //     ppdMarkOption(m_ppd,
0266     //                   m_codec->fromUnicode(keyword),
0267     //                   m_codec->fromUnicode(choice));
0268     // store the new value
0269     if (isDifferent) {
0270         m_customValues[keyword] = radioGroup;
0271     } else {
0272         m_customValues.remove(keyword);
0273     }
0274 }
0275 
0276 QWidget *PrinterOptions::pickMany(ppd_option_t *option, const QString &keyword, QWidget *parent) const
0277 {
0278     Q_UNUSED(keyword)
0279     auto listView = new QListView(parent);
0280     auto model = new QStandardItemModel(listView);
0281     listView->setModel(model);
0282     listView->setItemDelegate(new NoSelectionRectDelegate(listView));
0283 
0284     int i;
0285     ppd_choice_t *choice;
0286     const QString oDefChoice = m_codec->toUnicode(option->defchoice);
0287     // Iterate over the choices in the option
0288     for (i = 0, choice = option->choices; i < option->num_choices; ++i, ++choice) {
0289         const QString cName = m_codec->toUnicode(choice->choice);
0290         const QString cText = m_codec->toUnicode(choice->text);
0291 
0292         auto item = new QStandardItem(cText);
0293         item->setData(cName);
0294         item->setCheckable(true);
0295         item->setEditable(false);
0296         // TODO there is only ONE default choice, what about the other
0297         // Items selected?!
0298         item->setCheckState(oDefChoice == cName ? Qt::Checked : Qt::Unchecked);
0299         model->appendRow(item);
0300     }
0301     // if we are in looking at a remote printer we can't save it
0302     listView->setEnabled(!m_isRemote);
0303     return qobject_cast<QWidget *>(listView);
0304 }
0305 
0306 QWidget *PrinterOptions::pickOne(ppd_option_t *option, const QString &keyword, QWidget *parent) const
0307 {
0308     int i;
0309     ppd_choice_t *choice;
0310     const QString defChoice = m_codec->toUnicode(option->defchoice);
0311     auto comboBox = new QComboBox(parent);
0312     // Iterate over the choices in the option
0313     for (i = 0, choice = option->choices; i < option->num_choices; ++i, ++choice) {
0314         const QString cName = m_codec->toUnicode(choice->choice);
0315         const QString cText = m_codec->toUnicode(choice->text);
0316 
0317         comboBox->addItem(cText, cName);
0318     }
0319     // store the default choice
0320     comboBox->setProperty(DEFAULT_CHOICE, defChoice);
0321     comboBox->setProperty("Keyword", keyword);
0322     comboBox->setCurrentIndex(comboBox->findData(defChoice));
0323     // connect the signal AFTER setCurrentIndex is called
0324     connect(comboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &PrinterOptions::currentIndexChangedCB);
0325     // if we are in looking at a remote printer we can't save it
0326     comboBox->setEnabled(!m_isRemote);
0327     return qobject_cast<QWidget *>(comboBox);
0328 }
0329 
0330 void PrinterOptions::currentIndexChangedCB(int index)
0331 {
0332     auto comboBox = qobject_cast<QComboBox *>(sender());
0333     bool isDifferent = comboBox->property(DEFAULT_CHOICE).toString() != comboBox->itemData(index);
0334 
0335     if (isDifferent != comboBox->property("different").toBool()) {
0336         // it's different from the last time so add or remove changes
0337         isDifferent ? m_changes++ : m_changes--;
0338 
0339         comboBox->setProperty("different", isDifferent);
0340         Q_EMIT changed(m_changes);
0341     }
0342 
0343     QString keyword = comboBox->property("Keyword").toString();
0344     QString value = comboBox->itemData(index).toString();
0345     comboBox->setProperty("currentChoice", value);
0346 
0347     // TODO warning about conflicts
0348     //     ppdMarkOption(m_ppd,
0349     //                   m_codec->fromUnicode(keyword),
0350     //                   m_codec->fromUnicode(value));
0351     // store the new value
0352     if (isDifferent) {
0353         m_customValues[keyword] = qobject_cast<QObject *>(comboBox);
0354     } else {
0355         m_customValues.remove(keyword);
0356     }
0357 }
0358 
0359 PrinterOptions::~PrinterOptions()
0360 {
0361     if (m_ppd != nullptr) {
0362         ppdClose(m_ppd);
0363     }
0364 
0365     if (!m_filename.isEmpty()) {
0366         unlink(qUtf8Printable(m_filename));
0367     }
0368 
0369     delete ui;
0370 }
0371 
0372 const char * /* O - Value of variable */
0373 PrinterOptions::getVariable(const char *name) const /* I - Name of variable */
0374 {
0375     const QString keyword = m_codec->toUnicode(name);
0376     auto it = m_customValues.constFind(keyword);
0377     if (it != m_customValues.constEnd()) {
0378         const QString value = it.value()->property("currentChoice").toString();
0379         return m_codec->fromUnicode(value).constData();
0380     } else {
0381         return nullptr;
0382     }
0383 }
0384 
0385 /*
0386  * 'get_points()' - Get a value in points.
0387  */
0388 double /* O - Number in points */
0389 PrinterOptions::get_points(double number, /* I - Original number */
0390                            const char *uval) /* I - Units */
0391 {
0392     if (!strcmp(uval, "mm")) /* Millimeters */
0393         return (number * 72.0 / 25.4);
0394     else if (!strcmp(uval, "cm")) /* Centimeters */
0395         return (number * 72.0 / 2.54);
0396     else if (!strcmp(uval, "in")) /* Inches */
0397         return (number * 72.0);
0398     else if (!strcmp(uval, "ft")) /* Feet */
0399         return (number * 72.0 * 12.0);
0400     else if (!strcmp(uval, "m")) /* Meters */
0401         return (number * 72.0 / 0.0254);
0402     else /* Points */
0403         return (number);
0404 }
0405 
0406 /*
0407  * 'get_option_value()' - Return the value of an option.
0408  *
0409  * This function also handles generation of custom option values.
0410  */
0411 
0412 char * /* O - Value string or nullptr on error */
0413 PrinterOptions::get_option_value(ppd_file_t *ppd, /* I - PPD file */
0414                                  const char *name, /* I - Option name */
0415                                  char *buffer, /* I - String buffer */
0416                                  size_t bufsize) const /* I - Size of buffer */
0417 {
0418     char *bufptr, /* Pointer into buffer */
0419         *bufend; /* End of buffer */
0420     ppd_coption_t *coption; /* Custom option */
0421     ppd_cparam_t *cparam; /* Current custom parameter */
0422     char keyword[256]; /* Parameter name */
0423     const char *val, /* Parameter value */
0424         *uval; /* Units value */
0425     long integer; /* Integer value */
0426     double number, /* Number value */
0427         number_points; /* Number in points */
0428 
0429     /*
0430      * See if we have a custom option choice...
0431      */
0432 
0433     if ((val = getVariable(name)) == nullptr) {
0434         /*
0435          * Option not found!
0436          */
0437 
0438         return (nullptr);
0439     } else if (strcasecmp(val, "Custom") || (coption = ppdFindCustomOption(ppd, name)) == nullptr) {
0440         /*
0441          * Not a custom choice...
0442          */
0443 
0444         qstrncpy(buffer, val, bufsize);
0445         return (buffer);
0446     }
0447 
0448     /*
0449      * OK, we have a custom option choice, format it...
0450      */
0451 
0452     *buffer = '\0';
0453 
0454     if (!strcmp(coption->keyword, "PageSize")) {
0455         const char *lval; /* Length string value */
0456         double width, /* Width value */
0457             width_points, /* Width in points */
0458             length, /* Length value */
0459             length_points; /* Length in points */
0460 
0461         val = getVariable("PageSize.Width");
0462         lval = getVariable("PageSize.Height");
0463         uval = getVariable("PageSize.Units");
0464 
0465         if (!val || !lval || !uval || (width = strtod(val, nullptr)) == 0.0 || (length = strtod(lval, nullptr)) == 0.0
0466             || (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) {
0467             return (nullptr);
0468         }
0469 
0470         width_points = get_points(width, uval);
0471         length_points = get_points(length, uval);
0472 
0473         if (width_points < ppd->custom_min[0] || width_points > ppd->custom_max[0] || length_points < ppd->custom_min[1]
0474             || length_points > ppd->custom_max[1]) {
0475             return (nullptr);
0476         }
0477 
0478         snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval);
0479     } else if (cupsArrayCount(coption->params) == 1) {
0480         cparam = ppdFirstCustomParam(coption);
0481         snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name);
0482 
0483         if ((val = getVariable(keyword)) == nullptr)
0484             return (nullptr);
0485 
0486         switch (cparam->type) {
0487         case PPD_CUSTOM_CURVE:
0488         case PPD_CUSTOM_INVCURVE:
0489         case PPD_CUSTOM_REAL:
0490             if ((number = strtod(val, nullptr)) == 0.0 || number < cparam->minimum.custom_real || number > cparam->maximum.custom_real)
0491                 return (nullptr);
0492 
0493             snprintf(buffer, bufsize, "Custom.%g", number);
0494             break;
0495         case PPD_CUSTOM_INT:
0496             if (!*val || (integer = strtol(val, nullptr, 10)) == LONG_MIN || integer == LONG_MAX || integer < cparam->minimum.custom_int
0497                 || integer > cparam->maximum.custom_int)
0498                 return (nullptr);
0499 
0500             snprintf(buffer, bufsize, "Custom.%ld", integer);
0501             break;
0502         case PPD_CUSTOM_POINTS:
0503             snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
0504 
0505             if ((number = strtod(val, nullptr)) == 0.0 || (uval = getVariable(keyword)) == nullptr
0506                 || (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
0507                 return (nullptr);
0508 
0509             number_points = get_points(number, uval);
0510             if (number_points < cparam->minimum.custom_points || number_points > cparam->maximum.custom_points)
0511                 return (nullptr);
0512 
0513             snprintf(buffer, bufsize, "Custom.%g%s", number, uval);
0514             break;
0515         case PPD_CUSTOM_PASSCODE:
0516             for (uval = val; *uval; ++uval) {
0517                 if (!isdigit(*uval & 255)) {
0518                     return (nullptr);
0519                 }
0520             }
0521         case PPD_CUSTOM_PASSWORD:
0522         case PPD_CUSTOM_STRING:
0523             integer = (long)strlen(val);
0524             if (integer < cparam->minimum.custom_string || integer > cparam->maximum.custom_string) {
0525                 return (nullptr);
0526             }
0527 
0528             snprintf(buffer, bufsize, "Custom.%s", val);
0529             break;
0530 #if (CUPS_VERSION_MAJOR >= 3) || (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR >= 3)                                                                          \
0531     || (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR == 2 && CUPS_VERSION_PATCH >= 12)
0532         case PPD_CUSTOM_UNKNOWN:
0533 #endif
0534         default:
0535             break;
0536         }
0537     } else {
0538         const char *prefix = "{"; /* Prefix string */
0539 
0540         bufptr = buffer;
0541         bufend = buffer + bufsize;
0542 
0543         for (cparam = ppdFirstCustomParam(coption); cparam; cparam = ppdNextCustomParam(coption)) {
0544             snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name);
0545 
0546             if ((val = getVariable(keyword)) == nullptr) {
0547                 return (nullptr);
0548             }
0549 
0550             snprintf(bufptr, bufend - bufptr, "%s%s=", prefix, cparam->name);
0551             bufptr += strlen(bufptr);
0552             prefix = " ";
0553 
0554             switch (cparam->type) {
0555             case PPD_CUSTOM_CURVE:
0556             case PPD_CUSTOM_INVCURVE:
0557             case PPD_CUSTOM_REAL:
0558                 if ((number = strtod(val, nullptr)) == 0.0 || number < cparam->minimum.custom_real || number > cparam->maximum.custom_real)
0559                     return (nullptr);
0560 
0561                 snprintf(bufptr, bufend - bufptr, "%g", number);
0562                 break;
0563             case PPD_CUSTOM_INT:
0564                 if (!*val || (integer = strtol(val, nullptr, 10)) == LONG_MIN || integer == LONG_MAX || integer < cparam->minimum.custom_int
0565                     || integer > cparam->maximum.custom_int) {
0566                     return (nullptr);
0567                 }
0568 
0569                 snprintf(bufptr, bufend - bufptr, "%ld", integer);
0570                 break;
0571             case PPD_CUSTOM_POINTS:
0572                 snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
0573 
0574                 if ((number = strtod(val, nullptr)) == 0.0 || (uval = getVariable(keyword)) == nullptr
0575                     || (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) {
0576                     return (nullptr);
0577                 }
0578 
0579                 number_points = get_points(number, uval);
0580                 if (number_points < cparam->minimum.custom_points || number_points > cparam->maximum.custom_points) {
0581                     return (nullptr);
0582                 }
0583 
0584                 snprintf(bufptr, bufend - bufptr, "%g%s", number, uval);
0585                 break;
0586 
0587             case PPD_CUSTOM_PASSCODE:
0588                 for (uval = val; *uval; uval++) {
0589                     if (!isdigit(*uval & 255)) {
0590                         return (nullptr);
0591                     }
0592                 }
0593             case PPD_CUSTOM_PASSWORD:
0594             case PPD_CUSTOM_STRING:
0595                 integer = (long)strlen(val);
0596                 if (integer < cparam->minimum.custom_string || integer > cparam->maximum.custom_string) {
0597                     return (nullptr);
0598                 }
0599 
0600                 if ((bufptr + 2) > bufend) {
0601                     return (nullptr);
0602                 }
0603 
0604                 bufend--;
0605                 *bufptr++ = '\"';
0606 
0607                 while (*val && bufptr < bufend) {
0608                     if (*val == '\\' || *val == '\"') {
0609                         if ((bufptr + 1) >= bufend) {
0610                             return (nullptr);
0611                         }
0612 
0613                         *bufptr++ = '\\';
0614                     }
0615 
0616                     *bufptr++ = *val++;
0617                 }
0618 
0619                 if (bufptr >= bufend) {
0620                     return (nullptr);
0621                 }
0622 
0623                 *bufptr++ = '\"';
0624                 *bufptr = '\0';
0625                 bufend++;
0626                 break;
0627 #if (CUPS_VERSION_MAJOR >= 3) || (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR >= 3)                                                                          \
0628     || (CUPS_VERSION_MAJOR == 2 && CUPS_VERSION_MINOR == 2 && CUPS_VERSION_PATCH >= 12)
0629             case PPD_CUSTOM_UNKNOWN:
0630 #endif
0631             default:
0632                 break;
0633             }
0634 
0635             bufptr += strlen(bufptr);
0636         }
0637 
0638         if (bufptr == buffer || (bufend - bufptr) < 2) {
0639             return (nullptr);
0640         }
0641 
0642         strcpy(bufptr, "}");
0643     }
0644 
0645     return (buffer);
0646 }
0647 
0648 void PrinterOptions::save()
0649 {
0650     char tempfile[1024];
0651     const char *var;
0652     cups_file_t *in, /* Input file */
0653         *out; /* Output file */
0654     char line[1024], /* Line from PPD file */
0655         value[1024], /* Option value */
0656         keyword[1024], /* Keyword from Default line */
0657         *keyptr; /* Pointer into keyword... */
0658 
0659     // copy cups-1.4.2/cgi-bin line 3779
0660     if (!m_filename.isEmpty()) {
0661         out = cupsTempFile2(tempfile, sizeof(tempfile));
0662         in = cupsFileOpen(qUtf8Printable(m_filename), "r");
0663 
0664         if (!in || !out) {
0665             if (in) {
0666                 cupsFileClose(in);
0667             }
0668 
0669             if (out) {
0670                 cupsFileClose(out);
0671                 unlink(tempfile);
0672             }
0673 
0674             // TODO add a KMessageBox::error
0675 
0676             return;
0677         }
0678 
0679         while (cupsFileGets(in, line, sizeof(line))) {
0680             if (!strncmp(line, "*cupsProtocol:", 14)) {
0681                 continue;
0682             } else if (strncmp(line, "*Default", 8)) {
0683                 cupsFilePrintf(out, "%s\n", line);
0684             } else {
0685                 /*
0686                  * Get default option name...
0687                  */
0688                 qstrncpy(keyword, line + 8, sizeof(keyword));
0689 
0690                 for (keyptr = keyword; *keyptr; keyptr++) {
0691                     if (*keyptr == ':' || isspace(*keyptr & 255)) {
0692                         break;
0693                     }
0694                 }
0695 
0696                 *keyptr = '\0';
0697 
0698                 if (!strcmp(keyword, "PageRegion") || !strcmp(keyword, "PaperDimension") || !strcmp(keyword, "ImageableArea")) {
0699                     var = get_option_value(m_ppd, "PageSize", value, sizeof(value));
0700                 } else {
0701                     var = get_option_value(m_ppd, keyword, value, sizeof(value));
0702                 }
0703 
0704                 if (!var) {
0705                     cupsFilePrintf(out, "%s\n", line);
0706                 } else {
0707                     cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
0708                 }
0709             }
0710         }
0711 
0712         cupsFileClose(in);
0713         cupsFileClose(out);
0714     } else {
0715         // TODO add a KMessageBox::error
0716         qCWarning(PM_CONFIGURE_PRINTER) << "No printer PPD file set, can't save options.";
0717 
0718         return;
0719     }
0720 
0721     QVariantMap values; // we need null values
0722     QPointer<KCupsRequest> request = new KCupsRequest;
0723     if (m_isClass) {
0724         request->addOrModifyClass(m_destName, values);
0725     } else {
0726         request->addOrModifyPrinter(m_destName, values, QString::fromUtf8(tempfile));
0727     }
0728 
0729     // Disable the widget till the request is processed
0730     // Otherwise the user might change something in the ui
0731     // which won't be saved but the apply but when the request
0732     // finishes we will set the current options as default
0733     setEnabled(false);
0734     request->waitTillFinished();
0735 
0736     // unlink the file
0737     unlink(tempfile);
0738 
0739     if (request) {
0740         setEnabled(true);
0741         if (!request->hasError()) {
0742             // if we succefully save the new ppd we need now to
0743             // clear our changes
0744             auto i = m_customValues.constBegin();
0745             while (i != m_customValues.constEnd()) {
0746                 QObject *obj = i.value();
0747                 const QString currentChoice = obj->property("currentChoice").toString();
0748                 // Store the current choice as the default one
0749                 obj->setProperty(DEFAULT_CHOICE, currentChoice);
0750                 obj->setProperty("currentChoice", QVariant());
0751                 obj->setProperty("different", false);
0752                 ++i;
0753             }
0754             m_changes = 0;
0755             m_customValues.clear();
0756             Q_EMIT changed(false);
0757         }
0758         request->deleteLater();
0759     }
0760 }
0761 
0762 bool PrinterOptions::hasChanges()
0763 {
0764     return m_changes;
0765 }
0766 
0767 QString PrinterOptions::currentMake() const
0768 {
0769     return m_make;
0770 }
0771 
0772 QString PrinterOptions::currentMakeAndModel() const
0773 {
0774     return m_makeAndModel;
0775 }
0776 
0777 #include "moc_PrinterOptions.cpp"