home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.cs.arizona.edu
/
ftp.cs.arizona.edu.tar
/
ftp.cs.arizona.edu
/
icon
/
historic
/
v941.tgz
/
icon.v941src.tar
/
icon.v941src
/
ipl
/
progs
/
htprep.icn
< prev
next >
Wrap
Text File
|
2000-11-03
|
7KB
|
328 lines
############################################################################
#
# File: htprep.icn
#
# Subject: Program to prepare HTML files
#
# Author: Gregg M. Townsend
#
# Date: November 3, 2000
#
############################################################################
#
# This file is in the public domain.
#
############################################################################
#
# usage: htprep [file]
#
# Htprep is a filter for preparing HTML files (used, e.g., by Mosaic)
# from a simpler and less error-prone input language.
#
# The following transformations are applied:
#
# input output
# ------------ ------------
# {}
# {!comment} <!--comment-->
# {tag} <tag>
# {tag ... } <tag> ... <\tag>
# att=val... att="val"...
# {@url ... <a href="url" ...
# {:lbl ... <a name="lbl" ...
#
# Any input character can be preceded by a backslash (\) to prevent
# special interpretation by htprep.
#
# Output is normally to stdout, but the command
# {divert fname}
# redirects output to the named file. This can be used to produce
# multiple related output files from a single input file.
#
############################################################################
$define SIGNATURE "<!-- Created by HTPREP -->"
$define WSPACE ' \t' # whitespace cset
record tag(label, line) # tag record
global tagstack # currently open tags
global cmdtable # table of known special commands
global infile # input file
global outfile # output file
global stdout # standard output, if usable
global lineno # current input line number
global errors # error count
global idset # identifier characters
# main procedure
procedure main(args)
local line, t
idset := &letters ++ &digits ++ '.-_:'
lineno := 0
errors := 0
tagstack := []
stdout := &output
cmdtable := table()
cmdtable["divert"] := divert
if *args = 0 then
infile := &input
else
infile := open(args[1]) | stop("can't open ", args[1])
while line := in() do {
lineno +:= 1
line := braces(line)
out(line)
}
while t := pop(tagstack) do
warn("unclosed tag {", t.label, "} from line ", t.line)
if errors > 0 then
stop
else
return
end
# braces(line) -- process items identified by braces ('{}')
procedure braces(line)
local c, s, t
line ? {
s := ""
while s ||:= tab(upto('{}')) do {
c := move(1)
if c == "{" then
s ||:= newtag()
else { # "}"
if t := pop(tagstack) then {
if t.label == "!" then
s ||:= "-->"
else
s ||:= "</" || t.label || ">"
}
else
lwarn("tag stack underflow")
}
}
return s ||:= tab(0)
}
end
# newtag() -- process text following left brace ('{')
procedure newtag()
local label, s, c
if ="}" then
return ""
if ="!" then {
push(tagstack, tag("!", lineno))
return "<!--"
}
if c := tab(any('@:')) then {
label := "a"
if c == "@" then
s := "<a href="
else
s := "<a name="
s ||:= attval()
}
else {
label := tab(many(idset)) | (lwarn("unlabeled tag") & "noname")
s := "<" || label
}
if \cmdtable[map(label)] then
return s := docommand(label)
while s ||:= attrib()
tab(many(WSPACE))
="}" | push(tagstack, tag(label, lineno))
return s || ">"
end
# attrib() -- match and return attribute
procedure attrib()
return tab(many(WSPACE)) || tab(many(idset)) || ="=" || attval()
end
# attval() -- match and return attribute value
procedure attval()
static valset
initial valset := &cset[34+:94] -- '\'\\"{}'
return (="\"" || tab(upto('"')) || move(1)) |
(="'" || tab(upto('\'')) || move(1)) |
aquote(tab(many(valset)))
end
# aquote(s) -- quote attribute value, but only if needed
procedure aquote(s)
if many(idset, s) = *s + 1 then
return s
else
return '"' || s || '"'
end
# docommand(label) -- process a tag recognized as a command
procedure docommand(label)
local p, atts, words, id, s
p := cmdtable[label]
atts := table()
words := []
while s := attrib() do s ? {
tab(many(WSPACE))
id := tab(many(idset))
move(2)
atts[id] := tab(-1)
}
while tab(many(WSPACE)) & (s := tab(bal(' }', '{', '}'))) do
put(words, s)
tab(many(WSPACE))
="}" | lwarn(label, ": unterminated command")
return p(atts, words) | ""
end
# in() -- read next line, interpreting escapes
#
# Reads the next line from infile, removing leading and trailing whitespace.
#
# If an ASCII character is preceded by a backslash, the character's eighth
# bit is set to prevent its recognition as a special character, and the
# backslash is retained. If it's not an ASCII character (that is, if the
# eighth bit is already set) the backslash is simply discarded.
procedure in()
local s
trim(read(infile), WSPACE) ? {
tab(many(WSPACE))
s := ""
while s ||:= tab(upto('\\')) do {
move(1)
if any(&ascii) then
s ||:= "\\" || char(128 + ord(move(1)))
else
s ||:= move(1)
}
return s ||:= tab(0)
}
fail
end
# divert(attlist, wordlist) -- process "divert" command
#
# If an error is seen, a message is issued and subsequent output is
# simply discarded.
procedure divert(atts, words)
local fname, f
close(\outfile) # always close current file
outfile := stdout := &null # no current file, and no fallback
if *words ~= 1 then {
lwarn("usage: {divert filename}")
fail
}
fname := get(words)
if f := open(fname) then {
if read(f) ~== SIGNATURE then {
lwarn("divert: won't overwrite non-htprep file ", fname)
close(f)
fail
}
close(f)
}
if outfile := open(fname, "w") then {
out(SIGNATURE)
return ""
}
else {
lwarn("divert: can't open ", fname)
fail
}
end
# out(s) -- write line, interpreting escapes
#
# When a backslash is seen, the backslash is discarded and the eighth
# bit of the following character is cleared.
procedure out(s)
local c
if /outfile := (\stdout | fail) then
write(outfile, SIGNATURE) # if first write to &output
s ? {
while writes(outfile, tab(upto('\\'))) do {
move(1)
writes(outfile, char(iand(127, ord(move(1)))))
}
write(outfile, tab(0))
}
return
end
# lwarn(s, ...) -- issue warning with line number
procedure lwarn(a[])
push(a, "line " || lineno || ": ")
warn ! a
return
end
# warn(s,...) -- issue warning message
procedure warn(a[])
push(a, " ")
push(a, &errout)
write ! a
errors +:= 1
return
end