Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port PAM interface for reading PAM image format #26

Merged
merged 1 commit into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions Sources/netpbm/PAMImageSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -629,3 +629,151 @@ func _pbm_readpbmrow_packed(_ file: UnsafeMutablePointer<FILE>, cols: Int32, for
throw ParseError.internalInconsistency
}
}

let MAX_LABEL_LENGTH = 8
let MAX_VALUE_LENGTH = 255

func _parseHeaderLine(buffer: UnsafeMutableRawBufferPointer, label: UnsafeMutableRawBufferPointer, value: UnsafeMutableRawBufferPointer) {
/*----------------------------------------------------------------------------
We truncate the label to MAX_LABEL_LENGTH and the value to
MAX_VALUE_LENGTH. There must be at least that much space (plus space
for a terminating NUL) at 'label' and 'value', respectively.
-----------------------------------------------------------------------------*/
var bufferCurs: Int = 0

/* Skip initial white space */
while CharacterSet.whitespacesAndNewlines.contains(UnicodeScalar(buffer[bufferCurs])) { bufferCurs += 1 }

/* Read off label, put as much as will fit into label[] */
var labelCurs = 0

while !CharacterSet.whitespacesAndNewlines.contains(UnicodeScalar(buffer[bufferCurs])) && buffer[bufferCurs] != 0 {
if (labelCurs < MAX_LABEL_LENGTH) {
label[labelCurs] = buffer[bufferCurs]
labelCurs += 1
}
bufferCurs += 1
}
label[labelCurs] = 0 /* null terminate it */

/* Skip white space between label and value */
while CharacterSet.whitespacesAndNewlines.contains(UnicodeScalar(buffer[bufferCurs])) { bufferCurs += 1 }

/* copy value into value[], truncating as necessary */
strncpy(value.baseAddress!, buffer.baseAddress! + bufferCurs, MAX_VALUE_LENGTH)
value[MAX_VALUE_LENGTH] = 0

/* Remove trailing white space from value[] */
var valueCurs = strlen(value.baseAddress!)
while valueCurs > 0 && CharacterSet.whitespacesAndNewlines.contains(UnicodeScalar(value[valueCurs - 1])) { valueCurs -= 1 }
value[valueCurs] = 0
}

func _processHeaderLine(buffer: UnsafeMutableRawBufferPointer, pam: inout Pam, headerSeen: inout HeaderSeen) throws {
/*----------------------------------------------------------------------------
Process a line from the PAM header. The line is buffer[], and it is not
a comment or blank.

Put the value that the line defines in *pamP (unless it's ENDHDR).

Update *headerSeenP with whatever we see.
-----------------------------------------------------------------------------*/
let label = UnsafeMutableRawBufferPointer.allocate(byteCount: MAX_LABEL_LENGTH + 1, alignment: MemoryLayout<UInt8>.alignment)
defer { label.deallocate() }
let value = UnsafeMutableRawBufferPointer.allocate(byteCount: MAX_VALUE_LENGTH + 1, alignment: MemoryLayout<UInt8>.alignment)
defer { value.deallocate() }

_parseHeaderLine(buffer: buffer, label: label, value: value)

if strcmp(label.baseAddress!, "ENDHDR") == 0 {
headerSeen.endhdr = true
} else if strcmp(label.baseAddress!, "WIDTH") == 0 {
pam.width = try Int32(_parseHeaderInt(valueString: value, name: label))
headerSeen.width = true
} else if strcmp(label.baseAddress!, "HEIGHT") == 0 {
pam.height = try Int32(_parseHeaderInt(valueString: value, name: label))
headerSeen.height = true
} else if strcmp(label.baseAddress!, "DEPTH") == 0 {
pam.depth = try UInt32(_parseHeaderUint(valueString: value, name: label))
headerSeen.depth = true
} else if strcmp(label.baseAddress!, "MAXVAL") == 0 {
let maxval = try _parseHeaderUint(valueString: value, name: label)
guard maxval < 1<<16 else {
print("Maxval too large: %u. Max is 65535", maxval)
throw ParseError.wrongFormat
}
pam.maxVal = maxval
headerSeen.maxval = true
} else if strcmp(label.baseAddress!, "TUPLTYPE") == 0 {
guard strlen(value.baseAddress!) != 0 else {
print("TUPLTYPE header does not have any tuple type text")
throw ParseError.wrongFormat
}
if !pam.tuple_type.isEmpty {
pam.tuple_type += " "
}
pam.tuple_type += String(cString: UnsafePointer(value.baseAddress!.assumingMemoryBound(to: CChar.self)))
} else {
print("Unrecognized header line type: '\(String(cString: UnsafePointer(label.baseAddress!.assumingMemoryBound(to: CChar.self))))'. Possible missing ENDHDR line?")
throw ParseError.wrongFormat
}
}

