%%
%% NOTE: switches-by-context-fn.R is to be edited (if necessary) in inst/misc
%%
\documentclass[10pt,a4paper]{article}
\usepackage{verbatim}
\usepackage[top=1.0cm,bottom=1.5cm,left=1.0cm,right=1.0cm]{geometry}
\usepackage{longtable} %,booktabs}
\usepackage[utf8]{inputenc}
\usepackage[pdftex,bookmarks,colorlinks,linkcolor=blue]{hyperref}
\usepackage{listings}
\usepackage{array}
\usepackage{framed}
\usepackage{Sweave}
\usepackage{placeins} % for FloatBarrier
\usepackage[tablewithin=section]{caption}
%% formatting
% use a san-serif font for online display
\renewcommand\familydefault{cmss}
\usepackage{parskip}
\newcommand{\Lsmc}[1]{\small #1}

\title{Switch Summary by Context}
%\VignetteIndexEntry{Switch Summary by Context}
%\SweaveUTF8
%

\begin{document}
%\maketitle
%\tableofcontents
\SweaveSyntax{SweaveSyntaxLatex}

%
% First we need to download the switches summary file from
% https://reduce-algebra.sourceforge.io. This was originally switches.php, but following
% the PHP critical security issue covered by CVE-2026-109618 and CVE-2026-109619 has been
% renamed by SourceForge to switches.php.html and is a static file (all .php have been
% renamed to .php.html). In fact it has been static since Jan 2025 as Francis Wright
% discovered. He plans to change how the web site to a static one built by code but will
% leave the names as .php.html in the meantime.
%

% In order to do this we need to define the function php2txt() which downloads the first
% file in the list c(switches.php.html, switches.html, switches.php) that it finds to
% hopefully avoid having to publish a new version of redcas if the name changes. It saves
% the timestamp in the file to avoid recreating the vignette if the time stamp has not
% changed. It then converts HTML markup to LaTeX markup and writes the modified file to
% the file named in the "out" argument or switches.data.time-stamp if out is missing.
%
% We also define txt2tex() which produces the tables for each context.
%
\begin{Scode}{echo=false}
## Function: getSwitches: download whichever switches file is present
##     Args:
##      urls: vector of the possible (base)names of the switches file
##    Value: list of two elements:
##           1. name of downloaded file or "none" if download failed
##           2. vector of the contents of the downloaded file
getSwitches <- function (urls) {
    ## we'll return the first one that does not fail
    for (i in 1:length(urls)) {
        switches <- tryCatch(readLines(ucon <- url(urls[i]), warn=FALSE),
                             warning=function(w) FALSE,
                             error=function(e) FALSE)
        close(ucon)
        if (is.logical(switches)) {
            if (i == length(urls)) {
                message("Switches document could not be downloaded.")
                swname <- "none"
                switches <- ""
            }
            next
        } else {
            message("Downloading ", urls[i])
            swname <- basename(urls[i])
            break
        }
    }
    return(list(swname, switches))
}
## Function: php2txt: download and create a text file with HTML markup converted to LaTeX
##     Args:
##      out: character, optional. file name for converted switch.html. Default is
##           switch.data.mod-date
##    Value: only side-effects:
##              1. create switches.data.last-mod-date containing html converted to LaTeX
##              2. create switches.last.update.RDS containing dates of downloaded pages,
##                 most recent first.
php2txt <- function(out) {
    ## download, or read from local copy if already downloaded today
    phpfiles <- c("switches.html", "switches.php.html", "switches.php")
    ## download but avoid downloading multiple times on the same day
    if (file.exists(phpfiles[1]) && as.Date(file.mtime(phpfiles[1])) == Sys.Date() ) {
        message("note: reading local switches.html")
        sw.1 <- list("switches.html", readLines("switches.html"))
    } else {
        sw.1 <- getSwitches(paste0("https://reduce-algebra.sourceforge.io/", phpfiles))
        if (sw.1[[1]] == "none") stop("Cannot create switches-by-context.tex. Stopping.")
        writeLines(sw.1[[2]], phpfiles[1])
    }
    ## input has invalid utf8 chars x201C (“), x201D (”), x2019 (’) and x2013 (–) on
    ## various records. Neither Sweave nor texi2dvi like this. So preamble includes
    ## [utf8]{inputenc} and the SweaveUTF8 command

    sw.1[[2]] <- gsub('[“”]', '"', sw.1[[2]])
    sw.1[[2]] <- gsub('’', "'", sw.1[[2]])
    sw.1[[2]] <- gsub("–", "-", sw.1[[2]])
    
    ## check whether the page has been updated since last fetch
    if (file.exists("switches.last.update.RDS")) {
        switches.last.update <- readRDS("switches.last.update.RDS")
    } else {
        switches.last.update <- as.Date("1970-01-01")
    }
    if (length(plm.idx <- grep("Page last modified on", sw.1[[2]])) == 0) {
        stop("Could not find \"Page last modified on\".")
    } else {
        page.mod.date <- as.Date(gsub("(^ *|st|nd|rd|th|\\.)", "", sw.1[[2]][plm.idx+1]),
                                 "%A %e %B %Y")
        if (page.mod.date <= switches.last.update[1]) {
            message(
                paste0("note: nothing to do https://reduce-algebra.sourceforge.io/", sw.1[[1]],
                      "\n      not updated since last download."))
            q("no")
        } else {
            pmd.sfx <- as.character(page.mod.date)
            if (missing(out)) out <- paste0("switches.data.", pmd.sfx)
        }
    }

    ## find the start/end of the two tables (tbody) and extract them
    sw.idx <- which(grepl("</?tbody",sw.1[[2]],ignore.case=TRUE))
    sw.doc <- sw.1[[2]][(sw.idx[1]+1):(sw.idx[2]-1)]
    sw.undoc <- sw.1[[2]][(sw.idx[3]+1):(sw.idx[4]-1)]

    ## convert html markup to LaTeX markup. This is partly very specialized (handling of xi)
    h2l <- c(
        ## needs to be before </?em> translation
        "&radic[^.]+.",              "$\\\\sqrt{x^2}$ to $|x|$.",
        "&rarr;",                    "$\\\\rightarrow$",
        "</?code>",                  "$",
        "<em>",                      "{\\\\it ",
        "</em>",                     "}",
        "_",                         "\\\\_",
        ## must be after _ -> \_ but before <sub>
        "&xi;<sub>([^<]+)</sub>",    "$\\\\xi_\\1$",
        "<sup>([^<]+)</sup>",        "$^\\1$",
        "<sub>([^<]+)</sub>",        "$_\\1$",
        "</?span[^>]*>",             "",
        "&ndash;",                   "",
        "([^$])&xi;",                "\\1$\\\\xi$",
        "&lt;",                      "",
        "&gt;",                      "",
        "&quot;",                    "\042",
        "&nbsp;",                     " "
    )
    sw.from <- h2l[seq(1, length(h2l), 2)]
    sw.to <- h2l[seq(2, length(h2l), 2)]
    for (i in 1:length(sw.from)) sw.doc <- gsub(sw.from[i], sw.to[i], sw.doc)

    ## create data frame for sw.doc. This assumes that each table row consists of 6 lines of
    ## text: tr, 4x th/td and /tr
    sw.doc.len <- length(sw.doc)
    sw.doc.df <- data.frame(
        switch=gsub("( +<th scope=\"row\">|</th>)", "", sw.doc[seq(2, sw.doc.len, 6)]),
        state=gsub("( +<td>|</td>)", "", sw.doc[seq(3, sw.doc.len, 6)]),
        context=gsub("( +<td>|</td>)", "", sw.doc[seq(4, sw.doc.len, 6)]),
        description=paste0(gsub("( +<td>|</td>)", "", sw.doc[seq(5, sw.doc.len, 6)]),
                           " \\\\")
    )

    write.table(sw.doc.df, out, quote=FALSE, sep="\t", row.names=FALSE)
    message(paste("note: wrote converted ", phpfiles[1], " to ", out))
    
    ## save this modification date (stack)
    switches.last.update <- c(page.mod.date, switches.last.update)
    saveRDS(switches.last.update, "switches.last.update.RDS")
    return(pmd.sfx)
}

## Function: txt2tex: return formatted LaTeX for a context type
##     Args:
##    date.idx: integer, required. index of desired update date. Default: 1, most recent
## date.suffix: logical, required. Use modification date as file suffix. Default: TRUE
##        type: char, general or package. Default: general
##   longtable: logical, required. Use longtable or table/tabular. Default: FALSE
##       fsize: logical, wrap each column in \Lsmc{}. \Lsmc is defined in the Rnw file
##    Value: only side-effects: create switches.last-mod-date.tex|pdf
txt2tex <- function(date.idx=1, date.suffix=TRUE, type="general", longtable=FALSE, fsize=FALSE) {
    pmd.date <- readRDS("switches.last.update.RDS")[date.idx]
    pmd.sfx <- paste0(".", pmd.date)
    idata <- paste0("switches.data", if (date.suffix) pmd.sfx else "")

    ## Read the data
    switches <- read.table(idata, header = TRUE, sep = "\t", quote="",
                           colClasses = c('character','character','character','character'))
    if (fsize) {
        for (var in c('switch','state','description')) {
            switches[,var] <- paste0("\\Lsmc{", switches[,var], "}")
        }
    }
    isGeneral <- grepl("([A-Z][a-z]+|\\*)", switches$context)
    if (tolower(type) == "general") {swtype <- switches[which(isGeneral),] }
    else {swtype <- switches[which(!isGeneral),] }
    sw.by.c <- swtype[order(swtype$context), c('context','switch','state','description')]
    ## in order to have * (global) as first table, this must be done no earlier than here
    if (any(c.global <- grepl("^\\*$", sw.by.c$context))) {
        sw.by.c$context[which(c.global)] <- "Global"
    }
    ## debugging
    ## return(sw.by.c)
    ## end debugging
    ## by context
    contexts <- unique(sw.by.c$context)
    ## for default size >10pt some contexts need longtable so define separate begin/end
    ## for longtable and table. Order of caption differs too. By using
    ## caption[]{continued} in the endhead, duplicated entries in LoT are avoided when a
    ## table spans pages.
    tab.head <- "\\bfseries{Switch} & 	\\bfseries{Default} & \\bfseries{Effect} \\\\"
    hline <- "\\hline"
    topline <- hline #"\\toprule"
    botline <- hline #"\\bottomrule"
    txt.c <- vector("character")
    for (context in contexts) {
        this.context <- sw.by.c[sw.by.c$context == context,c('switch','state','description')]
        ## to get hyphenation working in the switch column
        ## if (any(nchar(this.context$switch) > 15)) {
        ##     this.context$switch <- paste0("\\hspace{0pt}", this.context$switch)
        ## }
        this.context$switch <- ifelse(nchar(this.context$switch) > 18,
                                      sub("\\_", "\\_\\\\\\\\", this.context$switch),
                                      this.context$switch)
        txt.c <- c(txt.c,
                   if (longtable) {
                       c("\n\\begin{longtable}[c]{p{4cm}lp{10cm}}",
                         paste0("\\caption{", context, "}\\\\"), ## needs newline in longtable
                         tab.head, topline, "\\endfirsthead",
                         "\\caption[]{(continued)}\\\\",
                         tab.head, topline, "\\endhead"
                         )
                   } else {
                       c("\n\\begin{table}[hp]",
                         "\\begin{center}",
                         paste0("\\caption{", context, "}"),
                         "\\begin{tabular}{p{4cm}lp{10cm}}",
                         tab.head, topline
                         )
                   },
                   apply(this.context, 1, paste, collapse = " & "),
                   botline,
                   if (longtable) {
                       "\\end{longtable}\n"
                   } else {
                      c("\\end{tabular}", "\\end{center}", "\\end{table}\n")
                   }
                   )
    }
    return(txt.c)
}

pmd.date <- php2txt()
\end{Scode}

\section*{Switch Summary by Context \Sexpr{pmd.date}}
\label{sec:introduction}
This document list switches available within REDUCE via the commands {\it ON} and {\it
  OFF}. The switches are presented by context. Section~\ref{sec:general} covers switches
which are not specific to a package while Section~\ref{sec:package} shows package-specific
ones. For an alphabetical listing see the online
\href{https://reduce-algebra.sourceforge.io/switches.html}{REDUCE Switch Summary} from which this
document is derived.

See appendix~\ref{sec:rebuild} for instructions on how to rebuild this document.

\listoftables
\newpage
\section{Switch summary by context: general switches}
\label{sec:general}

\begin{Scode}{label=update, echo=FALSE, results=tex}
  writeLines(swgen <- txt2tex(date.idx=1, date.suffix=TRUE, type="general", longtable=FALSE))
\end{Scode}

\FloatBarrier
\section{Switch summary by context: package-specific switches}
\label{sec:package}

\begin{Scode}{label=update, echo=FALSE, results=tex}
  writeLines(swpkg <- txt2tex(date.idx=1, date.suffix=TRUE, type="package", longtable=FALSE))
\end{Scode}

\FloatBarrier
\appendix
\section{Rebuilding this document}
\label{sec:rebuild}

This document was created using the source for this vignette, {\it
  switches-by-context.Rnw}, which includes all the code necessary for downloading the
\href{https://reduce-algebra.sourceforge.io/switches.html}{Switch Summary} page and
building this vignette.  Since the switch summary may change, a new version of this
document may be created by saving the Rnw file to a directory of your choice and executing
it using {\textit R CMD Sweave -{}-pdf}.  The first time this is run it saves the last
update date from the Switch Summary to {\it switches.last.update.RDS} and on subsequent
runs only updates if the date in the current Switch Summary is later. To unconditionally
update, delete or rename {\it switches.last.update.RDS} prior to executing {\textit
    Sweave}.

Note that updating the actual vignette in the package requires a new version of the
package.

\end{document}
