#!/usr/local/bin/bash #!/bin/sh #!/usr/bin/sh # ...because it's newer, more portable, and better maintained than ksh. # @(#) $Revision: 1.1 $ $Date: 2006/04/20 21:21:51 $ # @(#) $Source: /u/jer/public_html/tmp/RCS/tasks,v $ # CONVERT TEXT FORMAT TASKS FILE TO BROWSEABLE HTML FORM. # # Usage: See Usage(), or invoke with "-?" for a message. # # Enhancement ideas: # # - If details text paragraphs look like bullets (lines start with "*" or "-"), # automatically create output. # # This is harder than it looks because the script can't tell when the next, # unbulleted paragraph is a continuation of the last bullet (indented) or # past the end of the list. Also it could only easily automate unordered # lists, not ordered lists. So just document that users should manually mark # up their bullets; it's not that hard anyway. # # - If a word looks like a URL (starts with "http:/"), make it a hyperlink. # # It's problematic knowing where the URL ends, when followed by punctuation # that can also appear in the URL itself; plus, in some cases the user might # already have put a hyperlink into the text with a hyperlink label that is # user-friendlier than the URL itself. So, just require users to create # their own hyperlinks (other than for CRs) explicitly if desired. # INITIALIZE MyFullName="$0" MyName="${0##*/}" # basename part of path. ############################################################################### # U S A G E # # Given global $MyName usually but not necessarily set to one of a few known # values, emit a usage message to stdout and exit non-zero. function Usage { cat <.htm; if ends in ".txt", this suffix is removed. (The old file, if any, is pre-removed.) Format required in textfile: - blank lines are ignored except in task detail sections (see below) - comment lines start with "#" in column 1 and are ignored - first data line in textfile is an HTML page title, must begin in column 1 - task descriptions start with a title line followed by optional text; each title line begins with a three-letter code in column 1: [] is one of A, B, C is one of L(ow), M(edium), H(igh) is one of A(SAP), P(ending), D(eferred), C(ompleted) is zero or more indented lines, with blank lines separating paragraphs, describing the task in any detail desired - HTML may be used in ; special characters <, >, & are not escaped (beware using them incorrectly) - tasks appear in the HTML file in the same order they appear in the textfile See also prior examples of tasks textfiles. UNQUOTED_HERE_DOC exit 1 } # Usage() ############################################################################### # E R R O R # # Emit all args (strings) to stderr preceded by $MyFullName and "ERROR:" or # "WARNING:", then for errors, exit non-zero. function Error { echo >&2 "$MyFullName: ERROR: $@"; exit 1; } ############################################################################### # M A K E H T M L # # Given stdin containing an alleged tasks textfile (see Usage()) with comment # lines blanked (to preserve line numbers), write an HTML equivalent to stdout. # Error out in case of misformatting. function MakeHTML { # Since awk can't easily write to stderr, write errors to a temp file: temp='/var/tmp/MakeHTML_errors' rm -rf $temp trap "rm -rf $temp; exit" 0 1 2 3 13 15 awk ' BEGIN \ { space = "[ ]"; # space or tab: nonspace = "[^ ]"; CRpatt = "JAG[a-z]{2}[0-9]{5}"; # CHART CR number pattern. CRpatt2 = "^" CRpatt; # anchored. CRlen = 10; # size of it. } # Report error to imported filename using imported filename: function Error(msg) { print "Text file \"'"$textfile"'\", line", NR ":", msg >"'$temp'"; error = 1; exit; } # Trim leading whitespace: function Trim(text) { sub("^" space "*", "", text); return(text); } # Check for an indented comment; if so, return extra message text, but always a # period: function IndComment(line) { return((line ~ "^" space "*#") ? \ " (and comments cannot be indented)." : "."); } # Given cell text and optional pre/post text (to make it a hyperlink), write to # stdout a priority/effort/status table cell color-coded if appropriate, and in # that case also make the text color black, even if it is a hyperlink: function ColorTD(text, pre, post) { bg = font = fontend = ""; fontb = ""; fontbend = ""; A = "ff9090"; B = "ffff90"; C = "90ff90"; if ((text == "A") || (text == "H") || (text == "deferred")) {bg = " bgcolor=\"" A "\""; font = fontb; fontend = fontbend} else if ((text == "B") || (text == "M") || (text == "pending")) {bg = " bgcolor=\"" B "\""; font = fontb; fontend = fontbend} else if ((text == "C") || (text == "L") || (text == "ASAP")) {bg = " bgcolor=\"" C "\""; font = fontb; fontend = fontbend} return(" " pre font text fontend post \ ""); } # ColorTD() # Convert CR numbers to hyperlinks: # # Note: See also file header comments about hyperlinking apparent URLs. function Hyper(text) { if (text ~ CRpatt) { # Locate every CR number in the line (tedious but effective): for (pos = 1; pos <= length(text); ++pos) { if (substr(text, pos) !~ CRpatt2) continue; CR = substr(text, pos, CRlen); # Insert hyperlink around CR number: len = length(text); text = substr(text, 1, pos - 1) \ "" CR "" substr(text, pos + CRlen); pos += (length(text) - len) + CRlen; # skip ahead. } } return(text); } # Look for page title line; note, here and below, consider a line blank if it # contains only whitespace: (! title) && NF \ { if ((title = $0) ~ "^" space) Error("Title line cannot begin with whitespace" IndComment($0)); # Include a hyperlink to the raw text file, assuming it is in the same # directory: print "\n\n" title "\n"; print "\n

