*
* Contents:
*
- * main() - Scan for drivers and return an IPP response.
- * add_ppd() - Add a PPD file.
- * cat_drv() - Generate a PPD from a driver info file.
- * cat_ppd() - Copy a PPD file to stdout.
- * copy_static() - Copy a static PPD file to stdout.
- * compare_names() - Compare PPD filenames for sorting.
- * compare_ppds() - Compare PPD file make and model names for sorting.
- * free_array() - Free an array of strings.
- * list_ppds() - List PPD files.
- * load_ppds() - Load PPD files recursively.
- * load_drv() - Load the PPDs from a driver information file.
- * load_drivers() - Load driver-generated PPD files.
+ * main() - Scan for drivers and return an IPP response.
+ * add_ppd() - Add a PPD file.
+ * cat_drv() - Generate a PPD from a driver info file.
+ * cat_ppd() - Copy a PPD file to stdout.
+ * copy_static() - Copy a static PPD file to stdout.
+ * compare_matches() - Compare PPD match scores for sorting.
+ * compare_names() - Compare PPD filenames for sorting.
+ * compare_ppds() - Compare PPD file make and model names for sorting.
+ * free_array() - Free an array of strings.
+ * list_ppds() - List PPD files.
+ * load_ppds() - Load PPD files recursively.
+ * load_drv() - Load the PPDs from a driver information file.
+ * load_drivers() - Load driver-generated PPD files.
+ * regex_device_id() - Compile a regular expression based on the 1284 device
+ * ID.
+ * regex_string() - Construct a regular expression to compare a simple
+ * string.
*/
/*
#include <cups/transcode.h>
#include <cups/ppd-private.h>
#include <ppdc/ppdc.h>
+#include <regex.h>
/*
typedef struct /**** In-memory record ****/
{
int found; /* 1 if PPD is found */
+ int matches; /* Match count */
ppd_rec_t record; /* PPDs.dat record */
} ppd_info_t;
static int cat_drv(const char *name, int request_id);
static int cat_ppd(const char *name, int request_id);
static int cat_static(const char *name, int request_id);
+static int compare_matches(const ppd_info_t *p0,
+ const ppd_info_t *p1);
static int compare_names(const ppd_info_t *p0,
const ppd_info_t *p1);
static int compare_ppds(const ppd_info_t *p0,
static int load_drv(const char *filename, const char *name,
cups_file_t *fp, time_t mtime, off_t size);
static int load_ppds(const char *d, const char *p, int descend);
+static regex_t *regex_device_id(const char *device_id);
+static regex_t *regex_string(const char *s);
/*
ppdcCatalog *catalog; // Message catalog in .drv file
- fprintf(stderr, "DEBUG: %d locales defined in \"%s\"...\n",
+ fprintf(stderr, "DEBUG: [cups-driverd] %d locales defined in \"%s\"...\n",
src->po_files->count, filename);
locales = new ppdcArray();
catalog;
catalog = (ppdcCatalog *)src->po_files->next())
{
- fprintf(stderr, "DEBUG: Adding locale \"%s\"...\n",
+ fprintf(stderr, "DEBUG: [cups-driverd] Adding locale \"%s\"...\n",
catalog->locale->value);
locales->add(catalog->locale);
}
}
+/*
+ * 'compare_matches()' - Compare PPD match scores for sorting.
+ */
+
+static int
+compare_matches(const ppd_info_t *p0, /* I - First PPD */
+ const ppd_info_t *p1) /* I - Second PPD */
+{
+ if (p1->matches != p0->matches)
+ return (p1->matches - p0->matches);
+ else
+ return (cupsdCompareNames(p1->record.make_and_model,
+ p0->record.make_and_model));
+}
+
+
/*
* 'compare_names()' - Compare PPD filenames for sorting.
*/
*type_str; /* ppd-type option */
int model_number, /* ppd-model-number value */
type, /* ppd-type value */
- mam_len, /* Length of ppd-make-and-model */
- device_id_len, /* Length of ppd-device-id */
+ make_and_model_len, /* Length of ppd-make-and-model */
+ product_len, /* Length of ppd-product */
send_device_id, /* Send ppd-device-id? */
send_make, /* Send ppd-make? */
send_make_and_model, /* Send ppd-make-and-model? */
send_psversion, /* Send ppd-psversion? */
send_type, /* Send ppd-type? */
sent_header; /* Sent the IPP header? */
+ regex_t *device_id_re, /* Regular expression for matching device ID */
+ *make_and_model_re; /* Regular expression for matching make and model */
+ regmatch_t re_matches[6]; /* Regular expression matches */
+ cups_array_t *matches; /* Matching PPDs */
fprintf(stderr,
type_str = cupsGetOption("ppd-type", num_options, options);
if (make_and_model)
- mam_len = strlen(make_and_model);
+ make_and_model_len = strlen(make_and_model);
else
- mam_len = 0;
+ make_and_model_len = 0;
- if (device_id)
- device_id_len = strlen(device_id);
+ if (product)
+ product_len = strlen(product);
else
- device_id_len = 0;
+ product_len = 0;
if (model_number_str)
model_number = atoi(model_number_str);
else
count = limit;
- for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
- count > 0 && ppd;
- ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
+ if (device_id || language || make || make_and_model || model_number_str ||
+ product)
{
- /*
- * Filter PPDs based on make, model, or device ID...
- */
+ matches = cupsArrayNew((cups_array_func_t)compare_matches, NULL);
- if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
- ppd->record.type >= PPD_TYPE_DRV)
- continue;
+ if (device_id)
+ device_id_re = regex_device_id(device_id);
+ else
+ device_id_re = NULL;
- if (device_id && strncasecmp(ppd->record.device_id, device_id,
- device_id_len))
- continue; /* TODO: implement smart compare */
+ if (make_and_model)
+ make_and_model_re = regex_string(make_and_model);
+ else
+ make_and_model_re = NULL;
- if (language)
+ for (ppd = (ppd_info_t *)cupsArrayFirst(PPDsByMakeModel);
+ ppd;
+ ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
{
- for (i = 0; i < PPD_MAX_LANG; i ++)
- if (!ppd->record.languages[i][0] ||
- !strcasecmp(ppd->record.languages[i], language))
- break;
+ /*
+ * Filter PPDs based on make, model, product, language, model number,
+ * and/or device ID using the "matches" score value. An exact match
+ * for product, make-and-model, or device-id adds 3 to the score.
+ * Partial matches for make-and-model yield 1 or 2 points, and matches
+ * for the make and language add a single point. Results are then sorted
+ * by score, highest score first.
+ */
- if (i >= PPD_MAX_LANG || !ppd->record.languages[i][0])
+ if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
+ ppd->record.type >= PPD_TYPE_DRV)
continue;
- }
- if (make && strcasecmp(ppd->record.make, make))
- continue;
+ ppd->matches = 0;
- if (make_and_model && strncasecmp(ppd->record.make_and_model,
- make_and_model, mam_len))
- continue;
+ if (device_id_re &&
+ !regexec(device_id_re, ppd->record.device_id,
+ (int)(sizeof(re_matches) / sizeof(re_matches[0])),
+ re_matches, 0))
+ {
+ /*
+ * Add the number of matching values from the device ID - it will be
+ * at least 2 (manufacturer and model), and as much as 3 (command set).
+ */
- if (model_number_str && ppd->record.model_number != model_number)
- continue;
+ for (i = 1; i < (int)(sizeof(re_matches) / sizeof(re_matches[0])); i ++)
+ if (re_matches[i].rm_so >= 0)
+ ppd->matches ++;
+ }
- if (product)
- {
- for (i = 0; i < PPD_MAX_PROD; i ++)
- if (!ppd->record.products[i][0] ||
- !strcasecmp(ppd->record.products[i], product))
- break;
+ if (language)
+ {
+ for (i = 0; i < PPD_MAX_LANG; i ++)
+ if (!ppd->record.languages[i][0] ||
+ !strcasecmp(ppd->record.languages[i], language))
+ {
+ ppd->matches ++;
+ break;
+ }
+ }
- if (i >= PPD_MAX_PROD || !ppd->record.products[i][0])
- continue;
- }
+ if (make && !strcasecmp(ppd->record.make, make))
+ ppd->matches ++;
- if (psversion)
- {
- for (i = 0; i < PPD_MAX_VERS; i ++)
- if (!ppd->record.psversions[i][0] ||
- !strcasecmp(ppd->record.psversions[i], psversion))
- break;
+ if (make_and_model_re &&
+ !regexec(make_and_model_re, ppd->record.make_and_model,
+ (int)(sizeof(re_matches) / sizeof(re_matches[0])),
+ re_matches, 0))
+ {
+ // See how much of the make-and-model string we matched...
+ if (re_matches[0].rm_so == 0)
+ {
+ if (re_matches[0].rm_eo == make_and_model_len)
+ ppd->matches += 3; // Exact match
+ else
+ ppd->matches += 2; // Prefix match
+ }
+ else
+ ppd->matches ++; // Infix match
+ }
- if (i >= PPD_MAX_VERS || !ppd->record.psversions[i][0])
- continue;
+ if (model_number_str && ppd->record.model_number == model_number)
+ ppd->matches ++;
+
+ if (product)
+ {
+ for (i = 0; i < PPD_MAX_PROD; i ++)
+ if (!ppd->record.products[i][0] ||
+ !strcasecmp(ppd->record.products[i], product))
+ {
+ ppd->matches += 3;
+ break;
+ }
+ }
+
+ if (psversion)
+ {
+ for (i = 0; i < PPD_MAX_VERS; i ++)
+ if (!ppd->record.psversions[i][0] ||
+ !strcasecmp(ppd->record.psversions[i], psversion))
+ {
+ ppd->matches ++;
+ break;
+ }
+ }
+
+ if (type_str && ppd->record.type == type)
+ ppd->matches ++;
+
+ if (ppd->matches)
+ {
+ fprintf(stderr, "DEBUG: [cups-driverd] %s matches with score %d!\n",
+ ppd->record.name, ppd->matches);
+ cupsArrayAdd(matches, ppd);
+ }
}
+ }
+ else
+ matches = PPDsByMakeModel;
- if (type_str && ppd->record.type != type)
+ for (ppd = (ppd_info_t *)cupsArrayFirst(matches);
+ count > 0 && ppd;
+ ppd = (ppd_info_t *)cupsArrayNext(matches))
+ {
+ /*
+ * Skip invalid PPDs...
+ */
+
+ if (ppd->record.type < PPD_TYPE_POSTSCRIPT ||
+ ppd->record.type >= PPD_TYPE_DRV)
continue;
/*
cupsdSendIPPHeader(IPP_OK, request_id);
cupsdSendIPPGroup(IPP_TAG_OPERATION);
cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
- cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
+ cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
+ "en-US");
}
fprintf(stderr, "DEBUG: [cups-driverd] Sending %s (%s)...\n",
for (this_make = ppd->record.make,
- ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel);
+ ppd = (ppd_info_t *)cupsArrayNext(matches);
ppd;
- ppd = (ppd_info_t *)cupsArrayNext(PPDsByMakeModel))
+ ppd = (ppd_info_t *)cupsArrayNext(matches))
if (strcasecmp(this_make, ppd->record.make))
break;
- cupsArrayPrev(PPDsByMakeModel);
+ cupsArrayPrev(matches);
}
}
else if (!strncmp(line, "*NickName:", 10))
sscanf(line, "%*[^\"]\"%255[^\"]", nick_name);
else if (!strncasecmp(line, "*1284DeviceID:", 14))
+ {
sscanf(line, "%*[^\"]\"%255[^\"]", device_id);
+
+ // Make sure device ID ends with a semicolon...
+ if (device_id[0] && device_id[strlen(device_id) - 1] != ';')
+ strlcat(device_id, ";", sizeof(device_id));
+ }
else if (!strncmp(line, "*Product:", 9))
{
if (sscanf(line, "%*[^\"]\"(%255[^)]", product) == 1)
}
+/*
+ * 'regex_device_id()' - Compile a regular expression based on the 1284 device
+ * ID.
+ */
+
+static regex_t * /* O - Regular expression */
+regex_device_id(const char *device_id) /* I - IEEE-1284 device ID */
+{
+ char res[2048], /* Regular expression string */
+ *ptr; /* Pointer into string */
+ regex_t *re; /* Regular expression */
+ int cmd; /* Command set string? */
+
+
+ fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id(\"%s\")\n", device_id);
+
+ /*
+ * Scan the device ID string and insert class, command set, manufacturer, and
+ * model attributes to match. We assume that the device ID in the PPD and the
+ * device ID reported by the device itself use the same attribute names and
+ * order of attributes.
+ */
+
+ ptr = res;
+
+ while (*device_id && ptr < (res + sizeof(res) - 6))
+ {
+ cmd = !strncasecmp(device_id, "COMMAND SET:", 12) ||
+ !strncasecmp(device_id, "CMD:", 4);
+
+ if (cmd || !strncasecmp(device_id, "MANUFACTURER:", 13) ||
+ !strncasecmp(device_id, "MFG:", 4) ||
+ !strncasecmp(device_id, "MFR:", 4) ||
+ !strncasecmp(device_id, "MODEL:", 6) ||
+ !strncasecmp(device_id, "MDL:", 4))
+ {
+ if (ptr > res)
+ {
+ *ptr++ = '.';
+ *ptr++ = '*';
+ }
+
+ *ptr++ = '(';
+
+ while (*device_id && *device_id != ';' && ptr < (res + sizeof(res) - 4))
+ {
+ if (strchr("[]{}().*\\|", *device_id))
+ *ptr++ = '\\';
+ *ptr++ = *device_id++;
+ }
+
+ if (*device_id == ';' || !*device_id)
+ *ptr++ = ';';
+ *ptr++ = ')';
+ if (cmd)
+ *ptr++ = '?';
+ }
+ else if ((device_id = strchr(device_id, ';')) == NULL)
+ break;
+ else
+ device_id ++;
+ }
+
+ *ptr = '\0';
+
+ fprintf(stderr, "DEBUG: [cups-driverd] regex_device_id: \"%s\"\n", res);
+
+ /*
+ * Compile the regular expression and return...
+ */
+
+ if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
+ {
+ if (!regcomp(re, res, REG_EXTENDED | REG_ICASE))
+ {
+ fputs("DEBUG: [cups-driverd] regex_device_id: OK\n", stderr);
+ return (re);
+ }
+
+ free(re);
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * 'regex_string()' - Construct a regular expression to compare a simple string.
+ */
+
+static regex_t * /* O - Regular expression */
+regex_string(const char *s) /* I - String to compare */
+{
+ char res[2048], /* Regular expression string */
+ *ptr; /* Pointer into string */
+ regex_t *re; /* Regular expression */
+
+
+ fprintf(stderr, "DEBUG: [cups-driverd] regex_string(\"%s\")\n", s);
+
+ /*
+ * Convert the string to a regular expression, escaping special characters
+ * as needed.
+ */
+
+ ptr = res;
+
+ while (*s && ptr < (res + sizeof(res) - 2))
+ {
+ if (strchr("[]{}().*\\", *s))
+ *ptr++ = '\\';
+
+ *ptr++ = *s++;
+ }
+
+ *ptr = '\0';
+
+ fprintf(stderr, "DEBUG: [cups-driverd] regex_string: \"%s\"\n", res);
+
+ /*
+ * Create a case-insensitive regular expression...
+ */
+
+ if (res[0] && (re = (regex_t *)calloc(1, sizeof(regex_t))) != NULL)
+ {
+ if (!regcomp(re, res, REG_ICASE))
+ {
+ fputs("DEBUG: [cups-driverd] regex_string: OK\n", stderr);
+ return (re);
+ }
+
+ free(re);
+ }
+
+ return (NULL);
+}
+
+
/*
* End of "$Id$".
*/