Source file webup.icn
############################################################################
#
#	File:	  webup.icn
#
#	Subject:  A rudimentary web-based updater/patcher.
#
#	Author:   Clint Jeffery
#
#	Date:     30 June 2010
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
# webupdate(URL, title) pulls down files from URL to replace local files
#   with different file sizes or older modified time stamps.
#
# Web site structure is at present: three subdirectories
# ("linux","windows","shared"), each of which has a MANIFEST directory
# containing a list of files; each line of the MANIFEST is of the form
#
# filename filesize timestamp
#
# MANIFEST may also contain blank and commented-out lines beginning with #
#
# At present this is a GUI module that opens its own dialog.
# It is desirable that the updating be separated from the GUI part.
#
############################################################################
#
#  Links: gui
#
############################################################################

global webupsite, webuptitle

procedure webupdate(website: string, title: "web update")
local d
   webupsite := website
   webuptitle := title
   d := dialogtable()
   d.show_modal()
end

class dialogtable()
   method show_modal()
   local d, L, platform, dir, N

      d := UnicronProgressBar()
      d.show_modeless()

      platform :=
$ifdef _UNIX
	 "linux"
$else
	 "windows"
$endif

      N := 0
      every dir := (platform | "shared") do {
	 d.syncdir := dir

	 if *(L := d.check()) > 0 then {
	    N +:= *L
	    handle_updates(L, d)
	    }
	 }
      if N = 0 then {
	 d.message("no files updated")
	 delay(500)
	 d.dispose()
	 }
      else {
	 d.message((N/3) || " files updated")
	 delay(3500)
	 d.dispose()
	 }
   end

   method handle_updates(L, d)
   local fn, reason, size, errs, nerrs
      errs := ""   
      nerrs := 0
      while fn := pop(L) do {
	 reason := pop(L)
	 size := pop(L)
	 errs ||:= d.update(fn, reason, size) || " "
	 }
      if errs ~== "" then {
	 d.message("exiting because: " || errs)
	 dispatcher.message_loop(d)
	 exit(-1)
	 }
      d.message("updated " || fn || ", continuing")
      delay(800)
      return
   end
end


$include "guiconst.icn"
class ProgressBar : Component (
   p,
   bar_x, bar_y, bar_w, bar_h
)

   method resize()
      /h_spec := WAttrib(cwin, "fheight") + 2 * DEFAULT_TEXT_Y_SURROUND
      self$Component.resize()
      bar_x := x + DEFAULT_TEXT_X_SURROUND
      bar_y := y + BORDER_WIDTH + 3
      bar_w := w - 2 * DEFAULT_TEXT_X_SURROUND
      bar_h := h - 2 * (BORDER_WIDTH + 3)
   end

   method display(buffer_flag)
   local cw
      EraseRectangle(cbwin,x,y,w,h)
      DrawRaisedRectangle(cbwin, x, y, w, h)
      FillRectangle(cbwin, bar_x, bar_y, bar_w * p / 100.0, bar_h)
      cw := Clone(cbwin, "drawop=reverse")
      center_string(cw, x + w/2, y + h/2, p || "%")
      Uncouple(cw)
      if /buffer_flag then CopyArea(cbwin, cwin, x, y, w, h, x, y)
      return
   end

   method handle_event(e)
      if integer(e) = (&lpress|&rpress|&mpress) &
         in_region() & (&x <= bar_x + bar_w) then {
         set_percentage((100 * (&x - bar_x)) / bar_w)
         return _Event(e, self, 0)
         }
   end

   method get_percentage()
      return p
   end

   method set_percentage(pct)
     p := pct
     redisplay()
   end
initially(argv[])
   self$Component.initially()
   set_percentage(0)
   if *argv > 0 then set_fields(argv)
end

