You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeadmin/kpackage/fbsdInterface.cpp

642 lines
18 KiB

/*
** Copyright (C) 2000 by Alex Hayward <xelah@xelah.com>
*/
/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
** MA 02110-1301, USA.
*/
/*
** Bug reports and questions can be sent to kde-devel@kde.org
*/
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/utsname.h>
#include <tqstringlist.h>
#include <klocale.h>
#include <kglobal.h>
#include <kiconloader.h>
#include <kdebug.h>
#include "fbsdInterface.h"
#include "kpackage.h"
#include "updateLoc.h"
#include "cache.h"
#include "options.h"
#define PKG_INFO_BIN "/usr/sbin/pkg_info"
#define PKG_ADD_BIN "/usr/sbin/pkg_add"
#define PKG_DELETE_BIN "/usr/sbin/pkg_delete"
#define INFO_SEPARATOR "f;g#z-@IqbX%"
fbsdInterface::fbsdInterface():pkgInterface() {
head = "BSD";
name = i18n("BSD");
icon = "bsd";
pict = UserIcon(icon);
updated_pict = UserIcon("bupdated");
new_pict = UserIcon("bnew");
packagePattern = "*.tgz *.tbz";
typeID = "/tbz";
TQDict <bsdPortsIndexItem> ports(17777, false);
queryMsg = i18n("Querying package list: ");
locatedialog = new Locations(i18n("Location of BSD Packages and Ports"));
locatedialog->dLocations(1, 1, this, i18n("Ports"), "Pkg", "*.tbz",
i18n("Location of Ports Tree (e.g. /usr/ports or /usr/opt)"),FALSE);
locatedialog->dLocations(1, 6, this, i18n("Packages"), "Pkg", "*.tbz",
i18n("Location of Folders Containing BSD Packages or Package Trees"));
connect(locatedialog, TQT_SIGNAL(returnVal(LcacheObj *)), this, TQT_SLOT(setAvail(LcacheObj *)));
locatedialog->apply_slot();
paramsInst.append(new param(i18n("Ignore Scripts"),FALSE,FALSE,"-I"));
paramsInst.append(new param(i18n("Check Dependencies"),TRUE,TRUE,"-f"));
paramsInst.append(new param(i18n("Test (do not install)"),FALSE,FALSE,"-n"));
paramsUninst.append(new param(i18n("Ignore Scripts"),FALSE,FALSE, "-I"));
paramsUninst.append(new param(i18n("Check Dependencies"),TRUE,TRUE, "-f"));
paramsUninst.append(new param(i18n("Test (do not uninstall)"),FALSE,FALSE, "-n"));
hasProgram = ifExe("pkg_info") && ifExe("pkg_add");
}
fbsdInterface::~fbsdInterface() {
}
bool fbsdInterface::isType(char *, const TQString &fname) {
// These files are .tgz or .tbz files. Pass it to pkg_info and see whether it
// succeeds.
if (hasProgram) {
TQString cmd = PKG_INFO_BIN; // cmd += "_q";
cmd += " -q ";
cmd += fname;
kpty->run(cmd);
if (!kpty->Result)
return true;
else
return false;
} else {
return false;
}
}
static void insertGroups(TQMap<TQString, TQString> *a, TQString cats)
{
/* Create the list of groups (which is space-separated), and then
** iterate through it with the iterator i. count is just to
** distinguish the first entry (count==0) from the rest, since
** the key used in a->insert() needs to be different.
*/
TQStringList grlist = TQStringList::split(' ',cats);
unsigned int count = 0;
for (TQStringList::Iterator i = grlist.begin();
i != grlist.end(); ++count,++i) {
a->insert( (count ? "also in" : "group"), *i);
}
}
packageInfo *fbsdInterface::getPackageInfo(char mode, const TQString &pname, const TQString &version) {
TQString name( pname);
bool installed = false;
kpackage->settqStatus(i18n("Getting package info"));
kdDebug() << "Looking at package " << pname << endl;
if (mode == 'i' && !version.isEmpty()) {
name += "-" + version;
}
TQMap<TQString, TQString> a;
// Get the package name first (for mode = 'u').
if (mode == 'u') {
TQString cmd = PKG_INFO_BIN; // cmd += "_qf";
cmd += " -qf ";
cmd += name;
TQStringList list = kpty->run(cmd);
int last_dir = name.find('/');
if (last_dir != -1) {
a["filename"] = name.mid(last_dir+1);
a["base"] = name.left(last_dir + 1);
} else {
a["filename"] = name;
a["base"] = "";
}
if (list.count() > 0) {
for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
// Look for a line of the form '@name <pkgname>'
if ((*it).left(5) == "@name") {
TQString n = (*it).mid(6);
addNV(a, n);
break;
}
}
} else addNV(a, name);
}
// Open a pipe to a pkg_info process in order to read the one line comment
// and description for the package. This works for both installed packages
// and for files.
TQString cmd = PKG_INFO_BIN; // cmd += "_q";
cmd += " -q ";
cmd += name;
TQStringList list = kpty->run(cmd);
TQStringList::Iterator it = list.begin();
if (list.count() > 0) {
TQStringList::Iterator it = list.begin();
a["summary"] = *it;
it++;
TQString desc;
int prevlen = 0, len;
for ( ; it != list.end(); ++it ) {
len = (*it).length();
desc += (*it);
// kdDebug() << len << " " << prevlen << "=" << *it << "\n";
if (len > 0 || prevlen > 0)
desc += "<br>\n";
prevlen = (*it).length();
}
// kdDebug( << desc << "\n";
bsdPortsIndexItem *inditem = ports[name];
if (inditem) {
installed = inditem->installed;
a["maintainer"] = inditem->fields[bsdPortsIndexItem::MAINT];
insertGroups(&a,inditem->fields[bsdPortsIndexItem::CATS]);
a["build depends"] = !inditem->fields[bsdPortsIndexItem::BDEPS].isEmpty() ? inditem->fields[bsdPortsIndexItem::BDEPS] : i18n("none");
a["available as"] = inditem->bin ? (inditem->port? i18n("binary package and source port") : i18n("binary package")) : i18n("source port");
}
a["description"] = desc;
} else {
kpackage->settqStatus(TQString());
return 0;
}
packageInfo *ret = new packageInfo(a, this);
ret->packageState = installed? packageInfo::INSTALLED : packageInfo::AVAILABLE;
ret->fixup();
if (!installed) ret->smerge(typeID);
kpackage->settqStatus(TQString());
return ret;
}
TQStringList fbsdInterface::getChangeLog(packageInfo *) {
return 0;
}
bool fbsdInterface::filesTab(packageInfo *) {
return TRUE;
}
bool fbsdInterface::changeTab(packageInfo *) {
return FALSE;
}
TQStringList fbsdInterface::getFileList(packageInfo *p) {
// Run pkg_info on the package name to get the file list.
// The file list is returned on stdout, one per line.
kpackage->settqStatus(i18n("Getting file list"));
TQStringList ret;
// Find the full name 'name-version', or just 'name' if version is empty.
// Check first that it is actually installed.
TQString name( p->getProperty("filename"));
if (!name.isEmpty() && (p->packageState != packageInfo::INSTALLED)) {
TQString qbname( p->getProperty("base"));
if (!qbname.isEmpty())
name = qbname + "/" + name;
} else {
if (!p->hasProperty("name")) {
ret.append(i18n("Can't find package name!"));
kpackage->settqStatus(TQString());
return ret;
}
name = p->getProperty("name");
TQString version( p->getProperty("version"));
if (!version.isEmpty()) {
name = name + "-" + version;
}
}
// Open a pipe to a pkg_info process in order to read the file list.
// This works for both installed packages and for files.
TQString cmd = PKG_INFO_BIN; // cmd += "_Lq";
cmd += " -L -q ";
cmd += name;
TQStringList list = kpty->run(cmd);
ret = list;
kpackage->settqStatus(TQString());
return ret;
}
// TQPtrList<char> *verify(packageInfo *p, TQPtrList<char> *files);
TQString fbsdInterface::doUninstall(int uninstallFlags, const TQString &packs, bool &)
{
TQString s = PKG_DELETE_BIN;
s += " ";
s += setOptions(uninstallFlags, paramsUninst);
s += packs;
kdDebug() << "uCMD=" << s << "\n";
return s;
}
TQString fbsdInterface::doInstall(int installFlags, const TQString &packs, bool &)
{
TQString s = PKG_ADD_BIN;
s += " ";
s += setOptions(installFlags, paramsInst);
s += packs;
kdDebug() << "iCMD=" << s << "\n";
return s;
}
TQString fbsdInterface::uninstall(int uninstallFlags, packageInfo *p, bool &test)
{
TQString packs( p->getProperty("name"));
TQString vers( p->getProperty("version"));
if (vers.length() > 0) packs += "-" + vers;
return doUninstall(uninstallFlags, packs, test);
}
TQString fbsdInterface::uninstall(int uninstallFlags, TQPtrList<packageInfo> *p, bool &test)
{
TQString packs ;
packageInfo *i;
for (i = p->first(); i!= 0; i = p->next()) {
packs += i->getProperty("name");
TQString vers( i->getProperty("version"));
if (vers.length() != 0) packs += "-" + vers;
packs += " ";
}
return doUninstall( uninstallFlags, packs, test);
}
TQStringList fbsdInterface::FindFile(const TQString &, bool) {
TQStringList tmp;
return tmp;
}
bool fbsdInterface::parseName(const TQString &name, TQString *n, TQString *v) {
int m1;
m1 = name.findRev('-');
if (m1 <= 0) return false;
*n = name.left(m1);
*v = name.right(name.length() - m1 - 1);
return true;
}
void fbsdInterface::addNV(TQMap<TQString, TQString> &d, const TQString &name) {
TQString n, v;
if (!parseName(name, &n, &v)) {
n = name;
v = TQString();
}
d.insert("name", n);
d.insert("version", v);
}
//public slots
void fbsdInterface::setLocation() {
locatedialog->restore();
}
void fbsdInterface::setAvail(LcacheObj *slist) {
kdDebug() << k_funcinfo << endl;
if (packageLoc) delete packageLoc;
packageLoc = slist;
cacheObj *cp = packageLoc->first();
if (cp && !cp->location.isEmpty()) {
for (; cp != 0; cp = packageLoc->next()) {
TQString oldloc = cp->location;
cp->location += "/INDEX";
TQString s = getPackList(cp);
if (!s.isEmpty()) bsdPortsIndexItem::processFile(this, TQFile::encodeName(s), true, oldloc);
cp->location = oldloc;
}
}
// Try /usr/port/INDEX-<major version> on FreeBSD
struct utsname fbsdName;
if(uname(&fbsdName) != -1 && !strcmp(fbsdName.sysname, "FreeBSD"))
bsdPortsIndexItem::processFile(this, TQString("/usr/ports/INDEX-").append(*fbsdName.release), false, "/usr/ports");
// Try the standard ports tree locations.
bsdPortsIndexItem::processFile(this, "/usr/ports/INDEX", false, "/usr/ports"); // FreeBSD/OpenBSD
bsdPortsIndexItem::processFile(this, "/usr/opt/INDEX", false, "/usr/opt"); // NetBSD
}
void fbsdInterface::listPackages(TQPtrList<packageInfo> *pki) {
kdDebug() << k_funcinfo << endl;
listInstalledPackages(pki);
TQDictIterator<bsdPortsIndexItem> it( ports ); // See TQDictIterator
for( ; it.current(); ++it ) {
bsdPortsIndexItem *scan = it.current();
if (!scan->installed /*&& scan->bin */) {
TQMap<TQString, TQString> a;
addNV(a, scan->fields[bsdPortsIndexItem::NAME]);
a["summary"] = scan->fields[bsdPortsIndexItem::COMMENT];
a["maintainer"] = scan->fields[bsdPortsIndexItem::MAINT];
insertGroups(&a,scan->fields[bsdPortsIndexItem::CATS]);
a["run depends"] = !scan->fields[bsdPortsIndexItem::RDEPS].isEmpty() ? scan->fields[bsdPortsIndexItem::RDEPS] : i18n("none");
a["build depends"] = !scan->fields[bsdPortsIndexItem::BDEPS].isEmpty() ? scan->fields[bsdPortsIndexItem::BDEPS] : i18n("none");
a["available as"] = scan->bin ? (scan->port? i18n("binary package and source port") : i18n("binary package")) : i18n("source port");
a["filename"] = scan->bin_filename;
a["base"] = scan->bin_filename_base;
packageInfo *info = new packageInfo(a, this);
info->packageState = packageInfo::AVAILABLE;
info->smerge(typeID);
info->fixup();
info->pkgInsert(pki, typeID, false);
// pki->append(info);
}
}
}
int fbsdInterface::parseItem(TQStringList::Iterator &it, TQString &name, TQString &value, TQString separator, TQStringList list ) {
if ((*it).left(separator.length()) == separator) {
name = *it;
name = name.mid(separator.length());
} else {
return -1;
}
if (it == list.end())
return -1;
it++;
value = "";
int prevlen = 0, len;
while ((*it).left(separator.length()) != separator) {
len = (*it).length();
value += *it;
if (len > 0 || prevlen > 0)
value += "<br>";
if (it == list.end())
return -1;
prevlen = (*it).length();
it++;
}
return 1;
}
int fbsdInterface::pathInfo(TQMap<TQString, TQString> &a)
{
int pkg_state = packageInfo::INSTALLED;
if (a["group"].isEmpty()) {
TQString s, ps;
ps = a["name"];
if (ps.isEmpty())
s = ps;
else
s = "<anonymous>";
ps = a["version"];
if (!ps.isEmpty())
s.append(TQString("-")+(ps));
kdDebug() << "Package " << (s) << " has no group." << endl;
/* This must be an installed package with no INDEX entry,
** which usually means that the port has been updated.
*/
TQString cmd = PKG_INFO_BIN;
// cmd += "2";
cmd += " -ol ";
cmd += INFO_SEPARATOR;
TQStringList list = kpty->run(cmd);
if (list.count() > 0) {
TQStringList::Iterator it = list.begin();
TQString name, value;
parseItem(it, name, value, INFO_SEPARATOR, list); // Information
parseItem(it, name, value, INFO_SEPARATOR, list); // Path
int pos = value.findRev('/');
value.truncate(pos);
a["group"] = value;
} else {
kdDebug() << "Could not read package origin info." << endl;
}
}
return pkg_state;
}
void fbsdInterface::listInstalledPackages(TQPtrList<packageInfo> *pki) {
kdDebug() << k_funcinfo << endl;
// Open a pipe to a pkg_info process in order to read the comment, name
// and description for the packages.
kpackage->settqStatus(i18n("Querying BSD packages database for installed packages"));
TQString cmd = PKG_INFO_BIN;
cmd += " -acdl ";
cmd += INFO_SEPARATOR;
TQStringList list = kpty->run(cmd);
// We should now get:
// INFO_SEPARATORInformation for pkgname:
//
// INFO_SEPARATORComment:
// <one line description>
//
// INFO_SEPARATORDescription:
// <description>
//
//
// INFO_SEPARATOR
// INFO_SEPARATORInformation for [etc]
TQMap<TQString, TQString> a;
TQString name, value;
if (list.count() > 0) {
for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
parseItem(it, name, value, INFO_SEPARATOR, list); // Information
// Find the last word on this line (which should be the package name) minus a trailing :.
TQString pkg = name.section(' ',-1);
if (pkg.isEmpty()) {
KpMsgE(i18n("Unexpected output from pkg_info (looking for package name): %1").tqarg(value), TRUE);
kpackage->settqStatus(TQString());
return;
} else {
if (pkg[pkg.length()-1] == ':') {
pkg.truncate(pkg.length()-1);
}
}
addNV(a, pkg);
parseItem(it, name, value, INFO_SEPARATOR, list); //Comment
a["summary"] = value;
parseItem(it, name, value, INFO_SEPARATOR, list); //Description
bsdPortsIndexItem *inditem = ports[pkg];
if (inditem) {
inditem->installed = true;
a["maintainer"] = inditem->fields[bsdPortsIndexItem::MAINT];
insertGroups(&a,inditem->fields[bsdPortsIndexItem::CATS]);
if (a["group"].isEmpty()) {
kdDebug() << "Line <" << name << "=" << value << "> has no group?" << endl;
}
a["run depends"] = !inditem->fields[bsdPortsIndexItem::RDEPS].isEmpty() ?
inditem->fields[bsdPortsIndexItem::RDEPS] : i18n("none");
a["build depends"] = !inditem->fields[bsdPortsIndexItem::BDEPS].isEmpty() ?
inditem->fields[bsdPortsIndexItem::BDEPS] : i18n("none");
a["available as"] = inditem->bin ? (inditem->port? i18n("binary package and source port") : i18n("binary package")) : i18n("source port");
}
a["description"] = value;
int pkg_state = pathInfo(a);
packageInfo *info = new packageInfo(a, this);
info->packageState = pkg_state;
info->fixup();
//pki->append(info);
info->pkgInsert(pki, typeID, true);
}
}
}
bsdPortsIndexItem::bsdPortsIndexItem(fbsdInterface *parent, char *desc, bool binaries, const TQString &dname) : bin(binaries), port(!binaries), installed(false) {
fields = TQStringList::split('|', desc, TRUE);
TQString name = fields[NAME];
bsdPortsIndexItem *port = parent->ports[name];
if (port) {
port->bin = port->bin || bin;
port->port = port->port || port;
if (binaries) {
port->bin_filename = TQString(name) + ".tbz";
port->bin_filename_base = dname + "/";
}
fields[NAME] = ""; // Acts as a 'not used' tag.
return;
}
if (binaries) {
bin_filename = TQString(name) + ".tbz";
bin_filename_base = dname + "/";
}
}
void bsdPortsIndexItem::processFile(fbsdInterface *parent, const TQString &fname, bool binaries, const TQString &dname) {
// Read the file in to a buffer and null terminate it.
struct stat s;
if (stat(fname.ascii(), &s) == -1) {
// Error message?
return;
}
char *index = (char *) malloc(s.st_size);
int fd;
fd = open(fname.ascii(), O_RDONLY);
if (fd == -1) {
// Error message?
return;
}
int size = read(fd, index, s.st_size);
index[size] = 0;
close(fd);
// Go through each line and create a new bsdPortsIndexItem.
char *line = strtok(index, "\n");
while (line != 0) {
bsdPortsIndexItem *i = new bsdPortsIndexItem(parent, line, binaries, dname + "/All");
if (i->fields[NAME].isEmpty()) {
delete i;
} else {
parent->ports.insert(i->fields[NAME] , i);
}
line = strtok(0, "\n");
}
}
#include "fbsdInterface.moc"