Tizen 2.0 Release
[platform/upstream/cups-filters.git] / backend / ieee1284.c
1 /*
2  * "$Id$"
3  *
4  *   IEEE-1284 support functions for CUPS.
5  *
6  *   Copyright 2007-2011 by Apple Inc.
7  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
8  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Apple Inc. and are protected by Federal copyright
11  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12  *   "LICENSE" which should have been included with this file.  If this
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  *   This file is subject to the Apple OS-Developed Software exception.
16  *
17  * Contents:
18  *
19  *   backendGetDeviceID()       - Get the IEEE-1284 device ID string and
20  *                                corresponding URI.
21  *   backendGetMakeModel()      - Get the make and model string from the device
22  *                                ID.
23  *   get_1284_values()          - Get 1284 device ID keys and values.
24  *   normalize_make_and_model() - Normalize a product/make-and-model string.
25  */
26
27 /*
28  * Include necessary headers.
29  */
30
31 #include "backend-private.h"
32 #include <cups/cups.h>
33 #include <string.h>
34 #include <ctype.h>
35 #define DEBUG_printf(x)
36 #define DEBUG_puts(x)
37 #define _cups_isspace(x) isspace((x) & 255)
38 #define _cups_strcasecmp strcasecmp
39 #define _cups_strncasecmp strncasecmp
40
41
42 /*
43  * Local functions...
44  */
45
46 static int      get_1284_values(const char *device_id, cups_option_t **values);
47 static char     *normalize_make_and_model(const char *make_and_model,
48                                           char *buffer, size_t bufsize);
49
50
51 /*
52  * 'backendGetDeviceID()' - Get the IEEE-1284 device ID string and
53  *                          corresponding URI.
54  */
55
56 int                                     /* O - 0 on success, -1 on failure */
57 backendGetDeviceID(
58     int        fd,                      /* I - File descriptor */
59     char       *device_id,              /* O - 1284 device ID */
60     int        device_id_size,          /* I - Size of buffer */
61     char       *make_model,             /* O - Make/model */
62     int        make_model_size,         /* I - Size of buffer */
63     const char *scheme,                 /* I - URI scheme */
64     char       *uri,                    /* O - Device URI */
65     int        uri_size)                /* I - Size of buffer */
66 {
67 #ifdef __APPLE__ /* This function is a no-op */
68   (void)fd;
69   (void)device_id;
70   (void)device_id_size;
71   (void)make_model;
72   (void)make_model_size;
73   (void)scheme;
74   (void)uri;
75   (void)uri_size;
76
77   return (-1);
78
79 #else /* Get the device ID from the specified file descriptor... */
80 #  ifdef __linux
81   int   length;                         /* Length of device ID info */
82   int   got_id = 0;
83 #  endif /* __linux */
84 #  if defined(__sun) && defined(ECPPIOC_GETDEVID)
85   struct ecpp_device_id did;            /* Device ID buffer */
86 #  endif /* __sun && ECPPIOC_GETDEVID */
87   char  *ptr;                           /* Pointer into device ID */
88
89
90   DEBUG_printf(("backendGetDeviceID(fd=%d, device_id=%p, device_id_size=%d, "
91                 "make_model=%p, make_model_size=%d, scheme=\"%s\", "
92                 "uri=%p, uri_size=%d)\n", fd, device_id, device_id_size,
93                 make_model, make_model_size, scheme ? scheme : "(null)",
94                 uri, uri_size));
95
96  /*
97   * Range check input...
98   */
99
100   if (!device_id || device_id_size < 32)
101   {
102     DEBUG_puts("backendGetDeviceID: Bad args!");
103     return (-1);
104   }
105
106   if (make_model)
107     *make_model = '\0';
108
109   if (fd >= 0)
110   {
111    /*
112     * Get the device ID string...
113     */
114
115     *device_id = '\0';
116
117 #  ifdef __linux
118     if (ioctl(fd, LPIOC_GET_DEVICE_ID(device_id_size), device_id))
119     {
120      /*
121       * Linux has to implement things differently for every device it seems.
122       * Since the standard parallel port driver does not provide a simple
123       * ioctl() to get the 1284 device ID, we have to open the "raw" parallel
124       * device corresponding to this port and do some negotiation trickery
125       * to get the current device ID.
126       */
127
128       if (uri && !strncmp(uri, "parallel:/dev/", 14))
129       {
130         char    devparport[16];         /* /dev/parportN */
131         int     devparportfd,           /* File descriptor for raw device */
132                   mode;                 /* Port mode */
133
134
135        /*
136         * Since the Linux parallel backend only supports 4 parallel port
137         * devices, just grab the trailing digit and use it to construct a
138         * /dev/parportN filename...
139         */
140
141         snprintf(devparport, sizeof(devparport), "/dev/parport%s",
142                  uri + strlen(uri) - 1);
143
144         if ((devparportfd = open(devparport, O_RDWR | O_NOCTTY)) != -1)
145         {
146          /*
147           * Claim the device...
148           */
149
150           if (!ioctl(devparportfd, PPCLAIM))
151           {
152             fcntl(devparportfd, F_SETFL, fcntl(devparportfd, F_GETFL) | O_NONBLOCK);
153
154             mode = IEEE1284_MODE_COMPAT;
155
156             if (!ioctl(devparportfd, PPNEGOT, &mode))
157             {
158              /*
159               * Put the device into Device ID mode...
160               */
161
162               mode = IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID;
163
164               if (!ioctl(devparportfd, PPNEGOT, &mode))
165               {
166                /*
167                 * Read the 1284 device ID...
168                 */
169
170                 if ((length = read(devparportfd, device_id,
171                                    device_id_size - 1)) >= 2)
172                 {
173                   device_id[length] = '\0';
174                   got_id = 1;
175                 }
176               }
177             }
178
179            /*
180             * Release the device...
181             */
182
183             ioctl(devparportfd, PPRELEASE);
184           }
185
186           close(devparportfd);
187         }
188       }
189     }
190     else
191       got_id = 1;
192
193     if (got_id)
194     {
195      /*
196       * Extract the length of the device ID string from the first two
197       * bytes.  The 1284 spec says the length is stored MSB first...
198       */
199
200       length = (((unsigned)device_id[0] & 255) << 8) +
201                ((unsigned)device_id[1] & 255);
202
203      /*
204       * Check to see if the length is larger than our buffer; first
205       * assume that the vendor incorrectly implemented the 1284 spec,
206       * and then limit the length to the size of our buffer...
207       */
208
209       if (length > device_id_size || length < 14)
210         length = (((unsigned)device_id[1] & 255) << 8) +
211                  ((unsigned)device_id[0] & 255);
212
213       if (length > device_id_size)
214         length = device_id_size;
215
216      /*
217       * The length field counts the number of bytes in the string
218       * including the length field itself (2 bytes).  The minimum
219       * length for a valid/usable device ID is 14 bytes:
220       *
221       *     <LENGTH> MFG: <MFG> ;MDL: <MDL> ;
222       *        2  +   4  +  1  +  5 +  1 +  1
223       */
224
225       if (length < 14)
226       {
227        /*
228         * Can't use this device ID, so don't try to copy it...
229         */
230
231         device_id[0] = '\0';
232         got_id       = 0;
233       }
234       else
235       {
236        /*
237         * Copy the device ID text to the beginning of the buffer and
238         * nul-terminate.
239         */
240
241         length -= 2;
242
243         memmove(device_id, device_id + 2, length);
244         device_id[length] = '\0';
245       }
246     }
247     else
248     {
249       DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n",
250                     strerror(errno)));
251       *device_id = '\0';
252     }
253 #  endif /* __linux */
254
255 #   if defined(__sun) && defined(ECPPIOC_GETDEVID)
256     did.mode = ECPP_CENTRONICS;
257     did.len  = device_id_size - 1;
258     did.rlen = 0;
259     did.addr = device_id;
260
261     if (!ioctl(fd, ECPPIOC_GETDEVID, &did))
262     {
263      /*
264       * Nul-terminate the device ID text.
265       */
266
267       if (did.rlen < (device_id_size - 1))
268         device_id[did.rlen] = '\0';
269       else
270         device_id[device_id_size - 1] = '\0';
271     }
272 #    ifdef DEBUG
273     else
274       DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n",
275                     strerror(errno)));
276 #    endif /* DEBUG */
277 #  endif /* __sun && ECPPIOC_GETDEVID */
278   }
279
280  /*
281   * Check whether device ID is valid. Turn line breaks and tabs to spaces and
282   * reject device IDs with non-printable characters.
283   */
284
285   for (ptr = device_id; *ptr; ptr ++)
286     if (_cups_isspace(*ptr))
287       *ptr = ' ';
288     else if ((*ptr & 255) < ' ' || *ptr == 127)
289     {
290       DEBUG_printf(("backendGetDeviceID: Bad device_id character %d.",
291                     *ptr & 255));
292       *device_id = '\0';
293       break;
294     }
295
296   DEBUG_printf(("backendGetDeviceID: device_id=\"%s\"\n", device_id));
297
298   if (scheme && uri)
299     *uri = '\0';
300
301   if (!*device_id)
302     return (-1);
303
304  /*
305   * Get the make and model...
306   */
307
308   if (make_model)
309     backendGetMakeModel(device_id, make_model, make_model_size);
310
311  /*
312   * Then generate a device URI...
313   */
314
315   if (scheme && uri && uri_size > 32)
316   {
317     int                 num_values;     /* Number of keys and values */
318     cups_option_t       *values;        /* Keys and values in device ID */
319     const char          *mfg,           /* Manufacturer */
320                         *mdl,           /* Model */
321                         *sern;          /* Serial number */
322     char                temp[256],      /* Temporary manufacturer string */
323                         *tempptr;       /* Pointer into temp string */
324
325
326    /*
327     * Get the make, model, and serial numbers...
328     */
329
330     num_values = get_1284_values(device_id, &values);
331
332     if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL)
333       if ((sern = cupsGetOption("SERN", num_values, values)) == NULL)
334         sern = cupsGetOption("SN", num_values, values);
335
336     if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
337       mfg = cupsGetOption("MFG", num_values, values);
338
339     if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
340       mdl = cupsGetOption("MDL", num_values, values);
341
342     if (mfg)
343     {
344       if (!_cups_strcasecmp(mfg, "Hewlett-Packard"))
345         mfg = "HP";
346       else if (!_cups_strcasecmp(mfg, "Lexmark International"))
347         mfg = "Lexmark";
348     }
349     else
350     {
351       strncpy(temp, make_model, sizeof(temp) - 1);
352       temp[sizeof(temp) - 1] = '\0';
353
354       if ((tempptr = strchr(temp, ' ')) != NULL)
355         *tempptr = '\0';
356
357       mfg = temp;
358     }
359
360     if (!mdl)
361       mdl = "";
362
363     if (!_cups_strncasecmp(mdl, mfg, strlen(mfg)))
364     {
365       mdl += strlen(mfg);
366
367       while (isspace(*mdl & 255))
368         mdl ++;
369     }
370
371    /*
372     * Generate the device URI from the manufacturer, make_model, and
373     * serial number strings.
374     */
375
376     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, scheme, NULL, mfg, 0,
377                      "/%s%s%s", mdl, sern ? "?serial=" : "", sern ? sern : "");
378
379     cupsFreeOptions(num_values, values);
380   }
381
382   return (0);
383 #endif /* __APPLE__ */
384 }
385
386
387 /*
388  * 'backendGetMakeModel()' - Get the make and model string from the device ID.
389  */
390
391 int                                     /* O - 0 on success, -1 on failure */
392 backendGetMakeModel(
393     const char *device_id,              /* O - 1284 device ID */
394     char       *make_model,             /* O - Make/model */
395     int        make_model_size)         /* I - Size of buffer */
396 {
397   int           num_values;             /* Number of keys and values */
398   cups_option_t *values;                /* Keys and values */
399   const char    *mfg,                   /* Manufacturer string */
400                 *mdl,                   /* Model string */
401                 *des;                   /* Description string */
402
403
404   DEBUG_printf(("backendGetMakeModel(device_id=\"%s\", "
405                 "make_model=%p, make_model_size=%d)\n", device_id,
406                 make_model, make_model_size));
407
408  /*
409   * Range check input...
410   */
411
412   if (!device_id || !*device_id || !make_model || make_model_size < 32)
413   {
414     DEBUG_puts("backendGetMakeModel: Bad args!");
415     return (-1);
416   }
417
418   *make_model = '\0';
419
420  /*
421   * Look for the description field...
422   */
423
424   num_values = get_1284_values(device_id, &values);
425
426   if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
427     mdl = cupsGetOption("MDL", num_values, values);
428
429   if (mdl)
430   {
431    /*
432     * Build a make-model string from the manufacturer and model attributes...
433     */
434
435     if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
436       mfg = cupsGetOption("MFG", num_values, values);
437
438     if (!mfg || !_cups_strncasecmp(mdl, mfg, strlen(mfg)))
439     {
440      /*
441       * Just copy the model string, since it has the manufacturer...
442       */
443
444       normalize_make_and_model(mdl, make_model, make_model_size);
445     }
446     else
447     {
448      /*
449       * Concatenate the make and model...
450       */
451
452       char      temp[1024];             /* Temporary make and model */
453
454       snprintf(temp, sizeof(temp), "%s %s", mfg, mdl);
455
456       normalize_make_and_model(temp, make_model, make_model_size);
457     }
458   }
459   else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL ||
460            (des = cupsGetOption("DES", num_values, values)) != NULL)
461   {
462    /*
463     * Make sure the description contains something useful, since some
464     * printer manufacturers (HP) apparently don't follow the standards
465     * they helped to define...
466     *
467     * Here we require the description to be 8 or more characters in length,
468     * containing at least one space and one letter.
469     */
470
471     if (strlen(des) >= 8)
472     {
473       const char        *ptr;           /* Pointer into description */
474       int               letters,        /* Number of letters seen */
475                         spaces;         /* Number of spaces seen */
476
477
478       for (ptr = des, letters = 0, spaces = 0; *ptr; ptr ++)
479       {
480         if (isspace(*ptr & 255))
481           spaces ++;
482         else if (isalpha(*ptr & 255))
483           letters ++;
484
485         if (spaces && letters)
486           break;
487       }
488
489       if (spaces && letters)
490         normalize_make_and_model(des, make_model, make_model_size);
491     }
492   }
493
494   if (!make_model[0])
495   {
496    /*
497     * Use "Unknown" as the printer make and model...
498     */
499
500     strncpy(make_model, "Unknown", make_model_size - 1);
501     make_model[make_model_size - 1] = '\0';
502   }
503
504   cupsFreeOptions(num_values, values);
505
506   return (0);
507 }
508
509
510 /*
511  * 'get_1284_values()' - Get 1284 device ID keys and values.
512  *
513  * The returned dictionary is a CUPS option array that can be queried with
514  * cupsGetOption and freed with cupsFreeOptions.
515  */
516
517 static int                              /* O - Number of key/value pairs */
518 get_1284_values(
519     const char *device_id,              /* I - IEEE-1284 device ID string */
520     cups_option_t **values)             /* O - Array of key/value pairs */
521 {
522   int           num_values;             /* Number of values */
523   char          key[256],               /* Key string */
524                 value[256],             /* Value string */
525                 *ptr;                   /* Pointer into key/value */
526
527
528  /*
529   * Range check input...
530   */
531
532   if (values)
533     *values = NULL;
534
535   if (!device_id || !values)
536     return (0);
537
538  /*
539   * Parse the 1284 device ID value into keys and values.  The format is
540   * repeating sequences of:
541   *
542   *   [whitespace]key:value[whitespace];
543   */
544
545   num_values = 0;
546   while (*device_id)
547   {
548     while (_cups_isspace(*device_id))
549       device_id ++;
550
551     if (!*device_id)
552       break;
553
554     for (ptr = key; *device_id && *device_id != ':'; device_id ++)
555       if (ptr < (key + sizeof(key) - 1))
556         *ptr++ = *device_id;
557
558     if (!*device_id)
559       break;
560
561     while (ptr > key && _cups_isspace(ptr[-1]))
562       ptr --;
563
564     *ptr = '\0';
565     device_id ++;
566
567     while (_cups_isspace(*device_id))
568       device_id ++;
569
570     if (!*device_id)
571       break;
572
573     for (ptr = value; *device_id && *device_id != ';'; device_id ++)
574       if (ptr < (value + sizeof(value) - 1))
575         *ptr++ = *device_id;
576
577     if (!*device_id)
578       break;
579
580     while (ptr > value && _cups_isspace(ptr[-1]))
581       ptr --;
582
583     *ptr = '\0';
584     device_id ++;
585
586     num_values = cupsAddOption(key, value, num_values, values);
587   }
588
589   return (num_values);
590 }
591
592
593 /*
594  * 'normalize_make_and_model()' - Normalize a product/make-and-model string.
595  *
596  * This function tries to undo the mistakes made by many printer manufacturers
597  * to produce a clean make-and-model string we can use.
598  */
599
600 static char *                           /* O - Normalized make-and-model string or NULL on error */
601 normalize_make_and_model(
602     const char *make_and_model,         /* I - Original make-and-model string */
603     char       *buffer,                 /* I - String buffer */
604     size_t     bufsize)                 /* I - Size of string buffer */
605 {
606   char  *bufptr;                        /* Pointer into buffer */
607
608
609   if (!make_and_model || !buffer || bufsize < 1)
610   {
611     if (buffer)
612       *buffer = '\0';
613
614     return (NULL);
615   }
616
617  /*
618   * Skip leading whitespace...
619   */
620
621   while (_cups_isspace(*make_and_model))
622     make_and_model ++;
623
624  /*
625   * Remove parenthesis and add manufacturers as needed...
626   */
627
628   if (make_and_model[0] == '(')
629   {
630     strncpy(buffer, make_and_model + 1, bufsize - 1);
631     buffer[bufsize - 1] = '\0';
632
633     if ((bufptr = strrchr(buffer, ')')) != NULL)
634       *bufptr = '\0';
635   }
636   else if (!_cups_strncasecmp(make_and_model, "XPrint", 6))
637   {
638    /*
639     * Xerox XPrint...
640     */
641
642     snprintf(buffer, bufsize, "Xerox %s", make_and_model);
643   }
644   else if (!_cups_strncasecmp(make_and_model, "Eastman", 7))
645   {
646    /*
647     * Kodak...
648     */
649
650     snprintf(buffer, bufsize, "Kodak %s", make_and_model + 7);
651   }
652   else if (!_cups_strncasecmp(make_and_model, "laserwriter", 11))
653   {
654    /*
655     * Apple LaserWriter...
656     */
657
658     snprintf(buffer, bufsize, "Apple LaserWriter%s", make_and_model + 11);
659   }
660   else if (!_cups_strncasecmp(make_and_model, "colorpoint", 10))
661   {
662    /*
663     * Seiko...
664     */
665
666     snprintf(buffer, bufsize, "Seiko %s", make_and_model);
667   }
668   else if (!_cups_strncasecmp(make_and_model, "fiery", 5))
669   {
670    /*
671     * EFI...
672     */
673
674     snprintf(buffer, bufsize, "EFI %s", make_and_model);
675   }
676   else if (!_cups_strncasecmp(make_and_model, "ps ", 3) ||
677            !_cups_strncasecmp(make_and_model, "colorpass", 9))
678   {
679    /*
680     * Canon...
681     */
682
683     snprintf(buffer, bufsize, "Canon %s", make_and_model);
684   }
685   else if (!_cups_strncasecmp(make_and_model, "primera", 7))
686   {
687    /*
688     * Fargo...
689     */
690
691     snprintf(buffer, bufsize, "Fargo %s", make_and_model);
692   }
693   else if (!_cups_strncasecmp(make_and_model, "designjet", 9) ||
694            !_cups_strncasecmp(make_and_model, "deskjet", 7))
695   {
696    /*
697     * HP...
698     */
699
700     snprintf(buffer, bufsize, "HP %s", make_and_model);
701   }
702   else
703   {
704     strncpy(buffer, make_and_model, bufsize - 1);
705     buffer[bufsize - 1] = '\0';
706   }
707
708  /*
709   * Clean up the make...
710   */
711
712   if (!_cups_strncasecmp(buffer, "agfa", 4))
713   {
714    /*
715     * Replace with AGFA (all uppercase)...
716     */
717
718     buffer[0] = 'A';
719     buffer[1] = 'G';
720     buffer[2] = 'F';
721     buffer[3] = 'A';
722   }
723   else if (!_cups_strncasecmp(buffer, "Hewlett-Packard hp ", 19))
724   {
725    /*
726     * Just put "HP" on the front...
727     */
728
729     buffer[0] = 'H';
730     buffer[1] = 'P';
731     memmove(buffer + 2, buffer + 18, strlen(buffer + 18) + 1);
732   }
733   else if (!_cups_strncasecmp(buffer, "Hewlett-Packard ", 16))
734   {
735    /*
736     * Just put "HP" on the front...
737     */
738
739     buffer[0] = 'H';
740     buffer[1] = 'P';
741     memmove(buffer + 2, buffer + 15, strlen(buffer + 15) + 1);
742   }
743   else if (!_cups_strncasecmp(buffer, "Lexmark International", 21))
744   {
745    /*
746     * Strip "International"...
747     */
748
749     memmove(buffer + 8, buffer + 21, strlen(buffer + 21) + 1);
750   }
751   else if (!_cups_strncasecmp(buffer, "herk", 4))
752   {
753    /*
754     * Replace with LHAG...
755     */
756
757     buffer[0] = 'L';
758     buffer[1] = 'H';
759     buffer[2] = 'A';
760     buffer[3] = 'G';
761   }
762   else if (!_cups_strncasecmp(buffer, "linotype", 8))
763   {
764    /*
765     * Replace with LHAG...
766     */
767
768     buffer[0] = 'L';
769     buffer[1] = 'H';
770     buffer[2] = 'A';
771     buffer[3] = 'G';
772     memmove(buffer + 4, buffer + 8, strlen(buffer + 8) + 1);
773   }
774
775  /*
776   * Remove trailing whitespace and return...
777   */
778
779   for (bufptr = buffer + strlen(buffer) - 1;
780        bufptr >= buffer && _cups_isspace(*bufptr);
781        bufptr --);
782
783   bufptr[1] = '\0';
784
785   return (buffer[0] ? buffer : NULL);
786 }
787
788
789 /*
790  * End of "$Id$".
791  */