func _parseHeaderInt(valueString: UnsafeMutableRawBufferPointer, name: UnsafeMutableRawBufferPointer) throws -> Int {
/*----------------------------------------------------------------------------
This is not what it seems. It is the same thing as
parseHeaderUint, except that the type of the value it returns is
"int" instead of "unsigned int". But that doesn't mean the value can
be negative. We throw an error is it is not positive.
-----------------------------------------------------------------------------*/
let valueNum = try _parseHeaderUint(valueString: valueString, name: name)
// if ((int)valueNum != valueNum)
// pm_error("Ridiculously large value for %s in "
// "PAM file header: %u", name, valueNum);
// else
return Int(valueNum)
}

func _parseHeaderUint(valueString: UnsafeMutableRawBufferPointer, name: UnsafeMutableRawBufferPointer) throws -> UInt {
/*----------------------------------------------------------------------------
Interpret 'valueString' as the number in a header such as
"WIDTH 200".

'name' is the header name ("WIDTH" in the example).
-----------------------------------------------------------------------------*/
guard strlen(valueString.baseAddress!) > 0 else {
print("Missing value for \(String(cString: UnsafePointer(name.baseAddress!.assumingMemoryBound(to: CChar.self)))) in PAM file header.")
throw ParseError.wrongFormat
}
var endptr: UnsafeMutablePointer<CChar>?
errno = 0; /* Clear errno so we can detect strtol() failure */
let valueNum = strtol(valueString.baseAddress!, &endptr, 10);
guard errno == 0 else {
print("Too-large value for \(String(cString: UnsafePointer(name.baseAddress!.assumingMemoryBound(to: CChar.self)))) in PAM file header: '\(String(cString: UnsafePointer(valueString.baseAddress!.assumingMemoryBound(to: CChar.self))))'")
throw ParseError.wrongFormat
}
guard endptr?.pointee == Optional(0) else {
print("Non-numeric value for \(String(cString: UnsafePointer(name.baseAddress!.assumingMemoryBound(to: CChar.self)))) in PAM file header: '\(String(cString: UnsafePointer(valueString.baseAddress!.assumingMemoryBound(to: CChar.self))))'")
throw ParseError.wrongFormat
}
guard valueNum > 0 else {
print("Negative value for \(String(cString: UnsafePointer(name.baseAddress!.assumingMemoryBound(to: CChar.self)))) in PAM file header: '\(String(cString: UnsafePointer(valueString.baseAddress!.assumingMemoryBound(to: CChar.self))))'")
throw ParseError.wrongFormat
}
// if ((unsigned int)valueNum != valueNum)
// pm_error("Ridiculously large value for %s in "
// "PAM file header: %lu", name, valueNum);
return UInt(valueNum)
}


func _appendComment(comments: inout String,
commentHeader: UnsafeMutableRawPointer) throws {
assert(commentHeader.assumingMemoryBound(to: CChar.self).pointee == Character("#").asciiValue!)
comments += String(cString: commentHeader.assumingMemoryBound(to: CChar.self))
}


func _disposeOfComments(pam: inout Pam, comments: String) {
pam.comment = comments
}
134 changes: 79 additions & 55 deletions Sources/netpbm/PGMImageSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -260,74 +260,98 @@ func _pgm_validateComputableMaxval(maxVal: Gray) throws {
}
}

struct HeaderSeen {
/*----------------------------------------------------------------------------
This structure tells what we've seen so far in our progress through the
PAM header
------------------------------------------------------------------------*/
var width: Bool
var height: Bool
var depth: Bool
var maxval: Bool
var endhdr: Bool
init() {
width = false
height = false
depth = false
maxval = false
endhdr = false
}
}

