Anybody who ever looked at the C89 standard, section "18.104.22.168 Types" (I'm even considering the age of the program in question here) knows that "char" may have value representation equivalent to that of "char signed".
An object declared as type _char_ is large enough to store any member of the basic execution character set. If a member of the required source character set enumerated in 5.2.1 is stored in a _char_ object, its value is guaranteed to be positive. If other quantities are stored in a char object, the behavior is implementation-defined; the values are treated as either signed or nonnegative integers.
The real fuckup here is that "ptr" was declared pointer-to-char, instead of pointer-to-char-unsigned. "char unsigned" is the type to access binary data or the object representation of objects.
"char" (which is a different type from "char signed", but may have identical value representation) does not even have to be able to represent more than 255 (NOT 256!) distinct values, if we rely on nothing else than the C89 standard. This accomodates sign-magnitude and one's complement representations. See C89 "22.214.171.124.1 Sizes of integral types <limits.h>":
[...] Their implementation-defined values shall be equal or greater in magnitude (absolute value) to those shown, with the same sign.
- number of bits for smallest object that is not a bit-field (byte)
- minimum value for an object of type signed char
- maximum value for an object of type signed char
- maximum value for an object of type unsigned char
- minimum value for an object of type char
CHAR_MIN see below
- maximum value for an object of type char
CHAR_MAX see below
If the value of an object of type char is treated as a signed integer when used in an expression, the value of CHAR_MIN shall be the same as that of SCHAR_MIN and the value of CHAR_MAX shall be the same as that of SCHAR_MAX. Otherwise, the value of CHAR_MIN shall be 0 and the value of CHAR_MAX shall be the same as that of UCHAR_MAX.
"char *" is there so you can work with _TEXT_ that consists of elements of the (basic or extended) execution character set. "char unsigned *" is there for everything else "binary". If in doubt, use "char unsigned *".
Another example: reading something from a socket and using string functions (like strstr(), strcmp() etc) on the result is _broken_, from a portability aspect.