module autopatch; % Fetch and update patches fasl file.

% Author: Arthur C. Norman.

% Modifications by: Anthony C. Hearn.

fluid '(!*home);

global '(patch!-url!-list!* personal!-dir!*);

symbolic procedure add!_patch!_url u;
  patch!-url!-list!* := u . patch!-url!-list!*;

add!_patch!_url "http://reduce-algebra.com/support/patches/patches.fsl";

symbolic procedure get!-checksum file;
   begin scalar file1; integer c,checksum;
      if not filep file then return nil;
      file1 := binopen(file,'input);
      for i := 1:16 do <<if (c := readb file1) = !$eof!$ then c := 0;
			 checksum := 256*checksum + c>>;
      return checksum
   end;

symbolic procedure
      write!-patch!-file(dir,checksum,remote!-file,remote!-checksum);
   begin scalar p,w; integer c;
      % Read rest of remote file.
      while (c := readb remote!-file) neq !$eof!$ do p := c . p;
      close remote!-file;
      % Transcribe remote file data into a string.
      w := make!-simple!-string length p;
      c := -1;
      for each x in reversip p do putv!-char(w,c := c + 1,x);
      % Check checksum of data as fetched.
      if md5 w neq remote!-checksum
	then rederr "Checksum on fetched patches is incorrect";
      % Write out updated file.
      p := concat(dir,"/patches.fsl");
      if filep p then rename!-file(p,concat(dir,"/patches.old"));
      binary_open_output p;
      for each x in reversip checksum do binary_prinbyte x;
      for i := 0:upbv w do binary_prinbyte scharn(w,i);
      binary_close_output()
   end;

symbolic procedure rename!-home!-patch!-file;
   (filep x and rename!-file(x,concat(personal!-dir!*,"/patches.old")))
      where x = concat(personal!-dir!*,"/patches.fsl");

symbolic procedure update!_reduce;
   begin scalar c,lisp!-d,p,remote,w; integer remote!-checksum;
      if memq('demo, lispsystem!*) then
         rederr "Update service not available in demo version";
      lisp!-d := get!-lisp!-directory();
   % Find a site with the updates.
      w := patch!-url!-list!*;
      while null remote and w do <<remote:= open!-url car w; w:= cdr w>>;
      if null remote
	then <<terpri();
	 printc "*** Unable to access any of the remote patches files:";
	 for each s in patch!-url!-list!* do <<ttab 4; printc s>>;
	 return nil>>;
   % Fetch 16 bytes of checksum from the start of update file.
      for i := 1:16 do <<if (c := readb remote) = !$eof!$ then c := 0;
			 p := c . p;
			 remote!-checksum := 256*remote!-checksum + c>>;
   % Install updated file if needed.
      if !*home
	then <<if remote!-checksum =
		  (c := get!-checksum concat(lisp!-d,"/patches.fsl"))
		  or (remote!-checksum =
			get!-checksum concat(personal!-dir!*,"/patches.fsl"))
		 then <<terpri();
			printc "*** System is already up-to-date";
			close remote;
			if remote!-checksum = c
			 then rename!-home!-patch!-file();
			return nil>>;
	       write!-patch!-file(personal!-dir!*,p,remote,remote!-checksum)>>
       else if remote!-checksum =
	     get!-checksum concat(lisp!-d,"/patches.fsl")
	then <<terpri();
	       printc "*** System is up-to-date already";
	       close remote;
	       rename!-home!-patch!-file();
	       return nil>>
       else if not file!-writeablep lisp!-d
	then rederr list("Cannot write to",lisp!-d)
       else <<write!-patch!-file(lisp!-d,p,remote,remote!-checksum);
	      rename!-home!-patch!-file()>>;
   % Load new patch file;
    load!-patches!-file()
  end;

flag('(update!_reduce),'opfn);

endmodule;

end;
