### # Copyright (c) 2021, B. Watson # All rights reserved. # # ### import os import subprocess import sys import re import sqlite3 from supybot import utils, plugins, ircutils, callbacks from supybot.commands import * import supybot.utils.minisix as minisix try: from supybot.i18n import PluginInternationalization _ = PluginInternationalization('SlackTools') except ImportError: # Placeholder that allows to run the plugin on a bot # without the i18n module _ = lambda x: x # This code could stand to be refactored. Lots of almost-identical # code in FileQuery and PkgQuery. Also, I'm pretty much a python # n00b, so the code might read a little weird if you're a python # expert (I learned just enough of the language, on the fly, to # get this stuff written). That's also why the database-creation # script is in perl (I'm much more familiar and competent with perl). # notes about the database: # it gets created by the makepkgdb.pl script, which requires: # - PACKAGES.txt and extra/PACKAGES.txt from the installer. # - copies of /var/log/scripts and /var/log/packages from a full # slackware install (including /extra but not /testing nor # /pasture) with no 3rd-party packages installed. # see the comments in makepkgdb.pl for more info. # The database only contains packages from one slackware version # and arch. Currently that's x86_64 14.2. Only packages from the # main slackware64/ and extra/ trees are in the db. If there's any # demand for it, it wouldn't be hard to support multiple slack # versions and/or arches... or add pasture/ and/or testing/ to the db. # It would have been easy to include package sizes in the db, but # I didn't think it would be that useful. # The files table also includes symlinks. # /lib64/incoming/ paths get their /incoming removed before being # inserted in the db. # file paths are stored *with* the leading slash, so e.g. /bin/ls # rather than the bin/ls. This is unlike usual slackpkg and # /var/log/packages usage, but it seems more intuitive for users. class SlackTools(callbacks.Plugin): """Provides Slackware package db lookups""" threaded = True db = None def getMaxResults(self, msg): maxresults = self.registryValue('maxresults') if msg.channel is None: # private message, increase limit maxresults *= 5 return maxresults def InitDB(self): if self.db is None: filename = self.registryValue('dbpath') if(os.path.exists(filename)): self.db = sqlite3.connect(filename) return self.db def PkgQuery(self, irc, msg, searchtype, term): db = self.InitDB(); if db is None: irc.error("slackpkg database doesn't exist", Raise=True) maxresults = self.getMaxResults(msg) cursor = db.cursor() if(searchtype == 'pkg'): cursor.execute("select c.name, p.name, p.descrip from categories c, packages p where c.id=p.category and p.name glob ? order by c.name, p.name limit ?", (term, maxresults+1)) else: cursor.execute("select distinct c.name, p.name, p.descrip from categories c, packages p, files f where c.id=p.category and p.id=f.package and f.path glob ? order by c.name, p.name limit ?", (term, maxresults+1)) result = cursor.fetchall() lines = [] if len(result) == 0: irc.reply("no matching packages") else: for (category, pkg, descrip) in result: lines.append(ircutils.bold(category + "/" + pkg) + ": " + descrip) if(len(result) > maxresults): lines.append("[too many results, only showing first " + str(maxresults) + "]") irc.replies(lines, joiner=' | ') # search for packages by name def pkgsearch(self, irc, msg, args, pkg): """ Search the Slackware package database for packages named like . This is a case-sensitive substring match (e.g. "core" matches "coreutils" and "xapian-core"). You can use * for globbing, e.g. "c*s" to find all packages whose names begin with "c" and end with "s". """ self.PkgQuery(irc, msg, 'pkg', "*" + pkg + "*") pkgsearch = thread(wrap(pkgsearch, ['somethingWithoutSpaces'])) # search for packages by contents def filesearch(self, irc, msg, args, file): """ Search the Slackware package database for packages that contain files named . This is a case-sensitive exact match, by default (e.g. "/bin/ls" does not match "/usr/bin/ls" nor "/bin/lsattr"). You can use * for globbing, e.g. "/bin/ls*" to match all files in /bin whose names begin with "ls". """ self.PkgQuery(irc, msg, 'file', file) filesearch = thread(wrap(filesearch, ['somethingWithoutSpaces'])) # run filesearch or pkgsearch, save some typing. def pkg(self, irc, msg, args, fileflag, term): """ [-f] Without -f, same as pkgsearch. With -f, same as filesearch. See the help for pkgsearch and/or filesearch for details. """ if(fileflag): self.PkgQuery(irc, msg, 'file', term) else: self.PkgQuery(irc, msg, 'pkg', "*" + term + "*") pkg = thread(wrap(pkg, [optional(('literal', ['-f'])), 'somethingWithoutSpaces'])) def FileQuery(self, irc, msg, term): db = self.InitDB(); if db is None: irc.error("slackpkg database doesn't exist", Raise=True) maxresults = self.getMaxResults(msg) cursor = db.cursor() cursor.execute("select path, symlink from files where path glob ? order by path limit ?", (term, maxresults+1)) result = cursor.fetchall() lines = [] if len(result) == 0: irc.reply("nothing found") else: for (file,symlink) in result: if(symlink): file = file + " [l]" lines.append(file) if(len(result) > maxresults): lines.append("[too many results, only showing first " + str(maxresults) + "]") irc.replies(lines, joiner=' | ') # subset of locate/slocate/mlocate. def locate(self, irc, msg, args, file): """ Search the package database for files named like . This is a case-sensitive substring match (basically works like the actual 'locate' command). You can use * for globbing. """ self.FileQuery(irc, msg, '*' + file + '*') locate = thread(wrap(locate, ['somethingWithoutSpaces'])) # faux 'which' command. def which(self, irc, msg, args, file): """ Search the package database for files named like */bin/. This is similar to the OS "which" command, except it doesn't search a $PATH, it doesn't know whether the file has +x permission, it can return multiple results, and you can use * for globbing. """ self.FileQuery(irc, msg, '*/bin/' + file) which = thread(wrap(which, ['somethingWithoutSpaces'])) Class = SlackTools