#!/usr/local/bin/kermit +
#
# Converts a plain text file to HTML.
# Handles paragraphs and single-level numbered and bullet lists.
# Works best with block-style text.
#
# F. da Cruz, Columbia University, Jan 2004.
# Requires C-Kermit 8.0 or Kermit 95 2.1.
# Last update: Mon Jan 12 10:34:50 2004
#
# Usage:
#   html.ksc inputfilename [ "title" ]
#
# Illustrates:
#  . File i/o
#  . String functions
#  . Modularization
#
# Assumes:
#  . Bullet or numbered list items are indented by at least one space.
#  . Bullet characters can be "." "-" "+" "o". 
#  . Numbered lists are numbered 1, 2, 3, ... 99.
#  . A list is terminated by a line that is not indented.
#  . If first text line is followed by a blank line it is to be the title.
#
# Paragraphs are separated by blank lines.  Some attempt is made to also
# handle paragraphs marked by indentation alone, but these don't mix well
# with lists.
#
# Does not handle:
#  . multilevel lists
#  . description lists
#  . ordered lists numbered with anything but consecutive numbers
#  . blockquotes
#  . preformatted text
#  . tables, etc
#  . headings within the text
#  . anchors and links
#  . email (add an otion to handle mail headers)
#
# Result file will need some hand-tuning if it contains any of the
# elements listed just above.

define badversion echo C-Kermit 8.0 or K95 2.0 required, exit
if LLT \v(version) 80000 badversion
if not def \%1 exit 1 "Usage: \%0 filename [ title ]"

# Open input and output files

fopen /read \%i \%1                     # Input file
if fail exit 1

.oname := \fstripx(\fcontents(\%1)).html # Name of output file
fopen /write \%o \m(oname)              # Try to open it
if fail exit 1

# Major states:
# 0 start
# 1 in a paragraph
# 2 between paragraphs

.state = 0                              # State
.\%n = 0                                # Input line count
.oldlen = 0                             # Length of previous output line
.inol = 0                               # In ordered list
.inul = 0                               # In unordered list
.cset = utf-8                           # Change if necessary

# Macro Definitions...

def putout {                            # File output routine
    local out                           # Avoids consecutive blank lines
    .out := \fcontents(\%1)
    .\%9 := \flen(\m(out))
    if ( == \%9 0 && == \m(oldlen) 0 ) end 0
    fwrite /line \%o \m(out)
    if fail exit 1 "FILE WRITE ERROR: \m(oname)"
    .oldlen := \%9
}

def getline {                           # Read input line
    .prev := \m(line)                   # with lookahead and lookback
    .line := \m(next)
    undef next
    fread /line \%i next
    if fail end 1
    incr \%n
    asg next \freplace(\ftrim(\m(next)),\9,\32)
}

def putback {                           # "Rewind" the current record
    .next := \m(line)                   # (works only once for any given line)
    .line := \m(prev)
    undef prev
}

def putline {                           # Write current data line
    local bullet
    .out := \freplace(\m(line),&,&amp;)
    .out := \freplace(\m(out),<,&lt;)
    .out := \freplace(\m(out),>,&gt;)
    .tmp := \fltrim(\m(out))
    if not def tmp { putout, end 0 }

    .bullet = 0
    .number = 0

    if ( equal "\s(out[1:1])" "\32" ) { # Line starts with blank
        if ( eq "\s(tmp[2:1])" "\32" ) { # Unordered list item?
            .\%9 := \fleft(\m(tmp),1)   # Bullet candidate
            if \findex(\%9,.-+o) {      # If valid bullet character
                .bullet = 1             # It's a bullet item
                if not \m(inul) {       # If new list start it.
                    putout "<ul>"           
                    .inul := \fcode(\fleft(\m(tmp),1)) # And remember
                }
            }
        } else if match "\m(tmp)" "{[1-9],[1-9][0-9]}.\32*" { # Ordered list?
            .number = 1
            if not \m(inol) {
                putout "<ol>"
                .inol = 1
            }
        }
    } else if \m(inul) {                # Line does not start with space.
        putout "</ul>"                  # If in unordered list close it.
        putout "<p>"
        putout
        .inul := 0
    } else if \m(inol) {
        putout "</ol>"                  # If in ordered list close it.
        putout "<p>"
        putout
        .inol := 0
    }
    if \m(bullet) {                     # This line has bullet
        .out := <li>\s(tmp[3])
    } else if \m(number) {              # This line has a number
        .\%9 := \findex(.\32,\m(tmp))
        .out := <li>\s(tmp[\%9+2])
    } else if \m(inul) {                # Continuation line of list item
        .out := \m(tmp)
    } else if \m(inol) {                # Ditto
        .out := \m(tmp)
    } else if ( > \flen(\m(tmp)) 8 && == 0 \fverify(-,\m(tmp)) ) { # Rule
        .out = "<hr>"
    }
    putout "\m(out)"
}

def dump {                              # For debugging
    echo DUMP \%1:
    echo PREV=[\m(prev)]
    echo LINE=[\m(line)]
    echo NEXT=[\m(next)]
    echo n=\%n
}

# Begin actions

putout {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">}
putout "<html><head>"

while not def line {                    # Get first nonblank line
    getline
    if fail break
}
getline                                 # Get line after first nonblank line

if not def \%2 {                        # No Title given
    if ( def prev && not def line ) {   # If top line followed by blank line
        .\%2 := \m(prev)                # use it as the title
        getline                         # and read another line
    } else {                            # Otherwise
        .\%2 = "Untitled"               # use "Untitled"
        putback                         # and put this line back
    }
} else {                                # Title given on command line
    putback                             # Save this line for next time
}
# Write rest of prolog...

putout "<title>\%2</title>"
putout {<META http-equiv="Content-Type" content="text/html; charset=\m(cset)">}
putout "</head>"
putout {<body bgcolor="#ffffff" text="#000000">}
putout "<h3>\%2</h3>"
putout
 
.first = 1                              # Start loop with first = 1

while true {                            # Loop for each line in file.
    if not \m(first) {                  # Get next line if not first trip.
        getline
        if fail break
    }
    .first = 0
    if ( > \%n 1 && not \flen(\m(line)) ) { # Blank line
        if ( == \m(state) 2 ) continue      # Ignore extra ones
        if ( \m(inul) || \m(inol) ) {       # Watch out for end of list
            if not eq "\fleft(\m(next),1)" "\32" continue
        }
        putout                          # Emit paragraph separator.
        putout "<p>"
        putout
        .state = 2                      # State becomes "between paragraphs"
        continue
    } else if ( eq "\fleft(\m(line),1)" "\32" ) { # Line is indented
        .\%8 := \fcode(\m(prev))        # Check previous and next lines
        if not def \%8 .\%8 = 32
        .\%9 := \fcode(\m(next))
        if not def \%9 .\%9 = 32
        if ( > \%8 32 && > \%9 32 ) {   # Previous and next line not indented
            putout                      # So this is a new paragraph
            putout "<p>"                # but not separated by blank lines.
            putout
        }
    }
    .state = 1                          # State become "in paragraph"
    if \%n putline                      # If we have a line write it out
}
if > \%n 0 {                            # Last lookahead line (if any)
    putline
    putout
    putout "<p>"
}
putout "<hr>"                           # Write out the epilog
putout "<address>"
putout "\m(oname): \v(date) \v(time)"
putout "</address>"
putout "</body></html>"

fclose \%i                              # And close the files
fclose \%o
exit
