/*
 * XML Catalog Manager (xmlcatmgr)
 * $Id: generic.c,v 1.5 2004/05/01 21:06:40 jmmv Exp $
 *
 * Copyright (c) 2003, 2004 Julio M. Merino Vidal.  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.
 * 3. Neither the name of the author nor the names of contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * 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.
 */

/*
 * Functions in this file are called directly from main, based on the
 * action given in the command line.  They issue tasks that are common
 * to both XML and SGML modes, and the proceed to execute the specific
 * mode action based on function pointers.
 */

#include "system.h"

#ifndef lint
__RCSID("$Id: generic.c,v 1.5 2004/05/01 21:06:40 jmmv Exp $");
#endif

#include "generic.h"
#include "mem.h"

/* --------------------------------------------------------------------- */

/*
 * Common tasks to the add action: open the catalog file for read and
 * write, lock it and execute the specific function depending on the
 * mode.
 */
bool
generic_add(int argc, char *const *argv, const char *catname,
            bool (*func)(int, char *const *, FILE *, bool), bool prepend)
{
    bool res;
    FILE *f;

    assert(argv != NULL && catname != NULL && func != NULL);

    f = fopen(catname, "r+");
    if (f == NULL) {
        warn("failed to open `%s'", catname);
        res = false;
    } else {
        (void)lockf(fileno(f), F_LOCK, 0);
        res = func(argc, argv, f, prepend);
        (void)lockf(fileno(f), F_ULOCK, 0);
        fclose(f);
    }

    return res;
}

/* --------------------------------------------------------------------- */

/*
 * Common tasks to the create action: create the catalog file, wait for
 * a lock and initialize it with the respective mode function.
 * Obtaining the lock is tricky, because open(2) can't do it atomically
 * in a standard way.
 */
bool
generic_create(const char *catname, bool (*func)(FILE *))
{
    bool res;
    int fd;

    assert(catname != NULL && func != NULL);

    res = false;

    fd = open(catname, O_WRONLY | O_CREAT | O_EXCL, 0644);
    if (fd == -1) {
        warn("failed to create `%s'", catname);
    } else {
        FILE *f;

        f = fdopen(fd, "a");
        if (f == NULL) {
            warn("failed to create `%s'", catname);
            close(fd);
        } else {
            (void)lockf(fileno(f), F_LOCK, 0);
            fseek(f, 0, SEEK_END);
            if (ftell(f) != 0L) {
                warnx("failed to create `%s': modified by another process",
                      catname);
            } else {
                res = func(f);
                if (!res)
                    warn("failed to initialize `%s'", catname);
            }
            (void)lockf(fileno(f), F_ULOCK, 0);
            fclose(f);

            if (!res)
                (void)generic_destroy(catname);
        }
    }

    return res;
}

/* --------------------------------------------------------------------- */

/*
 * Destroy action: remove the catalog file.  Catalog modes do not have
 * to do anything special.
 */
bool
generic_destroy(const char *catname)
{
    bool res;

    assert(catname != NULL);

    res = unlink(catname) == -1 ? false : true;
    if (!res)
        warn("failed to destroy `%s'", catname);

    return res;
}

/* --------------------------------------------------------------------- */

/*
 * Common tasks to the lookup action: open the catalog for read and call
 * the respective mode function.
 */
bool
generic_lookup(int argc, char *const *argv, const char *catname,
               bool (*func)(int, char *const *, FILE *))
{
    bool res;
    FILE *f;

    assert(argv != NULL && catname != NULL && func != NULL);

    if (argc == 0) {
        warnx("too few arguments for `lookup' action");
        res = false;
    } else {
        f = fopen(catname, "r");
        if (f == NULL) {
            warn("failed to open `%s'", catname);
            res = false;
        } else {
            res = func(argc, argv, f);
            fclose(f);
        }
    }

    return res;
}

/* --------------------------------------------------------------------- */

/*
 * Common tasks to the remove action: open the catalog file for read and
 * write, lock it and execute the specific function depending on the
 * mode.
 */
bool
generic_remove(int argc, char *const *argv, const char *catname,
               bool (*func)(int, char *const *, FILE *))
{
    bool res;
    FILE *f;

    assert(argv != NULL && catname != NULL && func != NULL);

    f = fopen(catname, "r+");
    if (f == NULL) {
        warn("failed to open `%s'", catname);
        res = false;
    } else {
        (void)lockf(fileno(f), F_LOCK, 0);
        res = func(argc, argv, f);
        (void)lockf(fileno(f), F_ULOCK, 0);
        fclose(f);
    }

    return res;
}

/*
 * Local Variables: ***
 * mode: c ***
 * c-file-style: "stroustrup" ***
 * End: ***
 * vim: syntax=c:expandtab:shiftwidth=4:softtabstop=4
 */
