/* Copyright (C) 2001-2003, Ghostgum Software Pty Ltd.  All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/* 
 * This file is part of GSview.  The above notice applies only 
 * to this file.  It does NOT apply to any other file in GSview
 * unless that file includes the above copyright notice.
 */

/* $Id: wgsver.c,v 1.1.2.4 2003/07/14 08:03:09 ghostgum Exp $ */
/* Obtain details of copies of Ghostscript installed under Windows */

/* To compile as a demo program, define DUMP_GSVER */
/* #define DUMP_GSVER */

#include <windows.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include "wgsver.h"

/* Ghostscript may be known in the Windows Registry by
 * the following names.
 */
#define GS_PRODUCT_AFPL "AFPL Ghostscript"
#define GS_PRODUCT_ALADDIN "Aladdin Ghostscript"
#define GS_PRODUCT_GPL "GPL Ghostscript"
#define GS_PRODUCT_GNU "GNU Ghostscript"

/* Get Ghostscript versions for given product.
 * Store results starting at pver + 1 + offset.
 * Returns total number of versions in pver.
 */
static int get_gs_versions_product(int *pver, int offset, 
    const char *gs_productfamily)
{
    HKEY hkey;
    DWORD cbData;
    HKEY hkeyroot;
    char key[256];
    int ver;
    char *p;
    int n = 0;

    sprintf(key, "Software\\%s", gs_productfamily);
    hkeyroot = HKEY_LOCAL_MACHINE;
    if (RegOpenKeyExA(hkeyroot, key, 0, KEY_READ, &hkey) == ERROR_SUCCESS) {
	/* Now enumerate the keys */
	cbData = sizeof(key) / sizeof(char);
	while (RegEnumKeyA(hkey, n, key, cbData) == ERROR_SUCCESS) {
	    n++;
	    ver = 0;
	    p = key;
	    while (*p && (*p!='.')) {
		ver = (ver * 10) + (*p - '0')*100;
		p++;
	    }
	    if (*p == '.')
		p++;
	    if (*p) {
		ver += (*p - '0') * 10;
		p++;
	    }
	    if (*p)
		ver += (*p - '0');
	    if (n + offset < pver[0])
		pver[n+offset] = ver;
	}
    }
    return n+offset;
}

/* Query registry to find which versions of Ghostscript are installed.
 * Return version numbers in an integer array.   
 * On entry, the first element in the array must be the array size 
 * in elements.
 * If all is well, TRUE is returned.
 * On exit, the first element is set to the number of Ghostscript
 * versions installed, and subsequent elements to the version
 * numbers of Ghostscript.
 * e.g. on entry {5, 0, 0, 0, 0}, on exit {3, 550, 600, 596, 0}
 * Returned version numbers may not be sorted.
 *
 * If Ghostscript is not installed at all, return FALSE
 * and set pver[0] to 0.
 * If the array is not large enough, return FALSE 
 * and set pver[0] to the number of Ghostscript versions installed.
 */
BOOL get_gs_versions(int *pver)
{
    int n;
    if (pver == (int *)NULL)
	    return FALSE;

    n = get_gs_versions_product(pver, 0, GS_PRODUCT_AFPL);
    n = get_gs_versions_product(pver, n, GS_PRODUCT_ALADDIN);
    n = get_gs_versions_product(pver, n, GS_PRODUCT_GPL);
    n = get_gs_versions_product(pver, n, GS_PRODUCT_GNU);

    if (n >= pver[0]) {
	pver[0] = n;
	return FALSE;	/* too small */
    }

    if (n == 0) {
	pver[0] = 0;
	return FALSE;	/* not installed */
    }
    pver[0] = n;
    return TRUE;
}

 
/*
 * Get a named registry value.
 * Key = hkeyroot\\key, named value = name.
 * name, ptr, plen and return values are the same as in gp_getenv();
 */

int 
gp_getenv_registry(HKEY hkeyroot, const char *key, const char *name, 
    char *ptr, int *plen)
{
    HKEY hkey;
    DWORD cbData, keytype;
    BYTE b;
    LONG rc;
    BYTE *bptr = (BYTE *)ptr;

    if (RegOpenKeyExA(hkeyroot, key, 0, KEY_READ, &hkey)
	== ERROR_SUCCESS) {
	keytype = REG_SZ;
	cbData = *plen;
	if (bptr == (BYTE *)NULL)
	    bptr = &b;	/* Registry API won't return ERROR_MORE_DATA */
			/* if ptr is NULL */
	rc = RegQueryValueExA(hkey, (char *)name, 0, &keytype, bptr, &cbData);
	RegCloseKey(hkey);
	if (rc == ERROR_SUCCESS) {
	    *plen = cbData;
	    return 0;	/* found environment variable and copied it */
	} else if (rc == ERROR_MORE_DATA) {
	    /* buffer wasn't large enough */
	    *plen = cbData;
	    return -1;
	}
    }
    return 1;	/* not found */
}


