Skip to main content
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: John Watson
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: John Watson ( @wizputer )

ColdFusion Path Usage And Manipulation Overview

By on
Tags:

Just a quick little ColdFusion path explanation. Sometimes I get confused about some path stuff, so I thought I would jot it down to help imprint in my grey matter. All path demonstrations use the following directory structure:

./index.cfm
./sub/sub.cfm

Additionally, we never call the sub.cfm page directly. Sub.cfm is included by index.cfm so that we can see how included sub-directories work with ColdFusion paths:

ExpandPath()

This creates an absolute path using a path relative to the directory of the currently executing page. This does NOT necessarily mean the currently executing template. If we run:

#ExpandPath( "./" )#

... in both of the template, index.cfm and sub.cfm respectively give us:

D:\sites\kinky_solutions\site_v1\testing\paths\
D:\sites\kinky_solutions\site_v1\testing\paths\

Notice that regardless of the template from which it was called, ExpandPath() gives us an absolute path based on index.cfm, the currently executing page. This is one of the trickiest things for me when I deal with uploading files. I always have to remember that ExpandPath() is NOT relative to the action page I am processing - it is relative to my front controller.

GetCurrentTemplatePath()

This returns the absolute path of the currently executing template. The currently executing template is the one in which the code is being called from, not necessarily the path in the browser URL. If we run:

#GetCurrentTemplatePath()#

... in both of the templates, index.cfm and sub.cfm respectively give us:

D:\sites\kinky_solutions\site_v1\testing\paths\index.cfm
D:\sites\kinky_solutions\site_v1\testing\paths\sub\sub.cfm

This is a really useful function when you need to get a path relative to the current template. To get the directory of this template, you can always use GetDirectoryFromPath(). If we run:

#GetDirectoryFromPath(
	GetCurrentTemplatePath()
	)#

... in both templates, index.cfm and sub.cfm respectively give us:

D:\sites\kinky_solutions\site_v1\testing\paths\
D:\sites\kinky_solutions\site_v1\testing\paths\sub\

This combination of ColdFusion methods can help us get "ExpandPath" functionality bases on the currently executing template, NOT the currently executing page.

GetBaseTemplatePath()

This returns the absolute path the base template. This is the path of the template that maps to the currently executing page. If we run:

#GetBaseTemplatePath()#

... in both templates, index.cfm and sub.cfm respectively give us:

D:\sites\kinky_solutions\site_v1\testing\paths\index.cfm
D:\sites\kinky_solutions\site_v1\testing\paths\index.cfm

As you case see, it gives us the absolute path of index.cfm (the currently executing page) regardless of which template it is called from. This is akin to running:

#ExpandPath( CGI.script_name )#

... which will get an absolute path to the currently executing page.

GetDirectoryFromPath()

This takes a path of any kind and returns the a path that maps to the final directory in the given path. This is a utility function and does not rely on an existing path. If we run:

#GetDirectoryFromPath( "/test/page.cfm" )#

... we get:

"/test/"

We just made up the path "/test/page.cfm" to prove that this has nothing to do with the physical file structure. This method will always leave a trailing slash and will not check to see if the current path is a directory. So, be caution, if you run:

#GetDirectoryFromPath( "/I_AM_A_DIRECTORY" )#

... and do NOT include a trailing slash, it will return:

"/"

... because it doesn't care what is actually at the end of the given path if it is not a slash. So basically, this function removes every trailing character until it hits a slash or runs out of characters and then returns a path with a trailing slash.

GetFileFromPath()

This takes a path of any kind and returns the file at the end of the path. If we run:

#GetFileFromPath( "/test/page.cfm" )#

... we get:

page.cfm

In this case, we used an obvious file name; however, this method really only returns everything it can starting at the end of the given path until it hits a slash. If we ran:

#GetFileFromPath( "/test" )#

... where "test" is actually a directory, it will still return:

"test"

... since it is the most appropriate choice for a page (the path element located after the final path slash).

GetTempDirectory()

This gets the absolute path of the directory that ColdFusion uses for temporary files. This can be handy for uploading files:

<cffile
	action="UPLOAD"
	filefield="file"
	destination="#GetTempDirectory()#"
	/>

This will upload the given file into the temp directory.

GetTempFile()

