/*
 * Maketool - GTK-based front end for gmake
 * Copyright (c) 1999-2003 Greg Banks
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "util.h"
#include <stdarg.h>

CVSID("$Id: util.c,v 1.19 2003/10/03 12:18:33 gnb Exp $");

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/


void
estring_init(estring *e)
{
    e->data = 0;
    e->length = 0;
    e->available = 0;
}

#define _estring_expand_by(e, dl) \
   if ((e)->length + (dl) + 1 > (e)->available) \
   { \
   	(e)->available += MIN(1024, ((e)->length + (dl) + 1 - (e)->available)); \
   	(e)->data = ((e)->data == 0 ? g_new(char, (e)->available) : g_renew(char, (e)->data, (e)->available)); \
   }

void
estring_append_string(estring *e, const char *str)
{
    if (str != 0 && *str != '\0')
	estring_append_chars(e, str, strlen(str));
}

void
estring_append_char(estring *e, char c)
{
    _estring_expand_by(e, 1);
    e->data[e->length++] = c;
    e->data[e->length] = '\0';
}

void
estring_append_chars(estring *e, const char *buf, int len)
{
    _estring_expand_by(e, len);
    memcpy(&e->data[e->length], buf, len);
    e->length += len;
    e->data[e->length] = '\0';
}

void
estring_append_printf(estring *e, const char *fmt, ...)
{
    va_list args;
    int len;

    /* ensure enough space exists for result, possibly too much */    
    va_start(args, fmt);
    len = g_printf_string_upper_bound(fmt, args);
    va_end(args);
    _estring_expand_by(e, len);

    /* format the string into the new space */    
    va_start(args, fmt);
    vsprintf(e->data+e->length, fmt, args);
    va_end(args);
    e->length += strlen(e->data+e->length);
}

void
estring_truncate(estring *e)
{
    e->length = 0;
    if (e->data != 0)
	e->data[0] = '\0';
}

/* remove leading whitespace */
void
estring_chug(estring *e)
{
    int skip;

    for (skip = 0 ;
    	 skip < e->length && isspace(e->data[skip]) ;
	 skip++)
	;
	
    if (skip != 0)
    {
	e->length -= skip;
    	memmove(e->data, e->data+skip, e->length);
	e->data[e->length] = '\0';
    }
}

/* remove trailing whitespace */
void
estring_chomp(estring *e)
{
    int len;

    for (len = e->length ;
    	 len > 0 && isspace(e->data[len-1]) ;
	 len--)
	;
	
    if (len != e->length)
    {
	e->length = len;
	e->data[len] = '\0';
    }
}

void
estring_free(estring *e)
{
   if (e->data != 0)
   {
   	g_free(e->data);
	e->data = 0;
	e->length = 0;
	e->available = 0;
   }
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

const char _util_metachars[] = "'`\"&|$(){};<>\\#*?";


int
strprefix(const char *str, const char *pref)
{
    int sl = strlen(str), pl = strlen(pref);
    if (sl < pl)
    	return -1;
    return strncmp(str, pref, pl);
}


char *
strescape(const char *s)
{
    estring e;
    
    estring_init(&e);
    
    for ( ; *s ; s++)
    {
    	if (ismetachar(*s) || isspace(*s))
	    estring_append_char(&e, '\\');
	estring_append_char(&e, *s);
    }
    
    return (e.length == 0 ? g_strdup("") : e.data);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

#define LEFT_CURLY '{'
#define RIGHT_CURLY '}'

static void
do_expands(const char *in, estring *out, const char *expands[256])
{
    while (*in)
    {
    	if (*in == '%')
	{
	    if (in[1] == LEFT_CURLY)
	    {	
	    	char *x;
		int len;
		gboolean doit = FALSE;
		
		if ((x = strchr(in, RIGHT_CURLY)) == 0)
		    return;
		len = (x - in) + 1;

		switch (in[4])
		{
		case '+':
		    doit = (expands[(int)in[2]] != 0);
		    break;
		}
		if (doit)
		{
		    int sublen = len-6;
		    char *sub = g_new(char, sublen+1);
		    memcpy(sub, in+5, sublen);
		    sub[sublen] = '\0';
	    	    do_expands(sub, out, expands);
		    g_free(sub);
		}
		in += len;
	    }
	    else
	    {
		estring_append_string(out, expands[(int)in[1]]);
		in += 2;
	    }
	}
	else
	    estring_append_char(out, *in++);
    }
}

char *
expand_string(const char *in, const char *expands[256])
{
    estring out;
    
    estring_init(&out);
    do_expands(in, &out, expands);
        
#if DEBUG
    fprintf(stderr, "expand_string(): \"%s\" -> \"%s\"\n", in, out.data);
#endif
    return out.data;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

gboolean
file_exists(const char *pathname)
{
    struct stat sb;
    
    return (stat(pathname, &sb) >= 0 && S_ISREG(sb.st_mode));
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

const char *
file_home(void)
{
    static char *home = 0;

    if (home == 0)
    {
    	home = getenv("HOME");
	if (home == 0)
	    home = "";
	home = g_strdup(home);
    }

    assert(home != 0);
    return home;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

static char *currdir = 0;

const char *
file_current(void)
{
    if (currdir == 0)
    	currdir = g_get_current_dir();
    return currdir;
}

int
file_change_current(const char *dir)
{
    if (chdir(dir) < 0)
    	return -1;

    if (currdir != 0)
    {
    	g_free(currdir);
	currdir = 0;
    }
    return 0;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

/* TODO: handle path components "." and ".." */
char *
file_normalise(const char *path)
{
    if (path[0] == '/')
    	return g_strdup(path);
    
    return g_strconcat(file_current(), "/", path, 0);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

/*
 * If `pref' is the initial subset of pathname components of `path',
 * return a new string, else return 0.  The returned string has
 * the prefix replaced with `replace1', or `replace0' if the 
 * prefix is the whole path.
 */
static char *
file_denormalise_1(
    const char *path,
    const char *pref,
    const char *replace0,
    const char *replace1)
{
    int preflen = strlen(pref);

    if (!strncmp(path, pref, preflen) &&
    	(path[preflen] == '\0' || path[preflen] == '/'))
    {
    	path += preflen;
	while (*path == '/')
    	    path++;
	if (*path == '\0')
	    return g_strdup(replace0);
	return g_strconcat(replace1, path, 0);
    }
    return 0;
}

char *
file_denormalise(const char *path, unsigned flags)
{
    char *d;
    
    if ((flags & DEN_PWD) &&
    	(d = file_denormalise_1(path, file_current(), ".", "")) != 0)
    	return d;
    if ((flags & DEN_HOME) &&
    	(d = file_denormalise_1(path, file_home(), "~", "~/")) != 0)
    	return d;
    return g_strdup(path);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*END*/


syntax highlighted by Code2HTML, v. 0.9.1