blob: 94d67ff839a34e38b102f175f34e0dc533193505 [file] [log] [blame]
//
// 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);
}