summaryrefslogtreecommitdiff
path: root/macros/generic/termmenu
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/generic/termmenu
Initial commit
Diffstat (limited to 'macros/generic/termmenu')
-rw-r--r--macros/generic/termmenu/README80
-rw-r--r--macros/generic/termmenu/termmenu.dtx661
-rw-r--r--macros/generic/termmenu/termmenu.ins38
-rw-r--r--macros/generic/termmenu/termmenu.pdfbin0 -> 472608 bytes
4 files changed, 779 insertions, 0 deletions
diff --git a/macros/generic/termmenu/README b/macros/generic/termmenu/README
new file mode 100644
index 0000000000..9a110bbd12
--- /dev/null
+++ b/macros/generic/termmenu/README
@@ -0,0 +1,80 @@
+termmenu - Terminal-driven menus in TeX
+=======================================
+
+When writing programs, it's often required to present the user with a
+list of options/actions. The user is then expected to select one of
+these options for the program to process.
+
+`termmenu` provides this mechanism for TeX. Using `termmenu` requires
+only `expl3` support, thus the `l3kernel` and `l3packages` are both
+required.
+
+----------------------------------------------------------------------
+
+`termmenu.dtx` may be distributed and/or modified under the conditions
+of the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file
+
+ http://www.latex-project.org/lppl.txt
+
+The released version is available from CTAN.
+
+----------------------------------------------------------------------
+
+The development version can be found at
+
+ http://www.github.com/vermiculus/tex-termmenu
+
+for those people who are interested.
+
+Installation
+------------
+
+To install `termmenu`, simply run
+
+ tex termmenu.ins
+
+`termmenu.tex` will be produced. Since it can be run by anything that
+can run `expl3`, I recommend placing it in `tex/generic/termmenu`.
+
+To produce the documentation, run `pdflatex` or
+[`arara`](https://github.com/cereda/arara) on the file as normal.
+
+Usage
+-----
+
+Full documentation is available in `termmenu.pdf`, but using
+`termmenu` is very simple if you're familiar with `expl3` programming:
+
+ \termmenu_new:N \g_demo_termmenu
+
+will create a menu and
+
+ \termmenu_add:Nnnn \g_demo_termmenu { option }
+ { Description text }
+ { \msg_term:n { Actions } }
+
+will add a menu item. The user is prompted with
+
+ \termmenu_get_choice:NN \g_demo_termmenu \l_tmpa_tl
+
+and an action is taken with
+
+ \termmenu_exec:NV \g_demo_termmenu \l_tmpa_tl
+
+The menu itself looks something like this:
+
+ *************************************************
+ * Menu
+ *************************************************
+
+ The following commands are available:
+
+ > option
+ Description text
+
+ \choice=option
+ *************************************************
+ * Actions
+ *************************************************
diff --git a/macros/generic/termmenu/termmenu.dtx b/macros/generic/termmenu/termmenu.dtx
new file mode 100644
index 0000000000..04a279bf81
--- /dev/null
+++ b/macros/generic/termmenu/termmenu.dtx
@@ -0,0 +1,661 @@
+% \iffalse meta-comment
+% arara: clean: { files: [ termmenu.aux, termmenu.glo, termmenu.hd, termmenu.idx, termmenu.ilg, termmenu.ind, termmenu.out ] }
+% arara: pdflatex
+% !arara: makeindex
+% !arara: pdflatex
+% arara: pdflatex
+%
+%%
+%% File: termmenu.dtx Copyright(C) 2015 Sean Allred
+%%
+%% termmenu.dtx may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License (LPPL),
+%% either version 1.3c of this license or (at your option)
+%% any later version. The latest version of this license is
+%% in the file
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% The released version is available from CTAN.
+%%
+%% -----------------------------------------------------------------------
+%%
+%% The development version can be found at
+%%
+%% http://www.github.com/vermiculus/tex-termmenu
+%%
+%% for those people who are interested.
+%%
+%% -----------------------------------------------------------------------
+%%
+%
+%<*driver>
+\documentclass[full]{l3doc}
+%</driver>
+%<*driver|package>
+\def\ExplFileName{termmenu}
+\def\ExplFileVersion{0.2}
+\def\ExplFileDate{2015-05-25}
+\def\ExplFileDescription{Terminal-driven menu support}
+\def\ExplFileExtension{dtx}
+%</driver|package>
+%<*driver>
+\begin{document}
+\renewcommand\partname{Part}
+\DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \textsf{termmenu} package\\ Terminal-driven menu support^^A
+% \thanks{This file describes v\ExplFileVersion,
+% last revised \ExplFileDate.}^^A
+% }
+%
+% \author{^^A
+% Sean Allred\thanks
+% {^^A
+% E-mail:
+% \href{mailto:tex@seanallred.com}
+% {tex@seanallred.com}^^A
+% }^^A
+% }
+%
+% \date{Released \ExplFileDate}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module provides simple support for terminal-driven menus in
+% \pkg{expl3}.
+%
+% \paragraph{Example of use}
+% \begin{verbatim}
+% \termmenu_new:N \g_demo_termmenu
+% \termmenu_set_name:Nn \g_demo_termmenu { Demo }
+%
+% \termmenu_add:Nnnn \g_demo_termmenu { d, duck }
+% { Oh,~you~know...~:) }
+% { \msg_term:n { Quack! } }
+%
+% \termmenu_do:N \g_demo_termmenu
+%
+% \bye
+% \end{verbatim}
+% \begin{verbatim}
+% $ pdftex demo.tex
+% *************************************************
+% * Demo
+% *************************************************
+%
+% The following commands are available:
+%
+% > d, duck
+% Oh, you know... :)
+%
+% \choice=d
+% *************************************************
+% * Quack!
+% *************************************************
+% \end{verbatim}
+%
+% \newpage
+% \tableofcontents
+% \section*{Introduction}
+%
+% Menus are effectively documented property lists with a fancy,
+% user-friendly version of \cs{prop_show:N}.
+%
+% \newpage
+%
+% \part{Interface Documentation}
+%
+% \section{Creating and initializing menus}
+%
+% \begin{function}[added = 2015-05-23]{\termmenu_new:N}
+% \begin{syntax}
+% \cs{termmenu_new:N} \meta{menu}
+% \end{syntax}
+% Creates a new \meta{menu} or raises an error if the name is
+% already taken. The declaration is global. Initially, the menu
+% will be empty.
+% \end{function}
+%
+% \begin{function}[added = 2015-05-23]{\termmenu_set_name:Nn}
+% \begin{syntax}
+% \cs{termmenu_set_name:Nn} \meta{menu} \Arg{name}
+% \end{syntax}
+% Give \meta{menu} a human-friendly name. When a menu is being
+% presented, \meta{name} will appear as a title.
+% \end{function}
+%
+% \begin{function}[added = 2015-05-23]{\termmenu_add:Nnnn}
+% \begin{syntax}
+% \cs{termmenu_add:Nnnn} \meta{menu} \Arg{entry} \Arg{help text} \Arg{value}
+% \end{syntax}
+% Insert \meta{entry} into a \meta{menu} and provide \meta{help
+% text}. When a menu is being presented, both \meta{entry} and
+% \meta{help text} will be shown. When \meta{entry} is being used,
+% \meta{value} will be associated with it.
+%
+% To simplify the user experience, \meta{entry} can be a
+% comma-separated list of synonymous options. This can be used to
+% create shortcuts to functionality: instead of always typing out
+% |entry|, the end-user can simply say |o| if something like the
+% following is used:
+% \begin{verbatim}
+% \termmenu_add:Nnnn \g_tmpa_termmenu { o, option } { ... } { ... }
+% \end{verbatim}
+% \end{function}
+%
+% \section{Using menus}
+%
+% To allow for ad-hoc processing, menus are shown and acted upon in
+% four separate phases: display, input, retrieval, insertion.
+%
+% \begin{enumerate}
+% \item The display phase displays the menu to the end-user.
+% \item Then, an input routine is called upon to store information
+% into a specific variable while prompting for a different one.
+% (This is useful to keep the interface clean. The internal
+% variable can be very strange-looking, but the user can see
+% whatever you which them to see.)
+% \item The retrieval phase receives the user's input and looks it up
+% in the list of option synonyms, returning the matching menu entry.
+% \item When the entry has been found, the appropriate value is either
+% returned to the programmer or inserted into the input stream.
+% \end{enumerate}
+%
+% \begin{function}[added = 2015-05-24]{\termmenu_do:NNNNN,
+% \termmenu_do:NNNN,
+% \termmenu_do:NNN,
+% \termmenu_do:NN,
+% \termmenu_do:N}
+% \begin{syntax}
+% \cs{termmenu_do:NNNNN} \meta{menu} \meta{prompt token} \meta{input tl} \meta{entry tl} \meta{value tl}
+% \cs{termmenu_do:NNNN} \meta{menu} \meta{prompt token} \meta{input tl} \meta{entry tl}
+% \cs{termmenu_do:NNN} \meta{menu} \meta{input tl} \meta{value tl}
+% \cs{termmenu_do:NN} \meta{menu} \meta{prompt token}
+% \cs{termmenu_do:N} \meta{menu}
+% \end{syntax}
+% The \cs{termmenu_do:} family of functions are convenience wrappers
+% around the basic display, prompt, and lookup functions.
+% \begin{description}
+% \item[|do:NNNNN|] uses \meta{menu} to prompt the user for \meta{prompt
+% token}, setting their input to \meta{input tl}, setting the
+% matched entry to \meta{entry tl}, and storing the associated value
+% in \meta{value tl}.
+%
+% \item[|do:NNNN|] is the same as |do:NNNNN|, but instead of storing the
+% output in a token list, it is inserted into the input stread.
+%
+% \item[|do:NNN|] is similar to |do:NNNNN|, but does not allow the
+% programmer to set what the user sees as a prompt. Instead, the
+% generic \cs{choice} name is used for \meta{input tl}.
+% Additionally, the associated menu entry is not stored.
+%
+% \item[|do:NN|] is like |do:NNNN|, but does not store either the user's
+% input or its associated menu entry. The only parameter this takes
+% is the \meta{prompt token}.
+%
+% \item[|do:N|] is the simplest way to use a menu. It uses \cs{choice} for
+% the \meta{prompt token} and places the associated value in the
+% input stream.
+% \end{description}
+% \end{function}
+%
+% \section{Input and output}
+%
+% \begin{function}[added = 2015-05-23, updated = 2015-05-24]{\termmenu_prompt:NN, \termmenu_prompt:N}
+% \begin{syntax}
+% \cs{termmenu_prompt:NN} \meta{input tl} \meta{prompt token}
+% \cs{termmenu_prompt:N} \meta{input tl}
+% \end{syntax}
+% Read a value for \meta{input tl} showing \meta{prompt token} to
+% the user. When \meta{prompt token} isn't provided, it defaults to
+% \cs{choice}.
+% \end{function}
+%
+% \begin{variable}[added = 2015-05-23]{\l_termmenu_prompt_tl}
+% This is the message displayed to the user when a menu is
+% presented. The default value is
+% |The~following~commands~are~available:|.
+% \end{variable}
+%
+% \section{Inspection}
+%
+% \begin{function}[added = 2015-05-23]{\termmenu_show:N}
+% \begin{syntax}
+% \cs{termmenu_show:N} \meta{menu}
+% \end{syntax}
+% Show the contents of \meta{menu}. Right now, this is the same as
+% \cs{prop_show:N}.
+% \end{function}
+%
+% \begin{function}[added = 2015-05-23, updated = 2015-05-24]{\termmenu_get_name:NN}
+% \begin{syntax}
+% \cs{termmenu_get_name:NN} \meta{menu} \meta{name tl}
+% \end{syntax}
+% Retrieve the name of \meta{menu} and place it in \meta{name tl}.
+% \end{function}
+%
+% \begin{function}[added = 2015-05-25]{\termmenu_entry:NnN, \termmenu_entry:NVN}
+% \begin{syntax}
+% \cs{termmenu_entry:NnN} \meta{menu} \Arg{option} \meta{entry tl}
+% \end{syntax}
+% Finds the entry that \meta{option} would refer to in \meta{menu}
+% and places it in \meta{entry tl}.
+% \end{function}
+%
+% \begin{function}[added = 2015-05-25]{\termmenu_doc:NnN}
+% \begin{syntax}
+% \cs{termmenu_doc:NnN} \meta{menu} \Arg{entry} \meta{doc tl}
+% \end{syntax}
+% Finds the documentation for \meta{entry} in \meta{menu} and places
+% it in \meta{doc tl}.
+% \end{function}
+%
+% \begin{function}[added = 2015-05-25]{\termmenu_value:NnN, \termmenu_value:NVN}
+% \begin{syntax}
+% \cs{termmenu_value:NnN} \meta{menu} \Arg{entry} \meta{value tl}
+% \end{syntax}
+% Places \meta{menu}'s value for \meta{entry} in \meta{value tl}.
+% \end{function}
+%
+% \section{Internal variables and functions}
+%
+% \begin{variable}[added = 2015-05-23]{\g__termmenu_names_prop}
+% This property list stores the names of each menu. The keys of the
+% property lists are menus (e.g., \cs{g_demo_termmenu}) and the
+% values are their names.
+% \end{variable}
+%
+% \begin{variable}[added = 2015-05-23]{\l__termmenu_spec_tl}
+% Generally speaking, this scratch variable stores lower-level
+% information about a menu (or one of its entries).
+% \end{variable}
+%
+% \begin{variable}[added = 2015-05-23]{\g__termmenu_doc_tl}
+% This scratch variable stores the documentation for an entry. It
+% is necessary because none of |f|, |x|, |o|-type expansions seem to
+% work where they need to. Patches\slash pull requests welcome.
+% ^^A For reference, this place is \cs{termmenu_display:N}.
+% \end{variable}
+%
+% \begin{variable}[added = 2015-05-24]{\l__termmenu_value_tl}
+% This scratch variable stores the value of an entry. It is
+% immediately inserted back into the input stream.
+% \end{variable}
+%
+% \begin{variable}[added = 2015-05-24]{\l__termmenu_tmp_tl}
+% This scratch variable holds values returned by the various |do:|
+% convenience macros. This value serves as a sort of
+% \enquote*{trash bin} to throw away some unwanted values.
+% \end{variable}
+%
+% \begin{variable}[added = 2015-05-24]{\g__termmenu_tmp_tl}
+% This scratch variable is used to help set \meta{tl var} from
+% \cs{termmenu_find:NnN}.
+% \end{variable}
+%
+% \begin{variable}[added = 2015-05-24]{\g__termmenu_opt_bool}
+% This variable is used to signal if the user's input was able to
+% match against a known option.
+% \end{variable}
+%
+% \begin{function}[added = 2015-05-24]{\termmenu_display:N}
+% \begin{syntax}
+% \cs{termmenu_display:N} \meta{menu}
+% \end{syntax}
+% Writes out \meta{menu} to the terminal. No associated actions
+% are performed. If \meta{menu} has a name (i.e., an entry in
+% \cs{__termmenu_names_prop}), print it as well.
+% \end{function}
+%
+% \end{documentation}
+%
+% \newpage
+%
+% \part{Implementation}
+%
+% Before I begin, I want to establish a few definitions:
+% \begin{description}
+% \item[menu] The over-arching data structure that holds (nearly) all
+% related information for a given menu.
+% \item[entry] A full, comma-separated specification of valid
+% inputs for a given menu item.
+% \item[option] A valid input for an entry.
+% \item[value] The associated action for an entry.
+% \item[prompt token] The token read in by \TeX{} on a low level; this
+% token's name is presented to the user in output.
+% \end{description}
+%
+% \begin{implementation}
+%
+% \section{Creation and initialization}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+% \begin{macrocode}
+%<@@=termmenu>
+% \end{macrocode}
+%
+% \begin{macro}{\termmenu_new:N, \termmenu_show:N, \termmenu_add:Nnnn}
+% Each menu is implemented as a property list of options mapped
+% to documentation and actions. Each option is a property list
+% key. Since the there is no good way to distinguish between
+% when documentation ends and an associated action begins, the
+% documentation is placed in a group at the head of the key's
+% value.\footnote{This could also be done with sequences, but I
+% consider that unnecessary overhead and complication.}
+% \begin{macrocode}
+\cs_set_eq:NN \termmenu_new:N \prop_new:N
+\cs_set_eq:NN \termmenu_show:N \prop_show:N
+\cs_new_nopar:Nn \termmenu_add:Nnnn
+ { \prop_put:Nnn #1 {#2} { {#3} #4 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \section{Convenience functions}
+%
+% \begin{variable}{\l_@@_tmp_tl, \l_@@_value_tl}
+% These scratch variables are used in the following code. You may
+% be reviewing the code and thing to yourself, \enquote{Wait,
+% couldn't you use just one variable?} Rest assured that you
+% cannot: \cs{termmenu_do:NNNN} uses \cs{l_@@_value_tl} to insert
+% the value code into the input stream. Using the same variable for
+% this would be at best confusing\slash unmaintainble. Besides,
+% \cs{l_@@_tmp_tl} is supposed to be a variable for unwanted values.
+% \begin{macrocode}
+\tl_new:N \l_@@_tmp_tl
+\tl_new:N \l_@@_value_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\termmenu_do:NNNNN,
+% \termmenu_do:NNNN,
+% \termmenu_do:NNN,
+% \termmenu_do:NN,
+% \termmenu_do:N}
+% These functions make menus easier to use by choosing some sane
+% defaults and running through the entire flow. Each of these
+% macros is ultimately based on the most general one,
+% \cs{termmenu_do:NNNNN}. This results in some waste of work, but
+% those extra gets\slash sets pale in comparison to wrapping the
+% menu output, executing whatever the menu entry stands for,
+% \emph{actually waiting for the user to make a choice}, etc.
+% ^^A NNNNN <in menu> <out input> <in prompt> <out entry> <out value>
+% ^^A NNNN <in menu> <out input> <in prompt> <out entry> >>value
+% ^^A NNN <in menu> <out input> <out value>
+% ^^A NN <in menu> <in prompt>
+% ^^A N <in menu>
+% \begin{macrocode}
+\cs_new_nopar:Nn \termmenu_do:NNNNN
+ {
+ \termmenu_display:N #1
+ \termmenu_prompt:NN #2 #3
+ \termmenu_entry:NVN #1 #2 #4
+ \termmenu_value:NVN #1 #4 #5
+ }
+\cs_new_nopar:Nn \termmenu_do:NNNN
+ {
+ \tl_clear:N \l_@@_value_tl
+ \termmenu_do:NNNNN #1 #2 #3 #4 \l_@@_value_tl
+ \tl_use:N \l_@@_value_tl
+ }
+\cs_new_nopar:Nn \termmenu_do:NNN
+ { \termmenu_do:NNNNN #1 #2 \choice \l_@@_tmp_tl #3 }
+\cs_new_nopar:Nn \termmenu_do:NN
+ { \termmenu_do:NNNN #1 \l_@@_tmp_tl #2 \l_@@_tmp_tl }
+\cs_new_nopar:Nn \termmenu_do:N
+ { \termmenu_do:NN #1 \choice }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \section{Retrieving values}
+%
+% \begin{variable}{\l_@@_spec_tl}
+% \cs{l_@@_spec_tl} is used when retrieving data from property
+% lists. It's used quite a bit in the following functions.
+% \begin{macrocode}
+\tl_new:N \l_@@_tmp_tl
+\tl_new:N \l_@@_value_tl
+\tl_new:N \l_@@_spec_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\termmenu_value:NnN, \termmenu_value:NVN}
+% This function retrieves the value for entry |#2| in menu |#1| and
+% places it in |#3|. Remember that the documentation and value are
+% stored together in the property list. Since the documentation is
+% kept in a group, we can use \cs{tl_tail:N} to grab just the
+% desired value.
+% \begin{macrocode}
+\cs_new_nopar:Nn \termmenu_value:NnN
+ {
+ \prop_get:NnN #1 {#2} \l_@@_spec_tl
+ \tl_set:Nf #3 { \tl_tail:N \l_@@_spec_tl } %@todo test expansion
+ }
+\cs_generate_variant:Nn \termmenu_value:NnN { NVN }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \section{Entry documentation}
+%
+%
+% \begin{variable}{\g_@@_doc_tl}
+% This variable stores the help text for an option.
+% \begin{macrocode}
+\tl_new:N \l__termmenu_doc_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\termmenu_doc:NnN}
+% These functions retrieve the help text for entry |#2| in |#1| and
+% place it in |#3|. Keep in mind the structure of
+% \cs{l_@@_names_prop}.
+% \begin{macrocode}
+\cs_new_nopar:Nn \termmenu_doc:NnN
+ {
+ \prop_get:NnN #1 {#2} \l_@@_spec_tl
+ \tl_set:Nf #3 { \tl_head:N \l_@@_spec_tl } %@todo test expansion
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \section{Entry lookup}
+%
+%
+% \begin{variable}{\g_@@_tmp_tl}
+% This scratch variable is used to escape the groups of mapping
+% constructs like \cs{prop_map_inline:Nn}.
+% \begin{macrocode}
+\tl_new:N \g_@@_tmp_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_opt_bool}
+% This scratch variable is used to signal if a suitable option was
+% found when searching for a match based on user input. Since it is
+% used inside two inlined mappings, it is most straightforward for
+% all assignments to be global.
+% \begin{macrocode}
+\bool_new:N \g_@@_opt_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\termmenu_entry:NnN, \termmenu_entry:NVN}
+% Using the menu in |#1|, find a match for |#2| and stick that match
+% in |#3|.
+% \begin{macrocode}
+\cs_new_nopar:Nn \termmenu_entry:NnN
+ {
+ \prop_map_inline:Nn #1
+ {
+% \end{macrocode}
+% If the input value is in the list of synonyms, set our output value
+% to the full list, indicate that we've found a match, and break out
+% of the loop. Note that we use global variables to free ourselves
+% from the confines of the group.
+% \begin{macrocode}
+ \clist_if_in:nnT {##1} {#2}
+ {
+ \tl_gset:Nn \g_@@_tmp_tl {##1}
+ \bool_gset_true:N \g_@@_opt_bool
+ \prop_map_break:
+ }
+ }
+% \end{macrocode}
+% If we haven't set \cs{g_@@_opt_bool} by the time we've finished
+% looking, we never found anything. Set |#3| to \cs{q_no_value} to
+% indicate the lack of a match and reset the value of
+% \cs{g_@@_opt_bool}.
+% \begin{macrocode}
+ \bool_if:NTF \g_@@_opt_bool
+ { \tl_set_eq:NN #3 \g_@@_tmp_tl }
+ { \tl_set:Nn #3 { \q_no_value } }
+ \bool_gset_false:N \g_@@_opt_bool
+ }
+\cs_generate_variant:Nn \termmenu_entry:NnN { NVN }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \section{Retrieving input}
+%
+%
+% \begin{variable}{\l_termmenu_prompt_tl}
+% This public variable contains the text to be used as a prompt when
+% displaying a menu. It is given a reasonable default.
+% \begin{macrocode}
+\tl_new:N \l_termmenu_prompt_tl
+\tl_set:Nn \l_termmenu_prompt_tl
+ { The~following~commands~are~available: }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\termmenu_prompt:NN, \termmenu_prompt:N}
+% This function is a little interesting. In order to keep the
+% end-user's interface clean, we can't simply prompt for the
+% destination variable. Say, if the destination variable were
+% something like \cs{l__some_confusing_variable_name}, this would
+% cause \TeX{} to display that confusing variable name to the
+% end-user as part of the prompt. Instead, we ask for |#2|
+% (starting a new group to avoid clobbering any existing definition)
+% and \TeX{} puts the user's input in |#2|. Now we have to get this
+% value outside the group and into |#1|. The
+% |\expandafter\endgroup| trick works nicely here (and is the
+% official\footnote{\url{http://tex.stackexchange.com/a/246542}} way
+% to do this).
+% \begin{macrocode}
+\cs_new_nopar:Nn \termmenu_prompt:NN
+ {
+ \group_begin:
+ \termmenu_prompt:N #2
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn #1 #2
+ }
+\cs_new_nopar:Nn \termmenu_prompt:N
+ { \ior_get_str:NN \c_term_ior #1 }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \section{Names}
+%
+%
+% \begin{variable}{\g_@@_names_prop}
+% Globally define the property list to store menu names.
+% \begin{macrocode}
+\prop_new:N \g_@@_names_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\termmenu_get_name:NN}
+% \begin{macrocode}
+\cs_new_nopar:Nn \termmenu_get_name:NN
+ { \prop_get:NnN \g__termmenu_names_prop {#1} #2 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\termmenu_set_name:Nn}
+% This function names a menu. An entry is placed in
+% \cs{g_@@_names_prop} with the menu |#1| as a key and its name |#2|
+% as the value.
+% \begin{macrocode}
+\cs_new_nopar:Nn \termmenu_set_name:Nn
+ { \prop_put:Nnn \g__termmenu_names_prop {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \section{Display}
+%
+%
+%
+% \begin{macro}{\termmenu_display:N}
+% Perhaps the only truly complicated part of this package is this
+% function. Let's generate some new terminal output variants to
+% make our lives easier.
+% \begin{macrocode}
+\cs_generate_variant:Nn \iow_term:n { V }
+\cs_generate_variant:Nn \msg_term:n { V }
+\cs_new_nopar:Nn \termmenu_display:N
+ {
+% \end{macrocode}
+% First, we retrieve the name of the menu. If it does not
+% exist, print a generic \enquote{Menu} header. If it does
+% exist, use the menu's title as the header.
+% \begin{macrocode}
+ \termmenu_get_name:NN #1 \l_@@_spec_tl
+ \quark_if_no_value:NTF \l_@@_spec_tl
+ { \msg_term:n { Menu } }
+ { \msg_term:V \l_@@_spec_tl }
+% \end{macrocode}
+% Display the prompt. Note that \cs{iow_term:n} without an argument
+% will simply output one blank line.
+% \begin{macrocode}
+ \iow_term:n { }
+ \iow_term:V \l_termmenu_prompt_tl
+ \iow_term:n { }
+% \end{macrocode}
+% We want to display each option with its help text in a way that is
+% easy to read.
+% \begin{macrocode}
+ \prop_map_inline:Nn #1
+ {
+% \end{macrocode}
+% For each entry |##1| in the menu |#1|, send wrapped output to the
+% terminal that contains the entry \cs{l_@@_tmp_clist} indented by
+% four spaces, a new line, and the documentation \cs{l_@@_tmp_tl}, all
+% wrapped with a running indent of eight spaces. Note that since we
+% introduce a new line with the |\\| macro in \cs{iow_wrap:nnnN}, we
+% get the first indentation of the documentation for free.
+% \begin{macrocode}
+ \clist_set:Nn \l_@@_tmp_clist {##1}
+ \termmenu_doc:NnN #1 {##1} \l_@@_tmp_tl
+ \iow_wrap:nnnN
+ {
+ \prg_replicate:nn {4} { \iow_char:N \ }
+ > ~ \clist_use:Nn \l_@@_tmp_clist { ,~ } \\
+ \tl_use:N \l_@@_tmp_tl
+ } { \prg_replicate:nn {8} { \ } } { } \iow_term:n
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/generic/termmenu/termmenu.ins b/macros/generic/termmenu/termmenu.ins
new file mode 100644
index 0000000000..7042b88341
--- /dev/null
+++ b/macros/generic/termmenu/termmenu.ins
@@ -0,0 +1,38 @@
+% -*- tex -*-
+%%
+%% Copyright (C) 2015 Sean Allred
+%%
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.3c or later is part of all distributions of
+%% LaTeX version 2008/05/04 or later.
+%%
+
+\input l3docstrip.tex \relax
+\keepsilent
+
+\usedir{tex/generic/termmenu}
+
+\generate{
+ \file{termmenu.tex}{
+ \from{termmenu.dtx}{package}}}
+
+\Msg{*********************************************************}
+\Msg{*}
+\Msg{* To finish the installation, you have to move the}
+\Msg{* following file into a directory searched by TeX:}
+\Msg{*}
+\Msg{* \space\space termmenu.tex}
+\Msg{*}
+\Msg{* To produce the documentation, run the file termmenu.dtx}
+\Msg{* through LaTeX.}
+\Msg{*}
+\Msg{* Happy TeXing!}
+\Msg{*********************************************************}
+
+\endbatchfile
diff --git a/macros/generic/termmenu/termmenu.pdf b/macros/generic/termmenu/termmenu.pdf
new file mode 100644
index 0000000000..decef56323
--- /dev/null
+++ b/macros/generic/termmenu/termmenu.pdf
Binary files differ