/*-
 * Copyright (c) 1999 James Howard
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	$Id$
 */

#include <sys/types.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <zlib.h>

#include "grep.h"

/*
 *  Process a file line by line...
 */

int
procfile(char *fn)
{
    str_t ln;
    int c;
    FILE *f;
#if GZ
    gzFile *gzf;
#endif
    char *tmp;

    ln.file = fn;
    ln.line_no = 0;

    if (Zflag) {
#if GZ
	if (fn == NULL) {
	    /*
	     *  I do not check for errors here because I do not
	     *  believe this can ever fail.
	     */
	    gzf = gzdopen(STDIN_FILENO, "rb");
	    fn = "-";
	} else if ((gzf = gzopen(fn, "r")) == NULL) {
	    if (!sflag)
		warn("%s", fn);
	    return 0;
	}
	
	if (aflag && gzbin_file(gzf))
	    return 0;

	for (c = 0;;) {
	    ln.off = gztell(gzf);
	    if ((tmp = gzfgetln(gzf, &ln.len)) == NULL)
		break;
	    ln.dat = grep_malloc(ln.len + 1);
	    memcpy(ln.dat, tmp, ln.len);
	    ln.dat[ln.len] = 0;
	    if (ln.len > 0 && ln.dat[ln.len-1] == '\n')
		ln.dat[--ln.len] = 0;
	    ln.line_no++;
	    c += procline(ln);
	    free(ln.dat);
	}

	gzclose(gzf);
#else
	; /* nada */
#endif
    } else {
	if (fn == NULL) {
	    f = stdin;
	    fn = "-";
	} else if ((f = fopen(fn, "r")) == NULL) {
	    if (!sflag)
		warn("%s", fn);
	    return 0;
	}
	
	if (aflag && bin_file(f))
	    return 0;

	for (c = 0;;) {
	    ln.off = ftell(f);
	    if ((tmp = fgetln(f, &ln.len)) == NULL)
		break;
	    ln.dat = grep_malloc(ln.len + 1);
	    memcpy(ln.dat, tmp, ln.len);
	    ln.dat[ln.len] = 0;
	    if (ln.len > 0 && ln.dat[ln.len-1] == '\n')
		ln.dat[--ln.len] = 0;
	    ln.line_no++;
	    c += procline(ln);
	    free(ln.dat);
	}

	fclose(f);
    }

    if (cflag) {
	if (!hflag)
	    printf("%s:", ln.file);
	printf("%u\n", c);
    }
    if (lflag && c != 0)
	printf("%s\n", fn);
    if (Lflag && c == 0)
	printf("%s\n", fn);
    return c;
}


/* 
 *  Process an individual line in a file
 */ 

#define isword(x) (isalnum(x) || (x) == '_')

int
procline(str_t l)
{
    regmatch_t pmatch;
    int c, i, r, t;

    t = vflag ? REG_NOMATCH : 0;
    pmatch.rm_so = 0;
    pmatch.rm_eo = l.len;
    for (c = i = 0; i < patterns; i++) {
	r = regexec(&pat[i], l.dat, 0, &pmatch, eflags);
	if (r == REG_NOMATCH && t == 0)
	    continue;
	if (wflag && r == 0) {
	    if ((pmatch.rm_so != 0 && isword(l.dat[pmatch.rm_so-1]))
		|| (pmatch.rm_eo != l.len && isword(l.dat[pmatch.rm_eo])))
		r = REG_NOMATCH;
	}
	if (xflag && r == 0) {
	    if (pmatch.rm_so != 0 || pmatch.rm_eo != l.len)
		r = REG_NOMATCH;
	}
	if (r == t) {
	    c++;
	    if (!cflag && !qflag) {
		if (!hflag)
		    printf("%s:", l.file);
		if (nflag)
		    printf("%u:", l.line_no);
		if (bflag)
		    printf("%u:", l.off);
		fwrite(l.dat, l.len, 1, stdout);
		fputc('\n', stdout);
	    }
	    break;
	}
    }
    return c;
}

#if GZ
char *
gzfgetln(gzFile *f, size_t *len) 
{
    size_t n;
    int c;
    static char *s = NULL;

    for (n = 1, c = gzgetc(f); c != -1; c = gzgetc(f)) {
	s = realloc(s, n);
	s[n - 1] = c;
	n++;
	if (c == '\n')
	    break;
    }

    if (gzeof(f) && n == 1)
	return NULL;

    s[n - 1] = '\0';
    *len = n;
    return s;
}
#endif

void *
grep_malloc(size_t size)
{
    void *ptr;

    if ((ptr = malloc(size)) == NULL)
	err(1, "malloc");
    return ptr;
}

void *
grep_realloc(void *ptr, size_t size)
{
    if ((ptr = realloc(ptr, size)) == NULL)
	err(1, "realloc");
    return ptr;
}
