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

on gatherAttributes (f, adratts, pta=nil) {
		<<3/24/02; 1:12:22 PM by JES
			<<Only call html.deletePageTableAddress if we had set the pageTableAddress locally.
		<<3/21/01; 3:24:41 PM by JES
			<<Set the pageTableAddress, so that macros called from directives get a rendering context. Delete it before returning, or when an error occurs.
		<<12/8/01; 8:45:10 PM by JES
			<<Fixed storage of the subTemplate attribute. It was being saved in the pageTable, when it should have been saved in pta^.radioResponder.atts.
		<<12/8/01; 6:32:07 PM by JES
			<<Convert line-endings to \r before running directives. Prevents errors for #prefs.txt files which have \n line-endings.
		<<11/12/01; 6:27:04 PM by JES
			<<Fixed a bug where higher-level # files would take precidence over lower-level ones. This broke, among other things, multiple upstream specs.
		<<11/11/01; 8:13:13 PM by JES
			<<Reversed the direction of the loop so that directives in #prefs files would override properly. Now we start at the top of the www folder, and traverse down to the file being rendered, instead of starting at the folder that contains the file being rendered, and popping out to the www folder.
			<<When running directives in #prefs files, call html.runDirectives with callScript, in the context of the pageTable, so that directives in #prefs files can access objects in the page table.
			<<Call html.runDirectives in a try so that we can show a more meaningful error message in the browser, if there's an error processing the directives.
			<<Made it possible to run directives in #prefs.opml files.
		<<6/23/01; 11:49:19 AM by DW
			<<We need to be able to gather attributes for files that have been deleted.
		<<6/14/01; 5:58:42 PM by DW
			<<Bug fixed -- possible infinite loop in radio.webServer.gatherAttributes. It breaks out of the loop when folder == The comparison is case-sensitive, which means the loop might never terminate. To be thread-safe, one might also check if the folder is the top level of the disk (in case the www folder pref changes while atts are being gathered for a file).
		<<5/6/01; 9:41:01 AM by DW
			<<If it's a folder, start the walk in the folder itself. This means that the rendering of the folder uses the template contained within the folder. This is essential for the top-level folder (where else could its template come from?) and probably the correct behavior for lower-level folders.
	local (flDeletePageTableAddress = false);
	if pta != nil {
		try { //check to see if a pageTableAddress is set
			html.getPageTableAddress ()}
		else { //pageTableAddress not set -- set it, and set the flag to delete it
			html.setPageTableAddress (pta);
			flDeletePageTableAddress = true}};
	try { //if an error happens, we need a chance to delete the pageTableAddress
		new (tabletype, adratts);
		local (folder);
		if file.exists (f) {
			if file.isfolder (f) {
				folder = string.lower (f)}
			else {
				folder = string.lower (file.folderfrompath (f))}}
		else {
			folder = file.folderfrompath (f)};
		on dofolder (folder) {
			local (f, attname, adratt);
			fileloop (f in folder) {
				fname = file.filefrompath (f);
				if fname beginswith "#" {
					attname = string.nthfield (fname, ".", 1);
					if file.isfolder (f) { //drop pathchar at end of name
						attname = string.mid (attname, 1, sizeof (attname) - 1)};
					attname = string.delete (attname, 1, 1); //delete the pound sign
					adratt = @adratts^.[attname];
					case string.lower (attname) {
						"subtemplate" {
							if pta != nil {
								local (adrsubtemplate = @pta^.radioResponder.atts.subTemplate);
								local (filetext = file.readWholeFile (f));
								if radio.webserver.getFileMimeType (f) == "text/x-opml" {
									local (adrfile =[f]);
									if not defined (adrfile^) {
										radio.file.getFileAttributes (f, @adrfile)};
									filetext = string (adrfile^.outline)}
								else { //fix line-endings
									filetext = string.replaceAll (filetext, "\r\n", "\r");
									filetext = string.replaceAll (filetext, "\n", "\r")};
									<<For subtemplates we store the text of the subtemplate, and do substitution.
								if defined (adrsubtemplate^) {
									adrsubtemplate^ = string.replaceall (adrsubtemplate^, "{bodytext}", filetext)}
								else {
									adrsubtemplate^ = string (filetext)}}};
						"prefs" {
							try { //directory listings will fail if directives fail to process
								if pta != nil {
									<<html.rundirectives (string (file.readwholefile (f)), pta)
									local (directivesText = file.readWholeFile (f));
									case radio.webserver.getFileMimeType (f) {
										"text/x-opml" {
											op.xmlToOutline (directivesText, @directivesText);
											table.assign (@directivesText, string (directivesText))}};
									directivesText = string.replaceAll (directivesText, "\r\n", "\r");
									directivesText = string.replaceAll (directivesText, "\n", "\r");
									callScript (@html.rundirectives, {directivesText, pta}, pta)}}
							else {
								scriptError ("Error running directives in \"" + f + "\":<p>" + tryError)}}}
					else {
						<<if not defined (adratt^)
						adratt^ = f}}}};
		local (pc = file.getPathChar ());
		local (nomad =;
		loop {
			dofolder (nomad);
			if string.lower (nomad) == folder {
			local (pathpart = string.nthField (string.delete (folder, 1, sizeOf (nomad)), pc, 1));
			if pathPart == "" {
			nomad = nomad + pathpart + pc};
		<< = pta^; edit (
		if flDeletePageTableAddress {
			html.deletePageTableAddress ()};
		return (true)}
	else { //delete the pageTableAddress if necessary, and re-throw the error
		if flDeletePageTableAddress {
			html.deletePageTableAddress ()};
		scriptError (tryError)}} //throw the error up the stack
<<bundle //test code
	<<bundle //attribute testing
		<<gatherAttributes (radio.file.getAbsolutePath ("/index.txt"), @scratchpad.atts, @scratchpad.atts)
		<<edit (@scratchpad.atts)
	<<bundle //pageTableAddressTesting
		<<html.setPageTableAddress (@scratchpad.atts)
		<<gatherAttributes (radio.file.getAbsolutePath ("/index.txt"), @scratchpad.atts, @scratchpad.atts)
		<<local (pta = html.getPageTableAddress ())
		<<gatherAttributes (radio.file.getAbsolutePath ("/index.txt"), @scratchpad.atts, @scratchpad.atts)
		<<html.deletePageTableAddress ()

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.