;;; password-gen.el --- Simple password generator -*- lexical-binding: t; -*- ;; Copyright (C) 2024 Lucien Cartier-Tilet ;; Author: Lucien Cartier-Tilet ;; Maintainer: Lucien Cartier-Tilet ;; URL: https://labs.phundrak.com/phundrak/password-gen.el ;; Version: 0.1.0 ;; Package-Requires: ((emacs "26.1") (transient "0.3.7")) ;; Keywords: convenience ;; This file is not part of GNU Emacs. ;; 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 . ;;; Commentary: ;; ;; Simple password generator, based on transient. ;; ;;; Code: ;;; Requires (require 'transient) (defconst password-gen-version "0.1.0") (defgroup password-gen () "Password generator for Emacs." :group 'applications :prefix "password-gen-" :link '(url-link :tag "Gitea" "https://labs.phundrak.com/phundrak/password-gen.el")) ;;; Internal Functions (eval-when-compile (defmacro password-gen--define-infix (key name description type version default &rest reader) "Define infix and its corresponding variable at once. The variable is named password-gen--NAME and is of type TYPE, has a DESCRIPTION and a specified VERSION. They KEY and READER are for the infix declaration. This macro is largely copied from Tecosaur’s screenshot.el" (let ((var-name (concat "password-gen--" name))) `(progn (defcustom ,(intern var-name) ,default ,description :type ,type :group 'password-gen :version ,version) (transient-define-infix ,(intern (concat "password-gen--set-" name)) () "Set password-gen options." :class 'transient-lisp-variable :variable ',(intern var-name) :key ,key :description ,description :argument ,(concat "--" name) :reader (lambda (&rest _) ,@reader))))) (password-gen--define-infix "-n" "numbers" "Include 0-9 numbers" 'boolean "0.1.0" t (not password-gen--numbers)) (password-gen--define-infix "-C" "uppercase" "Include A-Z characters" 'boolean "0.1.0" t (not password-gen--uppercase)) (password-gen--define-infix "-c" "lowercase" "Include a-z characters" 'boolean "0.1.0" t (not password-gen--lowercase)) (password-gen--define-infix "-s" "symbols" "Include !@#$%^&* symbols" 'boolean "0.1.0" t (not password-gen--symbols)) (password-gen--define-infix "-l" "length" "Length of the password" 'boolean "0.1.0" 16 (read-number "Length of the password: "))) (defun password-gen--generate-password () "Generate a password based on user preferences." (shell-command-to-string (format "tr -dc '%s%s%s%s' < /dev/urandom | head -c %d" (if password-gen--uppercase "A-Z" "") (if password-gen--lowercase "a-z" "") (if password-gen--numbers "0-9" "") (if password-gen--symbols "!@#$%^&*") password-gen--length))) (defun password-gen--copy-generated-password () "Generate new password and add it to kill ring." (kill-new (password-gen--generate-password))) (defun password-gen--insert-generated-password () "Insert at cursor a newly generated password." (insert (password-gen--generate-password))) ;;; Transient Actions (eval-when-compile (defmacro password-gen--def-action (name description transient &rest body) "Create a function called from TRANSIENT. DESCRIPTION is the docstring of the function named password-gen--NAME, and it gets BODY as its body." `(defun ,(intern (concat "password-gen--action-" name)) (&optional _args) ,(concat description " This function is meant to be called by a transient.") (interactive (list (transient-args ,transient))) ,@body)) (password-gen--def-action "quit" "Exit password generator" 'password-gen-main #'nil) (password-gen--def-action "copy" "Generate and copy password in kill-ring." 'password-gen-main (password-gen--copy-generated-password)) (password-gen--def-action "insert" "Generate and insert password at cursor." 'password-gen-main (password-gen--insert-generated-password))) ;;; Transient Prefixes (transient-define-prefix password-gen-main () ["Options" (password-gen--set-length) (password-gen--set-uppercase) (password-gen--set-lowercase) (password-gen--set-numbers) (password-gen--set-symbols)] ["Actions" ("c" "Copy" password-gen--action-copy) ("i" "Insert" password-gen--action-insert) ("q" "Quit" password-gen--action-quit)] (interactive) (transient-setup 'password-gen)) ;;;###autoload (defun password-gen () "Call the main transient." (interactive) (call-interactively #'password-gen-main)) (provide 'password-gen) ;;; password-gen.el ends here