func _readpaminitrest(pam: inout Pam) throws {
fatalError("Not implemented")
/*----------------------------------------------------------------------------
Read the rest of the PAM header (after the first line -- the magic
number line). Fill in all the information in *pamP.
-----------------------------------------------------------------------------*/
/* struct headerSeen headerSeen;
char * comments;
var headerSeen: HeaderSeen = HeaderSeen()

headerSeen.width = FALSE;
headerSeen.height = FALSE;
headerSeen.depth = FALSE;
headerSeen.maxval = FALSE;
headerSeen.endhdr = FALSE;
pam.tuple_type = ""

pamP->tuple_type[0] = '\0';
var comments = ""

comments = strdup("");
var c: Int32
repeat {
c = getc(pam.file)
} while c != -1 && c != Int32(Character("\n").asciiValue!)

{
int c;
/* Read off rest of 1st line -- probably just the newline after the
magic number
*/
while ((c = getc(pamP->file)) != -1 && c != '\n');
let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 256, alignment: MemoryLayout<UInt8>.alignment)
defer { buffer.deallocate() }
while !headerSeen.endhdr {
let rc = fgets(buffer.baseAddress!, Int32(buffer.count), pam.file)
guard rc != nil else {
print("EOF or error reading file while trying to read the PAM header")
throw ParseError.ioError
}
buffer[buffer.count - 1 - 1] = Character("\n").asciiValue! /* In case fgets() truncated */
if buffer[0] == Character("#").asciiValue! {
try _appendComment(comments: &comments, commentHeader: buffer.baseAddress!)
} else if _pm_stripeq(String(cString: buffer.baseAddress!.assumingMemoryBound(to: CChar.self)), "") {
/* Ignore it; it's a blank line */
} else {
try _processHeaderLine(buffer: buffer, pam: &pam, headerSeen: &headerSeen)
}
}

while (!headerSeen.endhdr) {
char buffer[256];
char * rc;
rc = fgets(buffer, sizeof(buffer), pamP->file);
if (rc == NULL)
pm_error("EOF or error reading file while trying to read the "
"PAM header");
else {
buffer[256-1-1] = '\n'; /* In case fgets() truncated */
if (buffer[0] == '#')
appendComment(&comments, buffer);
else if (pm_stripeq(buffer, ""));
/* Ignore it; it's a blank line */
else
processHeaderLine(buffer, pamP, &headerSeen);
}
_disposeOfComments(pam: &pam, comments: comments)

guard headerSeen.height else {
print("No HEIGHT header line in PAM header")
throw ParseError.wrongFormat
}
guard headerSeen.width else {
print("No WIDTH header line in PAM header")
throw ParseError.wrongFormat
}
guard headerSeen.depth else {
print("No DEPTH header line in PAM header")
throw ParseError.wrongFormat
}
guard headerSeen.maxval else {
print("No MAXVAL header line in PAM header")
throw ParseError.wrongFormat
}

disposeOfComments(pamP, comments);

if (!headerSeen.height)
pm_error("No HEIGHT header line in PAM header");
if (!headerSeen.width)
pm_error("No WIDTH header line in PAM header");
if (!headerSeen.depth)
pm_error("No DEPTH header line in PAM header");
if (!headerSeen.maxval)
pm_error("No MAXVAL header line in PAM header");

if (pamP->height == 0)
pm_error("HEIGHT value is zero in PAM header");
if (pamP->width == 0)
pm_error("WIDTH value is zero in PAM header");
if (pamP->depth == 0)
pm_error("DEPTH value is zero in PAM header");
if (pamP->maxval == 0)
pm_error("MAXVAL value is zero in PAM header");
if (pamP->maxval > PAM_OVERALL_MAXVAL)
pm_error("MAXVAL value (%lu) in PAM header is greater than %lu",
pamP->maxval, PAM_OVERALL_MAXVAL);
*/
guard pam.height > 0 else {
print("HEIGHT value is zero in PAM header")
throw ParseError.wrongFormat
}
guard pam.width > 0 else {
print("WIDTH value is zero in PAM header")
throw ParseError.wrongFormat
}
guard pam.depth > 0 else {
print("DEPTH value is zero in PAM header")
throw ParseError.wrongFormat
}
guard pam.maxVal > 0 else {
print("MAXVAL value is zero in PAM header")
throw ParseError.wrongFormat
}
guard pam.maxVal <= PAM_OVERALL_MAXVAL else {
print("MAXVAL value (\(pam.maxVal)) in PAM header is greater than \(PAM_OVERALL_MAXVAL)")
throw ParseError.wrongFormat
}
}

func _pgm_readpgmrow(_ file: UnsafeMutablePointer<FILE>, cols: Int32, maxVal: Gray, format: Int32) throws -> [Gray] {
Expand Down
Loading
Loading