Blob Blame History Raw
/**
 * @file expGperf.c
 *
 *  Create a perfect hash function program and use it to compute
 *  index values for a list of provided names.  It also documents how
 *  to incorporate that hashing function into a generated C program.
 *
 * @addtogroup autogen
 * @{
 */
/*
 *  This file is part of AutoGen.
 *  Copyright (C) 1992-2016 Bruce Korb - all rights reserved
 *
 * AutoGen 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 3 of the License, or
 * (at your option) any later version.
 *
 * AutoGen 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, see <http://www.gnu.org/licenses/>.
 */

#ifndef SHELL_ENABLED
HIDE_FN(SCM ag_scm_make_gperf(SCM name, SCM hlist))
{
    return SCM_UNDEFINED;
}

HIDE_FN(SCM ag_scm_gperf(SCM name, SCM str))
{
    return SCM_UNDEFINED;
}
#else

/*=gfunc make_gperf
 *
 * what:   build a perfect hash function program
 * general_use:
 *
 * exparg: name , name of hash list
 * exparg: strings , list of strings to hash ,, list
 *
 * doc:  Build a program to perform perfect hashes of a known list of input
 *       strings.  This function produces no output, but prepares a program
 *       named, @file{gperf_<name>} for use by the gperf function
 *       @xref{SCM gperf}.
 *
 *       This program will be obliterated as AutoGen exits.
 *       However, you may incorporate the generated hashing function
 *       into your C program with commands something like the following:
 *
 *       @example
 *       [+ (shellf "sed '/^int main(/,$d;/^#line/d' $@{gpdir@}/%s.c"
 *                  name ) +]
 *       @end example
 *
 *       where @code{name} matches the name provided to this @code{make-perf}
 *       function.  @code{gpdir} is the variable used to store the name of the
 *       temporary directory used to stash all the files.
=*/
SCM
ag_scm_make_gperf(SCM name, SCM hlist)
{
    static bool do_cleanup = true;

    char const * gp_nam = ag_scm2zchars(name, "gp nm");
    char const * h_list;
    SCM          nl_scm = scm_from_latin1_stringn(NEWLINE, (size_t)1);

    if (! scm_is_string(name))
        return SCM_UNDEFINED;

    /*
     *  Construct the newline separated list of values
     */
    hlist  = ag_scm_join(nl_scm, hlist);
    h_list = ag_scm2zchars(hlist, "hash list");

    /*
     *  Stash the concatenated list somewhere, hopefully without an alloc.
     */
    {
        char * cmd = aprf(MK_GPERF_SCRIPT, make_prog, gp_nam, h_list);

        /*
         *  Run the command and ignore the results.
         *  In theory, the program should be ready.
         */
        h_list = shell_cmd(cmd);
        AGFREE(cmd);

        if (h_list != NULL)
            free(VOIDP(h_list));
    }

    if (do_cleanup) {
        SCM_EVAL_CONST(MAKE_GPERF_CLEANUP);
        do_cleanup = false;
    }

    return SCM_BOOL_T;
}


/*=gfunc gperf
 *
 * what:   perform a perfect hash function
 * general_use:
 *
 * exparg: name , name of hash list
 * exparg: str  , string to hash
 *
 * doc:  Perform the perfect hash on the input string.  This is only useful if
 *       you have previously created a gperf program with the @code{make-gperf}
 *       function @xref{SCM make-gperf}.  The @code{name} you supply here must
 *       match the name used to create the program and the string to hash must
 *       be one of the strings supplied in the @code{make-gperf} string list.
 *       The result will be a perfect hash index.
 *
 *       See the documentation for @command{gperf(1GNU)} for more details.
=*/
SCM
ag_scm_gperf(SCM name, SCM str)
{
    char const * cmd;
    char const * key2hash = ag_scm2zchars(str,  "key-to-hash");
    char const * gp_name  = ag_scm2zchars(name, "gperf name");

    /*
     *  Format the gperf command and check the result.  If it fits in
     *  scribble space, use that.
     *  (If it does fit, then the test string fits already).
     */
    cmd = aprf(RUN_GPERF_CMD, gp_name, key2hash);
    key2hash = shell_cmd(cmd);
    if (*key2hash == NUL)
        str = SCM_UNDEFINED;
    else
        str = scm_from_latin1_string(key2hash);

    AGFREE(cmd);
    AGFREE(key2hash);
    return str;
}
#endif
/**
 * @}
 *
 * Local Variables:
 * mode: C
 * c-file-style: "stroustrup"
 * indent-tabs-mode: nil
 * End:
 * end of agen5/expGperf.c */