#!/usr/bin/python # kw (c) 2003- Roey Katz, distributed under the following license: # 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, 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; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # revisions: # # 20031229 rk # added -w option to specify another attribute name # 20031227 rk # initial release # surgeon generals' warning: viewing this code may lead to dizziness, # headache, and possibly vomit DEFAULT_KEYWORDNAME='keywords' PREFIX='user.' import sys,os,xattr,sets,glob from optparse import OptionParser def calcMaxLength( l ): if len(l)==0: return 0 return max(map(len,l)) def changeKeyword( filename, options ): if options.ignoredotfiles==True and os.path.basename(filename)[0]=='.': return # 1. get file's keyword list or create it if it does not exist attrlist = [] try: attrlist = xattr.getxattr( filename, options.keywordName ) if attrlist=='': fileKeys = sets.Set() else: # we have to do the above because for empty user.keywords strings we don't want Set(['']) fileKeys = sets.Set( [i.strip() for i in attrlist.split(',')] ) except IOError: if options.deleteList != None: return else: fileKeys = sets.Set() # 2a. just show? if hasattr( options, 'show' ): keys = [i.strip() for i in list(fileKeys)] keys.sort() print '%-*.*s %s' % (options.maxlen, options.maxlen, filename, ", ".join(keys)) return # 2b. is query? if options.queryList != None: keyList = options.keyList if fileKeys & keyList: if options.verbose: keys = [i.strip() for i in list(fileKeys)] keys.sort() print '%-*.*s %s' % (options.maxlen, options.maxlen, filename, ", ".join(keys)) else: print filename return # 2c. is add keyword? elif options.deleteList != None: kw = fileKeys - options.keyList # 2d. or delete keyword? elif options.addList != None: kw = fileKeys | options.keyList # 3. write to file's keyword list keys = [i.strip() for i in list(kw)] # adds an '' element for some reason keys.sort() if options.verbose==True: print '%-*.*s: %s -> %s' % (options.maxlen, options.maxlen, filename, ",".join(list(fileKeys)), ",".join(keys)) if options.nothing: return xattr.setxattr( filename, options.keywordName, ",".join(keys) ) if __name__=='__main__': parser = OptionParser() parser.add_option("-a", "--add", type="string", action="store", dest="addList", help="add keyword (keyword list is of the form key1,key2,...,keyN)", ) parser.add_option("-d", "--delete", type="string", action="store", dest="deleteList", help="delete keyword (keyword list is of the form key1,key2,...,keyN)", ) parser.add_option("-q", "--query", type="string", action="store", dest="queryList", help="print files whose keywords match the given keylist") parser.add_option("-r", "--recursive",action="store_true", dest="recursive", default=False, help="recursive operation") parser.add_option("-n", "--nothing", action="store_true", dest="nothing", default=False, help="just simulate; perform no action") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="print description of the actions taken to stdout") parser.add_option("-t", "--ignore-dotfiles", action="store_true", dest="ignoredotfiles", default=False, help="ignore files beginning with \".\"") parser.add_option("-w", "--keyword", type="string", action="store", dest="keywordName", help="specify a different extended attribute name (default is 'user.keywords')") (options, args) = parser.parse_args() if options.addList == options.deleteList == options.queryList == None: options.show = True if options.keywordName == None: options.keywordName = PREFIX + DEFAULT_KEYWORDNAME else: options.keywordName = PREFIX + options.keywordName # begin script if len(args)==0: args = glob.glob('*') if options.queryList != None: options.keyList = sets.Set([i.strip() for i in options.queryList.split(",")]) elif options.deleteList != None: options.keyList = sets.Set([i.strip() for i in options.deleteList.split(",")]) elif options.addList != None: options.keyList = sets.Set([i.strip() for i in options.addList.split(",")]) options.maxlen = calcMaxLength(args)+1 for arg in args: # go through every argument if os.path.isdir(arg) and options.recursive: # if we're recursing... changeKeyword(arg, options) # ...then first change the dir itself... for root, dirs, files in os.walk(arg): # ...and then change the files and dirs under it options.maxlen = calcMaxLength(files+dirs)+len(root)+1 for f in files+dirs: # go through list of files fname = os.path.join(root,f) changeKeyword(fname,options) else: options.maxlen = calcMaxLength(args)+1 changeKeyword(arg, options) # otherwise just change the file or dir by itself