filter.c
author viric@mandarina
Sat, 11 Aug 2007 16:01:25 +0200
changeset 3 ba1b3c2fcff2
parent 2 57a1fcb0c75c
child 4 b2dfe3374454
permissions -rw-r--r--
Less memory use, less often output.

#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>

#include "dictre.h"

enum
{
    SIZESTEP=1000
};

extern struct Def defs[];
extern int ndefs;
extern int dont_touch[];
extern int ndont_touch;

static void more_memory(void **ptr, int size)
{
    void *new;

    new = realloc(*ptr, size);
    *ptr = new;
}

static
char * manage_filter(const char *def, int deflen, int writeto, int readfrom,
        int *outlen)
{
    int maxfd;
    int defptr;
    int outptr;
    char *out;
    int outsize;
    int outrest;
    int res;

    out = 0;
    outsize = SIZESTEP;
    outptr = 0;
    more_memory((void **) &out, outsize);
    outrest = SIZESTEP;

    /* We need unblocking write for select() */
    res = fcntl(writeto, F_SETFL, O_NONBLOCK);
    if (res == -1)
    {
        perror("Error setting nonblock to writeto");
        exit(-1);
    }

    maxfd = writeto;
    if (readfrom > maxfd)
        maxfd = readfrom;

    defptr = 0;
    do
    {
        fd_set writeset, readset;
        FD_ZERO(&writeset);
        FD_ZERO(&readset);
        if (defptr < deflen)
            FD_SET(writeto, &writeset);
        FD_SET(readfrom, &readset);

        select(maxfd+1, &readset, &writeset, 0, 0);

        if (FD_ISSET(readfrom, &readset))
        {
            int res;
            res = read(readfrom, out + outptr, outrest);
            if (res == 0)
            {
                close(readfrom);
                break;
            }
            outrest -= res;
            outptr += res;
            if (outrest == 0)
            {
                outrest = SIZESTEP;
                outsize += SIZESTEP;
                more_memory((void **) &out, outsize);
            }
        }

        if (FD_ISSET(writeto, &writeset))
        {
            int res;
            res = write(writeto, def+defptr, deflen - defptr);
            defptr += res;
            if (defptr >= deflen)
                close(writeto);
        }
    } while(1);

    /*
    {
        int i;
        printf("In : ");
        for(i=0; i < deflen; ++i)
            putchar(def[i]);
        printf("\nOut: ");
        for(i=0; i < outptr; ++i)
            putchar(out[i]);
        putchar('\n');
    }
    */

    if (defptr < deflen)
    {
        fprintf(stderr, "Error in filter! not all written.\n");
        exit(-1);
    }

    /* Give away memory don't needed */
    more_memory((void **) &out, outptr);

    *outlen = outptr;
    return out;
}

static char * filter(char *def, int deflen, const char *filter_par, int *outlen)
{
    int write_pipe[2];
    int read_pipe[2];
    int pid;
    int res;
    int status;
    char *out;

    pipe(write_pipe);
    pipe(read_pipe);


    pid = fork();
    switch(pid)
    {
        case 0:  /* child */
            close(0);
            dup(write_pipe[0]);
            close(write_pipe[0]);
            close(write_pipe[1]);
            close(1);
            dup(read_pipe[1]);
            close(read_pipe[1]);
            close(read_pipe[0]);
            execl(filter_par, filter_par, 0);
            perror("execlp");
            exit(-1);
            break;
        case -1:
            perror("fork");
            exit(-1);
            break;
        default:  /* parent */
            close(write_pipe[0]);
            close(read_pipe[1]);
            break;
    }

    /* parent */
    out = manage_filter(def, deflen, write_pipe[1], read_pipe[0], outlen);

    res = wait(&status);
    if (res != pid || WEXITSTATUS(status) != 0)
    {
        fprintf(stderr, "Error filtering: pid=%i status=%i",
                pid, WEXITSTATUS(status));
        exit(-1);
    }

    return out;
}

static int in_dont_touch(int n)
{
    int i;
    for(i =0; i < ndont_touch; ++i)
    {
        if (n == dont_touch[i])
        {
            return 1;
        }
    }
    return 0;
}

void filter_all(const char *filter_par)
{
    int i;
    static int dispndefs = 0;
    static int filtereddefs = 0;

    for(i=0; i < ndefs; ++i)
    {
        char *newdef;
        int newdeflen;
        if (!in_dont_touch(i))
        {
            if (defs[i].length > 0)
            {
                newdef = filter(defs[i].d, defs[i].length,
                        filter_par, &newdeflen);
                defs[i].length = newdeflen;
                free(defs[i].d);
                defs[i].d = newdef;
            }
            filtereddefs++; /* Not really all filtered. All but the 00-database* */
            dispndefs++;
            if (dispndefs >= 1000)
            {
                dispndefs = 0;
                printf("Filtered: %i/%i (%f%%)\n", filtereddefs, ndefs,
                        (float) filtereddefs / (float) ndefs);
            }
        }
    }
}