/*
  $NiH: util.c,v 1.27 2002/05/13 16:27:29 wiz Exp $

  util.c -- miscellaneous functions
  Copyright (C) 2002 Dieter Baron and Thomas Klausner

  This file is part of cg, a program to assemble and decode binary Usenet
  postings.  The authors can be contacted at <nih@giga.or.at>

  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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pwd.h>

#include "util.h"

#define HOUSENUMBER 102400

#define DEBUG_FILE_NAME ".debug"

static FILE *debug_file;
static int do_debug_file, do_debug_stdout;



void *
xmalloc(size_t size)
{
    void *p;

    if ((p=malloc(size)) == NULL) {
	fprintf(stderr, "%s: malloc failure (size=%ld)\n", prg, (long)size);
	exit(1);
    }

    return p;
}



void *
xrealloc(void *p, size_t size)
{
    if ((p=realloc(p, size)) == NULL) {
	fprintf(stderr, "%s: realloc failure (size=%ld)\n", prg, (long)size);
	exit(1);
    }

    return p;
}



char *
expand(char *path)
{
    char *home, *s, *p;
    struct passwd *pw;

    if (path[0] != '~')
	return strdup(path);

    if (path[1] == '/' || path[1] == '\0') {
	s = path+1;

	if ((home=getenv("HOME")) == NULL) {
	    if ((pw=getpwuid(getuid())) == NULL
		|| (home=pw->pw_dir) == NULL)
		return NULL;
	}
    }
    else {
	if ((s=strchr(path, '/')))
	    *s = '\0';
	if ((pw=getpwnam(path+1)) == NULL
	    || (home=pw->pw_dir) == NULL) {
	    if (s)
		*s = '/';
	    return NULL;
	}
	if (s)
	    *s = '/';
	else
	    s = path+strlen(path);
    }

    if ((p=(char *)malloc(strlen(home)+strlen(s)+1)) == NULL)
	return NULL;

    sprintf(p, "%s%s", home, s);

    return p;
}



FILE *
fopen_uniq(char **fnp)
{
    char *s, b[8192];
    int fd, i;
    FILE *f;

    s=*fnp;
    if (s == NULL) {
	s = "UNKNOWN";
	strcpy(b, "UNKNOWN.001");
	i = 1;
    }
    else {
	strcpy(b, s);
	i = 0;
    }

    for (;;) {
	if ((fd=open(b, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) {
	    if (errno != EEXIST) {
		return NULL;
	    }
	}
	else {
	    if ((f=fdopen(fd, "wb")) == NULL) {
		close(fd);
		return NULL;
	    }
	    *fnp = strdup(b);
	    return f;
	}

	sprintf(b, (*fnp) ? "%s.%d" : "%s.%03d", s, ++i);
    }
}



int
rename_uniq(const char *from, char **to)
{
    char b[8192];
    int i;

    strcpy(b, *to);
    i = 0;

    for (;;) {
	if (link(from, b) == 0) {
	    unlink(from);
	    *to = strdup(b);
	    return 0;
	}
	else if (errno != EEXIST)
	    return -1;

	sprintf(b, "%s.%d", *to, ++i);
    }
}



char *
getline(FILE *f)
{
    static char *b;
    static int bsize;
    int len;

    if (bsize == 0) {
	bsize = BUFSIZE;
	b = (char *)xmalloc(bsize);
    }

    if (fgets(b, bsize, f) == NULL) {
	return NULL;
    }

    len = strlen(b);

    while (b[len-1] != '\n') {
	/* line too long */
	bsize += BUFSIZE;
	if (bsize >= HOUSENUMBER) {
	    /* prerror(errline, "line longer than %d", HOUSENUMBER); */
	    bsize -= BUFSIZE;
	    return b;
	}
	b = xrealloc(b, bsize);
	if (fgets(b+bsize-BUFSIZE, BUFSIZE, f) == NULL) {
	    return NULL;
	}
	len += strlen(b+bsize-BUFSIZE);
    }

    if (b[len-2] == '\r')
	b[len-2] = '\0';
    else
	b[len-1] = '\0';


    if (b[0] == '.') {
	if (b[1] == '\0')
	    return NULL;
	/* errlineno++; */
	return b+1;
    }

    /* errlineno++; */
    return b;
}



