| // |
| // PPD file merge utility for the CUPS PPD Compiler. |
| // |
| // Copyright © 2007-2018 by Apple Inc. |
| // Copyright © 2002-2007 by Easy Software Products. |
| // |
| // Licensed under Apache License v2.0. See the file "LICENSE" for more |
| // information. |
| // |
| |
| // |
| // Include necessary headers... |
| // |
| |
| #include <cups/cups-private.h> |
| #include <cups/ppd-private.h> |
| #include <cups/array.h> |
| |
| |
| // |
| // Local functions... |
| // |
| |
| static const char *ppd_locale(ppd_file_t *ppd); |
| static void usage(void); |
| |
| |
| // |
| // 'main()' - Main entry for the PPD merge utility. |
| // |
| |
| int // O - Exit status |
| main(int argc, // I - Number of command-line arguments |
| char *argv[]) // I - Command-line arguments |
| { |
| int i; // Looping var |
| char *opt; // Current option |
| ppd_file_t *ppd; // PPD file |
| cups_array_t *ppds; // Array of PPD files |
| const char *inname, // First input filename |
| *outname; // Output filename (if any) |
| char bckname[1024]; // Backup filename |
| cups_file_t *infile, // Input file |
| *outfile; // Output file |
| cups_array_t *languages; // Languages in file |
| const char *locale; // Current locale |
| char line[1024]; // Line from file |
| |
| |
| _cupsSetLocale(argv); |
| |
| // Scan the command-line... |
| inname = NULL; |
| outname = NULL; |
| outfile = NULL; |
| languages = NULL; |
| ppds = cupsArrayNew(NULL, NULL); |
| |
| for (i = 1; i < argc; i ++) |
| if (argv[i][0] == '-') |
| { |
| for (opt = argv[i] + 1; *opt; opt ++) |
| switch (*opt) |
| { |
| case 'o' : // Output file |
| if (outname) |
| usage(); |
| |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| outname = argv[i]; |
| break; |
| |
| default : // Unknown |
| usage(); |
| break; |
| } |
| } |
| else |
| { |
| // Open and load the PPD file... |
| if ((infile = cupsFileOpen(argv[i], "r")) == NULL) |
| { |
| _cupsLangPrintf(stderr, _("%s: Unable to open %s: %s"), "ppdmerge", |
| argv[i], strerror(errno)); |
| return (1); |
| } |
| |
| // Open the PPD file... |
| if ((ppd = ppdOpen2(infile)) == NULL) |
| { |
| ppd_status_t status; // PPD open status |
| int curline, // Current line |
| linenum; // Line number |
| |
| |
| status = ppdLastError(&linenum); |
| |
| _cupsLangPrintf(stderr, |
| _("%s: Unable to open PPD file: %s on line %d."), |
| "ppdmerge", ppdErrorString(status), linenum); |
| cupsFileRewind(infile); |
| |
| line[0] = '\0'; |
| curline = 0; |
| |
| while (cupsFileGets(infile, line, sizeof(line))) |
| { |
| curline ++; |
| if (curline >= linenum) |
| break; |
| } |
| |
| _cupsLangPrintf(stderr, "%d: %s", linenum, line); |
| |
| cupsFileClose(infile); |
| return (1); |
| } |
| |
| // Figure out the locale... |
| if ((locale = ppd_locale(ppd)) == NULL) |
| { |
| _cupsLangPrintf(stderr, |
| _("ppdmerge: Bad LanguageVersion \"%s\" in %s."), |
| ppd->lang_version, argv[i]); |
| cupsFileClose(infile); |
| ppdClose(ppd); |
| return (1); |
| } |
| |
| if (!strcmp(locale, "en") && !inname && !outfile) |
| { |
| // Set the English PPD's filename... |
| inname = argv[i]; |
| languages = _ppdGetLanguages(ppd); |
| |
| if (outname && !strcmp(inname, outname)) |
| { |
| // Rename input filename so that we don't overwrite it... |
| snprintf(bckname, sizeof(bckname), "%s.bck", inname); |
| |
| if (rename(inname, bckname)) |
| { |
| _cupsLangPrintf(stderr, |
| _("ppdmerge: Unable to backup %s to %s - %s"), |
| inname, bckname, strerror(errno)); |
| return (1); |
| } |
| |
| inname = bckname; |
| } |
| } |
| else if (strcmp(locale, "en")) |
| { |
| // Save this PPD for later processing... |
| cupsArrayAdd(ppds, ppd); |
| } |
| else |
| { |
| // Don't need this PPD... |
| _cupsLangPrintf(stderr, _("ppdmerge: Ignoring PPD file %s."), |
| argv[i]); |
| ppdClose(ppd); |
| } |
| |
| // Close and move on... |
| cupsFileClose(infile); |
| } |
| |
| // If no PPDs have been loaded, display the program usage message. |
| if (!inname) |
| usage(); |
| |
| // Loop through the PPD files we loaded to generate a new language list... |
| if (!languages) |
| languages = cupsArrayNew((cups_array_func_t)strcmp, NULL); |
| |
| for (ppd = (ppd_file_t *)cupsArrayFirst(ppds); |
| ppd; |
| ppd = (ppd_file_t *)cupsArrayNext(ppds)) |
| { |
| locale = ppd_locale(ppd); |
| |
| if (cupsArrayFind(languages, (void *)locale)) |
| { |
| // Already have this language, remove the PPD from the list. |
| ppdClose(ppd); |
| cupsArrayRemove(ppds, ppd); |
| } |
| else |
| cupsArrayAdd(languages, (void *)locale); |
| } |
| |
| // Copy the English PPD starting with a cupsLanguages line... |
| infile = cupsFileOpen(inname, "r"); |
| |
| if (outname) |
| { |
| const char *ext = strrchr(outname, '.'); |
| if (ext && !strcmp(ext, ".gz")) |
| outfile = cupsFileOpen(outname, "w9"); |
| else |
| outfile = cupsFileOpen(outname, "w"); |
| } |
| else |
| outfile = cupsFileStdout(); |
| |
| cupsFileGets(infile, line, sizeof(line)); |
| cupsFilePrintf(outfile, "%s\n", line); |
| if ((locale = (char *)cupsArrayFirst(languages)) != NULL) |
| { |
| cupsFilePrintf(outfile, "*cupsLanguages: \"%s", locale); |
| while ((locale = (char *)cupsArrayNext(languages)) != NULL) |
| cupsFilePrintf(outfile, " %s", locale); |
| cupsFilePuts(outfile, "\"\n"); |
| } |
| |
| while (cupsFileGets(infile, line, sizeof(line))) |
| { |
| if (strncmp(line, "*cupsLanguages:", 15)) |
| cupsFilePrintf(outfile, "%s\n", line); |
| } |
| |
| // Loop through the other PPD files we loaded to provide the translations... |
| for (ppd = (ppd_file_t *)cupsArrayFirst(ppds); |
| ppd; |
| ppd = (ppd_file_t *)cupsArrayNext(ppds)) |
| { |
| // Output all of the UI text for this language... |
| int j, k, l; // Looping vars |
| ppd_group_t *g; // Option group |
| ppd_option_t *o; // Option |
| ppd_choice_t *c; // Choice |
| ppd_coption_t *co; // Custom option |
| ppd_cparam_t *cp; // Custom parameter |
| ppd_attr_t *attr; // PPD attribute |
| |
| locale = ppd_locale(ppd); |
| |
| cupsFilePrintf(outfile, "*%% %s localization\n", ppd->lang_version); |
| cupsFilePrintf(outfile, "*%s.Translation ModelName/%s: \"\"\n", locale, |
| ppd->modelname); |
| |
| for (j = ppd->num_groups, g = ppd->groups; j > 0; j --, g ++) |
| { |
| cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale, |
| g->name, g->text); |
| |
| for (k = g->num_options, o = g->options; k > 0; k --, o ++) |
| { |
| cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale, |
| o->keyword, o->text); |
| |
| for (l = o->num_choices, c = o->choices; l > 0; l --, c ++) |
| cupsFilePrintf(outfile, "*%s.%s %s/%s: \"\"\n", locale, |
| o->keyword, c->choice, c->text); |
| |
| if ((co = ppdFindCustomOption(ppd, o->keyword)) != NULL) |
| { |
| snprintf(line, sizeof(line), "Custom%s", o->keyword); |
| attr = ppdFindAttr(ppd, line, "True"); |
| cupsFilePrintf(outfile, "*%s.Custom%s True/%s: \"\"\n", locale, |
| o->keyword, attr->text); |
| for (cp = ppdFirstCustomParam(co); cp; cp = ppdNextCustomParam(co)) |
| cupsFilePrintf(outfile, "*%s.ParamCustom%s %s/%s: \"\"\n", locale, |
| o->keyword, cp->name, cp->text); |
| } |
| } |
| } |
| |
| ppdClose(ppd); |
| } |
| |
| cupsArrayDelete(ppds); |
| |
| cupsFileClose(outfile); |
| |
| // Return with no errors. |
| return (0); |
| } |
| |
| |
| // |
| // 'ppd_locale()' - Return the locale associated with a PPD file. |
| // |
| |
| static const char * // O - Locale string |
| ppd_locale(ppd_file_t *ppd) // I - PPD file |
| { |
| int i; // Looping var |
| size_t vlen; // Length of LanguageVersion string |
| static char locale[255]; // Locale string |
| static struct // LanguageVersion translation table |
| { |
| const char *version, // LanguageVersion string */ |
| *language; // Language code */ |
| } languages[] = |
| { |
| { "chinese", "zh" }, |
| { "czech", "cs" }, |
| { "danish", "da" }, |
| { "dutch", "nl" }, |
| { "english", "en" }, |
| { "finnish", "fi" }, |
| { "french", "fr" }, |
| { "german", "de" }, |
| { "greek", "el" }, |
| { "hungarian", "hu" }, |
| { "italian", "it" }, |
| { "japanese", "ja" }, |
| { "korean", "ko" }, |
| { "norwegian", "no" }, |
| { "polish", "pl" }, |
| { "portuguese", "pt" }, |
| { "russian", "ru" }, |
| { "simplified chinese", "zh_CN" }, |
| { "slovak", "sk" }, |
| { "spanish", "es" }, |
| { "swedish", "sv" }, |
| { "traditional chinese", "zh_TW" }, |
| { "turkish", "tr" } |
| }; |
| |
| |
| for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++) |
| { |
| vlen = strlen(languages[i].version); |
| |
| if (!_cups_strncasecmp(ppd->lang_version, languages[i].version, vlen)) |
| { |
| if (ppd->lang_version[vlen] == '-' || |
| ppd->lang_version[vlen] == '_') |
| snprintf(locale, sizeof(locale), "%s_%s", languages[i].language, |
| ppd->lang_version + vlen + 1); |
| else |
| strlcpy(locale, languages[i].language, sizeof(locale)); |
| |
| return (locale); |
| } |
| } |
| |
| return (NULL); |
| } |
| |
| // |
| // 'usage()' - Show usage and exit. |
| // |
| |
| static void |
| usage(void) |
| { |
| _cupsLangPuts(stdout, _("Usage: ppdmerge [options] filename.ppd [ ... " |
| "filenameN.ppd ]")); |
| _cupsLangPuts(stdout, _("Options:")); |
| _cupsLangPuts(stdout, _(" -o filename.ppd[.gz] Set output file " |
| "(otherwise stdout).")); |
| |
| exit(1); |
| } |