From rose@galtee.cs.wisc.edu Wed Jul 13 10:09:57 1994 Article: 1541 of comp.infosystems.www.providers From: rose@galtee.cs.wisc.edu (Scott Rose) Subject: Re: Is there a calendar file to HTML converter? Date: Tue, 12 Jul 94 21:08:35 GMT-1:00 Organization: CS Department, University of Wisconsin In article <2v1c8c$8m9@usenet.rpi.edu> wiseb@fomalhaut.cs.rpi.edu (Bowden Wise) writes: > I am thinking of providing a web page that details events happening around > here in the summer. So far, I have been adding the information by hand. > It occurred to me that I could put these entries into a calendar file and > then perhaps generate the HTML page automatically. > > Has anyone seen such a tool? > -- > -------------------------------------------------------------------- > - G. Bowden Wise Computer Science Department > Internet: wiseb@cs.rpi.edu Rensselaer Polytechnic Institute Troy, NY 12180 I've written a gawk script that does pretty much what you might want, and I've included it below. What it doesn't do is accept input in any standard format. What it does do is accept input in a pretty simple format, with entries not necessarily ordered or constrained to be in the future, and produce HTML with all the events in order, ignores past events, and provides a header that looks just like the output of cal(1) with anchors on all the date on which events occur. Here is an example entry: ==begin example entry== 062694 Menominee River Century Sunday, June 26, 1994 Routes range from 10 to 70 miles for this $15 ride, which includes a T-shirt and is sponsored by the Spokes and Folks club. Starts at the National Guard Armory in Marinette. Call (800) 447-5613 or (715) 732-0558 for more information, or register electronically by calling (715) 732-1036 (8 bits, no parity, one stop bit), login as "MRC" with password "BIKE" and type "/GO MRC"; have your VISA or MasterCard number ready. ==end example entry== The script searches for a six-digit key to introduce each entry, and treats the rest of the line as a title, which becomes an anchor for a reference in the leading calendar. This might work with vanilla awk, I can't say, but I can say that, sadly, a2p choked on it. #!/usr/psup/bin/gawk -f # # To create "events.html", read a file that is in the following format: # # Each new entry is of the form # <date> is of the form MMDDYY # <title> is a string # Each entry is followed by one or more paragraphs. # Paragraphs are separated by blank lines. # # The output file contains ordered entries in HTML and excludes any entries # for which the date has passed. # # Each entry is maintained in a series of parallel arrays: # # title[<date>,<e_index>] contains the titles # body[<date>,<e_index>,<p_index>] contains the text # nxt[<date>] contains the index of the next date (not the next entry!) # # 'head' selects the first entry. All the numeric array indices are one-based. # BEGIN { # debug = 1 state = "start" if(debug) print "state => start" > "/dev/stderr" pndx = 0 today = strftime("%m%d%y") monthStr["01"] = "January" monthStr["02"] = "February" monthStr["03"] = "March" monthStr["04"] = "April" monthStr["05"] = "May" monthStr["06"] = "June" monthStr["07"] = "July" monthStr["08"] = "August" monthStr["09"] = "September" monthStr["10"] = "October" monthStr["11"] = "November" monthStr["12"] = "December" blanks = " " } /^[0-9][0-9][0-9][0-9][0-9][0-9]/ { # # Start of a new entry. # date = $1 # # If it is a stale entry, reject it. # if((i = cmpDate(date, today)) == -1) { state = "reject" if(debug) print "state => reject" > "/dev/stderr" continue } pndx = 0 ndex = 1 state = "accept" if(debug) print "state => accept" > "/dev/stderr" if(length(head) == 0) { # # This is the first entry we found, so it becomes the head of the list. # head = date if(debug) print "head = " date > "/dev/stderr" } else { # # There are already some entries; see where this one belongs. # # The idea here is to follow along in nxt[] until we run out of elements # or find one with an index later than the date of the current event. # if(debug) print "Adding " date > "/dev/stderr" for(this = head; \ length(this) && (diff = cmpDate(date, this)) > 0;\ this = nxt[was = this]); if(debug) print "date = " date " diff = " diff > "/dev/stderr" if(length(this) == 0) { # # We ran out of elements before we found one that occurs later than # the current one. # nxt[was] = date } else { # # We found an event later than or on the same day as the current one, # insert it. # if(debug) print "diff = " diff > "/dev/stderr" if(diff == -1) { # # The current event is the first one on that date. # if(this == head) { # # The current event is earlier than the former earliest event, so # becomes the head of the list. # nxt[date] = head head = date if(debug) print date " becomes new head" > "/dev/stderr" } else { # # Insert. # nxt[date] = this nxt[was] = date if(debug) print "Insert " date " between " was " and " this > "/dev/stderr" } } else { # # This is not the first event on this date; count the others. # for(ndex = 1; (date, ndex) in title; ndex++); if(debug) print "ndex = " ndex > "/dev/stderr" } } } title[date, ndex] = substr($0, 7) if(debug) { print "Added title["date "," ndex"] = " title[date,ndex] > "/dev/stderr" for(i in nxt) print "nxt["i"] = " nxt[i] > "/dev/stderr" } continue } # entry start { # # Everything that isn't an entry start line is handled here. We collect # paragraphs in body[<date>,<e_index>,<p_index>]. 'date', 'ndex', and 'pndx' # give us the indices. If 'state' == 'reject', we collect nothing, because # it is a stale event. # # if(debug) # print "processing " length($0) " " $0 > "/dev/stderr" if(state == "reject") continue if(length($0) == 0) { # if(debug && pndx > 0) # print body[date,ndex,pndx] > "/dev/stderr" pndx++ # if(debug) # print "pndx = " pndx > "/dev/stderr" continue } body[date,ndex,pndx] = body[date,ndex,pndx] $0 " " } END { # # All the output is emitted in the END rule. # print "<html>" print "<head>" print "<title>Special Events" print "" print "" print "" print "

Calendar

" print "

Click on any highlighted date in the calendar below to move to" print "the events scheduled for that date, or click" print " here" print "to move directly to the chronological list of events.

" # # Emit a leading calendar with links to the events: the first event on each # date has an associated anchor with the date as its name. # # Determine the dates of the first day of the month of the first event and # last day of the month of the last event. # startMonth = substr(head, 1, 2) startYear = substr(head, 5, 2) wday = dow(startMonth "01" startYear) for(date = head; nxt[date]; date = nxt[date]); endMonth = substr(date, 1, 2) endYear = substr(date, 5, 2) # # Loop on all the days in all the months within the range of all the events # that are listed. Collect the output in cal[,] # mdx = 1 for(year = startYear; year <= endYear; year = incr(year)) { if(year == startYear) firstMonth = startMonth else firstMonth = "01" if(year == endYear) lastMonth = endMonth else lastMonth = "12" for(month = firstMonth; month <= lastMonth; month = incr(month)) { indent = substr(blanks, 1, 11 - ((length(monthStr[month])+5)/2)) cal[mdx,1] = indent monthStr[month] " 19" year cal[mdx,1] = cal[mdx,1] substr(blanks, 1, 21 - length(cal[mdx,1])) cal[mdx,2] = " S M Tu W Th F S " lastDay = ldom(month, year) ldx = 3 for(day = "01"; day <= lastDay; day = incr(day)) { if(day == "01") cal[mdx,ldx] = substr(blanks, 1, (wday-1) * 3) thisDate = month day year if((thisDate, 1) in title) cal[mdx,ldx] = cal[mdx,ldx] sprintf("%2d ", thisDate, day) else cal[mdx,ldx] = cal[mdx,ldx] sprintf("%2d ", day) if(wday == 7) { ldx++ wday = 1 } else wday++ } # end loop on days if(wday != 1) cal[mdx,ldx] = cal[mdx,ldx] substr(blanks,1,(8-wday)*3) mdx++ } # end loop on months } # end loop on years # # Now emit the calendar. Right now, it's done in two columns. # print "
"
  for(i = 1; i < mdx; i += 2) {
    for(j = 1; (i,j) in cal || (i+1,j) in cal; j++) {
      if((i,j) in cal)
        filler = substr(blanks, 1, 5)
      else
        filler = substr(blanks, 1, 26)
      print cal[i,j] filler cal[i+1,j]
    }
    print
  }
  print "
" print # # Now, emit the events. # print "

Events

" # # Loop on dates with associated events. # for(date = head; date; date = nxt[date]) { # # Loop on events. # for(i = 1; (date,i) in title; i++) { print "

" title[date,i] "

" print # # Loop on paragraphs. # for(j = 1; (date,i,j) in body; j++) { print "

" body[date,i,j] "

" print } # end loop on paragraphs } # end loop on events } # end loop on dates print "" print "" } # # cmpDate() # # Compare dates, returning 0 if they are equal, 1 if 'one' is later than # 'two', and -1 if 'one' is earlier than 'two'. Special case: if the day # field of date 'one' is zero, return -2. # function cmpDate(one, two) { # # Compare the years. # if((diff = substr(one, 5) - substr(two, 5)) > 0) rval = 1 else if(diff < 0) rval = -1 # # Compare the months. # else if((diff = substr(one, 1, 2) - substr(two, 1, 2)) > 0) rval = 1 else if(diff < 0) rval = -1 # # Compare the days. # else if((diff = (dayOne = substr(one, 3, 2)) - substr(two, 3, 2)) > 0) rval = 1 else if(dayOne == "00") rval = -2 else if(diff < 0) rval = -1 else rval = 0 # if(debug) # print "Compared " one " with " two ", returned " rval > "/dev/stderr" return rval } # end cmpDates() # # ldom() # # Return the last day of the month. Valid for years 1901-2199. # function ldom(month, year) { if(month == "01") return "31" else if(month == "02") { if(year % 4 == 0) return 29 else return 28 } else if(month == "03") return "31" else if(month == "04") return "30" else if(month == "05") return "31" else if(month == "06") return "30" else if(month == "07") return "31" else if(month == "08") return "31" else if(month == "09") return "30" else if(month == "10") return "31" else if(month == "11") return "30" else return "31" } # end ldom() # # incr() # # Auto-increment for a two-digit decimal string. # function incr(i) { return sprintf("%02d", i+1) } # end incr() # # dow() # # Returns the day of the week-- as a digit between 1 and 7-- for any date # on or after 010194-- using the fact that that date was a Saturday (coded # as a 7) and the lame algorithm of determining how many days have since # passed. # function dow(date) { since = 0 year = substr(date, 5, 2) for(i = "94"; i < year; i = incr(i)) if(i % 4 == 0) since += 366 else since += 365 month = substr(date, 1, 2) for(i = "01"; i < month; i = incr(i)) since += ldom(i, year) day = substr(date, 3, 2) since += day-1 return (rVal = (since % 7)) ? rVal : 7 } # end dow()