/* mkstemp.c -- create a temporary file
 *
 * This code is Copyright (c) 2014 by the authors of nmh.
 * See the COPYRIGHT file in the root directory of the nmh
 * distribution for complete copyright information.
 */

/* define NMH to 0 to remove dependencies on nmh. */
#ifndef NMH
#   define NMH 1
#endif /* ! NMH */

#ifdef HAVE_CONFIG_H
#   include <config.h>
#endif /* HAVE_CONFIG_H */
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>

#include <h/utils.h>

#if ! defined HAVE_MKSTEMPS
#   define HAVE_MKSTEMPS 0
#endif /* ! HAVE_MKSTEMPS */

char *build_template(const char *, const char *, const char *);
void process_args(int, char **, const char **, const char **, const char **);

/*
 * Use a template of the form:
 *     [directory/][prefix]XXXXXX[suffix]
 * where some of those named components might be null.  suffix is only
 * supported if HAVE_MKSTEMPS.
 */

int
main(int argc, char *argv[]) {
    const char *directory = "", *prefix = "", *suffix = "";
    size_t suffix_len;
    int fd;
    char *template;

    process_args(argc, argv, &directory, &prefix, &suffix);
    if ((template = build_template(directory, prefix, suffix)) == NULL) {
        return -1;
    }

    if ((suffix_len = strlen(suffix)) > 0) {
#       if HAVE_MKSTEMPS
        if ((fd = mkstemps(template, suffix_len)) < 0) { perror("mkstemps"); }
#       else  /* ! HAVE_MKSTEMPS */
        fd = 0;
#       endif /* ! HAVE_MKSTEMPS */
    } else {
        if ((fd = mkstemp(template)) < 0) { perror("mkstemp"); }
    }

    if (fd >= 0) {
        (void) puts(template);
        (void) close(fd);
    }

    free(template);

    return fd >= 0  ?  0  :  -1;
}


char *
build_template(const char *directory, const char *prefix, const char *suffix) {
    const char pattern[] = "XXXXXX";
    size_t len, directory_len, pathsep_len, prefix_len, suffix_len;
    char *template;

    directory_len = strlen(directory);
    if (directory_len > 0) {
        pathsep_len = 1;
        if (directory[directory_len - 1] == '/') {
            /* Will insert a '/' separately, so truncate the one provided
               in the directory name. */
            --directory_len;
        }
    } else {
        pathsep_len = 0;
    }
    prefix_len = strlen(prefix);
    suffix_len = strlen(suffix);
    /* sizeof pattern includes its final NULL, so don't add another. */
    len = directory_len  +  pathsep_len  +  prefix_len  +   sizeof pattern  +
        suffix_len;

    if ((template = malloc(len))) {
        char *tp = template;

        (void) strncpy(tp, directory, directory_len);
        tp += directory_len;

        if (pathsep_len == 1) { *tp++ = '/'; }

        (void) strncpy(tp, prefix, prefix_len);
        tp += prefix_len;

        (void) strncpy(tp, pattern, sizeof pattern - 1);
        tp += sizeof pattern - 1;

        (void) strncpy(tp, suffix, suffix_len);
        /* tp += suffix_len; */

        template[len-1] = '\0';

        return template;
    }

    perror("malloc");
    return NULL;
}


#if NMH
#include <h/mh.h>

#if HAVE_MKSTEMPS
#   define MHFIXMSG_SWITCHES \
    X("directory", 0, DIRECTORYSW) \
    X("prefix", 0, PREFIXSW) \
    X("suffix", 0, SUFFIXSW) \
    X("version", 0, VERSIONSW) \
    X("help", 0, HELPSW)
#else  /* ! HAVE_MKSTEMPS */
#   define MHFIXMSG_SWITCHES \
    X("directory", 0, DIRECTORYSW) \
    X("prefix", 0, PREFIXSW) \
    X("version", 0, VERSIONSW) \
    X("help", 0, HELPSW)
#endif /* ! HAVE_MKSTEMPS */

#define X(sw, minchars, id) id,
DEFINE_SWITCH_ENUM(MHFIXMSG);
#undef X

#define X(sw, minchars, id) { sw, minchars, id },
DEFINE_SWITCH_ARRAY(MHFIXMSG, switches);
#undef X

void
process_args(int argc, char **argv, const char **directory,
             const char **prefix, const char **suffix) {
    char **argp, **arguments, *cp, buf[100];
#   if ! HAVE_MKSTEMPS
    NMH_UNUSED(suffix);
#   endif /* ! HAVE_MKSTEMPS */

    if (nmh_init(argv[0], 2)) { done(NOTOK); }
    arguments = getarguments (invo_name, argc, argv, 1);
    argp = arguments;

    /*
     * Parse arguments
     */
    while ((cp = *argp++)) {
        if (*cp == '-') {
            switch (smatch(++cp, switches)) {
            case AMBIGSW:
                ambigsw(cp, switches);
                done(NOTOK);
            case UNKWNSW:
                inform("-%s unknown", cp);
                (void) snprintf(buf, sizeof buf, "%s [switches]", invo_name);
                print_help(buf, switches, 1);
                done(NOTOK);
            case HELPSW:
                (void) snprintf(buf, sizeof buf, "%s [switches]", invo_name);
                print_help(buf, switches, 1);
                done(OK);
            case VERSIONSW:
                print_version(invo_name);
                done(OK);

            case DIRECTORYSW:
                /* Allow the directory to start with '-'. */
                if ((cp = *argp++) == NULL) {
                    adios(NULL, "missing argument to %s", argp[-2]);
                }
                *directory = cp;
                continue;

            case PREFIXSW:
                /* Allow the prefix to start with '-'. */
                if ((cp = *argp++) == NULL) {
                    adios(NULL, "missing argument to %s", argp[-2]);
                }
                *prefix = cp;
                continue;

#           if HAVE_MKSTEMPS
            case SUFFIXSW:
                /* Allow the suffix to start with '-'. */
                if ((cp = *argp++) == NULL) {
                    adios(NULL, "missing argument to %s", argp[-2]);
                }
                *suffix = cp;
                continue;
#           endif /* HAVE_MKSTEMPS */
            }
        }
    }
}
#else  /* ! NMH */
void
process_args(int argc, char **argv, const char **directory,
             const char **prefix, const char **suffix) {
#   if HAVE_MKSTEMPS
    const char usage[] =
        "usage: %s [-h] [-d directory] [-p prefix] [-s suffix]\n";
    const char optstring[] = "d:hp:s:";
#   else  /* ! HAVE_MKSTEMPS */
    const char usage[] = "usage: %s [-h] [-d directory] [-p prefix]\n";
    const char optstring[] = "d:hp:";
#   endif /* ! HAVE_MKSTEMPS */
    int opt;

    while ((opt = getopt(argc, argv, optstring)) != -1) {
        switch (opt) {
        case 'd':
            *directory = optarg;
            break;
        case 'p':
            *prefix = optarg;
            break;
        case 's':
            *suffix = optarg;
            break;
        case 'h':
            (void) printf(usage, argv[0]);
            exit(0);
        default:
            (void) fprintf(stderr, usage, argv[0]);
            exit(-1);
        }
    }
}
#endif /* ! NMH */