static BOOL get_gs_string_product(int gs_revision, const char *name, 
    char *ptr, int len, const char *gs_productfamily)
{
    /* If using Win32, look in the registry for a value with
     * the given name.  The registry value will be under the key
     * HKEY_CURRENT_USER\Software\AFPL Ghostscript\N.NN
     * or if that fails under the key
     * HKEY_LOCAL_MACHINE\Software\AFPL Ghostscript\N.NN
     * where "AFPL Ghostscript" is actually gs_productfamily
     * and N.NN is obtained from gs_revision.
     */

    int code;
    char key[256];
    char dotversion[16];
    int length;
    DWORD version = GetVersion();

    if (((HIWORD(version) & 0x8000) != 0)
	  && ((HIWORD(version) & 0x4000) == 0)) {
	/* Win32s */
	return FALSE;
    }


    sprintf(dotversion, "%d.%02d", 
	    (int)(gs_revision / 100), (int)(gs_revision % 100));
    sprintf(key, "Software\\%s\\%s", gs_productfamily, dotversion);

    length = len;
    code = gp_getenv_registry(HKEY_CURRENT_USER, key, name, ptr, &length);
    if ( code == 0 )
	return TRUE;	/* found it */

    length = len;
    code = gp_getenv_registry(HKEY_LOCAL_MACHINE, key, name, ptr, &length);

    if ( code == 0 )
	return TRUE;	/* found it */

    return FALSE;
}

BOOL get_gs_string(int gs_revision, const char *name, char *ptr, int len)
{
    if (get_gs_string_product(gs_revision, name, ptr, len, GS_PRODUCT_AFPL))
	return TRUE;
    if (get_gs_string_product(gs_revision, name, ptr, len, GS_PRODUCT_ALADDIN))
	return TRUE;
    if (get_gs_string_product(gs_revision, name, ptr, len, GS_PRODUCT_GPL))
	return TRUE;
    if (get_gs_string_product(gs_revision, name, ptr, len, GS_PRODUCT_GNU))
	return TRUE;
    return FALSE;
}



/* Set the latest Ghostscript EXE or DLL from the registry */
BOOL
find_gs(char *gspath, int len, int minver, BOOL bDLL)
{
    int count;
    int *ver;
    int gsver;
    char buf[256];
    char *p;
    int i;

    DWORD version = GetVersion();
    if ( ((HIWORD(version) & 0x8000)!=0) && ((HIWORD(version) & 0x4000)==0) )
	return FALSE;  // win32s

    count = 1;
    get_gs_versions(&count);
    if (count < 1)
	return FALSE;
    ver = (int *)malloc((count+1)*sizeof(int));
    if (ver == (int *)NULL)
	return FALSE;
    ver[0] = count+1;
    if (!get_gs_versions(ver)) {
	free(ver);
	return FALSE;
    }
    gsver = 0;
    for (i=1; i<=ver[0]; i++) {
	if (ver[i] > gsver)
	    gsver = ver[i];
    }
    free(ver);
    if (gsver < minver)	// minimum version (e.g. for gsprint)
	return FALSE;
    
    if (!get_gs_string(gsver, "GS_DLL", buf, sizeof(buf)))
	return FALSE;

    if (bDLL) {
	strncpy(gspath, buf, len-1);
	return TRUE;
    }

    p = strrchr(buf, '\\');
    if (p) {
	p++;
	*p = 0;
	strncpy(p, "gswin32c.exe", sizeof(buf)-1-strlen(buf));
	strncpy(gspath, buf, len-1);
	return TRUE;
    }

    return FALSE;
}


#ifdef DUMP_GSVER
/* This is an example of how you can use the above functions */
int main(int argc, char *argv[])
{
    BOOL flag;
    int n;
    int ver[10];
    int i;
    char buf[256];

    if (find_gs(buf, sizeof(buf), 550, TRUE))
	printf("Latest GS DLL is %s\n", buf);
    if (find_gs(buf, sizeof(buf), 550, FALSE))
	printf("Latest GS EXE is %s\n", buf);

    ver[0] = sizeof(ver) / sizeof(int);
    flag = get_gs_versions(ver);
    printf("Versions: %d\n", ver[0]);

    if (flag == FALSE) {
	printf("get_gs_versions failed, need %d\n", ver[0]);
	return 1;
    }

    for (i=1; i <= ver[0]; i++) {
	printf(" %d\n", ver[i]);
	if (get_gs_string(ver[i], "GS_DLL", buf, sizeof(buf)))
	    printf("   GS_DLL=%s\n", buf);
	if (get_gs_string(ver[i], "GS_LIB", buf, sizeof(buf)))
	    printf("   GS_LIB=%s\n", buf);
    }
    return 0;
}
#endif
