2 * Destination localization support for CUPS.
4 * Copyright 2012-2014 by Apple Inc.
6 * These coded instructions, statements, and computer programs are the
7 * property of Apple Inc. and are protected by Federal copyright
8 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
9 * which should have been included with this file. If this file is
10 * missing or damaged, see the license at "http://www.cups.org/".
12 * This file is subject to the Apple OS-Developed Software exception.
16 * Include necessary headers...
19 #include "cups-private.h"
26 static void cups_create_localizations(http_t
*http
, cups_dinfo_t
*dinfo
);
27 static int cups_read_strings(cups_file_t
*fp
, char *buffer
, size_t bufsize
,
28 char **id
, char **str
);
29 static char *cups_scan_strings(char *buffer
);
33 * 'cupsLocalizeDestMedia()' - Get the localized string for a destination media
36 * The returned string is stored in the destination information and will become
37 * invalid if the destination information is deleted.
39 * @since CUPS 2.0/macOS 10.10@
42 const char * /* O - Localized string */
43 cupsLocalizeDestMedia(
44 http_t
*http
, /* I - Connection to destination */
45 cups_dest_t
*dest
, /* I - Destination */
46 cups_dinfo_t
*dinfo
, /* I - Destination information */
47 unsigned flags
, /* I - Media flags */
48 cups_size_t
*size
) /* I - Media size */
50 cups_lang_t
*lang
; /* Standard localizations */
51 _cups_message_t key
, /* Search key */
52 *match
; /* Matching entry */
53 pwg_media_t
*pwg
; /* PWG media information */
54 cups_array_t
*db
; /* Media database */
55 _cups_media_db_t
*mdb
; /* Media database entry */
56 char name
[1024], /* Size name */
57 temp
[256]; /* Temporary string */
58 const char *lsize
, /* Localized media size */
59 *lsource
, /* Localized media source */
60 *ltype
; /* Localized media type */
63 DEBUG_printf(("cupsLocalizeDestMedia(http=%p, dest=%p, dinfo=%p, flags=%x, size=%p(\"%s\"))", (void *)http
, (void *)dest
, (void *)dinfo
, flags
, (void *)size
, size
? size
->media
: "(null)"));
66 * Range check input...
69 if (!http
|| !dest
|| !dinfo
|| !size
)
71 DEBUG_puts("1cupsLocalizeDestMedia: Returning NULL.");
72 _cupsSetError(IPP_STATUS_ERROR_INTERNAL
, strerror(EINVAL
), 0);
77 * See if the localization is cached...
80 if (!dinfo
->localizations
)
81 cups_create_localizations(http
, dinfo
);
84 if ((match
= (_cups_message_t
*)cupsArrayFind(dinfo
->localizations
, &key
)) != NULL
)
86 DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match
->str
));
91 * If not, get the localized size, source, and type strings...
94 lang
= cupsLangDefault();
96 snprintf(temp
, sizeof(temp
), "media.%s", size
->media
);
97 if ((lsize
= _cupsLangString(lang
, temp
)) != NULL
&& strcmp(lsize
, temp
))
99 DEBUG_printf(("1cupsLocalizeDestMedia: Returning standard localization \"%s\".", lsize
));
103 pwg
= pwgMediaForSize(size
->width
, size
->length
);
106 lsize
= _cupsLangString(lang
, pwg
->ppd
);
112 if ((size
->width
% 635) == 0 && (size
->length
% 635) == 0)
115 * Use inches since the size is a multiple of 1/4 inch.
118 snprintf(temp
, sizeof(temp
), _cupsLangString(lang
, _("%g x %g \"")), size
->width
/ 2540.0, size
->length
/ 2540.0);
123 * Use millimeters since the size is not a multiple of 1/4 inch.
126 snprintf(temp
, sizeof(temp
), _cupsLangString(lang
, _("%d x %d mm")), (size
->width
+ 50) / 100, (size
->length
+ 50) / 100);
132 if (flags
& CUPS_MEDIA_FLAGS_READY
)
133 db
= dinfo
->ready_db
;
135 db
= dinfo
->media_db
;
137 DEBUG_printf(("1cupsLocalizeDestMedia: size->media=\"%s\"", size
->media
));
139 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
); mdb
; mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
141 if (mdb
->key
&& !strcmp(mdb
->key
, size
->media
))
143 else if (mdb
->size_name
&& !strcmp(mdb
->size_name
, size
->media
))
149 for (mdb
= (_cups_media_db_t
*)cupsArrayFirst(db
); mdb
; mdb
= (_cups_media_db_t
*)cupsArrayNext(db
))
151 if (mdb
->width
== size
->width
&& mdb
->length
== size
->length
&& mdb
->bottom
== size
->bottom
&& mdb
->left
== size
->left
&& mdb
->right
== size
->right
&& mdb
->top
== size
->top
)
158 DEBUG_printf(("1cupsLocalizeDestMedia: MATCH mdb%p [key=\"%s\" size_name=\"%s\" source=\"%s\" type=\"%s\" width=%d length=%d B%d L%d R%d T%d]", (void *)mdb
, mdb
->key
, mdb
->size_name
, mdb
->source
, mdb
->type
, mdb
->width
, mdb
->length
, mdb
->bottom
, mdb
->left
, mdb
->right
, mdb
->top
));
160 lsource
= cupsLocalizeDestValue(http
, dest
, dinfo
, "media-source", mdb
->source
);
161 ltype
= cupsLocalizeDestValue(http
, dest
, dinfo
, "media-type", mdb
->type
);
169 if (!lsource
&& !ltype
)
171 if (size
->bottom
|| size
->left
|| size
->right
|| size
->top
)
172 snprintf(name
, sizeof(name
), _cupsLangString(lang
, _("%s (Borderless)")), lsize
);
174 strlcpy(name
, lsize
, sizeof(name
));
178 if (size
->bottom
|| size
->left
|| size
->right
|| size
->top
)
179 snprintf(name
, sizeof(name
), _cupsLangString(lang
, _("%s (Borderless, %s)")), lsize
, ltype
);
181 snprintf(name
, sizeof(name
), _cupsLangString(lang
, _("%s (%s)")), lsize
, ltype
);
185 if (size
->bottom
|| size
->left
|| size
->right
|| size
->top
)
186 snprintf(name
, sizeof(name
), _cupsLangString(lang
, _("%s (Borderless, %s)")), lsize
, lsource
);
188 snprintf(name
, sizeof(name
), _cupsLangString(lang
, _("%s (%s)")), lsize
, lsource
);
192 if (size
->bottom
|| size
->left
|| size
->right
|| size
->top
)
193 snprintf(name
, sizeof(name
), _cupsLangString(lang
, _("%s (Borderless, %s, %s)")), lsize
, ltype
, lsource
);
195 snprintf(name
, sizeof(name
), _cupsLangString(lang
, _("%s (%s, %s)")), lsize
, ltype
, lsource
);
198 if ((match
= (_cups_message_t
*)calloc(1, sizeof(_cups_message_t
))) == NULL
)
201 match
->id
= strdup(size
->media
);
202 match
->str
= strdup(name
);
204 cupsArrayAdd(dinfo
->localizations
, match
);
206 DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match
->str
));
213 * 'cupsLocalizeDestOption()' - Get the localized string for a destination
216 * The returned string is stored in the destination information and will become
217 * invalid if the destination information is deleted.
219 * @since CUPS 1.6/macOS 10.8@
222 const char * /* O - Localized string */
223 cupsLocalizeDestOption(
224 http_t
*http
, /* I - Connection to destination */
225 cups_dest_t
*dest
, /* I - Destination */
226 cups_dinfo_t
*dinfo
, /* I - Destination information */
227 const char *option
) /* I - Option to localize */
229 _cups_message_t key
, /* Search key */
230 *match
; /* Matching entry */
231 const char *localized
; /* Localized string */
234 DEBUG_printf(("cupsLocalizeDestOption(http=%p, dest=%p, dinfo=%p, option=\"%s\")", (void *)http
, (void *)dest
, (void *)dinfo
, option
));
236 if (!http
|| !dest
|| !dinfo
)
239 if (!dinfo
->localizations
)
240 cups_create_localizations(http
, dinfo
);
242 key
.id
= (char *)option
;
243 if ((match
= (_cups_message_t
*)cupsArrayFind(dinfo
->localizations
,
246 else if ((localized
= _cupsLangString(cupsLangDefault(), option
)) != NULL
)
254 * 'cupsLocalizeDestValue()' - Get the localized string for a destination
257 * The returned string is stored in the destination information and will become
258 * invalid if the destination information is deleted.
260 * @since CUPS 1.6/macOS 10.8@
263 const char * /* O - Localized string */
264 cupsLocalizeDestValue(
265 http_t
*http
, /* I - Connection to destination */
266 cups_dest_t
*dest
, /* I - Destination */
267 cups_dinfo_t
*dinfo
, /* I - Destination information */
268 const char *option
, /* I - Option to localize */
269 const char *value
) /* I - Value to localize */
271 _cups_message_t key
, /* Search key */
272 *match
; /* Matching entry */
273 char pair
[256]; /* option.value pair */
274 const char *localized
; /* Localized string */
277 DEBUG_printf(("cupsLocalizeDestValue(http=%p, dest=%p, dinfo=%p, option=\"%s\", value=\"%s\")", (void *)http
, (void *)dest
, (void *)dinfo
, option
, value
));
279 if (!http
|| !dest
|| !dinfo
)
282 if (!strcmp(option
, "media"))
284 pwg_media_t
*media
= pwgMediaForPWG(value
);
287 strlcpy(size
.media
, value
, sizeof(size
.media
));
288 size
.width
= media
? media
->width
: 0;
289 size
.length
= media
? media
->length
: 0;
295 return (cupsLocalizeDestMedia(http
, dest
, dinfo
, CUPS_MEDIA_FLAGS_DEFAULT
, &size
));
298 if (!dinfo
->localizations
)
299 cups_create_localizations(http
, dinfo
);
301 snprintf(pair
, sizeof(pair
), "%s.%s", option
, value
);
303 if ((match
= (_cups_message_t
*)cupsArrayFind(dinfo
->localizations
,
306 else if ((localized
= _cupsLangString(cupsLangDefault(), pair
)) != NULL
&& strcmp(localized
, pair
))
314 * 'cups_create_localizations()' - Create the localizations array for a
319 cups_create_localizations(
320 http_t
*http
, /* I - Connection to destination */
321 cups_dinfo_t
*dinfo
) /* I - Destination informations */
323 http_t
*http2
; /* Connection for strings file */
324 http_status_t status
; /* Request status */
325 ipp_attribute_t
*attr
; /* "printer-strings-uri" attribute */
326 char scheme
[32], /* URI scheme */
327 userpass
[256], /* Username/password info */
328 hostname
[256], /* Hostname */
329 resource
[1024], /* Resource */
331 /* Hostname of connection */
332 tempfile
[1024]; /* Temporary filename */
333 int port
; /* Port number */
334 http_encryption_t encryption
; /* Encryption to use */
335 cups_file_t
*temp
; /* Temporary file */
339 * Create an empty message catalog...
342 dinfo
->localizations
= _cupsMessageNew(NULL
);
345 * See if there are any localizations...
348 if ((attr
= ippFindAttribute(dinfo
->attrs
, "printer-strings-uri",
349 IPP_TAG_URI
)) == NULL
)
355 DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) "
361 * Pull apart the URI and determine whether we need to try a different
365 if (httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[0].string
.text
,
366 scheme
, sizeof(scheme
), userpass
, sizeof(userpass
),
367 hostname
, sizeof(hostname
), &port
, resource
,
368 sizeof(resource
)) < HTTP_URI_STATUS_OK
)
370 DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value "
371 "\"%s\".", attr
->values
[0].string
.text
));
375 httpGetHostname(http
, http_hostname
, sizeof(http_hostname
));
377 if (!_cups_strcasecmp(http_hostname
, hostname
) &&
378 port
== httpAddrPort(http
->hostaddr
))
381 * Use the same connection...
389 * Connect to the alternate host...
392 if (!strcmp(scheme
, "https"))
393 encryption
= HTTP_ENCRYPTION_ALWAYS
;
395 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
397 if ((http2
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
, 1,
398 30000, NULL
)) == NULL
)
400 DEBUG_printf(("4cups_create_localizations: Unable to connect to "
401 "%s:%d: %s", hostname
, port
, cupsLastErrorString()));
407 * Get a temporary file...
410 if ((temp
= cupsTempFile2(tempfile
, sizeof(tempfile
))) == NULL
)
412 DEBUG_printf(("4cups_create_localizations: Unable to create temporary "
413 "file: %s", cupsLastErrorString()));
419 status
= cupsGetFd(http2
, resource
, cupsFileNumber(temp
));
421 DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource
,
422 httpStatus(status
)));
424 if (status
== HTTP_STATUS_OK
)
427 * Got the file, read it...
430 char buffer
[8192], /* Message buffer */
432 *str
; /* Translated message */
433 _cups_message_t
*m
; /* Current message */
435 lseek(cupsFileNumber(temp
), 0, SEEK_SET
);
437 while (cups_read_strings(temp
, buffer
, sizeof(buffer
), &id
, &str
))
439 if ((m
= malloc(sizeof(_cups_message_t
))) == NULL
)
443 m
->str
= strdup(str
);
446 cupsArrayAdd(dinfo
->localizations
, m
);
461 DEBUG_printf(("4cups_create_localizations: %d messages loaded.",
462 cupsArrayCount(dinfo
->localizations
)));
477 * 'cups_read_strings()' - Read a pair of strings from a .strings file.
480 static int /* O - 1 on success, 0 on failure */
481 cups_read_strings(cups_file_t
*strings
, /* I - .strings file */
482 char *buffer
, /* I - Line buffer */
483 size_t bufsize
, /* I - Size of line buffer */
484 char **id
, /* O - Pointer to ID string */
485 char **str
) /* O - Pointer to translation string */
487 char *bufptr
; /* Pointer into buffer */
490 while (cupsFileGets(strings
, buffer
, bufsize
))
492 if (buffer
[0] != '\"')
496 bufptr
= cups_scan_strings(buffer
);
503 while (*bufptr
&& *bufptr
!= '\"')
510 bufptr
= cups_scan_strings(bufptr
);
525 * 'cups_scan_strings()' - Scan a quoted string.
528 static char * /* O - End of string */
529 cups_scan_strings(char *buffer
) /* I - Start of string */
531 char *bufptr
; /* Pointer into string */
534 for (bufptr
= buffer
+ 1; *bufptr
&& *bufptr
!= '\"'; bufptr
++)
538 if (bufptr
[1] >= '0' && bufptr
[1] <= '3' &&
539 bufptr
[2] >= '0' && bufptr
[2] <= '7' &&
540 bufptr
[3] >= '0' && bufptr
[3] <= '7')
543 * Decode \nnn octal escape...
546 *bufptr
= (char)(((((bufptr
[1] - '0') << 3) | (bufptr
[2] - '0')) << 3) | (bufptr
[3] - '0'));
547 _cups_strcpy(bufptr
+ 1, bufptr
+ 4);
552 * Decode \C escape...
555 _cups_strcpy(bufptr
, bufptr
+ 1);
558 else if (*bufptr
== 'r')
560 else if (*bufptr
== 't')