1//
2// "$Id: ppdmerge.cxx 3277 2011-05-20 07:30:39Z msweet $"
3//
4//   PPD file merge utility for the CUPS PPD Compiler.
5//
6//   Copyright 2007-2011 by Apple Inc.
7//   Copyright 2002-2007 by Easy Software Products.
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//   which should have been included with this file.  If this file is
13//   file is missing or damaged, see the license at "http://www.cups.org/".
14//
15// Contents:
16//
17//   main()       - Main entry for the PPD merge utility.
18//   ppd_locale() - Return the locale associated with a PPD file.
19//   usage()      - Show usage and exit.
20//
21
22//
23// Include necessary headers...
24//
25
26#include <cups/cups-private.h>
27#include <cups/ppd-private.h>
28#include <cups/array.h>
29
30
31//
32// Local functions...
33//
34
35static const char	*ppd_locale(ppd_file_t *ppd);
36static void		usage(void);
37
38
39//
40// 'main()' - Main entry for the PPD merge utility.
41//
42
43int					// O - Exit status
44main(int  argc,				// I - Number of command-line arguments
45     char *argv[])			// I - Command-line arguments
46{
47  int		i;			// Looping var
48  char		*opt;			// Current option
49  ppd_file_t	*ppd;			// PPD file
50  cups_array_t	*ppds;			// Array of PPD files
51  const char	*inname,		// First input filename
52		*outname;		// Output filename (if any)
53  cups_file_t	*infile,		// Input file
54		*outfile;		// Output file
55  cups_array_t	*languages;		// Languages in file
56  const char	*locale;		// Current locale
57  char		line[1024];		// Line from file
58
59
60  _cupsSetLocale(argv);
61
62  // Scan the command-line...
63  inname    = NULL;
64  outname   = NULL;
65  outfile   = NULL;
66  languages = NULL;
67  ppds      = cupsArrayNew(NULL, NULL);
68
69  for (i = 1; i < argc; i ++)
70    if (argv[i][0] == '-')
71    {
72      for (opt = argv[i] + 1; *opt; opt ++)
73        switch (*opt)
74	{
75	  case 'o' :			// Output file
76              if (outname)
77	        usage();
78
79	      i ++;
80	      if (i >= argc)
81        	usage();
82
83	      outname = argv[i];
84	      break;
85
86	  default :			// Unknown
87	      usage();
88	      break;
89        }
90    }
91    else
92    {
93      // Open and load the PPD file...
94      if ((infile = cupsFileOpen(argv[i], "r")) == NULL)
95      {
96        _cupsLangPrintf(stderr, _("%s: Unable to open %s: %s"), "ppdmerge",
97	                argv[i], strerror(errno));
98	return (1);
99      }
100
101      // Open the PPD file...
102      if ((ppd = ppdOpen2(infile)) == NULL)
103      {
104        ppd_status_t	status;		// PPD open status
105	int		curline,	// Current line
106			linenum;	// Line number
107
108
109        status = ppdLastError(&linenum);
110
111	_cupsLangPrintf(stderr,
112	                _("%s: Unable to open PPD file: %s on line %d."),
113	                "ppdmerge", ppdErrorString(status), linenum);
114        cupsFileRewind(infile);
115
116        line[0] = '\0';
117	curline = 0;
118
119        while (cupsFileGets(infile, line, sizeof(line)))
120	{
121	  curline ++;
122	  if (curline >= linenum)
123	    break;
124	}
125
126	_cupsLangPrintf(stderr, "%d: %s", linenum, line);
127
128        cupsFileClose(infile);
129	return (1);
130      }
131
132      // Figure out the locale...
133      if ((locale = ppd_locale(ppd)) == NULL)
134      {
135        _cupsLangPrintf(stderr,
136	                _("ppdmerge: Bad LanguageVersion \"%s\" in %s."),
137			ppd->lang_version, argv[i]);
138        cupsFileClose(infile);
139	ppdClose(ppd);
140	return (1);
141      }
142
143      if (!strcmp(locale, "en") && !inname && !outfile)
144      {
145        // Set the English PPD's filename...
146	inname    = argv[i];
147	languages = _ppdGetLanguages(ppd);
148
149        if (outname && !strcmp(inname, outname))
150	{
151	  // Rename input filename so that we don't overwrite it...
152	  char bckname[1024];		// Backup filename
153
154
155	  snprintf(bckname, sizeof(bckname), "%s.bck", inname);
156
157	  if (rename(inname, bckname))
158	  {
159	    _cupsLangPrintf(stderr,
160	                    _("ppdmerge: Unable to backup %s to %s - %s"),
161			    inname, bckname, strerror(errno));
162	    return (1);
163	  }
164
165	  inname = bckname;
166	}
167      }
168      else if (strcmp(locale, "en"))
169      {
170	// Save this PPD for later processing...
171        cupsArrayAdd(ppds, ppd);
172      }
173      else
174      {
175        // Don't need this PPD...
176	_cupsLangPrintf(stderr, _("ppdmerge: Ignoring PPD file %s."),
177	                argv[i]);
178        ppdClose(ppd);
179      }
180
181      // Close and move on...
182      cupsFileClose(infile);
183    }
184
185  // If no PPDs have been loaded, display the program usage message.
186  if (!inname)
187    usage();
188
189  // Loop through the PPD files we loaded to generate a new language list...
190  if (!languages)
191    languages = cupsArrayNew((cups_array_func_t)strcmp, NULL);
192
193  for (ppd = (ppd_file_t *)cupsArrayFirst(ppds);
194       ppd;
195       ppd = (ppd_file_t *)cupsArrayNext(ppds))
196  {
197    locale = ppd_locale(ppd);
198
199    if (cupsArrayFind(languages, (void *)locale))
200    {
201      // Already have this language, remove the PPD from the list.
202      ppdClose(ppd);
203      cupsArrayRemove(ppds, ppd);
204    }
205    else
206      cupsArrayAdd(languages, (void *)locale);
207  }
208
209  // Copy the English PPD starting with a cupsLanguages line...
210  infile = cupsFileOpen(inname, "r");
211
212  if (outname)
213  {
214    const char *ext = strrchr(outname, '.');
215    if (ext && !strcmp(ext, ".gz"))
216      outfile = cupsFileOpen(outname, "w9");
217    else
218      outfile = cupsFileOpen(outname, "w");
219  }
220  else
221    outfile = cupsFileStdout();
222
223  cupsFileGets(infile, line, sizeof(line));
224  cupsFilePrintf(outfile, "%s\n", line);
225  if ((locale = (char *)cupsArrayFirst(languages)) != NULL)
226  {
227    cupsFilePrintf(outfile, "*cupsLanguages: \"%s", locale);
228    while ((locale = (char *)cupsArrayNext(languages)) != NULL)
229      cupsFilePrintf(outfile, " %s", locale);
230    cupsFilePuts(outfile, "\"\n");
231  }
232
233  while (cupsFileGets(infile, line, sizeof(line)))
234  {
235    if (strncmp(line, "*cupsLanguages:", 15))
236      cupsFilePrintf(outfile, "%s\n", line);
237  }
238
239  // Loop through the other PPD files we loaded to provide the translations...
240  for (ppd = (ppd_file_t *)cupsArrayFirst(ppds);
241       ppd;
242       ppd = (ppd_file_t *)cupsArrayNext(ppds))
243  {
244    // Output all of the UI text for this language...
245    int			j, k, l;	// Looping vars
246    ppd_group_t		*g;		// Option group
247    ppd_option_t	*o;		// Option
248    ppd_choice_t	*c;		// Choice
249    ppd_coption_t	*co;		// Custom option
250    ppd_cparam_t	*cp;		// Custom parameter
251    ppd_attr_t		*attr;		// PPD attribute
252
253    locale = ppd_locale(ppd);
254
255    cupsFilePrintf(outfile, "*%% %s localization\n", ppd->lang_version);
256    cupsFilePrintf(outfile, "*%s.Translation ModelName/%s: \"\"\n", locale,
257		   ppd->modelname);
258
259    for (j = ppd->num_groups, g = ppd->groups; j > 0; j --, g ++)
260    {
261      cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale,
262		     g->name, g->text);
263
264      for (k = g->num_options, o = g->options; k > 0; k --, o ++)
265      {
266	cupsFilePrintf(outfile, "*%s.Translation %s/%s: \"\"\n", locale,
267		       o->keyword, o->text);
268
269	for (l = o->num_choices, c = o->choices; l > 0; l --, c ++)
270	  cupsFilePrintf(outfile, "*%s.%s %s/%s: \"\"\n", locale,
271			 o->keyword, c->choice, c->text);
272
273	if ((co = ppdFindCustomOption(ppd, o->keyword)) != NULL)
274	{
275	  snprintf(line, sizeof(line), "Custom%s", o->keyword);
276	  attr = ppdFindAttr(ppd, line, "True");
277	  cupsFilePrintf(outfile, "*%s.Custom%s True/%s: \"\"\n", locale,
278			 o->keyword, attr->text);
279	  for (cp = ppdFirstCustomParam(co); cp; cp = ppdNextCustomParam(co))
280	    cupsFilePrintf(outfile, "*%s.ParamCustom%s %s/%s: \"\"\n", locale,
281			   o->keyword, cp->name, cp->text);
282	}
283      }
284    }
285
286    ppdClose(ppd);
287  }
288
289  cupsArrayDelete(ppds);
290
291  cupsFileClose(outfile);
292
293  // Return with no errors.
294  return (0);
295}
296
297
298//
299// 'ppd_locale()' - Return the locale associated with a PPD file.
300//
301
302static const char *			// O - Locale string
303ppd_locale(ppd_file_t *ppd)		// I - PPD file
304{
305  int		i,			// Looping var
306		vlen;			// Length of LanguageVersion string
307  static char	locale[255];		// Locale string
308  static struct				// LanguageVersion translation table
309  {
310    const char	*version,		// LanguageVersion string */
311		*language;		// Language code */
312  }		languages[] =
313  {
314    { "chinese",		"zh" },
315    { "czech",			"cs" },
316    { "danish",			"da" },
317    { "dutch",			"nl" },
318    { "english",		"en" },
319    { "finnish",		"fi" },
320    { "french",			"fr" },
321    { "german",			"de" },
322    { "greek",			"el" },
323    { "hungarian",		"hu" },
324    { "italian",		"it" },
325    { "japanese",		"ja" },
326    { "korean",			"ko" },
327    { "norwegian",		"no" },
328    { "polish",			"pl" },
329    { "portuguese",		"pt" },
330    { "russian",		"ru" },
331    { "simplified chinese",	"zh_CN" },
332    { "slovak",			"sk" },
333    { "spanish",		"es" },
334    { "swedish",		"sv" },
335    { "traditional chinese",	"zh_TW" },
336    { "turkish",		"tr" }
337  };
338
339
340  for (i = 0; i < (int)(sizeof(languages) / sizeof(languages[0])); i ++)
341  {
342    vlen = strlen(languages[i].version);
343
344    if (!_cups_strncasecmp(ppd->lang_version, languages[i].version, vlen))
345    {
346      if (ppd->lang_version[vlen] == '-' ||
347          ppd->lang_version[vlen] == '_')
348        snprintf(locale, sizeof(locale), "%s_%s", languages[i].language,
349	         ppd->lang_version + vlen + 1);
350      else
351        strlcpy(locale, languages[i].language, sizeof(locale));
352
353      return (locale);
354    }
355  }
356
357  return (NULL);
358}
359
360//
361// 'usage()' - Show usage and exit.
362//
363
364static void
365usage(void)
366{
367  _cupsLangPuts(stdout, _("Usage: ppdmerge [options] filename.ppd [ ... "
368                          "filenameN.ppd ]"));
369  _cupsLangPuts(stdout, _("Options:"));
370  _cupsLangPuts(stdout, _("  -o filename.ppd[.gz]    Set output file "
371                          "(otherwise stdout)."));
372
373  exit(1);
374}
375
376
377//
378// End of "$Id: ppdmerge.cxx 3277 2011-05-20 07:30:39Z msweet $".
379//
380