/*
 * util.c
 *
 * Copyright 2004, Max Vozeler <max@hinterhof.net>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */ 

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include "util.h"

#if HAVE_MNTENT_H
#include <mntent.h>
#endif

/* globals */
int cdtool_show_crlf = DOLF;
int cdtool_show_verbose = FALSE;
int cdtool_show_debug = FALSE;
char *cdtool_progname = "unknown";

/*
 *
 */

char *device_detect(char *choice)
{
    char dev[PATH_MAX];
    struct device_name *m;
    int i;
    
    /*  device name, number of subdevices */
    static struct device_name {
        char *name;
        int subcount;
    } device_map[] = {
        { "/dev/cdrom", 1 },        /* Linux default */
        { "/dev/cdrom%d", 16 },     /* Other Linuxes? */
        { "/dev/scd%d", 16 },       /* Linux SCSI */
        { "/dev/sonycd", 1 },       /* Sony */
        { "/dev/mcd", 1 },          /* Mitsumi */
        { "/dev/sbpcd", 1 },        /* SoundBlaster Pro */ 
    /*  { "/dev/sr%d", 16 }, */     /* Sun SCSI (?) */
        { "/vol/dev/aliases/cdrom%d", 16 }, /* Sun Volume Manager */
        { NULL, 0 }
    };

    
    /* user choice; display any error messages */
    if (choice != NULL || (choice = getenv("CDTOOLDEV")) != NULL) {
        if (!is_device(choice, 1))
            return NULL;
        return choice;
    }

    /* try detection */
    m = &device_map[0];
    while (m->name != NULL) {
        i = 0;
        while (m->subcount > i) {
            snprintf(dev, sizeof(dev), m->name, i);
            if (is_device(dev, 0)) {
                /* found */
                debugmsg("using detected cd_device=%s", dev);
                return strdup(dev);
            }
            i++;
        }
        m++;
    }
 
    return NULL;
}

/*
 *
 */

int is_device(char *path, int verbose)
{
    struct stat s;

    if (stat(path, &s) != 0) {
        if (verbose)
            errormsg("%P: can't open cdrom (%s): %s", path, strerror(errno));
        return 0;
    }

    if (!S_ISBLK(s.st_mode)) {
        if (verbose)
            errormsg("%P: can't open cdrom (%s): not a block device.", path);
        return 0;
    }

    return 1;
}

void show_permissions(char *device)
{
    struct stat sb;
    struct group *g;
    gid_t groups[1024];
    int ngroups;

    if (stat(device, &sb) < 0) {
        errormsg("%P: cannot stat %s: %s", device, strerror(errno));
    } else {
        if (!(sb.st_mode & 0040)) {
            errormsg("    %s is not group-readable.");
            errormsg("    Try, as root: chmod g+r %s", device, device);
        }
        if (!(g = getgrgid(sb.st_gid))) {
            errormsg("    %s is owned by unknown group %lu%s", device,
              (unsigned long)sb.st_gid);
            errormsg("    Try, as root: chgrp --dereference cdrom %s", device);
        } else if (strcmp(g->gr_name, "cdrom")) {
            errormsg("    %s is owned by group \"%s\"", device, g->gr_name);
            errormsg("    Try, as root: chgrp --dereference cdrom %s", device);
        }
    }

    if (!(g = getgrnam("cdrom"))) {
        errormsg("    This system appears to have no cdrom group!");
        errormsg("    Try, as root: addgroup cdrom");
    } else {
        if ((ngroups = getgroups(sizeof groups/sizeof *groups, groups)) < 0)
            errormsg("%P: cannot get group list: %s", strerror(errno));
        else {
            int found = 0;
            while (ngroups--) {
                if (groups[ngroups] == g->gr_gid) {
                    found = 1;
                    break;
                }
            }
            if (!found) {
                struct passwd *p;
                p = getpwuid(getuid());
                errormsg("    You are not in the \"cdrom\" group.");
                errormsg("    Try, as root: adduser %s cdrom",
                        p ? p->pw_name : "<your username>");
                errormsg("    You will need to log in again before this"
                        "change takes effect");
            }
        }
    }
}

