// // PPD file compiler main entry for the CUPS PPD Compiler. // // Copyright 2007-2014 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 "ppdc-private.h" #include #include #include // // Local functions... // static void usage(void) _CUPS_NORETURN; // // 'main()' - Main entry for the PPD compiler. // int // O - Exit status main(int argc, // I - Number of command-line arguments char *argv[]) // I - Command-line arguments { int i, j; // Looping vars ppdcCatalog *catalog; // Message catalog const char *outdir; // Output directory ppdcSource *src; // PPD source file data ppdcDriver *d; // Current driver cups_file_t *fp; // PPD file char *opt, // Current option *value, // Value in option *outname, // Output filename make_model[1024], // Make and model pcfilename[1024], // Lowercase pcfilename filename[1024]; // PPD filename int comp, // Compress do_test, // Test PPD files single_language,// Generate single-language files use_model_name, // Use ModelName for filename verbose; // Verbosity ppdcLineEnding le; // Line ending to use ppdcArray *locales; // List of locales cups_array_t *filenames; // List of generated filenames _cupsSetLocale(argv); // Scan the command-line... catalog = NULL; comp = 0; do_test = 0; le = PPDC_LFONLY; locales = NULL; outdir = "ppd"; single_language = 0; src = new ppdcSource(); use_model_name = 0; verbose = 0; filenames = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL); for (i = 1; i < argc; i ++) if (argv[i][0] == '-') { for (opt = argv[i] + 1; *opt; opt ++) switch (*opt) { case 'D' : // Define variable i ++; if (i >= argc) usage(); if ((value = strchr(argv[i], '=')) != NULL) { *value++ = '\0'; src->set_variable(argv[i], value); } else src->set_variable(argv[i], "1"); break; case 'I' : // Include directory... i ++; if (i >= argc) usage(); if (verbose > 1) _cupsLangPrintf(stdout, _("ppdc: Adding include directory \"%s\"."), argv[i]); ppdcSource::add_include(argv[i]); break; case 'c' : // Message catalog... i ++; if (i >= argc) usage(); if (verbose > 1) _cupsLangPrintf(stdout, _("ppdc: Loading messages from \"%s\"."), argv[i]); if (!catalog) catalog = new ppdcCatalog("en"); if (catalog->load_messages(argv[i])) { _cupsLangPrintf(stderr, _("ppdc: Unable to load localization file " "\"%s\" - %s"), argv[i], strerror(errno)); return (1); } break; case 'd' : // Output directory... i ++; if (i >= argc) usage(); if (verbose > 1) _cupsLangPrintf(stdout, _("ppdc: Writing PPD files to directory " "\"%s\"."), argv[i]); outdir = argv[i]; break; case 'l' : // Language(s)... i ++; if (i >= argc) usage(); if (strchr(argv[i], ',')) { // Comma-delimited list of languages... char temp[1024], // Copy of language list *start, // Start of current locale name *end; // End of current locale name locales = new ppdcArray(); strlcpy(temp, argv[i], sizeof(temp)); for (start = temp; *start; start = end) { if ((end = strchr(start, ',')) != NULL) *end++ = '\0'; else end = start + strlen(start); if (end > start) locales->add(new ppdcString(start)); } } else { single_language = 1; if (verbose > 1) _cupsLangPrintf(stdout, _("ppdc: Loading messages for locale " "\"%s\"."), argv[i]); if (catalog) catalog->release(); catalog = new ppdcCatalog(argv[i]); if (catalog->messages->count == 0 && strcmp(argv[i], "en")) { _cupsLangPrintf(stderr, _("ppdc: Unable to find localization for " "\"%s\" - %s"), argv[i], strerror(errno)); return (1); } } break; case 'm' : // Use ModelName for filename use_model_name = 1; break; case 't' : // Test PPDs instead of generating them do_test = 1; break; case 'v' : // Be verbose... verbose ++; break; case 'z' : // Compress files... comp = 1; break; case '-' : // --option if (!strcmp(opt, "-lf")) { le = PPDC_LFONLY; opt += strlen(opt) - 1; break; } else if (!strcmp(opt, "-cr")) { le = PPDC_CRONLY; opt += strlen(opt) - 1; break; } else if (!strcmp(opt, "-crlf")) { le = PPDC_CRLF; opt += strlen(opt) - 1; break; } default : // Unknown usage(); } } else { // Open and load the driver info file... if (verbose > 1) _cupsLangPrintf(stdout, _("ppdc: Loading driver information file \"%s\"."), argv[i]); src->read_file(argv[i]); } if (src->drivers->count > 0) { // Create the output directory... if (mkdir(outdir, 0777)) { if (errno != EEXIST) { _cupsLangPrintf(stderr, _("ppdc: Unable to create output directory %s: %s"), outdir, strerror(errno)); return (1); } } // Write PPD files... for (d = (ppdcDriver *)src->drivers->first(); d; d = (ppdcDriver *)src->drivers->next()) { if (do_test) { // Test the PPD file for this driver... int pid, // Process ID fds[2]; // Pipe file descriptors if (pipe(fds)) { _cupsLangPrintf(stderr, _("ppdc: Unable to create output pipes: %s"), strerror(errno)); return (1); } if ((pid = fork()) == 0) { // Child process comes here... dup2(fds[0], 0); close(fds[0]); close(fds[1]); execlp("cupstestppd", "cupstestppd", "-", (char *)0); _cupsLangPrintf(stderr, _("ppdc: Unable to execute cupstestppd: %s"), strerror(errno)); return (errno); } else if (pid < 0) { _cupsLangPrintf(stderr, _("ppdc: Unable to execute cupstestppd: %s"), strerror(errno)); return (errno); } close(fds[0]); fp = cupsFileOpenFd(fds[1], "w"); } else { // Write the PPD file for this driver... if (use_model_name) { if (!_cups_strncasecmp(d->model_name->value, d->manufacturer->value, strlen(d->manufacturer->value))) { // Model name already starts with the manufacturer... outname = d->model_name->value; } else { // Add manufacturer to the front of the model name... snprintf(make_model, sizeof(make_model), "%s %s", d->manufacturer->value, d->model_name->value); outname = make_model; } } else if (d->file_name) outname = d->file_name->value; else outname = d->pc_file_name->value; if (strstr(outname, ".PPD")) { // Convert PCFileName to lowercase... for (j = 0; outname[j] && j < (int)(sizeof(pcfilename) - 1); j ++) pcfilename[j] = (char)tolower(outname[j] & 255); pcfilename[j] = '\0'; } else { // Leave PCFileName as-is... strlcpy(pcfilename, outname, sizeof(pcfilename)); } // Open the PPD file for writing... if (comp) snprintf(filename, sizeof(filename), "%s/%s.gz", outdir, pcfilename); else snprintf(filename, sizeof(filename), "%s/%s", outdir, pcfilename); if (cupsArrayFind(filenames, filename)) _cupsLangPrintf(stderr, _("ppdc: Warning - overlapping filename \"%s\"."), filename); else cupsArrayAdd(filenames, strdup(filename)); fp = cupsFileOpen(filename, comp ? "w9" : "w"); if (!fp) { _cupsLangPrintf(stderr, _("ppdc: Unable to create PPD file \"%s\" - %s."), filename, strerror(errno)); return (1); } if (verbose) _cupsLangPrintf(stdout, _("ppdc: Writing %s."), filename); } /* * Write the PPD file... */ ppdcArray *templocales = locales; if (!templocales && !single_language) { templocales = new ppdcArray(); for (ppdcCatalog *tempcatalog = (ppdcCatalog *)src->po_files->first(); tempcatalog; tempcatalog = (ppdcCatalog *)src->po_files->next()) { tempcatalog->locale->retain(); templocales->add(tempcatalog->locale); } } if (d->write_ppd_file(fp, catalog, templocales, src, le)) { cupsFileClose(fp); return (1); } if (templocales && templocales != locales) templocales->release(); cupsFileClose(fp); } } else usage(); // Delete the printer driver information... src->release(); // Message catalog... if (catalog) catalog->release(); // Return with no errors. return (0); } // // 'usage()' - Show usage and exit. // static void usage(void) { _cupsLangPuts(stdout, _("Usage: ppdc [options] filename.drv [ ... " "filenameN.drv ]")); _cupsLangPuts(stdout, _("Options:")); _cupsLangPuts(stdout, _(" -D name=value Set named variable to " "value.")); _cupsLangPuts(stdout, _(" -I include-dir Add include directory to " "search path.")); _cupsLangPuts(stdout, _(" -c catalog.po Load the specified " "message catalog.")); _cupsLangPuts(stdout, _(" -d output-dir Specify the output " "directory.")); _cupsLangPuts(stdout, _(" -l lang[,lang,...] Specify the output " "language(s) (locale).")); _cupsLangPuts(stdout, _(" -m Use the ModelName value " "as the filename.")); _cupsLangPuts(stdout, _(" -t Test PPDs instead of " "generating them.")); _cupsLangPuts(stdout, _(" -v Be verbose.")); _cupsLangPuts(stdout, _(" -z Compress PPD files using " "GNU zip.")); _cupsLangPuts(stdout, _(" --cr End lines with CR (Mac " "OS 9).")); _cupsLangPuts(stdout, _(" --crlf End lines with CR + LF " "(Windows).")); _cupsLangPuts(stdout, _(" --lf End lines with LF " "(UNIX/Linux/macOS).")); exit(1); }