static char rcsid[] = "@(#)$Id: url_path.c,v 1.5 2006/05/07 08:35:31 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.5 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI>
 *      or  Kari Hurtta <elm@elmme-mailer.org>
 *****************************************************************************/

#include "def_url.h"
#include "s_me.h"

DEBUG_VAR(Debug,__FILE__,"url");

static unsigned char * s2us P_((char *str));
static unsigned char * s2us(str)
     char *str;
{
    return (unsigned char *)str;
}

static struct url_path_elem * alloc_url_path_elem P_((void));
static struct url_path_elem * alloc_url_path_elem()
{
    struct url_path_elem * ret = safe_malloc (sizeof (*ret));

    /* bzero is defined hdrs/defs.h */
    bzero((void *)ret,sizeof (*ret));

    ret->magic  = URL_path_elem_magic;
    ret->elem   = NULL;
    ret->trailing_slash = 0;

    return ret;
}

static void free_url_path_elem P_((struct url_path_elem **ptr));
static void free_url_path_elem(ptr)
     struct url_path_elem **ptr;
{
    if (URL_path_elem_magic != (*ptr)->magic)
	panic("URL PANIC",__FILE__,__LINE__,"free_url_path_elem",
	      "bad magic number",0);

    if ((*ptr)->elem)
	free_url_element(& ((*ptr)->elem));
    
    (*ptr)->trailing_slash = 0;
    (*ptr)->magic = 0;     /* Invalidate */

    free(*ptr);
    *ptr = NULL;
}

struct url_path_elem * dup_url_path_elem P_((struct url_path_elem *ptr));
struct url_path_elem * dup_url_path_elem(ptr)
     struct url_path_elem *ptr;
{
    struct url_path_elem * ret = alloc_url_path_elem();

    if (URL_path_elem_magic != ptr->magic)
	panic("URL PANIC",__FILE__,__LINE__,"dup_url_path_elem",
	      "bad magic number",0);
    
    if (ptr->elem)
	ret->elem = dup_url_element(ptr->elem);
    ret->trailing_slash = ptr->trailing_slash;

    return ret;
}

enum elem_class { p_error, p_normal, p_this, p_up 
} classify_url_path_elem P_((struct url_path_elem *ptr));

enum elem_class classify_url_path_elem(ptr)
     struct url_path_elem *ptr;
{
    CONST struct string * parsed;
    uint16 ch;

    int len;

    if (URL_path_elem_magic != ptr->magic)
	panic("URL PANIC",__FILE__,__LINE__,"classify_url_path_elem",
	      "bad magic number",0);
    
    if (!ptr->elem)
	panic("URL PANIC",__FILE__,__LINE__,"classify_url_path_elem",
	      "Is root element?",0);

    parsed = parsed_from_element(ptr->elem,NULL);
    if (!parsed)
	return p_error;

    len = string_len(parsed);
    if (len < 1)
	return p_normal;

    ch = give_unicode_from_string(parsed,0);
    if (0x002E /* . */ != ch)
	return p_normal;

    if (1 == len)
	return p_this;    /*   "." */

    ch = give_unicode_from_string(parsed,1);
    if (0x002E /* . */ != ch)
	return p_normal;

    if (2 == len)
	return p_up;    /*   ".." */

    return p_normal;
}

/* ------------------------------------------------------------------------- */

#define URL_path_magic          0xEC04

struct url_path {
    unsigned short        magic;     /* URL_path_magic */

    struct url_path_elem  **elems;
    int                   elem_count;
};

static struct url_path * alloc_url_path P_((void));
static struct url_path * alloc_url_path()
{
    struct url_path *ret = safe_malloc (sizeof (*ret));

    /* bzero is defined hdrs/defs.h */
    bzero((void *)ret,sizeof (*ret));

    ret->magic  = URL_path_magic;
    ret->elems  = NULL;
    ret->elem_count = 0;

    return ret;
}

struct url_path * raw_to_url_path(raw)
     struct string *raw;
{
    int len = string_len(raw);

    int X = 0;
    int startpos = 0;
    int count = 1;
    struct url_path_elem **elems;
    int elem_count = 0;

