Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH] (2nd try) new function passphrase generator
Taking Mikael's feedback into consideration, I've reworked the function
and renamed it zgenpassphrase. I also got the completion system working
so I was able to test the completion function. (zshcompsys is a mess and
examples are limited.)
diff --git a/Completion/Zsh/Function/_zgenpassphrase
b/Completion/Zsh/Function/_zgenpassphrase
new file mode 100644
index 000000000..d1c9793a2
--- /dev/null
+++ b/Completion/Zsh/Function/_zgenpassphrase
@@ -0,0 +1,21 @@
+#compdef zgenpassphrase
+
+# Zsh completion function for the 'zgenpassphrase' command.
+#
+
+local -a context
+local curcontext="$curcontext"
+local -A opt_args
+
+_arguments -C \
+ '(-h --help)'{-h,--help}'[Display help message]' \
+ '(-c --capitalize)'{-c,--capitalize}'[Capitalize the first letter of
each word]' \
+ '(-n --num-words)'{-n+,--num-words=}'[Specify the number of words
(default: 6,min 2)]:number of words:' \
+ '(-s --separator)'{-s+,--separator=}'[Use SEP as the word separator
(default: " ")]:separator string:' \
+ '(-d --digits)'{-d+,--digits}'[Include random digits (optional
COUNT)]:number of digits:' \
+ '(-f --file -p --add-search-path -P --override-path -w
--wordlist-name)'{-f+,--file=}'[Use a specific wordlist file (bypasses
searching)]:wordlist file:_files' \
+ '(-w --wordlist-name -f --file)'{-w+,--wordlist-name=}'[Search for a
different file NAME in default paths]:wordlist name:' \
+ \
+ '(-P --override-path -f --file)*'{-p+,--add-search-path=}'[Prepend a
directory to the search path]:directory:_files -/' \
+ '(-p --add-search-path -f --file)*'{-P+,--override-path=}'[Override
the default search path]:directory:_files -/'
+
diff --git a/Functions/Misc/zgenpassphrase b/Functions/Misc/zgenpassphrase
new file mode 100644
index 000000000..e22cc44b7
--- /dev/null
+++ b/Functions/Misc/zgenpassphrase
@@ -0,0 +1,229 @@
+#!/usr/bin/zsh
+# diceware - Generates a diceware style passphrase
+#
+emulate -L zsh # Ensure Zsh option defaults and FUNCTION_ARGZERO are set
+
+local wordlist_file="eff_large_wordlist.txt"
+local -a wordlist_search_paths=(
+ '.'
+ '${ZDOTDIR:-$HOME}'
+)
+local usage
+read -rd '' usage << EOU
+ Usage: $0 [options]
+
+ Options:
+ -c, --capitalize
+ Capitalize the first letter of each word.
+ -s SEP, --separator SEP
+ Use SEP as the word separator (default: " ").
+ -d COUNT, --digits COUNT
+ Include COUNT random digits (0-9) pasted to random words.
+ -n N, --num-words N
+ Specify the number of words in the passphrase (defaults to 6,
minimum 2).
+ -f FILE, --file FILE
+ Specify the diceware wordlist.
+ If omitted, '${wordlist_file}' is searched for in:
+ ${(pj:\n :)wordlist_search_paths}
+ -w WORDLIST_FILENAME, --wordlist-file WORDLIST_FILENAME
+ The wordlist file name to search for in the paths
+ -p SEARCH_PATH, --add-search-path SEARCH_PATH
+ Additional paths to prepend to the default search path.
+ May be specified multiple times.
+ -P SEARCH_PATH, --override-path SEARCH_PATH
+ Paths to replace default search paths.
+ May be specified multiple times.
+ -h, --help : This message
+
+ Examples:
+# 6 words, searches for wordlist, no options
+ $0
+# 8 words, searches for wordlist
+ $0 -n 8
+# 6 words, custom wordlist path
+ $0 --file /my/list.txt
+# Capitalize, hyphen-separated, 7 words
+ $0 -c --separator '-' -n 7
+# 6 words, 1 number pasted to a random word
+ $0 -d 1
+# 6 words, 3 numbers pasted to random words
+ $0 --digits 3
+# Capitalize, dot-separated, 2 numbers, 5 words
+ $0 -c -s'.' -N 2 -n 5
+EOU
+#
+# Requires: zsh/random module, zsh/zutil, sed, and wc
+
+if ! zmodload zsh/random; then
+ print -u2 "Error: zsh/random module could not be loaded. Is it
installed and available?"
+ return 1
+fi
+if ! zmodload zsh/zutil; then
+ print -u2 "Error: zsh/zutil module could not be loaded. Is it
installed and available?"
+ return 1
+fi
+
+
+# Declare option variables with default values
+integer capitalize=0
+local separator=" "
+integer num_to_add_to_words=0
+integer num_words=6
+
+# zparseopts setup for short and long options
+local -A parsed_opts # Associative array to hold parsed options
+local -a additional_paths override_paths
+
+zparseopts -E -D -M -A parsed_opts \
+ c='-capitalize' -capitalize \
+ s:='-separator' -separator: \
+ d:='-digits' -digits: \
+ n:='-num-words' -num-words: \
+ f:='-file' -file: \
+ w:='-wordlist-file' -wordlist-file: \
+ h='-help' "'?'"='-help' -help \
+ p+:='-add-search-path' -add-search-path+:=additional_paths \
+ P+:='-override-path' -override-path+:=override_paths \
+ || { print -ru2 -- "$usage" ; return 1; }
+
+# Process parsed options
+local opt_name opt_val wordlist_path
+
+for opt_name opt_val in "${(@kv)parsed_opts}"; do
+ # Handle --opt-name= by stripping first = unless it is the entire value
+ if [[ $opt_val =~ '^=.*' && $#opt_val -gt 1 ]]; then
+ opt_val="${opt_val#=}"
+ fi
+ case "$opt_name" in
+ --capitalize)
+ capitalize=1
+ ;;
+ --separator)
+ separator="${opt_val}"
+ ;;
+ --digits)
+ # Check if an argument was provided for -N/--numbers
+ if [[ -z "${opt_val}" ]]; then
+ num_to_add_to_words=1
+ else
+ num_to_add_to_words="${opt_val}"
+ fi
+ ;;
+ --num-words)
+ num_words="${opt_val}"
+ ;;
+ --file)
+ wordlist_path="${opt_val}"
+ ;;
+ --wordlist-file)
+ wordlist_file="${opt_val}"
+ ;;
+ --add-search-path)
+ wordlist_search_paths=( "${additional_paths[@]}"
"${wordlist_search_paths[@]}")
+ ;;
+ --override-path)
+ wordlist_search_paths=( "${override_paths[@]}")
+ ;;
+ --help)
+ print -u2 $usage
+ return 0
+ ;;
+ *)
+ print -u2 $usage
+ return 1
+ ;;
+ esac
+done
+
+if (( num_words < 2 )); then
+ print -u2 "Error: Minimum number of words is 2."
+ print -u2 "$usage"
+ return 1
+fi
+
+# Determine wordlist file if not explicitly specified
+if [[ -z "$wordlist_path" ]]; then
+ for p in "${(@e)wordlist_search_paths}"; do
+ if [[ -f "$p/$wordlist_file" ]]; then
+ wordlist_path="$p/$wordlist_file"
+ break
+ fi
+ done
+
+ if [[ -z "$wordlist_path" ]]; then
+ print -ru2 -- "
+Error: Diceware wordlist '$wordlist_file' not found in any search path.
+
+Please download the official wordlist from the Electronic Frontier
Foundation
+and place it in one of the searched directories (e.g., \$ZDOTDIR or
\$HOME).
+
+Canonical URL: https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt
+
+Alternatively, specify a custom path using the -f or --file option."
+ return 1
+ fi
+fi
+
+if [[ -n "$separator" && "$separator" =~ [[:cntrl:]] ]]; then
+ print -ru2 -- "Warning: Using a non-printable character as a
separator can produce unexpected output."
+fi
+
+integer num_lines=$(wc -l < "$wordlist_path")
+if (( num_lines == 0 )); then
+ print -ru2 -- "Error: Wordlist is empty."
+ print -ru2 -- "$usage"
+ return 1
+fi
+
+local -a line_numbers=()
+integer i
+for i in {1..$num_words}
+do
+ line_numbers+=( $(( zrand_int($num_lines,1,1) )) )
+done
+
+local sed_command=""
+if (( num_words > 0 )); then
+ local -a tmp_sed_parts=(${^${(n)line_numbers}}p)
+ sed_command="${(j:;:)tmp_sed_parts}"
+fi
+
+local -a raw_words
+if [[ -n "$sed_command" ]]; then
+ raw_words=( ${(f)"$(sed -n "$sed_command" "$wordlist_path")"} )
+else
+ raw_words=() # No words to fetch if num_words was 0
+fi
+
+if (( ${#raw_words} == 0 )); then
+ print -ru2 -- "Error: sed returned no words"
+ return 1
+fi
+
+local -a passphrase_words=()
+local word
+for word in "${raw_words[@]}"; do
+ passphrase_words+=( "${${(Az)word}[-1]}" )
+done
+
+# Paste random numbers to words if requested
+if (( num_to_add_to_words > 0 && ${#passphrase_words[@]} > 0 )); then
+ for i in {1..$num_to_add_to_words}
+ do
+ integer random_digit=$(( zrand_int(10) ))
+ # Use zrand_int with (upper, lower, inclusive) for 1-based array
indexing
+ # directly
+ integer target_word_index=$(( zrand_int(${#passphrase_words},1,1) ))
+ integer paste_position=$(( zrand_int(2) ))
+
+ if (( paste_position == 0 )); then
+
passphrase_words[target_word_index]="${random_digit}${passphrase_words[target_word_index]}"
# Prefix
+ else
+
passphrase_words[target_word_index]="${passphrase_words[target_word_index]}${random_digit}"
# Suffix
+ fi
+ done
+fi
+
+local final_passphrase="${(pj:$separator:)passphrase_words}"
+
+print -r -- "$final_passphrase"
Messages sorted by:
Reverse Date,
Date,
Thread,
Author