DNA.
wyrm-uri
Version.
1.1.9
Namespace.
::wyrm
Command.
::wyrm::uri
Language.
c
Manpage.
uri (1WY)
Manpage.
uriassist (1WY)
Manpage.
wyrm_uriGet (3WY)
Manpage.
wyrm_uriAbsolutise (3WY)
Manpage.
wyrm_uriMethod (3WY)
Manpage.
wyrm_urispace (3WY)
Manpage.
urispace (4WY)
Testbase.
Test Script
Test Report
Import.
Interface.
wyrm-io
wyrmwif
wyrm-oav
Package.
wyrmwif
wyrm-assoc
Export.
Implementation.
wyrm-uri.c
Interface.
wyrm-urispace.h
wyrm-uri.h
Interface-generator.
wyrm-urispace.gen
Package.
wyrmuri.dylib

URI Parsing and Methods

Sections.
Compile Files
URI Command
URI part manipulation
Absolutising URIs
URI Method
URI Space
Character Escapes
Parse URI into parts
Generate parts into URI
Paths
Parameters
URI parts
uriassist command
urispace
Generic
Relative
file
data
ftp
gopher
http
mailto
nntp
pop
channel
none
oav
Test Base
Make.
Interface.
rule $include/wyrm-urispace.h [
      list \
        $include/wyrm-urispace.gen \
        $so/wyrmwif[info sharedlibextension] \
        $so/wyrmassoc[info sharedlibextension] \
] "
  -rm -f $include/wyrm-urispace.h
  TCLLIBPATH=$so tclsh <$include/wyrm-urispace.gen >$include/wyrm-urispace.h
"
Package.
compile -ld -cc -o [export package] [export implementation] -- -list [
  import interface
] -list [
  export interface
] [
  export implementation
]
Script.
rule clean :: {} "
  -rm $test/wyrm-uri.TESTING
"
rule clobber :: {} "
  -rm $include/wyrm-uri.h
  -rm $so/wyrmuri[info sharedlibextension]
"
   
top

1 :: This is an object orientish way to use URIs, with the URI scheme identifying the kind of objects. URIs can be parsed with parts extracted, replaced, or added. A method can be identified from the scheme and then evaluated. The objects are stored in an OAV mapping and use OAV delegation and parent identification.

Copyright (C) 2002 SM Ryan

Wyrmwif Tcl extensions. For non-profit uses only, provided this copyright is preserved on all copies, this work may be freely copied, modified, redistributed, compiled, and incorporated in other works. This work is distributed with no warranty of any kind; no author or distributor accepts any responsibility for the consequences of using it, or for whether it serves any particular purpose or works at all, unless he or she says so in writing.

   
   

Compile Files

   
top
 
section    top

static const char COPYRIGHT[] = "wyrm-uri.dna - Copyright (C) 2002 SM Ryan.  All rights reserved.";

#include "wyrm-uri.h"
#include "wyrm-io.h"
#include "wyrm-assoc.h"
#include "wyrm-oav.h"
static
#include "wyrm-urispace.h"


#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct Part Part,*pPart,*Parts;

<Forward declarations>
<OAV space>
<Convert protected string to escapes>
<Convert escapes to raw string>
<Parts>
<Make the path canonical>
<Get part of a URI>
<Get all parts of a URI>
<Convert from a Macintosh file join compatiable path list>
<Convert from a Unix file join compatiable path list>
<Put a part into a URI>
<Put a list of parts into a URI>
<Remove a part from a URI>
<Select and evaluate a URI method>
<Absolutise a URI from a base URI>
<URI command>
<Define the URI command to the interpretter>
<uriassist (1WY)>
			
		
   
   

URI Command

   
top
 
section    top

5. URI definitions :: This package provides an object orientish way to use URIs, with the URI scheme identifying the kind of objects. URIs can be parsed with parts extracted, replaced, or added. A method can be identified from the scheme and then evaluated. The objects are stored in an OAV mapping and use OAV delegation and parent identification. This OAV mapping is called the urispace.

