Changes in / [44a88528:6c6e36c]


Ignore:
Location:
doc/theses/andrew_beach_MMath
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • doc/theses/andrew_beach_MMath/cfalab.sty

    r44a88528 r6c6e36c  
    11% Package for CFA Research Lab.
    22%
    3 % Made by combining and updating various macro files people had made.
    4 %
    5 % Internal commands are prefixed with "\cfalab@".
     3% Made by combining and updating
    64
    75% I don't know what the oldest LaTeX2e version with everything needed is.
    86\NeedsTeXFormat{LaTeX2e}
    9 \ProvidesPackage{cfalab}[2020/03/24 v0.1 CFA Laboratory LaTeX Tools]
     7\ProvidesPackage{cfalab}[2020/03/09 v0.1 CFA Laboratory LaTeX Tools]
    108
    119% Other packages required.
    1210\RequirePackage{etoolbox}
    13 \RequirePackage{listings}
    1411\RequirePackage{xspace}
    1512
     
    1916% by a star (which the command will consume) to disable this behaviour.
    2017
    21 % \newsymbolcmd{<command>}{<replacement text>}
    22 %     Defines <command> to be a symbol that has the given <replacement text>.
     18% \newsymbolcmd{<command>}{<expansion>}
     19%    Defines <command> to be a symbol that has the given <expansion>.
    2320\newrobustcmd*\newsymbolcmd[2]{\newrobustcmd{#1}{\cfalab@symbol{#2}}}
    2421\def\cfalab@symbol#1{\@ifnextchar*{#1\cfalab@eatstar}{#1\xspace}}
     
    2825\newsymbolcmd\CFA{\textsf{C}\raisebox{\depth}{\rotatebox{180}{\textsf{A}}}}
    2926
    30 % The CFA listings language. Based off of ANCI C and including GCC extensions.
    31 % The UW dialect is the default (and only so far) dialect of the language.
    32 \lstdefinelanguage[UW]{CFA}[ANSI]{C}{
    33     morekeywords={_Alignas, _Alignof, __alignof, __alignof__, asm, __asm,
    34         __asm__, __attribute, __attribute__, auto, _Bool, catch, catchResume,
    35         choose, _Complex, __complex, __complex__, __const, __const__,
    36         coroutine, disable, dtype, enable, exception, __extension__,
    37         fallthrough, fallthru, finally, __float80, float80, __float128,
    38         float128, forall, ftype, generator, _Generic, _Imaginary, __imag,
    39         __imag__, inline, __inline, __inline__, __int128, int128, __label__,
    40         monitor, mutex, _Noreturn, one_t, or, otype, restrict, resume,
    41         __restrict, __restrict__, __signed, __signed__, _Static_assert,
    42         suspend, thread, _Thread_local, throw, throwResume, timeout, trait,
    43         try, ttype, typeof, __typeof, __typeof__, virtual, __volatile,
    44         __volatile__, waitfor, when, with, zero_t
    45     },
    46     moredirectives={defined,include_next},
    47 }
    48 \lstset{defaultdialect={[UW]CFA}}
    49 
    50 % The cfalab style defines some common settings useful in different languages.
    51 \lstdefinestyle{cfalab}{%
    52     columns=fullflexible,
    53     basicstyle=\linespread{0.9}\tt,
    54     stringstyle=\tt,
    55 }
    56 
    57 % \code*[<escape character>]{<code>}
    58 %     Use the listings package to format a snipit of <code>.
    59 %     The <escape character> must be a character that does not appear in
    60 %     <code> and defaults to a backtick.
    61 \newcommand*\codeC[2][\`]{\lstinline[language=C]#1#2#1}
    62 \newcommand*\codeCFA[2][\`]{\lstinline[language=CFA]#1#2#1}
    63 
    64 % \settextunderscore{(new|old)}
    65 %     Redefines the underscore either as a new repersentation or the old one.
    66 %     Not that some other packages (ex. hyperref) can override this. Set it
    67 %     up after loading them.
    68 \let\cfalab@textunderscore@old=\textunderscore
    69 \newcommand\cfalab@textunderscore@new{%
    70     \leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}}
    71 \newcommand\settextunderscore[1]{%
    72     \renewcommand\textunderscore{\csuse{cfalab@textunderscore@#1}}}
     27\newcommand\codeC[1]{\texttt{#1}}
    7328
    7429\endinput
  • doc/theses/andrew_beach_MMath/thesis.tex

    r44a88528 r6c6e36c  
    11% Main tex file for thesis document.
    22\documentclass[digital]{uw-ethesis}
     3\usepackage{comment}
     4\usepackage{fullpage}
     5\setlength{\textheight}{8.75in}
     6\renewcommand\labelitemi{\footnotesize$\bullet$} % shrink bullet for level 1 itemize
     7\usepackage{lmodern}                            % bold typewriter font
     8
     9\usepackage{listings}                           % format program code
     10% Default underscore is too low and wide. Cannot use lstlisting "literate" as replacing underscore
     11% removes it as a variable-name character so keywords in variables are highlighted. MUST APPEAR
     12% AFTER HYPERREF.
     13%\DeclareTextCommandDefault{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.1ex}}}
     14\renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}}
     15% parindent is relative, i.e., toggled on/off in environments like itemize, so store the value for
     16% use rather than use \parident directly.
     17\newlength{\parindentlnth}
     18\setlength{\parindentlnth}{\parindent}
     19
     20% CFA programming language, based on ANSI C (with some gcc additions)
     21\lstdefinelanguage{CFA}[ANSI]{C}{
     22        morekeywords={
     23                _Alignas, _Alignof, __alignof, __alignof__, asm, __asm, __asm__, __attribute, __attribute__,
     24                auto, _Bool, catch, catchResume, choose, _Complex, __complex, __complex__, __const, __const__,
     25                coroutine, disable, dtype, enable, exception, __extension__, fallthrough, fallthru, finally,
     26                __float80, float80, __float128, float128, forall, ftype, generator, _Generic, _Imaginary, __imag, __imag__,
     27                inline, __inline, __inline__, __int128, int128, __label__, monitor, mutex, _Noreturn, one_t, or,
     28                otype, restrict, resume, __restrict, __restrict__, __signed, __signed__, _Static_assert, suspend, thread,
     29                _Thread_local, throw, throwResume, timeout, trait, try, ttype, typeof, __typeof, __typeof__,
     30                virtual, __volatile, __volatile__, waitfor, when, with, zero_t},
     31        moredirectives={defined,include_next},
     32        % replace/adjust listing characters that look bad in sanserif
     33        literate={-}{\makebox[1ex][c]{\raisebox{0.5ex}{\rule{0.8ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1
     34                {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 % {`}{\ttfamily\upshape\hspace*{-0.1ex}`}1
     35                {<}{\textrm{\textless}}1 {>}{\textrm{\textgreater}}1
     36                {<-}{$\leftarrow$}2 {=>}{$\Rightarrow$}2 {->}{\makebox[1ex][c]{\raisebox{0.5ex}{\rule{0.8ex}{0.075ex}}}\kern-0.2ex{\textrm{\textgreater}}}2,
     37}
     38
     39\lstset{
     40language=CFA,
     41columns=fullflexible,
     42basicstyle=\linespread{0.9}\tt,                 % reduce line spacing and use sanserif font
     43stringstyle=\tt,                                % use typewriter font
     44tabsize=5,                                      % N space tabbing
     45xleftmargin=\parindentlnth,                     % indent code to paragraph indentation
     46%mathescape=true,                               % LaTeX math escape in CFA code $...$
     47escapechar=\$,                                  % LaTeX escape in CFA code
     48keepspaces=true,
     49showstringspaces=false,                         % do not show spaces with cup
     50showlines=true,                                 % show blank lines at end of code
     51aboveskip=4pt,                                  % spacing above/below code block
     52belowskip=3pt,
     53moredelim=**[is][\color{red}]{`}{`},
     54}% lstset
     55
     56\lstnewenvironment{cfa}[1][]
     57{\lstset{#1}}
     58{}
     59% inline code @...@
    360
    461% Commands used in documenting how to use the template. To remove.
     
    2481
    2582% The \phantomsection is used to help the hyperref package create links.
    26 
    27 % Post hyperref setup from cfalab.
    28 \lstset{style=cfalab}
    29 \settextunderscore{new}
    3083
    3184% Maybe only package that should be loaded after the hyperref package.
     
    67120               each dimensional component is determined by a random process}
    68121}
     122
     123% Must be here of cause problems with glossaries-extra.
     124\lstMakeShortInline$%
    69125
    70126% Generate the glossaries defined above.
  • doc/theses/andrew_beach_MMath/unwinding.tex

    r44a88528 r6c6e36c  
    11\chapter{Unwinding in \CFA}
    22
    3 Stack unwinding is the process of removing things from the stack. Within
    4 functions and on function return this is handled directly by the code in the
    5 function itself as it knows exactly what is on the stack just from the
    6 current location in the function. Unwinding across stack frames means that it
    7 is no longer knows exactly what is on the stack or even how much of the stack
    8 needs to be removed.
     3When a function returns, a \emph{single} stack frame is unwound, removing the
     4function's parameters and local variables, and control continues in the
     5function's caller using the caller's stack frame.  When an exception is raised,
     6\emph{multiple} stack frames are unwound, removing the function parameters and
     7local variables for called functions from the exception raise-frame to the
     8exception catch-frame.
    99
    10 Even this is fairly simple if nothing needs to happen when the stack unwinds.
    11 Traditional C can unwind the stack by saving and restoring state (with
    12 \codeC{setjmp} \& \codeC{longjmp}). However many languages define actions that
    13 have to be taken when something is removed from the stack, such as running
    14 a variable's destructor or a \codeCFA{try} statement's \codeCFA{finally}
    15 clause. Handling this requires walking the stack going through each stack
    16 frame.
     10Unwinding multiple levels is simple for a programming languages without object
     11destructors or block finalizers because a direct transfer is possible from the
     12current stack frame to a prior stack frame, where control continues at a
     13location within the prior caller's function. For example, C provides non-local
     14transfer using $longjmp$, which stores a function's state including its
     15frame pointer and program counter, and simply reloads this information to
     16continue at this prior location on the stack.
    1717
    18 For exceptions, this means everything from the point the exception is raised
    19 to the point it is caught, while checking each frame for handlers during the
    20 stack walk to find out where it should be caught. This is where the most of
    21 the expense and complexity of exception handling comes from.
     18For programming languages with object destructors or block finalizers it is
     19necessary to walk the stack frames from raise to catch, checking for code that
     20must be executed as part of terminating each frame. Walking the stack has a
     21higher cost, and necessary information must be available to detect
     22destructors/finalizers and call them.
    2223
    23 To do all of this we use libunwind, a low level library that provides tools
    24 for stack walking and stack unwinding. What follows is an overview of all the
    25 relivant features of libunwind and then how \CFA uses them to implement its
    26 exception handling.
     24A powerful package to provide stack-walking capabilities is $libunwind$,
     25which is used in this work to provide exception handling in \CFA. The following
     26explains how $libunwind$ works and how it is used.
     27
     28% Stack unwinding is the process of removing things from the stack from outside
     29% the functions there. In languages that don't provide a way to guaranty that
     30% code will run when the program leaves a scope or finishes a function, this
     31% can be relatively trivial. C does this with $longjmp$ by setting the
     32% stack pointer and a few other registers.
    2733
    2834\section{libunwind Usage}
    2935
    30 \CFA uses two primary functions in libunwind to create most of its
    31 exceptional control-flow: \codeC{_Unwind_RaiseException} and
    32 \codeC{_Unwind_ForcedUnwind}.
     36\CFA uses two primary functions in $libunwind$ to create most of its
     37exceptional control-flow: $_Unwind_RaiseException$ and $_Unwind_ForcedUnwind$.
    3338Their operation is divided into two phases: search and clean-up. The search
    3439phase -- phase 1 -- is used to scan the stack but not unwinding it. The
    3540clean-up phase -- phase 2 -- is used for unwinding.
    3641
     42% Somewhere around here I need to talk about the control structures.
     43% $_Unwind_Exception$ is used to carry the API's universal data. Some
     44% of this is internal, other fields are used to communicate between different
     45% exception handling mechanisms in different runtimes.
     46% $_Unwind_Context$ is an opaque data structure that is used to pass
     47% information to helper functions.
     48
    3749The raise-exception function uses both phases. It starts by searching for a
    3850handler, and if found, performs a clean-up phase to unwind the stack to the
    3951handler. If a handler is not found, control returns allowing the
    40 exception-handling policy for unhandled exception to be executed. During both
     52exception-handling policy for unhandled exception to be executed.  During both
    4153phases, the raise-exception function searches down the stack, calling each
    4254function's \emph{personality function}.
     
    4456A personality function performs three tasks, although not all have to be
    4557present. The tasks performed are decided by the actions provided.
    46 \codeC{_Unwind_Action} is a bitmask of possible actions and an argument of
    47 this type is passed into the personality function.
     58% Something argument something bitmask.
    4859\begin{itemize}
    49 \item\codeC{_UA_SEARCH_PHASE} is passed in search phase and tells the
    50 personality function to check for handlers. If there is a handler in this
    51 stack frame, as defined by the language, the personality function should
    52 return \codeC{_URC_HANDLER_FOUND}. Otherwise it should return
    53 \codeC{_URC_CONTINUE_UNWIND}.
    54 \item\codeC{_UA_CLEANUP_PHASE} is passed in during the clean-up phase and
    55 means part or all of the stack frame is removed. The personality function
    56 should do whatever clean-up the language defines
    57 (such as running destructors/finalizers) and then generally returns
    58 \codeC{_URC_CONTINUE_UNWIND}.
    59 \item\codeC{_UA_HANDLER_FRAME} means the personality function must install
    60 a handler. It is also passed in during the clean-up phase and is in addition
    61 to the clean-up action. libunwind provides several helpers for the personality
     60\item$_UA_SEARCH_PHASE$ is called during the clean-up phase and means search
     61for handlers. If a hander is found, the personality function should return
     62$_URC_HANDLER_FOUND$, otherwise it returns $_URC_CONTINUE_UNWIND$.
     63{\color{red}What is the connection between finding the handler and the
     64personality function?}
     65\item$_UA_CLEANUP_PHASE$ is passed in during the clean-up phase and means part
     66or all of the stack frame is removed. The personality function should do
     67whatever clean-up the language defines (such as running destructors/finalizers)
     68and then generally returns $_URC_CONTINUE_UNWIND$.
     69\item$_UA_HANDLER_FRAME$ means the personality function must install a
     70handler. It is also passed in during the clean-up phase and is in addition to
     71the clean-up action. $libunwind$ provides several helpers for the personality
    6272function here. Once it is done, the personality function must return
    63 \codeC{_URC_INSTALL_CONTEXT}.
     73$_URC_INSTALL_CONTEXT$.
    6474\end{itemize}
    65 The personality function is given a number of other arguments. Some are for
    66 compatability and there is the \codeC{struct _Unwind_Context} pointer which
    67 passed to many helpers to get information about the current stack frame.
    6875
    69 Forced-unwind only performs the clean-up phase. It takes three arguments:
    70 a pointer to the exception, a pointer to the stop function and a pointer to
    71 the stop parameter. It does most of the same things as phase two of
    72 raise-exception but with some extras.
    73 The first it passes in an extra action to the personality function on each
    74 stack frame, \codeC{_UA_FORCE_UNWIND}, which means a handler cannot be
    75 installed.
     76Forced unwind only performs the clean-up phase. It is similar to the phase 2
     77section of raise exception with a few changes. A simple difference is that it
     78passes in an extra action to the personality function $_UA_FORCE_UNWIND$, which
     79means a handler cannot be installed. The most difference significant is the
     80addition of the $stop$ function, which is passed in as an argument to forced
     81unwind.
    7682
    77 The big change is that forced-unwind calls the stop function. Each time it
    78 steps into a frame, before calling the personality function, it calls the
    79 stop function. The stop function receives all the same arguments as the
    80 personality function will and the stop parameter supplied to forced-unwind.
     83The $stop$ function is similar to a personality function. It takes an extra
     84argument: a $void$ pointer passed into force unwind. It may return
     85$_URC_NO_REASON$ to continue unwinding or it can transfer control out of the
     86unwind code using its own mechanism.
     87% Is there a reason that NO_REASON is used instead of CONTINUE_UNWIND?
     88The $stop$ function is called for each stack frame and at the end of the
     89stack. In a stack frame, it is called before the personality routine with the
     90same arguments (except for the extra $void$ pointer). At the end of the stack,
     91the arguments are mostly the same, except the stack pointer stored in the
     92context is set to null. Because of this change, both GCC and Clang add an extra
     93action in this case $_UA_END_OF_STACK$.  The $stop$ function may not return at
     94the end of the stack.
    8195
    82 The stop function is called one more time at the end of the stack after all
    83 stack frames have been removed. By the standard API this is marked by setting
    84 the stack pointer inside the context passed to the stop function. However both
    85 GCC and Clang add an extra action for this case \codeC{_UA_END_OF_STACK}.
     96{\color{red}This needs work as I do not understand all of it.}
    8697
    87 Each time function the stop function is called it can do one or two things.
    88 When it is not the end of the stack it can return \codeC{_URC_NO_REASON} to
    89 continue unwinding.
    90 % Is there a reason that NO_REASON is used instead of CONTINUE_UNWIND?
    91 Its only other option is to use its own means to transfer control elsewhere
    92 and never return to its caller. It may always do this and no additional tools
    93 are provided to do it.
    9498
    9599\section{\CFA Implementation}
    96100
    97 To use libunwind, \CFA provides several wrappers, its own storage,
    98 personality functions, and a stop function.
     101To use $libunwind$, \CFA provides several wrappers, its own storage,
     102personality functions, and a $stop$ function.
    99103
    100104The wrappers perform three tasks: set-up, clean-up and controlling the
    101105unwinding. The set-up allocates a copy of the \CFA exception into a handler to
    102 control its lifetime, and stores it in the exception context. Clean-up -- run
     106control its lifetime, and stores it in the exception context.  Clean-up -- run
    103107when control exits a catch clause and returns to normal code -- frees the
    104108exception copy.
     
    106110% runtime/language features. Also the exception context is global.
    107111
    108 The core control code is called every time a throw -- after set-up -- or
    109 re-throw is run. It uses raise-exception to search for a handler and to run it
    110 if one is found. If no handler is found and raise-exception returns then
    111 forced-unwind is called to run all destructors on the stack before terminating
    112 the process.
     112The control code in the middle {\color{red}(In the middle of what?)} is run
     113every time a throw or re-throw is called. It uses raise exception to search for
     114a handler and to run it, if one is found. Otherwise, it uses forced unwind to
     115unwind the stack, running all destructors, before terminating the process.
    113116
    114 The stop function is very simple. It checks the end of stack flag to see if
    115 it is finished unwinding. If so, it calls \codeC{exit} to end the process,
    116 otherwise it returns with no-reason to continue unwinding.
     117The $stop$ function is very simple. It checks the end of stack flag to see if
     118it is finished unwinding. If so, it calls $exit$ to end the process, otherwise
     119it tells the system {\color{red}(What system?)} to continue unwinding.
    117120% Yeah, this is going to have to change.
    118121
     
    120123about the function by scanning the LSDA (Language Specific Data Area). This
    121124step allows a single personality function to be used for multiple functions and
    122 let that personaliity function figure out exactly where in the function
    123 execution was, what is currently in the stack frame and what handlers should
    124 be checked.
     125it accounts for multiple regions{\color{red}(What's a region?)} and possible
     126handlers in a single function.
    125127% Not that we do that yet.
    126128
    127129However, generating the LSDA is difficult. It requires knowledge about the
    128 location of the instruction pointer and stack layout, which varies with
    129 compiler and optimization levels. So for frames where there are only
    130 destructors, GCC's attribute cleanup with the \texttt{-fexception} flag is
    131 sufficient to handle unwinding.
     130location of the instruction pointer and stack layout, which varies by
     131optimization levels. So for frames where there are only destructors, GCC's
     132attribute cleanup with the $-fexception$ flag is sufficient to handle unwinding.
    132133
    133 The only functions that require more than that are those that contain
    134 \codeCFA{try} statements. A \codeCFA{try} statement has a \codeCFA{try}
    135 clause, some number of \codeCFA{catch} clauses and \codeCFA{catchResume}
    136 clauses and may have a \codeCFA{finally} clause. Of these only \codeCFA{try}
    137 statements with \codeCFA{catch} clauses need to be transformed and only they
    138 and the \codeCFA{try} clause are involved.
     134For functions with handlers (defined in the $try$ statement) the function is
     135split into several functions. Everything outside the $try$ statement is the
     136first function, which only has destructors to be run during unwinding. The
     137catch clauses of the $try$ block are then converted into GCC inner functions,
     138which are passed via function pointers while still having access to the outer
     139function's scope. $catchResume$ and $finally$ clauses are handled separately
     140and not discussed here.
    139141
    140 The \codeCFA{try} statement is converted into a series of closures which can
    141 access other parts of the function according to scoping rules but can be
    142 passed around. The \codeCFA{try} clause is converted into the try functions,
    143 almost entirely unchanged. The \codeCFA{catch} clauses are converted into two
    144 functions; the match function and the catch function.
     142The $try$ clause {\color{red}You have $try$ statement, $try$ block, and $try$
     143clause, which need clarification.)} is converted to a function directly. The
     144$catch$ clauses are combined into two functions. The first is the match
     145function, which is used during the search phase to find a handler. The second
     146it the catch function, which is a large switch-case for the different
     147handlers. These functions do not interact with unwinding except for running
     148destructors and so can be handled by GCC.
    145149
    146 Together the match function and the catch function form the code that runs
    147 when an exception passes out of a try block. The match function is used during
    148 the search phase, it is passed an exception and checks each handler to see if
    149 it will handle the exception. It returns an index that repersents which
    150 handler matched or that none of them did. The catch function is used during
    151 the clean-up phase, it is passed an exception and the index of a handler. It
    152 casts the exception to the exception type declared in that handler and then
    153 runs the handler's body.
     150These three functions are passed into $try_terminate$, an internal function
     151that represents the $try$ statement. This function uses the generated
     152personality functions as well as assembly statements to create the LSDA.  In
     153normal execution, this function only calls the $try$ block closure. However,
     154using $libunwind$, its personality function now handles exception matching and
     155catching. {\color{red}(I don't understand the last sentence.)}
    154156
    155 These three functions are passed to \codeC{try_terminate}. This is an
    156 % Maybe I shouldn't quote that, it isn't its actual name.
    157 internal hand-written function that has its own personality function and
    158 custom assembly LSD does the exception handling in \CFA. During normal
    159 execution all this function does is call the try function and then return.
    160 It is only when exceptions are thrown that anything interesting happens.
     157During the search phase, the personality function retrieves the match function
     158from the stack using the saved stack pointer. The function is called, either
     159returning 0 for no match or the index (a positive integer) of the handler for a
     160match. If a handler is found, the personality function reports it after saving
     161the index to the exception context.
    161162
    162 During the search phase the personality function gets the pointer to the match
    163 function and calls it. If the match function returns a handler index the
    164 personality function saves it and reports that the handler has been found,
    165 otherwise unwinding continues.
    166 During the clean-up phase the personality function only does anything if the
    167 handler was found in this frame. If it was then the personality function
    168 installs the handler, which is setting the instruction pointer in
    169 \codeC{try_terminate} to an otherwise unused section that calls the catch
    170 function, passing it the current exception and handler index.
    171 \codeC{try_terminate} returns as soon as the catch function returns.
     163During the clean-up phase there is nothing for the personality function to
     164clean-up in $try_terminate$. So if this is not the handler frame, unwinding
     165continues. If this is the handler frame, control is transferred to the catch
     166function, giving it the exception and the handler index.
    172167
    173 At this point control has returned to normal control flow.
     168{\color{red}This needs work as I do not understand all of it.}
  • doc/theses/andrew_beach_MMath/uw-ethesis.cls

    r44a88528 r6c6e36c  
    2828%     with options in <setup> (set hyperref's \hypersetup for details).
    2929\NeedsTeXFormat{LaTeX2e}
    30 \ProvidesClass{uw-ethesis}[2020/03/24 v0.1 UW-eThesis Template Document Class]
     30\ProvidesClass{uw-ethesis}[2020/03/09 v0.1 UW E-Thesis Template Document Class]
    3131
    3232\RequirePackage{etoolbox}
     
    3939\ProcessOptions\relax
    4040
    41 \newcommand\ifformat[3]{\ifdefstring{\ethesis@format}{#1}{#2}{#3}}
     41\def\ifformat#1#2#3{\ifdefstring{\ethesis@format}{#1}{#2}{#3}}
    4242
    4343\ifformat{print}{%
     
    136136%  \usepackage[#1]{glossaries-extra}
    137137%}
    138 
    139 \let\colour=\color
Note: See TracChangeset for help on using the changeset viewer.