From 32481205179f7a481770e1176c4412fc9ea9f3dd Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 00:49:32 +0530 Subject: [PATCH 01/24] Introduce argparse. --- buku | 86 ++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 20 deletions(-) diff --git a/buku b/buku index 8861a23..cb1677a 100755 --- a/buku +++ b/buku @@ -20,7 +20,7 @@ import sys import os import sqlite3 -from getopt import getopt, GetoptError +import argparse import readline import webbrowser import html.parser as HTMLParser @@ -65,7 +65,6 @@ update = False # Update a bookmark in DB debug = False # Enable debug logs titleData = None # Title fetched from a page titleManual = None # Manually add a title offline -replace = False # Replace a tag encrypt = False # Lock database file decrypt = False # Unlock database file iterations = 8 # Number of hash iterations to generate key @@ -882,7 +881,6 @@ def encrypt_file(): os.remove(dbpath) print("File encrypted") - sys.exit(0) @@ -940,8 +938,6 @@ def decrypt_file(): os.remove(encpath) print("File decrypted") - sys.exit(0) - def sigint_handler(signum, frame): @@ -1025,12 +1021,59 @@ if __name__ == "__main__": except KeyboardInterrupt: pass -optlist = None -keywords = None +class ExtendedArgumentParser(argparse.ArgumentParser): + + def print_help(self, file=None): + super(ExtendedArgumentParser, self).print_help(file) + +argparser = ExtendedArgumentParser( + add_help=False, + description='A private cmdline bookmark manager. Your mini web!' +) +addarg = argparser.add_argument + +addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), + help='add URL as bookmark with comma separated tags') +addarg('-d', '--delete', dest='delete', type=int, metavar='N', + help='delete entry at DB index N (from -p 0), N=0 deletes all') +addarg('-g', '--tags', dest='showTags', action='store_true', + help='list all tags alphabetically') +addarg('-m', '--title', dest='titleManual', metavar='title', + help="manually set title, for -a, -i, -u; '-m none' clears title") +addarg('-s', '--sany', nargs='+', metavar='KEYWORD', + help='search bookmarks for any keyword') +addarg('-S', '--sall', nargs='+', metavar='KEYWORD', + help='search bookmarks with all keywords') +addarg('-u', '--update', nargs='+', dest='update', metavar=('N', 'URL tags'), + help='update fields of the entry at DB index N. The first keyword, if available, is treated as the URL. If URL is omitted (and -m is not used) the title of entry at index N is refreshed from the web, N=0 refreshes all titles.') +addarg('-e', '--empty', dest='empty', action='store_true', + help='show bookmarks with empty titles or no tags') +addarg('-i', '--insert', dest='insert', nargs='+', metavar=('N', 'URL tags'), + help='insert new bookmark with URL and tags at free DB index N') +addarg('-j', '--json', dest='jsonOutput', action='store_true', + help='show results in Json format') +addarg('-k', '--decrypt', dest='decrypt', action='store_true', + help='decrypt (unlock) database file') +addarg('-l', '--encrypt', dest='encrypt', action='store_true', + help='encrypt (lock) database file') +addarg('-o', '--open', dest='openurl', type=int, metavar='N', + help='open URL at DB index N in browser') +addarg('-p', '--print', dest='showindex', type=int, metavar='N', + help='show details of bookmark record at DB index N, N=0 shows all') +addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), + help='replace oldtag with newtag, delete oldtag if newtag=none') +addarg('-t', '--iterate', dest='iterations', type=int, metavar='N', + help='use N (> 0) hash iterations to generate key, for -k, -l') +addarg('-x', '--format', dest='showOpt', metavar='N', + help='modify -p behaviour, N=1: show only URL, N=2: show URL and tag') +addarg('-z', '--debug', dest='debug', action='store_true', + help='show debug information') if len(sys.argv) < 2: - usage() + argparser.print_help(sys.stderr) + sys.exit(1) +''' # Check cmdline options try: @@ -1147,7 +1190,19 @@ try: except GetoptError as e: print("buku:", e) sys.exit(1) +''' +args = argparser.parse_args() + +showTags = args.showTags +empty = args.empty +jsonOutput = args.jsonOutput +encrypt = args.encrypt +decrypt = args.decrypt +debug = args.debug +keywords = [] + +# Show version in debug logs if debug: print("Version %.1f" % _VERSION_) @@ -1165,18 +1220,9 @@ if decrypt == True: conn, cur = initdb() # Replace a tag in DB -if replace == True: - numargs = len(keywords) - - if addurl == True or update == True or delete == True: - print("Tag replace doesn't work with add or update or delete.\n") - conn.close() - usage() - elif numargs < 1 or numargs > 2: - print("Tag replace accepts 1 or 2 arguments\n") - conn.close() - usage() - elif numargs == 1: +if args.replace is not None: + keywords = args.replace + if keywords[1] == "none": replaceTags(conn, cur, keywords[0], "") else: replaceTags(conn, cur, keywords[0], keywords[1]) From 340f048d46b134bd3997829fb1a66a6d3d5b1d70 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 02:03:59 +0530 Subject: [PATCH 02/24] Support more options in argparse. --- buku | 100 +++++++++++++++++++++++++++-------------------------------- 1 file changed, 45 insertions(+), 55 deletions(-) diff --git a/buku b/buku index cb1677a..01c542d 100755 --- a/buku +++ b/buku @@ -50,23 +50,15 @@ except ImportError: # Globals -addurl = False # Add a URL addindex = None # DB index to insert URL into -delete = False # Delete bookmark(s) -empty = False # List all bookmarks with no title or tag openurl = None # Open URL in browser showindex = None # Index of bookmark to show showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag -showTags = False # List all unique tags -search = False # Search for keywords -searchAll = False # Match all keywords in search entry = None # DB index to update or delete update = False # Update a bookmark in DB debug = False # Enable debug logs titleData = None # Title fetched from a page titleManual = None # Manually add a title offline -encrypt = False # Lock database file -decrypt = False # Unlock database file iterations = 8 # Number of hash iterations to generate key jsonOutput = False # Output json formatted result pipeargs = [] # Holds arguments piped to the program @@ -481,7 +473,7 @@ def dbRefresh(conn, cur, index): -def searchdb(cur, keywords): +def searchdb(cur, keywords, all_keywords=False): """Search the database for an entries with tags or URL or title info matching keywords and list those. @@ -493,7 +485,7 @@ def searchdb(cur, keywords): placeholder = "'%' || ? || '%'" query = "SELECT id, url, metadata, tags FROM bookmarks WHERE" - if searchAll == True: # Match all keywords in URL or Title + if all_keywords == True: # Match all keywords in URL or Title for token in keywords: query += " (tags LIKE (%s) OR URL LIKE (%s) OR metadata LIKE (%s)) AND" % (placeholder, placeholder, placeholder) arguments.append(token) @@ -580,7 +572,7 @@ def cleardb(conn, cur, index): Params: connection, cursor, index to delete """ - if int(index) == 0: # Remove the table + if index == 0: # Remove the table resp = input("ALL bookmarks will be removed. Enter \x1b[1my\x1b[21m to confirm: ") if resp != 'y': print("No bookmarks deleted") @@ -591,11 +583,11 @@ def cleardb(conn, cur, index): print("All bookmarks deleted") else: # Remove a single entry try: - cur.execute('DELETE FROM bookmarks WHERE id = ?', (int(index),)) + cur.execute('DELETE FROM bookmarks WHERE id = ?', (index,)) conn.commit() if cur.rowcount == 1: - print("Removed index %d" % int(index)) - compactDB(conn, cur, int(index)) + print("Removed index %d" % index) + compactDB(conn, cur, index) else: print("No matching index") except IndexError: @@ -881,6 +873,7 @@ def encrypt_file(): os.remove(dbpath) print("File encrypted") + sys.exit(0) @@ -1032,29 +1025,29 @@ argparser = ExtendedArgumentParser( ) addarg = argparser.add_argument -addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), +addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), # DONE help='add URL as bookmark with comma separated tags') -addarg('-d', '--delete', dest='delete', type=int, metavar='N', +addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', # DONE help='delete entry at DB index N (from -p 0), N=0 deletes all') -addarg('-g', '--tags', dest='showTags', action='store_true', +addarg('-g', '--tags', dest='showTags', action='store_true', # DONE help='list all tags alphabetically') addarg('-m', '--title', dest='titleManual', metavar='title', help="manually set title, for -a, -i, -u; '-m none' clears title") -addarg('-s', '--sany', nargs='+', metavar='KEYWORD', +addarg('-s', '--sany', nargs='+', metavar='KEYWORD', # DONE help='search bookmarks for any keyword') -addarg('-S', '--sall', nargs='+', metavar='KEYWORD', +addarg('-S', '--sall', nargs='+', metavar='KEYWORD', # DONE help='search bookmarks with all keywords') addarg('-u', '--update', nargs='+', dest='update', metavar=('N', 'URL tags'), help='update fields of the entry at DB index N. The first keyword, if available, is treated as the URL. If URL is omitted (and -m is not used) the title of entry at index N is refreshed from the web, N=0 refreshes all titles.') -addarg('-e', '--empty', dest='empty', action='store_true', +addarg('-e', '--empty', dest='empty', action='store_true', # DONE help='show bookmarks with empty titles or no tags') addarg('-i', '--insert', dest='insert', nargs='+', metavar=('N', 'URL tags'), help='insert new bookmark with URL and tags at free DB index N') addarg('-j', '--json', dest='jsonOutput', action='store_true', help='show results in Json format') -addarg('-k', '--decrypt', dest='decrypt', action='store_true', +addarg('-k', '--decrypt', dest='decrypt', action='store_true', # DONE help='decrypt (unlock) database file') -addarg('-l', '--encrypt', dest='encrypt', action='store_true', +addarg('-l', '--encrypt', dest='encrypt', action='store_true', # DONE help='encrypt (lock) database file') addarg('-o', '--open', dest='openurl', type=int, metavar='N', help='open URL at DB index N in browser') @@ -1066,7 +1059,7 @@ addarg('-t', '--iterate', dest='iterations', type=int, metavar='N', help='use N (> 0) hash iterations to generate key, for -k, -l') addarg('-x', '--format', dest='showOpt', metavar='N', help='modify -p behaviour, N=1: show only URL, N=2: show URL and tag') -addarg('-z', '--debug', dest='debug', action='store_true', +addarg('-z', '--debug', dest='debug', action='store_true', # DONE help='show debug information') if len(sys.argv) < 2: @@ -1194,11 +1187,8 @@ except GetoptError as e: args = argparser.parse_args() -showTags = args.showTags -empty = args.empty +titleManual = args.titleManual jsonOutput = args.jsonOutput -encrypt = args.encrypt -decrypt = args.decrypt debug = args.debug keywords = [] @@ -1210,15 +1200,39 @@ if debug: moveOldDatabase() # Handle encrypt/decrypt options at top priority -if encrypt == True: +if args.encrypt == True: + if no_crypto: + printmsg("PyCrypto missing", "ERROR") + sys.exit(1) encrypt_file() -if decrypt == True: +if args.decrypt == True: + if no_crypto: + printmsg("PyCrypto missing", "ERROR") + sys.exit(1) decrypt_file() # Initilize the database and get handles conn, cur = initdb() +# Add a record +if args.addurl is not None: + AddUpdateEntry(conn, cur, args.addurl, None) + +# Remove a single record or all records +if args.delete is not None: + cleardb(conn, cur, args.delete) + +# Show all unique tags +if args.showTags == True: + showUniqueTags(cur) + +# Search URLs, titles, tags for keywords +if args.sany is not None: + searchdb(cur, args.sany) +if args.sall is not None: + searchdb(cur, args.sall, True) + # Replace a tag in DB if args.replace is not None: keywords = args.replace @@ -1227,14 +1241,6 @@ if args.replace is not None: else: replaceTags(conn, cur, keywords[0], keywords[1]) -# Add record -if addurl == True: - if len(keywords) < 1: - conn.close() - usage() - - AddUpdateEntry(conn, cur, keywords, entry) - # Update record if update == True: if len(keywords) < 1: @@ -1242,32 +1248,16 @@ if update == True: else: AddUpdateEntry(conn, cur, keywords, entry) -# Search tags, URLs, Title info -if search == True: - if len(keywords) < 1: - conn.close() - usage() - - searchdb(cur, keywords) - # Print all records if showindex is not None: printdb(cur, showindex) -# Show all unique tags -if showTags == True: - showUniqueTags(cur) - -if empty == True: - printdb(cur, 0, empty) +if args.empty == True: + printdb(cur, 0, True) # Open URL in browser if openurl != None: fetchopen(openurl) -# Remove a single record of all records -if delete == True: - cleardb(conn, cur, entry) - # Close the connection before exiting conn.close() From 58b8805b70c3ad5599bbea1d3b77b179fa8479ac Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 02:48:56 +0530 Subject: [PATCH 03/24] More options added to argparse. --- buku | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/buku b/buku index 01c542d..ebe912c 100755 --- a/buku +++ b/buku @@ -51,16 +51,15 @@ except ImportError: # Globals addindex = None # DB index to insert URL into -openurl = None # Open URL in browser showindex = None # Index of bookmark to show showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag entry = None # DB index to update or delete update = False # Update a bookmark in DB -debug = False # Enable debug logs titleData = None # Title fetched from a page titleManual = None # Manually add a title offline iterations = 8 # Number of hash iterations to generate key jsonOutput = False # Output json formatted result +debug = False # Enable debug logs pipeargs = [] # Holds arguments piped to the program _VERSION_ = 1.9 # Program version @@ -597,7 +596,8 @@ def cleardb(conn, cur, index): def printdb(cur, index, empty=False): """Print bookmark details at index or all bookmarks if index is None - Print only bookmarks with empty title or tags if empty is True + Print only bookmarks with blank title or tags if empty is True + Note: URL is printed on top because title may be blank Params: cursor, index to print, flag to show only bookmarks with no title or tags """ @@ -748,7 +748,7 @@ def fetchopen(index): """ try: - for row in cur.execute("SELECT URL FROM bookmarks WHERE id = ?", (int(index),)): + for row in cur.execute("SELECT URL FROM bookmarks WHERE id = ?", (index,)): url = unquote(row[0]) browser_open(url) return @@ -927,6 +927,7 @@ def decrypt_file(): if dbhash != enchash: os.remove(dbpath) print("Decryption failed"); + sys.exit(1) else: os.remove(encpath) print("File decrypted") @@ -1049,13 +1050,13 @@ addarg('-k', '--decrypt', dest='decrypt', action='store_true', help='decrypt (unlock) database file') addarg('-l', '--encrypt', dest='encrypt', action='store_true', # DONE help='encrypt (lock) database file') -addarg('-o', '--open', dest='openurl', type=int, metavar='N', +addarg('-o', '--open', dest='openurl', type=int, metavar='N', # DONE help='open URL at DB index N in browser') addarg('-p', '--print', dest='showindex', type=int, metavar='N', help='show details of bookmark record at DB index N, N=0 shows all') -addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), +addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), # DONE help='replace oldtag with newtag, delete oldtag if newtag=none') -addarg('-t', '--iterate', dest='iterations', type=int, metavar='N', +addarg('-t', '--iterate', dest='iterations', type=int, metavar='N', # DONE help='use N (> 0) hash iterations to generate key, for -k, -l') addarg('-x', '--format', dest='showOpt', metavar='N', help='modify -p behaviour, N=1: show only URL, N=2: show URL and tag') @@ -1188,6 +1189,8 @@ except GetoptError as e: args = argparser.parse_args() titleManual = args.titleManual +if args.iterations is not None: + iterations = args.iterations jsonOutput = args.jsonOutput debug = args.debug keywords = [] @@ -1212,7 +1215,7 @@ if args.decrypt == True: sys.exit(1) decrypt_file() -# Initilize the database and get handles +# Initialize the database and get handles conn, cur = initdb() # Add a record @@ -1256,8 +1259,8 @@ if args.empty == True: printdb(cur, 0, True) # Open URL in browser -if openurl != None: - fetchopen(openurl) +if args.openurl is not None: + fetchopen(args.openurl) # Close the connection before exiting conn.close() From 0fb89072d37515c4ace0e24ad671426ddf1bbe12 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 03:35:46 +0530 Subject: [PATCH 04/24] Support print function. --- buku | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/buku b/buku index ebe912c..cb6f1f1 100755 --- a/buku +++ b/buku @@ -51,7 +51,6 @@ except ImportError: # Globals addindex = None # DB index to insert URL into -showindex = None # Index of bookmark to show showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag entry = None # DB index to update or delete update = False # Update a bookmark in DB @@ -408,7 +407,7 @@ def AddUpdateEntry(conn, cur, keywords, index): cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags) VALUES (?, ?, ?, ?)', (int(addindex), url, meta, tags,)) conn.commit() print("Added at index %d\n" % cur.lastrowid) - printdb(cur, str(cur.lastrowid)) + printdb(cur, cur.lastrowid) except sqlite3.IntegrityError: for row in cur.execute("SELECT id from bookmarks where URL LIKE ?", (url,)): print("URL already exists at index %s" % row[0]) @@ -596,7 +595,7 @@ def cleardb(conn, cur, index): def printdb(cur, index, empty=False): """Print bookmark details at index or all bookmarks if index is None - Print only bookmarks with blank title or tags if empty is True + Print only bookmarks with blank title or tag if empty is True Note: URL is printed on top because title may be blank Params: cursor, index to print, flag to show only bookmarks with no title or tags @@ -606,7 +605,7 @@ def printdb(cur, index, empty=False): global jsonOutput resultset = None - if int(index) == 0: # Show all entries + if index == 0: # Show all entries if empty == False: cur.execute('SELECT * FROM bookmarks') resultset = cur.fetchall() @@ -630,7 +629,7 @@ def printdb(cur, index, empty=False): print(formatJson(resultset)) else: # Show record at index try: - resultset = cur.execute("SELECT * FROM bookmarks WHERE id = ?", (int(index),)) + resultset = cur.execute("SELECT * FROM bookmarks WHERE id = ?", (index,)) except IndexError: print("Index out of bound") return @@ -894,7 +893,7 @@ def decrypt_file(): password = '' password = getpass.getpass() if password == '': - print("Decryption failed"); + printmsg("Decryption failed", "ERROR"); sys.exit(1) with open(encpath, 'rb') as infile: @@ -926,7 +925,7 @@ def decrypt_file(): dbhash = get_filehash(dbpath) if dbhash != enchash: os.remove(dbpath) - print("Decryption failed"); + printmsg("Decryption failed", "ERROR"); sys.exit(1) else: os.remove(encpath) @@ -1044,7 +1043,7 @@ addarg('-e', '--empty', dest='empty', action='store_true', help='show bookmarks with empty titles or no tags') addarg('-i', '--insert', dest='insert', nargs='+', metavar=('N', 'URL tags'), help='insert new bookmark with URL and tags at free DB index N') -addarg('-j', '--json', dest='jsonOutput', action='store_true', +addarg('-j', '--json', dest='jsonOutput', action='store_true', # DONE help='show results in Json format') addarg('-k', '--decrypt', dest='decrypt', action='store_true', # DONE help='decrypt (unlock) database file') @@ -1052,13 +1051,13 @@ addarg('-l', '--encrypt', dest='encrypt', action='store_true', help='encrypt (lock) database file') addarg('-o', '--open', dest='openurl', type=int, metavar='N', # DONE help='open URL at DB index N in browser') -addarg('-p', '--print', dest='showindex', type=int, metavar='N', +addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', # DONE help='show details of bookmark record at DB index N, N=0 shows all') addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), # DONE help='replace oldtag with newtag, delete oldtag if newtag=none') addarg('-t', '--iterate', dest='iterations', type=int, metavar='N', # DONE help='use N (> 0) hash iterations to generate key, for -k, -l') -addarg('-x', '--format', dest='showOpt', metavar='N', +addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', # DONE help='modify -p behaviour, N=1: show only URL, N=2: show URL and tag') addarg('-z', '--debug', dest='debug', action='store_true', # DONE help='show debug information') @@ -1188,6 +1187,9 @@ except GetoptError as e: args = argparser.parse_args() +# Assign the values to globals +if args.showOpt is not None: + showOpt = args.showOpt titleManual = args.titleManual if args.iterations is not None: iterations = args.iterations @@ -1207,12 +1209,18 @@ if args.encrypt == True: if no_crypto: printmsg("PyCrypto missing", "ERROR") sys.exit(1) + if iterations < 1: + printmsg("Iterations must be >= 1", "ERROR") + sys.exit(1) encrypt_file() if args.decrypt == True: if no_crypto: printmsg("PyCrypto missing", "ERROR") sys.exit(1) + if iterations < 1: + printmsg("Decryption failed", "ERROR"); + sys.exit(1) decrypt_file() # Initialize the database and get handles @@ -1224,6 +1232,10 @@ if args.addurl is not None: # Remove a single record or all records if args.delete is not None: + if args.delete < 0: + printmsg("Index must be >= 0", "ERROR") + conn.close() + sys.exit(1) cleardb(conn, cur, args.delete) # Show all unique tags @@ -1252,14 +1264,23 @@ if update == True: AddUpdateEntry(conn, cur, keywords, entry) # Print all records -if showindex is not None: - printdb(cur, showindex) +if args.printindex is not None: + if args.printindex < 0: + printmsg("Index must be >= 0", "ERROR") + conn.close() + sys.exit(1) + printdb(cur, args.printindex) +# Print records with blank title or tag if args.empty == True: printdb(cur, 0, True) # Open URL in browser if args.openurl is not None: + if args.openurl < 1: + printmsg("Index must be >= 1", "ERROR") + conn.close() + sys.exit(1) fetchopen(args.openurl) # Close the connection before exiting From 7a0d98e09f280a2b48af00a9ecbc76a2bf3fa76e Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 04:17:25 +0530 Subject: [PATCH 05/24] Remove redundant variable. --- buku | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/buku b/buku index cb6f1f1..3fe5b41 100755 --- a/buku +++ b/buku @@ -1250,11 +1250,10 @@ if args.sall is not None: # Replace a tag in DB if args.replace is not None: - keywords = args.replace - if keywords[1] == "none": - replaceTags(conn, cur, keywords[0], "") + if args.replace[1] == "none": + replaceTags(conn, cur, args.replace[0], "") else: - replaceTags(conn, cur, keywords[0], keywords[1]) + replaceTags(conn, cur, args.replace[0], args.replace[1]) # Update record if update == True: From ef1ccfd59ef9c7f813d2ad9b47861e12b8c0d2ed Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 04:22:15 +0530 Subject: [PATCH 06/24] Push lesser option down. --- buku | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/buku b/buku index 3fe5b41..a4ed7e5 100755 --- a/buku +++ b/buku @@ -1248,13 +1248,6 @@ if args.sany is not None: if args.sall is not None: searchdb(cur, args.sall, True) -# Replace a tag in DB -if args.replace is not None: - if args.replace[1] == "none": - replaceTags(conn, cur, args.replace[0], "") - else: - replaceTags(conn, cur, args.replace[0], args.replace[1]) - # Update record if update == True: if len(keywords) < 1: @@ -1270,6 +1263,13 @@ if args.printindex is not None: sys.exit(1) printdb(cur, args.printindex) +# Replace a tag in DB +if args.replace is not None: + if args.replace[1] == "none": + replaceTags(conn, cur, args.replace[0], "") + else: + replaceTags(conn, cur, args.replace[0], args.replace[1]) + # Print records with blank title or tag if args.empty == True: printdb(cur, 0, True) From fb2c886ec550b1b2341ff59134b052b4adb5b23c Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 18:36:23 +0530 Subject: [PATCH 07/24] insert functional but deprecated by auto-compact. --- buku | 55 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/buku b/buku index a4ed7e5..98a2f40 100755 --- a/buku +++ b/buku @@ -50,7 +50,6 @@ except ImportError: # Globals -addindex = None # DB index to insert URL into showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag entry = None # DB index to update or delete update = False # Update a bookmark in DB @@ -347,11 +346,11 @@ def isBookmarkAdded(cur, url): -def AddUpdateEntry(conn, cur, keywords, index): - """Add a new bookmark or update an existing record at index - or insert a new record at addindex (if empty) +def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0): + """Add a new bookmark or update an existing record at + updateindex or insert a new record at insertindex (if empty) - Params: connection, cursor, index to update + Params: connection, cursor, keywords, index to update, index to insert at """ global titleManual @@ -362,7 +361,7 @@ def AddUpdateEntry(conn, cur, keywords, index): """In case of an add or insert operation ensure that the URL does not exist in DB already """ - if index is None: + if updateindex is None: id = isBookmarkAdded(cur, url) if id != -1: print("URL already exists at index %d" % id) @@ -387,7 +386,7 @@ def AddUpdateEntry(conn, cur, keywords, index): if tags[-1] != ',': tags += ',' - if titleManual != None: + if titleManual is not None: if titleManual == "none": meta = '' else: @@ -399,12 +398,12 @@ def AddUpdateEntry(conn, cur, keywords, index): else: print("Title: [%s]" % meta) - if index == None: # Insert a new entry + if updateindex is None: # Add or insert a new entry try: - if addindex == None: # addindex is index number to insert record at + if insertindex == 0: # insertindex is index number to insert record at cur.execute('INSERT INTO bookmarks(URL, metadata, tags) VALUES (?, ?, ?)', (url, meta, tags,)) else: - cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags) VALUES (?, ?, ?, ?)', (int(addindex), url, meta, tags,)) + cur.execute('INSERT INTO bookmarks(id, URL, metadata, tags) VALUES (?, ?, ?, ?)', (insertindex, url, meta, tags,)) conn.commit() print("Added at index %d\n" % cur.lastrowid) printdb(cur, cur.lastrowid) @@ -413,14 +412,14 @@ def AddUpdateEntry(conn, cur, keywords, index): print("URL already exists at index %s" % row[0]) return - print("Index %s exists" % addindex) + print("Index %d exists" % insertindex) else: # Update an existing entry try: - cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ? WHERE id = ?", (url, meta, tags, int(index),)) + cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ? WHERE id = ?", (url, meta, tags, int(updateindex),)) conn.commit() if cur.rowcount == 1: - print("Updated index %d\n" % int(index)) - printdb(cur, int(index)) + print("Updated index %d\n" % int(updateindex)) + printdb(cur, int(updateindex)) else: print("No matching index") except sqlite3.IntegrityError: @@ -475,7 +474,7 @@ def searchdb(cur, keywords, all_keywords=False): """Search the database for an entries with tags or URL or title info matching keywords and list those. - Params: cursor, keywords to search + Params: cursor, keywords to search, search any or all keywords """ global jsonOutput @@ -1041,8 +1040,8 @@ addarg('-u', '--update', nargs='+', dest='update', metavar=('N', 'URL tags'), help='update fields of the entry at DB index N. The first keyword, if available, is treated as the URL. If URL is omitted (and -m is not used) the title of entry at index N is refreshed from the web, N=0 refreshes all titles.') addarg('-e', '--empty', dest='empty', action='store_true', # DONE help='show bookmarks with empty titles or no tags') -addarg('-i', '--insert', dest='insert', nargs='+', metavar=('N', 'URL tags'), - help='insert new bookmark with URL and tags at free DB index N') +#addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), +# help='insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted') addarg('-j', '--json', dest='jsonOutput', action='store_true', # DONE help='show results in Json format') addarg('-k', '--decrypt', dest='decrypt', action='store_true', # DONE @@ -1109,8 +1108,8 @@ try: if not opt[1].isdigit(): usage() - addindex = opt[1] - if int(addindex) <= 0: + insertindex = opt[1] + if int(insertindex) <= 0: usage() addurl = True @@ -1274,6 +1273,24 @@ if args.replace is not None: if args.empty == True: printdb(cur, 0, True) +''' +# Insert a record at an index +if args.insert is not None: + if not args.insert[0].isdigit(): + printmsg("Index must be a number >= 1", "ERROR") + conn.close() + sys.exit(1) + insertindex = int(args.insert[0]) + if insertindex < 1: + printmsg("Index must be a number >= 1", "ERROR") + conn.close() + sys.exit(1) + if len(args.insert) == 1: + pass # We need to add logic here to empty + else: + AddUpdateEntry(conn, cur, args.insert[1:], None, insertindex) +''' + # Open URL in browser if args.openurl is not None: if args.openurl < 1: From ff42af341f45237cc39e6a823516c584dfa1929f Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 21:53:03 +0530 Subject: [PATCH 08/24] Support update. --- buku | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/buku b/buku index 98a2f40..28de8de 100755 --- a/buku +++ b/buku @@ -50,13 +50,12 @@ except ImportError: # Globals -showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag -entry = None # DB index to update or delete update = False # Update a bookmark in DB titleData = None # Title fetched from a page titleManual = None # Manually add a title offline iterations = 8 # Number of hash iterations to generate key jsonOutput = False # Output json formatted result +showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag debug = False # Enable debug logs pipeargs = [] # Holds arguments piped to the program _VERSION_ = 1.9 # Program version @@ -361,7 +360,7 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0): """In case of an add or insert operation ensure that the URL does not exist in DB already """ - if updateindex is None: + if updateindex == 0: id = isBookmarkAdded(cur, url) if id != -1: print("URL already exists at index %d" % id) @@ -398,7 +397,7 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0): else: print("Title: [%s]" % meta) - if updateindex is None: # Add or insert a new entry + if updateindex == 0: # Add or insert a new entry try: if insertindex == 0: # insertindex is index number to insert record at cur.execute('INSERT INTO bookmarks(URL, metadata, tags) VALUES (?, ?, ?)', (url, meta, tags,)) @@ -415,11 +414,11 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0): print("Index %d exists" % insertindex) else: # Update an existing entry try: - cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ? WHERE id = ?", (url, meta, tags, int(updateindex),)) + cur.execute("UPDATE bookmarks SET URL = ?, metadata = ?, tags = ? WHERE id = ?", (url, meta, tags, updateindex,)) conn.commit() if cur.rowcount == 1: - print("Updated index %d\n" % int(updateindex)) - printdb(cur, int(updateindex)) + print("Updated index %d\n" % updateindex) + printdb(cur, updateindex) else: print("No matching index") except sqlite3.IntegrityError: @@ -1013,6 +1012,14 @@ if __name__ == "__main__": except KeyboardInterrupt: pass +class customAction(argparse.Action): + def __call__(self, parser, args, values, option_string=None): + global update + + update = True + # NOTE: the following converts a None argument to an empty array [] + setattr(args, self.dest, values) + class ExtendedArgumentParser(argparse.ArgumentParser): def print_help(self, file=None): @@ -1036,7 +1043,7 @@ addarg('-s', '--sany', nargs='+', metavar='KEYWORD', help='search bookmarks for any keyword') addarg('-S', '--sall', nargs='+', metavar='KEYWORD', # DONE help='search bookmarks with all keywords') -addarg('-u', '--update', nargs='+', dest='update', metavar=('N', 'URL tags'), +addarg('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), help='update fields of the entry at DB index N. The first keyword, if available, is treated as the URL. If URL is omitted (and -m is not used) the title of entry at index N is refreshed from the web, N=0 refreshes all titles.') addarg('-e', '--empty', dest='empty', action='store_true', # DONE help='show bookmarks with empty titles or no tags') @@ -1227,7 +1234,7 @@ conn, cur = initdb() # Add a record if args.addurl is not None: - AddUpdateEntry(conn, cur, args.addurl, None) + AddUpdateEntry(conn, cur, args.addurl, 0) # Remove a single record or all records if args.delete is not None: @@ -1249,10 +1256,20 @@ if args.sall is not None: # Update record if update == True: - if len(keywords) < 1: - dbRefresh(conn, cur, int(entry)) + if len(args.update) == 0: + dbRefresh(conn, cur, 0) + elif not args.update[0].isdigit(): + printmsg("Index must be a number >= 0", "ERROR") + conn.close() + sys.exit(1) + elif int(args.update[0]) == 0: + dbRefresh(conn, cur, 0) + elif len(args.update) == 1: + printmsg("At least URL should be provided for non-zero index", "ERROR") + conn.close() + sys.exit(1) else: - AddUpdateEntry(conn, cur, keywords, entry) + AddUpdateEntry(conn, cur, args.update[1:], int(args.update[0])) # Print all records if args.printindex is not None: @@ -1288,7 +1305,7 @@ if args.insert is not None: if len(args.insert) == 1: pass # We need to add logic here to empty else: - AddUpdateEntry(conn, cur, args.insert[1:], None, insertindex) + AddUpdateEntry(conn, cur, args.insert[1:], 0, insertindex) ''' # Open URL in browser From c95d3a4339b41dca42a3f9bde340c6d537baa925 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 22:58:35 +0530 Subject: [PATCH 09/24] Integrate iterations in encrypt/decrypt args. --- buku | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/buku b/buku index 28de8de..49bef24 100755 --- a/buku +++ b/buku @@ -53,7 +53,6 @@ except ImportError: update = False # Update a bookmark in DB titleData = None # Title fetched from a page titleManual = None # Manually add a title offline -iterations = 8 # Number of hash iterations to generate key jsonOutput = False # Output json formatted result showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag debug = False # Enable debug logs @@ -813,7 +812,7 @@ def get_filehash(filepath): -def encrypt_file(): +def encrypt_file(iterations): """Encrypt the bookmarks database file""" dbpath = os.path.join(getDataPath(), 'bookmarks.db') @@ -874,7 +873,7 @@ def encrypt_file(): -def decrypt_file(): +def decrypt_file(iterations): """Decrypt the bookmarks database file""" dbpath = os.path.join(getDataPath(), 'bookmarks.db') @@ -1051,18 +1050,16 @@ addarg('-e', '--empty', dest='empty', action='store_true', # help='insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted') addarg('-j', '--json', dest='jsonOutput', action='store_true', # DONE help='show results in Json format') -addarg('-k', '--decrypt', dest='decrypt', action='store_true', # DONE - help='decrypt (unlock) database file') -addarg('-l', '--encrypt', dest='encrypt', action='store_true', # DONE - help='encrypt (lock) database file') +addarg('-k', '--decrypt', nargs='?', dest='decrypt', type=int, const=8, metavar='N', # DONE + help='decrypt (unlock) database file with N (> 0) hash iterations to generate key, default 8') +addarg('-l', '--encrypt', nargs='?', dest='encrypt', type=int, const=8, metavar='N', # DONE + help='encrypt (lock) database file with N (> 0) hash iterations to generate key, default 8') addarg('-o', '--open', dest='openurl', type=int, metavar='N', # DONE help='open URL at DB index N in browser') addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', # DONE help='show details of bookmark record at DB index N, N=0 shows all') addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), # DONE help='replace oldtag with newtag, delete oldtag if newtag=none') -addarg('-t', '--iterate', dest='iterations', type=int, metavar='N', # DONE - help='use N (> 0) hash iterations to generate key, for -k, -l') addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', # DONE help='modify -p behaviour, N=1: show only URL, N=2: show URL and tag') addarg('-z', '--debug', dest='debug', action='store_true', # DONE @@ -1197,8 +1194,6 @@ args = argparser.parse_args() if args.showOpt is not None: showOpt = args.showOpt titleManual = args.titleManual -if args.iterations is not None: - iterations = args.iterations jsonOutput = args.jsonOutput debug = args.debug keywords = [] @@ -1211,23 +1206,23 @@ if debug: moveOldDatabase() # Handle encrypt/decrypt options at top priority -if args.encrypt == True: +if args.encrypt is not None: if no_crypto: printmsg("PyCrypto missing", "ERROR") sys.exit(1) - if iterations < 1: + if args.encrypt < 1: printmsg("Iterations must be >= 1", "ERROR") sys.exit(1) - encrypt_file() + encrypt_file(args.encrypt) -if args.decrypt == True: +if args.decrypt is not None: if no_crypto: printmsg("PyCrypto missing", "ERROR") sys.exit(1) - if iterations < 1: + if args.decrypt < 1: printmsg("Decryption failed", "ERROR"); sys.exit(1) - decrypt_file() + decrypt_file(args.decrypt) # Initialize the database and get handles conn, cur = initdb() From fc211b5d66878eab1c6b833306441d11b4c9529f Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 23:03:07 +0530 Subject: [PATCH 10/24] Add note on why insert is commented. --- buku | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/buku b/buku index 49bef24..d550c9d 100755 --- a/buku +++ b/buku @@ -1285,7 +1285,9 @@ if args.replace is not None: if args.empty == True: printdb(cur, 0, True) -''' +"""NOTE: Insert is functional but commented +because DB compaction serves the purpose. + # Insert a record at an index if args.insert is not None: if not args.insert[0].isdigit(): @@ -1301,7 +1303,7 @@ if args.insert is not None: pass # We need to add logic here to empty else: AddUpdateEntry(conn, cur, args.insert[1:], 0, insertindex) -''' +""" # Open URL in browser if args.openurl is not None: From 892ad3a0406990b371509598d26dd63db83e4ec4 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 23:27:01 +0530 Subject: [PATCH 11/24] Add class comments, remove function comments. --- buku | 156 +++++++++-------------------------------------------------- 1 file changed, 22 insertions(+), 134 deletions(-) diff --git a/buku b/buku index d550c9d..49e0ab1 100755 --- a/buku +++ b/buku @@ -1012,6 +1012,10 @@ if __name__ == "__main__": pass class customAction(argparse.Action): + """Class to capture if an optional param + is actually used, even if sans arguments + """ + def __call__(self, parser, args, values, option_string=None): global update @@ -1020,6 +1024,7 @@ class customAction(argparse.Action): setattr(args, self.dest, values) class ExtendedArgumentParser(argparse.ArgumentParser): + """Extend classic argument parser""" def print_help(self, file=None): super(ExtendedArgumentParser, self).print_help(file) @@ -1030,164 +1035,47 @@ argparser = ExtendedArgumentParser( ) addarg = argparser.add_argument -addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), # DONE +addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help='add URL as bookmark with comma separated tags') -addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', # DONE +addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help='delete entry at DB index N (from -p 0), N=0 deletes all') -addarg('-g', '--tags', dest='showTags', action='store_true', # DONE +addarg('-g', '--tags', dest='showTags', action='store_true', help='list all tags alphabetically') addarg('-m', '--title', dest='titleManual', metavar='title', help="manually set title, for -a, -i, -u; '-m none' clears title") -addarg('-s', '--sany', nargs='+', metavar='KEYWORD', # DONE +addarg('-s', '--sany', nargs='+', metavar='KEYWORD', help='search bookmarks for any keyword') -addarg('-S', '--sall', nargs='+', metavar='KEYWORD', # DONE +addarg('-S', '--sall', nargs='+', metavar='KEYWORD', help='search bookmarks with all keywords') addarg('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), - help='update fields of the entry at DB index N. The first keyword, if available, is treated as the URL. If URL is omitted (and -m is not used) the title of entry at index N is refreshed from the web, N=0 refreshes all titles.') -addarg('-e', '--empty', dest='empty', action='store_true', # DONE + help='update fields of the entry at DB index N. The first keyword, ' + 'if available, is treated as the URL. If URL is omitted (and -m is ' + 'not used) the title of entry at index N is refreshed from the web, N=0 refreshes all titles.') +addarg('-e', '--empty', dest='empty', action='store_true', help='show bookmarks with empty titles or no tags') #addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), # help='insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted') -addarg('-j', '--json', dest='jsonOutput', action='store_true', # DONE +addarg('-j', '--json', dest='jsonOutput', action='store_true', help='show results in Json format') -addarg('-k', '--decrypt', nargs='?', dest='decrypt', type=int, const=8, metavar='N', # DONE +addarg('-k', '--decrypt', nargs='?', dest='decrypt', type=int, const=8, metavar='N', help='decrypt (unlock) database file with N (> 0) hash iterations to generate key, default 8') -addarg('-l', '--encrypt', nargs='?', dest='encrypt', type=int, const=8, metavar='N', # DONE +addarg('-l', '--encrypt', nargs='?', dest='encrypt', type=int, const=8, metavar='N', help='encrypt (lock) database file with N (> 0) hash iterations to generate key, default 8') -addarg('-o', '--open', dest='openurl', type=int, metavar='N', # DONE +addarg('-o', '--open', dest='openurl', type=int, metavar='N', help='open URL at DB index N in browser') -addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', # DONE +addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', help='show details of bookmark record at DB index N, N=0 shows all') -addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), # DONE +addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), help='replace oldtag with newtag, delete oldtag if newtag=none') -addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', # DONE +addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', help='modify -p behaviour, N=1: show only URL, N=2: show URL and tag') -addarg('-z', '--debug', dest='debug', action='store_true', # DONE +addarg('-z', '--debug', dest='debug', action='store_true', help='show debug information') if len(sys.argv) < 2: argparser.print_help(sys.stderr) sys.exit(1) -''' -# Check cmdline options -try: - - if len(pipeargs) > 0: - optlist, keywords = getopt(pipeargs[1:], "d:i:m:o:p:t:u:x:aegjklrsSz") - else: - optlist, keywords = getopt(sys.argv[1:], "d:i:m:o:p:t:u:x:aegjklrsSz") - if len(optlist) < 1: - usage() - - for opt in optlist: - if opt[0] == "-a": - if update == True or delete == True: - print("You can either add or update or delete in one instance\n") - usage() - - addurl = True - elif opt[0] == "-d": - if addurl == True or update == True: - print("You can either add or update or delete in one instance\n") - usage() - - if not opt[1].isdigit(): - usage() - - entry = opt[1] - if int(entry) < 0: - usage() - - delete = True - elif opt[0] == "-e": - empty = True - elif opt[0] == "-g": - showTags = True - elif opt[0] == "-i": - if update == True or delete == True: - print("You can either add or update or delete in one instance\n") - usage() - - if not opt[1].isdigit(): - usage() - - insertindex = opt[1] - if int(insertindex) <= 0: - usage() - - addurl = True - elif opt[0] == "-j": - jsonOutput = True - elif opt[0] == "-k": - if no_crypto == True: - printmsg("PyCrypto missing", "ERROR") - sys.exit(0) - - decrypt = True - elif opt[0] == "-l": - if no_crypto == True: - printmsg("PyCrypto missing", "ERROR") - sys.exit(0) - - encrypt = True - elif opt[0] == "-m": - titleManual = opt[1] - elif opt[0] == "-o": - if not opt[1].isdigit(): - usage() - - openurl = opt[1] - if int(openurl) <= 0: - usage() - elif opt[0] == "-p": - if not opt[1].isdigit(): - usage() - - showindex = opt[1] - if int(showindex) < 0: - usage() - elif opt[0] == "-r": - replace = True - elif opt[0] == "-s": - search = True - elif opt[0] == "-S": - searchAll = True - search = True - elif opt[0] == "-t": - if not opt[1].isdigit(): - usage() - - iterations = int(opt[1]) - if iterations <= 0: - usage() - elif opt[0] == "-u": - if addurl == True or delete == True: - print("You can either add or update or delete in one instance\n") - usage() - - if not opt[1].isdigit(): - usage() - - entry = opt[1] - if int(entry) < 0: - usage() - - update = True - elif opt[0] == "-x": - if not opt[1].isdigit(): - usage() - - showOpt = int(opt[1]) - if showOpt < 1 or showOpt > 2: - usage() - elif opt[0] == "-z": - debug = True -except GetoptError as e: - print("buku:", e) - sys.exit(1) -''' - args = argparser.parse_args() # Assign the values to globals From b4e4744b8dac0b22a49fd99b5fda1e53fcda24f4 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Mon, 25 Apr 2016 23:46:13 +0530 Subject: [PATCH 12/24] Better help. --- buku | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/buku b/buku index 49e0ab1..7af2a9e 100755 --- a/buku +++ b/buku @@ -1038,7 +1038,7 @@ addarg = argparser.add_argument addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help='add URL as bookmark with comma separated tags') addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', - help='delete entry at DB index N (from -p 0), N=0 deletes all') + help='delete entry at DB index N (from -p 0), or all, if N is omitted') addarg('-g', '--tags', dest='showTags', action='store_true', help='list all tags alphabetically') addarg('-m', '--title', dest='titleManual', metavar='title', @@ -1048,9 +1048,9 @@ addarg('-s', '--sany', nargs='+', metavar='KEYWORD', addarg('-S', '--sall', nargs='+', metavar='KEYWORD', help='search bookmarks with all keywords') addarg('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), - help='update fields of the entry at DB index N. The first keyword, ' - 'if available, is treated as the URL. If URL is omitted (and -m is ' - 'not used) the title of entry at index N is refreshed from the web, N=0 refreshes all titles.') + help='update fields of the entry at DB index N. If URL is omitted (and -m is ' + 'not used) the title of entry at index N is refreshed from the web. ' + 'All titles are refreshed if N is omitted.') addarg('-e', '--empty', dest='empty', action='store_true', help='show bookmarks with empty titles or no tags') #addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), @@ -1058,13 +1058,13 @@ addarg('-e', '--empty', dest='empty', action='store_true', addarg('-j', '--json', dest='jsonOutput', action='store_true', help='show results in Json format') addarg('-k', '--decrypt', nargs='?', dest='decrypt', type=int, const=8, metavar='N', - help='decrypt (unlock) database file with N (> 0) hash iterations to generate key, default 8') + help='decrypt (unlock) database file with N (> 0, default 8) hash iterations to generate key') addarg('-l', '--encrypt', nargs='?', dest='encrypt', type=int, const=8, metavar='N', - help='encrypt (lock) database file with N (> 0) hash iterations to generate key, default 8') + help='encrypt (lock) database file with N (> 0, default 8) hash iterations to generate key') addarg('-o', '--open', dest='openurl', type=int, metavar='N', help='open URL at DB index N in browser') addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', - help='show details of bookmark record at DB index N, N=0 shows all') + help='show details of bookmark at DB index N, or all, if N is omitted') addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), help='replace oldtag with newtag, delete oldtag if newtag=none') addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', From 422a51705ca9348858175857067ff2c94c5cb18b Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Tue, 26 Apr 2016 00:13:28 +0530 Subject: [PATCH 13/24] Merge -g and -e to -S. --- buku | 50 +++++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/buku b/buku index 7af2a9e..13476ec 100755 --- a/buku +++ b/buku @@ -58,6 +58,7 @@ showOpt = 0 # Modify show. 1: show only URL, 2: show URL and tag debug = False # Enable debug logs pipeargs = [] # Holds arguments piped to the program _VERSION_ = 1.9 # Program version +BLANK = 'blank' class BMHTMLParser(HTMLParser.HTMLParser): @@ -385,7 +386,7 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0): tags += ',' if titleManual is not None: - if titleManual == "none": + if titleManual == BLANK: meta = '' else: meta = titleManual @@ -456,7 +457,7 @@ def dbRefresh(conn, cur, index): conn.commit() print("Updated index %d\n" % row[0]) else: - if titleManual == "none": + if titleManual == BLANK: title = '' else: title = titleManual @@ -1039,10 +1040,8 @@ addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help='add URL as bookmark with comma separated tags') addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help='delete entry at DB index N (from -p 0), or all, if N is omitted') -addarg('-g', '--tags', dest='showTags', action='store_true', - help='list all tags alphabetically') addarg('-m', '--title', dest='titleManual', metavar='title', - help="manually set title, for -a, -i, -u; '-m none' clears title") + help="manually set title, for -a, -i, -u; title='blank' clears title") addarg('-s', '--sany', nargs='+', metavar='KEYWORD', help='search bookmarks for any keyword') addarg('-S', '--sall', nargs='+', metavar='KEYWORD', @@ -1051,8 +1050,6 @@ addarg('-u', '--update', nargs='*', dest='update', action=customAction, metavar= help='update fields of the entry at DB index N. If URL is omitted (and -m is ' 'not used) the title of entry at index N is refreshed from the web. ' 'All titles are refreshed if N is omitted.') -addarg('-e', '--empty', dest='empty', action='store_true', - help='show bookmarks with empty titles or no tags') #addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), # help='insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted') addarg('-j', '--json', dest='jsonOutput', action='store_true', @@ -1066,7 +1063,7 @@ addarg('-o', '--open', dest='openurl', type=int, metavar='N', addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', help='show details of bookmark at DB index N, or all, if N is omitted') addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), - help='replace oldtag with newtag, delete oldtag if newtag=none') + help="replace oldtag with newtag, delete oldtag if newtag='blank'") addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', help='modify -p behaviour, N=1: show only URL, N=2: show URL and tag') addarg('-z', '--debug', dest='debug', action='store_true', @@ -1127,15 +1124,18 @@ if args.delete is not None: sys.exit(1) cleardb(conn, cur, args.delete) -# Show all unique tags -if args.showTags == True: - showUniqueTags(cur) - -# Search URLs, titles, tags for keywords +# Search URLs, titles, tags for any keyword if args.sany is not None: searchdb(cur, args.sany) + +# Search URLs, titles, tags with all keywords if args.sall is not None: - searchdb(cur, args.sall, True) + if args.sall[0] == 'tags' and len(args.sall) == 1: + showUniqueTags(cur) + elif args.sall[0] == BLANK and len(args.sall) == 1: + printdb(cur, 0, True) + else: + searchdb(cur, args.sall, True) # Update record if update == True: @@ -1164,14 +1164,18 @@ if args.printindex is not None: # Replace a tag in DB if args.replace is not None: - if args.replace[1] == "none": + if args.replace[1] == BLANK: replaceTags(conn, cur, args.replace[0], "") else: replaceTags(conn, cur, args.replace[0], args.replace[1]) -# Print records with blank title or tag -if args.empty == True: - printdb(cur, 0, True) +# Open URL in browser +if args.openurl is not None: + if args.openurl < 1: + printmsg("Index must be >= 1", "ERROR") + conn.close() + sys.exit(1) + fetchopen(args.openurl) """NOTE: Insert is functional but commented because DB compaction serves the purpose. @@ -1188,18 +1192,10 @@ if args.insert is not None: conn.close() sys.exit(1) if len(args.insert) == 1: - pass # We need to add logic here to empty + pass # No operation else: AddUpdateEntry(conn, cur, args.insert[1:], 0, insertindex) """ -# Open URL in browser -if args.openurl is not None: - if args.openurl < 1: - printmsg("Index must be >= 1", "ERROR") - conn.close() - sys.exit(1) - fetchopen(args.openurl) - # Close the connection before exiting conn.close() From 923b5b7e34d4a13b120cec7f5ea772adab47d1ac Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Tue, 26 Apr 2016 02:15:10 +0530 Subject: [PATCH 14/24] Reformat help. --- buku | 84 +++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/buku b/buku index 13476ec..5d74294 100755 --- a/buku +++ b/buku @@ -21,6 +21,7 @@ import sys import os import sqlite3 import argparse +from argparse import RawTextHelpFormatter import readline import webbrowser import html.parser as HTMLParser @@ -31,6 +32,7 @@ import io import signal import json import shutil +import textwrap # Import libraries needed for encryption try: @@ -1027,47 +1029,87 @@ class customAction(argparse.Action): class ExtendedArgumentParser(argparse.ArgumentParser): """Extend classic argument parser""" + # Print additional help and info + @staticmethod + def print_extended_help(file=None): + file.write(textwrap.dedent(""" + prompt keys: + 1-N open Nth search result in browser + Enter exit buku + + Version %.1f + Copyright (C) 2015 Arun Prakash Jana + License: GPLv3 + Webpage: https://github.com/jarun/buku + """ % _VERSION_)) + + # Help def print_help(self, file=None): super(ExtendedArgumentParser, self).print_help(file) + self.print_extended_help(file) argparser = ExtendedArgumentParser( add_help=False, - description='A private cmdline bookmark manager. Your mini web!' + description='A private cmdline bookmark manager. Your mini web!', + formatter_class=RawTextHelpFormatter ) addarg = argparser.add_argument addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), - help='add URL as bookmark with comma separated tags') + help="bookmark URL with comma separated tags\n" + " ") addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', - help='delete entry at DB index N (from -p 0), or all, if N is omitted') -addarg('-m', '--title', dest='titleManual', metavar='title', - help="manually set title, for -a, -i, -u; title='blank' clears title") + help="delete bookmark at DB index N (from -p 0)\n" + "delete all bookmarks, if N is omitted\n" + " ") addarg('-s', '--sany', nargs='+', metavar='KEYWORD', - help='search bookmarks for any keyword') + help="search bookmarks for any keyword\n" + " ") addarg('-S', '--sall', nargs='+', metavar='KEYWORD', - help='search bookmarks with all keywords') + help="search bookmarks with all keywords\n" + "keyword='tags' : list all tags alphabetically\n" + "keyword='blank': list entries with empty title/tag\n" + " ") +addarg('-t', '--title', dest='titleManual', metavar='title', + help="manually set title, works with -a, -u\n" + "title='blank': clear title\n" + " ") addarg('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), - help='update fields of the entry at DB index N. If URL is omitted (and -m is ' - 'not used) the title of entry at index N is refreshed from the web. ' - 'All titles are refreshed if N is omitted.') + help="update fields of the bookmark at DB index N\n" + "if URL is omitted (and -m is not used), refresh\n" + "title of bookmark at index N from the web;\n" + "refresh all titles, if N is omitted\n" + " ") #addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), -# help='insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted') +# help=" insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted") addarg('-j', '--json', dest='jsonOutput', action='store_true', - help='show results in Json format') -addarg('-k', '--decrypt', nargs='?', dest='decrypt', type=int, const=8, metavar='N', - help='decrypt (unlock) database file with N (> 0, default 8) hash iterations to generate key') -addarg('-l', '--encrypt', nargs='?', dest='encrypt', type=int, const=8, metavar='N', - help='encrypt (lock) database file with N (> 0, default 8) hash iterations to generate key') + help="Json formatted output, works with -p, -s\n" + " ") +addarg('-k', '--unlock', nargs='?', dest='decrypt', type=int, const=8, metavar='N', + help="decrypt DB file with N (> 0, default 8)\n" + "hash iterations to generate key\n" + " ") +addarg('-l', '--lock', nargs='?', dest='encrypt', type=int, const=8, metavar='N', + help="encrypt DB file with N (> 0, default 8)\n" + "hash iterations to generate key\n" + " ") addarg('-o', '--open', dest='openurl', type=int, metavar='N', - help='open URL at DB index N in browser') + help="open bookmark at DB index N in browser\n" + " ") addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', - help='show details of bookmark at DB index N, or all, if N is omitted') + help="show details of bookmark at DB index N\n" + "show all bookmarks, if N is omitted\n" + " ") addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), - help="replace oldtag with newtag, delete oldtag if newtag='blank'") + help="replace oldtag with newtag\n" + "newtag='blank': delete oldtag\n" + " ") addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', - help='modify -p behaviour, N=1: show only URL, N=2: show URL and tag') + help="modify -p output\n" + "N=1: show only URL, N=2: show URL and tag\n" + " ") addarg('-z', '--debug', dest='debug', action='store_true', - help='show debug information') + help="show debug information") if len(sys.argv) < 2: argparser.print_help(sys.stderr) From aaa85e785796560240ec25a669f210abc8f668cf Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Tue, 26 Apr 2016 03:58:59 +0530 Subject: [PATCH 15/24] A more uniform help. --- buku | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/buku b/buku index 5d74294..670de95 100755 --- a/buku +++ b/buku @@ -1034,7 +1034,7 @@ class ExtendedArgumentParser(argparse.ArgumentParser): def print_extended_help(file=None): file.write(textwrap.dedent(""" prompt keys: - 1-N open Nth search result in browser + 1-N open the Nth search result in web browser Enter exit buku Version %.1f @@ -1061,25 +1061,26 @@ addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help="delete bookmark at DB index N (from -p 0)\n" "delete all bookmarks, if N is omitted\n" - " ") + " ") addarg('-s', '--sany', nargs='+', metavar='KEYWORD', - help="search bookmarks for any keyword\n" + help="search bookmarks for ANY matching keyword\n" " ") addarg('-S', '--sall', nargs='+', metavar='KEYWORD', - help="search bookmarks with all keywords\n" - "keyword='tags' : list all tags alphabetically\n" - "keyword='blank': list entries with empty title/tag\n" - " ") + help="search bookmarks with ALL keywords\n" + "special keywords:\n" + "'tags' - list all tags alphabetically\n" + "'blank'- list entries with empty title/tag\n" + " ") addarg('-t', '--title', dest='titleManual', metavar='title', help="manually set title, works with -a, -u\n" "title='blank': clear title\n" - " ") + " ") addarg('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), - help="update fields of the bookmark at DB index N\n" - "if URL is omitted (and -m is not used), refresh\n" + help="update fields of bookmark at DB index N\n" + "if URL is omitted (and -m unused), refresh\n" "title of bookmark at index N from the web;\n" "refresh all titles, if N is omitted\n" - " ") + " ") #addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), # help=" insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted") addarg('-j', '--json', dest='jsonOutput', action='store_true', @@ -1088,28 +1089,28 @@ addarg('-j', '--json', dest='jsonOutput', action='store_true', addarg('-k', '--unlock', nargs='?', dest='decrypt', type=int, const=8, metavar='N', help="decrypt DB file with N (> 0, default 8)\n" "hash iterations to generate key\n" - " ") + " ") addarg('-l', '--lock', nargs='?', dest='encrypt', type=int, const=8, metavar='N', help="encrypt DB file with N (> 0, default 8)\n" "hash iterations to generate key\n" - " ") + " ") addarg('-o', '--open', dest='openurl', type=int, metavar='N', - help="open bookmark at DB index N in browser\n" + help="open bookmark at DB index N in web browser\n" " ") addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', help="show details of bookmark at DB index N\n" "show all bookmarks, if N is omitted\n" - " ") + " ") addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), - help="replace oldtag with newtag\n" + help="replace oldtag with newtag for all bookmarks\n" "newtag='blank': delete oldtag\n" - " ") + " ") addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', help="modify -p output\n" "N=1: show only URL, N=2: show URL and tag\n" - " ") + " ") addarg('-z', '--debug', dest='debug', action='store_true', - help="show debug information") + help="show debug information and additional logs") if len(sys.argv) < 2: argparser.print_help(sys.stderr) From 185ca828d1e5bfce13ce0c9358a79ce3a64bd183 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Tue, 26 Apr 2016 04:25:49 +0530 Subject: [PATCH 16/24] Help re-format. --- buku | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/buku b/buku index 670de95..ba0994a 100755 --- a/buku +++ b/buku @@ -21,7 +21,6 @@ import sys import os import sqlite3 import argparse -from argparse import RawTextHelpFormatter import readline import webbrowser import html.parser as HTMLParser @@ -1051,64 +1050,51 @@ class ExtendedArgumentParser(argparse.ArgumentParser): argparser = ExtendedArgumentParser( add_help=False, description='A private cmdline bookmark manager. Your mini web!', - formatter_class=RawTextHelpFormatter + formatter_class=argparse.RawTextHelpFormatter ) addarg = argparser.add_argument addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), - help="bookmark URL with comma separated tags\n" - " ") + help="bookmark URL with comma separated tags") addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help="delete bookmark at DB index N (from -p 0)\n" - "delete all bookmarks, if N is omitted\n" - " ") + "delete all bookmarks, if N is omitted") addarg('-s', '--sany', nargs='+', metavar='KEYWORD', - help="search bookmarks for ANY matching keyword\n" - " ") + help="search bookmarks for ANY matching keyword") addarg('-S', '--sall', nargs='+', metavar='KEYWORD', help="search bookmarks with ALL keywords\n" "special keywords:\n" "'tags' - list all tags alphabetically\n" - "'blank'- list entries with empty title/tag\n" - " ") + "'blank'- list entries with empty title/tag") addarg('-t', '--title', dest='titleManual', metavar='title', help="manually set title, works with -a, -u\n" - "title='blank': clear title\n" - " ") + "title='blank': clear title") addarg('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), help="update fields of bookmark at DB index N\n" "if URL is omitted (and -m unused), refresh\n" "title of bookmark at index N from the web;\n" - "refresh all titles, if N is omitted\n" - " ") + "refresh all titles, if N is omitted") #addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), # help=" insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted") addarg('-j', '--json', dest='jsonOutput', action='store_true', - help="Json formatted output, works with -p, -s\n" - " ") + help="Json formatted output, works with -p, -s") addarg('-k', '--unlock', nargs='?', dest='decrypt', type=int, const=8, metavar='N', help="decrypt DB file with N (> 0, default 8)\n" - "hash iterations to generate key\n" - " ") + "hash iterations to generate key") addarg('-l', '--lock', nargs='?', dest='encrypt', type=int, const=8, metavar='N', help="encrypt DB file with N (> 0, default 8)\n" - "hash iterations to generate key\n" - " ") + "hash iterations to generate key") addarg('-o', '--open', dest='openurl', type=int, metavar='N', - help="open bookmark at DB index N in web browser\n" - " ") + help="open bookmark at DB index N in web browser") addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', help="show details of bookmark at DB index N\n" - "show all bookmarks, if N is omitted\n" - " ") + "show all bookmarks, if N is omitted") addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), help="replace oldtag with newtag for all bookmarks\n" - "newtag='blank': delete oldtag\n" - " ") + "newtag='blank': delete oldtag") addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', help="modify -p output\n" - "N=1: show only URL, N=2: show URL and tag\n" - " ") + "N=1: show only URL, N=2: show URL and tag") addarg('-z', '--debug', dest='debug', action='store_true', help="show debug information and additional logs") From e6b36ba89266eb39774e585d8156b28b87da78cb Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Tue, 26 Apr 2016 20:33:23 +0530 Subject: [PATCH 17/24] Add input and output files. Remove old help. --- buku | 46 ++-------------------------------------------- 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/buku b/buku index ba0994a..d51acec 100755 --- a/buku +++ b/buku @@ -955,50 +955,6 @@ def printmsg(msg, level=None): -def usage(): - """Show buku usage, options, general information and exit""" - - print("Usage: OPTIONS [URL] [TAGS] [KEYWORDS ...]\n\n" - - "A private cmdline bookmark manager. Your mini web!\n\n" - - "General options\n" - " -a URL [tags] add URL as bookmark with comma separated tags\n" - " -d N delete entry at DB index N (from -p 0), N=0 deletes all\n" - " -g list all tags alphabetically\n" - " -m title manually set title, for -a, -i, -u; '-m none' clears title\n" - " -s keyword(s) search bookmarks for any keyword\n" - " -S keyword(s) search bookmarks with all keywords\n" - " -u N [URL] [tags] update fields of the entry at DB index N\n" - " The first keyword, if available, is treated as the URL.\n" - " If URL is omitted (and -m is not used) the title of entry at\n" - " index N is refreshed from the web, N=0 refreshes all titles.\n\n" - - "Power toys\n" - " -e show bookmarks with empty titles or no tags\n" - " -i N insert new bookmark at free DB index N\n" - " -j show results in Json format\n" - " -k decrypt (unlock) database file\n" - " -l encrypt (lock) database file\n" - " -o N open URL at DB index N in browser\n" - " -p N show details of bookmark record at DB index N, N=0 shows all\n" - " -r oldtag [newtag] replace oldtag with newtag, delete oldtag if newtag empty\n" - " -t N use N (> 0) hash iterations to generate key, for -k, -l\n" - " -x N modify -p behaviour, N=1: show only URL, N=2: show URL & tag\n" - " -z show debug information\n\n" - - "Prompt keys\n" - " 1-N open Nth search result in browser\n" - " Enter exit buku\n\n" - - "Version %.1f\n" - "Copyright (C) 2015 Arun Prakash Jana \n" - "License: GPLv3\n" - "Webpage: https://github.com/jarun/buku\n" % _VERSION_) - sys.exit(1) - - - """main starts here""" def main(argv = sys.argv): # detects whether have pipe line parsing in @@ -1054,6 +1010,8 @@ argparser = ExtendedArgumentParser( ) addarg = argparser.add_argument +#addarg('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin, help=argparse.SUPPRESS) +addarg('outfile', nargs='?', type=argparse.FileType('w'), default=sys.stdout, help=argparse.SUPPRESS) addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help="bookmark URL with comma separated tags") addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', From 045eec94671da37293d0e94582cf06cb9b6ac1e3 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Tue, 26 Apr 2016 21:43:02 +0530 Subject: [PATCH 18/24] Support piped input. --- buku | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/buku b/buku index d51acec..43283fd 100755 --- a/buku +++ b/buku @@ -969,6 +969,9 @@ if __name__ == "__main__": except KeyboardInterrupt: pass +if len(pipeargs) > 0: + sys.argv = pipeargs + class customAction(argparse.Action): """Class to capture if an optional param is actually used, even if sans arguments @@ -1010,8 +1013,6 @@ argparser = ExtendedArgumentParser( ) addarg = argparser.add_argument -#addarg('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin, help=argparse.SUPPRESS) -addarg('outfile', nargs='?', type=argparse.FileType('w'), default=sys.stdout, help=argparse.SUPPRESS) addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help="bookmark URL with comma separated tags") addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', @@ -1068,7 +1069,6 @@ if args.showOpt is not None: titleManual = args.titleManual jsonOutput = args.jsonOutput debug = args.debug -keywords = [] # Show version in debug logs if debug: From 5c1105e950943423d485fdd86f696de7f538ffb1 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Tue, 26 Apr 2016 22:53:48 +0530 Subject: [PATCH 19/24] title can have multiple tokens. --- buku | 50 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/buku b/buku index 43283fd..06058f1 100755 --- a/buku +++ b/buku @@ -955,23 +955,6 @@ def printmsg(msg, level=None): -"""main starts here""" -def main(argv = sys.argv): - # detects whether have pipe line parsing in - if not sys.stdin.isatty(): - pipeargs.extend(sys.argv) - for s in sys.stdin.readlines(): - pipeargs.extend(s.split()) - -if __name__ == "__main__": - try: - main(sys.argv) - except KeyboardInterrupt: - pass - -if len(pipeargs) > 0: - sys.argv = pipeargs - class customAction(argparse.Action): """Class to capture if an optional param is actually used, even if sans arguments @@ -984,6 +967,8 @@ class customAction(argparse.Action): # NOTE: the following converts a None argument to an empty array [] setattr(args, self.dest, values) + + class ExtendedArgumentParser(argparse.ArgumentParser): """Extend classic argument parser""" @@ -1006,6 +991,28 @@ class ExtendedArgumentParser(argparse.ArgumentParser): super(ExtendedArgumentParser, self).print_help(file) self.print_extended_help(file) + + +"""main starts here""" + +# Handle piped input +def main(argv = sys.argv): + if not sys.stdin.isatty(): + pipeargs.extend(sys.argv) + for s in sys.stdin.readlines(): + pipeargs.extend(s.split()) + +if __name__ == "__main__": + try: + main(sys.argv) + except KeyboardInterrupt: + pass + +# If piped input, set argument vector +if len(pipeargs) > 0: + sys.argv = pipeargs + +# Setup custom argument parser argparser = ExtendedArgumentParser( add_help=False, description='A private cmdline bookmark manager. Your mini web!', @@ -1025,9 +1032,9 @@ addarg('-S', '--sall', nargs='+', metavar='KEYWORD', "special keywords:\n" "'tags' - list all tags alphabetically\n" "'blank'- list entries with empty title/tag") -addarg('-t', '--title', dest='titleManual', metavar='title', +addarg('-t', '--title', nargs='+', dest='titleManual', metavar='title', help="manually set title, works with -a, -u\n" - "title='blank': clear title") + "title='blank': no title") addarg('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), help="update fields of bookmark at DB index N\n" "if URL is omitted (and -m unused), refresh\n" @@ -1057,16 +1064,19 @@ addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', addarg('-z', '--debug', dest='debug', action='store_true', help="show debug information and additional logs") +# Show help if no arguments passed if len(sys.argv) < 2: argparser.print_help(sys.stderr) sys.exit(1) +# Parse the arguments args = argparser.parse_args() # Assign the values to globals if args.showOpt is not None: showOpt = args.showOpt -titleManual = args.titleManual +if args.titleManual is not None: + titleManual = " ".join(args.titleManual) jsonOutput = args.jsonOutput debug = args.debug From 0a314a0ad4db78029a7f405c1ffaf41825262ebe Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Tue, 26 Apr 2016 23:33:00 +0530 Subject: [PATCH 20/24] Use appropriate short option for format. --- buku | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buku b/buku index 06058f1..075e220 100755 --- a/buku +++ b/buku @@ -1058,7 +1058,7 @@ addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), help="replace oldtag with newtag for all bookmarks\n" "newtag='blank': delete oldtag") -addarg('-x', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', +addarg('-f', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', help="modify -p output\n" "N=1: show only URL, N=2: show URL and tag") addarg('-z', '--debug', dest='debug', action='store_true', From 7affa004424bc01e0b4240f959ac6ac7fd06c6e0 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Wed, 27 Apr 2016 00:52:59 +0530 Subject: [PATCH 21/24] Group options. --- buku | 78 ++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/buku b/buku index 075e220..836dc2e 100755 --- a/buku +++ b/buku @@ -1014,56 +1014,71 @@ if len(pipeargs) > 0: # Setup custom argument parser argparser = ExtendedArgumentParser( - add_help=False, description='A private cmdline bookmark manager. Your mini web!', - formatter_class=argparse.RawTextHelpFormatter + formatter_class=argparse.RawTextHelpFormatter, + usage='''buku [-a URL [tags ...]] [-u [N [URL tags ...]]] + [-t title [...]] [-d [N]] [-h] + [-s keyword [...]] [-S keyword [...]] + [-k [N]] [-l [N]] [-p [N]] [-f N] + [-r oldtag newtag] [-j] [-o N] [-z]''', + add_help=False ) -addarg = argparser.add_argument -addarg('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), +general_group = argparser.add_argument_group(title="general options") +general_group.add_argument('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help="bookmark URL with comma separated tags") -addarg('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', +general_group.add_argument('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), + help="update fields of bookmark at DB index N\n" + "if URL is omitted (and -m unused), refresh\n" + "title of bookmark at index N from the web;\n" + "refresh all titles, if N is omitted") +general_group.add_argument('-t', '--title', nargs='+', dest='titleManual', metavar='title', + help="manually set title, works with -a, -u\n" + "title='blank': no title") +general_group.add_argument('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help="delete bookmark at DB index N (from -p 0)\n" "delete all bookmarks, if N is omitted") -addarg('-s', '--sany', nargs='+', metavar='KEYWORD', +general_group.add_argument('-h', '--help', dest='help', action='store_true', + help="show this information") + +search_group=argparser.add_argument_group(title="search options") +search_group.add_argument('-s', '--sany', nargs='+', metavar='keyword', help="search bookmarks for ANY matching keyword") -addarg('-S', '--sall', nargs='+', metavar='KEYWORD', +search_group.add_argument('-S', '--sall', nargs='+', metavar='keyword', help="search bookmarks with ALL keywords\n" "special keywords:\n" "'tags' - list all tags alphabetically\n" "'blank'- list entries with empty title/tag") -addarg('-t', '--title', nargs='+', dest='titleManual', metavar='title', - help="manually set title, works with -a, -u\n" - "title='blank': no title") -addarg('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), - help="update fields of bookmark at DB index N\n" - "if URL is omitted (and -m unused), refresh\n" - "title of bookmark at index N from the web;\n" - "refresh all titles, if N is omitted") -#addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), -# help=" insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted") -addarg('-j', '--json', dest='jsonOutput', action='store_true', - help="Json formatted output, works with -p, -s") -addarg('-k', '--unlock', nargs='?', dest='decrypt', type=int, const=8, metavar='N', + +crypto_group=argparser.add_argument_group(title="encryption options") +crypto_group.add_argument('-k', '--unlock', nargs='?', dest='decrypt', type=int, const=8, metavar='N', help="decrypt DB file with N (> 0, default 8)\n" "hash iterations to generate key") -addarg('-l', '--lock', nargs='?', dest='encrypt', type=int, const=8, metavar='N', +crypto_group.add_argument('-l', '--lock', nargs='?', dest='encrypt', type=int, const=8, metavar='N', help="encrypt DB file with N (> 0, default 8)\n" "hash iterations to generate key") -addarg('-o', '--open', dest='openurl', type=int, metavar='N', - help="open bookmark at DB index N in web browser") -addarg('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', + +power_group=argparser.add_argument_group(title="power toys") +power_group.add_argument('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', help="show details of bookmark at DB index N\n" "show all bookmarks, if N is omitted") -addarg('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), - help="replace oldtag with newtag for all bookmarks\n" - "newtag='blank': delete oldtag") -addarg('-f', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', +power_group.add_argument('-f', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', help="modify -p output\n" "N=1: show only URL, N=2: show URL and tag") -addarg('-z', '--debug', dest='debug', action='store_true', +power_group.add_argument('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), + help="replace oldtag with newtag for all bookmarks\n" + "newtag='blank': delete oldtag") +power_group.add_argument('-j', '--json', dest='jsonOutput', action='store_true', + help="Json formatted output, works with -p, -s") +power_group.add_argument('-o', '--open', dest='openurl', type=int, metavar='N', + help="open bookmark at DB index N in web browser") +power_group.add_argument('-z', '--debug', dest='debug', action='store_true', help="show debug information and additional logs") +#addarg = argparser.add_argument +#addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), +# help=" insert new bookmark with URL and tags at free DB index N; frees index if URL and tags are omitted") + # Show help if no arguments passed if len(sys.argv) < 2: argparser.print_help(sys.stderr) @@ -1072,6 +1087,11 @@ if len(sys.argv) < 2: # Parse the arguments args = argparser.parse_args() +# Show help and exit if help requested +if args.help == True: + argparser.print_help(sys.stderr) + sys.exit(0) + # Assign the values to globals if args.showOpt is not None: showOpt = args.showOpt From 1549f51bf834d4aa85d55f6a9bbb127fea1d2181 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Wed, 27 Apr 2016 02:43:27 +0530 Subject: [PATCH 22/24] Grouped help. --- buku | 112 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 61 insertions(+), 51 deletions(-) diff --git a/buku b/buku index 836dc2e..f89cc23 100755 --- a/buku +++ b/buku @@ -975,16 +975,16 @@ class ExtendedArgumentParser(argparse.ArgumentParser): # Print additional help and info @staticmethod def print_extended_help(file=None): - file.write(textwrap.dedent(""" + file.write(textwrap.dedent(''' prompt keys: - 1-N open the Nth search result in web browser - Enter exit buku + 1-N open the Nth search result in web browser + Enter exit buku Version %.1f Copyright (C) 2015 Arun Prakash Jana License: GPLv3 Webpage: https://github.com/jarun/buku - """ % _VERSION_)) + ''' % _VERSION_)) # Help def print_help(self, file=None): @@ -1024,56 +1024,66 @@ argparser = ExtendedArgumentParser( add_help=False ) -general_group = argparser.add_argument_group(title="general options") -general_group.add_argument('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), - help="bookmark URL with comma separated tags") -general_group.add_argument('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), - help="update fields of bookmark at DB index N\n" - "if URL is omitted (and -m unused), refresh\n" - "title of bookmark at index N from the web;\n" - "refresh all titles, if N is omitted") -general_group.add_argument('-t', '--title', nargs='+', dest='titleManual', metavar='title', - help="manually set title, works with -a, -u\n" - "title='blank': no title") -general_group.add_argument('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', - help="delete bookmark at DB index N (from -p 0)\n" - "delete all bookmarks, if N is omitted") -general_group.add_argument('-h', '--help', dest='help', action='store_true', - help="show this information") +# General options +general_group = argparser.add_argument_group(title="general options", + description='''-a, --add URL [tags ...] + bookmark URL with comma separated tags +-u, --update [N [URL tags ...]] + update fields of bookmark at DB index N + refresh all titles, if no arguments + if URL omitted and -t is unused, update + title of bookmark at index N from web +-t, --title title [...] + manually set title, works with -a, -u + title='blank': no title +-d, --delete [N] delete bookmark at DB index N + delete all bookmarks, if no arguments +-h, --help show this information''') +general_group.add_argument('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help=argparse.SUPPRESS) +general_group.add_argument('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), help=argparse.SUPPRESS) +general_group.add_argument('-t', '--title', nargs='+', dest='titleManual', metavar='title', help=argparse.SUPPRESS) +general_group.add_argument('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help=argparse.SUPPRESS) +general_group.add_argument('-h', '--help', dest='help', action='store_true', help=argparse.SUPPRESS) -search_group=argparser.add_argument_group(title="search options") -search_group.add_argument('-s', '--sany', nargs='+', metavar='keyword', - help="search bookmarks for ANY matching keyword") -search_group.add_argument('-S', '--sall', nargs='+', metavar='keyword', - help="search bookmarks with ALL keywords\n" - "special keywords:\n" - "'tags' - list all tags alphabetically\n" - "'blank'- list entries with empty title/tag") +# Search options +search_group=argparser.add_argument_group(title="search options", + description='''-s, --sany keyword [...] + search bookmarks for ANY matching keyword +-S, --sall keyword [...] + search bookmarks with ALL keywords + special keywords - + "tags" : list all tags alphabetically + "blank": list entries with empty title/tag''') +search_group.add_argument('-s', '--sany', nargs='+', metavar='keyword', help=argparse.SUPPRESS) +search_group.add_argument('-S', '--sall', nargs='+', metavar='keyword', help=argparse.SUPPRESS) -crypto_group=argparser.add_argument_group(title="encryption options") -crypto_group.add_argument('-k', '--unlock', nargs='?', dest='decrypt', type=int, const=8, metavar='N', - help="decrypt DB file with N (> 0, default 8)\n" - "hash iterations to generate key") -crypto_group.add_argument('-l', '--lock', nargs='?', dest='encrypt', type=int, const=8, metavar='N', - help="encrypt DB file with N (> 0, default 8)\n" - "hash iterations to generate key") +# Encryption options +crypto_group=argparser.add_argument_group(title="encryption options", + description='''-l, --lock [N] encrypt DB file with N (> 0, default 8) + hash iterations to generate key +-k, --unlock [N] decrypt DB file with N (> 0, default 8) + hash iterations to generate key''') +crypto_group.add_argument('-k', '--unlock', nargs='?', dest='decrypt', type=int, const=8, metavar='N', help=argparse.SUPPRESS) +crypto_group.add_argument('-l', '--lock', nargs='?', dest='encrypt', type=int, const=8, metavar='N', help=argparse.SUPPRESS) -power_group=argparser.add_argument_group(title="power toys") -power_group.add_argument('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', - help="show details of bookmark at DB index N\n" - "show all bookmarks, if N is omitted") -power_group.add_argument('-f', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', - help="modify -p output\n" - "N=1: show only URL, N=2: show URL and tag") -power_group.add_argument('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), - help="replace oldtag with newtag for all bookmarks\n" - "newtag='blank': delete oldtag") -power_group.add_argument('-j', '--json', dest='jsonOutput', action='store_true', - help="Json formatted output, works with -p, -s") -power_group.add_argument('-o', '--open', dest='openurl', type=int, metavar='N', - help="open bookmark at DB index N in web browser") -power_group.add_argument('-z', '--debug', dest='debug', action='store_true', - help="show debug information and additional logs") +# Power toys +power_group=argparser.add_argument_group(title="power toys", + description='''-p, --print [N] show details of bookmark at DB index N + show all bookmarks, if no arguments +-f, --format N modify -p output + N=1: show only URL, N=2: show URL and tag +-r, --replace oldtag newtag + replace oldtag with newtag for all bookmarks + newtag='blank': delete oldtag +-j, --jason Json formatted output, works with -p, -s +-o, --open open bookmark at DB index N in web browser +-z, --debug show debug information and additional logs''') +power_group.add_argument('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', help=argparse.SUPPRESS) +power_group.add_argument('-f', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', help=argparse.SUPPRESS) +power_group.add_argument('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), help=argparse.SUPPRESS) +power_group.add_argument('-j', '--json', dest='jsonOutput', action='store_true', help=argparse.SUPPRESS) +power_group.add_argument('-o', '--open', dest='openurl', type=int, metavar='N', help=argparse.SUPPRESS) +power_group.add_argument('-z', '--debug', dest='debug', action='store_true', help=argparse.SUPPRESS) #addarg = argparser.add_argument #addarg('-i', '--insert', nargs='+', dest='insert', metavar=('N', 'URL tags'), From 99ebbac663c4642a92553693872d11839f0839e7 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Wed, 27 Apr 2016 23:26:57 +0530 Subject: [PATCH 23/24] Omit title if -t is used without arguments. --- buku | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/buku b/buku index f89cc23..2bc8fd1 100755 --- a/buku +++ b/buku @@ -387,10 +387,7 @@ def AddUpdateEntry(conn, cur, keywords, updateindex, insertindex=0): tags += ',' if titleManual is not None: - if titleManual == BLANK: - meta = '' - else: - meta = titleManual + meta = titleManual else: meta = fetchTitle(url) if meta == '': @@ -458,10 +455,7 @@ def dbRefresh(conn, cur, index): conn.commit() print("Updated index %d\n" % row[0]) else: - if titleManual == BLANK: - title = '' - else: - title = titleManual + title = titleManual for row in resultset: cur.execute("UPDATE bookmarks SET metadata = ? WHERE id = ?", (title, row[0],)) @@ -955,7 +949,7 @@ def printmsg(msg, level=None): -class customAction(argparse.Action): +class customUpdateAction(argparse.Action): """Class to capture if an optional param is actually used, even if sans arguments """ @@ -969,6 +963,22 @@ class customAction(argparse.Action): +class customTitleAction(argparse.Action): + """Class to capture if an optional param + is actually used, even if sans arguments + """ + + def __call__(self, parser, args, values, option_string=None): + global titleManual + + titleManual = '' + if titleManual is not None: + print("titleManual is not None") + # NOTE: the following converts a None argument to an empty array [] + setattr(args, self.dest, values) + + + class ExtendedArgumentParser(argparse.ArgumentParser): """Extend classic argument parser""" @@ -1017,7 +1027,7 @@ argparser = ExtendedArgumentParser( description='A private cmdline bookmark manager. Your mini web!', formatter_class=argparse.RawTextHelpFormatter, usage='''buku [-a URL [tags ...]] [-u [N [URL tags ...]]] - [-t title [...]] [-d [N]] [-h] + [-t [...]] [-d [N]] [-h] [-s keyword [...]] [-S keyword [...]] [-k [N]] [-l [N]] [-p [N]] [-f N] [-r oldtag newtag] [-j] [-o N] [-z]''', @@ -1033,15 +1043,14 @@ general_group = argparser.add_argument_group(title="general options", refresh all titles, if no arguments if URL omitted and -t is unused, update title of bookmark at index N from web --t, --title title [...] - manually set title, works with -a, -u - title='blank': no title +-t, --title [...] manually set title, works with -a, -u + do not set title, is no arguments -d, --delete [N] delete bookmark at DB index N delete all bookmarks, if no arguments -h, --help show this information''') general_group.add_argument('-a', '--add', nargs='+', dest='addurl', metavar=('URL', 'tags'), help=argparse.SUPPRESS) -general_group.add_argument('-u', '--update', nargs='*', dest='update', action=customAction, metavar=('N', 'URL tags'), help=argparse.SUPPRESS) -general_group.add_argument('-t', '--title', nargs='+', dest='titleManual', metavar='title', help=argparse.SUPPRESS) +general_group.add_argument('-u', '--update', nargs='*', dest='update', action=customUpdateAction, metavar=('N', 'URL tags'), help=argparse.SUPPRESS) +general_group.add_argument('-t', '--title', nargs='*', dest='title', action=customTitleAction, metavar='title', help=argparse.SUPPRESS) general_group.add_argument('-d', '--delete', nargs='?', dest='delete', type=int, const=0, metavar='N', help=argparse.SUPPRESS) general_group.add_argument('-h', '--help', dest='help', action='store_true', help=argparse.SUPPRESS) @@ -1073,7 +1082,7 @@ power_group=argparser.add_argument_group(title="power toys", -f, --format N modify -p output N=1: show only URL, N=2: show URL and tag -r, --replace oldtag newtag - replace oldtag with newtag for all bookmarks + replace oldtag with newtag in all bookmarks newtag='blank': delete oldtag -j, --jason Json formatted output, works with -p, -s -o, --open open bookmark at DB index N in web browser @@ -1105,8 +1114,8 @@ if args.help == True: # Assign the values to globals if args.showOpt is not None: showOpt = args.showOpt -if args.titleManual is not None: - titleManual = " ".join(args.titleManual) +if titleManual is not None and len(args.title) > 0: + titleManual = " ".join(args.title) jsonOutput = args.jsonOutput debug = args.debug From af35c6ef6136852b6e7eb6b0f9bd98c053dfced6 Mon Sep 17 00:00:00 2001 From: Arun Prakash Jana Date: Fri, 29 Apr 2016 22:59:06 +0530 Subject: [PATCH 24/24] Support replace tags. --- buku | 53 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/buku b/buku index 2bc8fd1..3ac75bb 100755 --- a/buku +++ b/buku @@ -693,7 +693,7 @@ def showUniqueTags(cur): -def replaceTags(conn, cur, orig, new): +def replaceTags(conn, cur, orig, new=None): """Replace orig tags with new tags in DB for all records. Remove orig tag is new tag is empty. @@ -704,14 +704,32 @@ def replaceTags(conn, cur, orig, new): delete = False orig = ',' + orig + ',' - new = new.strip(',') - if new == '': - new = ',' + if new is None: + newtags = ',' delete = True else: - new = ',' + new + ',' + newtags = ',' + for tag in new: + if tag[-1] == ',': + tag = tag.strip(',') + ',' # if delimiter is present, maintain it + else: + tag = tag.strip(',') # a token in a multi-word tag - if orig == new: + if tag == ',': + continue + + if newtags[-1] == ',': + newtags += tag + else: + newtags += ' ' + tag + + if newtags[-1] != ',': + newtags += ',' + + if newtags == ',': + delete = True + + if orig == newtags: print("Tags are same.") return @@ -720,10 +738,11 @@ def replaceTags(conn, cur, orig, new): for row in results: if delete == False: - if row[1].find(new) >= 0: - new = ',' + # Check if tag newtags is already added + if row[1].find(newtags) >= 0: + newtags = ',' - newtags = row[1].replace(orig, new) + newtags = row[1].replace(orig, newtags) cur.execute("UPDATE bookmarks SET tags = ? WHERE id = ?", (newtags, row[0],)) print("Updated index %d" % row[0]) update = True @@ -1030,7 +1049,7 @@ argparser = ExtendedArgumentParser( [-t [...]] [-d [N]] [-h] [-s keyword [...]] [-S keyword [...]] [-k [N]] [-l [N]] [-p [N]] [-f N] - [-r oldtag newtag] [-j] [-o N] [-z]''', + [-r oldtag [newtag ...]] [-j] [-o N] [-z]''', add_help=False ) @@ -1044,7 +1063,7 @@ general_group = argparser.add_argument_group(title="general options", if URL omitted and -t is unused, update title of bookmark at index N from web -t, --title [...] manually set title, works with -a, -u - do not set title, is no arguments + do not set title, if no arguments -d, --delete [N] delete bookmark at DB index N delete all bookmarks, if no arguments -h, --help show this information''') @@ -1081,15 +1100,15 @@ power_group=argparser.add_argument_group(title="power toys", show all bookmarks, if no arguments -f, --format N modify -p output N=1: show only URL, N=2: show URL and tag --r, --replace oldtag newtag +-r, --replace oldtag [newtag ...] replace oldtag with newtag in all bookmarks - newtag='blank': delete oldtag + delete oldtag, if no newtag -j, --jason Json formatted output, works with -p, -s -o, --open open bookmark at DB index N in web browser -z, --debug show debug information and additional logs''') power_group.add_argument('-p', '--print', nargs='?', dest='printindex', type=int, const=0, metavar='N', help=argparse.SUPPRESS) power_group.add_argument('-f', '--format', dest='showOpt', type=int, choices=[1, 2], metavar='N', help=argparse.SUPPRESS) -power_group.add_argument('-r', '--replace', nargs=2, dest='replace', metavar=('oldtag', 'newtag'), help=argparse.SUPPRESS) +power_group.add_argument('-r', '--replace', nargs='+', dest='replace', metavar=('oldtag', 'newtag'), help=argparse.SUPPRESS) power_group.add_argument('-j', '--json', dest='jsonOutput', action='store_true', help=argparse.SUPPRESS) power_group.add_argument('-o', '--open', dest='openurl', type=int, metavar='N', help=argparse.SUPPRESS) power_group.add_argument('-z', '--debug', dest='debug', action='store_true', help=argparse.SUPPRESS) @@ -1200,10 +1219,10 @@ if args.printindex is not None: # Replace a tag in DB if args.replace is not None: - if args.replace[1] == BLANK: - replaceTags(conn, cur, args.replace[0], "") + if len(args.replace) == 1: + replaceTags(conn, cur, args.replace[0]) else: - replaceTags(conn, cur, args.replace[0], args.replace[1]) + replaceTags(conn, cur, args.replace[0], args.replace[1:]) # Open URL in browser if args.openurl is not None: