#!/bin/sh
_rem=--[=[
# POSIX shell wrapper to call correct version of Lua or Lunacy

LUNACY=""
if command -v lunacy64 >/dev/null 2>&1 ; then
  LUNACY=lunacy64
elif command -v lua5.1 >/dev/null 2>&1 ; then
  LUNACY=lua5.1
elif command -v lua-5.1 >/dev/null 2>&1 ; then
  LUNACY=lua-5.1
elif command -v lunacy >/dev/null 2>&1 ; then
  LUNACY=lunacy
fi
if [ -z "$LUNACY" ] ; then
  echo Please install Lunacy or Lua 5.1
  echo Either the version included with MaraDNS -or- the version at
  echo https://github.com/samboy/lunacy
  echo To compile and install the version of Lunacy with MaraDNS:
  echo
  echo     cd MaraDNS/coLunacyDNS/lunacy
  echo     make
  echo     sudo cp lunacy /usr/local/bin/
  exit 1
fi

exec $LUNACY $0 "$@"

# ]=]1
-- This script is written in Lua 5.1

-- This script has been donated to the public domain in 2022 by Sam Trenholme
-- If, for some reason, a public domain declation is not acceptable, it
-- may be licensed under the following terms:

-- Copyright 2022 Sam Trenholme
-- Permission to use, copy, modify, and/or distribute this software for
-- any purpose with or without fee is hereby granted.
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
-- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
-- OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-- Note that, unlike the old Perl ej2html, the character set *must* be utf-8
-- There is not an ej document out there using another charset, and here in
-- the 2020s there is no good reason to *not* use UTF-8 on a POSIX system

-- Convert an ej-formatted doc in to an html page
-- Input: First argument or standard input
-- Output: Standard output

-- If they give a filename as an argument try to open that file
if arg[1] then 
 fh = io.open(arg[1],"rb")
 if not fh then
   print("Error opening file " .. arg[1])
   os.exit(1)
 end
 io.input(fh)
 fhSave = fh
else
 fhSave = io.stdin
end
inInclude = false

-- Mixed case regular expressions
-- mc() converts a string in to a case insensitive regex; mc('th') is [Tt][Hh]
function mc(i) 
  local out = ""
  local afterPercent = false
  for a=1,i:len() do
    seek = i:sub(a,a) 
    -- Do not mangle anything right after a %
    if seek == "%" and not afterPercent then
      out = out .. seek
      afterPercent = true
    -- Only letters not after a % get mangled
    elseif seek:find("%a") and not afterPercent then 
      out = out .. "[" .. seek:upper() .. seek:lower() .. "]"
    -- Other stuff is copied as is
    elseif seek and not afterPercent then
      out = out .. seek
    -- Everything right after a % is passed as-is
    elseif afterPercent then
      out = out .. seek
      afterPercent = false
    end
  end
  return out
end
-- Various names we have in code
head=mc('head')
body=mc('body')
dtwidth=mc('dtwidth')
th=mc('th')
meta_http_equiv=mc('meta%s+http%-equiv')
content=mc('content')
content_type=mc('content%-type')
text_html=mc('text/html')
charset_utf8=mc('charset=utf%-8')
bodyflags=mc('bodyflags')
include=mc('h?include')

l = io.read()
state = "INIT"
oldstate = "INIT"
bflags = ""
metaSeen = false
print("<HTML>") -- Upper case because that's how it was done in 2002
-- Put note about how this HTML document was auto generated
print("<!-- Do *not* edit this file; it was automatically generated by " ..
      "ej2html")
print("     Look for a name.ej file with the same name as this filename -->")
function get_timestamp_string()
  local timestamp = "Time unknown"
  if lunacy then
    local year, mon, day = lunacy.today()
    if year then
      timestamp = string.format("%d-%02d-%02d",year, mon, day)
    else 
      -- lunacy.today() returns nil if time_t is 32-bit
      -- Linux has had 64-bit time support on 32-bit systems since 2020
      -- Alpine Linux, for example, has a 64-bit time_t on 32-bit x86
      -- Another option is to patch the Lunacy source using the
      -- code at https://github.com/evalEmpire/y2038 and making a 
      -- non-portable syscall() to get the undelying 64-bit timestamp
      timestamp = "If your time_t is 32-bit, please upgrade"
    end
  elseif os.date then
    local a = os.date("*t")
    local year = a.year
    local mon = a.month
    local day = a.day
    timestamp = string.format("%d-%02d-%02d",year, mon, day)
  elseif os.time then
    timestamp = string.format("Unix timestamp %d",os.time())
  end
  return timestamp
end
print("<!-- Last updated " .. get_timestamp_string() .. " -->")

laststate = "POSTBODY"
oldstate = "POSTBODY"
while l do
  -- Process .ej and make it HTML
  l = l:gsub(mc("<nofmt>"),"") -- Remove <NOFMT> tag
  l = l:gsub("<!%-%-.*%-%->","") -- Remove single line comments
  if l:find("<!%-%-.*") then -- Multi-line comments
    oldstate = state
    state = "COMMENT"
  end
  if state ~= "COMMENT" then

    -- Process the <include "filename"> tag.  Note that includes
    -- can not be nested.
    -- <hinclude "filename"> is also supported as per the old Perl script 
    -- but not used
    if l:find(mc('<h?include%s+"')) and not inInclude then
      includeFileName=l:gsub(mc('.*<h?include%s+"([^"]+)".*'),"%1")
      l=l:gsub(mc('<h?include%s+"([^"]+)"%s*>'),"")
      fh = io.open(includeFileName,"rb")
      if not fh then
        print("Error opening file " .. includeFileName)
        os.exit(1)
      end
      io.input(fh)
      inInclude = true
    end

    if l:find("<"..head..">") then state = "HEAD" end
    if l:find("</"..head..">") then state = "POSTHEAD" end
    if l:find("<"..body..">") then 
      if not metaSeen then
        print("FATAL: Please add this line to the header")
        print('<META HTTP-EQUIV="Content-Type" CONTENT="text/html; '..
              ' CHARSET=utf-8">')
        os.exit(1)
      end
      state = "BODY" 
      -- Add bodyflags to the BODY tag
      l=l:gsub("<("..body..")>","<%1"..bflags..">") 
    end
    if l:find("</"..body..">") then state = "POSTBODY" end
    if l:find(mc("<pre>")) then 
      laststate = state
      state = "PRE"
    end
    if l:find(mc("</pre>")) then
      state = laststate
    end

    if state == 'HEAD' then
      -- See if they have the mandatory
      -- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=utf-8">
      -- line in the header
      if l:find("<"..meta_http_equiv..'="'..content_type..'"%s+'..content..
                '="'..text_html..';%s+'..charset_utf8..'">') then
        metaSeen = true
      end
      -- Remove headers used for man page
      l=l:gsub("<"..dtwidth..">.*</"..dtwidth..">","") 
      l=l:gsub("<"..th..">.*</"..th..">","") 
      -- BODYFLAGS was to allow the BODY tag to have flags like colors,
      -- which was commonly used with 2002-era HTML the script was
      -- written for.  We will still support it here in the 2020s
      if l:find("<"..bodyflags..">") then
        bflags=l:gsub(".*<"..bodyflags..">([^<]*)","%1")
        bflags=" "..bflags
        l=l:gsub("<"..bodyflags..">[^<]*","")
      end
      l=l:gsub("</"..bodyflags.."%s*>","") -- Remove closing </BODYTAGS>
    -- Show HTML tags and entities as-is in PRE blocks
    elseif state == 'PRE' and not l:find(mc("<pre>")) then 
      l=l:gsub("&","&amp;")
      l=l:gsub("<","&lt;")
      l=l:gsub(">","&gt;")
    end
    print(l)
  else -- In HTML comment
    if l:find(".*%-%->") then -- End of comment
      l=l:gsub(".*%-%->","")
      print(l)
      state = oldstate
    end
  end

  l = io.read()
  -- Handle end of included files
  if not l and inInclude then
    inInclude = false
    io.input(fhSave)
    l = io.read()
  end
end
print("</HTML>")