A URI is a Uniform Resource Indicator and a URL is a Uniform Resource Locator, a subset of URIs. The other kind of URI is the experimental URN Uniform Resource Name. A URL indicates one specific location of a resource and how to access it. A URN indicates a resource in general; the URN must then be resolved to a URL automatically. URNs are still experimental; in practice all URIs are just URLs. These routines can however manipulate both URLs and URNs.

Each URI belongs to a scheme, such as 'ftp' or 'telnet'. The scheme begins the URI (except the relative scheme). A URI without a beginning scheme is always the '(relative)' scheme URL. If the scheme cannot be identified in the urispace OAV mapping, it is treated as a generic scheme.

The syntax of a URI depends on its scheme; the syntax defines the URI as sequence of parts with various delimiters. For example, with the URL 'ftp://hodgepodge/x/y/z;type=d', the scheme is 'ftp' and the parts are 'scheme' (ftp), 'host' (hodgepodge), 'path' (/x/y/z), and the 'parameter' (;type=d). The 'ftp' scheme has additional parts such as 'user' and 'port' which are empty in this specific URI. Each part of a URI is named; these names are used to extract, replace, and remove parts in parsed URIs. Part names are case sensitive.

 
section    top
proc uriCommand—Implement the Tcl command uri. returnsTCL_OK or TCL_ERROR.
input N—Number of parameters.
input P—Parameters.
io intr—The command result or an error message.

static int uriCommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	chars opc;
	Obj op,uri,r;
	int rc = TCL_OK;
	Tcl_ResetResult(intr);
	if (N<2) {usage:
		Tcl_AppendResult(intr,"usage: uri <operation> <uri> ...",0);
		return TCL_ERROR;
	}
	P++; N--;
	opc = Tcl_GetStringFromObj(op=*P++,0); N--;
	if (!streq(opc,"space")) {
		if (N<=0) goto usage;
		uri = *P++; N--;
	}
	if (streq(opc,"base")) {
		if (N!=1) {
			Tcl_AppendResult(intr,"usage: uri base <relative uri> <base uri>",0);
			return TCL_ERROR;
		}
		r = wyrm_uriAbsolutise(intr,uri,*P);
		if (r) Tcl_SetObjResult(intr,r); else rc = TCL_ERROR;
		decr(r);
	}else if (streq(opc,"space")) {
		if (N>1) {
			Tcl_AppendResult(intr,"usage: uri space [<mapping>]",0);
			return TCL_ERROR;
		}
		Tcl_SetObjResult(intr,wyrm_urispace(intr,N==0?0:*P));
	}else if (streq(opc,"part")) {
		switch (N) {
			case 0:		r = wyrm_uriGetAll(intr,uri); break;
			case 1:		r = wyrm_uriGet(intr,uri,Tcl_GetStringFromObj(P[0],0)); break;
			case 2:		r = wyrm_uriPut(intr,uri,Tcl_GetStringFromObj(P[0],0),P[1]); break;
			default:	r = wyrm_uriPutList(intr,uri,N,(Obj*)P); break;
		}
		if (r) Tcl_SetObjResult(intr,r); else rc = TCL_ERROR;
		decr(r);
	}else if (streq(opc,"remove")) {
		if (N==0) {
			Tcl_AppendResult(intr,"usage: uri remove <uri> <part>...",0);
			return TCL_ERROR;
		}
		incr(uri);
		while (N>0) {
			r = wyrm_uriRemove(intr,uri,Tcl_GetStringFromObj(*P,0)); P++; N--;
			decr(uri); uri = r;
		}
		Tcl_SetObjResult(intr,r); decr(r);
	}else {
		rc = wyrm_uriMethod(intr,uri,opc,N,(Obj*)P);
	}
	return rc;
}

		
 
section    top

static int uriCommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]);

		
 
section    top
proc Wyrmuri_Init—Implement the Tcl command uri.
output intr—The uri command is defined.

int Wyrmuri_Init(Intr intr) {
	Tcl_PkgProvide(intr,"wyrmuri",VERSION);
	Tcl_PkgRequire(intr,"wyrmwif","1",0);
	Tcl_PkgRequire(intr,"wyrmassoc","1",0);
	Tcl_CreateObjCommand(intr,"::wyrm::uri",uriCommand,0,0);
	Tcl_CreateObjCommand(intr,"::wyrm::uriassist",uriassist,0,0);
	if (Tcl_VarEval(intr,
		"proc ::wyrm::wif::protocol {/line/ /vars/ /script/} {\n",
			"foreach {/var/ /val/} ${/vars/} {set ${/var/} ${/val/}}\n",
			"set line ${/line/}\n",
			"set /rc/ [catch ${/script/} /rs/]\n",
			"set /result/ [list ${/rc/} ${/rs/}]"
			"foreach {/var/ /val/} ${/vars/} {lappend /result/ ${/var/} [set ${/var/}]}\n",
			"return ${/result/}\n",
		"}\n",
	0)!=TCL_OK) return TCL_ERROR;
			
	staticUriSpace();
	return Tcl_VarEval(intr,
			"namespace eval ::wyrm {namespace export uri}\n",
			"namespace eval ::wyrm::wif {variable urispace /%static/urispace.map}\n",
			0);
}
int Wyrmuri_SafeInit(Intr intr) {
	return rprintf(intr,"%!package not available in a safe interpretter",TCL_ERROR);
}

		
 
section    top

int Wyrmuri_Init(Intr intr);
int Wyrmuri_SafeInit(Intr intr);

		
   
   

URI part manipulation

   
top
 
section    top
 
section    top
 
section    top
proc wyrm_uriGet—Get a URI part. Returns extracted part or NULL if error.
Memory. Caller decrements reference count while finished.
output intr—Possible error message.
input part—Which part to extract.
input uri—URI to parse and examine.

Obj wyrm_uriGet(Intr intr,Obj uri,chars part) {
	Parts parts = parseURI(intr,uri),gotten; Obj R;
	if (!parts) return 0;
	if (streq(part,"PATH") || strieq(part,"macintosh") || strieq(part,"unix")) {
		<Get file join compatiable path list>
	}else {
		gotten = getPart(parts,part);
		if (!gotten) {
			rprintf(intr,"unknown part: %s",part); R = 0;
		}else if (gotten->value) {
			R = incr(Tcl_NewStringObj(gotten->value,gotten->length));
		}else {
			rprintf(intr,"missing: %s",part); R = 0;
		}
		if (streq(part,"path")) R = canonicalPath(R);
	}
	while (parts) {
		parts = deletePart(parts);
	}
	return R;
}

		
 
section    top
Obj wyrm_uriGet(Intr intr,Obj uri,chars part);
 
section    top
 
section    top
 
section    top
proc wyrm_uriGetAll—Get all URI parts. Returns part/value pairs list of existing parts or NULL if error.
Memory. Caller decrements reference count while finished.
output intr—Possible error message.
input uri—URI to parse and examine.

Obj wyrm_uriGetAll(Intr intr,Obj uri) {
	Parts parts = parseURI(intr,uri); Obj R;
	if (!parts) return 0;
	R = incr(Tcl_NewObj());
	while (parts) {
		if (parts->value) {
			Obj name,value;
			deescapePart(parts);
			name = Tcl_NewStringObj(parts->name,-1);
			value = incr(Tcl_NewStringObj(parts->value,parts->length));
			if (streq(parts->name,"path")) value = canonicalPath(value);
			if (Tcl_ListObjAppendElement(intr,R,name)!=TCL_OK
					|| Tcl_ListObjAppendElement(intr,R,value)!=TCL_OK)
			{
				decr(value); decr(R); R = 0; break;
			}
			decr(value);
		}
		parts = deletePart(parts);
	}
	while (parts) parts = deletePart(parts);
	return R;
}

		
 
section    top
Obj wyrm_uriGetAll(Intr intr,Obj uri);
 
section    top
 
section    top
 
section    top
proc wyrm_uriPut—Put URI part value; returns new URI or NULL if error.
Memory. Caller decrements reference count while finished.
output intr—Possible error message.
input newpart—New value of the part.
input part—Which part to replace or add.
input uri—URI to parse and modify.

Obj wyrm_uriPut(Intr intr,Obj uri,chars part,Obj newpart) {
	Parts parts = parseURI(intr,uri); Obj cpart = 0, R = 0; char state = 'D';
	incr(newpart);
	if (streq(part,"macintosh")) {
		Obj X = convertMacintosh(intr,newpart);
		if (X) {
			decr(newpart); newpart = X;
			part = "path"; state = 'E';
		}
	}else if (streq(part,"PATH") || strieq(part,"unix")) {
		Obj X = convertUnix(intr,newpart);
		if (X) {
			decr(newpart); newpart = X;
			part = "path"; state = 'E';
		}
	}
	cpart = streq(part,"path") ? canonicalPath(newpart) : newpart;
	if (cpart) {
		Parts putting; int n; chars s = Tcl_GetStringFromObj(cpart,&n);
		if (!parts) {decr(cpart); return 0;}
		putting = putPart(parts,part,s,n,state);
		if (!putting) {
			beforeParts(parts,part,-1,s,n,state);
		}
		decr(cpart);
		R = generateURI(intr,parts);
	}else {
		R = 0;
	}
	while (parts) parts = deletePart(parts);
	return R;
}

		
 
section    top
Obj wyrm_uriPut(Intr intr,Obj uri,chars part,Obj newpart);
 
section    top
proc wyrm_uriPutList—Put URI part value. Returns new URI or NULL if error.
Memory. Caller decrements reference count while finished.
input N—Number of part/value pairs list.
input P—Part/value pairs list.
output intr—Possible error message.
input uri—URI to parse and modify.

Obj wyrm_uriPutList(Intr intr,Obj uri,int N,Obj *P) {
	Parts parts = parseURI(intr,uri),putting; Obj R;
	if (!parts) return 0;
	while (N>1) {
		chars part = Tcl_GetStringFromObj(P[0],0); Obj cpart;
		Obj newpart = incr(P[1]); int n; chars s; char state = 'D';
		if (streq(part,"macintosh")) {
			Obj X = convertMacintosh(intr,newpart);
			if (X) {
				decr(newpart); newpart = X;
				part = "path"; state = 'E';
			}
		}else if (streq(part,"PATH") || strieq(part,"unix")) {
			Obj X = convertUnix(intr,newpart);
			if (X) {
				decr(newpart); newpart = X;
				part = "path"; state = 'E';
			}
		}
		cpart = streq(part,"path") ? canonicalPath(newpart) : newpart;
		s = Tcl_GetStringFromObj(cpart,&n);
		N -= 2; P += 2;
		putting = putPart(parts,part,s,n,state);
		if (!putting) {
			beforeParts(parts,part,-1,s,n,state);
		}
		decr(cpart);
	}
	R = generateURI(intr,parts);
	while (parts) parts = deletePart(parts);
	return R;
}

		
 
section    top
Obj wyrm_uriPutList(Intr intr,Obj uri,int N,Obj *P);
 
section    top
 
section    top
 
section    top
proc wyrm_uriRemove—Put URI part value. Returns new URI or NULL if error.
Memory. Caller decrements reference count while finished.
output intr—Possible error message.
input part—Which part to remove.
input uri—URI to parse and modify.

Obj wyrm_uriRemove(Intr intr,Obj uri,chars part) {
	Parts parts = parseURI(intr,uri),removing; Obj R;
	if (!parts) return 0;
	removing = findPart(parts,part);
	if (removing) removing = deletePart(removing);
	if (streq(part,"scheme")) {
		parts = beforeParts(removing,"scheme",-1,"(relative)",-1,'e')->prev;
		parts = parts->prev;
	}
	R = generateURI(intr,parts);
	while (parts) parts = deletePart(parts);
	return R;
}

		
 
section    top
Obj wyrm_uriRemove(Intr intr,Obj uri,chars part);
   
   

Absolutising URIs

   
top
 
section    top

30. How to absolutise a relative URI from a base URI :: Absolutising a URI is not just concatenating two paths together. The details are given in RFC2396. In summary,

If the URI has a scheme, it is not a relative URI, and it is returned unchanged.
Otherwise the absolutised URI has the same scheme as base URI.
If the URI has a network address (// [ user@ ] host [ :port ] ), the absolutised URI has that network address and path of the URI. Otherwise it has the network address of the base.
If the URI has a network address and/or it has absolute path (starts with '/'), this is the path of the absolutised URI.
Otherwise path of base URI up to its last '/' is concatenated with the path of URI. For example,
relative /a/b/c + base /x/y/z becomes /a/b/c
relative a/b/c + base /x/y/z becomes /x/y/a/b/c
relative a/b/c + base /x/y/z/ becomes /x/y/z/a/b/c
relative ../b/c + base /x/y/z becomes /x/y/../b/c which is reduced to /x/b/c
The absolutised parameter, search, and fragment are those of the URI. (The uri routines regard the fragment as part of the URI. Officially the fragment is a separate construct next to the URI.)

 
section    top
 
section    top
 
section    top
proc wyrm_uriRelative—The absolutised URI or NULL if error. It might be the same as rel input. Absolutise a relative URI with respect to a base.
Memory. Caller decrements reference count while finished, even if this is the same object as rel.
input base—An absolute URI (not verified), which supplies missing parts to make a relative URI absolute.
output intr—Possible error message.
input rel—If a relative URI, it will be made absolute from base.

static pPart absp(pPart p) {return p && p->value ? p : 0;}

Obj wyrm_uriAbsolutise(Intr intr,Obj rel,Obj base) {
	Parts rparts = parseURI(intr,rel);
	if (streq(getPart(rparts,"scheme")->value,"(relative)")) {
		pPart	ruser  = absp(getPart(rparts,"user"));
		pPart	rhost  = absp(getPart(rparts,"host"));
		pPart	rport  = absp(getPart(rparts,"port"));
		Parts	bparts = parseURI(intr,base);
		if (bparts) {
			putPart(rparts,"scheme",bparts->value,bparts->length,-1);
			if (!ruser && !rhost && !rport) {
				pPart	buser  = absp(getPart(bparts,"user"));
				pPart	bhost  = absp(getPart(bparts,"host"));
				pPart	bport  = absp(getPart(bparts,"port"));
				pPart	rpath  = absp(getPart(rparts,"path"));
				if (buser) putPart(rparts,"user",buser->value,buser->length,-1);
				if (bhost) putPart(rparts,"host",bhost->value,bhost->length,-1);
				if (bport) putPart(rparts,"port",bport->value,bport->length,-1);
				if (!(rpath && rpath->length>0 && rpath->value[0]=='/')) {
					pPart	bpath  = absp(getPart(bparts,"path"));
					chars s; int n;
					Obj S = incr(Tcl_NewObj());
					if (bpath) {
						s = bpath->value; n = bpath->length;
						while (n>0 && s[n-1]!='/') n--;
						Tcl_AppendToObj(S,s,n);
					}
					if (bpath && rpath) {
						Tcl_AppendToObj(S,"/",-1);
					}
					if (rpath) {
						Tcl_AppendToObj(S,rpath->value,rpath->length);
					}
					S = canonicalPath(S);
					s = Tcl_GetStringFromObj(S,&n);
					putPart(rparts,"path",s,n,-1);
					decr(S);
				}
			}
			while (bparts) bparts = deletePart(bparts);
		}else {
			rel = 0; goto exit;
		}
	}
	rel = generateURI(intr,rparts);
exit:
	while (rparts) rparts = deletePart(rparts);
	return rel;
}

		
 
section    top
Obj wyrm_uriAbsolutise(Intr intr,Obj rel,Obj base);
   
   

URI Method

   
top
 
section    top
 
section    top
 
section    top
 
section    top
proc wyrm_uriMethod—Set and get the urispace. Returns TCL_OK or TCL_ERROR.
input N—Number of additional parameters.
input P—Additional parameters.
output intr—Method result or error message; also the evaluation context.
input method—The method name.
input uri—The URI.

int wyrm_uriMethod(Intr intr,Obj uri,chars method,int N,Obj *P) {
	Obj substkey = oprintf("subst.%{y}s",uri);
	Obj subst = wyrm_oavGet(intr,wyrm_urispace(intr,0),substkey,0,0);
	Obj actualuri = subst ? subst : incr(uri);
	Parts parts = parseURI(intr,actualuri);
	decr(substkey);
	if (parts) {
		chars scheme = parts->value;
		Obj	key = oprintf("uri.%s.%s",scheme,method);
		int rc; Obj *Q;
		Q = nheap(N+1,Obj);
		*Q = actualuri;
		if (N>0) memcpy(Q+1,P,sizeof(Obj)*N);
		rc = wyrm_oavDo(intr,wyrm_urispace(intr,0),key,N+1,Q);
		if (rc!=TCL_OK) {
			chars r = Tcl_GetStringResult(intr);
			chars k = Tcl_GetStringFromObj(key,0);
			static char  missing[] = "missing: ";
			if (strbegins(missing,r) && streq(k,r+sizeof missing-1)) {
				rprintf(intr,"unknown %s method: %s",scheme,method);
			}
		}
		while (parts) parts = deletePart(parts);
		decr(key); dispose(Q); decr(actualuri);
		return rc;
	}else {
		decr(actualuri); return TCL_ERROR;
	}
}

		
 
section    top
int wyrm_uriMethod(Intr intr,Obj uri,chars method,int N,Obj *P);
   
   

URI Space

   
top
 
section    top

42. Purpose of a URI space :: Information about URIs is stored in an OAV mapping referred to as the urispace. This includes the parsing pattern, the generation format, the list of parts, and definitions of methods. OAV permits objects to be delegated, and for parents to be searched. All this flexibility is available for resolving information about URIs. The uri command consults this OAV mapping to parse URIs and evaluate methods on them.

The uri command has a static mapping compiled into it to provide a default set of definitions. A default internal urispace is initially defined. This space can be supplemented by defining another mapping, possibly delegating to or copying from the original default mapping. The new mapping can then be made the new urispace.

The wyrm-urispace defines standard URLs for file, data, ftp, gopher, http, mailto, news, nntp, pop, relative, and nonstandard URLs for oav mappings and no resource. A generic URI is defined which should match most other URIs. The methods open, get, put, delete, status, and children methods are defined for each of these URIs, though in many cases the default implementation is to return an error that the operation is not really implemented.

 
section    top

43. Construction of a URI space :: If a uri has the scheme, the parse pattern is gotten with the OAV key uri.scheme.+pattern, the parts list with uri.scheme.+parts, the generate format with uri.scheme.+generate, and the Tcl code implementing method with uri.scheme.method. And these keys may end up delegated elsewhere.

New URIs can be added to a modifiable urispace with the oav command. For example a "qwerty" scheme can be defined with

oav put [uri space] uri.qwerty.+parts {scheme text}
oav put [uri space] uri.qwerty.+pattern ^(qwerty):(.*)$}
oav put [uri space] uri.qwerty.+generate {s:s}
oav method [uri space] uri.qwerty.open {uri args} {open "qwerty" r}