/************************************************************************/
/* Procedure:  checkmount 
 * Purpose:    to check if this CD is mounted
 * 
 * Inputs:     name of program, name of CD-ROM
 * Outputs:    debug
 * Returns:    T/F
 * Notes:  
 */
/************************************************************************/
int checkmount(char *pszName) {
#if HAVE_MNTENT_H
    FILE  *fp;
    struct mntent *mnt;
    int    ii;
    char   caB[100];
    char   *pszTest;

    /* find out if the device is a link, resolve link name */
    ii = readlink(pszName, caB, sizeof(caB)-1);
    if (ii < 0) {
        pszTest = pszName;
        debugmsg("checkmount:  %s not a link", pszName);
    } else {
        pszTest = &caB[0];
        caB[ii] = '\0';
        debugmsg("checkmount:  link -> %s", caB);
    }

    /* check if drive is mounted (from Mark Buckaway's cdplayer code) */
    if ((fp = setmntent (MOUNTED, "r")) == NULL) {
        errormsg("%s: Couldn't open %s: %s", pszName, MOUNTED,
                strerror (errno));
        return (-1);
    }
    while ((mnt = getmntent (fp)) != NULL) {
        if (strstr (mnt->mnt_fsname, pszTest) != NULL) {
            endmntent (fp);
            return (TRUE);
        }
    }
    endmntent (fp);
#endif
    return (FALSE);
}

/*
 * Solaris needs this
 *
 */

#ifndef HAVE_STRSEP
char *strsep(char **stringp, const char *delim)
{
    char *str = *stringp;
    char *end;

    if (!str)
        return NULL;

    end = strpbrk(str, delim);
    if (end)
        *end++ = '\0';

    *stringp = end;
    return str;
}
#endif

/*
 * 
 */

void do_crlf(FILE *term)
{
    static char *newline[] = { "", "\n", "\r\n" };
    fprintf(term, "%s", newline[cdtool_show_crlf]);
}

void bugfound(char *message, char *file, char *function, int line)
{
    struct utsname uts;
    uname(&uts);

    errormsg("\n"
     "===========================================================\n"
     "BUG: %s at file %s, function %s (line %d)\n",
     message, file, function, line);

    errormsg(
     "Oops. Looks like you found a bug in cdtool. Please send this\n"
     "message along with other information that you think could be\n"
     "useful to cdtool-devel@lists.hinterhof.net. Thanks.\n"
     );

    errormsg("CDTOOL %s running on %s", PACKAGE_VERSION, uts.sysname);
    errormsg("  %s %s %s (version %s)",
     uts.machine,
     uts.sysname,
     uts.release,
     uts.version);

    errormsg("===========================================================\n");
}

void usermsg(FILE *term, char *fmt, va_list args)
{
    if (strncmp(fmt, "%P", 2) == 0) {
        fprintf(term, "%s", cdtool_progname);
        fmt += 2;
    }

    vfprintf(term, fmt, args);
    do_crlf(term);
}

void debugmsg(char *fmt, ...)
{
    va_list args;
    if (!cdtool_show_debug)
        return;
    va_start(args, fmt);
    usermsg(stderr, fmt, args);
    va_end(args);
}

void errormsg(char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    usermsg(stderr, fmt, args);
    va_end(args);
}

void verbosemsg(char *fmt, ...)
{
    va_list args;
    if (!cdtool_show_verbose)
        return;
    va_start(args, fmt);
    usermsg(stdout, fmt, args);
    va_end(args);
}

void infomsg(char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    usermsg(stdout, fmt, args);
    va_end(args);
}

char *getprogname(void)
{
    return cdtool_progname;
}

char *setprogname(char *name)
{
    char *p;
    p = strrchr(name, '/');
    if (!p)
        p = name;
    else
        p++;
    cdtool_progname = p;
    return p;
}

