├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── README.md ├── doc ├── install.md └── troubleshoot-xelatex.md ├── pandoc-from-markdown-to-pdf ├── posix-shell-script-kit ├── pygments.theme └── tex ├── blockquote.tex ├── bullet.tex ├── fontspec.tex ├── hyphen.tex ├── inline_code.tex ├── listings.tex ├── raggedright.tex ├── sectionstyle.tex └── setspace.tex /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: pandoc-from-markdown-to-pdf 3 | message: >- 4 | If you use this work and you want to cite it, 5 | then you can use the metadata from this file. 6 | type: software 7 | authors: 8 | - given-names: Joel Parker 9 | family-names: Henderson 10 | email: joel@joelparkerhenderson.com 11 | affiliation: joelparkerhenderson.com 12 | orcid: 'https://orcid.org/0009-0000-4681-282X' 13 | identifiers: 14 | - type: url 15 | value: 'https://github.com/SixArm/pandoc-from-markdown-to-pdf/' 16 | description: pandoc-from-markdown-to-pdf 17 | repository-code: 'https://github.com/SixArm/pandoc-from-markdown-to-pdf/' 18 | abstract: >- 19 | pandoc-from-markdown-to-pdf 20 | license: See license file 21 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pandoc-from-markdown-to-pdf 2 | 3 | Use the pandoc command to convert from a markdown file to a PDF file. 4 | 5 | This tool uses our favorite settings for layouts, styles, fonts, etc. 6 | 7 | You can edit this tool as you wish. 8 | 9 | Syntax: 10 | 11 | ```sh 12 | pandoc-from-markdown-to-pdf 13 | ``` 14 | 15 | Syntax for typical use: 16 | 17 | ```sh 18 | pandoc-from-markdown-to-pdf -o 19 | ``` 20 | 21 | Example for typical use: 22 | 23 | ```sh 24 | $ pandoc-from-markdown-to-pdf example.md -o example.pdf 25 | ``` 26 | 27 | For documentation, see the `doc` directory. 28 | 29 | 30 | ## Pandoc fonts 31 | 32 | Our preferred Pandoc fonts are here: 33 | 34 | https://github.com/sixarm/pandoc-fonts 35 | 36 | You can download the fonts that you want, then install them on your system. 37 | 38 | 39 | ## Thanks 40 | 41 | Thanks to https://learnbyexample.github.io/customizing-pandoc/ 42 | 43 | Thanks to https://github.com/sixarm/posix-shell-script-kit/ 44 | 45 | 46 | ## Tracking 47 | 48 | * Package: pandoc-from-markdown-to-pdf 49 | * Version: 2.2.1 50 | * Created: 2022-03-13T22:05:34Z 51 | * Updated: 2024-01-05T19:47:35Z 52 | * License: GPL-2.0 or GPL-3.0 or contact us for more 53 | * Website: https://github.com/sixarm/pandoc-from-markdown-to-pdf 54 | * Contact: Joel Parker Henderson (joel@sixarm.com) 55 | -------------------------------------------------------------------------------- /doc/install.md: -------------------------------------------------------------------------------- 1 | # Install 2 | 3 | Clone this repo to anywhere you want, and add it to your path, such as: 4 | 5 | ```sh 6 | cd $HOME 7 | git clone https://github.com/SixArm/pandoc-from-markdown-to-pdf.git 8 | export PATH="$PATH:$HOME/pandoc-from-markdown-to-pdf" 9 | ``` 10 | 11 | 12 | ## Prerequisites 13 | 14 | This script requires these prerequisites to be installed: 15 | 16 | * pandoc document converter 17 | 18 | * xelatex implementation of LaTex converters 19 | 20 | * pygments syntax highlighting 21 | 22 | * Bitstream Vera fonts and/or Adobe Source Pro fonts 23 | 24 | If you want a different choices for any of the above, 25 | then please contact us or open an issue, to let us know. 26 | 27 | 28 | ## macOS install with brew 29 | 30 | Example using brew: 31 | 32 | ```sh 33 | brew install pandoc 34 | ``` 35 | 36 | If you prefer a heavyweight install: 37 | 38 | ```sh 39 | brew install mactex 40 | ``` 41 | 42 | If you prefer a lightweight install: 43 | 44 | ```sh 45 | brew install basictex 46 | eval "$(/usr/libexec/path_helper)" 47 | # Update $PATH to include `/usr/local/texlive/2022basic/bin/universal-darwin` 48 | sudo tlmgr update --self 49 | sudo tlmgr install texliveonfly 50 | sudo tlmgr install adjustbox 51 | sudo tlmgr install tcolorbox 52 | sudo tlmgr install collectbox 53 | sudo tlmgr install ucs 54 | sudo tlmgr install environ 55 | sudo tlmgr install trimspaces 56 | sudo tlmgr install titling 57 | sudo tlmgr install enumitem 58 | sudo tlmgr install rsfs 59 | ``` 60 | 61 | Verify the programs are runnable: 62 | 63 | ```sh 64 | $ which pandoc 65 | /usr/local/bin/pandoc 66 | 67 | $ pandoc --version 68 | pandoc 2.17.1.1 69 | 70 | $ which xelatex 71 | /Library/TeX/texbin/xelatex 72 | 73 | $ xelatex --version 74 | XeTeX 3.141592653-2.6-0.999993 (TeX Live 2021) 75 | ``` 76 | 77 | See also: 78 | 79 | * [Troubleshoot XeLatex](troubleshoot-xelatex.md) 80 | -------------------------------------------------------------------------------- /doc/troubleshoot-xelatex.md: -------------------------------------------------------------------------------- 1 | 2 | # Troubleshoot XeLatex 3 | 4 | If the `xelatex` command is not found, then look for it: 5 | 6 | ```sh 7 | find / -name xelatex 8 | ``` 9 | 10 | Example result on macOS after brew install: 11 | 12 | ```sh 13 | /Library/TeX/texbin/xelatex 14 | ``` 15 | 16 | Example to append the directory to your path: 17 | 18 | ```sh 19 | export PATH="$PATH:/Library/TeX/texbin" 20 | ``` 21 | 22 | If that works, then adjust your path as you wish, such as in your `.env` file, or `~/.zshenv` file, etc. 23 | -------------------------------------------------------------------------------- /pandoc-from-markdown-to-pdf: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # pandoc-from-markdown-to-pdf 4 | # 5 | # Use the pandoc command to convert from a markdown file to a PDF file. 6 | # 7 | # This tool uses our favorite settings for layouts, styles, fonts, etc. 8 | # 9 | # You can edit this tool as you wish. 10 | # 11 | # Syntax: 12 | # 13 | # ```sh 14 | # pandoc-from-markdown-to-pdf 15 | # ``` 16 | # 17 | # Syntax for typical use: 18 | # 19 | # ```sh 20 | # pandoc-from-markdown-to-pdf -o 21 | # ``` 22 | # 23 | # Example for typical use: 24 | # 25 | # ```sh 26 | # $ pandoc-from-markdown-to-pdf example.md -o example.pdf 27 | # ``` 28 | # 29 | # For documentation, see the `doc` directory. 30 | # 31 | # 32 | # ## Thanks 33 | # 34 | # Thanks to https://learnbyexample.github.io/customizing-pandoc/ 35 | # 36 | # Thanks to https://github.com/sixarm/posix-shell-script-kit/ 37 | # 38 | # 39 | # ## Tracking 40 | # 41 | # * Command: pandoc-from-markdown-to-pdf 42 | # * Version: 2.2.1 43 | # * Created: 2022-03-13T22:05:34Z 44 | # * Updated: 2024-01-05T19:47:35Z 45 | # * License: GPL-2.0 or GPL-3.0 or contact us for more 46 | # * Website: https://github.com/sixarm/pandoc-from-markdown-to-pdf 47 | # * Contact: Joel Parker Henderson (joel@sixarm.com) 48 | 49 | set -euf 50 | 51 | # Include https://github.com/SixArm/posix-shell-script-kit 52 | . "$(dirname "$(readlink -f "$0")")/posix-shell-script-kit" 53 | 54 | # Preflight 55 | 56 | command_exists_or_die "pandoc" 57 | command_exists_or_die "xelatex" 58 | 59 | # Choose fonts 60 | 61 | mainfont="Source Serif Pro" 62 | sansfont="Source Sans Pro" 63 | monofont="Source Code Pro" 64 | mathfont="Source Code Pro" 65 | 66 | font_name_exists_or_die "$mainfont" 67 | font_name_exists_or_die "$sansfont" 68 | font_name_exists_or_die "$monofont" 69 | font_name_exists_or_die "$mathfont" 70 | 71 | # Main 72 | 73 | DIR0=$(dirname "$0") 74 | 75 | pandoc \ 76 | -V linkcolor:blue \ 77 | -V geometry:b5paper \ 78 | -V geometry:margin=2cm \ 79 | -V mainfont="$mainfont" \ 80 | -V sansfont="$sansfont" \ 81 | -V monofont="$monofont" \ 82 | -V mathfont="$mathfont" \ 83 | -V fontsize=12pt \ 84 | --pdf-engine=xelatex \ 85 | --listings \ 86 | --toc \ 87 | --toc-depth=2 \ 88 | --include-in-header="$DIR0/tex/blockquote.tex" \ 89 | --include-in-header="$DIR0/tex/bullet.tex" \ 90 | --include-in-header="$DIR0/tex/fontspec.tex" \ 91 | --include-in-header="$DIR0/tex/hyphen.tex" \ 92 | --include-in-header="$DIR0/tex/inline_code.tex" \ 93 | --include-in-header="$DIR0/tex/listings.tex" \ 94 | --include-in-header="$DIR0/tex/sectionstyle.tex" \ 95 | --include-in-header="$DIR0/tex/setspace.tex" \ 96 | --include-in-header="$DIR0/tex/raggedright.tex" \ 97 | "$@" 98 | 99 | # Other ideas to consider: 100 | # 101 | # -f gfm \ 102 | # --highlight-style="$DIR0/pygments.theme" \ 103 | # --template eisvogel_mod 104 | # --columns=50 105 | # --number-sections 106 | # --pdf-engine lualatex 107 | # --dpi=300 -M 108 | # date="$DATE" 109 | # HEADER.YAML 110 | # $SOURCE.md 111 | -------------------------------------------------------------------------------- /posix-shell-script-kit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # POSIX shell script kit. 4 | # 5 | # The POSIX shell script kit is one file of helper functions and constants. 6 | # 7 | # These work for bash, zsh, ksh, dash, sh, and any other modern POSIX shell. 8 | # 9 | # You can include this file in your own scripts, or copy anything you want. 10 | # 11 | # This file is free libre open source software, created by SixArm. 12 | # 13 | # Constructive feedback is welcome and appreciated. 14 | # 15 | # To download this file: 16 | # 17 | # ```sh 18 | # curl -O "https://raw.githubusercontent.com/SixArm/posix-shell-script-kit/main/posix-shell-script-kit" 19 | # ``` 20 | # 21 | # To include this file in your own script, in the same directory: 22 | # 23 | # ```sh 24 | # # Include https://github.com/SixArm/posix-shell-script-kit 25 | # . "$(dirname "$(readlink -f "$0")")/posix-shell-script-kit" 26 | # ``` 27 | # 28 | # ## Tracking 29 | # 30 | # * Package: posix-shell-script-kit 31 | # * Version: 11.0.5 32 | # * Created: 2017-08-22T00:00:00Z 33 | # * Updated: 2023-03-24T13:23:36Z 34 | # * License: GPL-2.0 or GPL-3.0 or contact us for more 35 | # * Website: https://github.com/sixarm/posix-shell-script-kit 36 | # * Contact: Joel Parker Henderson (joel@sixarm.com) 37 | 38 | ## 39 | # Exit codes 40 | ## 41 | 42 | # Our POSIX shell programs call "exit" with an exit code value. 43 | # 44 | # Conventions: 45 | # 46 | # * 0 = success, and non-zero indicates any other issue. 47 | # 48 | # * 1 = failure 49 | # 50 | # * 2 = failure due to a usage problem 51 | # 52 | # * 3-63 are for a program-specific exit codes. 53 | # 54 | # * 64-78 are based on sysexits documentation from the 1980's. 55 | # 56 | # * 80-119 are SixArm conventions that we find useful in many programs. 57 | # 58 | # Many POSIX shells use exit codes 126 and 127 to signal specific error status: 59 | # 60 | # * 126 is for the shell and indicates command found but not executable. 61 | # 62 | # * 127 is for the shell and indicate command not found. 63 | # 64 | # Many POSIX shells use exit codes above 128 in their $? representation of the 65 | # exit status to encode the signal number of a process being killed. 66 | # 67 | # The pre-defined exit codes from sysexits can be used, so the caller of the 68 | # process can get a rough estimation about the failure class without looking up 69 | # the source code. 70 | # 71 | # See https://man.openbsd.org/sysexits.3 72 | # 73 | # We recommend: 74 | # 75 | # * Authentication issues: exit $EXIT_NOUSER 76 | # 77 | # * Authorization issues: exit $EXIT_NOPERM 78 | # 79 | # * A user chooses cancel: exit $EXIT_QUIT 80 | # 81 | # The exit code list below is subject to change over time, as we learn more. 82 | 83 | # Success 84 | # 85 | # The program succeeded. 86 | # 87 | # E.g. everything worked as expected; any pipe processing will continue. 88 | # 89 | # Exit 0 meaning success is a widespread convention as a catch-all code. 90 | # 91 | EXIT_SUCCESS=0 92 | 93 | # Failure 94 | # 95 | # The program failed. 96 | # 97 | # E.g. an error, an abort, found no results, lack of data, etc. 98 | # 99 | # Exit 1 meaning failure is a widespread convention as a catch-all code. 100 | EXIT_FAILURE=1 101 | 102 | # Usage 103 | # 104 | # The program usage is incorrect, or malformed, or in conflict, etc. 105 | # 106 | # E.g. wrong number of args, a bad flag, a syntax error in an option, etc. 107 | # 108 | # Exit 2 meaning usage is a widespread convention as a catch-all CLI code. 109 | # 110 | EXIT_USAGE=2 111 | 112 | # Data Err 113 | # 114 | # The input data was incorrect in some way. 115 | # 116 | # This should only be used for user's data and not system files. 117 | # 118 | EXIT_DATAERR=65 119 | 120 | # No Input 121 | # 122 | # An input file-- not a system file-- did not exist or was not readable. 123 | # 124 | # This could include errors like "No message" to a mailer, if it cared about it. 125 | # 126 | EXIT_NOINPUT=66 127 | 128 | # No User 129 | # 130 | # The user specified did not exist. 131 | # 132 | # E.g. for email addresses, or remote logins, or authentication issues, etc. 133 | # 134 | EXIT_NOUSER=67 135 | 136 | # No Host 137 | # 138 | # The host specified did not exist. 139 | # 140 | # E.g. for email addresses, or network requests, or webs links, etc. 141 | # 142 | EXIT_NOHOST=68 143 | 144 | # Unavailable 145 | # 146 | # A service is unavailable. 147 | # 148 | # E.g. a support program or file does not exist. This can also be a catchall 149 | # message when something does not work, but you do not know why. 150 | # 151 | EXIT_UNAVAILABLE=69 152 | 153 | # Software 154 | # 155 | # An internal software error has been detected. 156 | # 157 | # This should be limited to non-operating system related errors as possible. 158 | # 159 | EXIT_SOFTWARE=70 160 | 161 | # OS Err 162 | # 163 | # An operating system error has been detected. 164 | # 165 | # E.g. errors such as "cannot fork", "cannot create pipe", or getuid returns a 166 | # user that does not exist in the passwd file, etc. 167 | # 168 | EXIT_OSERR=71 169 | 170 | # OS File 171 | # 172 | # An operating system file (e.g. /etc/passwd) does not exist, or cannot 173 | # be opened, or has some sort of error (e.g. syntax error). 174 | # 175 | EXIT_OSFILE=72 176 | 177 | # Can't Create 178 | # 179 | # A user-specified output (e.g. a file) cannot be created. 180 | # 181 | EXIT_CANTCREATE=73 182 | 183 | # IO Err 184 | # 185 | # An error occurred while doing input/output on some file, or stream, etc. 186 | # 187 | EXIT_IOERR=74 188 | 189 | # Temp Fail 190 | # 191 | # A temporary failure occurred; this is not a permanent error. 192 | # 193 | # E.g. a mailer could not create a connection. The request can be retried later. 194 | # 195 | EXIT_TEMPFAIL=75 196 | 197 | # Protocol 198 | # 199 | # The remote system returned something that was "not possible" during 200 | # a protocol exchange. 201 | # 202 | EXIT_PROTOCOL=76 203 | 204 | # No Perm 205 | # 206 | # You did not have sufficient permission to perform the operation. 207 | # 208 | # This is not for file system problems, which use EXIT_NOINPUT or 209 | # EXIT_CANTCREATE, but for higher level permissions, authorizations, etc. 210 | # 211 | EXIT_NOPERM=77 212 | 213 | # Config 214 | # 215 | # Something was found in an unconfigured or misconfigured state. 216 | # 217 | EXIT_CONFIG=78 218 | 219 | # Exit codes 80-99 are for our own SixArm conventions. 220 | # We propose these are generally useful to many kinds of programs. 221 | # 222 | # Caution: these exit codes and their values are work in progress, 223 | # draft only, as a request for comments, in version 11.x of this file. 224 | # These exit codes will be set in version 12.x when it's released. 225 | # 226 | # * 80+ for user interation issues 227 | # 228 | # * 90+ for access control issues 229 | # 230 | # * 100+: process runtime issues 231 | # 232 | # * 110+: expected ability issues 233 | 234 | # Exit codes 80+ for user interation issues... 235 | 236 | # Quit 237 | # 238 | # The user chose to quit, or cancel, or abort, or discontinue, etc. 239 | # 240 | EXIT_QUIT=80 241 | 242 | # KYC (Know Your Customer) 243 | # 244 | # The program requires more user interaction, or user information, etc. 245 | # 246 | # E.g. email validation, age verification, terms of service agreement, etc. 247 | # 248 | EXIT_KYC=81 249 | 250 | # Update 251 | # 252 | # The program or its dependencies need an update, or upgrade, etc. 253 | # 254 | EXIT_UPDATE=89 255 | 256 | # Exit codes 90+ for access control issues... 257 | 258 | # Conflict 259 | # 260 | # An item has a conflict e.g. edit collision, or merge error, etc. 261 | # 262 | # Akin to HTTP status code 409 Conflict. 263 | # 264 | EXIT_CONFLICT=90 265 | 266 | # Unlawful 267 | # 268 | # Something is prohibited due to law, or warrant, or court order, etc. 269 | # 270 | # Akin to HTTP status code 451 Unavailable For Legal Reasons (RFC 7725). 271 | EXIT_UNLAWFUL=91 272 | 273 | # Payment Issue 274 | # 275 | # Something needs a credit card, or invoice, or billing, etc. 276 | # 277 | # Akin to HTTP status code 402 Payment Required. 278 | # 279 | EXIT_PAYMENT_ISSUE=92 280 | 281 | # Exit codes 100+ for process runtime issues... 282 | 283 | # Busy 284 | # 285 | # A process is too busy, or overloaded, or throttled, or breakered, etc. 286 | # 287 | # Akin to HTTP status code 503 Service Unavailable; always means overloaded. 288 | # 289 | EXIT_BUSY=100 290 | 291 | # Timeout 292 | # 293 | # A process is too slow, or estimated to take too long, etc. 294 | # 295 | # Akin to HTTP status code 408 Request Timeout. 296 | # 297 | EXIT_TIMEOUT=101 298 | 299 | # Lockout 300 | # 301 | # A process is intentionally blocked as a danger, hazard, risk, etc. 302 | # 303 | # This is for lockout-tagout (LOTO) safety, or protecting users or data, etc. 304 | # 305 | EXIT_LOCKOUT=102 306 | 307 | # Loop 308 | # 309 | # A process has detected an infinite loop, so is aborting. 310 | # 311 | # Akin to HTTP status code 508 Loop Detected. 312 | # 313 | EXIT_LOOP=103 314 | 315 | # Exit codes 110+ for expected ability issues... 316 | 317 | # Moved Permanently 318 | # 319 | # An expected ability has been moved permanently. 320 | # 321 | # Akin to HTTP status code 301 Moved Permanently. 322 | # 323 | EXIT_MOVED_PERMANENTLY=110 324 | 325 | # Moved Temporarily 326 | # 327 | # An expected ability has been moved temporarily. 328 | # 329 | # Akin to HTTP status code 302 Moved Temporarily. 330 | # 331 | EXIT_MOVED_TEMPORARILY=111 332 | 333 | # Gone 334 | # 335 | # An expected ability has been intentionally removed, or deleted, etc. 336 | # 337 | # Akin to HTTP status code 410 Gone; the ability should be purged. 338 | # 339 | EXIT_GONE=112 340 | 341 | # Future 342 | # 343 | # An expected ability is not yet implemented, or work in progress, etc. 344 | # 345 | # Akin to HTTP status code 501 Not Implemented; implies future availability. 346 | # 347 | EXIT_FUTURE=119 348 | 349 | # Exit code 125 for git... 350 | 351 | # Git bisect skip 352 | # 353 | # The special exit code 125 should be used when the current source code cannot 354 | # be tested. If the script exits with this code, the current revision will be 355 | # skipped (see git bisect skip above). 356 | # 357 | # Value 125 was chosen as the highest sensible value to use for this 358 | # purpose, because 126 and 127 are used by shells to signal specific errors. 359 | # 360 | EXIT_GIT_BISECT_SKIP=125 361 | 362 | # Exit codes 126-127 for shell conventions... 363 | 364 | # Command found but not executable 365 | # 366 | # A command is found but is not executable. 367 | # 368 | EXIT_COMMAND_FOUND_BUT_NOT_EXECUTABLE=126 369 | 370 | # Command not found 371 | # 372 | # A command is not found. 373 | # 374 | EXIT_COMMAND_NOT_FOUND=127 375 | 376 | ## 377 | # Input/output helpers 378 | ## 379 | 380 | # out: print output message to stdout. 381 | # 382 | # Example: 383 | # ``` 384 | # out "my message" 385 | # => my message 386 | # ``` 387 | # 388 | # We use `printf` instead of `echo` because `printf` is more consistent 389 | # on more systems, such a for escape sequence handling. 390 | # 391 | # Compare: 392 | # 393 | # * Use the `out` function to print to STDOUT. 394 | # 395 | # * Use the `err` function to print to STDERR. 396 | # 397 | out() { 398 | printf %s\\n "$*" 399 | } 400 | 401 | # err: print error message to stderr. 402 | # 403 | # Example: 404 | # ``` 405 | # err "my message" 406 | # STDERR=> my message 407 | # ```` 408 | # 409 | # We use `printf` instead of `echo` because `printf` is more consistent 410 | # on more systems, such a for escape sequence handling. 411 | # 412 | # Compare: 413 | # 414 | # * Use the `out` function to print to STDOUT. 415 | # 416 | # * Use the `err` function to print to STDERR. 417 | # 418 | err() { 419 | >&2 printf %s\\n "$*" 420 | } 421 | 422 | # die: print error message to stderr, then exit with error code. 423 | # 424 | # Example: 425 | # ``` 426 | # die 1 "my message" 427 | # STDERR=> my message 428 | # => exit 1 429 | # ``` 430 | die() { 431 | n="$1" ; shift ; >&2 printf %s\\n "$*" ; exit "$n" 432 | } 433 | 434 | # big: print a big banner to stdout, good for human readability. 435 | # 436 | # Example: 437 | # ``` 438 | # big "my message" 439 | # => 440 | # ### 441 | # # 442 | # # my message 443 | # # 444 | # ### 445 | # ``` 446 | big() { 447 | printf \\n###\\n#\\n#\ %s\\n#\\n###\\n\\n "$*" 448 | } 449 | 450 | # log: print a datestamp, unique random id, hostname, process id, and message. 451 | # 452 | # Example: 453 | # ``` 454 | # log "my message" 455 | # => 2021-05-04T22:57:54.000000000+00:00 7e7151dc24bd511098ebb248771d8ffb abc.example.com 1234 my message 456 | # ``` 457 | # 458 | # We prefer this log file format for many of our scripts because we prefer 459 | # logging the additional diagnositc information that we use for our systems: 460 | # the datetime with nanosecond-friendly format and timezone-friendly format, 461 | # unique random id a.k.a. zid, hostname, and process number. 462 | # 463 | log() { 464 | printf '%s %s %s %s\n' "$( now )" "$( zid )" "$( hostname )" $$ "$*" 465 | } 466 | 467 | # zid: generate a 32-bit secure random lowercase hex identifier. 468 | # 469 | # Example: 470 | # ``` 471 | # zid 472 | # => 78577554e967951388b5907854b4c337 473 | # ``` 474 | zid() { 475 | hexdump -n 16 -v -e '16/1 "%02x" "\n"' /dev/random 476 | } 477 | 478 | # ask: prompt the user for a line of input, then return a trimmed string. 479 | # 480 | # Example: 481 | # ``` 482 | # ask 483 | # => prompt 484 | # ``` 485 | ask() { 486 | read x ; echo "$x" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' 487 | } 488 | 489 | ## 490 | # Date & time helpers 491 | ## 492 | 493 | # now: get a datetime using our preferred ISO format. 494 | # 495 | # Example with the current datetime: 496 | # ``` 497 | # now 498 | # => 2021-05-04T22:59:28.769653000+00:00 499 | # ``` 500 | # 501 | # Example with a custom datetime, if your date command offers option -d: 502 | # ``` 503 | # now -d "January 1, 2021" 504 | # => 2021-01-01T00:00:00.000000000+00:00 505 | # ``` 506 | # 507 | # We prefer this date-time format for many of our scripts: 508 | # 509 | # * We prefer ISO standard because it's well documented and supported. 510 | # Specifically, we use ISO "YYYY-MM-DDTHH:MM:SS.NNNNNNNNN+00:00". 511 | # 512 | # * We prefer nanosecond width because it aligns with high-speed systems. 513 | # Specifically, we use GNU `date` and tools that print nanoseconds. 514 | # 515 | # * We prefer timezone width because it aligns with localized systems. 516 | # Specifically, we use some systems and tools that require timezones. 517 | # 518 | # Note: the custom datetime capabilty relies on the system "date" command, 519 | # because this script sends the args along to the system "date" command. 520 | # For example Linux GNU "date" handles this, but macOS BSD "date" doesn't. 521 | # 522 | now() { 523 | # shellcheck disable=SC2120 524 | date -u "+%Y-%m-%dT%H:%M:%S.%N+00:00" "$@" | sed 's/N/000000000/' 525 | } 526 | 527 | # now_date: get a date using our preferred ISO format 528 | # 529 | # Example: 530 | # 531 | # ```sh 532 | # now_date 533 | # => 2021-05-04 534 | # ``` 535 | # 536 | # Example with a custom date, if your date command offers option -d: 537 | # ``` 538 | # now_date -d "January 1, 2021" 539 | # => 2021-01-01 540 | # ``` 541 | # 542 | now_date() { 543 | # shellcheck disable=SC2120 544 | date -u "+%Y-%m-%d" "$@" 545 | } 546 | 547 | # sec: get the current time in POSIX seconds. 548 | # 549 | # Example: 550 | # ``` 551 | # sec 552 | # => 1620169178 553 | # ``` 554 | sec() { 555 | date "+%s" "$@" 556 | } 557 | 558 | # age: get the age of a given time in POSIX seconds. 559 | # 560 | # Example: 561 | # ``` 562 | # age 1620169178 563 | # => 19 564 | # ``` 565 | age() { 566 | printf %s\\n "$(( $(date "+%s") - $1 ))" 567 | } 568 | 569 | # newer: is the age of a given time newer than a given number of seconds? 570 | # 571 | # Example: 572 | # ``` 573 | # newer 2000000000 && echo "true" || echo "false 574 | # => true 575 | # ``` 576 | newer() { 577 | [ "$(( $(date "+%s") - $1 ))" -lt "$2" ] 578 | } 579 | 580 | # older: is the age of a given time older than a given number of seconds? 581 | # 582 | # Example: 583 | # ``` 584 | # older 1000000000 && echo "true" || echo "false" 585 | # => true 586 | # ``` 587 | older() { 588 | [ "$(( $(date "+%s") - $1 ))" -gt "$2" ] 589 | } 590 | 591 | ## 592 | # Validation helpers 593 | ## 594 | 595 | # command_exists: does a command exist? 596 | # 597 | # Example: 598 | # ``` 599 | # command_exists grep 600 | # => true 601 | # 602 | # command_exists curl 603 | # => false 604 | # ``` 605 | command_exists() { 606 | command -v "$1" >/dev/null 2>&1 607 | } 608 | 609 | # command_exists_or_die: ensure a command exists. 610 | # 611 | # Example: 612 | # ``` 613 | # command_exists_or_die grep 614 | # => true 615 | # 616 | # command_exists_or_die curl 617 | # STDERR=> Command needed: curl 618 | # => exit 1 619 | # ``` 620 | command_exists_or_die() { 621 | command_exists "$1" || die "$EXIT_UNAVAILABLE" "Command needed: $1" 622 | } 623 | 624 | # command_version_exists_or_die: ensure a command version exists. 625 | # 626 | # Example: 627 | # ``` 628 | # command_version_exists_or_die grep 2.2 1.1 629 | # => true 630 | # 631 | # version_or_die grep 2.2 3.3 632 | # STDERR=> Command version needed: grep >= 3.x 633 | # => exit 1 634 | # ``` 635 | command_version_exists_or_die() { 636 | command_exists "$1" && version "$2" "$3" || die "$EXIT_UNAVAILABLE" "Command version needed: $1 >= $2 (not ${3:-?})" 637 | } 638 | 639 | # var_exists: does a variable exist? 640 | # 641 | # Example: 642 | # ``` 643 | # var_exists HOME 644 | # => true 645 | # 646 | # var_exists FOO 647 | # => false 648 | # ``` 649 | var() { 650 | ! eval 'test -z ${'$1'+x}' 651 | } 652 | 653 | # var_exists_or_die: ensure a variable exists. 654 | # 655 | # Example: 656 | # ``` 657 | # var_exists_or_die HOME 658 | # => true 659 | # 660 | # var_exists_or_die FOO 661 | # STDERR=> Variable needed: FOO 662 | # => exit 1 663 | # ``` 664 | var_exists_or_die() { 665 | var_exists "$1" || die "$EXIT_CONFIG" "Variable needed: $1" 666 | } 667 | 668 | # version: is a version sufficient? 669 | # 670 | # Example: 671 | # ``` 672 | # version 1.1 2.2 673 | # => true 674 | # 675 | # version 3.3 2.2 676 | # => false 677 | # ``` 678 | version() { 679 | [ "$(cmp_digits "$1" "$2")" -le 0 ] 680 | } 681 | 682 | # version_or_die: ensure a version is sufficient. 683 | # 684 | # Example: 685 | # ``` 686 | # version_or_die 1.1 2.2 687 | # => true 688 | # 689 | # version_or_die 3.3 2.2 690 | # STDERR=> Version needed: >= 3.3 (not 2.2) 691 | # ``` 692 | version_or_die() { 693 | version "$1" "$2" || die "$EXIT_CONFIG" "Version needed: >= $1 (not ${2:-?})" 694 | } 695 | 696 | ## 697 | # Number helpers 698 | ## 699 | 700 | # int: convert a number string to an integer number string. 701 | # 702 | # Example: 703 | # ``` 704 | # int 1.23 705 | # => 1 706 | # ``` 707 | int() { 708 | printf %s\\n "$1" | awk '{ print int($0); exit }' 709 | } 710 | 711 | # sum: print the sum of numbers. 712 | # 713 | # Example: 714 | # ``` 715 | # sum 1 2 3 716 | # => 6 717 | # ``` 718 | sum() { 719 | awk '{for(i=1; i<=NF; i++) sum+=$i; } END {print sum}' 720 | } 721 | 722 | ## 723 | # Comparison helpers 724 | ## 725 | 726 | # cmp_alnums: compare alnums as groups, such as for word version strings. 727 | # 728 | # Example: 729 | # 730 | # ``` 731 | # cmp_alnums "a.b.c" "a.b.c" 732 | # => 0 (zero means left == right) 733 | # 734 | # cmp_alnums "a.b.c" "a.b.d" 735 | # => -1 (negative one means left < right) 736 | # 737 | # cmp_alnums "a.b.d" "a.b.c" 738 | # => 1 (positive one means left > right) 739 | # ``` 740 | # 741 | cmp_alnums() { 742 | if [ "$1" = "$2" ]; then 743 | echo "0"; return 0 744 | fi 745 | a=$(printf %s\\n "$1" | sed 's/^[^[:alnum:]]*//') 746 | b=$(printf %s\\n "$2" | sed 's/^[^[:alnum:]]*//') 747 | while true; do 748 | x=$(printf %s\\n "$a" | sed 's/[^[:alnum:]].*//') 749 | y=$(printf %s\\n "$b" | sed 's/[^[:alnum:]].*//') 750 | if [ "$x" = "" ] && [ "$y" = "" ]; then 751 | echo "0"; return 0 752 | fi 753 | if [ "$x" = "" ] || [ "$(expr "$x" \< "$y")" = 1 ]; then 754 | echo "-1"; return 0 755 | fi 756 | if [ "$y" = "" ] || [ "$(expr "$x" \> "$y")" = 1 ]; then 757 | echo "1"; return 0 758 | fi 759 | a=$(printf %s\\n "$a" | sed 's/^[[:alnum:]]*[^[:alnum:]]*//') 760 | b=$(printf %s\\n "$b" | sed 's/^[[:alnum:]]*[^[:alnum:]]*//') 761 | done 762 | } 763 | 764 | # cmp_digits: compare digits as groups, such as for numeric version strings. 765 | # 766 | # Example: 767 | # 768 | # ``` 769 | # cmp_digits 1.2.3 1.2.3 770 | # => 0 (zero means left == right) 771 | # 772 | # cmp_digits 1.2.3 1.2.4 773 | # => -1 (negative one means left < right) 774 | # 775 | # cmp_digits 1.2.4 1.2.3 776 | # => 1 (positive one means left > right) 777 | # ``` 778 | # 779 | cmp_digits() { 780 | if [ "$1" = "$2" ]; then 781 | echo "0"; return 0 782 | fi 783 | a=$(printf %s\\n "$1" | sed 's/^[^[:digit:]]*//') 784 | b=$(printf %s\\n "$2" | sed 's/^[^[:digit:]]*//') 785 | while true; do 786 | x=$(printf %s\\n "$a" | sed 's/[^[:digit:]].*//') 787 | y=$(printf %s\\n "$b" | sed 's/[^[:digit:]].*//') 788 | if [ "$x" = "" ] && [ "$y" = "" ]; then 789 | echo "0"; return 0 790 | fi 791 | if [ "$x" = "" ] || [ $x -lt $y ]; then 792 | echo "-1"; return 0 793 | fi 794 | if [ "$y" = "" ] || [ $x -gt $y ]; then 795 | echo "1"; return 0 796 | fi 797 | a=$(printf %s\\n "$a" | sed 's/^[[:digit:]]*[^[:digit:]]*//') 798 | b=$(printf %s\\n "$b" | sed 's/^[[:digit:]]*[^[:digit:]]*//') 799 | done 800 | } 801 | 802 | ## 803 | # Extensibility helpers 804 | ## 805 | 806 | # dot_all: source all the executable files in a given directory and subdirectories. 807 | # 808 | # Example: 809 | # ``` 810 | # dot_all ~/temp 811 | # => . ~/temp/a.sh 812 | # => . ~/temp/b.pl 813 | # => . ~/temp/c.js 814 | # ``` 815 | dot_all() { 816 | find "${1:-.}" -type f \( -perm -u=x -o -perm -g=x -o -perm -o=x \) -exec test -x {} \; -exec . {} \; 817 | } 818 | 819 | # run_all: run all the executable commands in a given directory and subdirectories. 820 | # 821 | # Example: 822 | # ``` 823 | # run_all ~/temp 824 | # => ~/temp/a.sh 825 | # => ~/temp/b.pl 826 | # => ~/temp/c.js 827 | # ``` 828 | run_all() { 829 | find "${1:-.}" -type f \( -perm -u=x -o -perm -g=x -o -perm -o=x \) -exec test -x {} \; -exec {} \; 830 | } 831 | 832 | # sh_all: shell all the executable commands in a given directory and subdirectories. 833 | # 834 | # Example: 835 | # ``` 836 | # sh_all ~/temp 837 | # => sh -c ~/temp/a.sh 838 | # => sh -c ~/temp/b.pl 839 | # => sh -c ~/temp/c.js 840 | # ``` 841 | sh_all() { 842 | find "${1:-.}" -type f \( -perm -u=x -o -perm -g=x -o -perm -o=x \) -exec test -x {} \; -print0 | xargs -0I{} -n1 sh -c "{}" 843 | } 844 | 845 | # rm_all: remove all files in a given directory and subdirectories-- use with caution. 846 | # 847 | # Example: 848 | # ``` 849 | # rm_all ~/temp 850 | # => rm ~/temp/a.sh 851 | # => rm ~/temp/b.pl 852 | # => rm ~/temp/c.js 853 | # ``` 854 | rm_all() { 855 | find "${1:-.}" -type f -exec rm {} \; 856 | } 857 | 858 | ## 859 | # Text helpers 860 | ## 861 | 862 | # trim: remove any space characters at the text's start or finish. 863 | # 864 | # Example: 865 | # ``` 866 | # trim " foo " 867 | # => foo 868 | #``` 869 | trim() { 870 | printf %s\\n "$*" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' 871 | } 872 | 873 | # slug: convert a string from any characters to solely lowercase and single internal dash characters. 874 | # 875 | # Example: 876 | # ``` 877 | # slug "**Foo** **Goo** **Hoo**" 878 | # => foo-goo-hoo 879 | #``` 880 | slug() { 881 | printf %s\\n "$*" | sed 's/[^[:alnum:]]/-/g; s/--*/-/g; s/^-*//; s/-*$//;' | tr '[[:upper:]]' '[[:lower:]]' 882 | } 883 | 884 | # slugs: convert a string from any characters to solely lowercase and single internal dash characters and slash characters. 885 | # 886 | # Example: 887 | # ``` 888 | # slugs "**Foo** / **Goo** / **Hoo**" 889 | # => foo/goo/hoo 890 | #``` 891 | slugs(){ 892 | printf %s\\n "$*" | sed 's/[^[:alnum:]\/]/-/g; s/--*/-/g; s/^-*//; s/-*$//; s/-*\/-*/\//g' | tr '[[:upper:]]' '[[:lower:]]' 893 | } 894 | 895 | # upper_format: convert text from any lowercase letters to uppercase letters. 896 | # 897 | # Example: 898 | # ``` 899 | # upper_format AbCdEf 900 | # => ABCDEF 901 | #``` 902 | upper_format() { 903 | printf %s\\n "$*" | tr '[[:lower:]]' '[[:upper:]]' 904 | } 905 | 906 | # lower_format: convert text from any uppercase letters to lowercase letters. 907 | # 908 | # Example: 909 | # ``` 910 | # lower_format AbCdEf 911 | # => abcdef 912 | #``` 913 | lower_format() { 914 | printf %s\\n "$*" | tr '[[:upper:]]' '[[:lower:]]' 915 | } 916 | 917 | # chain_format: convert a string from any characters to solely alphanumeric and single internal dash characters. 918 | # 919 | # Example: 920 | # ``` 921 | # chain_format "**Foo** **Goo** **Hoo**" 922 | # => Foo-Goo-Hoo 923 | #``` 924 | chain_format() { 925 | printf %s\\n "$*" | sed 's/[^[:alnum:]]\{1,\}/-/g; s/-\{2,\}/-/g; s/^-\{1,\}//; s/-\{1,\}$//;' 926 | } 927 | 928 | # snake_format: convert a string from any characters to solely alphanumeric and single internal underscore characters. 929 | # 930 | # Example: 931 | # ``` 932 | # snake_format "**Foo** **Goo** **Hoo**" 933 | # => Foo_Goo_Hoo 934 | #``` 935 | snake_format() { 936 | printf %s\\n "$*" | sed 's/[^[:alnum:]]\{1,\}/_/g; s/_\{2,\}/_/g; s/^_\{1,\}//; s/_\{1,\}$//;' 937 | } 938 | 939 | # space_format: convert a string from any characters to solely alphanumeric and single internal space characters. 940 | # 941 | # Example: 942 | # ``` 943 | # space_format "**Foo** **Goo** **Hoo**" 944 | # => Foo Goo Hoo 945 | #``` 946 | space_format() { 947 | printf %s\\n "$*" | sed 's/[^[:alnum:]]\{1,\}/ /g; s/ \{2,\}/ /g; s/^ \{1,\}//; s/ \{1,\}$//;' 948 | } 949 | 950 | # touch_format: convert a string from any characters to solely a command "touch -t" timestamp format. 951 | # 952 | # Example: 953 | # ``` 954 | # touch_format "Foo 2021-05-04 22:57:54 Goo" 955 | # => 202105042257.54 956 | #``` 957 | touch_format() { 958 | printf %s\\n "$*" | sed 's/[^[:digit:]]//g; s/^\([[:digit:]]\{12\}\)\([[:digit:]]\{2\}\)/\1.\2/;' 959 | } 960 | 961 | # select_character_class: get a string's characters that match a class, with optional offset and length. 962 | # 963 | # Syntax: select_character_class [offset [length]] 964 | # 965 | # Example with character class: 966 | # ``` 967 | # select_character_class foo123goo456 alpha 968 | # => foogoo 969 | # ``` 970 | # 971 | # Example with character class and substring offset: 972 | # ``` 973 | # select_character_class foo123goo456 alpha 3 974 | # => goo 975 | # ``` 976 | # 977 | # Example with character class and substring offset and length: 978 | # ``` 979 | # select_character_class foo123goo456 alpha 3 1 980 | # => g 981 | # ``` 982 | select_character_class() { 983 | string=${1//[^[:$2:]]/} 984 | offset=${3:-0} 985 | length=${4:-${#string}} 986 | printf %s\\n ${string:$offset:$length} 987 | } 988 | 989 | # reject_character_class: get a string's characters that don't match a class, with optional offset and length. 990 | # 991 | # Syntax: reject_character_class [offset [length]] 992 | # 993 | # Example with character class: 994 | # ``` 995 | # reject_character_class foo123goo456 alpha 996 | # => -123--456 997 | # ``` 998 | # 999 | # Example with character class and substring offset: 1000 | # ``` 1001 | # reject_character_class foo123goo456 alpha 6 1002 | # => 456 1003 | # ``` 1004 | # 1005 | # Example with character class and substring offset and length: 1006 | # ``` 1007 | # reject_character_class foo123goo456 alpha 6 1 1008 | # => 4 1009 | # ``` 1010 | reject_character_class() { 1011 | string=${1//[[:$2:]]/} 1012 | offset=${3:-0} 1013 | length=${4:-${#string}} 1014 | printf %s\\n ${string:$offset:$length} 1015 | } 1016 | 1017 | ## 1018 | # Array helpers 1019 | ## 1020 | 1021 | # array_n: get the array number of fields a.k.a. length a.k.a. size. 1022 | # 1023 | # Example: 1024 | # ``` 1025 | # set -- a b c d 1026 | # array_n "$@" 1027 | # => 4 1028 | # ``` 1029 | array_n() { 1030 | printf %s "$#" 1031 | } 1032 | 1033 | # array_i: get the array item at index `i` which is 1-based. 1034 | # 1035 | # Example: 1036 | # ``` 1037 | # set -- a b c d 1038 | # array_i "$@" 3 1039 | # => c 1040 | # ``` 1041 | # 1042 | # POSIX syntax uses an array index that starts at 1. 1043 | # 1044 | # Bash syntax uses an array index that starts at 0. 1045 | # 1046 | # Bash syntax can have more power this way if you prefer it: 1047 | # 1048 | # ``` 1049 | # [ $# == 3 ] && awk -F "$2" "{print \$$3}" <<< "$1" || awk "{print \$$2}" <<< "$1" 1050 | # ``` 1051 | array_i() { 1052 | for __array_i_i in "$@"; do true; done 1053 | if [ "$__array_i_i" -ge 1 -a "$__array_i_i" -lt $# ]; then 1054 | __array_i_j=1 1055 | for __array_i_x in "$@"; do 1056 | if [ "$__array_i_j" -eq "$__array_i_i" ]; then 1057 | printf %s "$__array_i_x" 1058 | return 1059 | fi 1060 | __array_i_j=$((__array_i_j+1)) 1061 | done 1062 | fi 1063 | exit $EXIT_USAGE 1064 | } 1065 | 1066 | # array_first: get the array's first item. 1067 | # 1068 | # Example: 1069 | # ``` 1070 | # set -- a b c d 1071 | # array_first "$@" 1072 | # => a 1073 | # ``` 1074 | array_first() { 1075 | printf %s "$1" 1076 | } 1077 | 1078 | # array_last: get the array's last item. 1079 | # 1080 | # Example: 1081 | # ``` 1082 | # set -- a b c d 1083 | # array_last "$@" 1084 | # => d 1085 | # ``` 1086 | array_last() { 1087 | for __array_last_x in "$@"; do true; done 1088 | printf %s "$__array_last_x" 1089 | } 1090 | 1091 | # array_car: get the array's car item a.k.a. first item. 1092 | # 1093 | # Example: 1094 | # ``` 1095 | # set -- a b c d 1096 | # array_car "$@" 1097 | # => a 1098 | # ``` 1099 | array_car() { 1100 | printf %s "$1" 1101 | } 1102 | 1103 | # array_cdr: get the array's cdr items a.k.a. everything after the first item. 1104 | # 1105 | # Example: 1106 | # ``` 1107 | # set -- a b c 1108 | # array_cdr "$@" 1109 | # => b c d 1110 | # ``` 1111 | array_cdr() { 1112 | shift 1113 | printf %s "$*" 1114 | } 1115 | 1116 | ## 1117 | # Assert helpers 1118 | ## 1119 | 1120 | # assert_test: assert a test utility command succeeds. 1121 | # 1122 | # Example: 1123 | # ``` 1124 | # assert_test -x program.sh 1125 | # => success i.e. no output 1126 | # 1127 | # assert_test -x notes.txt 1128 | # STDERR=> assert_test -x notes.txt 1129 | # ``` 1130 | assert_test() { 1131 | test "$1" "$2" || err assert_test "$@" 1132 | } 1133 | 1134 | # assert_empty: assert an item is empty. 1135 | # 1136 | # Example: 1137 | # ``` 1138 | # assert_empty "" 1139 | # => success i.e. no output 1140 | # 1141 | # assert_empty foo 1142 | # STDERR=> assert_empty foo 1143 | # ``` 1144 | assert_empty() { 1145 | [ -z "$1" ] || err assert_empty "$@" 1146 | } 1147 | 1148 | # assert_not_empty: assert an item is not empty. 1149 | # 1150 | # Example: 1151 | # ``` 1152 | # assert_not_empty foo 1153 | # => success i.e. no output 1154 | # 1155 | # assert_not_empty "" 1156 | # STDERR=> assert_not_empty 1157 | # ``` 1158 | assert_not_empty() { 1159 | [ -z "$1" ] || err assert_empty "$@" 1160 | } 1161 | 1162 | # assert_int_eq: assert an integer is equal to another integer. 1163 | # 1164 | # Example: 1165 | # ``` 1166 | # assert_int_eq 1 1 1167 | # => success i.e. no output 1168 | # 1169 | # assert_int_eq 1 2 1170 | # STDERR=> assert_int_eq 1 2 1171 | # ``` 1172 | assert_int_eq() { 1173 | [ "$1" -eq "$2" ] || err assert_int_eq "$@" 1174 | } 1175 | 1176 | # assert_int_ne: assert an integer is not equal to another integer. 1177 | # 1178 | # Example: 1179 | # ``` 1180 | # assert_int_eq 1 2 1181 | # => success i.e. no output 1182 | # 1183 | # assert_int_eq 1 1 1184 | # STDERR=> assert_int_ne 1 1 1185 | # ``` 1186 | assert_int_ne() { 1187 | [ "$1" -ne "$2" ] || err assert_int_equal "$@" 1188 | } 1189 | 1190 | # assert_int_ge: assert an integer is greater than or equal to another integer. 1191 | # 1192 | # Example: 1193 | # ``` 1194 | # assert_int_ge 2 1 1195 | # => success i.e. no output 1196 | # 1197 | # assert_int_ge 1 2 1198 | # STDERR=> assert_int_ge 1 2 1199 | # ``` 1200 | assert_int_ge() { 1201 | [ "$1" -ge "$2" ] || err assert_int_ge "$@" 1202 | } 1203 | 1204 | # assert_int_gt: assert an integer is greater than another integer. 1205 | # 1206 | # Example: 1207 | # ``` 1208 | # assert_int_gt 2 1 1209 | # => success i.e. no output 1210 | # 1211 | # assert_int_gt 1 2 1212 | # STDERR=> assert_int_gt 1 2 1213 | # ``` 1214 | assert_int_gt() { 1215 | [ "$1" -gt "$2" ] || err assert_int_gt "$@" 1216 | } 1217 | 1218 | # assert_int_le: assert an integer is less than or equal to another integer. 1219 | # 1220 | # Example: 1221 | # ``` 1222 | # assert_int_le 1 2 1223 | # => success i.e. no output 1224 | # 1225 | # assert_int_le 2 1 1226 | # STDERR=> assert_int_le 2 1 1227 | # ``` 1228 | assert_int_le() { 1229 | [ "$1" -le "$2" ] || err assert_int_le "$@" 1230 | } 1231 | 1232 | # assert_int_lt: assert an integer is less than to another integer. 1233 | # 1234 | # Example: 1235 | # ``` 1236 | # assert_int_lt 1 2 1237 | # => success i.e. no output 1238 | # 1239 | # assert_int_lt 2 1 1240 | # STDERR=> assert_int_lt 2 1 1241 | # ``` 1242 | assert_int_lt() { 1243 | [ "$1" -lt "$2" ] || err assert_int_lt "$@" 1244 | } 1245 | 1246 | # assert_str_eq: assert a string is equal to another string. 1247 | # 1248 | # Example: 1249 | # ``` 1250 | # assert_str_eq 1 1 1251 | # => success i.e. no output 1252 | # 1253 | # assert_str_eq 1 2 1254 | # STDERR=> assert_str_eq 1 2 1255 | # ``` 1256 | assert_str_eq() { 1257 | [ "$1" -eq "$2" ] || err assert_str_eq "$@" 1258 | } 1259 | 1260 | # assert_str_ne: assert a string is not equal to another string. 1261 | # 1262 | # Example: 1263 | # ``` 1264 | # assert_str_eq 1 2 1265 | # => success i.e. no output 1266 | # 1267 | # assert_str_eq 1 1 1268 | # STDERR=> assert_str_ne 1 1 1269 | # ``` 1270 | assert_str_ne() { 1271 | [ "$1" -ne "$2" ] || err assert_str_equal "$@" 1272 | } 1273 | 1274 | # assert_str_ge: assert a string is greater than or equal to another string. 1275 | # 1276 | # Example: 1277 | # ``` 1278 | # assert_str_ge 2 1 1279 | # => success i.e. no output 1280 | # 1281 | # assert_str_ge 1 2 1282 | # STDERR=> assert_str_ge 1 2 1283 | # ``` 1284 | assert_str_ge() { 1285 | [ "$1" -ge "$2" ] || err assert_str_ge "$@" 1286 | } 1287 | 1288 | # assert_str_gt: assert a string is greater than another string. 1289 | # 1290 | # Example: 1291 | # ``` 1292 | # assert_str_gt 2 1 1293 | # => success i.e. no output 1294 | # 1295 | # assert_str_gt 1 2 1296 | # STDERR=> assert_str_gt 1 2 1297 | # ``` 1298 | assert_str_gt() { 1299 | [ "$1" -gt "$2" ] || err assert_str_gt "$@" 1300 | } 1301 | 1302 | # assert_str_le: assert a string is less than or equal to another string. 1303 | # 1304 | # Example: 1305 | # ``` 1306 | # assert_str_le 1 2 1307 | # => success i.e. no output 1308 | # 1309 | # assert_str_le 2 1 1310 | # STDERR=> assert_str_le 2 1 1311 | # ``` 1312 | assert_str_le() { 1313 | [ "$1" -le "$2" ] || err assert_str_le "$@" 1314 | } 1315 | 1316 | # assert_str_lt: assert a string is less than to another string. 1317 | # 1318 | # Example: 1319 | # ``` 1320 | # assert_str_lt 1 2 1321 | # => success i.e. no output 1322 | # 1323 | # assert_str_lt 2 1 1324 | # STDERR=> assert_str_lt 2 1 1325 | # ``` 1326 | assert_str_lt() { 1327 | [ "$1" -lt "$2" ] || err assert_str_lt "$@" 1328 | } 1329 | 1330 | # assert_str_starts_with: assert a string starts with a substring. 1331 | # 1332 | # Example: 1333 | # ``` 1334 | # assert_str_starts_with foobar foo 1335 | # => success i.e. no output 1336 | # 1337 | # assert_str_starts_with foobar xxx 1338 | # STDERR=> assert_str_starts_with foobar xxx 1339 | # ``` 1340 | assert_str_starts_with() { 1341 | [ "$1" != "${1#"$2"}" ] || err assert_str_starts_with "$@" 1342 | } 1343 | 1344 | # assert_str_ends_with: assert a string ends with with a substring. 1345 | # 1346 | # Example: 1347 | # ``` 1348 | # assert_str_ends_with foobar bar 1349 | # => success i.e. no output 1350 | # 1351 | # assert_str_ends_with foobar xxx 1352 | # STDERR=> assert_str_ends_with foobar xxx 1353 | # ``` 1354 | assert_str_ends_with() { 1355 | [ "$1" != "${1%"$2"}" ] || err assert_str_ends_with "$@" 1356 | } 1357 | 1358 | ## 1359 | # Make temp helpers 1360 | ## 1361 | 1362 | # mktemp_dir: make a temporary directory path. 1363 | # 1364 | # Example: 1365 | # ``` 1366 | # mktemp_dir 1367 | # => /var/folders/4f7b65122b0fb65b0fdad568a65dc97d 1368 | # ``` 1369 | mktemp_dir() { 1370 | x=$(mktemp -d -t "${1:-$(zid)}") ; trap '{ rm -rf "$x"; }' EXIT ; out "$x" 1371 | } 1372 | 1373 | # mktemp_file: make a temporary file path. 1374 | # 1375 | # Example: 1376 | # ``` 1377 | # mktemp_file 1378 | # => /var/folders/4f7b65122b0fb65b0fdad568a65dc97d/1d9aafac5373be95d8b4c2dece0b1197 1379 | # ``` 1380 | mktemp_file() { 1381 | x=$(mktemp -t "${1:-$(zid)}") ; trap '{ rm -f "$x"; }' EXIT ; out "$x" 1382 | } 1383 | 1384 | ## 1385 | # Media helpers 1386 | ## 1387 | 1388 | # file_media_type: get a file's media type a.k.a. mime type such as "text/plain". 1389 | # 1390 | # Example: 1391 | # ``` 1392 | # file_media_type notes.txt 1393 | # => text/plain 1394 | # ``` 1395 | file_media_type() { 1396 | file --brief --mime "$1" 1397 | } 1398 | 1399 | # file_media_type_supertype: get a file's media type type a.k.a. mime type such as "text". 1400 | # 1401 | # Example: 1402 | # ``` 1403 | # file_media_type_supertype notes.txt 1404 | # => text 1405 | # ``` 1406 | file_media_type_supertype() { 1407 | file --brief --mime "$1" | sed 's#/.*##' 1408 | } 1409 | 1410 | # file_media_type_subtype: get a file's media type subtype a.k.a. mime type such as "plain". 1411 | # 1412 | # Example: 1413 | # ``` 1414 | # file_media_type_subtype notes.txt 1415 | # => plain 1416 | # ``` 1417 | file_media_type_subtype() { 1418 | file --brief --mime "$1" | sed 's#^[^/]*/##; s#;.*##' 1419 | } 1420 | 1421 | ## 1422 | # Font helpers 1423 | ## 1424 | 1425 | # font_name_exists: does a font name exist on this system? 1426 | # 1427 | # Example: 1428 | # ``` 1429 | # font_name_exists Arial 1430 | # => true 1431 | # 1432 | # font_name_exists Foo 1433 | # => false 1434 | # ``` 1435 | # 1436 | font_name_exists() { 1437 | fc-list | grep -q ": $1:" 1438 | } 1439 | 1440 | # font_name_exists_or_die: ensure a font name exists. 1441 | # 1442 | # Example: 1443 | # ``` 1444 | # font_name_exists_or_die Arial 1445 | # => true 1446 | # 1447 | # font_name_exists_or_die Foo 1448 | # STDERR=> Font needed: Foo 1449 | # => exit 1 1450 | # ``` 1451 | # 1452 | font_name_exists_or_die() { 1453 | font_name_exists "$1" || die "$EXIT_UNAVAILABLE" "Font needed: $1" 1454 | } 1455 | 1456 | ## 1457 | # Content helpers 1458 | ## 1459 | 1460 | # file_ends_with_newline: Does a file end with a newline? 1461 | # 1462 | # Example: 1463 | # ``` 1464 | # file_ends_with_newline notes.txt 1465 | # => true 1466 | # ``` 1467 | file_ends_with_newline() { 1468 | test $(tail -c1 "$1" | wc -l) -gt 0 1469 | } 1470 | 1471 | 1472 | ## 1473 | # Directory helpers 1474 | ## 1475 | 1476 | # user_dir: get a user-specific directory via env var, or XDG setting, or HOME. 1477 | # 1478 | # Example: 1479 | # ``` 1480 | # user_dir foo => $FOO_DIR || $FOO_HOME || $XDG_FOO_DIR || $XDG_FOO_HOME || $HOME/foo 1481 | # ``` 1482 | # 1483 | # Conventions: 1484 | # 1485 | # * `user_dir bin` => binary executable directory 1486 | # * `user_dir cache` => cache directory 1487 | # * `user_dir config` => configuration directory 1488 | # * `user_dir data` => data directory 1489 | # * `user_dir desktop` => desktop directory 1490 | # * `user_dir documents` => documents directory 1491 | # * `user_dir download` => download directory 1492 | # * `user_dir log` => logging directory 1493 | # * `user_dir music` => music directory 1494 | # * `user_dir pictures` => pictures directory 1495 | # * `user_dir publicshare` => public share directory 1496 | # * `user_dir runtime` => runtime directory 1497 | # * `user_dir state` => state directory 1498 | # * `user_dir temp` => temporary directory 1499 | # * `user_dir templates` => templates directory 1500 | # * `user_dir videos` => videos directory 1501 | # 1502 | # Popular XDG conventions: 1503 | # 1504 | # * `XDG_DESKTOP_DIR` => user-specific desktop, such as frequent apps and files. 1505 | # * `XDG_DOCUMENTS_DIR` => user-specific documents, such as typical working files. 1506 | # * `XDG_DOWNLOAD_DIR` => user-specific downloads, such as internet file downloads. 1507 | # * `XDG_MUSIC_DIR` => user-specific music files, such as songs. 1508 | # * `XDG_PICTURES_DIR` => user-specific pictures, such as photos. 1509 | # * `XDG_PUBLICSHARE_DIR` => user-specific public share, such as file sharing. 1510 | # * `XDG_TEMPLATES_DIR` => user-specific templates. 1511 | # * `XDG_VIDEOS_DIR` => user-specific videos, such as movies. 1512 | # 1513 | # POSIX XDG conventions: 1514 | # 1515 | # * `XDG_BIN_HOME` => user-specific binaries, analogous to system /usr/bin or $HOME/.local/bin. 1516 | # * `XDG_LOG_HOME` => user-specific log files, analogous to system /var/log or $HOME/.local/log. 1517 | # * `XDG_TEMP_HOME` => user-specific temporary files, analogous to system /temp or $HOME/.temp. 1518 | # * `XDG_DATA_HOME` => user-specific data files, analogous to system /usr/share or $HOME/.local/share. 1519 | # * `XDG_CACHE_HOME` => user-specific cache files, analogous to system /var/cache or $HOME/.cache. 1520 | # * `XDG_STATE_HOME` => user-specific cache files, analogous to system /var/state or $HOME/.local/state. 1521 | # * `XDG_CONFIG_HOME` => user-specific configuration files, analogous to system /etc or $HOME/.config. 1522 | # * `XDG_RUNTIME_HOME` => user-specific runtime files such as sockets, named pipes, etc. or $HOME/.runtime. 1523 | # 1524 | # See also: 1525 | # 1526 | # * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html 1527 | # 1528 | # * https://wiki.archlinux.org/title/XDG_user_directories 1529 | # 1530 | user_dir(){ 1531 | upper=$(printf %s\\n "$1" | tr '[:lower:]' '[:upper:]') 1532 | lower=$(printf %s\\n "$1" | tr '[:upper:]' '[:lower:]') 1533 | a=$(eval printf "%s\\\\n" \$${upper}_DIR) 1534 | b=$(eval printf "%s\\\\n" \$${upper}_HOME) 1535 | c=$(eval printf "%s\\\\n" \$XDG_${upper}_DIR) 1536 | d=$(eval printf "%s\\\\n" \$XDG_${upper}_HOME) 1537 | printf %s\\n "${a:=${b:=${c:=${d:=$HOME/$lower}}}}" 1538 | } 1539 | -------------------------------------------------------------------------------- /pygments.theme: -------------------------------------------------------------------------------- 1 | { 2 | "text-color": null, 3 | "background-color": "#eeeeee", 4 | "line-number-color": "#aaaaaa", 5 | "line-number-background-color": null, 6 | "text-styles": { 7 | "Alert": { 8 | "text-color": "#ff0000", 9 | "background-color": null, 10 | "bold": true, 11 | "italic": false, 12 | "underline": false 13 | }, 14 | "Annotation": { 15 | "text-color": "#60a0b0", 16 | "background-color": null, 17 | "bold": true, 18 | "italic": true, 19 | "underline": false 20 | }, 21 | "Attribute": { 22 | "text-color": "#7d9029", 23 | "background-color": null, 24 | "bold": false, 25 | "italic": false, 26 | "underline": false 27 | }, 28 | "BaseN": { 29 | "text-color": "#40a070", 30 | "background-color": null, 31 | "bold": false, 32 | "italic": false, 33 | "underline": false 34 | }, 35 | "BuiltIn": { 36 | "text-color": null, 37 | "background-color": null, 38 | "bold": false, 39 | "italic": false, 40 | "underline": false 41 | }, 42 | "Char": { 43 | "text-color": "#4070a0", 44 | "background-color": null, 45 | "bold": false, 46 | "italic": false, 47 | "underline": false 48 | }, 49 | "Comment": { 50 | "text-color": "#666666", 51 | "background-color": null, 52 | "bold": false, 53 | "italic": false, 54 | "underline": false 55 | }, 56 | "CommentVar": { 57 | "text-color": "#60a0b0", 58 | "background-color": null, 59 | "bold": true, 60 | "italic": true, 61 | "underline": false 62 | }, 63 | "Constant": { 64 | "text-color": "#880000", 65 | "background-color": null, 66 | "bold": false, 67 | "italic": false, 68 | "underline": false 69 | }, 70 | "ControlFlow": { 71 | "text-color": "#007020", 72 | "background-color": null, 73 | "bold": true, 74 | "italic": false, 75 | "underline": false 76 | }, 77 | "DataType": { 78 | "text-color": "#902000", 79 | "background-color": null, 80 | "bold": false, 81 | "italic": false, 82 | "underline": false 83 | }, 84 | "DecVal": { 85 | "text-color": "#40a070", 86 | "background-color": null, 87 | "bold": false, 88 | "italic": false, 89 | "underline": false 90 | }, 91 | "Documentation": { 92 | "text-color": "#ba2121", 93 | "background-color": null, 94 | "bold": false, 95 | "italic": true, 96 | "underline": false 97 | }, 98 | "Error": { 99 | "text-color": "#ff0000", 100 | "background-color": null, 101 | "bold": true, 102 | "italic": false, 103 | "underline": false 104 | }, 105 | "Extension": { 106 | "text-color": null, 107 | "background-color": null, 108 | "bold": false, 109 | "italic": false, 110 | "underline": false 111 | }, 112 | "Float": { 113 | "text-color": "#40a070", 114 | "background-color": null, 115 | "bold": false, 116 | "italic": false, 117 | "underline": false 118 | }, 119 | "Function": { 120 | "text-color": "#06287e", 121 | "background-color": null, 122 | "bold": false, 123 | "italic": false, 124 | "underline": false 125 | }, 126 | "Import": { 127 | "text-color": null, 128 | "background-color": null, 129 | "bold": false, 130 | "italic": false, 131 | "underline": false 132 | }, 133 | "Information": { 134 | "text-color": "#60a0b0", 135 | "background-color": null, 136 | "bold": true, 137 | "italic": true, 138 | "underline": false 139 | }, 140 | "Keyword": { 141 | "text-color": "#007020", 142 | "background-color": null, 143 | "bold": true, 144 | "italic": false, 145 | "underline": false 146 | }, 147 | "Operator": { 148 | "text-color": "#666666", 149 | "background-color": null, 150 | "bold": false, 151 | "italic": false, 152 | "underline": false 153 | }, 154 | "Other": { 155 | "text-color": "#007020", 156 | "background-color": null, 157 | "bold": false, 158 | "italic": false, 159 | "underline": false 160 | }, 161 | "Preprocessor": { 162 | "text-color": "#bc7a00", 163 | "background-color": null, 164 | "bold": false, 165 | "italic": false, 166 | "underline": false 167 | }, 168 | "SpecialChar": { 169 | "text-color": "#4070a0", 170 | "background-color": null, 171 | "bold": false, 172 | "italic": false, 173 | "underline": false 174 | }, 175 | "SpecialString": { 176 | "text-color": "#bb6688", 177 | "background-color": null, 178 | "bold": false, 179 | "italic": false, 180 | "underline": false 181 | }, 182 | "String": { 183 | "text-color": "#4070a0", 184 | "background-color": null, 185 | "bold": false, 186 | "italic": false, 187 | "underline": false 188 | }, 189 | "Variable": { 190 | "text-color": "#19177c", 191 | "background-color": null, 192 | "bold": false, 193 | "italic": false, 194 | "underline": false 195 | }, 196 | "VerbatimString": { 197 | "text-color": "#4070a0", 198 | "background-color": null, 199 | "bold": false, 200 | "italic": false, 201 | "underline": false 202 | }, 203 | "Warning": { 204 | "text-color": "#60a0b0", 205 | "background-color": null, 206 | "bold": true, 207 | "italic": true, 208 | "underline": false 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /tex/blockquote.tex: -------------------------------------------------------------------------------- 1 | \usepackage{tcolorbox} 2 | \newtcolorbox{myquote}{colback=red!5!white, colframe=red!75!black} 3 | \renewenvironment{quote}{\begin{myquote}}{\end{myquote}} 4 | -------------------------------------------------------------------------------- /tex/bullet.tex: -------------------------------------------------------------------------------- 1 | \usepackage{enumitem} 2 | \usepackage{amsfonts} 3 | 4 | % level one 5 | \setlist[itemize,1]{label=$\bullet$} 6 | % level two 7 | \setlist[itemize,2]{label=$\circ$} 8 | % level three 9 | \setlist[itemize,3]{label=$\circ$} 10 | -------------------------------------------------------------------------------- /tex/fontspec.tex: -------------------------------------------------------------------------------- 1 | \usepackage{fontspec} 2 | 3 | % Examples of how to set font family names: 4 | % 5 | % \setmainfont{Source Serif Pro} 6 | % \setsansfont{Source Sans Pro} 7 | % \setmonofont{Source Code Pro} 8 | 9 | % Examples of how to scale fonts: 10 | % 11 | % \setmonofont[Scale=0.8]{Source Code Pro} 12 | % \setmonofont[Scale=MatchLowercase]{Source Code Pro} 13 | -------------------------------------------------------------------------------- /tex/hyphen.tex: -------------------------------------------------------------------------------- 1 | \exhyphenpenalty=10000 2 | \hyphenpenalty=10000 3 | -------------------------------------------------------------------------------- /tex/inline_code.tex: -------------------------------------------------------------------------------- 1 | \usepackage{fancyvrb,newverbs,xcolor} 2 | 3 | \definecolor{Light}{HTML}{EEEEEE} 4 | 5 | \let\oldtexttt\texttt 6 | \renewcommand{\texttt}[1]{ 7 | \colorbox{Light}{\oldtexttt{#1}} 8 | } 9 | 10 | -------------------------------------------------------------------------------- /tex/listings.tex: -------------------------------------------------------------------------------- 1 | \usepackage{listings} 2 | \usepackage{xcolor} 3 | 4 | \lstset{ 5 | backgroundcolor=\color[RGB]{240,240,240}, 6 | basicstyle=\footnotesize\ttfamily\linespread{1.1}, % the size of the fonts that are used for the code; posssible values are (\ttfamily, \footnotesize, etc.) 7 | breaklines=true, % sets automatic line breaking 8 | breakatwhitespace=true, % sets if automatic breaks should only happen at whitespace 9 | breakautoindent=true, 10 | breaklines=true, 11 | captionpos=b, 12 | commentstyle=\color[rgb]{0.56,0.35,0.01}\itshape, 13 | escapeinside={\%*}{*)}, 14 | frame=single, % adds a frame around the code 15 | framesep=8pt, 16 | keywordstyle=\color[rgb]{0.13,0.29,0.53}\bfseries, 17 | linewidth=\textwidth, 18 | numbers=none, % where to put the line-numbers; possible values are (none, left, right) 19 | numbersep=5pt, % how far the line-numbers are from the code 20 | numberstyle=\tiny\color{gray}, % the style that is used for the line-numbers 21 | rulecolor=\color[RGB]{220,220,220}, % the frame color; we prefer slightly-darker than the background color 22 | showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces' 23 | showstringspaces=false, % underline spaces within strings only 24 | showtabs=false, % show tabs within strings adding particular underscores 25 | stepnumber=2, % the step between two line-numbers. If it's 1, each line will be numbered 26 | stringstyle=\color[rgb]{0.31,0.60,0.02}, 27 | tabsize=4, % sets default tabsize to 2 spaces 28 | xleftmargin=8pt, % use the same value as framesep 29 | xrightmargin=8pt, % use the same value as framesep 30 | } 31 | 32 | % Unused: 33 | % basewidth=0.9em, 34 | 35 | % \lstset{ 36 | % backgroundcolor=\color{white}, % choose the background color; you must add \usepackage{color} or \usepackage{xcolor}; should come as last argument 37 | % captionpos=b, % sets the caption-position to bottom 38 | % commentstyle=\color{mygreen}, % comment style 39 | % deletekeywords={...}, % if you want to delete keywords from the given language 40 | % escapeinside={\%*}{*)}, % if you want to add LaTeX within your code 41 | % extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8 42 | % firstnumber=1000, % start line enumeration with line 1000 43 | % keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible) 44 | % keywordstyle=\color{blue}, % keyword style 45 | % language=Octave, % the language of the code 46 | % morekeywords={*,...}, % if you want to add more keywords to the set 47 | % rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here)) 48 | % stringstyle=\color{mymauve}, % string literal style 49 | % title=\lstname % show the filename of files included with \lstinputlisting; also try caption instead of title 50 | % } 51 | -------------------------------------------------------------------------------- /tex/raggedright.tex: -------------------------------------------------------------------------------- 1 | \raggedright 2 | -------------------------------------------------------------------------------- /tex/sectionstyle.tex: -------------------------------------------------------------------------------- 1 | \usepackage{silence} 2 | \WarningFilter[temp]{latex}{Command \underbar has changed.} 3 | \WarningFilter[temp]{latex}{Command \underline has changed.} 4 | \usepackage{sectsty} 5 | \DeactivateWarningFilters[temp] 6 | 7 | \sectionfont{\clearpage} 8 | \sectionfont{\LARGE\clearpage} 9 | \subsectionfont{\clearpage} 10 | \subsectionfont{\LARGE\clearpage} 11 | -------------------------------------------------------------------------------- /tex/setspace.tex: -------------------------------------------------------------------------------- 1 | \usepackage{setspace} 2 | \setstretch{1.2} 3 | --------------------------------------------------------------------------------