void
skip_rest(FILE *f)
{
    while (getline(f) != NULL)
	;
}



char *
xstrdup(char *s)
{
    char *t;

    if (s == NULL)
	return NULL;

    t = xmalloc(strlen(s)+1);
    strcpy(t, s);

    return t;
}



char *
strcasestr(const char *big, const char *little)
{
    const char *p, *q, *r;

    /* XXX: more efficient implementation, and sync with gtunes */

    for (p=big; *p; p++) {
        for (q=p, r=little; *q && *r; q++, r++) {
            if (tolower(*q) != tolower(*r))
                break;
        }
        if (!*r)
            return (char *)p;
    }

    return NULL;

}



token *
skip_to(stream *in, enum token_type type)
{
    token *t;

    while ((t=stream_get(in))->type != TOK_EOF && t->type != type)
	;

    return t;
}



void
copy_stream(stream *in, out_state *out)
{
    token *t;

    while ((t=stream_get(in))->type != TOK_EOF)
	output(out, t);
}



void
debug(out_state *out, char *fmt, ...)
{
    token t;
    char *s;
    va_list argp;

    va_start(argp, fmt);
    vasprintf(&s, fmt, argp);
    va_end(argp);

    output(out, token_set(&t, TOK_DEBUG, s));

    free(s);
}



int
append_file(char *t, char *s, char *sep)
{
    FILE *fin, *fout;
    char b[8192];
    int n, m, i;

    if ((fin=fopen(s, "rb")) == NULL)
	return EOF;
    if ((fout=fopen(t, "ab")) == NULL) {
	fclose(fin);
	return EOF;
    }

    if (fputs(sep, fout) == EOF) {
	fclose(fin);
	fclose(fout);
	return EOF;
    }

    while ((n=fread(b, 1, 8192, fin)) > 0)
	for (i=0; n>0; n-=m,i+=m)
	    if ((m=fwrite(b+i, 1, n, fout)) <= 0) {
		fclose(fin);
		fclose(fout);
		return EOF;
	    }

    if (ferror(fin)) {
	fclose(fin);
	fclose(fout);
	return EOF;
    }

    fclose(fin);
    return fclose(fout);
}



char *
our_basename(char *name)
{
    char *p;

    if (name == NULL)
	return NULL;

    if ((p=strrchr(name, '/')))
	return p+1;
    if ((p=strrchr(name, '\\')))
	return p+1;
    return name;
}



void
prdebug(int mask, char *fmt, ...)
{
    char *s;
    va_list argp;

    if (!(mask & do_debug_file) && !(mask & do_debug_stdout))
	return;

    va_start(argp, fmt);
    vasprintf(&s, fmt, argp);
    va_end(argp);

    if (mask & do_debug_file)
	fprintf(debug_file, "%s\n", s);
    if (mask & do_debug_stdout)
	puts(s);

    free(s);
}



void
prdebug_init(int do_file, int do_stdout)
{
    if (do_file) {
	if ((debug_file=fopen(DEBUG_FILE_NAME, "w")) == NULL)
	    do_file = 0;
    }
    do_debug_file = do_file;
    do_debug_stdout = do_stdout;
}



int
str2hex(char *s)
{
#define HEX_DIGIT(x)  ((x) >= '0' && (x) <= '9' ? (x)-'0' \
		       : (x) >= 'a' && (x) <= 'f' ? (x)-'a'+10 \
		       : (x) >= 'A' && (x) <= 'F' ? (x)-'A'+10 : 0)

    return (HEX_DIGIT(s[0])<<4) | HEX_DIGIT(s[1]);
#undef HEX_DIGIT
}



void
output_header(out_state *out, symbol name, struct header *h)
{
    token t;
    char *value;
    char buffer[8192];

    value = header_get(h, name);
    if (value == NULL) {
	output(out, token_set(&t, TOK_DEBUG, "cannot find header value"));
	return;
    }

    /* XXX: split line if too long */
    snprintf(buffer, sizeof(buffer), "%s: %s", name, value);
    output(out, token_set(&t, TOK_LINE, buffer));
}


syntax highlighted by Code2HTML, v. 0.9.1