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 */