Move custom functions to bundle--mk file
This commit is contained in:
parent
5539f196a5
commit
8a0bc113e1
2 changed files with 501 additions and 489 deletions
496
bundle/bundle--mk.el
Normal file
496
bundle/bundle--mk.el
Normal file
|
@ -0,0 +1,496 @@
|
||||||
|
;;; bundle-mk.el --- Personal functions -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
|
;; Copyright (C) 2024 Marcus Kammer
|
||||||
|
|
||||||
|
;; Author: Marcus Kammer <marcus.kammer@mailbox.org>
|
||||||
|
;; Keywords:
|
||||||
|
|
||||||
|
;; This program is free software; you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU General Public License as published by
|
||||||
|
;; the Free Software Foundation, either version 3 of the License, or
|
||||||
|
;; (at your option) any later version.
|
||||||
|
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU General Public License for more details.
|
||||||
|
|
||||||
|
;; You should have received a copy of the GNU General Public License
|
||||||
|
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
|
||||||
|
;; Personal functions library
|
||||||
|
|
||||||
|
;;; Code:
|
||||||
|
|
||||||
|
(defun org-agenda-log-mode-colorize-block ()
|
||||||
|
"Set different line spacing based on clock time duration."
|
||||||
|
(save-excursion
|
||||||
|
(let* ((colors (cl-case (alist-get 'background-mode (frame-parameters))
|
||||||
|
('light (list "#d8dee9" "#e5e9f0" "#eceff4"))
|
||||||
|
('dark (list "#bf616a" "#d08770" "#ebcb8b" "#a3be8c" "#b48ead"))))
|
||||||
|
pos
|
||||||
|
duration)
|
||||||
|
(nconc colors colors)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(while (setq pos (next-single-property-change (point) 'duration))
|
||||||
|
(goto-char pos)
|
||||||
|
(when (and (not (equal pos (point-at-eol)))
|
||||||
|
(setq duration (org-get-at-bol 'duration)))
|
||||||
|
;; larger duration bar height
|
||||||
|
(let ((line-height (if (< duration 15) 1.0 (+ 0.5 (/ duration 30))))
|
||||||
|
(ov (make-overlay (point-at-bol) (1+ (point-at-eol)))))
|
||||||
|
(overlay-put ov 'face `(:background ,(car colors) :foreground "black"))
|
||||||
|
(setq colors (cdr colors))
|
||||||
|
(overlay-put ov 'line-height line-height)
|
||||||
|
(overlay-put ov 'line-spacing (1- line-height))))))))
|
||||||
|
|
||||||
|
(add-hook 'org-agenda-finalize-hook #'org-agenda-log-mode-colorize-block)
|
||||||
|
|
||||||
|
(defun mk/link-to-current-task ()
|
||||||
|
"Return an Org link to the current clocked-in task, if any, or prompt for manual entry."
|
||||||
|
(if (org-clock-is-active)
|
||||||
|
(with-current-buffer (marker-buffer org-clock-marker)
|
||||||
|
(save-excursion
|
||||||
|
(goto-char org-clock-marker)
|
||||||
|
(format ":TASK_ID: [[id:%s][%s]]" (org-id-get) (org-get-heading t t))))
|
||||||
|
(let ((manual-id (read-string "Enter Task ID (leave empty if not applicable): ")))
|
||||||
|
(unless (string-empty-p manual-id)
|
||||||
|
(format ":TASK_ID: [[id:%s][Task]]\n" manual-id)))))
|
||||||
|
|
||||||
|
(defun mk/extract-headlines-by-tag (tag)
|
||||||
|
"Extract headlines and their content from current buffer by TAG."
|
||||||
|
(interactive "sEnter tag: ")
|
||||||
|
(save-excursion
|
||||||
|
(let ((result-buffer (generate-new-buffer (concat "*Extracted Headlines " tag "*"))))
|
||||||
|
(with-current-buffer result-buffer
|
||||||
|
(org-mode)
|
||||||
|
(insert "* Extracted Headlines by Tag: " tag " *\n\n"))
|
||||||
|
(org-element-map (org-element-parse-buffer) 'headline
|
||||||
|
(lambda (headline)
|
||||||
|
(when (member tag (org-element-property :tags headline))
|
||||||
|
(with-current-buffer result-buffer
|
||||||
|
(insert (org-element-interpret-data headline) "\n")))))
|
||||||
|
(pop-to-buffer result-buffer))))
|
||||||
|
|
||||||
|
;; (load "bundle--mk")
|
||||||
|
|
||||||
|
(defvar mk/useful-websites
|
||||||
|
'(("https://regexr.com/" regex debug)
|
||||||
|
("https://regex101.com/" regex debug)
|
||||||
|
("https://www.regextester.com/" regex debug)
|
||||||
|
("https://extendsclass.com/regex-tester.html#python" regex debug)
|
||||||
|
("https://everything.curl.dev/" curl tool)))
|
||||||
|
|
||||||
|
(defvar mk/mirror-website
|
||||||
|
"Locally mirror a website using `wget -mkEpnp <url>`")
|
||||||
|
|
||||||
|
(defvar mk/useful-regex
|
||||||
|
'(("match any word or space that precedes the :" . "[\\w\\s]+:")
|
||||||
|
("search for anything in square brackets" . "\\[.*\\]")
|
||||||
|
("upper and lowercase English alphabet" . "[A-Za-z]+")
|
||||||
|
("numbers from 0 to 9" . "[0-9]")
|
||||||
|
("upper and lowercase English alphabet, - and ." . "[A-Za-z\\-\\.]+")
|
||||||
|
("a, - and z" . "(a-z)")
|
||||||
|
("spaces or a comma" . "(\\s+|,)")
|
||||||
|
("find hashtags" . "#\\w+")
|
||||||
|
("matches both mentions (@) and hashtags" . "([@|#]\\w+)")
|
||||||
|
("email regex" . "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")
|
||||||
|
("matching decimal numbers" . "-?\\d+(\\.\\d*)?")
|
||||||
|
("matching urls" . "(?:http|https|ftp|mailto|file|data|irc):\\/\\/[A-Za-z0-9\\-]{0,63}(\\.[A-Za-z0-9\\-]{0,63})+(:\\d{1,4})?\\/*(\\/*[A-Za-z0-9\\-._]+\\/*)*(\\?.*)?(#.*)?")
|
||||||
|
("matching dates yyyy/mm/dd" . "^\\d{4}/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])$")
|
||||||
|
("matching dates mm/dd/yyyy" . "^(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])/\\d{4}$")
|
||||||
|
("matching dates dd/mm/yyyy" . "^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/\\d{4}$")
|
||||||
|
("matching HTML" . "<.+>")
|
||||||
|
("matching specific tags" . "</?(?:p|a|b|img)(?: /)?>")
|
||||||
|
("ISO 8601 Date Format (YYYY-MM-DD)" . "\\([0-9]{4}\\)-\\([0-1][0-9]\\)-\\([0-3][0-9]\\)")))
|
||||||
|
|
||||||
|
(defun mk/split-windows-horizontal (count-windows)
|
||||||
|
"Split windows horizontal by equal width."
|
||||||
|
(interactive "nHow many splits? ")
|
||||||
|
(delete-other-windows)
|
||||||
|
(let ((width (/ (window-total-width) count-windows)))
|
||||||
|
(dotimes (i (1- count-windows))
|
||||||
|
(split-window-right (- width)))))
|
||||||
|
|
||||||
|
(defun mk/split-windows-vertical (count-windows)
|
||||||
|
"Split windows vertical by equal width."
|
||||||
|
(interactive "nHow many splits? ")
|
||||||
|
(delete-other-windows)
|
||||||
|
(let ((height (/ (window-total-height) count-windows)))
|
||||||
|
(dotimes (i (1- count-windows))
|
||||||
|
(split-window-below (- height)))))
|
||||||
|
|
||||||
|
(defun mk/split-h3 ()
|
||||||
|
(interactive)
|
||||||
|
(mk/split-windows-horizontal 3))
|
||||||
|
|
||||||
|
(defun mk/split-v3 ()
|
||||||
|
(interactive)
|
||||||
|
(mk/split-windows-vertical 3))
|
||||||
|
|
||||||
|
;; Set transparency of emacs
|
||||||
|
(defun transparency (value)
|
||||||
|
"Sets the transparency of the frame window. 0=transparent/100=opaque"
|
||||||
|
(interactive "nTransparency Value 0 - 100 opaque: ")
|
||||||
|
(set-frame-parameter (selected-frame) 'alpha value))
|
||||||
|
|
||||||
|
(defun mk/show-agenda-list ()
|
||||||
|
(if (display-graphic-p)
|
||||||
|
(add-hook 'after-init-hook (lambda () (org-agenda-list) (mk/split-h3)))
|
||||||
|
(org-agenda-list)))
|
||||||
|
|
||||||
|
(defun mk/list-files (folder suffix)
|
||||||
|
(let ((regexp (concat "\\." suffix "$")))
|
||||||
|
(directory-files folder nil regexp)))
|
||||||
|
|
||||||
|
(defun mk/build-file-suffix ())
|
||||||
|
|
||||||
|
(defun mk/copy-files (src-dir dst-dir suffix)
|
||||||
|
(let ((src-files '())
|
||||||
|
(src-dir (expand-file-name src-dir))
|
||||||
|
(dst-dir (expand-file-name dst-dir)))
|
||||||
|
(dolist (file (mk/list-files src-dir suffix) src-files)
|
||||||
|
(let ((src-file (expand-file-name (concat src-dir "/" file)))
|
||||||
|
(dst-file (expand-file-name (concat dst-dir "/" file))))
|
||||||
|
(add-to-list 'src-files src-file)
|
||||||
|
(copy-file src-file dst-file t)))))
|
||||||
|
|
||||||
|
(defun mk/delete-files (lst)
|
||||||
|
(dolist (file lst)
|
||||||
|
(delete-file file t)))
|
||||||
|
|
||||||
|
(defun mk/get-current-time-formatted ()
|
||||||
|
(concat "#+DATE: "
|
||||||
|
(format "[%s]" (format-time-string "%Y-%m-%d %R" (current-time)))))
|
||||||
|
|
||||||
|
(defun mk/generate-unique-id ()
|
||||||
|
(interactive)
|
||||||
|
(let ((random-number-1 (random 9999))
|
||||||
|
(random-number-2 (random 9999)))
|
||||||
|
(let ((unique-id (format "DE-%04d-%04d" random-number-1 random-number-2)))
|
||||||
|
(message "ID: %s" unique-id) unique-id)))
|
||||||
|
|
||||||
|
(defmacro mk/open-html-page (name path buffer-name)
|
||||||
|
"Make interactive functions to call important docs"
|
||||||
|
`(defun ,name ()
|
||||||
|
(interactive)
|
||||||
|
(eww (concat "file://"
|
||||||
|
(and (eq system-type 'windows-nt)
|
||||||
|
"/")
|
||||||
|
(expand-file-name ,path)))
|
||||||
|
(rename-buffer ,buffer-name)))
|
||||||
|
|
||||||
|
(mk/open-html-page mk/clsite-clm
|
||||||
|
"~/cl-sites/www.cs.cmu.edu/Groups/AI/html/cltl/clm/node1.html"
|
||||||
|
"*mk/clsite-clm*")
|
||||||
|
(mk/open-html-page mk/clsite-pcl
|
||||||
|
"~/cl-sites/gigamonkeys.com/book/index.html"
|
||||||
|
"*mk/clsite-pcl*")
|
||||||
|
(mk/open-html-page mk/clsite-clcb
|
||||||
|
"~/cl-sites/lispcookbook.github.io/cl-cookbook/index.html"
|
||||||
|
"*mk/clsite-clcb*")
|
||||||
|
(mk/open-html-page mk/clsite-sqlite
|
||||||
|
"~/cl-sites/sqlite-doc-3440000/index.html"
|
||||||
|
"*mk/clsite-sqlite*")
|
||||||
|
(mk/open-html-page mk/clsite-asdf
|
||||||
|
"~/cl-sites/asdf.common-lisp.dev/index.html"
|
||||||
|
"*mk/clsite-asdf*")
|
||||||
|
(mk/open-html-page mk/clsite-lisp-docs
|
||||||
|
"~/cl-sites/lisp-docs.github.io/index.html"
|
||||||
|
"*mk/clsite-lisp-docs*")
|
||||||
|
(mk/open-html-page mk/clsite-bgnet
|
||||||
|
"~/cl-sites/bgnet/index.html"
|
||||||
|
"*mk/clsite-bgnet*")
|
||||||
|
(mk/open-html-page mk/clsite-tbnl
|
||||||
|
"~/cl-sites/edicl.github.io/hunchentoot/index.html"
|
||||||
|
"*mk/clsite-tbnl*")
|
||||||
|
|
||||||
|
(defun mk/wget-mirror-site (url)
|
||||||
|
"Use wget to mirror a website for offline use. Takes a URL as argument."
|
||||||
|
(interactive "sEnter the URL to mirror: ")
|
||||||
|
(let ((cmd (format "wget --mirror --convert-links --adjust-extension --page-requisites --no-parent %s" url)))
|
||||||
|
(async-shell-command cmd)))
|
||||||
|
|
||||||
|
(defun mk/get-auth-source-key (host key)
|
||||||
|
"Retrieve the API key using 'auth-source'."
|
||||||
|
(require 'auth-source)
|
||||||
|
(let* ((auth-source-creation-prompts
|
||||||
|
'((secret . (format "API key for %s@%s: " key host))))
|
||||||
|
(found (nth 0 (auth-source-search
|
||||||
|
:max 1
|
||||||
|
:host host
|
||||||
|
:user key
|
||||||
|
:require '(:secret)
|
||||||
|
:create t)))
|
||||||
|
(secret (plist-get found :secret)))
|
||||||
|
(if (functionp secret)
|
||||||
|
(funcall secret)
|
||||||
|
secret)))
|
||||||
|
|
||||||
|
;; (load "bundle--linux")
|
||||||
|
|
||||||
|
(defvar linux-filesystem-alist
|
||||||
|
'(( "/" . "Root directory, the base of the filesystem hierarchy")
|
||||||
|
("/bin" . "Essential command binaries, needed for booting")
|
||||||
|
("/boot" . "Bootloader files, kernel, and other files needed during booting")
|
||||||
|
("/dev" . "Device files representing hardware components")
|
||||||
|
("/etc" . "System-wide configuration files")
|
||||||
|
("/home" . "User home directories")
|
||||||
|
("/lib" . "Shared libraries and kernel modules")
|
||||||
|
("/media" . "Mount points for removable media like CDs and USBs")
|
||||||
|
("/mnt" . "Temporary mount points for filesystems")
|
||||||
|
("/opt" . "Optional application software packages")
|
||||||
|
("/proc" . "Virtual filesystem providing info about processes and system")
|
||||||
|
("/root" . "Home directory for the root user")
|
||||||
|
("/sbin" . "Essential system binaries, usually for the root user")
|
||||||
|
("/srv" . "Data directories for services like HTTP, FTP, etc.")
|
||||||
|
("/sys" . "Virtual filesystem for kernel objects")
|
||||||
|
("/tmp" . "Temporary files, cleared on reboot")
|
||||||
|
("/usr" . "User binaries, documentation, libraries, etc.")
|
||||||
|
("/var" . "Variable files like logs, databases, etc."))
|
||||||
|
"Alist mapping Linux directories to their descriptions.")
|
||||||
|
|
||||||
|
(defun describe-linux-directory (dirname)
|
||||||
|
"Describe the purpose of a Linux directory.
|
||||||
|
Takes DIRNAME as an argument and prints its description."
|
||||||
|
(interactive "sEnter Linux directory name (e.g., /bin): ")
|
||||||
|
(let ((description (assoc-default dirname linux-filesystem-alist)))
|
||||||
|
(if description
|
||||||
|
(message "%s: %s" dirname description)
|
||||||
|
(message "Unknown directory: %s" dirname))))
|
||||||
|
|
||||||
|
(defvar bash-regex-alist
|
||||||
|
'(("empty line" . "^$")
|
||||||
|
("backslash" . "\\\\")
|
||||||
|
("line starts with a dot" . "^\\.")
|
||||||
|
("line ends with a dot" . "\\.$")
|
||||||
|
("line starts with a dollar sign" . "^\\$")
|
||||||
|
("line starts with a caret" . "^\\^")
|
||||||
|
("left square bracket" . "\\[")
|
||||||
|
("right square bracket" . "\\]")
|
||||||
|
("entire line" . "^.*$")
|
||||||
|
("any alphanumeric character" . "[a-zA-Z0-9]")
|
||||||
|
("IP Address" . "\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b")
|
||||||
|
("email" . "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}")
|
||||||
|
("hex color code" . "#[a-fA-F0-9]{6}")
|
||||||
|
("date in yyyy-mm-dd" . "\\b\\d{4}-\\d{2}-\\d{2}\\b")
|
||||||
|
("time in hh:mm:ss" . "\\b\\d{2}:\\d{2}:\\d{2}\\b")
|
||||||
|
("words without vowels" . "\\b[^aeiou\s]+\\b"))
|
||||||
|
"Alist mapping Bash regular expressions to their descriptions.")
|
||||||
|
|
||||||
|
(defun describe-bash-regex (regex)
|
||||||
|
"Describe the purpose of a Bash regular expression.
|
||||||
|
Takes REGEX as an argument and prints its description."
|
||||||
|
(interactive "sEnter Bash regex (e.g., empty line): ")
|
||||||
|
(let ((description (assoc-default regex bash-regex-alist)))
|
||||||
|
(if description
|
||||||
|
(message "%s: %s" regex description)
|
||||||
|
(message "Unknown regular expression: %s" regex))))
|
||||||
|
|
||||||
|
(defvar linux-process-commands-alist
|
||||||
|
'(("ps" . "Shows a snapshot of the current processes")
|
||||||
|
("top" . "Displays dynamic real-time view of system stats and processes")
|
||||||
|
("htop" . "An interactive process viewer, similar to top but more feature-rich")
|
||||||
|
("pgrep" . "Looks up processes based on name and other attributes")
|
||||||
|
("pstree" . "Displays the process tree in a tree-like diagram")
|
||||||
|
("ps -e" . "Lists all the processes running on the system")
|
||||||
|
("ps aux" . "Displays detailed information about all processes")
|
||||||
|
("kill" . "Terminates processes by sending signals")
|
||||||
|
("killall" . "Kills all processes that match the given name")
|
||||||
|
("pkill" . "Send signals to processes based on name and other attributes"))
|
||||||
|
"Alist mapping Linux process-checking commands to their descriptions.")
|
||||||
|
|
||||||
|
(defun describe-linux-process-command (command)
|
||||||
|
"Describe the purpose of a Linux process-checking command.
|
||||||
|
Takes COMMAND as an argument and prints its description."
|
||||||
|
(interactive "sEnter Linux process command (e.g., ps): ")
|
||||||
|
(let ((description (assoc-default command linux-process-commands-alist)))
|
||||||
|
(if description
|
||||||
|
(message "%s: %s" command description)
|
||||||
|
(message "Unknown command: %s" command))))
|
||||||
|
|
||||||
|
(defvar linux-logfiles-alist
|
||||||
|
'(("/var/log/syslog" . "System messages, including the messages that are logged during system startup")
|
||||||
|
("/var/log/auth.log" . "Security/authorization information, including user logins and authentication")
|
||||||
|
("/var/log/kern.log" . "Kernel logs")
|
||||||
|
("/var/log/cron.log" . "Logs for cron jobs")
|
||||||
|
("/var/log/messages" . "General system activity logs")
|
||||||
|
("/var/log/boot.log" . "System boot log")
|
||||||
|
("/var/log/daemon.log" . "Background daemon log messages")
|
||||||
|
("/var/log/dpkg.log" . "Logs for package installations and removals")
|
||||||
|
("/var/log/mail.log" . "Mail server logs")
|
||||||
|
("/var/log/user.log" . "User-level messages"))
|
||||||
|
"Alist mapping Linux log files to their descriptions.")
|
||||||
|
|
||||||
|
(defun describe-linux-logfile (logfile)
|
||||||
|
"Describe the purpose of a Linux log file.
|
||||||
|
Takes LOGFILE as an argument and prints its description."
|
||||||
|
(interactive "sEnter Linux log file path (e.g., /var/log/syslog): ")
|
||||||
|
(let ((description (assoc-default logfile linux-logfiles-alist)))
|
||||||
|
(if description
|
||||||
|
(message "%s: %s" logfile description)
|
||||||
|
(message "Unknown log file: %s" logfile))))
|
||||||
|
|
||||||
|
(defvar linux-basic-commands-alist
|
||||||
|
'(("list directory contents" . "ls")
|
||||||
|
("change directory" . "cd")
|
||||||
|
("move or rename files" . "mv")
|
||||||
|
("copy files" . "cp")
|
||||||
|
("remove files or directories" . "rm")
|
||||||
|
("print working directory" . "pwd")
|
||||||
|
("display variable value" . "echo")
|
||||||
|
("create an empty file" . "touch")
|
||||||
|
("change file permissions" . "chmod")
|
||||||
|
("change file ownership" . "chown"))
|
||||||
|
"Alist mapping basic Linux command descriptions to their commands.")
|
||||||
|
|
||||||
|
(defun describe-basic-linux-command (command)
|
||||||
|
"Describe the purpose of a basic Linux command.
|
||||||
|
Takes COMMAND as an argument and prints its description."
|
||||||
|
(interactive "sEnter basic Linux command (e.g., ls): ")
|
||||||
|
(let ((description (assoc-default command linux-basic-commands-alist)))
|
||||||
|
(if description
|
||||||
|
(message "%s: %s" command description)
|
||||||
|
(message "Unknown command: %s" command))))
|
||||||
|
|
||||||
|
(defvar chown-options-alist
|
||||||
|
'(("-R" . "Operate on files and directories recursively")
|
||||||
|
("--from" . "Change the owner and/or group of each file only if its current owner and/or group match specified values")
|
||||||
|
("--no-dereference" . "Affect symbolic links instead of the files they point to")
|
||||||
|
("--preserve-root" . "Fail when attempting to operate recursively on '/'")
|
||||||
|
("--reference" . "Use owner and group of a reference file")
|
||||||
|
("-c" . "Report when a change is made")
|
||||||
|
("-f" . "Suppress most error messages")
|
||||||
|
("-v" . "Output a diagnostic for every file processed"))
|
||||||
|
"Alist mapping chown command options to their descriptions.")
|
||||||
|
|
||||||
|
(defun describe-chown-option (option)
|
||||||
|
"Describe the purpose of a chown option.
|
||||||
|
Takes OPTION as an argument and prints its description."
|
||||||
|
(interactive "sEnter chown option (e.g., -R): ")
|
||||||
|
(let ((description (assoc-default option chown-options-alist)))
|
||||||
|
(if description
|
||||||
|
(message "%s: %s" option description)
|
||||||
|
(message "Unknown chown option: %s" option))))
|
||||||
|
|
||||||
|
(defvar chmod-options-alist
|
||||||
|
'(("-R" . "Operate on files and directories recursively")
|
||||||
|
("--preserve-root" . "Avoid operating recursively on '/'")
|
||||||
|
("-c" . "Report when a change is made")
|
||||||
|
("-f" . "Suppress most error messages")
|
||||||
|
("-v" . "Output a diagnostic for every file processed")
|
||||||
|
("--reference" . "Use mode of a reference file")
|
||||||
|
("-w" . "Remove write permission")
|
||||||
|
("-x" . "Remove execute permission")
|
||||||
|
("-u" . "Set user ID on execution")
|
||||||
|
("-g" . "Set group ID on execution"))
|
||||||
|
"Alist mapping chmod command options to their descriptions.")
|
||||||
|
|
||||||
|
(defun describe-chmod-option (option)
|
||||||
|
"Describe the purpose of a chmod option.
|
||||||
|
Takes OPTION as an argument and prints its description."
|
||||||
|
(interactive "sEnter chmod option (e.g., -R): ")
|
||||||
|
(let ((description (assoc-default option chmod-options-alist)))
|
||||||
|
(if description
|
||||||
|
(message "%s: %s" option description)
|
||||||
|
(message "Unknown chmod option: %s" option))))
|
||||||
|
|
||||||
|
(defvar ssh-use-cases-alist
|
||||||
|
'(("remote login" . "ssh user@host")
|
||||||
|
("run command" . "ssh user@host 'command'")
|
||||||
|
("file transfer" . "scp file.txt user@host:/path/")
|
||||||
|
("secure ftp" . "sftp user@host")
|
||||||
|
("port forwarding" . "ssh -L local_port:remote_host:remote_port user@host")
|
||||||
|
("dynamic port forwarding" . "ssh -D port user@host")
|
||||||
|
("remote port forwarding" . "ssh -R remote_port:local_host:local_port user@host")
|
||||||
|
("tunneling" . "ssh -L local_port:remote_host:remote_port user@host -f -N")
|
||||||
|
("agent forwarding" . "ssh -A user@host")
|
||||||
|
("ssh multiplexing" . "ssh -M -S /tmp/ssh_socket user@host; ssh -S /tmp/ssh_socket user@host"))
|
||||||
|
"Alist mapping SSH use cases to their corresponding commands.")
|
||||||
|
|
||||||
|
(defun describe-ssh-use-case (use-case)
|
||||||
|
"Describe the SSH command for a given use case."
|
||||||
|
(interactive "sEnter the SSH use case: ")
|
||||||
|
(let ((command (assoc-default use-case ssh-use-cases-alist)))
|
||||||
|
(if command
|
||||||
|
(message "Command for %s: %s" use-case command)
|
||||||
|
(message "Use case not found"))))
|
||||||
|
|
||||||
|
(defvar mk/remote-*host-aliases*
|
||||||
|
'(("website" . "marcus@www.marcuskammer.dev")
|
||||||
|
("survey" . "cl@survey.metalisp.dev")
|
||||||
|
("pihole" . "ubuntu@pi-hole.fritz.box"))
|
||||||
|
"Alist mapping friendly host names to actual SSH-compatible host strings.")
|
||||||
|
|
||||||
|
(defun mk/remote--get-real-host (alias)
|
||||||
|
"Lookup the real host name based on a given ALIAS."
|
||||||
|
(or (cdr (assoc alias mk/remote-*host-aliases*)) alias))
|
||||||
|
|
||||||
|
(defun mk/remote--systemctl-service (alias service command)
|
||||||
|
"Execute a systemctl COMMAND on a systemd SERVICE on a remote host identified by ALIAS.
|
||||||
|
|
||||||
|
ALIAS is a string that specifies the remote host; it can be an
|
||||||
|
alias defined in `mk/remote-*host-aliases*'.
|
||||||
|
|
||||||
|
SERVICE is the name of the systemd service to operate on.
|
||||||
|
|
||||||
|
COMMAND is the systemctl command to execute on the service (e.g.,
|
||||||
|
'start', 'stop', 'status')."
|
||||||
|
(let* ((host (mk/remote--get-real-host alias))
|
||||||
|
(buffer (generate-new-buffer (format "*%s-%s-%s*" alias service command)))
|
||||||
|
(process-name (format "systemctl-%s-%s" command service)))
|
||||||
|
(make-process :name process-name
|
||||||
|
:buffer buffer
|
||||||
|
:command `("ssh" ,host "sudo" "systemctl" ,command ,service)
|
||||||
|
:sentinel (lambda (process signal)
|
||||||
|
(when (memq (process-status process) '(exit signal))
|
||||||
|
(message "Process: %s %s" process signal))))))
|
||||||
|
|
||||||
|
(defmacro mk/remote-define-systemctl-functions (&rest actions)
|
||||||
|
"Dynamically create functions to interact with systemd services on a remote host.
|
||||||
|
|
||||||
|
Each function will be named `mk/remote-ACTION-service', where
|
||||||
|
ACTION is one of the symbols in ACTIONS. The functions will take
|
||||||
|
an ALIAS and SERVICE as arguments and call
|
||||||
|
`mk/remote--systemctl-service' accordingly."
|
||||||
|
`(progn
|
||||||
|
,@(mapcar
|
||||||
|
(lambda (action)
|
||||||
|
`(defun ,(intern (format "mk/remote-%s-service" action)) (alias service)
|
||||||
|
(interactive "sEnter the host alias or name: \nsEnter the service name: ")
|
||||||
|
(mk/remote--systemctl-service alias service ,(symbol-name action))))
|
||||||
|
actions)))
|
||||||
|
|
||||||
|
(mk/remote-define-systemctl-functions start stop status)
|
||||||
|
|
||||||
|
(defun mk/remote--log (alias service filename)
|
||||||
|
(let* ((filepath (concat "/var/log/" service (unless (string-empty-p filename) (concat "/" filename))))
|
||||||
|
(host (mk/remote--get-real-host alias))
|
||||||
|
(buffer (generate-new-buffer (format "*%s-%s-%s*" alias service filename)))
|
||||||
|
(process-name (format "log-%s-%s" service filename)))
|
||||||
|
(make-process :name process-name
|
||||||
|
:buffer buffer
|
||||||
|
:command `("ssh" ,host "sudo" "tail -f" ,filepath)
|
||||||
|
:sentinel (lambda (process signal)
|
||||||
|
(when (memq (process-status process) '(exit signal))
|
||||||
|
(message "Process: %s %s" process signal))))))
|
||||||
|
|
||||||
|
(defmacro mk/define-remote-log-function (alias service &optional filename)
|
||||||
|
"Define a function to asynchronously tail a remote log file."
|
||||||
|
(let ((fname (if filename filename "")))
|
||||||
|
`(defun ,(intern (format "mk/remote-log-%s-%s-%s" alias service filename)) ()
|
||||||
|
,(format "Tail the remote log file: %s" filename)
|
||||||
|
(interactive)
|
||||||
|
(mk/remote--log ,alias ,service ,fname))))
|
||||||
|
|
||||||
|
(mk/define-remote-log-function "website" "nginx" "access.csv")
|
||||||
|
(mk/define-remote-log-function "website" "nginx" "error.log")
|
||||||
|
(mk/define-remote-log-function "website" "syslog")
|
||||||
|
(mk/define-remote-log-function "survey" "nginx" "access.csv")
|
||||||
|
(mk/define-remote-log-function "survey" "syslog")
|
||||||
|
(mk/define-remote-log-function "pihole" "pihole" "pihole.log")
|
||||||
|
|
||||||
|
(provide 'bundle-mk)
|
||||||
|
;;; bundle-mk.el ends here
|
494
init.el
494
init.el
|
@ -1661,28 +1661,7 @@ Uses `mk/hyperspec-dir-locations' to find the directory."
|
||||||
(js . t)
|
(js . t)
|
||||||
(scheme . t)))
|
(scheme . t)))
|
||||||
;; work with org-agenda dispatcher [c] "Today Clocked Tasks" to view today's clocked tasks.
|
;; work with org-agenda dispatcher [c] "Today Clocked Tasks" to view today's clocked tasks.
|
||||||
(defun org-agenda-log-mode-colorize-block ()
|
)
|
||||||
"Set different line spacing based on clock time duration."
|
|
||||||
(save-excursion
|
|
||||||
(let* ((colors (cl-case (alist-get 'background-mode (frame-parameters))
|
|
||||||
('light (list "#d8dee9" "#e5e9f0" "#eceff4"))
|
|
||||||
('dark (list "#bf616a" "#d08770" "#ebcb8b" "#a3be8c" "#b48ead"))))
|
|
||||||
pos
|
|
||||||
duration)
|
|
||||||
(nconc colors colors)
|
|
||||||
(goto-char (point-min))
|
|
||||||
(while (setq pos (next-single-property-change (point) 'duration))
|
|
||||||
(goto-char pos)
|
|
||||||
(when (and (not (equal pos (point-at-eol)))
|
|
||||||
(setq duration (org-get-at-bol 'duration)))
|
|
||||||
;; larger duration bar height
|
|
||||||
(let ((line-height (if (< duration 15) 1.0 (+ 0.5 (/ duration 30))))
|
|
||||||
(ov (make-overlay (point-at-bol) (1+ (point-at-eol)))))
|
|
||||||
(overlay-put ov 'face `(:background ,(car colors) :foreground "black"))
|
|
||||||
(setq colors (cdr colors))
|
|
||||||
(overlay-put ov 'line-height line-height)
|
|
||||||
(overlay-put ov 'line-spacing (1- line-height))))))))
|
|
||||||
(add-hook 'org-agenda-finalize-hook #'org-agenda-log-mode-colorize-block))
|
|
||||||
|
|
||||||
;; Define custom LaTeX class with specific formatting
|
;; Define custom LaTeX class with specific formatting
|
||||||
(with-eval-after-load 'ox-latex
|
(with-eval-after-load 'ox-latex
|
||||||
|
@ -1723,17 +1702,6 @@ Uses `mk/hyperspec-dir-locations' to find the directory."
|
||||||
(":-)" . "😊")
|
(":-)" . "😊")
|
||||||
(":-(" . "😞")))
|
(":-(" . "😞")))
|
||||||
|
|
||||||
(defun mk/link-to-current-task ()
|
|
||||||
"Return an Org link to the current clocked-in task, if any, or prompt for manual entry."
|
|
||||||
(if (org-clock-is-active)
|
|
||||||
(with-current-buffer (marker-buffer org-clock-marker)
|
|
||||||
(save-excursion
|
|
||||||
(goto-char org-clock-marker)
|
|
||||||
(format ":TASK_ID: [[id:%s][%s]]" (org-id-get) (org-get-heading t t))))
|
|
||||||
(let ((manual-id (read-string "Enter Task ID (leave empty if not applicable): ")))
|
|
||||||
(unless (string-empty-p manual-id)
|
|
||||||
(format ":TASK_ID: [[id:%s][Task]]\n" manual-id)))))
|
|
||||||
|
|
||||||
;; Note Capture Template
|
;; Note Capture Template
|
||||||
;; Purpose: To capture general notes. These notes can optionally be linked to a currently clocked-in task.
|
;; Purpose: To capture general notes. These notes can optionally be linked to a currently clocked-in task.
|
||||||
;; Fields: Heading, optional task link, initial content, and a timestamp.
|
;; Fields: Heading, optional task link, initial content, and a timestamp.
|
||||||
|
@ -1934,30 +1902,6 @@ Uses `mk/hyperspec-dir-locations' to find the directory."
|
||||||
|
|
||||||
(require 'request)
|
(require 'request)
|
||||||
|
|
||||||
(defun fetch-gitlab-issue-and-capture ()
|
|
||||||
(interactive)
|
|
||||||
(let ((project-id (read-string "Enter GitLab Project ID: "))
|
|
||||||
(issue-id (read-string "Enter GitLab Issue ID: "))
|
|
||||||
(gitlab-token (getenv "GITLAB_TOKEN")))
|
|
||||||
(unless gitlab-token
|
|
||||||
(error "GITLAB_TOKEN environment variable not set"))
|
|
||||||
(request
|
|
||||||
(format "https://code.siemens.com/api/v4/projects/%s/issues/%s" project-id issue-id)
|
|
||||||
:headers `(("Private-Token" . ,gitlab-token))
|
|
||||||
:parser 'json-read
|
|
||||||
:success (cl-function
|
|
||||||
(lambda (&key data &allow-other-keys)
|
|
||||||
(org-capture nil "g")
|
|
||||||
(let ((title (assoc-default 'title data))
|
|
||||||
(description (assoc-default 'description data)))
|
|
||||||
(insert (format "%s\n%s" title description))))))))
|
|
||||||
|
|
||||||
(add-to-list 'org-capture-templates
|
|
||||||
'("g" "GitLab Issue" entry (file "gitlab_issues.org")
|
|
||||||
"* %?"))
|
|
||||||
|
|
||||||
;; (global-set-key (kbd "C-c g") 'fetch-gitlab-issue-and-capture)
|
|
||||||
|
|
||||||
(use-package org-roam
|
(use-package org-roam
|
||||||
:after org
|
:after org
|
||||||
:init
|
:init
|
||||||
|
@ -2057,435 +2001,7 @@ Uses `mk/hyperspec-dir-locations' to find the directory."
|
||||||
;; :config
|
;; :config
|
||||||
;; (append '((restclient . t)) org-babel-load-languages))
|
;; (append '((restclient . t)) org-babel-load-languages))
|
||||||
|
|
||||||
(defun mk/extract-headlines-by-tag (tag)
|
(let ((bundle-dir (expand-file-name "~/.emacs.d/bundle/")))
|
||||||
"Extract headlines and their content from current buffer by TAG."
|
(when (file-directory-p bundle-dir)
|
||||||
(interactive "sEnter tag: ")
|
(add-to-list 'load-path bundle-dir)
|
||||||
(save-excursion
|
(load "bundle--mk")))
|
||||||
(let ((result-buffer (generate-new-buffer (concat "*Extracted Headlines " tag "*"))))
|
|
||||||
(with-current-buffer result-buffer
|
|
||||||
(org-mode)
|
|
||||||
(insert "* Extracted Headlines by Tag: " tag " *\n\n"))
|
|
||||||
(org-element-map (org-element-parse-buffer) 'headline
|
|
||||||
(lambda (headline)
|
|
||||||
(when (member tag (org-element-property :tags headline))
|
|
||||||
(with-current-buffer result-buffer
|
|
||||||
(insert (org-element-interpret-data headline) "\n")))))
|
|
||||||
(pop-to-buffer result-buffer))))
|
|
||||||
|
|
||||||
;; (load "bundle--mk")
|
|
||||||
|
|
||||||
(defvar mk/useful-websites
|
|
||||||
'(("https://regexr.com/" regex debug)
|
|
||||||
("https://regex101.com/" regex debug)
|
|
||||||
("https://www.regextester.com/" regex debug)
|
|
||||||
("https://extendsclass.com/regex-tester.html#python" regex debug)
|
|
||||||
("https://everything.curl.dev/" curl tool)))
|
|
||||||
|
|
||||||
(defvar mk/mirror-website
|
|
||||||
"Locally mirror a website using `wget -mkEpnp <url>`")
|
|
||||||
|
|
||||||
(defvar mk/useful-regex
|
|
||||||
'(("match any word or space that precedes the :" . "[\\w\\s]+:")
|
|
||||||
("search for anything in square brackets" . "\\[.*\\]")
|
|
||||||
("upper and lowercase English alphabet" . "[A-Za-z]+")
|
|
||||||
("numbers from 0 to 9" . "[0-9]")
|
|
||||||
("upper and lowercase English alphabet, - and ." . "[A-Za-z\\-\\.]+")
|
|
||||||
("a, - and z" . "(a-z)")
|
|
||||||
("spaces or a comma" . "(\\s+|,)")
|
|
||||||
("find hashtags" . "#\\w+")
|
|
||||||
("matches both mentions (@) and hashtags" . "([@|#]\\w+)")
|
|
||||||
("email regex" . "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")
|
|
||||||
("matching decimal numbers" . "-?\\d+(\\.\\d*)?")
|
|
||||||
("matching urls" . "(?:http|https|ftp|mailto|file|data|irc):\\/\\/[A-Za-z0-9\\-]{0,63}(\\.[A-Za-z0-9\\-]{0,63})+(:\\d{1,4})?\\/*(\\/*[A-Za-z0-9\\-._]+\\/*)*(\\?.*)?(#.*)?")
|
|
||||||
("matching dates yyyy/mm/dd" . "^\\d{4}/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])$")
|
|
||||||
("matching dates mm/dd/yyyy" . "^(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])/\\d{4}$")
|
|
||||||
("matching dates dd/mm/yyyy" . "^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/\\d{4}$")
|
|
||||||
("matching HTML" . "<.+>")
|
|
||||||
("matching specific tags" . "</?(?:p|a|b|img)(?: /)?>")
|
|
||||||
("ISO 8601 Date Format (YYYY-MM-DD)" . "\\([0-9]{4}\\)-\\([0-1][0-9]\\)-\\([0-3][0-9]\\)")))
|
|
||||||
|
|
||||||
(defun mk/split-windows-horizontal (count-windows)
|
|
||||||
"Split windows horizontal by equal width."
|
|
||||||
(interactive "nHow many splits? ")
|
|
||||||
(delete-other-windows)
|
|
||||||
(let ((width (/ (window-total-width) count-windows)))
|
|
||||||
(dotimes (i (1- count-windows))
|
|
||||||
(split-window-right (- width)))))
|
|
||||||
|
|
||||||
(defun mk/split-windows-vertical (count-windows)
|
|
||||||
"Split windows vertical by equal width."
|
|
||||||
(interactive "nHow many splits? ")
|
|
||||||
(delete-other-windows)
|
|
||||||
(let ((height (/ (window-total-height) count-windows)))
|
|
||||||
(dotimes (i (1- count-windows))
|
|
||||||
(split-window-below (- height)))))
|
|
||||||
|
|
||||||
(defun mk/split-h3 ()
|
|
||||||
(interactive)
|
|
||||||
(mk/split-windows-horizontal 3))
|
|
||||||
|
|
||||||
(defun mk/split-v3 ()
|
|
||||||
(interactive)
|
|
||||||
(mk/split-windows-vertical 3))
|
|
||||||
|
|
||||||
;; Set transparency of emacs
|
|
||||||
(defun transparency (value)
|
|
||||||
"Sets the transparency of the frame window. 0=transparent/100=opaque"
|
|
||||||
(interactive "nTransparency Value 0 - 100 opaque: ")
|
|
||||||
(set-frame-parameter (selected-frame) 'alpha value))
|
|
||||||
|
|
||||||
(defun mk/show-agenda-list ()
|
|
||||||
(if (display-graphic-p)
|
|
||||||
(add-hook 'after-init-hook (lambda () (org-agenda-list) (me/split-h3)))
|
|
||||||
(org-agenda-list)))
|
|
||||||
|
|
||||||
(defun mk/list-files (folder suffix)
|
|
||||||
(let ((regexp (concat "\\." suffix "$")))
|
|
||||||
(directory-files folder nil regexp)))
|
|
||||||
|
|
||||||
(defun mk/build-file-suffix ())
|
|
||||||
|
|
||||||
(defun mk/copy-files (src-dir dst-dir suffix)
|
|
||||||
(let ((src-files '())
|
|
||||||
(src-dir (expand-file-name src-dir))
|
|
||||||
(dst-dir (expand-file-name dst-dir)))
|
|
||||||
(dolist (file (mk/list-files src-dir suffix) src-files)
|
|
||||||
(let ((src-file (expand-file-name (concat src-dir "/" file)))
|
|
||||||
(dst-file (expand-file-name (concat dst-dir "/" file))))
|
|
||||||
(add-to-list 'src-files src-file)
|
|
||||||
(copy-file src-file dst-file t)))))
|
|
||||||
|
|
||||||
(defun mk/delete-files (lst)
|
|
||||||
(dolist (file lst)
|
|
||||||
(delete-file file t)))
|
|
||||||
|
|
||||||
(defun mk/get-current-time-formatted ()
|
|
||||||
(concat "#+DATE: "
|
|
||||||
(format "[%s]" (format-time-string "%Y-%m-%d %R" (current-time)))))
|
|
||||||
|
|
||||||
(defun mk/generate-unique-id ()
|
|
||||||
(interactive)
|
|
||||||
(let ((random-number-1 (random 9999))
|
|
||||||
(random-number-2 (random 9999)))
|
|
||||||
(let ((unique-id (format "DE-%04d-%04d" random-number-1 random-number-2)))
|
|
||||||
(message "ID: %s" unique-id) unique-id)))
|
|
||||||
|
|
||||||
(defmacro mk/open-html-page (name path buffer-name)
|
|
||||||
"Make interactive functions to call important docs"
|
|
||||||
`(defun ,name ()
|
|
||||||
(interactive)
|
|
||||||
(eww (concat "file://"
|
|
||||||
(and (eq system-type 'windows-nt)
|
|
||||||
"/")
|
|
||||||
(expand-file-name ,path)))
|
|
||||||
(rename-buffer ,buffer-name)))
|
|
||||||
|
|
||||||
(mk/open-html-page mk/clsite-clm
|
|
||||||
"~/cl-sites/www.cs.cmu.edu/Groups/AI/html/cltl/clm/node1.html"
|
|
||||||
"*mk/clsite-clm*")
|
|
||||||
(mk/open-html-page mk/clsite-pcl
|
|
||||||
"~/cl-sites/gigamonkeys.com/book/index.html"
|
|
||||||
"*mk/clsite-pcl*")
|
|
||||||
(mk/open-html-page mk/clsite-clcb
|
|
||||||
"~/cl-sites/lispcookbook.github.io/cl-cookbook/index.html"
|
|
||||||
"*mk/clsite-clcb*")
|
|
||||||
(mk/open-html-page mk/clsite-sqlite
|
|
||||||
"~/cl-sites/sqlite-doc-3440000/index.html"
|
|
||||||
"*mk/clsite-sqlite*")
|
|
||||||
(mk/open-html-page mk/clsite-asdf
|
|
||||||
"~/cl-sites/asdf.common-lisp.dev/index.html"
|
|
||||||
"*mk/clsite-asdf*")
|
|
||||||
(mk/open-html-page mk/clsite-lisp-docs
|
|
||||||
"~/cl-sites/lisp-docs.github.io/index.html"
|
|
||||||
"*mk/clsite-lisp-docs*")
|
|
||||||
(mk/open-html-page mk/clsite-bgnet
|
|
||||||
"~/cl-sites/bgnet/index.html"
|
|
||||||
"*mk/clsite-bgnet*")
|
|
||||||
(mk/open-html-page mk/clsite-tbnl
|
|
||||||
"~/cl-sites/edicl.github.io/hunchentoot/index.html"
|
|
||||||
"*mk/clsite-tbnl*")
|
|
||||||
|
|
||||||
(defun mk/wget-mirror-site (url)
|
|
||||||
"Use wget to mirror a website for offline use. Takes a URL as argument."
|
|
||||||
(interactive "sEnter the URL to mirror: ")
|
|
||||||
(let ((cmd (format "wget --mirror --convert-links --adjust-extension --page-requisites --no-parent %s" url)))
|
|
||||||
(async-shell-command cmd)))
|
|
||||||
|
|
||||||
(defun mk/get-auth-source-key (host key)
|
|
||||||
"Retrieve the API key using 'auth-source'."
|
|
||||||
(require 'auth-source)
|
|
||||||
(let* ((auth-source-creation-prompts
|
|
||||||
'((secret . (format "API key for %s@%s: " key host))))
|
|
||||||
(found (nth 0 (auth-source-search
|
|
||||||
:max 1
|
|
||||||
:host host
|
|
||||||
:user key
|
|
||||||
:require '(:secret)
|
|
||||||
:create t)))
|
|
||||||
(secret (plist-get found :secret)))
|
|
||||||
(if (functionp secret)
|
|
||||||
(funcall secret)
|
|
||||||
secret)))
|
|
||||||
|
|
||||||
;; (load "bundle--linux")
|
|
||||||
|
|
||||||
(defvar linux-filesystem-alist
|
|
||||||
'(( "/" . "Root directory, the base of the filesystem hierarchy")
|
|
||||||
("/bin" . "Essential command binaries, needed for booting")
|
|
||||||
("/boot" . "Bootloader files, kernel, and other files needed during booting")
|
|
||||||
("/dev" . "Device files representing hardware components")
|
|
||||||
("/etc" . "System-wide configuration files")
|
|
||||||
("/home" . "User home directories")
|
|
||||||
("/lib" . "Shared libraries and kernel modules")
|
|
||||||
("/media" . "Mount points for removable media like CDs and USBs")
|
|
||||||
("/mnt" . "Temporary mount points for filesystems")
|
|
||||||
("/opt" . "Optional application software packages")
|
|
||||||
("/proc" . "Virtual filesystem providing info about processes and system")
|
|
||||||
("/root" . "Home directory for the root user")
|
|
||||||
("/sbin" . "Essential system binaries, usually for the root user")
|
|
||||||
("/srv" . "Data directories for services like HTTP, FTP, etc.")
|
|
||||||
("/sys" . "Virtual filesystem for kernel objects")
|
|
||||||
("/tmp" . "Temporary files, cleared on reboot")
|
|
||||||
("/usr" . "User binaries, documentation, libraries, etc.")
|
|
||||||
("/var" . "Variable files like logs, databases, etc."))
|
|
||||||
"Alist mapping Linux directories to their descriptions.")
|
|
||||||
|
|
||||||
(defun describe-linux-directory (dirname)
|
|
||||||
"Describe the purpose of a Linux directory.
|
|
||||||
Takes DIRNAME as an argument and prints its description."
|
|
||||||
(interactive "sEnter Linux directory name (e.g., /bin): ")
|
|
||||||
(let ((description (assoc-default dirname linux-filesystem-alist)))
|
|
||||||
(if description
|
|
||||||
(message "%s: %s" dirname description)
|
|
||||||
(message "Unknown directory: %s" dirname))))
|
|
||||||
|
|
||||||
(defvar bash-regex-alist
|
|
||||||
'(("empty line" . "^$")
|
|
||||||
("backslash" . "\\\\")
|
|
||||||
("line starts with a dot" . "^\\.")
|
|
||||||
("line ends with a dot" . "\\.$")
|
|
||||||
("line starts with a dollar sign" . "^\\$")
|
|
||||||
("line starts with a caret" . "^\\^")
|
|
||||||
("left square bracket" . "\\[")
|
|
||||||
("right square bracket" . "\\]")
|
|
||||||
("entire line" . "^.*$")
|
|
||||||
("any alphanumeric character" . "[a-zA-Z0-9]")
|
|
||||||
("IP Address" . "\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b")
|
|
||||||
("email" . "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}")
|
|
||||||
("hex color code" . "#[a-fA-F0-9]{6}")
|
|
||||||
("date in yyyy-mm-dd" . "\\b\\d{4}-\\d{2}-\\d{2}\\b")
|
|
||||||
("time in hh:mm:ss" . "\\b\\d{2}:\\d{2}:\\d{2}\\b")
|
|
||||||
("words without vowels" . "\\b[^aeiou\s]+\\b"))
|
|
||||||
"Alist mapping Bash regular expressions to their descriptions.")
|
|
||||||
|
|
||||||
(defun describe-bash-regex (regex)
|
|
||||||
"Describe the purpose of a Bash regular expression.
|
|
||||||
Takes REGEX as an argument and prints its description."
|
|
||||||
(interactive "sEnter Bash regex (e.g., empty line): ")
|
|
||||||
(let ((description (assoc-default regex bash-regex-alist)))
|
|
||||||
(if description
|
|
||||||
(message "%s: %s" regex description)
|
|
||||||
(message "Unknown regular expression: %s" regex))))
|
|
||||||
|
|
||||||
(defvar linux-process-commands-alist
|
|
||||||
'(("ps" . "Shows a snapshot of the current processes")
|
|
||||||
("top" . "Displays dynamic real-time view of system stats and processes")
|
|
||||||
("htop" . "An interactive process viewer, similar to top but more feature-rich")
|
|
||||||
("pgrep" . "Looks up processes based on name and other attributes")
|
|
||||||
("pstree" . "Displays the process tree in a tree-like diagram")
|
|
||||||
("ps -e" . "Lists all the processes running on the system")
|
|
||||||
("ps aux" . "Displays detailed information about all processes")
|
|
||||||
("kill" . "Terminates processes by sending signals")
|
|
||||||
("killall" . "Kills all processes that match the given name")
|
|
||||||
("pkill" . "Send signals to processes based on name and other attributes"))
|
|
||||||
"Alist mapping Linux process-checking commands to their descriptions.")
|
|
||||||
|
|
||||||
(defun describe-linux-process-command (command)
|
|
||||||
"Describe the purpose of a Linux process-checking command.
|
|
||||||
Takes COMMAND as an argument and prints its description."
|
|
||||||
(interactive "sEnter Linux process command (e.g., ps): ")
|
|
||||||
(let ((description (assoc-default command linux-process-commands-alist)))
|
|
||||||
(if description
|
|
||||||
(message "%s: %s" command description)
|
|
||||||
(message "Unknown command: %s" command))))
|
|
||||||
|
|
||||||
(defvar linux-logfiles-alist
|
|
||||||
'(("/var/log/syslog" . "System messages, including the messages that are logged during system startup")
|
|
||||||
("/var/log/auth.log" . "Security/authorization information, including user logins and authentication")
|
|
||||||
("/var/log/kern.log" . "Kernel logs")
|
|
||||||
("/var/log/cron.log" . "Logs for cron jobs")
|
|
||||||
("/var/log/messages" . "General system activity logs")
|
|
||||||
("/var/log/boot.log" . "System boot log")
|
|
||||||
("/var/log/daemon.log" . "Background daemon log messages")
|
|
||||||
("/var/log/dpkg.log" . "Logs for package installations and removals")
|
|
||||||
("/var/log/mail.log" . "Mail server logs")
|
|
||||||
("/var/log/user.log" . "User-level messages"))
|
|
||||||
"Alist mapping Linux log files to their descriptions.")
|
|
||||||
|
|
||||||
(defun describe-linux-logfile (logfile)
|
|
||||||
"Describe the purpose of a Linux log file.
|
|
||||||
Takes LOGFILE as an argument and prints its description."
|
|
||||||
(interactive "sEnter Linux log file path (e.g., /var/log/syslog): ")
|
|
||||||
(let ((description (assoc-default logfile linux-logfiles-alist)))
|
|
||||||
(if description
|
|
||||||
(message "%s: %s" logfile description)
|
|
||||||
(message "Unknown log file: %s" logfile))))
|
|
||||||
|
|
||||||
(defvar linux-basic-commands-alist
|
|
||||||
'(("list directory contents" . "ls")
|
|
||||||
("change directory" . "cd")
|
|
||||||
("move or rename files" . "mv")
|
|
||||||
("copy files" . "cp")
|
|
||||||
("remove files or directories" . "rm")
|
|
||||||
("print working directory" . "pwd")
|
|
||||||
("display variable value" . "echo")
|
|
||||||
("create an empty file" . "touch")
|
|
||||||
("change file permissions" . "chmod")
|
|
||||||
("change file ownership" . "chown"))
|
|
||||||
"Alist mapping basic Linux command descriptions to their commands.")
|
|
||||||
|
|
||||||
(defun describe-basic-linux-command (command)
|
|
||||||
"Describe the purpose of a basic Linux command.
|
|
||||||
Takes COMMAND as an argument and prints its description."
|
|
||||||
(interactive "sEnter basic Linux command (e.g., ls): ")
|
|
||||||
(let ((description (assoc-default command linux-basic-commands-alist)))
|
|
||||||
(if description
|
|
||||||
(message "%s: %s" command description)
|
|
||||||
(message "Unknown command: %s" command))))
|
|
||||||
|
|
||||||
(defvar chown-options-alist
|
|
||||||
'(("-R" . "Operate on files and directories recursively")
|
|
||||||
("--from" . "Change the owner and/or group of each file only if its current owner and/or group match specified values")
|
|
||||||
("--no-dereference" . "Affect symbolic links instead of the files they point to")
|
|
||||||
("--preserve-root" . "Fail when attempting to operate recursively on '/'")
|
|
||||||
("--reference" . "Use owner and group of a reference file")
|
|
||||||
("-c" . "Report when a change is made")
|
|
||||||
("-f" . "Suppress most error messages")
|
|
||||||
("-v" . "Output a diagnostic for every file processed"))
|
|
||||||
"Alist mapping chown command options to their descriptions.")
|
|
||||||
|
|
||||||
(defun describe-chown-option (option)
|
|
||||||
"Describe the purpose of a chown option.
|
|
||||||
Takes OPTION as an argument and prints its description."
|
|
||||||
(interactive "sEnter chown option (e.g., -R): ")
|
|
||||||
(let ((description (assoc-default option chown-options-alist)))
|
|
||||||
(if description
|
|
||||||
(message "%s: %s" option description)
|
|
||||||
(message "Unknown chown option: %s" option))))
|
|
||||||
|
|
||||||
(defvar chmod-options-alist
|
|
||||||
'(("-R" . "Operate on files and directories recursively")
|
|
||||||
("--preserve-root" . "Avoid operating recursively on '/'")
|
|
||||||
("-c" . "Report when a change is made")
|
|
||||||
("-f" . "Suppress most error messages")
|
|
||||||
("-v" . "Output a diagnostic for every file processed")
|
|
||||||
("--reference" . "Use mode of a reference file")
|
|
||||||
("-w" . "Remove write permission")
|
|
||||||
("-x" . "Remove execute permission")
|
|
||||||
("-u" . "Set user ID on execution")
|
|
||||||
("-g" . "Set group ID on execution"))
|
|
||||||
"Alist mapping chmod command options to their descriptions.")
|
|
||||||
|
|
||||||
(defun describe-chmod-option (option)
|
|
||||||
"Describe the purpose of a chmod option.
|
|
||||||
Takes OPTION as an argument and prints its description."
|
|
||||||
(interactive "sEnter chmod option (e.g., -R): ")
|
|
||||||
(let ((description (assoc-default option chmod-options-alist)))
|
|
||||||
(if description
|
|
||||||
(message "%s: %s" option description)
|
|
||||||
(message "Unknown chmod option: %s" option))))
|
|
||||||
|
|
||||||
(defvar ssh-use-cases-alist
|
|
||||||
'(("remote login" . "ssh user@host")
|
|
||||||
("run command" . "ssh user@host 'command'")
|
|
||||||
("file transfer" . "scp file.txt user@host:/path/")
|
|
||||||
("secure ftp" . "sftp user@host")
|
|
||||||
("port forwarding" . "ssh -L local_port:remote_host:remote_port user@host")
|
|
||||||
("dynamic port forwarding" . "ssh -D port user@host")
|
|
||||||
("remote port forwarding" . "ssh -R remote_port:local_host:local_port user@host")
|
|
||||||
("tunneling" . "ssh -L local_port:remote_host:remote_port user@host -f -N")
|
|
||||||
("agent forwarding" . "ssh -A user@host")
|
|
||||||
("ssh multiplexing" . "ssh -M -S /tmp/ssh_socket user@host; ssh -S /tmp/ssh_socket user@host"))
|
|
||||||
"Alist mapping SSH use cases to their corresponding commands.")
|
|
||||||
|
|
||||||
(defun describe-ssh-use-case (use-case)
|
|
||||||
"Describe the SSH command for a given use case."
|
|
||||||
(interactive "sEnter the SSH use case: ")
|
|
||||||
(let ((command (assoc-default use-case ssh-use-cases-alist)))
|
|
||||||
(if command
|
|
||||||
(message "Command for %s: %s" use-case command)
|
|
||||||
(message "Use case not found"))))
|
|
||||||
|
|
||||||
(defvar mk/remote-*host-aliases*
|
|
||||||
'(("website" . "marcus@www.marcuskammer.dev")
|
|
||||||
("survey" . "cl@survey.metalisp.dev")
|
|
||||||
("pihole" . "ubuntu@pi-hole.fritz.box"))
|
|
||||||
"Alist mapping friendly host names to actual SSH-compatible host strings.")
|
|
||||||
|
|
||||||
(defun mk/remote--get-real-host (alias)
|
|
||||||
"Lookup the real host name based on a given ALIAS."
|
|
||||||
(or (cdr (assoc alias mk/remote-*host-aliases*)) alias))
|
|
||||||
|
|
||||||
(defun mk/remote--systemctl-service (alias service command)
|
|
||||||
"Execute a systemctl COMMAND on a systemd SERVICE on a remote host identified by ALIAS.
|
|
||||||
|
|
||||||
ALIAS is a string that specifies the remote host; it can be an
|
|
||||||
alias defined in `mk/remote-*host-aliases*'.
|
|
||||||
|
|
||||||
SERVICE is the name of the systemd service to operate on.
|
|
||||||
|
|
||||||
COMMAND is the systemctl command to execute on the service (e.g.,
|
|
||||||
'start', 'stop', 'status')."
|
|
||||||
(let* ((host (mk/remote--get-real-host alias))
|
|
||||||
(buffer (generate-new-buffer (format "*%s-%s-%s*" alias service command)))
|
|
||||||
(process-name (format "systemctl-%s-%s" command service)))
|
|
||||||
(make-process :name process-name
|
|
||||||
:buffer buffer
|
|
||||||
:command `("ssh" ,host "sudo" "systemctl" ,command ,service)
|
|
||||||
:sentinel (lambda (process signal)
|
|
||||||
(when (memq (process-status process) '(exit signal))
|
|
||||||
(message "Process: %s %s" process signal))))))
|
|
||||||
|
|
||||||
(defmacro mk/remote-define-systemctl-functions (&rest actions)
|
|
||||||
"Dynamically create functions to interact with systemd services on a remote host.
|
|
||||||
|
|
||||||
Each function will be named `mk/remote-ACTION-service', where
|
|
||||||
ACTION is one of the symbols in ACTIONS. The functions will take
|
|
||||||
an ALIAS and SERVICE as arguments and call
|
|
||||||
`mk/remote--systemctl-service' accordingly."
|
|
||||||
`(progn
|
|
||||||
,@(mapcar
|
|
||||||
(lambda (action)
|
|
||||||
`(defun ,(intern (format "mk/remote-%s-service" action)) (alias service)
|
|
||||||
(interactive "sEnter the host alias or name: \nsEnter the service name: ")
|
|
||||||
(mk/remote--systemctl-service alias service ,(symbol-name action))))
|
|
||||||
actions)))
|
|
||||||
|
|
||||||
(mk/remote-define-systemctl-functions start stop status)
|
|
||||||
|
|
||||||
(defun mk/remote--log (alias service filename)
|
|
||||||
(let* ((filepath (concat "/var/log/" service (unless (string-empty-p filename) (concat "/" filename))))
|
|
||||||
(host (mk/remote--get-real-host alias))
|
|
||||||
(buffer (generate-new-buffer (format "*%s-%s-%s*" alias service filename)))
|
|
||||||
(process-name (format "log-%s-%s" service filename)))
|
|
||||||
(make-process :name process-name
|
|
||||||
:buffer buffer
|
|
||||||
:command `("ssh" ,host "sudo" "tail -f" ,filepath)
|
|
||||||
:sentinel (lambda (process signal)
|
|
||||||
(when (memq (process-status process) '(exit signal))
|
|
||||||
(message "Process: %s %s" process signal))))))
|
|
||||||
|
|
||||||
(defmacro mk/define-remote-log-function (alias service &optional filename)
|
|
||||||
"Define a function to asynchronously tail a remote log file."
|
|
||||||
(let ((fname (if filename filename "")))
|
|
||||||
`(defun ,(intern (format "mk/remote-log-%s-%s-%s" alias service filename)) ()
|
|
||||||
,(format "Tail the remote log file: %s" filename)
|
|
||||||
(interactive)
|
|
||||||
(mk/remote--log ,alias ,service ,fname))))
|
|
||||||
|
|
||||||
(mk/define-remote-log-function "website" "nginx" "access.csv")
|
|
||||||
(mk/define-remote-log-function "website" "nginx" "error.log")
|
|
||||||
(mk/define-remote-log-function "website" "syslog")
|
|
||||||
(mk/define-remote-log-function "survey" "nginx" "access.csv")
|
|
||||||
(mk/define-remote-log-function "survey" "syslog")
|
|
||||||
(mk/define-remote-log-function "pihole" "pihole" "pihole.log")
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue