1// 2// "$Id: ppdc.cxx 11801 2014-04-08 19:55:21Z msweet $" 3// 4// PPD file compiler main entry for the CUPS PPD Compiler. 5// 6// Copyright 2007-2014 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 16// 17// Include necessary headers... 18// 19 20#include "ppdc-private.h" 21#include <unistd.h> 22#include <sys/stat.h> 23#include <sys/types.h> 24 25 26// 27// Local functions... 28// 29 30static void usage(void); 31 32 33// 34// 'main()' - Main entry for the PPD compiler. 35// 36 37int // O - Exit status 38main(int argc, // I - Number of command-line arguments 39 char *argv[]) // I - Command-line arguments 40{ 41 int i, j; // Looping vars 42 ppdcCatalog *catalog; // Message catalog 43 const char *outdir; // Output directory 44 ppdcSource *src; // PPD source file data 45 ppdcDriver *d; // Current driver 46 cups_file_t *fp; // PPD file 47 char *opt, // Current option 48 *value, // Value in option 49 *outname, // Output filename 50 make_model[1024], 51 // Make and model 52 pcfilename[1024], 53 // Lowercase pcfilename 54 filename[1024]; // PPD filename 55 int comp, // Compress 56 do_test, // Test PPD files 57 single_language,// Generate single-language files 58 use_model_name, // Use ModelName for filename 59 verbose; // Verbosity 60 ppdcLineEnding le; // Line ending to use 61 ppdcArray *locales; // List of locales 62 cups_array_t *filenames; // List of generated filenames 63 64 65 _cupsSetLocale(argv); 66 67 // Scan the command-line... 68 catalog = NULL; 69 comp = 0; 70 do_test = 0; 71 le = PPDC_LFONLY; 72 locales = NULL; 73 outdir = "ppd"; 74 single_language = 0; 75 src = new ppdcSource(); 76 use_model_name = 0; 77 verbose = 0; 78 filenames = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL); 79 80 for (i = 1; i < argc; i ++) 81 if (argv[i][0] == '-') 82 { 83 for (opt = argv[i] + 1; *opt; opt ++) 84 switch (*opt) 85 { 86 case 'D' : // Define variable 87 i ++; 88 if (i >= argc) 89 usage(); 90 91 if ((value = strchr(argv[i], '=')) != NULL) 92 { 93 *value++ = '\0'; 94 95 src->set_variable(argv[i], value); 96 } 97 else 98 src->set_variable(argv[i], "1"); 99 break; 100 101 case 'I' : // Include directory... 102 i ++; 103 if (i >= argc) 104 usage(); 105 106 if (verbose > 1) 107 _cupsLangPrintf(stdout, 108 _("ppdc: Adding include directory \"%s\"."), 109 argv[i]); 110 111 ppdcSource::add_include(argv[i]); 112 break; 113 114 case 'c' : // Message catalog... 115 i ++; 116 if (i >= argc) 117 usage(); 118 119 if (verbose > 1) 120 _cupsLangPrintf(stdout, 121 _("ppdc: Loading messages from \"%s\"."), 122 argv[i]); 123 124 if (!catalog) 125 catalog = new ppdcCatalog("en"); 126 127 if (catalog->load_messages(argv[i])) 128 { 129 _cupsLangPrintf(stderr, 130 _("ppdc: Unable to load localization file " 131 "\"%s\" - %s"), argv[i], strerror(errno)); 132 return (1); 133 } 134 break; 135 136 case 'd' : // Output directory... 137 i ++; 138 if (i >= argc) 139 usage(); 140 141 if (verbose > 1) 142 _cupsLangPrintf(stdout, 143 _("ppdc: Writing PPD files to directory " 144 "\"%s\"."), argv[i]); 145 146 outdir = argv[i]; 147 break; 148 149 case 'l' : // Language(s)... 150 i ++; 151 if (i >= argc) 152 usage(); 153 154 if (strchr(argv[i], ',')) 155 { 156 // Comma-delimited list of languages... 157 char temp[1024], // Copy of language list 158 *start, // Start of current locale name 159 *end; // End of current locale name 160 161 162 locales = new ppdcArray(); 163 164 strlcpy(temp, argv[i], sizeof(temp)); 165 for (start = temp; *start; start = end) 166 { 167 if ((end = strchr(start, ',')) != NULL) 168 *end++ = '\0'; 169 else 170 end = start + strlen(start); 171 172 if (end > start) 173 locales->add(new ppdcString(start)); 174 } 175 } 176 else 177 { 178 single_language = 1; 179 180 if (verbose > 1) 181 _cupsLangPrintf(stdout, 182 _("ppdc: Loading messages for locale " 183 "\"%s\"."), argv[i]); 184 185 if (catalog) 186 catalog->release(); 187 188 catalog = new ppdcCatalog(argv[i]); 189 190 if (catalog->messages->count == 0 && strcmp(argv[i], "en")) 191 { 192 _cupsLangPrintf(stderr, 193 _("ppdc: Unable to find localization for " 194 "\"%s\" - %s"), argv[i], strerror(errno)); 195 return (1); 196 } 197 } 198 break; 199 200 case 'm' : // Use ModelName for filename 201 use_model_name = 1; 202 break; 203 204 case 't' : // Test PPDs instead of generating them 205 do_test = 1; 206 break; 207 208 case 'v' : // Be verbose... 209 verbose ++; 210 break; 211 212 case 'z' : // Compress files... 213 comp = 1; 214 break; 215 216 case '-' : // --option 217 if (!strcmp(opt, "-lf")) 218 { 219 le = PPDC_LFONLY; 220 opt += strlen(opt) - 1; 221 break; 222 } 223 else if (!strcmp(opt, "-cr")) 224 { 225 le = PPDC_CRONLY; 226 opt += strlen(opt) - 1; 227 break; 228 } 229 else if (!strcmp(opt, "-crlf")) 230 { 231 le = PPDC_CRLF; 232 opt += strlen(opt) - 1; 233 break; 234 } 235 236 default : // Unknown 237 usage(); 238 break; 239 } 240 } 241 else 242 { 243 // Open and load the driver info file... 244 if (verbose > 1) 245 _cupsLangPrintf(stdout, 246 _("ppdc: Loading driver information file \"%s\"."), 247 argv[i]); 248 249 src->read_file(argv[i]); 250 } 251 252 253 if (src->drivers->count > 0) 254 { 255 // Create the output directory... 256 if (mkdir(outdir, 0777)) 257 { 258 if (errno != EEXIST) 259 { 260 _cupsLangPrintf(stderr, 261 _("ppdc: Unable to create output directory %s: %s"), 262 outdir, strerror(errno)); 263 return (1); 264 } 265 } 266 267 // Write PPD files... 268 for (d = (ppdcDriver *)src->drivers->first(); 269 d; 270 d = (ppdcDriver *)src->drivers->next()) 271 { 272 if (do_test) 273 { 274 // Test the PPD file for this driver... 275 int pid, // Process ID 276 fds[2]; // Pipe file descriptors 277 278 279 if (pipe(fds)) 280 { 281 _cupsLangPrintf(stderr, 282 _("ppdc: Unable to create output pipes: %s"), 283 strerror(errno)); 284 return (1); 285 } 286 287 if ((pid = fork()) == 0) 288 { 289 // Child process comes here... 290 dup2(fds[0], 0); 291 292 close(fds[0]); 293 close(fds[1]); 294 295 execlp("cupstestppd", "cupstestppd", "-", (char *)0); 296 297 _cupsLangPrintf(stderr, 298 _("ppdc: Unable to execute cupstestppd: %s"), 299 strerror(errno)); 300 return (errno); 301 } 302 else if (pid < 0) 303 { 304 _cupsLangPrintf(stderr, _("ppdc: Unable to execute cupstestppd: %s"), 305 strerror(errno)); 306 return (errno); 307 } 308 309 close(fds[0]); 310 fp = cupsFileOpenFd(fds[1], "w"); 311 } 312 else 313 { 314 // Write the PPD file for this driver... 315 if (use_model_name) 316 { 317 if (!_cups_strncasecmp(d->model_name->value, d->manufacturer->value, 318 strlen(d->manufacturer->value))) 319 { 320 // Model name already starts with the manufacturer... 321 outname = d->model_name->value; 322 } 323 else 324 { 325 // Add manufacturer to the front of the model name... 326 snprintf(make_model, sizeof(make_model), "%s %s", 327 d->manufacturer->value, d->model_name->value); 328 outname = make_model; 329 } 330 } 331 else if (d->file_name) 332 outname = d->file_name->value; 333 else 334 outname = d->pc_file_name->value; 335 336 if (strstr(outname, ".PPD")) 337 { 338 // Convert PCFileName to lowercase... 339 for (j = 0; 340 outname[j] && j < (int)(sizeof(pcfilename) - 1); 341 j ++) 342 pcfilename[j] = (char)tolower(outname[j] & 255); 343 344 pcfilename[j] = '\0'; 345 } 346 else 347 { 348 // Leave PCFileName as-is... 349 strlcpy(pcfilename, outname, sizeof(pcfilename)); 350 } 351 352 // Open the PPD file for writing... 353 if (comp) 354 snprintf(filename, sizeof(filename), "%s/%s.gz", outdir, pcfilename); 355 else 356 snprintf(filename, sizeof(filename), "%s/%s", outdir, pcfilename); 357 358 if (cupsArrayFind(filenames, filename)) 359 _cupsLangPrintf(stderr, 360 _("ppdc: Warning - overlapping filename \"%s\"."), 361 filename); 362 else 363 cupsArrayAdd(filenames, strdup(filename)); 364 365 fp = cupsFileOpen(filename, comp ? "w9" : "w"); 366 if (!fp) 367 { 368 _cupsLangPrintf(stderr, 369 _("ppdc: Unable to create PPD file \"%s\" - %s."), 370 filename, strerror(errno)); 371 return (1); 372 } 373 374 if (verbose) 375 _cupsLangPrintf(stdout, _("ppdc: Writing %s."), filename); 376 } 377 378 /* 379 * Write the PPD file... 380 */ 381 382 ppdcArray *templocales = locales; 383 384 if (!templocales && !single_language) 385 { 386 templocales = new ppdcArray(); 387 for (ppdcCatalog *tempcatalog = (ppdcCatalog *)src->po_files->first(); 388 tempcatalog; 389 tempcatalog = (ppdcCatalog *)src->po_files->next()) 390 { 391 tempcatalog->locale->retain(); 392 templocales->add(tempcatalog->locale); 393 } 394 } 395 396 if (d->write_ppd_file(fp, catalog, templocales, src, le)) 397 { 398 cupsFileClose(fp); 399 return (1); 400 } 401 402 if (templocales != locales) 403 templocales->release(); 404 405 cupsFileClose(fp); 406 } 407 } 408 else 409 usage(); 410 411 // Delete the printer driver information... 412 src->release(); 413 414 // Message catalog... 415 if (catalog) 416 catalog->release(); 417 418 // Return with no errors. 419 return (0); 420} 421 422 423// 424// 'usage()' - Show usage and exit. 425// 426 427static void 428usage(void) 429{ 430 _cupsLangPuts(stdout, _("Usage: ppdc [options] filename.drv [ ... " 431 "filenameN.drv ]")); 432 _cupsLangPuts(stdout, _("Options:")); 433 _cupsLangPuts(stdout, _(" -D name=value Set named variable to " 434 "value.")); 435 _cupsLangPuts(stdout, _(" -I include-dir Add include directory to " 436 "search path.")); 437 _cupsLangPuts(stdout, _(" -c catalog.po Load the specified " 438 "message catalog.")); 439 _cupsLangPuts(stdout, _(" -d output-dir Specify the output " 440 "directory.")); 441 _cupsLangPuts(stdout, _(" -l lang[,lang,...] Specify the output " 442 "language(s) (locale).")); 443 _cupsLangPuts(stdout, _(" -m Use the ModelName value " 444 "as the filename.")); 445 _cupsLangPuts(stdout, _(" -t Test PPDs instead of " 446 "generating them.")); 447 _cupsLangPuts(stdout, _(" -v Be verbose.")); 448 _cupsLangPuts(stdout, _(" -z Compress PPD files using " 449 "GNU zip.")); 450 _cupsLangPuts(stdout, _(" --cr End lines with CR (Mac " 451 "OS 9).")); 452 _cupsLangPuts(stdout, _(" --crlf End lines with CR + LF " 453 "(Windows).")); 454 _cupsLangPuts(stdout, _(" --lf End lines with LF " 455 "(UNIX/Linux/OS X).")); 456 457 exit(1); 458} 459 460 461// 462// End of "$Id: ppdc.cxx 11801 2014-04-08 19:55:21Z msweet $". 463// 464