" title "

"; print "

Webpage version updated '"$(date)"' from"; print "'"$textfile"'"; next; } # Look for and verify start of a new task section (starts with non-whitespace): ($0 ~ "^" nonspace) \ { if (NF <= 1) Error("Task start line must contain at least 2 fields.") if (length($1) != 3) { Error("Task start line must begin with three-letter code " \ "(and task detail lines must be indented)."); } pri = substr($1,1,1); eff = substr($1,2,1); stat = substr($1,3,1); if (! index("ABC", pri)) Error("Task priority must be one of A, B, C."); if (! index("LMH", eff)) Error("Task effort must be one of L, M, H."); if (! index("APDC", stat)) Error("Task status must be one of A, P, D, C."); # Get description text, save P/E/S and text less any leading whitespace; also # create a (long) hyperlink label that is hopefully safe, stable, and unique: # # Note: Not all contexts expect URLs to contain punctuation, they get # confused, so remove common punctuation marks first. priority[++tasks] = pri; effort [ tasks] = eff; status [ tasks] = stat; oneline [ tasks] = Trim(substr($0, 4)); str = oneline[tasks]; gsub("[][><|/ .,;:\"!@#$%^&*()]", "", str); label [ tasks] = pri "_" eff "_" stat "_" substr(str, 1, 20); gsub(space, "", label[tasks]); next; } # task start line. # Save task details lines, if any, for the current task: { if ((! tasks) && NF) { Error("Task start line must precede any indented task " \ "details text" IndComment($0)); } # Skip any leading blank lines, and note the last non-blank line; trim leading # whitespace except in

 sections:

	    if ((! NF) && (! details[tasks])) next;
	    if ($0 ~ /
/)	inPRE = 1;
	    if ($0 ~ /<\/PRE>/) inPRE = 0;
	    detail[tasks "." ++details[tasks]] = (inPRE ? $0 : Trim($0));
	    if (NF) lastnb[tasks] = details[tasks];
	}

# If no errors, check that a title line and at least one task was found, then # emit the body of the HTML page:

	END \
	{
	    if (error) exit;
	    if (! title) Error("No page title line found.");
	    if (! tasks) Error("No tasks found.");

# Emit key table:

	    print "

\n"; print ""; print " "; print " "; print ""; print ""; print " "; print " "; print "\n
P =Priority: A, B, C
E =Effort: L(ow), M(edium), H(igh)
"; print " S ="; print " Status: A(SAP), P(ending), D(eferred), C(ompleted)"; print "\n"; # Emit tasks summary table: print "

\n

Task List:

\n\n"; print " "; print " "; print " "; print " "; print " "; print ""; # Expand status code: expand["A"] = "ASAP"; expand["P"] = "pending"; expand["D"] = "deferred"; expand["C"] = "completed"; for (at = 1; at <= tasks; ++at) { status[at] = expand[status[at]]; # If a task has details, make its status cell into a hyperlink to them: if (! details[at]) {pre = post = ""} else {pre = ""; post = ""} # Color-code some cells: print ""; print " "; print ColorTD(priority[at], "", ""); print ColorTD(effort [at], "", ""); print ColorTD(status [at], pre, post); print " "; print ""; } print "
NumPEStatusOne-line summary
" at "" oneline [at] "
"; # Emit task details sections, each preceded by an inbound hyperlink wrapping a # bold copy of the task start info, with embedded blank lines converted to # paragraph breaks (except within
 sections, causes doublespacing):

	    print "

Task Details:

"; for (at = 1; at <= tasks; ++at) { if (! details[at]) continue; havedetails = 1; print "

\n


" at ". (" \ priority[at] ",", effort[at] ",", status[at] ")", \ "" oneline[at] "

"; for (at2 = 1; at2 <= lastnb[at]; ++at2) { text = detail[at "." at2]; if (text ~ /

/)	  inPRE = 1;
		    if (text ~ /<\/PRE>/) inPRE = 0;

		    print (text ~ nonspace) ? Hyper(text) \
					    : inPRE ? "" : "

\n"; } } # Emit many trailing blank lines for inbound hyperlink scrolling to top of # browser window: if (havedetails) print "


"; for (at = 1; at <= 50; ++at) print "
"; print "\n\n"; }' # end awk # Check for awk error report: [[ -s $temp ]] \ && Error "$(< $temp) Invoke with \"-?\" for a format summary." } # MakeHTML ############################################################################### # M A I N # # function Main { # Check argument: [[ ($# != 1) || ("x$1" = 'x-?') ]] && Usage textfile="$1" [[ -r "$textfile" ]] \ || Error "Cannot read from text file \"$textfile\"." # Remove old HTML file if any: # # Note: Immediate recreation of the same file seems to reuse the same inode # number => some browsers refuse to refresh the file, so try to avoid this. HTMLfile="$(echo "$textfile" | sed 's/\.txt$//').htm" echo remove "$HTMLfile" rm -rf "$HTMLfile"; touch tasks_temp # Process $textfile; first blank any comment lines: sed < "$textfile" 's/^#.*//' | MakeHTML > "$HTMLfile" chmod 644 "$HTMLfile" # Skip this now that $HTMLfile is under SoftCM: # [[ -f "$HTMLfile" ]] && chmod 0444 "$HTMLfile" rm -rf tasks_temp exit 0 #} Main()