% censor.sty
\def\censorversionnumber{4.3}
\def\censorversiondate{2023/06/05}
\ProvidesPackage{censor}
[\censorversiondate\ \censorversionnumber\
 Provides capability for redaction of sensitive information]
%
% This work may be distributed and/or modified under the 
% conditions of the LaTeX Project Public License, either version 1.3 
% 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.3 or later is part of all distributions of LaTeX 
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is Steven B. Segletes.

\usepackage{pbox}

\newlength\censorruledepth
\newlength\censorruleheight
\newif\ifcensor

\censorruledepth=-0.3ex% -0.3ex DEFAULT
\censorruleheight=2.1ex%  2.1ex DEFAULT
\def\censordot{\censor{.}}% versus \def\censordot{.}%
\censortrue% DEFAULT

\newcommand\censorrule[1]{\protect\rule[\censorruledepth]{#1}{\censorruleheight}}

\DeclareRobustCommand\censor{\@ifstar{\@cenlen}{\@cenword}}
  \newcommand\@cenlen[1]{\censorrule{#1 ex}}
  \newcommand\@cenword[1]{\censorrule{\widthofpbox{#1}}}

\newcommand\un@censor{\@ifstar{\un@cenlen}{\un@cenword}}
  \newcommand\un@cenlen[1]{\protect\underline{\hspace{#1 ex}}}
  \newcommand\un@cenword[1]{#1}

\newcommand\StopCensoring{%
           \censorfalse%
           \let\censor\un@censor%
           \let\censorbox\un@censorbox%
           \renewcommand\censpace{ }%
}
\newcommand\RestartCensoring{%
           \censortrue%
           \renewcommand\censor{\@ifstar{\@cenlen}{\@cenword}}%
           \renewcommand\censorbox{\@ifstar{\censor@dim}{\censor@box}}%
           \let\censpace\sv@censpace%
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%

\usepackage{tokcycle}[2021/03/10]


\def\@dump#1{\if\relax\detokenize\expandafter{#1}\relax\else
  \addcytoks[1]{\expandafter\censor\expandafter{#1}}\fi\def#1{}}
\def\@append#1#2{\tc@defx#1{#1#2}}

\def\censpace{%
  \leaders\hrule height \dimexpr\censorruleheight+\censorruledepth\relax
    depth -\censorruledepth
  \hskip\fontdimen2\font plus \fontdimen3\font minus \fontdimen4\font
  \mbox{}}
\let\sv@censpace\censpace

\long\def\blackout#1{\protect\blackoutenv#1\endblackoutenv}
\long\def\xblackout#1{\protect\xblackoutenv#1\endxblackoutenv}

\newif\ifexpandarg

\xtokcycleenvironment\blackoutenv
  {\ifx.##1\@dump\censored@word\addcytoks[1]{\censordot}\else
    \ifnum\catcode`##1=3\relax\@dump\censored@word\addcytoks{##1}\else
    \ifnum\catcode`##1=4\relax\@dump\censored@word\addcytoks{##1}\else
    \ifnum\catcode`##1=7\relax\@dump\censored@word\addcytoks{##1}\else 
    \ifnum\catcode`##1=8\relax\@dump\censored@word\addcytoks{##1}\else
    \@append\censored@word{##1}%
    \tcpeek\@next\ifx\@next\@tcEscapeptr\@dump\censored@word\fi
    \fi\fi\fi\fi\fi}
  {\tctestifcon\ifexpandarg{\expandafter\processtoks\expandafter
    {\expanded{##1}}\@dump\censored@word}{\groupedcytoks{\processtoks{##1}%
    \@dump\censored@word}}\expandargfalse}
  {\tctestifx{~##1}{\@append\censored@word{##1}}%
    {\tctestifx{\expanded##1}{\@dump\censored@word\expandargtrue}%
    {\test@chars{##1}\if@char\if@mathgreek
    \@append\censored@word{\ensuremath{##1}}\else
    \@append\censored@word{##1}\fi\else
    \test@accents{##1}\if@accent\@append\censored@word{##1}\tcpop\tc@popped
    \expandafter\@append\expandafter\censored@word\expandafter{\tc@popped}%
    \else\@dump\censored@word\addcytoks{##1}\fi\fi}}}
  {\@dump\censored@word\addcytoks{##1}}  
  {\stripgroupingtrue\def\censored@word{}\sloppy}
  {\@dump\censored@word}

\xtokcycleenvironment\xblackoutenv
  {\ifx.##1\@dump\censored@word\addcytoks[1]{\censordot}\else
    \ifnum\catcode`##1=3\relax\@dump\censored@word\addcytoks{##1}\else
    \ifnum\catcode`##1=4\relax\@dump\censored@word\addcytoks{##1}\else
    \ifnum\catcode`##1=7\relax\@dump\censored@word\addcytoks{##1}\else 
    \ifnum\catcode`##1=8\relax\@dump\censored@word\addcytoks{##1}\else
    \@append\censored@word{##1}%
    \tcpeek\@next\ifx\@next\@tcEscapeptr\@dump\censored@word\fi
    \fi\fi\fi\fi\fi}
  {\tctestifcon\ifexpandarg{\expandafter\processtoks\expandafter
    {\expanded{##1}}\@dump\censored@word}{\groupedcytoks{\processtoks{##1}%
    \@dump\censored@word}}\expandargfalse}
  {\tctestifx{~##1}{\@append\censored@word{##1}}%
    {\tctestifx{\expanded##1}{\@dump\censored@word\expandargtrue}%
    {\test@chars{##1}\if@char\if@mathgreek
    \@append\censored@word{\ensuremath{##1}}\else
    \@append\censored@word{##1}\fi\else
    \test@accents{##1}\if@accent\@append\censored@word{##1}\tcpop\tc@popped
    \expandafter\@append\expandafter\censored@word\expandafter{\tc@popped}%
    \else\@dump\censored@word\addcytoks{##1}\fi\fi}}}
  {\@dump\censored@word\addcytoks{\ccenspace}}  
  {\stripgroupingtrue\def\censored@word{}\sloppy}
  {\@dump\censored@word}

\newcommand\ccenspace{\ifhmode\ifmmode\else\censpace\fi\fi}

\newif\if@char
\newif\if@mathgreek
\newif\if@accent

\newcommand\test@chars[1]{\@charfalse\@mathgreekfalse
  \ifx\$#1\@chartrue\else
  \ifx\&#1\@chartrue\else
  \ifx\##1\@chartrue\else
  \ifx\%#1\@chartrue\else
  \ifx\_#1\@chartrue\else
  \ifx\o#1\@chartrue\else
  \ifx\O#1\@chartrue\else
  \ifx\oe#1\@chartrue\else
  \ifx\OE#1\@chartrue\else
  \ifx\aa#1\@chartrue\else
  \ifx\AA#1\@chartrue\else
  \ifx\ae#1\@chartrue\else
  \ifx\AE#1\@chartrue\else
  \ifx\l#1\@chartrue\else
  \ifx\L#1\@chartrue\else
  \ifcensormathgreek\test@mathgreek{#1}\if@mathgreek\@chartrue\fi\fi%
  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
}

\newcommand\test@mathgreek[1]{%
  \ifx\alpha#1\@mathgreektrue\else
  \ifx\beta#1\@mathgreektrue\else
  \ifx\gamma#1\@mathgreektrue\else
  \ifx\delta#1\@mathgreektrue\else
  \ifx\epsilon#1\@mathgreektrue\else
  \ifx\varepsilon#1\@mathgreektrue\else
  \ifx\zeta#1\@mathgreektrue\else
  \ifx\eta#1\@mathgreektrue\else
  \ifx\theta#1\@mathgreektrue\else
  \ifx\vartheta#1\@mathgreektrue\else
  \ifx\iota#1\@mathgreektrue\else
  \ifx\kappa#1\@mathgreektrue\else
  \ifx\lambda#1\@mathgreektrue\else
  \ifx\mu#1\@mathgreektrue\else
  \ifx\nu#1\@mathgreektrue\else
  \ifx\xi#1\@mathgreektrue\else
  \ifx\pi#1\@mathgreektrue\else
  \ifx\varpi#1\@mathgreektrue\else
  \ifx\rho#1\@mathgreektrue\else
  \ifx\varrho#1\@mathgreektrue\else
  \ifx\sigma#1\@mathgreektrue\else
  \ifx\varsigma#1\@mathgreektrue\else
  \ifx\tau#1\@mathgreektrue\else
  \ifx\upsilon#1\@mathgreektrue\else
  \ifx\phi#1\@mathgreektrue\else
  \ifx\varphi#1\@mathgreektrue\else
  \ifx\chi#1\@mathgreektrue\else
  \ifx\psi#1\@mathgreektrue\else
  \ifx\omega#1\@mathgreektrue\else
  \ifx\Gamma#1\@mathgreektrue\else
  \ifx\Delta#1\@mathgreektrue\else
  \ifx\Theta#1\@mathgreektrue\else
  \ifx\Lambda#1\@mathgreektrue\else
  \ifx\Xi#1\@mathgreektrue\else
  \ifx\Pi#1\@mathgreektrue\else
  \ifx\Sigma#1\@mathgreektrue\else
  \ifx\Upsilon#1\@mathgreektrue\else
  \ifx\Phi#1\@mathgreektrue\else
  \ifx\Psi#1\@mathgreektrue\else
  \ifx\Omega#1\@mathgreektrue
  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
}

\newcommand\test@accents[1]{\@accentfalse
  \ifx\`#1\@accenttrue\else
  \ifx\=#1\@accenttrue\else
  \ifx\'#1\@accenttrue\else
  \ifx\.#1\@accenttrue\else
  \ifx\^#1\@accenttrue\else
  \ifx\"#1\@accenttrue\else
  \ifx\u#1\@accenttrue\else
  \ifx\d#1\@accenttrue\else
  \ifx\v#1\@accenttrue\else
  \ifx\b#1\@accenttrue\else
  \ifx\H#1\@accenttrue\else
  \ifx\t#1\@accenttrue\else
  \ifx\~#1\@accenttrue\else
  \ifx\c#1\@accenttrue
  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
}


\newif\ifcensormathgreek

\censormathgreektrue

%%%%%%%%%%%%%%%%%%%%%%%%%%

\DeclareRobustCommand\censorbox{\@ifstar{\censor@dim}{\censor@box}}
  \newcommand\censor@dim[4][]{{#1%
                      \rule[-#4\baselineskip]{#2ex}{#3\baselineskip}}}
  \newcommand\censor@box[2][]{#1\setbox0\hbox{#2}%
                      \rule[-\the\dp0]{\the\wd0}{\the\ht0+\the\dp0}}

\newcommand\un@censorbox{\@ifstar{\un@censor@dim}{\un@censor@box}}
  \newcommand\un@censor@dim[4][]{{#1%
                      \fbox{\rule[-#4\baselineskip]{0ex}{#3\baselineskip}
                      \rule{#2ex}{0ex}}}}
  \newcommand\un@censor@box[2][]{#1#2}

\endinput

VERSION:
1.00 - Initial release
2.00 - Added \blackout
2.10 - Allowed \blackout to cross paragraph boundaries with use of
       \bpar. Stopped censoring periods, in order to preserve
       end-of-sentence spacing, which differs from inter-word spacing.
3.00 - \censorbox introduced to handle figures, tables, etc.
3.10 - Made \blackout work with \par in argument.  Introduced 
       \xblackout
3.20 - Specify depth/height of censor rule.  Introduced \def\censordot{}
3.21 - Fixed bug regarding \xblackout rules remaining after a
       \StopCensoring
3.22 - changed `\if to \ifx in definition of \bl@t, to handle macros like \%
4.0  - Recast \blackout and \xblackout in terms of tokcycle environments
     - Introduced \blackoutenv...\endblackoutenv and 
       \xblackoutenv...\endxblackoutenv
4.1  - Added automatic support for censoring character macros such as \$, 
       etc. and character accents such as \', etc. See \test@chars and 
       \test@accents for details.
     - Fixed bug in which \expanded arguments remained inside a TeX group
4.2  - Extended capability (via switch) to autocensor Greek math (see 
       \test@mathgreek).  
     - In (x)blackout macros/environments, auto-intercepts catcode 3,4,7 
       and 8 characters and passes them through unchanged, to help facilitate 
       general censoring of math and tabular content.
     - Switched \censpace, used in \xblackout, to a leader rather than a
       lapped rule, which allows automatically for glue associated with
       \fontdim.
     - Made \censor and \censorbox robust, which will allow censoring 
       to occur across, for example, section headers, tocs, etc.
     - Revisited the documentation, bringing it up to date.
4.3  - Introduced \ifcensor condition, default true, set false via
       \StopCensoring and set true via \RestartCensoring.  Thus, the current
       state of censoring can be determined by the user.