Creates a temporary file (.tmp file) in a directory whose name starts with (at most) the first three characters of the given prefix. What took me a while to understand is that this actually created the physical file, it doesn't just create the file path. I suppose it does this so that no two calls to the same directory one after the other will return the same file path (that would be conflicting).

This can be nicely used in conjunction with GetTempDirectory():

<cfset strPath = GetTempFile(
	GetTempDirectory(),
	"temp"
	) />

This will create a .TMP file in ColdFusion's temp file directory and try to use given the prefix. Now, the help docs say that this will use at most the first three characters of the prefix, but in my experience, this method usually uses the entire passed in prefix regardless of length.

Traversing A Path

There is nothing built into ColdFusion that makes traversing a file path very intuitive. In order to move up the directories in a path, I usually use one of three methods: List manipulation, a combination of path methods, or a regular expression.

If you look at a list, it's pretty much a path with the slash as the list item separator. Using this view, you can move up a directory by removing the last two items in the path - remember, the last item in the path is the file. This however can be awkward and if the last element is a slash (there is no file), list manipulation will not use the final empty element as a list item.

I find it best to use a combination of path methods to move up a directory:

#GetDirectoryFromPath(
	GetDirectoryFromPath(
		GetCurrentTemplatePath()
		).ReplaceFirst( "[\\\/]{1}$", "" )
	)#

This gets the current template path. Then, it gets the current directory of that path. Remember, GetDirectoryFromPath() always leaves us with a trailing slash. I then use a regular expression to delete that final slash. Then, I call GetDirectoryFromPath() on the resultant slash. This returns the path to the parent directory.

Then, there is the regular expression approach:

#GetDirectoryFromPath(
	GetCurrentTemplatePath()
	).ReplaceFirst(
		"([^\\\/]+[\\\/]){1}$", ""
		)#

This takes gets the directory of the current template path and then removes a single path item and trailing slash from the resultant path. If you want to move up more than one directory, you can replace the {1} with a {N} where N is the number of directories you want to move up in the path. This method is nice because it is short and can handle multiple directories in a single command. However, if you are super concerned with performance, running this regular expression will be slower than running the combo-option several times in a row. And, the more directories you want to move up in the path, the slower the regular expression becomes.

CGI Paths

The CGI object gives us several paths to work with.

CGI.cf_template_path

This gives us the absolute path of the currently executing page. This is the same as running:

#ExpandPath( CGI.script_name )#

CGI.path_translated

This also returns the absolute path of the currently executing page. I am not sure what is the difference between this and cf_template_path.

CGI.script_name

This returns the web-page that is currently being executed.

So that's how ColdFusion paths work and a bit on how they can be manipulated. I hope that helps.

Want to use code from this post? Check out the license.

Reader Comments

92 Comments

Amazing there are no comments for this but maybe because its written so well. For those that have read this please bookmark it! Ben's work with programmatic configuration has done wonders for me. I've been able to have my applications figure out all my file paths so I don't have too and it was possible using these functions. They are very handy. Sorry I didn't notice this earlier Ben as I was on vacation. Bookmarked!

15,640 Comments

@Javier,

Thanks a lot for the kind words :) No worries about the bookmarking - it was only posted a few hours ago ;) Glad all the programmatic config stuff is working out for you.

92 Comments

Doh! I thought this was one of your older posts. You see I go through your blog daily and I thought when I opened this in a new tab that it was a post from a few days ago, not this morning. I didn't get a chance to read it right away. Still though what I said about bookmarking this still stands! :) Well done.

15,640 Comments

@Jim,

These are ColdFusion functions - they should be system independent (I think)? I know that there used to be some issues with the slashes, but I believe ColdFusion has done a better job of abstracting that in the most recent versions.

7 Comments

correct these work on linux as well....

just make sure that you have permissions to write to the directory on the linux box....something you not even think twice about when using windows unless you have it locked down...

3 Comments

Ben... Ben... dude... Thank you!

#GetDirectoryFromPath(
GetCurrentTemplatePath()
).ReplaceFirst(
"([^\\\/]+[\\\/]){1}$", ""
)#

The getDirectoryFromPath with the regex to delete.... Ohh Thank you!

7 Comments

Ben,
I'm working with a few application pages, and always want the root application page to be able to find configuration settings relative to its path when sent a reload param through the URL no matter where it's executed in the app. From what I can tell, all of the above functions use a path relative to the currently executing template. After digging around I've found the following Java that seems to reliably return the path of the application page it's on:

