#include        <u.h>
#include        <libc.h>
#include        "compat.h"
#include        "error.h"

Chan*
newchan(void)
{
       Chan *c;

       c = smalloc(sizeof(Chan));

       /* if you get an error before associating with a dev,
          close calls rootclose, a nop */
       c->type = 0;
       c->flag = 0;
       c->ref = 1;
       c->dev = 0;
       c->offset = 0;
       c->iounit = 0;
       c->aux = 0;
       c->name = 0;
       return c;
}

void
chanfree(Chan *c)
{
       c->flag = CFREE;

       cnameclose(c->name);
       free(c);
}

void
cclose(Chan *c)
{
       if(c->flag&CFREE)
               panic("cclose %#p", getcallerpc(&c));
       if(decref(c))
               return;

       if(!waserror()){
               devtab[c->type]->close(c);
               poperror();
       }

       chanfree(c);
}

Chan*
cclone(Chan *c)
{
       Chan *nc;
       Walkqid *wq;

       wq = devtab[c->type]->walk(c, nil, nil, 0);
       if(wq == nil)
               error("clone failed");
       nc = wq->clone;
       free(wq);
       nc->name = c->name;
       if(c->name)
               incref(c->name);
       return nc;
}

enum
{
       CNAMESLOP       = 20
};

static Ref ncname;

void cleancname(Cname*);

int
isdotdot(char *p)
{
       return p[0]=='.' && p[1]=='.' && p[2]=='\0';
}

int
incref(Ref *r)
{
       int x;

       lock(r);
       x = ++r->ref;
       unlock(r);
       return x;
}

int
decref(Ref *r)
{
       int x;

       lock(r);
       x = --r->ref;
       unlock(r);
       if(x < 0)
               panic("decref");

       return x;
}

Cname*
newcname(char *s)
{
       Cname *n;
       int i;

       n = smalloc(sizeof(Cname));
       i = strlen(s);
       n->len = i;
       n->alen = i+CNAMESLOP;
       n->s = smalloc(n->alen);
       memmove(n->s, s, i+1);
       n->ref = 1;
       incref(&ncname);
       return n;
}

void
cnameclose(Cname *n)
{
       if(n == nil)
               return;
       if(decref(n))
               return;
       decref(&ncname);
       free(n->s);
       free(n);
}

Cname*
addelem(Cname *n, char *s)
{
       int i, a;
       char *t;
       Cname *new;

       if(s[0]=='.' && s[1]=='\0')
               return n;

       if(n->ref > 1){
               /* copy on write */
               new = newcname(n->s);
               cnameclose(n);
               n = new;
       }

       i = strlen(s);
       if(n->len+1+i+1 > n->alen){
               a = n->len+1+i+1 + CNAMESLOP;
               t = smalloc(a);
               memmove(t, n->s, n->len+1);
               free(n->s);
               n->s = t;
               n->alen = a;
       }
       if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/')        /* don't insert extra slash if one is present */
               n->s[n->len++] = '/';
       memmove(n->s+n->len, s, i+1);
       n->len += i;
       if(isdotdot(s))
               cleancname(n);
       return n;
}

/*
* In place, rewrite name to compress multiple /, eliminate ., and process ..
*/
void
cleancname(Cname *n)
{
       char *p;

       if(n->s[0] == '#'){
               p = strchr(n->s, '/');
               if(p == nil)
                       return;
               cleanname(p);

               /*
                * The correct name is #i rather than #i/,
                * but the correct name of #/ is #/.
                */
               if(strcmp(p, "/")==0 && n->s[1] != '/')
                       *p = '\0';
       }else
               cleanname(n->s);
       n->len = strlen(n->s);
}

void
isdir(Chan *c)
{
       if(c->qid.type & QTDIR)
               return;
       error(Enotdir);
}