    struct url_path *ret = NULL;

    uint16 ch = 0;

    for (X = 0; X < len; X++) {
	ch = give_unicode_from_string(raw,X);
	if (0x002F /* / */ == ch)
	    count++;
    }

    DPRINT(Debug,12,(&Debug,
		     "url_from_raw: count=%d raw=%S\n",
		     count,raw));


    elems = safe_malloc(count * sizeof (elems[0]));
    
    /* Special first element ... */

    X = 0;
    if (len > 0) {
	ch = give_unicode_from_string(raw,X);

	if (0x002F /* / */ == ch) {

	    elems[0] = alloc_url_path_elem();

	    elems[0]->elem           = NULL;
	    elems[0]->trailing_slash = 1;

	    DPRINT(Debug,12,(&Debug,
			     "raw_to_url_path: trailing slash -- root element\n"));


	    elem_count = 1;
	    X++;
	    ch = 0;
	}
    }
    
    while ( X < len && elem_count < count) {
	struct string * elem = NULL;
	int L;

	startpos = X;

	for (; X < len; X++) {
	    ch = give_unicode_from_string(raw,X);

	    if (0x002F /* / */ == ch)
		break;
	}

	L = X - startpos;
	elem = clip_from_string(raw,&startpos,L);

	if (startpos != X)
	    panic("URL PANIC",__FILE__,__LINE__,"raw_to_url_path",
		  "Clipping Error",0);

	DPRINT(Debug,12,(&Debug,
			 "raw_to_url_path: elem=%S\n",elem));

	elems[elem_count] = alloc_url_path_elem();
	elems[elem_count]->elem =  element_from_raw(elem);

	if (0x002F /* / */ == ch) {
	    elems[elem_count]->trailing_slash = 1;

	    DPRINT(Debug,12,(&Debug,
			     "raw_to_url_path: trailing slash\n"));

	    X++;  /* Skip / */
	    ch = 0;
	}
	elem_count++;
	
	free_string(&elem);
    }

    ret = alloc_url_path();
    ret->elems = elems;
    ret->elem_count = elem_count;

    DPRINT(Debug,12,(&Debug,
		     "raw_to_url_path: elem_count=%d\n",elem_count));

    return ret;
}


int url_path_len(path)
     struct url_path *path;
{
    if (URL_path_magic != path->magic)
	panic("URL PANIC",__FILE__,__LINE__,"url_path_len",
	      "bad magic number",0);

    return path->elem_count;
}

CONST struct url_path_elem * get_url_path_element(path,idx)
     struct url_path *path;
     int idx;
{
    if (URL_path_magic != path->magic)
	panic("URL PANIC",__FILE__,__LINE__,"get_url_path_element",
	      "bad magic number",0);

    if (idx < 0 || idx >= path->elem_count)	
	panic("URL PANIC",__FILE__,__LINE__,"get_url_path_element",
	      "bad index",0);
    
    if (! path->elems[idx])
	panic("URL PANIC",__FILE__,__LINE__,"get_url_path_element",
	      "NULL element",0);
	
    return path->elems[idx];
}

void free_url_path(path)
     struct url_path **path;
{
    if (URL_path_magic != (*path)->magic)
	panic("URL PANIC",__FILE__,__LINE__,"free_url_path",
	      "bad magic number",0);

    if ((*path)->elems) {
	int i;

	for (i = 0; i < (*path)->elem_count; i++) {
	    if ((*path)->elems[i])
		free_url_path_elem(& ((*path)->elems[i]));

	}

	free((*path)->elems);
	(*path)->elems = NULL;
    }
    (*path)->elem_count = 0;

    (*path)->magic = 0; /* Invalidate */
   
    free(*path);
    *path = NULL;
}

struct url_path * join_url_path(parent,relative)
     struct url_path *parent;
     struct url_path *relative;
{
    int count;
    struct url_path_elem **elems;
    int elem_count = 0;
    int i;

    struct url_path *ret = NULL;

    if (URL_path_magic != parent->magic)
	panic("URL PANIC",__FILE__,__LINE__,"join_url_path",
	      "bad magic number (parent)",0);

