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