class UnicronProgressBar : _Dialog(lab, fnlab, pb, closebtn, syncdir)
   method dialog_event(ev)
      case ev.get_component() of {
         pb :
            write("pb produced an event - percentage = ", pb.get_percentage())
         closebtn: {
            if ev.get_code() > 0 then {
               dispose()
	       win := &null
               }
            }
      }
   end
   method message(s)
      lab.set_label(s)
      if \ (is_open) then {
         lab.resize()
         display()
         }
   end
   method check()
   local platform, url, db, filen, size, mtime, L, r, fs
      url := distfile("MANIFEST")
      message("checking "|| url|| "...")
      db := open(url, "m") | {
         write("can't update, was trying to read ", image(url))
	 fail
         }
      message("checking... opened " || url)
      L := []
      while r := read(db)  do {
	 if (r == "") | (r[1] == "#") | match("filename ",r) then next
	 r ? {
            filen := tab(upto(' \t'))|write("no filename in ", image(r))
            tab(many(' \t'))
            size := numeric(tab(many(&digits)))|
		write("no filesize in ", image(r))
            tab(many(' \t'))
            mtime := numeric(tab(many(&digits)))|
		write("no mtime in ", image(r))
	    }
         message("checking " || filen)
         if not (fs := stat(filen)) then {
            put(L, filen, "because it is a new file", size)
            }
         else if fs.size ~= size then {
            put(L, filen, "because its size ("||size||
	           ") is different than ours (" || fs.size||")", size)
            }
         else if fs.mtime < mtime then {
            put(L, filen, "because it is newer ("||mtime||
	        ") than ours (" || fs.mtime || ")", size)
         }
	 else {
	    #write("don't think we updated ", filen)
	    #write("\tbecause ", image(fs.size), " = ", image(size))
	    #write("\tbecause ", image(fs.mtime), " >= ", image(mtime))
	    }
	 }
      close(db)
      return L
   end
   method distfile(fn)
      return "http://" || webupsite || syncdir || "/" || fn
   end
   method update(fn, reason, size)
   local fetcher, s, fo, newfile, percent, total
      if /win then stop("can't update ", fn, " win is gone")
      message("updating " || fn || " " || reason)
      WAttrib(win, "pointer=" || ("wait"|"watch"))
      fnlab.set_label(fn)
      pb.set_percentage(0)
      (fetcher := open(distfile(fn), "m")) | {
         s := "can't get contents of " || fn
	 message(s)
         WAttrib(win, "pointer=arrow")
         return s
         }
      message("httpget (" || image(fetcher) || ")")
      if not (fo := datopen(fn, "wu")) then {
         fn := "new-" || fn
         if not (fo := datopen(fn, "wu")) then {
            s := "can't open "||fn||" for writing"
            message(s)
            WAttrib(win, "pointer=arrow")
            return s
            }
         }
      total := 0
      while writes(fo, s := reads(fetcher, 1024)) do {
	 total +:= *s
	 percent := 100 * total / size
	 if percent > 100 then percent := 100
	 pb.set_percentage(percent)
	 }
      close(fo)
      if match("new-", fn) then {
         s := "couldn't write " || fn[4:0] ||
               "; close ALL programs and rerun unicron to patch"
         message(s)
         newfile := datopen("new-files","a")
         write(newfile, fn)
         close(newfile)
         WAttrib(win, "pointer=arrow")
         return s
         }
      else
         message("updated " || fn)
      WAttrib(win, "pointer=arrow")
   end

initially
local t
   self$_Dialog.initially()
   set_attribs("label=" || webuptitle, "size=644,399",
	       "font=sans", "bg=yellowish white", "resize=on")
   t := Label("pos=10%,15%", "size=80%,10%",
	      "internal_alignment=c",
	      "label=Checking for update files! please wait...")
   t.set_attribs("font=serif,28,bold","fg=blue")
   add(t)
   lab := Label("pos=50%,80%", "size=90%,10%", "align=c,c",
		"label=initializing...")
   lab.set_attribs("font=serif,18,bold")
   add(lab)
   fnlab := Label("pos=50%,38%", "size=50%,15%", "align=c,c",
		  "label=no files updated")
   fnlab.set_attribs("font=serif,18,bold")
   add(fnlab)
   pb := ProgressBar("pos=50%,50%", "size=50%", "align=c,c")
   add(pb)
   closebtn := TextButton("label=Close", "pos=50%,66%", "align=c,c")
   add(closebtn)
   set_focus(closebtn)
end

This page produced by UniDoc on 2021/04/15 @ 23:59:43.