    if (URL_path_magic != relative->magic)
	panic("URL PANIC",__FILE__,__LINE__,"join_url_path",
	      "bad magic number (relative)",0);

    if (parent->elem_count < 1) 
	panic("URL PANIC",__FILE__,__LINE__,"join_url_path",
	      "Empty parent",0);
	
    count = parent->elem_count + relative->elem_count;

    elems = safe_malloc(count * sizeof (elems[0]));

    if (!parent->elems[0]->trailing_slash ||
	parent->elems[0]->elem)
	panic("URL PANIC",__FILE__,__LINE__,"join_url_path",
	      "Parent is not absolute",0);
	
    elems[elem_count++] = dup_url_path_elem(parent->elems[0]);

    for (i = 1; i < parent->elem_count; i++) {
	if (!parent->elems[i]->trailing_slash)
	    break;

	elems[elem_count++] = dup_url_path_elem(parent->elems[i]);
    }

    if (elem_count < parent->elem_count-1)
	panic("URL PANIC",__FILE__,__LINE__,"join_url_path",
	      "Bad parent path",0);


    for (i = 0; i < relative->elem_count; i++) {
	enum elem_class c = classify_url_path_elem(relative->elems[i]);

	if (p_error == c)
	    goto failure;

	if (p_this == c)   /* Skip "." element */
	    continue;
	
	if (p_up == c && elem_count > 1) {
	    enum elem_class c1 = classify_url_path_elem(elems[elem_count]);

	    if (p_error == c1)
		goto failure;

	    if (p_normal == c1) {   /* Remove one element */
		free_url_path_elem(& elems[elem_count]);
		elem_count--;

		continue;
	    }

	    elems[elem_count++] = dup_url_path_elem(relative->elems[i]);
	}
    }

    ret = alloc_url_path();
    ret->elems = elems;
    ret->elem_count = elem_count;

    DPRINT(Debug,12,(&Debug,
		     "join_url_path: elem_count=%d\n",elem_count));

    return ret;
	
 failure:
    for (i = 0; i < elem_count; i++)
	free_url_path_elem(& elems[i]);
    free(elems);

    return NULL;
}

struct url_path * dup_url_path(path)
     struct url_path *path;
{
    struct url_path_elem **elems = NULL;
    int elem_count = 0;

    struct url_path *ret = NULL;

    if (URL_path_magic != path->magic)
	panic("URL PANIC",__FILE__,__LINE__,"dup_url_path",
	      "bad magic number",0);

    if (path->elem_count > 0) {
	int i;

	elems = safe_malloc(path->elem_count * sizeof (elems[0]));

	for (i = 0; i < path->elem_count; i++) {
	    elems[elem_count++] = dup_url_path_elem(path->elems[i]);
	}
    }

    ret = alloc_url_path();
    ret->elems = elems;
    ret->elem_count = elem_count;

    DPRINT(Debug,12,(&Debug,
		     "dup_url_path: elem_count=%d\n",elem_count));

    return ret;
}

struct string * url_path_to_raw(url_path)
     struct url_path *url_path;
{
    struct string * ret = NULL;
    charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);
    int i;

    if (!ascii_ptr)
	panic("CHARSET PANIC",__FILE__,__LINE__,"url_path_to_raw",
	      "US-ASCII not found",0);


    if (URL_path_magic != url_path->magic)
	panic("URL PANIC",__FILE__,__LINE__,"url_path_to_raw",
	      "bad magic number",0);

    ret = new_string(ascii_ptr);

    for (i = 0; i < url_path->elem_count; i++) {

	/* On first element this is NULL */
	if (url_path->elems[i]->elem) {
	    CONST struct string * R = 
		raw_from_element(url_path->elems[i]->elem);
	    
	    if (!R) {
		DPRINT(Debug,12,(&Debug,
				 "url_path_to_raw: Failed to get url path element\n"));
		goto failure;
	    }
	    append_string(&ret,R);
	}

	/* This should be set on all elements except last */
	if (url_path->elems[i]->trailing_slash) {
	    add_ascii_to_string(ret,s2us("/"));
	}
    }

    return ret;

    failure:
	free_string(&ret);
	return NULL;  
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1