CAPTAIN'S PHLOG: 2020.01.10 GOPHER://TILDECOW.COM _________________________________________________________________ # Commenting your code for fun & profit... [UPDATE 1/14/20] THIS is quite good: gopher://josuah.net/0/ascii/obfuscation.txt Looking back over half a century of personal experience, I feel it's probably time to utter these words: KIDS THESE DAYS (don't properly comment their code). Really it has nothing to do with kids or the present day. I can still remember Mr. Jennings in my 11th grade BASIC programming class harping on the importance of comments until my ears bled. I didn't do it then. I didn't do it for most of my life. I've just started doing it this year. Following these pearls of wisdom is a random bash script I coppied from GitHub and REMOVED FROM ITS CONTEXT. Don't read it. For anyone but the more advanced shell scriptor, figuring out what is does line-by-line is a real investment in time! The script isn't important (sorry "drduh"). The illustration is. "But Tim", I hear you wondering, "exactly why DID you mend your ways?". When I was young, my code was disposable. Even the pieces I wrote as part of my short lived 'day job' as a programmer were written, deployed, and promptly abandoed if they worked more than half the time. (Granted this was BASIC on an AM Jacquard J-100 mini-computer and stored on a 20Mb Pertec that took two adults to lift safely, so no big loss there) This neatly expanded on my habbits from Mr. Jennings class. The assignment was handed in, no need to care anymore. There are three reasons I've become a staunch advocate of inline comments. One: UNLIMITED ACCESS TO OTHERS' EXAMPLE CODE. Thanks to easy access to gopher and the *cough* web, I can look at, build upon (rip off), and test terrabytes of code that others have developed. If I need an easily adaptable example, there it is. But thanks to coders finding no time to add section by section comments most of it is worthless to anyone not simply copy/pasting a theoretically 'working' script. Two: I'M CODING TO IMPROVE MY ART. I'm still an '80s programmer. I code for fun. I would starve if I had to make money at this shit. I really like it when I can pick up a piece of code, read it, and GET it with one or perhaps two read-throughs. My code is ART to me. Not High Art. It's not elegant or clever. It's recreation. Programs lacking sufficient commenting deprive me of simple pleasure. They're WORK. Three: I'M GETTING OLDER (Or I'm just a fart in a whirlwind). When I look back at my own uncodumented code - even a few months after I write it - I am left scratching my head. Elaborating on a script that I completely grokked less than a year ago becomes an exercise in mystery. Commenting keeps my forgetfulness in check. Even if I were a good writer and persuasive, I couldn't convince you to start showering your code in concise comments. Hell, you already know it's a good idea really. Some day, years from now perhaps, you'll realize I was correct but by that point I'll be the glimmer in your grandson's eye and definately NOT coming back as a programmer. Peace. # # THE EXAMPLE YOU NEEDN'T READ FOLLOWS... # #!/usr/bin/env bash # https://github.com/drduh/pwd.sh set -o errtrace set -o nounset set -o pipefail #set -x # uncomment to debug umask 077 now=$(date +%s) copy="$(command -v xclip || command -v pbcopy)" gpg="$(command -v gpg || command -v gpg2)" backuptar="${PWDSH_BACKUP:=pwd.$(hostname).$(date +%F).tar}" safeix="${PWDSH_INDEX:=pwd.index}" safedir="${PWDSH_SAFE:=safe}" timeout=9 fail () { # Print an error message and exit. tput setaf 1 1 1 ; printf "\nError: ${1}\n" ; tput sgr0 exit 1 } get_pass () { # Prompt for a password. password="" prompt="${1}" while IFS= read -p "${prompt}" -r -s -n 1 char ; do if [[ ${char} == $'\0' ]] ; then break elif [[ ${char} == $'\177' ]] ; then if [[ -z "${password}" ]] ; then prompt="" else prompt=$'\b \b' password="${password%?}" fi else prompt="*" password+="${char}" fi done } decrypt () { # Decrypt with GPG. printf '%s\n' "${1}" | ${gpg} --armor --batch --no-symkey-cache \ --decrypt --passphrase-fd 0 "${2}" 2>/dev/null } encrypt () { # Encrypt with GPG. ${gpg} --armor --batch \ --symmetric --yes --passphrase-fd 3 --no-symkey-cache \ --output "${2}" "${3}" 3< <(printf '%s\n' "${1}") 2>/dev/null } read_pass () { # Read a password from safe. if [[ ! -s ${safeix} ]] ; then fail "${safeix} not found" ; fi username="" while [[ -z "${username}" ]] ; do if [[ -z "${2+x}" ]] ; then read -r -p " Username: " username else username="${2}" ; fi done while [[ -z "${password}" ]] ; do get_pass " Password to unlock ${safeix}: " ; done printf "\n" spath=$(decrypt ${password} ${safeix} | \ grep -F "${username}" | tail -n1 | cut -d : -f2) || \ fail "Failed to decrypt ${safeix}" clip <(decrypt ${password} ${spath}) || \ fail "Failed to decrypt ${spath}" } gen_pass () { # Generate a password using GPG. len=20 max=80 if [[ -z "${3+x}" ]] ; then read -p " Password length (default: ${len}, max: ${max}): " length else length="${3}" ; fi if [[ ${length} =~ ^[0-9]+$ ]] ; then len=${length} ; fi # base64: 4 characters for every 3 bytes ${gpg} --armor --gen-random 0 "$((${max} * 3/4))" | cut -c -"${len}" } write_pass () { # Write a password and update index file. while [[ -z "${password}" ]] ; do get_pass " Password to unlock ${safeix}: " ; done printf "\n" fpath=$(tr -dc "[:lower:]" < /dev/urandom | fold -w8 | head -n1) spath=${safedir}/${fpath} printf '%s\n' "${userpass}" | \ encrypt "${password}" ${spath} - || \ fail "Failed to put ${spath}" ( if [[ -f "${safeix}" ]] ; then decrypt "${password}" ${safeix} || return ; fi printf "${username}@${now}:${spath}\n") | \ encrypt "${password}" ${safeix}.${now} - || \ fail "Failed to put ${safeix}.${now}" mv ${safeix}{.${now},} } list_entry () { # Decrypt the index to list entries. if [[ ! -s ${safeix} ]] ; then fail "${safeix} not found" ; fi while [[ -z "${password}" ]] ; do get_pass " Password to unlock ${safeix}: " ; done printf "\n\n" decrypt ${password} ${safeix} || fail "Decryption failed" } backup () { # Archive encrypted index and safe directory. if [[ -f ${safeix} && -d ${safedir} ]] ; then \ tar cfv ${backuptar} ${safeix} ${safedir} else fail "Nothing to archive" ; fi printf "\nArchived ${backuptar}\n" ; \ } clip () { # Use clipboard and clear after timeout. ${copy} < ${1} printf "\n" shift while [ $timeout -gt 0 ] ; do printf "\r\033[KPassword on clipboard! Clearing in %.d" $((timeout--)) sleep 1 done printf "" | ${copy} } new_entry () { # Prompt for new username and/or password. username="" while [[ -z "${username}" ]] ; do if [[ -z "${2+x}" ]] ; then read -r -p " Username: " username else username="${2}" ; fi done if [[ -z "${3+x}" ]] ; then get_pass " Password for \"${username}\" (Enter to generate): " userpass="${password}" fi printf "\n" if [[ -z "${password}" ]] ; then userpass=$(gen_pass "$@") ; fi } print_help () { # Print help text. printf """ pwd.sh is a Bash shell script to manage passwords with GnuPG symmetric encryption. pwd.sh can be used interactively or by passing one of the following options: * 'w' to write a password * 'r' to read a password * 'l' to list passwords * 'b' to create an archive for backup Example usage: * Generate a 30 character password for 'userName': ./pwd.sh w userName 30 * Copy the password for 'userName' to clipboard: ./pwd.sh r userName * List stored passwords and copy a previous version: ./pwd.sh l ./pwd.sh r userName@1574723625 * Create an archive for backup: ./pwd.sh b * Restore an archive from backup: tar xvf pwd*tar""" } if [[ -z ${gpg} && ! -x ${gpg} ]] ; then fail "GnuPG is not available" ; fi if [[ ! -d ${safedir} ]] ; then mkdir -p ${safedir} ; fi chmod -R 0600 ${safeix} 2>/dev/null chmod -R 0700 ${safedir} 2>/dev/null password="" action="" if [[ -n "${1+x}" ]] ; then action="${1}" ; fi while [[ -z "${action}" ]] ; do read -n 1 -p " Read or Write (or Help for more options): " action printf "\n" done if [[ "${action}" =~ ^([hH])$ ]] ; then print_help elif [[ "${action}" =~ ^([bB])$ ]] ; then backup elif [[ "${action}" =~ ^([lL])$ ]] ; then list_entry elif [[ "${action}" =~ ^([wW])$ ]] ; then new_entry "$@" write_pass else read_pass "$@" ; fi chmod -R 0400 ${safeix} ${safedir} 2>/dev/null tput setaf 2 2 2 ; printf "\nDone\n" ; tput sgr0