Monday, November 08, 2010 at 12:05 AM.

on checkForDeletions () {
		<<4/25/02; 12:23:14 AM by JES
			<<Skip files and folders whose name begins with the '.' character. Mirrors similar code in radio.upstream.uploadChangedFiles.
		<<3/10/02; 1:20:57 AM by JES
			<<Skip folders in pass 1 -- don't add them to the stream.
				<<This results in folders not being deleted from the upstreamed site.
				<<The reason for this is that there's a bug in the code that generates adrfile^.relativePath for folders, which could result in the entire site being deleted. Until that bug is fixed, folder deletion is disabled.
		<<2/24/02; 4:43:31 PM by JES
			<<Don't delete folders which contained their own upstream spec, since the upstream spec no longer exists.
		<<2/6/02; 7:26:35 PM by JES
			<<In pass 1, when stream of files to be deleted are filled, make sure that adrfile^.relativePath is defined. Prevents an error when deleting folders.
		<<1/31/02; 2:43:42 AM by JES
			<<When calling getUpstreamSpec, pass in the address of the filetable for the base upstream folder, instead of the address of the filetable for the file being deleted. This enables deletion of files which were contained in folders that no longer exist on disk.
			<<When adding files to the streams, add them at the begining of the list, instead of adding to the end. Also don't exclude folder paths. This enables deletion of folders as well as files. The files are deleted first, and then the folders containing the files are deleted.
		<<1/30/02; 12:09:54 AM by JES
			<<Call callbacks in radio.upstream.callbacks.upstreamFileWasDeleted and Callback scripts take the address of the upstream sub-table of the filetable. After the callbacks are called, the filetable is deleted, regardless of the return value.
		<<12/12/01; 9:13:19 PM by DW
			<<Don't upstream if the file is contained within a folder whose name begins with a #.
		<<12/11/01; 5:20:20 PM by DW
		<<11/16/01; 8:26:49 PM by PBS
			<<To make this more background-friendly, call thread.sleepTicks (0) in the loop.
		<<11/13/01; 2:15:41 AM by JES
			<<For deleted files, set[adrfile^.baseUpstreamFolder]. This triggeres the rebuild of directory.opml for the folder containing the pertinent upstream spec.
		<<9/28/01; 2:36:17 PM by JES
			<<Call callbacks defined at radio.upstream.callbacks.upstreamFileWasDeleted, before calling user callbacks at
	on logadd (htmltext) {
		if {
			radio.log.add ("Upstream", htmltext, startticks)}};
	local (pc = file.getPathChar ());
	on getBaseUpstreamFolder (f) {
		if string.lower (f) == string.lower ( {
			if file.exists ( + {
				return (f)}};
		while string.lower (f) != string.lower ( {
			f = string.popSuffix (string.delete (f, sizeOf (f), 1), pc) + pc;
			if file.exists (f + {
				return (f)}}};
	local (streams);
	new (tabletype, @streams);
	bundle { //pass 1 -- fill in streams
		local (adrfile);
		for adrfile in {
			if not file.exists (nameof (adrfile^)) {
				local (f = nameof (adrfile^), flDeleteNow = true);
				if not (f contains (pc + ".")) { //4/25/02 JES: skip invisible files/folders
					try { //add it to the streams table, if possible
						if not (file.filefrompath (f) beginswith "#") {
							if not adrfile^.flfolder {
								if not radio.upstream.inPoundFolder (f) { //12/12/01 by DW
									local (fldeleteupstream = true);
									if adrfile^.flfolder { //for folders that contain upstream specs, don't delete upstreamed folder
										if defined ([f] + pc + {
											local (sizetable = sizeOf (;
											local (ixfile = indexOf (adrfile^) + 1);
											local (lowerfoldername = string.lower (f));
											loop {
												if ixfile > sizetable {
												if string.lower (nameOf ([ixfile])) beginsWith lowerfoldername {
													<<2/22/02; 5:47:29 PM by JES
														<<Files/folders contained within this folder will always have filetables that appear ordinally after this folder's filetable.
														<<When we delete this filetable, the table at the same ordinal position in, will be a file contained within this folder (at least until we're done).
														<<After deleting the last filetable for this folder's contents, we break out of the loop, since the name of the filetable will not begin with the path to this folder.
													delete ([ixfile])}
												else {
											fldeleteupstream = false}};
									if fldeleteupstream {
										local (adrbasefolder =[getBaseUpstreamFolder (f)]);
										if defined (adrbasefolder^) {
											local (adrspec);
											if radio.upstream.getUpstreamSpec (adrbasefolder, @adrspec) {
												if not defined (adrfile^.relativePath) {
													adrfile^.relativePath = string.replaceAll (string.delete (f, 1, sizeOf (nameOf (adrspec^))), pc, "/")};
												local (adrstream = @streams.[nameof (adrspec^)]);
												if not defined (adrstream^) {
													new (tabletype, adrstream);
													adrstream^.adrspec = adrspec;
													adrstream^.fileslist = {}};
												adrstream^.fileslist = {adrfile} + adrstream^.fileslist;
												flDeleteNow = false}}}}}}};
					if flDeleteNow { //it's not needed in pass 2
						delete (adrfile)}}}}};
	<<scratchpad.streamsForDelete = streams
	bundle { //pass 2 -- send the delete messages to the clouds
		local (adrstream);
		for adrstream in @streams {
			local (startticks = clock.ticks ());
			local (adrspec = adrstream^.adrspec, adrdriver);
			if radio.upstream.findDriver (adrspec^.type, @adrdriver) {
				try {
					local (response);
					adrdriver^.deleteMultipleFiles (adrstream^.fileslist, adrspec, @response);
					<<scratchpad.response = response
					if response.flerror {
						<<local (errors = "")
						<<bundle //set errors
							<<Commented, it produces too much information, and even worse tells you about the folder structure on the server. Let's keep it simple for now.
							<<for s in response.errorlist
								<<if s != ""
									<<errors = errors + s + ", "
							<<errors = string.mid (errors, 1, sizeof (errors) - 2)
						logAdd (sizeof (adrstream^.fileslist) + " files were deleted on " + adrspec^.server + ", but there was at least one error reported by the server.")}
					else {
						local (ct = sizeof (adrstream^.fileslist));
						if ct == 1 {
							logAdd ("1 file was deleted on " + adrspec^.server + " without error.")}
						else {
							logAdd (ct + " files were deleted on " + adrspec^.server + " without error.")}}}
				else {
					logAdd ("Error deleting files on " + adrspec^.server + ": \"" + tryerror + "\"")}};
			bundle { //delete the file tables
				local (adrfile);
				for adrfile in adrstream^.fileslist {
					bundle { //call the callbacks, if present
						on runCallbacks (adrcallbacks) {
							for adr in adrcallbacks {
								try {
									while typeof (adr^) == addresstype {
										adr = adr^};
									try {adr^ (@adrfile^.upstream)}}}};
						if defined (radio.upstream.callbacks.upstreamFileWasDeleted) {
							runCallbacks (@radio.upstream.callbacks.upstreamFileWasDeleted)};
						if defined ( {
							runCallbacks (}};
					delete (adrfile)}}}};
	<<bundle //pre 12/11 code
		<<local (ctDeleted = 0, i, adrtable =, adrfile)
		<<local (lowerupstreamfolder = string.lower (
		<<for i = sizeof (adrtable^) downto 1
			<<thread.sleepTicks (0) //PBS 11/16/01: more background-friendly //PBS 11/20/01: interferes with debugging
			<<adrfile = @adrtable^ [i]
			<<if string.lower (nameof (adrfile^)) beginswith lowerupstreamfolder
				<<if not file.exists (nameof (adrfile^))
					<<bundle //call the driver for the file if it has one
						<<local (adrspec)
						<<if radio.upstream.getUpstreamSpec (adrfile, @adrspec)
							<<local (adrdriver)
							<<if radio.upstream.findDriver (adrspec^.type, @adrdriver)
								<<try {adrdriver^.fileWasDeleted (adrfile, adrspec)}
					<<bundle //call the callbacks, if present
						<<on runCallbacks (adrcallbacks)
							<<for adr in adrcallbacks
									<<while typeof (adr^) == addresstype
										<<adr = adr^
									<<try {adr^ (@adrfile^.upstream)}
						<<if defined (radio.upstream.callbacks.upstreamFileWasDeleted)
							<<runCallbacks (@radio.upstream.callbacks.upstreamFileWasDeleted)
						<<if defined (
							<<runCallbacks (
					<<[adrfile^.baseUpstreamFolder] = ()
					<<delete (adrfile)
		<<return (ctDeleted > 0)
<<bundle //test code
	<<checkForDeletions ()

This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.