<cfset variables.thisPage = getPageContext().getFusionContext().getPagePath()>
<cfset variables.thisPagePath = getDirectoryFromPath(variables.thisPage)>

Is this the only way to get the path of a page, whether it's included somewhere or not? Is it as reliable as it seems?

I realize this is a rather old post, but a good one as always.

15,640 Comments

@Ted,

Usually, when I need to always have a handle on a particular set of files, I put that code in the Application.cfc / cfm. This way, no matter what page gets calls, the Application file will be executed first and I can grab the files at known relative paths.

From within the Application.cfc, I use:

GetCurrentTemplatePath()

This always returns the full path of the Application.cfc file no matter what template the user requested.

15,640 Comments

@Chuck1411,

Thanks. Yeah, someone else mentioned the comment box issue. I will try to debug it. I cannot replicate it with my Firefox, so it will be hard to debug.

9 Comments

I hope this is not off topic, but one thing that I have not been able to get my arms around is the best practice for storing and accessing custom tags when you can't put them in the cf custom tag folder.

Ideally, I could have a custom tag folder in my application that would work the same way as the cf custom tag folder (automatic recursive searchers in sub directories). But even if I can't I would like to have a custom_tag that I could refer to no matter what template was running.

Is there a way using what you have provided above for doing this? Using cfmodule or cfimport to access them in a common manner (without having to consider the location of the currently running page).

Thanks in advance for your thoughts.

Tom

15,640 Comments

@Tom,

Custom tags paths are a bit different from the stuff I have outlined. What version of ColdFusion are you running? If you are running CF8, you can set up a per-application custom tag pathing.

If not, you can always use CFImport to import the custom tag paths... or CFModule to provide a template path to the given custom tag.

9 Comments

Ben - Thanks for you response. I am using CF8 and have setup an application specific mapping to the root of my app. Thank you for the suggestion.

So now I can use cfmodule and call the custom tag with an absolute path "/app/custom_tags/my_custom_tag.cfm". It seems to be working like a charm.

28 Comments

Anyone ever see this: expandpath("/") returns:

E:\ColdFusion9\wwwroot\

instead of

D:\inetpub\wwwroot

(your drive letters will vary)

I have an application that was working well (front controller, etc., etc.) until an OS hard drive crashed on me, one of a two disk RAID 0 array. @#$%#@! cheap SSD hard drives! Only 4 months on these stupid Patriot PS-100 crap drives. Anyway, I digress...

After the rebuild, expandpath("/") is returning this craziness!!!!! Apparently, I installed CF without pointing at the right web root? I can't remember. CFIDE and docs and other stuff work right...

In my case, the app needs physical paths to things in other directory structures off the web root, and the app has to be agnostic about where it lives (could be in a dir off webroot, could be three dirs deep off webroot. It will vary on where it is deployed). So, I need all the parts:

If path to app is:
d:/inetpub/wwwroot/clientname/clientapp/version3/application.cfc

I need these vars:

webroot=d:/inetpub/wwwroot
siteroot= /clientname/clientapp*
appdir= /version3

* When deployed, siteroot may be null if appdir in webroot (with version3 becoming appname).

I had been using this to get this stuff (application.cfc)

(Thanks to Ben and his handy .ReplaceFirst() trick)

// chop off "\application.cfc"
application.appdir = GetCurrentTemplatePath().ReplaceFirst( "[\\\/][^\\\/]*$", "" );

// strip off the webroot, what is left is the path off webroot to the app
application.appdir = replacenocase(application.appdir, expandpath("/"), "");

