#include <stdio.h>

/*
** These routines can be used to remove ../ and ./ entries from
** pathnames.  These routines do it radically, without any attempt
** at all at interpretation.  This is okay for a gopher server,
** because clients aren't supposed to ask for .. or . in a request.
**
** We want to do this so that we can avoid the chroot(), so that we
** can have symbolic links under the gopher directory to other things
** (like man pages for example) outside our structure, and still be
** safe.  Unless of course someone makes a link to / or somewhere silly.
**
** dedot1 will remove dots from one string in place, dedot2 will copy
** one to the other, removing in the process - the src and dst can be
** the same.  dedot1 can use this to advantage - it checks first if
** things have to be removed, and calls dedot2 if necessary.  This
** is a slight advantage because in most cases we won't do anything,
** and we save the expense of copying data back and forth.  This
** seems to save almost half the time (very loose testing though).
**
** John Sellens jmsellens@watdragon.waterloo.edu
*/

/* If DOSINGLE is defined, references to '.' are also removed */
#define DOSINGLE


dedot1( src )
char *src;
{
    if ( *src == '.' ) {
	if (
#ifdef DOSINGLE
	src[1] == '\0' || src[1] == '/' ||
#endif
	( src[1] == '.' && ( src[2] == '\0' || src[2] == '/' ) ) ) {
	    dedot2( src, src );
	    return;
	}
    }
    while ( *src ) {
	if ( *src++ == '/' ) {
	    if ( *src == '.' ) {
		if (
#ifdef DOSINGLE
		src[1] == '\0' || src[1] == '/' ||
#endif
		( src[1] == '.' && ( src[2] == '\0' || src[2] == '/' ) ) ) {
		    dedot2( src, src );
		    return;
		}
	    }
	}
    }
    return;
}

/* copy src to dst, blindly removing (not interpreting) ./ and ../ */
dedot2( src, dst )
char *src;
char *dst;
{
    /*
    ** We either have /, a filename, ./ or ../
    */

    while ( *src ) {
	switch ( *src ) {
	    case '/':
		/* copy it, and skip any extras e.g. /a///b */
		*dst++ = *src++;
		while ( *src == '/' )
		    src++;
		break;
	    case '.':
#ifdef DOSINGLE
		/* don't forget about trailing . and .. */
		if ( src[1] == '/'  ) {		/* ./ */
		    src += 2;
		    break;
		}
		if ( src[1] == '\0'  ) {	/* .\0 */
		    src += 1;	/* only 1 so we don't fall off the end */
		    break;
		}
#endif
		if ( src[1] == '.' && src[2] == '/' ) {		/* ../ */
		    src += 3;
		    break;
		}
		if ( src[1] == '.' && src[2] == '\0' ) {	/* .. */
		    src += 2;	/* don't fall off the end */
		    break;
		}
		/* must be a filename - fall through */
	    default:
		/* must be filename - copy it over */
		while ( *src != '\0' && *src != '/' )
		    *dst++ = *src++;
		break;
	}
    }
    /* and terminate it */
    *dst = '\0';
}
