diff --git a/README.md b/README.md index b9519fe..d6b60fa 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,9 @@ Please substitute `$version` with the appropriate package version. refresh all titles, if no arguments refresh title of bookmark at N, if only N is specified without any edit options - -d, --delete [N] delete bookmark at DB index N + -d, --delete [...] delete bookmarks. Valid inputs: either + a hyphenated single range (100-200), + OR space-separated indices (100 15 200) delete all bookmarks, if no arguments -h, --help show this information and exit @@ -198,6 +200,9 @@ Please substitute `$version` with the appropriate package version. - If --title, --tag or --comment is passed without argument, clear the corresponding field from DB. - If --url is passed (and --title is omitted), update the title from web using the URL. - If index number is passed without any other options (--url, --title, --tag and --comment), read the URL from DB and update title from web. +- **Delete** operation: + - When a record is deleted, the last record is moved to the index. + - Delete doesn't work with range and indices provided together as arguments. It's an intentional decision to avoid extra sorting, in-range checks and to keep the auto-DB compaction functionality intact. - **Search** works in mysterious ways: - Case-insensitive. - Substrings match (`match` matches `rematched`) for URL, title and tags. @@ -206,7 +211,6 @@ Please substitute `$version` with the appropriate package version. - --st : search bookmarks by tag, or show all tags alphabetically. - You can search bookmarks by tag (see [examples](#examples)). - Search results are indexed serially. This index is different from actual database index of a bookmark record which is shown in bold within `[]` after the URL. -- Auto DB compaction: when a record is deleted, the last record is moved to the index. - **Encryption** is optional and manual. AES256 algorithm is used. If you choose to use encryption, the database file should be unlocked (-k) before using buku and locked (-l) afterwards. Between these 2 operations, the database file lies unencrypted on the disk, and NOT in memory. Also, note that the database file is *unencrypted on creation*. # Examples @@ -260,48 +264,52 @@ The last index is moved to the deleted index to keep the DB compact. 11. **Delete all** bookmarks: $ buku -d -12. **Search** bookmarks for **ANY** of the keywords `kernel` and `debugging` in URL, title or tags: +12. **Delete** a **range or list** of bookmarks: + + $ buku -d 100-200 // delete bookmarks from index 100 to 200 + $ buku 100 15 200 // delete bookmarks at indices 100, 15 and 200 +13. **Search** bookmarks for **ANY** of the keywords `kernel` and `debugging` in URL, title or tags: $ buku -s kernel debugging -13. **Search** bookmarks with **ALL** the keywords `kernel` and `debugging` in URL, title or tags: +14. **Search** bookmarks with **ALL** the keywords `kernel` and `debugging` in URL, title or tags: $ buku -S kernel debugging -14. **Search** bookmarks with **tag** `general kernel concepts`: +15. **Search** bookmarks with **tag** `general kernel concepts`: $ buku --st general kernel concepts Note the commas (,) before and after the tag. Comma is the tag delimiter in DB. -15. List **all unique tags** alphabetically: +16. List **all unique tags** alphabetically: $ buku --st -16. **Encrypt or decrypt** DB with **custom number of iterations** (15) to generate key: +17. **Encrypt or decrypt** DB with **custom number of iterations** (15) to generate key: $ buku -l 15 $ buku -k 15 The same number of iterations must be used for one lock & unlock instance. Default is 8. -17. **Show details** of bookmark at index 15012014: +18. **Show details** of bookmark at index 15012014: $ buku -p 15012014 -18. **Show all** bookmarks with real index from database: +19. **Show all** bookmarks with real index from database: $ buku -p $ buku -p | more -19. **Replace tag** 'old tag' with 'new tag': +20. **Replace tag** 'old tag' with 'new tag': $ buku -r 'old tag' new tag -20. **Delete tag** 'old tag' from DB: +21. **Delete tag** 'old tag' from DB: $ buku -r 'old tag' -21. **Append tags** 'tag 1', 'tag 2' to existing tags of bookmark at index 15012014: +22. **Append tags** 'tag 1', 'tag 2' to existing tags of bookmark at index 15012014: $ buku -u 15012014 --tag + tag 1, tag 2 -22. **Open URL** at index 15012014 in browser: +23. **Open URL** at index 15012014 in browser: $ buku -o 15012014 -23. To list bookmarks with no title or tags for **bookkeeping**: +24. To list bookmarks with no title or tags for **bookkeeping**: $ buku -S blank -24. More **help**: +25. More **help**: $ buku $ man buku diff --git a/buku b/buku index 27f3ac1..6cc192e 100755 --- a/buku +++ b/buku @@ -664,13 +664,23 @@ class BukuDb: self.conn.commit() print('Index %d moved to %d' % (row[0], index)) - def delete_bookmark(self, index): + def delete_bookmark(self, index, low=0, high=0, is_range=False): """Delete a single record or remove the table if index is None Params: index to delete """ - if index == 0: # Remove the table + if is_range: + try: + self.cur.execute('DELETE from bookmarks where id BETWEEN ? AND ?', (low, high)) + self.conn.commit() + print('Bookmarks from index %s to %s deleted' % (low, high)) + except IndexError: + print('Index out of bound') + + for index in range(low, high): + self.compactdb(index) + elif 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') @@ -1459,12 +1469,14 @@ if __name__ == '__main__': refresh all titles, if no arguments refresh title of bookmark at N, if only N is specified without any edit options --d, --delete [N] delete bookmark at DB index N +-d, --delete [...] delete bookmarks. Valid inputs: either + a hyphenated single range (100-200), + OR space-separated indices (100 15 200) delete all bookmarks, if no arguments -h, --help show this information and exit''') 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=CustomUpdateAction, metavar=('N', 'URL tags'), 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('-d', '--delete', nargs='*', dest='delete', metavar='N', help=argparse.SUPPRESS) general_group.add_argument('-h', '--help', dest='help', action='store_true', help=argparse.SUPPRESS) # Edit options @@ -1634,10 +1646,33 @@ if __name__ == '__main__': # Delete record(s) if args.delete is not None: - if args.delete < 0: - printmsg('Index must be >= 0', 'ERROR') - bdb.close_quit(1) - bdb.delete_bookmark(args.delete) + if len(args.delete) == 0: + bdb.delete_bookmark(0) + elif len(args.delete) == 1 and '-' in args.delete[0]: + vals = str(args.delete[0]).split('-') + if len(vals) == 2 and is_int(vals[0]) and is_int(vals[1]): + if int(vals[0]) == int(vals[1]): + bdb.delete_bookmark(int(vals[0])) + elif int(vals[0]) < int(vals[1]): + bdb.delete_bookmark(0, int(vals[0]), int(vals[1]), True) + else: + bdb.delete_bookmark(0, int(vals[1]), int(vals[0]), True) + else: + printmsg('Incorrect range', 'ERROR') + bdb.close_quit(1) + else: + ids = [] + for idx in args.delete: + if idx not in ids: + ids += (idx,) + + ids.sort(key=lambda x: int(x), reverse=True) + for idx in ids: + if is_int(idx): + bdb.delete_bookmark(int(idx)) + else: + printmsg('Incorrect index %s' % idx, 'ERROR') + bdb.close_quit(1) # Search URLs, titles, tags for any keyword if args.sany is not None: diff --git a/buku.1 b/buku.1 index 0505fdb..3da2118 100644 --- a/buku.1 +++ b/buku.1 @@ -24,6 +24,10 @@ URLs are unique in DB. The same URL cannot be added twice. You can update tags a \fITags\fR: - Comma (',') is the tag delimiter in DB. Any tag cannot have comma(s) in it. Tags are filtered (for unique tags) and sorted. .PP +\fIDelete\fR operation: + - When a record is deleted, the last record is moved to the index. + - Delete doesn't work with range and indices provided together as arguments. It's an intentional decision to avoid extra sorting, in-range checks and to keep the auto-DB compaction functionality intact. +.PP \fIUpdate\fR operation: - If --title, --tag or --comment is passed without argument, clear the corresponding field from DB. - If --url is passed (and --title is omitted), update the title from web using the URL. @@ -38,8 +42,6 @@ URLs are unique in DB. The same URL cannot be added twice. You can update tags a - You can search bookmarks by tag (see examples below). - Search results are indexed serially. This index is different from actual database index of a bookmark record which is shown in bold within '[]' after the URL. .PP -Auto DB compaction: when a record is deleted, the last record is moved to the index. -.PP \fIEncryption\fR is optional and manual. AES256 algorithm is used. If you choose to use encryption, the database file should be unlocked (-k) before using buku and locked (-l) afterwards. Between these 2 operations, the database file lies unencrypted on the disk, and NOT in memory. Also, note that the database file is \fBunencrypted on creation\fR. .SH GENERAL OPTIONS .TP @@ -55,12 +57,8 @@ in DB. If .I N and other options are omitted, all titles are refreshed from the web. Works with update modifiers for the fields url, title, tag and comment. If only N is passed without any edit options, title is fetched and updated (if not empty). .TP -.BI \-d " " \--delete " [N]" -Delete bookmark at index -.I N -in DB (from -p output). The last record is moved to the removed index. If -.I N -is omitted, all records are deleted from the DB. +.BI \-d " " \--delete " [...]" +Delete bookmarks. You can either provide a single hyphenated range (e.g. 100-200) or a space-separated list of indices (e.g. 5 6 23 4 110 45). Note that range and list don't work together. .TP .BI \-h " " \--help Show program help and exit. @@ -272,20 +270,28 @@ The last index is moved to the deleted index to keep the DB compact. .B buku -d .PP .IP 12. 4 +\fBDelete\fR a \fBrange or list\fR of bookmarks: +.PP +.EX +.IP +.B $ buku -d 100-200 // delete bookmarks from index 100 to 200 +.B $ buku 100 15 200 // delete bookmarks at indices 100, 15 and 200 +.PP +.IP 13. 4 \fBSearch\fR bookmarks for \fBANY\fR of the keywords 'kernel' and 'debugging' in URL, title or tags: .PP .EX .IP .B buku -s kernel debugging .PP -.IP 13. 4 +.IP 14. 4 \fBSearch\fR bookmarks with \fBALL\fR the keywords 'kernel' and 'debugging' in URL, title or tags: .PP .EX .IP .B buku -S kernel debugging .PP -.IP 14. 4 +.IP 15. 4 \fBSearch\fR bookmarks with \fBtag\fR 'general kernel concepts': .PP .EX @@ -296,14 +302,14 @@ The last index is moved to the deleted index to keep the DB compact. .IP "" 4 Note the commas (,) before and after the tag. Comma is the tag delimiter in DB. .PP -.IP 15. 4 +.IP 16. 4 List \fBall unique tags\fR alphabetically: .PP .EX .IP .B buku --st .PP -.IP 16. 4 +.IP 17. 4 \fBEncrypt or decrypt\fR DB with \fBcustom number of iterations\fR (15) to generate key: .PP .EX @@ -316,14 +322,14 @@ List \fBall unique tags\fR alphabetically: .IP "" 4 The same number of iterations must be used for one lock & unlock instance. Default is 8. .PP -.IP 17. 4 +.IP 18. 4 \fBShow details\fR of bookmark at index 15012014: .PP .EX .IP .B buku -p 15012014 .PP -.IP 18. 4 +.IP 19. 4 \fBShow all\fR bookmarks with real index from database: .PP .EX @@ -331,35 +337,35 @@ The same number of iterations must be used for one lock & unlock instance. Defau .B buku -p .B buku -p | more .PP -.IP 19. 4 +.IP 20. 4 \fBReplace tag\fR 'old tag' with 'new tag': .PP .EX .IP .B buku -r 'old tag' new tag .PP -.IP 20. 4 +.IP 21. 4 \fBDelete tag\fR 'old tag' from DB: .PP .EX .IP .B buku -r 'old tag' .PP -.IP 21. 4 +.IP 22. 4 \fBAppend tags\fR 'tag 1', 'tag 2' to existing tags of bookmark at index 15012014: .PP .EX .IP .B buku -u 15012014 --tag + tag 1, tag 2 .PP -.IP 22. 4 +.IP 23. 4 \fBOpen URL\fR at index 15012014 in browser: .PP .EX .IP .B buku -o 15012014 .PP -.IP 23. 4 +.IP 24. 4 To list bookmarks with no title or tags for \fBbookkeeping\fR: .PP .EX