#include "cupsd.h"
#include <ctype.h>
typedef struct
{
char ppd_make[128],
ppd_make_and_model[248];
int ppd_size,
ppd_mtime;
char ppd_name[256],
ppd_natural_language[16];
} ppd_rec_t;
typedef struct
{
int found;
ppd_rec_t record;
} ppd_info_t;
static int num_ppds,
sorted_ppds,
alloc_ppds;
static ppd_info_t *ppds;
static int changed_ppd;
static int compare_names(const ppd_info_t *p0, const ppd_info_t *p1);
static int compare_ppds(const ppd_info_t *p0, const ppd_info_t *p1);
static void load_ppds(const char *d, const char *p);
void
LoadPPDs(const char *d)
{
int i;
ppd_info_t *ppd;
cups_file_t *fp;
struct stat fileinfo;
char filename[1024];
num_ppds = 0;
alloc_ppds = 0;
ppds = (ppd_info_t *)0;
changed_ppd = 0;
snprintf(filename, sizeof(filename), "%s/ppds.dat", ServerRoot);
if (!stat(filename, &fileinfo) &&
(num_ppds = fileinfo.st_size / sizeof(ppd_rec_t)) > 0)
{
alloc_ppds = num_ppds;
if ((ppds = malloc(sizeof(ppd_info_t) * num_ppds)) == NULL)
{
LogMessage(L_ERROR, "LoadPPDs: Unable to allocate memory for %d PPD files!",
num_ppds);
num_ppds = 0;
alloc_ppds = 0;
}
else if ((fp = cupsFileOpen(filename, "rb")) != NULL)
{
for (i = num_ppds, ppd = ppds; i > 0; i --, ppd ++)
{
cupsFileRead(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
ppd->found = 0;
}
cupsFileClose(fp);
LogMessage(L_INFO, "LoadPPDs: Read \"%s\", %d PPDs...", filename,
num_ppds);
if (num_ppds > 1)
{
qsort(ppds, num_ppds, sizeof(ppd_info_t),
(int (*)(const void *, const void *))compare_names);
}
}
else
{
LogMessage(L_ERROR, "LoadPPDs: Unable to read \"%s\" - %s", filename,
strerror(errno));
num_ppds = 0;
}
}
sorted_ppds = num_ppds;
load_ppds(d, "");
for (i = num_ppds, ppd = ppds; i > 0; i --, ppd ++)
if (!ppd->found)
{
if (i > 1)
memmove(ppd, ppd + 1, (i - 1) * sizeof(ppd_info_t));
num_ppds --;
ppd --;
}
if (num_ppds > 1)
qsort(ppds, num_ppds, sizeof(ppd_info_t),
(int (*)(const void *, const void *))compare_ppds);
if (changed_ppd)
{
if ((fp = cupsFileOpen(filename, "wb")) != NULL)
{
for (i = num_ppds, ppd = ppds; i > 0; i --, ppd ++)
cupsFileWrite(fp, (char *)&(ppd->record), sizeof(ppd_rec_t));
cupsFileClose(fp);
LogMessage(L_INFO, "LoadPPDs: Wrote \"%s\", %d PPDs...", filename,
num_ppds);
}
else
LogMessage(L_ERROR, "LoadPPDs: Unable to write \"%s\" - %s", filename,
strerror(errno));
}
else
LogMessage(L_INFO, "LoadPPDs: No new or changed PPDs...");
PPDs = ippNew();
ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_NAME,
"ppd-name", NULL, "raw");
ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"ppd-make", NULL, "Raw");
ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"ppd-make-and-model", NULL, "Raw Queue");
ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
"ppd-natural-language", NULL, "en");
for (i = num_ppds, ppd = ppds; i > 0; i --, ppd ++)
{
ippAddSeparator(PPDs);
ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_NAME,
"ppd-name", NULL, ppd->record.ppd_name);
ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"ppd-make", NULL, ppd->record.ppd_make);
ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"ppd-make-and-model", NULL, ppd->record.ppd_make_and_model);
ippAddString(PPDs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE,
"ppd-natural-language", NULL, ppd->record.ppd_natural_language);
}
if (alloc_ppds)
{
free(ppds);
alloc_ppds = 0;
}
}
static int
compare_names(const ppd_info_t *p0,
const ppd_info_t *p1)
{
return (strcasecmp(p0->record.ppd_name, p1->record.ppd_name));
}
static int
compare_ppds(const ppd_info_t *p0,
const ppd_info_t *p1)
{
const char *s,
*t;
int diff,
digits;
if ((diff = strcasecmp(p0->record.ppd_make, p1->record.ppd_make)) != 0)
return (diff);
s = p0->record.ppd_make_and_model;
t = p1->record.ppd_make_and_model;
while (*s && *t)
{
if (isdigit(*s & 255) && isdigit(*t & 255))
{
while (*s == '0')
s ++;
while (*t == '0')
t ++;
while (isdigit(*s & 255) && *s == *t)
{
s ++;
t ++;
}
if (isdigit(*s & 255) && !isdigit(*t & 255))
return (1);
else if (!isdigit(*s & 255) && isdigit(*t & 255))
return (-1);
else if (!isdigit(*s & 255) || !isdigit(*t & 255))
continue;
if (*s < *t)
diff = -1;
else
diff = 1;
digits = 0;
s ++;
t ++;
while (isdigit(*s & 255))
{
digits ++;
s ++;
}
while (isdigit(*t & 255))
{
digits --;
t ++;
}
if (digits < 0)
return (-1);
else if (digits > 0)
return (1);
else if (diff)
return (diff);
}
else if (tolower(*s & 255) < tolower(*t & 255))
return (-1);
else if (tolower(*s & 255) > tolower(*t & 255))
return (1);
else
{
s ++;
t ++;
}
}
if (*s)
return (1);
else if (*t)
return (-1);
else
return (strcasecmp(p0->record.ppd_natural_language,
p1->record.ppd_natural_language));
}
static void
load_ppds(const char *d,
const char *p)
{
int i;
cups_file_t *fp;
DIR *dir;
DIRENT *dent;
struct stat fileinfo;
char filename[1024],
line[256],
*ptr,
name[128],
language[64],
country[64],
manufacturer[256],
make_model[256],
model_name[256],
nick_name[256];
ppd_info_t *ppd,
key;
int new_ppd;
struct
{
const char *version,
*language;
} languages[] =
{
{ "chinese", "cn" },
{ "danish", "da" },
{ "dutch", "nl" },
{ "english", "en" },
{ "finnish", "fi" },
{ "french", "fr" },
{ "german", "de" },
{ "greek", "el" },
{ "italian", "it" },
{ "japanese", "jp" },
{ "norwegian", "no" },
{ "polish", "pl" },
{ "portuguese", "pt" },
{ "russian", "ru" },
{ "slovak", "sk" },
{ "spanish", "es" },
{ "swedish", "sv" },
{ "turkish", "tr" }
};
if ((dir = opendir(d)) == NULL)
{
LogMessage(L_ERROR, "LoadPPDs: Unable to open PPD directory \"%s\": %s",
d, strerror(errno));
return;
}
while ((dent = readdir(dir)) != NULL)
{
if (dent->d_name[0] == '.')
continue;
snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name);
if (p[0])
snprintf(name, sizeof(name), "%s/%s", p, dent->d_name);
else
strlcpy(name, dent->d_name, sizeof(name));
if (stat(filename, &fileinfo))
continue;
if (S_ISDIR(fileinfo.st_mode))
{
load_ppds(filename, name);
continue;
}
if (sorted_ppds > 0)
{
strcpy(key.record.ppd_name, name);
ppd = bsearch(&key, ppds, sorted_ppds, sizeof(ppd_info_t),
(int (*)(const void *, const void *))compare_names);
if (ppd &&
ppd->record.ppd_size == fileinfo.st_size &&
ppd->record.ppd_mtime == fileinfo.st_mtime)
{
ppd->found = 1;
continue;
}
}
else
ppd = NULL;
if ((fp = cupsFileOpen(filename, "rb")) == NULL)
continue;
line[0] = '\0';
cupsFileGets(fp, line, sizeof(line));
if (strncmp(line, "*PPD-Adobe:", 11) != 0)
{
cupsFileClose(fp);
continue;
}
model_name[0] = '\0';
nick_name[0] = '\0';
manufacturer[0] = '\0';
strcpy(language, "en");
while (cupsFileGets(fp, line, sizeof(line)) != NULL)
{
if (strncmp(line, "*Manufacturer:", 14) == 0)
sscanf(line, "%*[^\"]\"%255[^\"]", manufacturer);
else if (strncmp(line, "*ModelName:", 11) == 0)
sscanf(line, "%*[^\"]\"%127[^\"]", model_name);
else if (strncmp(line, "*LanguageVersion:", 17) == 0)
sscanf(line, "%*[^:]:%63s", language);
else if (strncmp(line, "*NickName:", 10) == 0)
sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
else if (strncmp(line, "*OpenUI", 7) == 0)
{
if (model_name[0] || nick_name[0])
break;
}
if (manufacturer[0] && nick_name[0])
break;
}
cupsFileClose(fp);
if (nick_name[0])
strcpy(make_model, nick_name);
else
strcpy(make_model, model_name);
while (isspace(make_model[0] & 255))
cups_strcpy(make_model, make_model + 1);
if (!make_model[0])
continue;
while (isspace(manufacturer[0] & 255))
cups_strcpy(manufacturer, manufacturer + 1);
if (!manufacturer[0] || strcmp(manufacturer, "ESP") == 0)
{
strlcpy(manufacturer, make_model, sizeof(manufacturer));
for (ptr = manufacturer; *ptr; ptr ++)
if (*ptr == ' ' || *ptr == '-' || *ptr == '/')
break;
if (*ptr && ptr > manufacturer)
*ptr = '\0';
else if (strncasecmp(manufacturer, "agfa", 4) == 0)
strcpy(manufacturer, "AGFA");
else if (strncasecmp(manufacturer, "herk", 4) == 0 ||
strncasecmp(manufacturer, "linotype", 8) == 0)
strcpy(manufacturer, "LHAG");
else
strcpy(manufacturer, "Other");
if (strcasecmp(manufacturer, "XPrint") == 0)
strcpy(manufacturer, "Xerox");
else if (strcasecmp(manufacturer, "Eastman") == 0)
strcpy(manufacturer, "Kodak");
else if (strcasecmp(manufacturer, "laserwriter") == 0)
strcpy(manufacturer, "Apple");
else if (strcasecmp(manufacturer, "colorpoint") == 0)
strcpy(manufacturer, "Seiko");
else if (strcasecmp(manufacturer, "fiery") == 0)
strcpy(manufacturer, "EFI");
else if (strcasecmp(manufacturer, "ps") == 0 ||
strcasecmp(manufacturer, "colorpass") == 0)
strcpy(manufacturer, "Canon");
else if (strncasecmp(manufacturer, "primera", 7) == 0)
strcpy(manufacturer, "Fargo");
else if (strcasecmp(manufacturer, "designjet") == 0)
strcpy(manufacturer, "HP");
}
else if (strncasecmp(manufacturer, "LHAG", 4) == 0 ||
strncasecmp(manufacturer, "linotype", 8) == 0)
strcpy(manufacturer, "LHAG");
if ((ptr = strchr(language, '-')) != NULL)
*ptr++ = '\0';
else if ((ptr = strchr(language, '_')) != NULL)
*ptr++ = '\0';
if (ptr)
{
country[0] = '_';
cups_strcpy(country + 1, ptr);
}
else
{
country[0] = '\0';
}
for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
if (strcasecmp(languages[i].version, language) == 0)
break;
if (i < (int)(sizeof(languages) / sizeof(languages[0])))
{
snprintf(language, sizeof(language), "%s%s", languages[i].language,
country);
}
else
{
strcpy(language, "xx");
}
new_ppd = !ppd;
if (new_ppd)
{
LogMessage(L_DEBUG, "LoadPPDs: Adding ppd \"%s\"...", name);
if (num_ppds >= alloc_ppds)
{
if (alloc_ppds == 0)
ppd = malloc(sizeof(ppd_info_t) * 32);
else
ppd = realloc(ppds, sizeof(ppd_info_t) * (alloc_ppds + 32));
if (ppd == NULL)
{
LogMessage(L_ERROR, "load_ppds: Ran out of memory for %d PPD files!",
alloc_ppds + 32);
closedir(dir);
return;
}
ppds = ppd;
alloc_ppds += 32;
}
ppd = ppds + num_ppds;
num_ppds ++;
}
else
LogMessage(L_DEBUG, "LoadPPDs: Updating ppd \"%s\"...", name);
memset(ppd, 0, sizeof(ppd_info_t));
ppd->found = 1;
ppd->record.ppd_mtime = fileinfo.st_mtime;
ppd->record.ppd_size = fileinfo.st_size;
strlcpy(ppd->record.ppd_name, name,
sizeof(ppd->record.ppd_name));
strlcpy(ppd->record.ppd_make, manufacturer,
sizeof(ppd->record.ppd_make));
strlcpy(ppd->record.ppd_make_and_model, make_model,
sizeof(ppd->record.ppd_make_and_model));
strlcpy(ppd->record.ppd_natural_language, language,
sizeof(ppd->record.ppd_natural_language));
changed_ppd = 1;
}
closedir(dir);
}