A generic URI is provided by defining the keys uri.+pattern, uri.+parts, uri.+generate, and uri.method for generic methods. If a scheme is not (completely) defined in the urispace, then the OAV delegation rules will find these generic pattern, parts, generate, and methods for what is not specifically defined.

And keys not conforming to these patterns may be in the urispace. They are not directly accessed, but they may be delegated to or used for routines other than uri.

 
section    top
 
section    top
 
section    top
proc wyrm_urispace—Set and get the urispace. Returns the urispace, always nonnull, even if an empty space has to be created.
input newspace—If not NULL, the new urispace.
input $::staticUriSpace"—Possible static mapping which is the default mapping.
 
section    top

Obj wyrm_urispace(Intr intr,Obj newspace) {
	Obj urispace = newspace
		? Tcl_SetVar2Ex(intr,"::wyrm::wif::urispace",0,newspace,0)
		: Tcl_GetVar2Ex(intr,"::wyrm::wif::urispace",0,0);
	return urispace;
}

		
 
section    top
Obj wyrm_urispace(Intr intr,Obj newspace);
   
   

Character Escapes

   
top
 
section    top
proc escapeString—Convert a raw string into one with reserved characters escaped.
output S—Characters from s, escaped and no, are appended.
Memory. Caller must initialise and free.
input does—Do escape these characters no matter what.
input dont—Do not escape these characters no matter what.
input n—Number of raw characters.
input s—Raw characters.

static void escapeString(Tcl_DString *S,chars s,int n,chars does,chars dont) {
	while (n>0) {
		char c[8]; int m;
		bool e = *s==0 ? true
				: does && strchr(does,*s)!=0 ? true
				: dont && strchr(dont,*s)!=0 ? false
				: *s<=32 || 127<=*s ? true
				: !(isalnum(*s) || *s=='-' || *s=='_' || *s=='.' || *s=='!'
								|| *s=='~' || *s=='*' || *s=='+' || *s=='\''
								|| *s=='(' || *s==')' || *s=='=' || *s=='/');
		if (e) {
			sprintf(c,"%%%02X",*s); m = -1;
		}else {
			c[0] = *s; c[1] = 0; m = 1;
		}
		Tcl_DStringAppend(S,c,m); s++; n--;
	}
}

		
 
section    top

static void escapeString(Tcl_DString *S,chars s,int n,chars does,chars dont);

		
 
section    top
proc deescapeString—Convert a string with escaped characters into one without; returns the length of escaped string.
output r—Deescaped characters from s.
Memory. Caller must allocate at least n+1 bytes. Caller frees.
input n—Length of escaped string.
input s—Escaped string.

static int deescapeString(chars s,int n,chars r) {
	int m = 0; char x[3]; x[2] = 0;
	while (n>0) {
		if (n>=3 && s[0]=='%' && isxdigit(s[1]) && isxdigit(s[2])) {
			x[0] = s[1]; x[1] = s[2]; s+=3; n-=3;
			*r++ = strtol(x,0,16); m++;
		}else {
			*r++ = *s++; m++; n--;
		}
	}
	*r = 0;
	return m;
}

		
 
section    top

static int deescapeString(chars s,int n,chars r);

		
   
   

Parse URI into parts

   
top
proc parseURI—URI parse patterns according to its scheme's pattern. Return parts of the URI or NULL if error.
Memory. Caller deletes all parts by looping deleteParts.
output intr—Possible error message.
input uri—The URI.

static Parts parseURI(Intr intr,Obj uri) {
	chars		scheme;
	Tcl_RegExp	re;
	Parts		parts = 0;
	Obj			pattern = 0, partlist = 0;
	<Parse uri scheme>
	<Parse the uri with the scheme pattern>
	<Assigned parsed substrings to parts>
exit:
	decr(pattern);
	decr(partlist);
	return parts;
error:
	while (parts) parts = deletePart(parts);
	goto exit;
}

		
 
section    top

static Parts parseURI(Intr intr,Obj uri);

		
 
section    top
{

Obj key = oprintf("uri.%s.+pattern",scheme);
Obj urispace = wyrm_urispace(intr,0);
pattern = wyrm_oavGet(intr,urispace,key,0,0);
decr(key); key = 0;

		}
 
