Index: systemv/lpinfo.c =================================================================== --- systemv/lpinfo.c (revision 8027) +++ systemv/lpinfo.c (working copy) @@ -42,8 +42,10 @@ const char *device_make_and_model, const char *device_uri, const char *device_location, void *user_data); -static int show_devices(http_t *, int); -static int show_models(http_t *, int); +static int show_devices(http_t *http, int long_status, int timeout); +static int show_models(http_t *http, int long_status, + const char *device_id, const char *language, + const char *make_model, const char *product); /* @@ -57,12 +59,22 @@ int i; /* Looping var */ http_t *http; /* Connection to server */ int long_status; /* Long listing? */ + const char *device_id, /* 1284 device ID */ + *language, /* Language */ + *make_model, /* Make and model */ + *product; /* Product */ + int timeout; /* Device timeout */ _cupsSetLocale(argv); http = NULL; long_status = 0; + device_id = NULL; + language = NULL; + make_model = NULL; + product = NULL; + timeout = CUPS_TIMEOUT_DEFAULT; for (i = 1; i < argc; i ++) if (argv[i][0] == '-') @@ -81,6 +93,30 @@ #endif /* HAVE_SSL */ break; + case 'h' : /* Connect to host */ + if (http) + { + httpClose(http); + http = NULL; + } + + if (argv[i][2] != '\0') + cupsSetServer(argv[i] + 2); + else + { + i ++; + + if (i >= argc) + { + _cupsLangPuts(stderr, + _("Error: need hostname after \'-h\' option!\n")); + return (1); + } + + cupsSetServer(argv[i]); + } + break; + case 'l' : /* Show long listing */ long_status = 1; break; @@ -100,7 +136,8 @@ } } - if (show_models(http, long_status)) + if (show_models(http, long_status, device_id, language, make_model, + product)) return (1); break; @@ -119,32 +156,102 @@ } } - if (show_devices(http, long_status)) + if (show_devices(http, long_status, timeout)) return (1); break; - case 'h' : /* Connect to host */ - if (http) + case '-' : /* --something */ + if (!strcmp(argv[i], "--device-id")) { - httpClose(http); - http = NULL; + i ++; + + if (i < argc) + device_id = argv[i]; + else + { + _cupsLangPuts(stderr, + _("lpinfo: Expected 1284 device ID string " + "after --device-id!\n")); + return (1); + } } - - if (argv[i][2] != '\0') - cupsSetServer(argv[i] + 2); - else + else if (!strncmp(argv[i], "--device-id=", 12) && argv[i][12]) { + device_id = argv[i] + 12; + } + else if (!strcmp(argv[i], "--language")) + { i ++; - - if (i >= argc) + if (i < argc) + language = argv[i]; + else { - _cupsLangPuts(stderr, - _("Error: need hostname after \'-h\' option!\n")); + _cupsLangPuts(stderr, + _("lpinfo: Expected language after " + "--language!\n")); return (1); - } - - cupsSetServer(argv[i]); + } } + else if (!strncmp(argv[i], "--language=", 11) && argv[i][11]) + { + language = argv[i] + 11; + } + else if (!strcmp(argv[i], "--make-and-model")) + { + i ++; + if (i < argc) + make_model= argv[i]; + else + { + _cupsLangPuts(stderr, + _("lpinfo: Expected make and model after " + "--make-and-model!\n")); + return (1); + } + } + else if (!strncmp(argv[i], "--make-and-model=", 17) && argv[i][17]) + { + make_model = argv[i] + 17; + } + else if (!strcmp(argv[i], "--product")) + { + i ++; + if (i < argc) + product = argv[i]; + else + { + _cupsLangPuts(stderr, + _("lpinfo: Expected product string after " + "--product!\n")); + return (1); + } + } + else if (!strncmp(argv[i], "--product=", 10) && argv[i][10]) + { + product = argv[i] + 10; + } + else if (!strcmp(argv[i], "--timeout")) + { + i ++; + if (i < argc) + timeout = atoi(argv[i]); + else + { + _cupsLangPuts(stderr, + _("lpinfo: Expected timeout after --timeout!\n")); + return (1); + } + } + else if (!strncmp(argv[i], "--timeout=", 10) && argv[i][10]) + { + timeout = atoi(argv[i] + 10); + } + else + { + _cupsLangPrintf(stderr, _("lpinfo: Unknown option \'%s\'!\n"), + argv[i]); + return (1); + } break; default : @@ -209,9 +316,10 @@ static int /* O - 0 on success, 1 on failure */ show_devices(http_t *http, /* I - HTTP connection to server */ - int long_status) /* I - Long status report? */ + int long_status, /* I - Long status report? */ + int timeout) /* I - Timeout */ { - if (cupsGetDevices(http, CUPS_TIMEOUT_DEFAULT, CUPS_EXCLUDE_NONE, device_cb, + if (cupsGetDevices(http, timeout, CUPS_EXCLUDE_NONE, device_cb, &long_status) != IPP_OK) { _cupsLangPrintf(stderr, "lpinfo: %s\n", cupsLastErrorString()); @@ -227,15 +335,19 @@ */ static int /* O - 0 on success, 1 on failure */ -show_models(http_t *http, /* I - HTTP connection to server */ - int long_status) /* I - Long status report? */ +show_models(http_t *http, /* I - HTTP connection to server */ + int long_status, /* I - Long status report? */ + const char *device_id, /* I - 1284 device ID */ + const char *language, /* I - Language */ + const char *make_model, /* I - Make and model */ + const char *product) /* I - Product */ { ipp_t *request, /* IPP Request */ *response; /* IPP Response */ ipp_attribute_t *attr; /* Current attribute */ const char *ppd_device_id, /* Pointer to ppd-device-id */ *ppd_language, /* Pointer to ppd-natural-language */ - *ppd_make, /* Pointer to ppd-make-and-model */ + *ppd_make_model, /* Pointer to ppd-make-and-model */ *ppd_name; /* Pointer to ppd-name */ @@ -243,15 +355,24 @@ return (1); /* - * Build a CUPS_GET_PPDS request, which requires the following - * attributes: - * - * attributes-charset - * attributes-natural-language + * Build a CUPS_GET_PPDS request... */ request = ippNewRequest(CUPS_GET_PPDS); + if (device_id) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-device-id", + NULL, device_id); + if (language) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "ppd-language", + NULL, language); + if (make_model) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make-and-model", + NULL, make_model); + if (product) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-product", + NULL, product); + /* * Do the request and get back a response... */ @@ -285,10 +406,10 @@ * Pull the needed attributes from this PPD... */ - ppd_device_id = "NONE"; - ppd_language = NULL; - ppd_make = NULL; - ppd_name = NULL; + ppd_device_id = "NONE"; + ppd_language = NULL; + ppd_make_model = NULL; + ppd_name = NULL; while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) { @@ -300,7 +421,7 @@ ppd_language = attr->values[0].string.text; else if (!strcmp(attr->name, "ppd-make-and-model") && attr->value_tag == IPP_TAG_TEXT) - ppd_make = attr->values[0].string.text; + ppd_make_model = attr->values[0].string.text; else if (!strcmp(attr->name, "ppd-name") && attr->value_tag == IPP_TAG_NAME) ppd_name = attr->values[0].string.text; @@ -312,7 +433,7 @@ * See if we have everything needed... */ - if (ppd_language == NULL || ppd_make == NULL || ppd_name == NULL) + if (ppd_language == NULL || ppd_make_model == NULL || ppd_name == NULL) { if (attr == NULL) break; @@ -331,10 +452,10 @@ " natural_language = %s\n" " make-and-model = %s\n" " device-id = %s\n"), - ppd_name, ppd_language, ppd_make, ppd_device_id); + ppd_name, ppd_language, ppd_make_model, ppd_device_id); } else - _cupsLangPrintf(stdout, "%s %s\n", ppd_name, ppd_make); + _cupsLangPrintf(stdout, "%s %s\n", ppd_name, ppd_make_model); if (attr == NULL) break; Index: man/lpinfo.man =================================================================== --- man/lpinfo.man (revision 8027) +++ man/lpinfo.man (working copy) @@ -3,7 +3,7 @@ .\" .\" lpinfo man page for the Common UNIX Printing System (CUPS). .\" -.\" Copyright 2007 by Apple Inc. +.\" Copyright 2007-2008 by Apple Inc. .\" Copyright 1997-2006 by Easy Software Products. .\" .\" These coded instructions, statements, and computer programs are the @@ -12,7 +12,7 @@ .\" which should have been included with this file. If this file is .\" file is missing or damaged, see the license at "http://www.cups.org/". .\" -.TH lpinfo 8 "Common UNIX Printing System" "12 February 2006" "Apple Inc." +.TH lpinfo 8 "Common UNIX Printing System" "8 October 2008" "Apple Inc." .SH NAME lpinfo \- show available devices or drivers .SH SYNOPSIS @@ -21,14 +21,24 @@ .I username ] [ -h .I server[:port] -] [ -l ] -m +] [ -l ] [ --device-id +.I device-id-string +] [ --language +.I locale +] [ --make-and-model +.I name +] [ --product +.I name +] -m .br .B lpinfo [ -E ] [ -U .I username ] [ -h .I server[:port] -] [ -l ] -v +] [ -l ] [ --timeout +.I seconds +] -v .SH DESCRIPTION \fIlpinfo\fR lists the available devices or drivers known to the CUPS server. The first form (\fI-m\fR) lists the available @@ -52,6 +62,28 @@ -l .br Shows a "long" listing of devices or drivers. +.TP 5 +--device-id device-id-string +.br +Specifies the IEEE-1284 device ID to match when listing drivers with the +\fI-m\fR option. +.TP 5 +--language locale +.br +Specifies the language to match when listing drivers with the \fI-m\fR option. +.TP 5 +--make-and-model name +.br +Specifies the make and model to match when listing drivers with the \fI-m\fR +option. +.TP 5 +--product name +.br +Specifies the product to match when listing drivers with the \fI-m\fR option. +.TP 5 +--timeout seconds +.br +Specifies the timeout when listing devices with the \fI-v\fR option. .SH COMPATIBILITY The \fIlpinfo\fR command is unique to CUPS. .SH SEE ALSO @@ -59,7 +91,7 @@ .br http://localhost:631/help .SH COPYRIGHT -Copyright 2007 by Apple Inc. +Copyright 2007-2008 by Apple Inc. .\" .\" End of "$Id$". .\" Index: cgi-bin/admin.c =================================================================== --- cgi-bin/admin.c (revision 8027) +++ cgi-bin/admin.c (working copy) @@ -32,7 +32,6 @@ * do_set_sharing() - Set printer-is-shared value. * get_option_value() - Return the value of an option. * get_points() - Get a value in points. - * match_string() - Return the number of matching characters. */ /* @@ -84,7 +83,6 @@ static char *get_option_value(ppd_file_t *ppd, const char *name, char *buffer, size_t bufsize); static double get_points(double number, const char *uval); -static int match_string(const char *a, const char *b); /* @@ -1116,8 +1114,14 @@ if ((var = cgiGetVariable("CURRENT_MAKE")) == NULL) var = cgiGetVariable("PPD_MAKE"); if (var) + { ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make", NULL, var); + + if ((var = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL) + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, + "ppd-make-and-model", NULL, var); + } else ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "ppd-make"); @@ -1167,47 +1171,9 @@ * Let the user choose a model... */ - const char *make_model; /* Current make/model string */ - - - if ((make_model = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL) - { - /* - * Scan for "close" matches... - */ - - int match, /* Current match */ - best_match, /* Best match so far */ - count; /* Number of drivers */ - const char *best, /* Best matching string */ - *current; /* Current string */ - - - count = cgiGetSize("PPD_MAKE_AND_MODEL"); - - for (i = 0, best_match = 0, best = NULL; i < count; i ++) - { - current = cgiGetArray("PPD_MAKE_AND_MODEL", i); - match = match_string(make_model, current); - - if (match > best_match) - { - best_match = match; - best = current; - } - } - - if (best_match > strlen(var)) - { - /* - * Found a match longer than the make... - */ - - cgiSetVariable("CURRENT_MAKE_AND_MODEL", best); - } - } - cgiStartHTML(title); + cgiSetVariable("CURRENT_MAKE_AND_MODEL", + cgiGetArray("PPD_MAKE_AND_MODEL", 0)); cgiCopyTemplateLang("choose-model.tmpl"); cgiEndHTML(); } @@ -4197,54 +4163,5 @@ /* - * 'match_string()' - Return the number of matching characters. - */ - -static int /* O - Number of matching characters */ -match_string(const char *a, /* I - First string */ - const char *b) /* I - Second string */ -{ - int count; /* Number of matching characters */ - - - /* - * Loop through both strings until we hit the end of either or we find - * a non-matching character. For the purposes of comparison, we ignore - * whitespace and do a case-insensitive comparison so that we have a - * better chance of finding a match... - */ - - for (count = 0; *a && *b; a++, b++, count ++) - { - /* - * Skip leading whitespace characters... - */ - - while (isspace(*a & 255)) - a ++; - - while (isspace(*b & 255)) - b ++; - - /* - * Break out if we run out of characters... - */ - - if (!*a || !*b) - break; - - /* - * Do a case-insensitive comparison of the next two chars... - */ - - if (tolower(*a & 255) != tolower(*b & 255)) - break; - } - - return (count); -} - - -/* * End of "$Id$". */ Index: scheduler/cups-driverd.cxx =================================================================== --- scheduler/cups-driverd.cxx (revision 8027) +++ scheduler/cups-driverd.cxx (working copy) @@ -18,18 +18,23 @@ * * 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. */ /* @@ -41,6 +46,7 @@ #include #include #include +#include /* @@ -95,6 +101,7 @@ 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; @@ -121,6 +128,8 @@ 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, @@ -131,6 +140,8 @@ 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); /* @@ -325,7 +336,7 @@ 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(); @@ -333,7 +344,7 @@ 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); } @@ -636,6 +647,22 @@ /* + * '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. */ @@ -729,8 +756,8 @@ *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? */ @@ -741,6 +768,10 @@ 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, @@ -926,14 +957,14 @@ 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); @@ -1021,66 +1052,135 @@ 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; /* @@ -1094,7 +1194,8 @@ 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", @@ -1164,13 +1265,13 @@ 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); } } @@ -1382,7 +1483,13 @@ 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) @@ -1981,5 +2088,143 @@ /* + * '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$". */