File indexing completed on 2024-04-14 05:38:46

0001 /***************************************************************************
0002  *   Copyright (C) 2004 by Juanjo Álvarez Martinez <juanjo@juanjoalvarez.net> *
0003  *   Copyright (C) 2004 by Mario Bensi <nef@ipsquad.net>                   *
0004  *   Copyright (C) 2004, 2008-2009 by Pino Toscano <pino@kde.org>          *
0005  *                                                                         *
0006  *   This program is free software; you can redistribute it and/or modify  *
0007  *   it under the terms of the GNU General Public License as published by  *
0008  *   the Free Software Foundation; either version 2 of the License, or     *
0009  *   (at your option) any later version.                                   *
0010  *                                                                         *
0011  *   This program is distributed in the hope that it will be useful,       *
0012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0014  *   GNU General Public License for more details.                          *
0015  *                                                                         *
0016  *   You should have received a copy of the GNU General Public License     *
0017  *   along with this program; if not, write to the                         *
0018  *   Free Software Foundation, Inc.,                                       *
0019  *   51 Franklin Steet, Fifth Floor, Boston, MA  02111-1307, USA.          *
0020  ***************************************************************************/
0021 
0022 #include "altparser.h"
0023 #include <qfileinfo.h>
0024 #include <qfile.h>
0025 #include <qdir.h>
0026 
0027 #include <klocalizedstring.h>
0028 
0029 #include <sys/types.h>
0030 #include <sys/stat.h>
0031 #include <unistd.h>
0032 #include <string.h>
0033 #include <errno.h>
0034 
0035 Alternative::Alternative(Item *parentarg) : m_parent(parentarg)
0036 {
0037     m_priority = 1;
0038 }
0039 
0040 // Copy constructor
0041 Alternative::Alternative(const Alternative &alt) :
0042     m_altPath(alt.getPath()),
0043     m_priority(alt.getPriority()),
0044     m_description(alt.getDescription()),
0045     m_parent(alt.getParent()),
0046     m_altSlaves(alt.getSlaves())
0047 {
0048 }
0049 
0050 Alternative::~Alternative()
0051 {
0052 }
0053 
0054 Alternative& Alternative::operator=(const Alternative &alt)
0055 {
0056     if(this != &alt)
0057     {
0058         m_altPath = alt.getPath();
0059         m_priority = alt.getPriority();
0060         m_description = alt.getDescription();
0061         m_parent = alt.getParent();
0062         m_altSlaves = alt.m_altSlaves;
0063     }
0064     return (*this);
0065 }
0066 
0067 void Alternative::setSlaves(const QStringList &slaves)
0068 {
0069     m_altSlaves = slaves;
0070 }
0071 
0072 bool Alternative::isSelected() const
0073 {
0074     if(m_parent->isBroken()) return false;
0075     QFileInfo file("/etc/alternatives/"+m_parent->getName());
0076     if(!file.isSymLink()) return false;
0077     if(file.symLinkTarget() == m_altPath) return 1;
0078     return 0;
0079 }
0080 
0081 bool Alternative::isBroken() const
0082 {
0083     return !QFile::exists(m_altPath);
0084 }
0085 
0086 bool Alternative::select(QString *selectError)
0087 {
0088 // This method was 19 lines in Python in the original kalternatives :-D
0089     if(isSelected()) return true;
0090     if(isBroken())
0091     {
0092         if (selectError)
0093             *selectError = QString("Broken alternative: Unexisting path %1").arg(m_altPath);
0094         return false;
0095     }
0096 
0097     struct stat info;
0098 
0099     // Remove the current link:
0100     QByteArray parentPath = QFile::encodeName(QString::fromLatin1("/etc/alternatives/") + m_parent->getName());
0101     if (lstat(parentPath, &info) != -1 && S_ISLNK(info.st_mode))
0102     {
0103         if (unlink(parentPath) == -1)
0104         {
0105             if (selectError)
0106             {
0107                 QString e("Could not delete alternative link %1: %2");
0108                 *selectError = e.arg(QFile::decodeName(parentPath)).arg(QString::fromLocal8Bit(strerror(errno)));
0109             }
0110             return false;
0111         }
0112     }
0113 
0114     // Then we do the main link:
0115     const QByteArray m_altPathAscii = QFile::encodeName(m_altPath);
0116     if (symlink(m_altPathAscii.constData(), parentPath) == -1)
0117     {
0118         if (selectError)
0119             *selectError = QString(strerror(errno));
0120         return false;
0121     }
0122 
0123     // And finally the slaves
0124     SlaveList *parslaves = m_parent->getSlaves();
0125     if(parslaves->count() == 0 || m_altSlaves.isEmpty()) return true;
0126     int count = 0;
0127     QStringList::iterator sl;
0128     Slave *parsl;
0129     for( sl = m_altSlaves.begin(); sl != m_altSlaves.end(); ++sl)
0130     {
0131         parsl = parslaves->at(count);
0132         QByteArray parstr = QFile::encodeName(QString::fromLatin1("/etc/alternatives/") + parsl->slname);
0133         if ((lstat(parstr, &info) == 0) && S_ISLNK(info.st_mode))
0134         {
0135             if (unlink(parstr) == -1)
0136             {
0137                 if (selectError)
0138                 {
0139                     QString e("Could not delete slave alternative link %1: %2");
0140                     *selectError = e.arg(QFile::decodeName(parstr)).arg(QString::fromLocal8Bit(strerror(errno)));
0141                 }
0142                 return false;
0143             }
0144         }
0145         const QByteArray slAscii = QFile::encodeName(*sl);
0146         if (!sl->isEmpty() && (symlink(slAscii.constData(), parstr) == -1))
0147         {
0148             if (selectError)
0149                 *selectError = QString(strerror(errno));
0150             return false;
0151         }
0152         ++count;
0153     }
0154     return true;
0155 }
0156 
0157 QString Alternative::prettyDescription(Alternative *alt)
0158 {
0159     return alt->getDescription().isEmpty() ? i18n("no description") : alt->getDescription();
0160 }
0161 
0162 
0163 //*************************************** Item
0164 
0165 Item::Item()
0166 {
0167     m_mode = AutoMode;
0168     m_itemSlaves = new SlaveList;
0169     m_itemAlts = new AltsPtrList;
0170 }
0171 
0172 // Deep copy
0173 Item::Item(const Item &item) :
0174     m_name(item.m_name),
0175     m_mode(item.m_mode),
0176     m_path(item.m_path)
0177 {
0178     m_itemSlaves = new SlaveList;
0179     m_itemAlts = new AltsPtrList;
0180     Slave *slave;
0181     Slave *slavecopy;
0182     Q_FOREACH (slave, *item.m_itemSlaves)
0183     {
0184         slavecopy = new Slave;
0185         slavecopy->slname = slave->slname;
0186         slavecopy->slpath = slave->slpath;
0187         m_itemSlaves->append(slavecopy);
0188     }
0189 
0190     Alternative *alt;
0191     Alternative *altcopy;
0192     Q_FOREACH (alt, *item.m_itemAlts)
0193     {
0194         // The Alternative class already has a deep copy constructor:
0195         altcopy = new Alternative( (*alt) );
0196         m_itemAlts->append(altcopy);
0197     }
0198 }
0199 
0200 Item& Item::operator=(const Item &item)
0201 {
0202     if(this != &item)
0203     {
0204         if(m_itemSlaves)delete m_itemSlaves;
0205         if(m_itemAlts)delete m_itemAlts;
0206         m_name = item.m_name;
0207         m_mode = item.m_mode;
0208         m_path = item.m_path;
0209         m_itemSlaves = new SlaveList;
0210         m_itemAlts = new AltsPtrList;
0211         Slave *slave;
0212         Slave *slavecopy;
0213         Q_FOREACH (slave, *item.m_itemSlaves)
0214         {
0215             slavecopy = new Slave;
0216             slavecopy->slname = slave->slname;
0217             slavecopy->slpath = slave->slpath;
0218             m_itemSlaves->append(slavecopy);
0219         }
0220 
0221         Alternative *alt;
0222         Alternative *altcopy;
0223         Q_FOREACH (alt, *item.m_itemAlts)
0224         {
0225             altcopy = new Alternative( (*alt) );
0226             m_itemAlts->append(altcopy);
0227         }
0228     }
0229     return (*this);
0230 }
0231 
0232 Item::~Item()
0233 {
0234     qDeleteAll(*m_itemSlaves);
0235     if(m_itemSlaves)delete m_itemSlaves;
0236     qDeleteAll(*m_itemAlts);
0237     if(m_itemAlts)delete m_itemAlts;
0238 }
0239 
0240 Alternative *Item::getSelected() const
0241 {
0242     Alternative *a;
0243     Q_FOREACH (a, *m_itemAlts)
0244     {
0245         if(a->isSelected())
0246         {
0247             return a;
0248         }
0249     }
0250     return NULL;
0251 }
0252 
0253 void Item::setSlaves(SlaveList *slaves)
0254 {
0255     qDeleteAll(*m_itemSlaves);
0256     if(this->m_itemSlaves)delete this->m_itemSlaves;
0257     this->m_itemSlaves = slaves;
0258 }
0259 
0260 void Item::addSlave(const QString &namearg, const QString &patharg)
0261 {
0262     Slave *s = new Slave;
0263     s->slname = namearg;
0264     s->slpath = patharg;
0265     m_itemSlaves->append(s);
0266 }
0267 
0268 void Item::delSlave(const QString &namearg)
0269 {
0270     QMutableListIterator<Slave *> it(*m_itemSlaves);
0271 
0272     Slave *s;
0273     while (it.hasNext())
0274     {
0275         s = it.next();
0276         if(s->slname == namearg)
0277         {
0278             it.remove();
0279             delete s;
0280             break;
0281         }
0282     }
0283 }
0284 
0285 void Item::delSlaveByPath(const QString &patharg)
0286 {
0287     QMutableListIterator<Slave *> it(*m_itemSlaves);
0288 
0289     Slave *s;
0290     while (it.hasNext())
0291     {
0292         s = it.next();
0293         if(s->slpath == patharg)
0294         {
0295             it.remove();
0296             delete s;
0297             break;
0298         }
0299     }
0300 }
0301 
0302 Alternative *Item::getAlternative(const QString &altpath)
0303 {
0304     Alternative *a;
0305     Q_FOREACH (a, *m_itemAlts)
0306     {
0307         if(a->getPath() == altpath)
0308         {
0309             return a;
0310         }
0311     }
0312     return NULL;
0313 }
0314 
0315 void Item::setAlternatives(AltsPtrList &alts)
0316 {
0317     qDeleteAll(*m_itemAlts);
0318     if(this->m_itemAlts)delete this->m_itemAlts;
0319     this->m_itemAlts = &alts;
0320 }
0321 
0322 void Item::delAlternativeByPath(const QString &patharg)
0323 {
0324     QMutableListIterator<Alternative *> it(*m_itemAlts);
0325 
0326     Alternative *a;
0327     while (it.hasNext())
0328     {
0329         a = it.next();
0330         if(a->getPath() == patharg)
0331         {
0332             it.remove();
0333             delete a;
0334             break;
0335         }
0336     }
0337 }
0338 
0339 void Item::delAlternativeByPriority(int priorityarg)
0340 {
0341     QMutableListIterator<Alternative *> it(*m_itemAlts);
0342 
0343     Alternative *a;
0344     while (it.hasNext())
0345     {
0346         a = it.next();
0347         if(a->getPriority() == priorityarg)
0348         {
0349             it.remove();
0350             delete a;
0351             break;
0352         }
0353     }
0354 }
0355 
0356 bool Item::isBroken() const
0357 {
0358     return !QFile::exists(m_path);
0359 }
0360 
0361 QString Item::modeString(Item::ItemMode mode)
0362 {
0363     switch (mode)
0364     {
0365         case AutoMode:
0366             return QString::fromLatin1("auto");
0367         case ManualMode:
0368             return QString::fromLatin1("manual");
0369     }
0370     Q_ASSERT(false);
0371     return QString();
0372 }
0373 
0374 /********************** AltFIlesManager ************/
0375 
0376 AltFilesManager::AltFilesManager(const QString &altdirarg) :
0377     m_altdir(altdirarg)
0378 {
0379     m_itemlist = new ItemPtrList;
0380     m_parseOk = true;
0381     m_errorMsg = "";
0382     if(!parseAltFiles(m_errorMsg))
0383     {
0384         m_parseOk = false;
0385     }
0386     //debugPrintAlts();
0387 }
0388 
0389 AltFilesManager::~AltFilesManager()
0390 {
0391     qDeleteAll(*m_itemlist);
0392     delete m_itemlist;
0393 }
0394 
0395 Item* AltFilesManager::getItem(const QString &name) const
0396 {
0397     Q_FOREACH (Item *i, *m_itemlist)
0398     {
0399         if(i->getName() == name)
0400         {
0401             return i;
0402         }
0403     }
0404     return NULL;
0405 }
0406 
0407 bool AltFilesManager::parseAltFiles(QString &errorstr)
0408 {
0409     QDir d(m_altdir);
0410     QStringList fileList = d.entryList(QDir::Files | QDir::NoDotAndDotDot);
0411     QStringList lines;
0412     QFile altFile;
0413     QString line, tmp;
0414     int nslaves;
0415     int index;
0416 
0417     for( QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it)
0418     {
0419         Item *item = new Item;
0420         item->setName(*it);
0421         altFile.setFileName(m_altdir+'/'+*it);
0422 
0423         if(!altFile.open(QIODevice::ReadOnly))
0424         {
0425             errorstr = altFile.errorString();
0426             delete item;
0427             return false;
0428         }
0429 
0430         // read the file and split it in lines, keeping empty ones
0431         lines = QString::fromLocal8Bit(altFile.readAll()).split('\n', Qt::KeepEmptyParts);
0432 
0433         line = lines[0];
0434         tmp = line;
0435         if (tmp == QLatin1String("auto"))
0436             item->setMode(Item::AutoMode);
0437         else if (tmp == QLatin1String("manual"))
0438             item->setMode(Item::ManualMode);
0439         else
0440         {
0441             errorstr = QString("Unrecognized mode '%1'").arg(tmp);
0442             item->setMode(Item::AutoMode);
0443         }
0444 
0445         line = lines[1];
0446         tmp = line;
0447         item->setPath(tmp);
0448 
0449         index = 2;
0450         line = lines[index];
0451         nslaves = 0;
0452         SlaveList *slaves = new SlaveList;
0453 
0454         while(!line.isEmpty())
0455         {
0456             tmp = line;
0457             Slave *slave = new Slave;
0458             nslaves++;
0459             slave->slname = tmp;
0460 
0461             line = lines[++index];
0462             tmp = line;
0463             slave->slpath = tmp;
0464 
0465             slaves->append(slave);
0466             line = lines[++index];
0467         }
0468 
0469         item->setSlaves(slaves);
0470 
0471         ++index;
0472         const int remLines = (lines.count() - index - 1) % (nslaves + 2);
0473         if (remLines != 0 && !lines.at(lines.count() - remLines).isEmpty())
0474         {
0475             errorstr = QString::fromLatin1("Mismatch in numer of lines left for the alternatives declarations for %1").arg(*it);
0476             delete item;
0477             return false;
0478         }
0479         const int altCount = (lines.count() - index - 1) / (nslaves + 2);
0480         for (int i = 0; i < altCount; ++i)
0481         {
0482             Alternative *a = new Alternative(item);
0483 
0484             line = lines[index];
0485             tmp = line;
0486             a->setPath(tmp);
0487             ++index;
0488 
0489             line = lines[index];
0490             tmp = line;
0491             a->setPriority(tmp.toInt());
0492             ++index;
0493 
0494             for (int j = 0; j < nslaves; ++j)
0495             {
0496                 line = lines[index];
0497                 tmp = line;
0498                 a->addSlave(tmp);
0499                 ++index;
0500             }
0501 
0502             item->addAlternative(a);
0503         }
0504         m_itemlist->append(item);
0505         altFile.close();
0506     }
0507 
0508     return true;
0509 }
0510 
0511 /*
0512 void AltFilesManager::debugPrintAlts() const
0513 {
0514     printf("----------------------------------\n");
0515     Item *item;
0516     Q_FOREACH (item, *m_itemlist)
0517     {
0518         printf("\nItem: %s\n", qPrintable(item->getName()));
0519         printf("\tMode: %s\n", qPrintable(Item::modeString(item->getMode())));
0520         printf("\tPath: %s\n", qPrintable(item->getPath()));
0521         if(item->getSlaves()->count() == 0)
0522             printf("\tNo slaves\n");
0523         else
0524         {
0525             Slave *slave;
0526             SlaveList *slaves = item->getSlaves();
0527             Q_FOREACH (slave, *slaves)
0528             {
0529                 printf("\tSlave name: %s\n", qPrintable(slave->slname));
0530                 printf("\tSlave path: %s\n", qPrintable(slave->slpath));
0531             }
0532         }
0533         printf("\tAlternatives:\n");
0534         if(item->getAlternatives()->count() == 0)
0535             printf("\t\tNO ALTERNATIVES!");
0536         else
0537         {
0538             Alternative *a;
0539             AltsPtrList *alts = item->getAlternatives();
0540             Q_FOREACH (a, *alts)
0541             {
0542                 printf("\t\tPath: %s\n", qPrintable(a->getPath()));
0543                 printf("\t\tPriority: %d\n", a->getPriority());
0544                 printf("\t\tSlaves:\n");
0545                 if(a->getSlaves().count() == 0)
0546                     printf("\t\t\tNo slaves\n");
0547                 else
0548                 {
0549                     QStringList altslaves = a->getSlaves();
0550                     QStringList::iterator sl;
0551                     for( sl = altslaves.begin(); sl != altslaves.end(); ++sl)
0552                     {
0553                         printf("\t\t\t%s\n", qPrintable(*sl));
0554                     }
0555                 }
0556             }
0557         }
0558     }
0559 }
0560 */
0561 /*
0562 // ************************************** Test
0563 int main(int argc, char **argv)
0564 {
0565     AltFilesManager a("/var/lib/rpm/alternatives");
0566     if(!a.parsingOk())
0567         printf("ERROR PARSING ALT FILES: %s\n", qPrintable(a.getErrorMsg()));
0568     else
0569         printf("\nOK, Finished parsing\n");
0570 
0571     Item *item= a.getItem("vi");
0572     if(item == NULL) return 0;
0573     printf("Item name: %s\n", qPrintable(item->getName()));
0574     printf("Item path: %s\n", qPrintable(item->getPath()));
0575     Alternative *alt = item->getSelected();
0576     if(alt == NULL) return 0;
0577     printf("Selected alt: %s\n", qPrintable(alt->getPath()));
0578 
0579     Alternative *vimminimal = item->getAlternative("/bin/vim-minimal");
0580     if(vimminimal == NULL) { printf("NULL!\n"); return 0; }
0581     printf("Not selected alt: %s\n", qPrintable(vimminimal->getPath()));
0582 
0583     printf("Selecting vim-minimal instead of vim-enhanced as vi\n");
0584     QString selectError;
0585     if(!vimminimal->select(&selectError))
0586     {
0587         printf("ERROR: %s\n", qPrintable(selectError));
0588     }
0589 
0590     printf("Now selecting vim-enhanced...\n");
0591     Alternative *vimen = item->getAlternative("/usr/bin/vim-enhanced");
0592     if(vimen == NULL) { printf("NULL!\n"); return 0; }
0593     if(!vimen->select(&selectError))
0594     {
0595         printf("ERROR: %s\n", qPrintable(selectError));
0596     }
0597     return 0;
0598   }
0599 */