section    top
{

Obj key = oprintf("uri.%s.+parts",scheme);
partlist = wyrm_oavGet(intr,wyrm_urispace(intr,0),key,0,0);
decr(key); key = 0;

		}
 
section    top

Obj key = oprintf("uri.%s.+generate",scheme);
Obj g = wyrm_oavGet(intr,wyrm_urispace(intr,0),key,0,0);
chars generate = g ? Tcl_GetStringFromObj(g,0) : 0;

		
 
section    top
{

int n,i; chars s = Tcl_GetStringFromObj(uri,&n);
for (i=0; ; i++) {
	if (s[i]==':') {
		n = i;
		break;
	}else if (i>=n || !(isalnum(s[i]) || s[i]=='%' || s[i]=='-' || s[i]=='+' || s[i]=='.')) {
		s = "(relative)"; n = strlen(s);
		break;
	}
}
parts = beforeParts(0,"scheme",-1,s,n,'e');
deescapePart(parts); scheme = parts->value;
		
		}
 
section    top
{

chars u = Tcl_GetStringFromObj(uri,0);
int rc;
<Get the scheme specific parse pattern>
if (!pattern) {
	rprintf(intr,"(missing generic pattern: scheme %s)",scheme);
	goto error;
}
re = Tcl_RegExpCompile(intr,Tcl_GetStringFromObj(pattern,0));
if (!re) goto error;
rc = Tcl_RegExpExec(intr,re,u,u);
if (rc<0) {
	goto error;
}else if (rc==0) {
	rprintf(intr,"could not parse uri: scheme %s: pattern %{y}s",scheme,pattern);
	goto error;
}

		}
 
section    top
{

int i,N; Obj *P;
<Get the scheme specific parts list>
if (!partlist) {
	rprintf(intr,"<parts are missing: scheme %s>",scheme);
	goto error;
}
if (Tcl_ListObjGetElements(intr,partlist,&N,&P)!=TCL_OK) goto error;
for (i=0; i<N; i++) {
	int n; chars name = Tcl_GetStringFromObj(P[i],&n);
	chars first,last;
	if (streq(name,"scheme")) continue;
	Tcl_RegExpRange(re,i+1,(CONST char**)&first,(CONST char**)&last);
	beforeParts(parts,name,n,first,first ? last-first : 0,first ? 'e' : '.');
}

		}
   
   

Generate parts into URI

   
top
 
section    top
proc generateURI—Generate a URI according to its scheme's pattern. Returns generated URI or NULL if error.
Memory. Caller decrements when finished.
output intr—Possible error message.
input parts—The parsed (and maybe editted) URI parts.
			
<Generate URI from parts recursive step>

static Obj generateURI(Intr intr,Parts parts) {
	if (!parts) {
		return 0;
	}else {
		chars scheme = parts->value; Obj partlist;
		pPart *vp = 0;
		<Get scheme specific parts>
		for (i=0,curr=parts; i<N; i++,j++) {
			chars name = Tcl_GetStringFromObj(P[i],0);
			if (streq(name,"parameter")) xparam = j;
			p = curr;
			do {
				if (streq(p->name,name)) {
					vp[j] = p; curr = p->next; break;
				}
			} while (p=p->next,p!=curr);
		}
		<Repack separated parameters>
		decr(partlist);
		<Generate URI from scheme parts list and format>
	}
}

		
 
section    top

static Obj generateURI(Intr intr,Parts parts);

		
 
section    top
 
section    top

case 'x':
	(*part) += 1;
	break;

		
 
section    top
 
section    top

case 'h':
	if ((**part) && any0 && (**part)->value && (**part)->length) {
		escapePart((**part),0,0);
		if ((**part)->length==0 || (**part)->value[0]!='/')
			Tcl_AppendToObj(G0,"/./",-1);
	}
	break;

		
 
section    top
 
section    top

		case 's':
			if ((**part) && (**part)->value) {
				escapePart((**part),0,0);
				Tcl_AppendToObj(G0,(**part)->value,(**part)->length);
				any0 = true;
			}
			(*part) += 1;
			break;