// rationalize stupid windows legacy slashes
application.appdir = replace(application.appdir, "\", "/", "all");

// get the last dir (for some url rewrite magic processing in the front controller)
application.appsignifier = "/" & listlast(application.appdir, "/");

// the rest is the path off webroot to the directory that contains the app
application.siteroot = "/" & listdeleteat(application.appdir, listlen(application.appdir, "/"), "/");

After the rebuild, this all stopped working because of the wanky result of expandpath("/"). Although I never figured out where my install of CF went wrong, the fix was to explicitly set the webroot in Coldfusion. To do this I edited <cfinstalldir>/wwwroot/WEB-INF/jrun-web.xml and added this:

<virtual-mapping>

<resource-path>/*</resource-path>

<system-path>D:/wwwroot</system-path>

</virtual-mapping>

See: http://www.adobe.com/support/coldfusion/adv_development/config_builtin_webserver/config_builtin_webserver04.html

Hope this helps somebody sometime in the future. If anyone has any tricks for getting webroot without using expandpath("/"), please post. Thanks.

Sean

41 Comments

Another thing you can do with file paths, especially if you're on a shared server or your files move drives regularly, is to save the primary path in a database. Query it when you need it. That way when you need to change it, you only have to change ONE place.

15,640 Comments

@Sean,

Is there any CF admin mapping of per-application mapping that is, for some reason, pointing the "/" to the wrong directory?

I typically use the approach you outlined in the latter half of the post - using the path returned within the Application.cfc; then, I use that base path to build the other paths.

@Brian,

That's an interesting idea. I haven't worked on a lot of shared hosting (mostly VPS); but, I could see that being good.

28 Comments

@Ben,

Nope. Fresh CF install, no mappings. Strangest thing I've seen in 5+ years using CF. To lazy to reinstall to see if that fixed it.

@Brian,

Unfortunately, no central db. If you care, more background below.

Sean

The app manages multiple instances. Each instance has it's own db, or multiple instances at one client may share a db. In any case, the app discovers from a file in each instance directory the DSN. No central config to manage. The app adds the dsn to a memory map the first time an instance url is encountered. What is an instance url? Read on.

App URLs are of the format:
webroot/sitepath/instpath/appsignifier/controllerpath/controller/param1><paramval1...

ex: thedomain.com/meat/shop/membersonly/view/specials

The "shop" appsignifier splits the url into two parts: instance path and controller path, and triggers a URL rewrite to become:

/index.cfm?i=/meat&q=/membersonly/view/specials

At a hosting company, expandpath() might return:

d:\inetpub\wwwroot\cust1232\thedomain\index.cfm

url params are parsed to id=specials and added to url scope. Instance info loaded from the db becomes a instance structure stuffed into the request.context structure.

The controller can choose to walk back up the controller path to read membersonly as a filter...or not.

&q is walked for a controller file (only once to build a memory map) and view.cfm is found at /core, but could be overridden if view.cfm is found in either /meat/ or /meat/membersonly.

So that's what all the walking is about...building the database link and controller memory map for each distinct url path.

28 Comments

@Ben,

Never did. Worked around it and moved on. Here is what I ended up with in onApplicationStart:

// chop off "\application.cfc"
application.appdir = GetCurrentTemplatePath().ReplaceFirst( "[\\\/][^\\\/]*$", "" );

// strip off the webroot, what is left is the path off webroot to the app
application.appdir = replacenocase(application.appdir, expandpath("/"), "");

// rationalize stupid windows legacy slashes
application.appdir = "/" & replace(application.appdir, "\", "/", "all");

// get the last dir (for some url rewrite magic processing in the front controller)
application.appsignifier = "/" & listlast(application.appdir, "/");

// the rest is the path off webroot to the directory that contains the app
application.siteroot = listdeleteat(application.appdir, listlen(application.appdir, "/"), "/");

15,640 Comments

@Sean,

Funky that expandPath() would work differently in different parts of the page flow. In the end, I'm glad you got your workaround working.

2 Comments

Ben (or anyone else),

I am trying to figure out how to convert the template's path (D:\InetPub\wwwroot\etc....) to a URL path to include a JavaScript file in the same folder.

For example, I have included a template in the page that uses a JavaScript "code-behind" file. This file exists in the same folder as the included template. However, the template is included in a page that exists in the root folder.

Here's what I mean:

Page.cfm exists in the root. It calls announcements.cfm, which is in the /helpers/content/ folder. What have been doing is manually linking the script to the file (/helpers/content/announcements.js). I'd rather make this dynamic, so that I can move the two files to another folder without having to change the link.

Is there a method for this? Does this even make sense?

Thanks

28 Comments

@Chris,

Not sure what you mean by Javascript code-behind?
There is no javascript on the server.

Anyway, you're having to hard code the include path in page.cfm, no? Then in that file you can set a variable that is used for both cf and js includes:

<cfset announPath = "/helpers/content">
<script src="#announPath#/accouncements.js">
<cfinclude template="#announPath#/accouncements.cfm">

15,640 Comments

@Chris,

Interesting question. The easy thing to do would be to hard-code the path as @Sean suggested. If you want to keep it dynamic, however, you could try playing around with the difference between the values:

getBaseTemplatePath()
getCurrentTemplatePath()

With CFIncludes, though, I am not 100% how that functions - it might not evaluate the way I think it does.

2 Comments

Thanks guys. I figured there wasn't an inherently elegant solution. I already have the helper path set as a variable in the Application scope and have been using it. I was just hoping to find a more dynamic method. No worries though.

@Sean:

Sorry for the confusion. "Code-Behind" is a term I use and probably shouldn't in this case. I've designed the file structure so that each included template has a corresponding javascript file to separate all the js code from the presentation code. I just borrowed the term from ASP.NET.

2 Comments

Hi,
I'm trying to store an picture file's path into a database...but I don't know how declare the path. I'm guessing it should be something along the lines of <cfset ...>.

I hope this makes sense,

Thanks,

R

28 Comments

@Remus,

Here is my standard image upload function:

<!--- --------------------------------------------- --->
<!--- uploadImage --->
<!--- --------------------------------------------- --->
<!--- Returns physical image filname to store in db (person & teammember) --->
<cffunction name="uploadImage" returntype="boolean" output="no">

<cftry>

<cfif not isdefined("form.image")> <cfreturn ""> </cfif>

<cfset request.imgpath = expandpath("#request.appdir#/user_images") />

<cfset Request.existingImageFile = this.profileimage />
<cfset Request.imageFile = this.profileimage />

<cftransaction>

<!--- Delete existing image at user's request --->
<cfif IsDefined("form.image_delete") and Request.imageFile NEQ "">
<cfset this.recordImageFilename("") />
<cfif FileExists("#request.imgpath#/#Request.imageFile#")><cffile action="delete" file="#request.imgpath#/#Request.imageFile#" /></cfif>
<cfif FileExists("#request.imgpath#/_#Request.imageFile#")><cffile action="delete" file="#request.imgpath#/_#Request.imageFile#" /></cfif>
<cfset Request.imageFile = "">
</cfif>

<cfif form.image NEQ "">

<!--- If overwritting existing image, temporarily move existing image and thumbnail, so can restore if upload fails --->
<cfif Request.imageFile NEQ "">
<cfif FileExists("#request.imgpath#/#Request.existingImageFile#")><cffile action="rename" source="#request.imgpath#/#Request.imageFile#" destination="#request.imgpath#/orig_#Request.existingImageFile#" /></cfif>
<cfif FileExists("#request.imgpath#/_#Request.existingImageFile#")><cffile action="rename" source="#request.imgpath#/_#Request.imageFile#" destination="#request.imgpath#/orig__#Request.existingImageFile#" /></cfif>
</cfif>

<!--- Only allow images --->
<cfset accept = "image/" & "*">
<!--- Upload the image --->
<cffile action="upload" filefield="image" destination="#request.imgpath#/" nameconflict="makeunique" accept="#accept#" result="imageresult" />

<cfif imageresult.fileWasSaved>

<cfset Request.imageFile = imageresult.serverFile />

<!--- Allow users to upload any size, but scale to max size (2 X the space for image on profile page) --->
<!--- Make a copy for the thumbnail, resize the thumbnail --->
<cfimage action="read" name="thumbImage" source="#request.imgpath#/#request.imageFile#"/>
<cfimage action="write" source="#thumbImage#" destination="#request.imgpath#/_#request.imageFile#"/>
<cfimage action="resize" width="440" height="" source="#request.imgpath#/_#request.imageFile#" destination="#request.imgpath#/_#request.imageFile#" overwrite="yes" />

<cfelse>
<cfif FileExists("#request.imgpath#/orig_#Request.existingImageFile#")><cffile action="rename" source="#request.imgpath#/orig_#Request.imageFile#" destination="#Request.existingImageFile#" /></cfif>
<cfif FileExists("#request.imgpath#/orig__#Request.existingImageFile#")>cffile action="rename" source="#request.imgpath#/orig__#Request.imageFile#" destination="_#Request.existingImageFile#" /></cfif>
<cfset error(_("An unkown error occured saving the image."))>
<cfreturn false />
</cfif>
</cfif>


<!--- Go update the database. Set person.profileimage column --->
<cfif request.imageFile NEQ "">
<cfset this.recordImageFilename(request.imageFile) />

<cfelse>

<cfset this.profileimage = request.imageFile />

</cfif>

<cftransaction action="commit">

<!--- Everything worked, so delete the orignal, replaced image --->
<cfif FileExists("#request.imgpath#/orig_#Request.existingImageFile#")><cffile action="delete" file="#request.imgpath#/#orig_Request.existingImageFile#" /></cfif>
<cfif FileExists("#request.imgpath#/orig__#Request.existingImageFile#")><cffile action="delete" file="#request.imgpath#/orig__#Request.existingImageFile#" /></cfif>


<cfreturn true />

<cfcatch>
<cfif FileExists("#request.imgpath#/orig_#Request.existingImageFile#")><cffile action="rename" source="#request.imgpath#/orig_#Request.existingImageFile#" destination="#Request.existingImageFile#" /></cfif>
<cfif FileExists("#request.imgpath#/orig__#Request.existingImageFile#")>cffile action="rename" source="#request.imgpath#/orig__#Request.existingImageFile#" destination="_#Request.existingImageFile#" /></cfif>

<cfset error(_("Error updating profile: #cfcatch.message#<br/>#cfcatch.detail#")) />
<cftransaction action="rollback">
<cfmodule template="./callStackTracePush.cfm" cfcatch="#cfcatch#" context="PersonProfile::Init" />
<cfthrow message="#cfcatch.message#" />
</cfcatch>
</cftry>


</cffunction>

28 Comments

@et al,

The code that creates the thumbnails was coded a certain way for a specific reason. I was finding problems otherwise (image not sizing proportionally). The key is to load the image into a variable and create a new file and then do the resize on the copy, rather than chaining commands by using a write method with src and dest params that are both the same filepath.

2 Comments

@ Sean,

I appreciate you sharing your code, but it doesn't help me in the list, partly because I'm such a n00b. I am trying to store the URL of the image I'm uploading into a database table, so I can retrieve it later.

28 Comments

@Remus,

<cfquery name="saveimagefilepath" datasource="something">
INSERT INTO TABLENAME (ImageFilePath) VALUES (#imagefilepath#)
</cfquery>

where:
something - connection to you db configured in the CF admin
TABLENAME - existing table in db with a column "ImageFilePath"
#imagefilepath# - a variable containing the data you want to store.

All can be found in the documentation.

1 Comments

I'm trying to get the path to a dataset from its datasource. The Datasource was added for me by my ISP and now I need to pull its attributes (specifically its last modified date) with a GetFileInfo. But I need the absolute path for that and can't figure out how to do it. (Probably staring me in the face but I haven't been able to find it.) Is there any way to get that?

28 Comments

@JP,

Assumptions:
The datasource is to an MS Access database (otherwise, there is no physical file to reference...OK, maybe an ISAM or something like that, but anyway)
Windows platform
Dedicated server (otherwise you're screwed. You won't have access to the API to get the info you want. If a shared account, Ask the ISP for the filename and hard code that.)

:

Maybe you could access the dbconnection properties through this:

https://www.adobe.com/livedocs/jrun/4/javadocs/jrunx/ide/model/DBConnection.html

Get the connection URL and then parse for a filename.

Good luck. You'll need some.

2 Comments

Thanks Ben for the great explanation.
I still struggle with some of the stuff.

Everything works fine except for images.

For whatever reson images will not load.
They only work if I use the correct URL (http://test/test/image.png)
A path starting with c: does not work.
I need to figure out how to create a full path starting from http://.
I looked at the cgi variables, but I can't find everything there I need to construct the path.

I have to say that I use includes a lot and I define the path on the calling page (the one with the include statement) and use that path-variable in the called page.

Any ideas?

Thanks for your great help with your site so far.

57 Comments

Anyone happen to know if the file created by getTempFile will be automatically removed at any point?

Nothing mentioned in the docs, and restarting CF doesn't remove them, so it seems it needs manual code, unless there's some scheduled task that will do it?

(As a side note, the part of the description which says with "whose name starts with (at most) the first three characters of prefix" is incorrect -- the actual behaviour is to include 'tmp' after the prefix if the prefix is less than three characters. No idea why. *shrug*)

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel