mirror of
https://github.com/kfattic/cupsadmin.git
synced 2026-11-13 19:06:43 -05:00
ppdreport accepts a PPD file path (plain or gzipped) as well as a queue
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -176,3 +176,25 @@ public struct PPD {
|
|||||||
options.first { $0.keyword == keyword }
|
options.first { $0.keyword == keyword }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension PPD {
|
||||||
|
/// Reads a PPD file, plain or gzip-compressed (vendors ship `.ppd.gz` / `.gz` under
|
||||||
|
/// /Library/Printers/PPDs). Text that isn't valid UTF-8 is read as ISO Latin-1, the PPD default.
|
||||||
|
public static func text(contentsOfFile path: String) throws -> String {
|
||||||
|
guard var data = FileManager.default.contents(atPath: path) else {
|
||||||
|
throw CupsAdminError.failed("can’t read \(path)")
|
||||||
|
}
|
||||||
|
if data.starts(with: [0x1F, 0x8B]) {
|
||||||
|
let result = try CupsTools.run("/usr/bin/gzip", ["-dc", path])
|
||||||
|
guard result.succeeded else { throw CupsAdminError.failed("can’t decompress \(path): \(result.message)") }
|
||||||
|
data = result.standardOutput
|
||||||
|
}
|
||||||
|
return String(data: data, encoding: .utf8) ?? String(data: data, encoding: .isoLatin1) ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True when a `ppdreport` argument names a PPD file rather than a queue. Queue names can't contain "/".
|
||||||
|
public static func looksLikeFile(_ argument: String) -> Bool {
|
||||||
|
argument.contains("/") || [".ppd", ".gz", ".ppd.gz"].contains { argument.lowercased().hasSuffix($0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ enum PPDReportCommand {
|
|||||||
let source: String
|
let source: String
|
||||||
var attributes: IPPGroup?
|
var attributes: IPPGroup?
|
||||||
if let file {
|
if let file {
|
||||||
guard let data = FileManager.default.contents(atPath: file) else { throw CupsAdminError.failed("can’t read \(file)") }
|
text = try PPD.text(contentsOfFile: file)
|
||||||
text = String(decoding: data, as: UTF8.self)
|
|
||||||
source = file
|
source = file
|
||||||
} else if let queue {
|
} else if let queue {
|
||||||
guard try await client.queueExists(queue) else { throw CupsAdminError.failed("no such queue \(queue)") }
|
guard try await client.queueExists(queue) else { throw CupsAdminError.failed("no such queue \(queue)") }
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ let usage = """
|
|||||||
quick list quick actions (task-named queue defaults)
|
quick list quick actions (task-named queue defaults)
|
||||||
quick <action> <queue> [code | --clear]
|
quick <action> <queue> [code | --clear]
|
||||||
apply one via lpadmin, then verify
|
apply one via lpadmin, then verify
|
||||||
ppdreport <queue> | --file <path.ppd>
|
ppdreport <queue | path.ppd[.gz]>
|
||||||
every PPD group and option: keyword, label, type, default, choices
|
every PPD group and option: keyword, label, type, default, choices
|
||||||
help show this help
|
help show this help
|
||||||
"""
|
"""
|
||||||
@@ -130,7 +130,12 @@ func runCommand(_ args: [String]) async throws -> String {
|
|||||||
}
|
}
|
||||||
try rejectUnknownFlags()
|
try rejectUnknownFlags()
|
||||||
guard (file == nil && rest.count == 1) || (file != nil && rest.isEmpty) else {
|
guard (file == nil && rest.count == 1) || (file != nil && rest.isEmpty) else {
|
||||||
throw CupsAdminError.usage("ppdreport needs one queue name or --file <path.ppd>")
|
throw CupsAdminError.usage("ppdreport needs one queue name or PPD file path")
|
||||||
|
}
|
||||||
|
// A path (anything with "/", or ending in .ppd / .gz) is a PPD file; queue names can't contain "/".
|
||||||
|
if file == nil, let argument = rest.first, PPD.looksLikeFile(argument) {
|
||||||
|
file = argument
|
||||||
|
rest.removeAll()
|
||||||
}
|
}
|
||||||
return try await PPDReportCommand.run(client: client, queue: rest.first, file: file)
|
return try await PPDReportCommand.run(client: client, queue: rest.first, file: file)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user