--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/parsre.lisp Mon Jan 31 21:28:01 2022 -0500
@@ -0,0 +1,227 @@
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (ql:quickload '(:adopt :cl-ppcre :iterate :with-user-abort
+ :jarl :conserve :losh)
+ :silent t))
+
+(defpackage :parsre
+ (:use :cl :iterate)
+ (:export :toplevel :*ui*))
+
+(in-package :parsre)
+
+;;;; Configuration ------------------------------------------------------------
+(defparameter *output-format* :text)
+(defparameter *case-sensitive* t)
+
+
+;;;; Errors -------------------------------------------------------------------
+(define-condition user-error (error) ())
+
+(define-condition missing-regex (user-error) ()
+ (:report "A regular expression is required."))
+
+(define-condition missing-registers (user-error) ()
+ (:report "Invalid regex, at least one named register is required."))
+
+(define-condition anonymous-register (user-error) ()
+ (:report "Invalid regex, anonymous registers are not supported. Use non-capturing groups (?:…) if you need grouping but not capturing."))
+
+(define-condition malformed-regex (user-error)
+ ((underlying-error :initarg :underlying-error))
+ (:report (lambda (c s)
+ (format s "Invalid regex: ~A" (slot-value c 'underlying-error)))))
+
+
+;;;; Functionality ------------------------------------------------------------
+(defgeneric header (format names))
+(defmethod header (format names) nil)
+(defmethod header ((format (eql :csv)) names) (conserve:write-row names))
+
+
+(defmethod output (format names values))
+
+(defmethod output ((format (eql :text)) names values)
+ (iterate (for name :in names)
+ (for value :in values)
+ (format t "~A: ~A~%" name value))
+ (terpri))
+
+(defmethod output ((format (eql :json)) names values)
+ (jarl:print (alexandria:alist-hash-table (mapcar #'cons names values) :test #'equal))
+ (terpri))
+
+(defmethod output ((format (eql :csv)) names values)
+ (declare (ignore names))
+ (conserve:write-row values))
+
+
+(defun register-values (string register-starts register-ends)
+ (iterate
+ (for start :in-vector register-starts)
+ (for end :in-vector register-ends)
+ (collect (subseq string start end))))
+
+(defun run%% (scanner register-names string)
+ (iterate (with start = 0)
+ (for (values ms me rs re) = (ppcre:scan scanner string :start start))
+ (while ms)
+ (output *output-format* register-names (register-values string rs re))
+ (setf start (max (1+ start) me))))
+
+(defun run% (scanner register-names stream)
+ (iterate (for line :in-stream stream :using #'read-line)
+ (run%% scanner register-names line)
+ (force-output)))
+
+
+(defun make-scanner (pattern)
+ (handler-case
+ (ppcre:create-scanner pattern
+ :case-insensitive-mode (not *case-sensitive*)
+ :single-line-mode t)
+ (ppcre:ppcre-syntax-error (c) (error 'malformed-regex :underlying-error c))))
+
+(defun run (pattern paths)
+ (let ((ppcre:*allow-named-registers* t)
+ (ppcre:*use-bmh-matchers* t)
+ (paths (or paths '("-"))))
+ (multiple-value-bind (scanner register-names) (make-scanner pattern)
+ (cond ((null register-names) (error 'missing-registers))
+ ((member nil register-names) (error 'anonymous-register)))
+ (header *output-format* register-names)
+ (dolist (path paths)
+ (if (string= "-" path)
+ (run% scanner register-names *standard-input*)
+ (with-open-file (stream path :direction :input)
+ (run% scanner register-names stream)))))))
+
+
+;;;; User Interface -----------------------------------------------------------
+(defparameter *examples*
+ '(("Parse some web service logs:" .
+ "parsre '^(?<timestamp>\\\\S+) (?<level>INFO|WARN|ERROR|DEBUG) (?<message>.*)$'")
+ ("Parse wordcount report into JSON:" .
+ "wc **.lisp | parsre --json '^ *(?<lines>\\\\d+) +(?<words>\\\\d+) +(?<bytes>\\\\d+) +(?<path>.+)$'")))
+
+
+(defparameter *option/help*
+ (adopt:make-option 'help
+ :help "Display help and exit."
+ :long "help"
+ :short #\h
+ :reduce (constantly t)))
+
+(defparameter *option/version*
+ (adopt:make-option 'version
+ :help "Display version information and exit."
+ :long "version"
+ :reduce (constantly t)))
+
+(adopt:defparameters (*option/debug* *option/no-debug*)
+ (adopt:make-boolean-options 'debug
+ :long "debug"
+ :short #\d
+ :help "Enable the Lisp debugger."
+ :help-no "Disable the Lisp debugger (default)."))
+
+(adopt:defparameters (*option/profile* *option/no-profile*)
+ (adopt:make-boolean-options 'profile
+ :long "profile"
+ :help "Profile the run and write results to lisp.prof."
+ :help-no "Do not profile (default)."))
+
+(adopt:defparameters (*option/matching/ignore-case* *option/matching/no-ignore-case*)
+ (adopt:make-boolean-options 'ignore-case
+ :long "ignore-case"
+ :short #\i
+ :help "Ignore case (i.e. case-insensitive)."
+ :help-no "Do not ignore case (i.e. case-sensitive) (default)."
+ :initial-value nil))
+
+(defparameter *option/output-format/text*
+ (adopt:make-option 'output-format/text
+ :result-key 'output-format
+ :help "Output results as text (default)."
+ :long "text"
+ :short #\t
+ :initial-value :text
+ :reduce (constantly :text)))
+
+(defparameter *option/output-format/json*
+ (adopt:make-option 'output-format/json
+ :result-key 'output-format
+ :help "Output results as JSON."
+ :long "json"
+ :short #\j
+ :reduce (constantly :json)))
+
+(defparameter *option/output-format/csv*
+ (adopt:make-option 'output-format/csv
+ :result-key 'output-format
+ :help "Output results as CSV."
+ :long "csv"
+ :short #\c
+ :reduce (constantly :csv)))
+
+
+(adopt:define-string *help-text*
+ "Parsre takes a Perl-compatible regular expression, matches it against input, ~
+ and outputs the expression's registers in a variety of formats.~@
+ ~@
+ The regular expression will be matched against the input line-by-line. ~
+ Multiple matches per line are supported.~@
+ ~@
+ Registers can be named or anonymous. Anonymous registers will be named ~
+ arbitrarily (the scheme may change in the future).")
+
+
+(defparameter *ui*
+ (adopt:make-interface
+ :name "parsre"
+ :usage "[OPTIONS] REGEX [FILE...]"
+ :summary "almost a parser"
+ :help *help-text*
+ :examples *examples*
+ :contents (list *option/help*
+ *option/version*
+ (adopt:make-group
+ 'matching-options
+ :title "Matching Options"
+ :options (list *option/matching/ignore-case*
+ *option/matching/no-ignore-case*))
+ (adopt:make-group
+ 'output-options
+ :title "Output Options"
+ :options (list *option/output-format/text*
+ *option/output-format/json*
+ *option/output-format/csv*))
+ (adopt:make-group
+ 'debugging-options
+ :title "Debugging Options"
+ :options (list *option/debug*
+ *option/no-debug*
+ *option/profile*
+ *option/no-profile*)))))
+
+(defun configure (options)
+ (setf *output-format* (gethash 'output-format options)
+ *case-sensitive* (not (gethash 'ignore-case options)))
+ (values))
+
+(defun toplevel ()
+ (sb-ext:disable-debugger)
+ (multiple-value-bind (arguments options) (adopt:parse-options-or-exit *ui*)
+ (when (gethash 'debug options)
+ (sb-ext:enable-debugger))
+ (handler-case
+ (with-user-abort:with-user-abort
+ (cond
+ ((gethash 'help options) (adopt:print-help-and-exit *ui*))
+ ((null arguments) (error 'missing-regex))
+ (t (destructuring-bind (pattern . files) arguments
+ (configure options)
+ (if (gethash 'profile options)
+ (losh:profile (run pattern files))
+ (run pattern files))))))
+ (with-user-abort:user-abort () (adopt:exit 130))
+ (user-error (e) (adopt:print-error-and-exit e)))))
--- a/stumpwmrc Sat Dec 04 22:49:41 2021 -0500
+++ b/stumpwmrc Mon Jan 31 21:28:01 2022 -0500
@@ -1,4 +1,5 @@
(in-package :stumpwm-user)
+(shadow :window)
(ql:quickload '(:losh :split-sequence :alexandria :parse-number :str :cl-ppcre :bordeaux-threads :jarl) :silent t)
(use-package :losh)
@@ -28,11 +29,11 @@
(and (search needle string :test #'char=) t))
(defun string-grep (needle text &key first-only)
- (-<> text
- (split-sequence:split-sequence #\newline <>)
+ (_ text
+ (split-sequence:split-sequence #\newline _)
(if first-only
- (find needle <> :test #'string-contains)
- (remove-if-not (alexandria:curry #'string-contains needle) <>))))
+ (find needle _ :test #'string-contains)
+ (remove-if-not (alexandria:curry #'string-contains needle) _))))
(defun string-split (delimiters string)
(split-sequence:split-sequence delimiters string
@@ -45,11 +46,11 @@
(defun volume ()
- (-<> (run-shell-command "amixer sget Master" t)
- (string-grep "Front Left:" <> :first-only t)
- (string-split "[]" <>)
+ (_ (run-shell-command "amixer sget Master" t)
+ (string-grep "Front Left:" _ :first-only t)
+ (string-split "[]" _)
second
- (string-trim "%" <>)
+ (string-trim "%" _)
parse-integer))
@@ -58,10 +59,10 @@
(defun keywordize (string)
- (-<> string
- (string-trim (string #\newline) <>)
+ (_ string
+ (string-trim (string #\newline) _)
string-upcase
- (intern <> (find-package :keyword))))
+ (intern _ (find-package :keyword))))
(defparameter *host* (keywordize (machine-instance)))
--- a/weechat/alias.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/alias.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- alias.conf
+# weechat -- alias.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
--- a/weechat/autosort.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/autosort.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- autosort.conf
+# weechat -- autosort.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
--- a/weechat/buflist.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/buflist.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- buflist.conf
+# weechat -- buflist.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
@@ -21,7 +21,6 @@
nick_prefix_empty = on
signals_refresh = ""
sort = "number,-active"
-use_items = 1
[format]
buffer = "${format_number}${indent}${format_nick_prefix}${color_hotlist}${format_name}"
@@ -38,4 +37,3 @@
name = "${name}"
nick_prefix = "${color_nick_prefix}${nick_prefix}"
number = "${color:green}${number}${if:${number_displayed}?.: }"
-tls_version = " ${color:default}(${if:${tls_version}==TLS1.3?${color:green}:${if:${tls_version}==TLS1.2?${color:yellow}:${color:red}}}${translate:${tls_version}}${color:default})"
--- a/weechat/charset.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/charset.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- charset.conf
+# weechat -- charset.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
--- a/weechat/exec.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/exec.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- exec.conf
+# weechat -- exec.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
@@ -12,7 +12,6 @@
[command]
default_options = ""
purge_delay = 0
-shell = "${env:SHELL}"
[color]
flag_finished = lightred
--- a/weechat/fifo.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/fifo.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- fifo.conf
+# weechat -- fifo.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
--- a/weechat/fset.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/fset.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,16 +1,15 @@
#
-# WeeChat -- fset.conf
+# weechat -- fset.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
[look]
-auto_refresh = "*"
auto_unmark = off
condition_catch_set = "${count} >= 1"
export_help_default = on
--- a/weechat/logger.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/logger.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- logger.conf
+# weechat -- logger.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
@@ -19,7 +19,6 @@
[file]
auto_log = on
-color_lines = off
flush_delay = 120
fsync = off
info_lines = off
--- a/weechat/python.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/python.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- python.conf
+# weechat -- python.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
--- a/weechat/relay.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/relay.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- relay.conf
+# weechat -- relay.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
@@ -27,20 +27,14 @@
[network]
allow_empty_password = off
allowed_ips = ""
-auth_timeout = 60
bind_address = ""
clients_purge_delay = 0
compression_level = 6
ipv6 = on
max_clients = 5
-nonce_size = 16
password = ""
-password_hash_algo = "*"
-password_hash_iterations = 100000
ssl_cert_key = "%h/ssl/relay.pem"
ssl_priorities = "NORMAL:-VERS-SSL3.0"
-totp_secret = ""
-totp_window = 0
websocket_allowed_origins = ""
[irc]
@@ -51,9 +45,4 @@
backlog_tags = "irc_privmsg"
backlog_time_format = "[%H:%M] "
-[weechat]
-commands = ""
-
[port]
-
-[path]
--- a/weechat/script.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/script.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- script.conf
+# weechat -- script.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
@@ -50,7 +50,6 @@
[scripts]
autoload = on
cache_expire = 60
-download_enabled = on
download_timeout = 30
hold = ""
path = "%h/script"
--- a/weechat/trigger.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/trigger.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- trigger.conf
+# weechat -- trigger.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
--- a/weechat/urlgrab.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/urlgrab.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- urlgrab.conf
+# weechat -- urlgrab.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
--- a/weechat/xfer.conf Sat Dec 04 22:49:41 2021 -0500
+++ b/weechat/xfer.conf Mon Jan 31 21:28:01 2022 -0500
@@ -1,10 +1,10 @@
#
-# WeeChat -- xfer.conf
+# weechat -- xfer.conf
#
# WARNING: It is NOT recommended to edit this file by hand,
# especially if WeeChat is running.
#
-# Use commands like /set or /fset to change settings in WeeChat.
+# Use /set or similar command to change settings in WeeChat.
#
# For more info, see: https://weechat.org/doc/quickstart
#
@@ -31,8 +31,7 @@
own_ip = ""
port_range = ""
send_ack = on
-speed_limit_recv = 0
-speed_limit_send = 0
+speed_limit = 0
timeout = 300
[file]
@@ -44,6 +43,5 @@
auto_resume = on
convert_spaces = on
download_path = "%h/xfer"
-download_temporary_suffix = ".part"
upload_path = "~"
use_nick_in_filename = on