PNG validation on iOS - ios

Writing a mapping application on iOS, making use of OpenStreetMap tiles.
Map tile images are downloaded asynchronously and stored in a dictionary, or persisted in a SQLite DB.
Occasionally, for whatever reason, while attempting to render a map tile image, I get the following error:
ImageIO: <ERROR> PNGinvalid distance too far back
This causes nasty black squares to appear over my map.
This is the piece of code in which this occurs:
NSData *imageData = [TileDownloader RetrieveDataAtTileX:(int)tilex Y:(int)tiley Zoom:(int)zoomLevel];
if (imageData != nil) {
NSLog(#"Obtained image data\n");
UIImage *img = [[UIImage imageWithData:imageData] retain];
// Perform the image render on the current UI context.
// ERROR OCCURS BETWEEN PUSH AND POP
UIGraphicsPushContext(context);
[img drawInRect:[self rectForMapRect:mapRect] blendMode:kCGBlendModeNormal alpha:1.0f];
UIGraphicsPopContext();
[img release];
}
Now, what I'm looking for is a way to ensure a png is valid before attempting to render it to my map.
Edit: The system also occasionally throws this error:
ImageIO: <ERROR> PNGIDAT: CRC error

I found this in other question and put together what solved the issue for me. Hope you find this helpful.
The PNG format has several built in checks. Each "chunk" has a CRC32 check, but to check that you'd need to read the full file.
A more basic check (not foolproof, of course) would be to read the start and ending of the file.
The first 8 bytes should always be the following (decimal) values { 137, 80, 78, 71, 13, 10, 26, 10 } (ref). In particular, the bytes second-to-fourth correspond to the ASCII string "PNG".
In hexadecimal:
89 50 4e 47 0d 0a 1a 0a
.. P N G ...........
You can also check the last 12 bytes of the file (IEND chunk). The middle 4 bytes should correspond to the ASCII string "IEND". More specifically the last 12 bytes should be (in hexa):
00 00 00 00 49 45 4e 44 ae 42 60 82
........... I E N D ...........
(Strictly speaking, it's not really obligatory for a PNG file to end with those 12 bytes, the IEND chunk itself signals the end of the PNG stream and so a file could in principle have extra trailing bytes which would be ignored by the PNG reader. In practice, this is extremely improbable).
Here is an implementation:
- (BOOL)dataIsValidPNG:(NSData *)data
{
if (!data || data.length < 12)
{
return NO;
}
NSInteger totalBytes = data.length;
const char *bytes = (const char *)[data bytes];
return (bytes[0] == (char)0x89 && // PNG
bytes[1] == (char)0x50 &&
bytes[2] == (char)0x4e &&
bytes[3] == (char)0x47 &&
bytes[4] == (char)0x0d &&
bytes[5] == (char)0x0a &&
bytes[6] == (char)0x1a &&
bytes[7] == (char)0x0a &&
bytes[totalBytes - 12] == (char)0x00 && // IEND
bytes[totalBytes - 11] == (char)0x00 &&
bytes[totalBytes - 10] == (char)0x00 &&
bytes[totalBytes - 9] == (char)0x00 &&
bytes[totalBytes - 8] == (char)0x49 &&
bytes[totalBytes - 7] == (char)0x45 &&
bytes[totalBytes - 6] == (char)0x4e &&
bytes[totalBytes - 5] == (char)0x44 &&
bytes[totalBytes - 4] == (char)0xae &&
bytes[totalBytes - 3] == (char)0x42 &&
bytes[totalBytes - 2] == (char)0x60 &&
bytes[totalBytes - 1] == (char)0x82);
}

I know this is a super old thread, but I was looking around for an NSData extension that actually validated the crc32's in the PNG data chunks. Having not found one, I adapted one from some other source.
This will actually flag bad PNG CRC's, which isn't done (shockingly) by most image libraries
static const unsigned int datacrc32_table[256] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
0x2d02ef8d
};
unsigned int
datacrc32 (unsigned int crc, unsigned char *buf, int len)
{
unsigned char *end;
crc = ~crc;
for (end = buf + len; buf < end; ++buf)
crc = datacrc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
return ~crc;
}
-(BOOL)isCRCValidPNG {
char chnk [5];
int l = 0;
int size = (int)[self length];
unsigned int crc = 0;
unsigned char c;
unsigned int csum = 0;
unsigned char b;
unsigned char *tileBytes = (unsigned char *)[self bytes];
if (self.length > 8){
const unsigned char pngHeaderBytes[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
for (int i = 0 ; i < 8 ; ++i){
if (tileBytes[i] != pngHeaderBytes[i])
return NO;
}
}
// process chunks
int bytePtr = 8;
strcpy (chnk, "");
do
{
// get chunk size
if (bytePtr+4 > size)
return NO;
l = 0;
for (int i = 0; i < 4; i++)
{
l = (l << 8) + tileBytes[bytePtr++];
}
printf("l is %08x",l);
// get chunk name
crc = 0;
strcpy (chnk, "");
if (bytePtr+4 > size)
return NO;
for (int i = 0; i < 4; i++)
{
c = tileBytes[bytePtr++];
crc = datacrc32 (crc, &c, 1);
chnk[i] = (char) c;
}
chnk[4] = '\0';
printf ("%s (%3d )", chnk, l);
// chunk data
if (bytePtr+l > size)
return NO;
for (int i = 0; i < l; i++)
{
c = tileBytes[bytePtr++];
crc = datacrc32 (crc, &c, 1);
}
// checksum
csum = 0;
if (bytePtr+4 > size)
return NO;
for (int i = 0; i < 4; i++)
{
c = tileBytes[bytePtr++];
csum = (csum << 8) + (int) c;
b = (unsigned char) ((crc >> 8 * (3 - i)) & 0xFF);
// printf ("b = %02x\n", b);
}
if (crc == csum)
NSLog(#"Chunk %s validated",chnk);
else
NSLog(#"chunk %s invalid ",chnk);
if (crc != csum)
return NO;
}
while (strcmp (chnk, "IEND") != 0);
return YES;
}

Switched from my own Asynchronous Download Queue Manager to the All Seeing I implementation. Problem became a moot point.

The Swift Version
func checkPNGImageDataFormat(_ imageData:Data) -> Bool
{
//More expensive since it has to go through entire data
//Check entire header magic number and IEND trailer in PNG data
var status:Bool = true
if(imageData.count < 12)
{
return false
}
let totalBytes = imageData.count
let bytes = imageData.withUnsafeBytes {
[UInt8](UnsafeBufferPointer(start: $0, count: totalBytes))
}
let header:Bool = bytes[0] == 0x89 && bytes[1] == 0x50 && bytes[2] == 0x4e && bytes[3] == 0x47 && bytes[4] == 0x0d && bytes[5] == 0x0a && bytes[6] == 0x1a && bytes[7] == 0x0a
let iend:Bool = bytes[totalBytes - 12] == 0x00 && bytes[totalBytes - 11] == 0x00 && bytes[totalBytes - 10] == 0x00 && bytes[totalBytes - 9] == 0x00 && bytes[totalBytes - 8] == 0x49 && bytes[totalBytes - 7] == 0x45 && bytes[totalBytes - 6] == 0x4e && bytes[totalBytes - 5] == 0x44 && bytes[totalBytes - 4] == 0xae && bytes[totalBytes - 3] == 0x42 && bytes[totalBytes - 2] == 0x60 && bytes[totalBytes - 1] == 0x82
status = header && iend
return status
}

Related

I dont understand this line of code (hexchar_to_int(str[i]) << 4) | hexchar_to_int(str[i+1])) . Can someone explains me

I reviewed these lines of codes which used to convert string to binary, but i cant understand what this
line of code is doing (hexchar_to_int(str[i]) << 4) | hexchar_to_int(str[i+1]), i am frustrated
with this bit manipulation here, 8 bit of unsigned int used and we shifted left to get the least 4
significant bits. But i don't know the purpose of doing so.
unsigned char hexchar_to_int(char const ch)
{
if (ch >= '0' && ch <= '9') return ch - '0';
if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
throw std::invalid_argument("Invalid hexadecimal character");
}
std::vector<unsigned char> hexstr_to_bytes(std::string_view str)
{
std::vector<unsigned char> result;
for (size_t i = 0; i < str.size(); i += 2)
{
result.push_back((hexchar_to_int(str[i]) << 4) | hexchar_to_int(str[i+1]));
}
return result;
}
<<4 is *2^4 or *16, shifting left, not right.
The | is a simple addition of the 2 hexa characters at their correct place.
See 0xab = 10*16 + 11 = 171

Is there a method to calculate the total duration a gif plays?

I want to calculate the total duration a gif plays. It can be either duration of a gif or frame count of the gif. Have tried using FLAnimatedImage, SDWebImage and YYImage but can't really attain what I am looking for. The gif is loaded from remote url and then I want to calculate the duration it plays.
This is the function that returns the total duration in GIF time units (1 unit = 10 msec).
data is a pointer to GIF data, size is its size.
long Duration(uint8_t *data, long size) {
long desc, time = 0;
uint8_t *buff;
if ((size > 13) && data && (data[0] == 71) && (data[1] == 73)
&& (data[2] == 70) && (data[3] == 56) && (data[5] == 97)
&& ((data[4] == 55) || (data[4] == 57))) {
buff = data + 13 + ((data[10] & 0x80)? 6 << (data[10] & 7) : 0);
if ((size -= buff - data) > 0)
while ((desc = *buff++) != 0x3B) {
size--;
if (desc == 0x2C) {
desc = 9 + ((buff[8] & 0x80)? 6 << (buff[8] & 7) : 0);
buff += desc;
if ((size -= desc) <= 0)
break;
}
else if ((desc == 0x21) && (*buff == 0xF9))
time += *(uint16_t*)(buff + 3);
buff++;
if (--size <= 0)
break;
do {
buff += (desc = 1 + *buff);
if ((size -= desc) <= 0)
return time;
} while (desc > 1);
}
}
return time;
}
This function parses GIF images by hand, extracting frame delay information and summing it.

IBAN Validator Swift

I am writing an algorithm to validate IBAN (International Bank Account Number) in Swift 3 and not able to figure one of the validation.
Example IBAN - BE68539007547034
Here are the rules to validate -
Input number should be of length 16.
First 2 characters are country code (not numeric).
Last 14 are numeric.
Last 2 characters are the modulo 97 result of the previous 12 numeric characters.
While #1 - #3 are clear I need clarity on #4. If anyone have done this before and know about it then please let me know.
The validation algorithm is rather simple if you follow the algorithm on wikipedia:
extension String {
private func mod97() -> Int {
let symbols: [Character] = Array(self)
let swapped = symbols.dropFirst(4) + symbols.prefix(4)
let mod: Int = swapped.reduce(0) { (previousMod, char) in
let value = Int(String(char), radix: 36)! // "0" => 0, "A" => 10, "Z" => 35
let factor = value < 10 ? 10 : 100
return (factor * previousMod + value) % 97
}
return mod
}
func passesMod97Check() -> Bool {
guard self.characters.count >= 4 else {
return false
}
let uppercase = self.uppercased()
guard uppercase.range(of: "^[0-9A-Z]*$", options: .regularExpression) != nil else {
return false
}
return (uppercase.mod97() == 1)
}
}
Usage:
let iban = "XX0000000..."
let valid = iban.passesMod97Check()
If you want to validate the format for a specific country, just modify the regular expression, e.g.
"^[A-Z]{2}[0-9]{14}$"
or directly
"^BE\\d{14}$"
From Wikipedia
let IBAN = "GB82WEST12345698765432" // uppercase, no whitespace !!!!
var a = IBAN.utf8.map{ $0 }
while a.count < 4 {
a.append(0)
}
let b = a[4..<a.count] + a[0..<4]
let c = b.reduce(0) { (r, u) -> Int in
let i = Int(u)
return i > 64 ? (100 * r + i - 55) % 97: (10 * r + i - 48) % 97
}
print( "IBAN \(IBAN) is", c == 1 ? "valid": "invalid")
prints
IBAN GB82WEST12345698765432 is valid
With IBAN from your question it prints
IBAN BE68539007547034 is valid
I finded a great solution that work for me in Objective-C
https://gist.github.com/0xc010d/5301790 you can rewrite for Swift or use bridging header. Objective-C implementation of mod97 IBAN checking algorithm
#import <Foundation/Foundation.h>
#interface NSString (Mod97Check)
- (BOOL)passesMod97Check; // Returns result of mod 97 checking algorithm. Might be used to check IBAN.
// Expects string to contain digits and/or upper-/lowercase letters; space and all the rest symbols are not acceptable.
#end
#import "NSString+Mod97Check.h"
#implementation NSString (Mod97Check)
- (BOOL)passesMod97Check {
NSString *string = [self uppercaseString];
NSInteger mod = 0, length = [self length];
for (NSInteger index = 4; index < length + 4; index ++) {
unichar character = [string characterAtIndex:index % length];
if (character >= '0' && character <= '9') {
mod = (10 * mod + (character - '0')) % 97; // '0'=>0, '1'=>1, ..., '9'=>9
}
else if (character >= 'A' && character <= 'Z') {
mod = (100 * mod + (character - 'A' + 10)) % 97; // 'A'=>10, 'B'=>11, ..., 'Z'=>35
}
else {
return NO;
}
}
return (mod == 1);
}
#end
-(BOOL)isValidIBAN {
NSString *iban = self;
static NSString* const LettersAndDecimals = #"ABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789";
iban = [[iban stringByReplacingOccurrencesOfString:#" " withString:#""] uppercaseString];
NSCharacterSet *invalidChars = [[NSCharacterSet characterSetWithCharactersInString:LettersAndDecimals] invertedSet];
if ([iban rangeOfCharacterFromSet:invalidChars].location != NSNotFound)
{
return NO;
}
int checkDigit = [iban substringWithRange:NSMakeRange(2, 2)].intValue;
iban = [NSString stringWithFormat:#"%#%#",[iban substringWithRange:NSMakeRange(4, iban.length - 4)], [iban substringWithRange:NSMakeRange(0, 4)]] ;
for (int i = 0; i < iban.length; i++) {
unichar c = [iban characterAtIndex:i];
if (c >= 'A' && c <= 'Z') {
iban = [NSString stringWithFormat:#"%#%d%#", [iban substringWithRange:NSMakeRange(0, i)], (c - 'A' + 10),[iban substringWithRange:NSMakeRange(i+1, iban.length - i - 1)]];
}
}
iban = [[iban substringWithRange:NSMakeRange(0, iban.length - 2)] stringByAppendingString:#"00"];
while(true)
{
int iMin = (int)MIN(iban.length, 9);
NSString* strPart = [iban substringWithRange:NSMakeRange(0, iMin)];
int decnumber = strPart.intValue;
if(decnumber < 97 || iban.length < 3)
break;
int del = decnumber % 97;
iban = [NSString stringWithFormat:#"%d%#", del, [iban substringFromIndex:iMin]];
}
int check = 98 - iban.intValue;
return checkDigit == check;
}
Here you go :
func isValidIBAN(text:String) -> Bool {
let ibanRegEx = "[a-zA-Z]{2}+[0-9]{2}+[a-zA-Z0-9]{4}+[0-9]{7}([a-zA-Z0-9]?){0,16}"
let ibanTest = NSPredicate(format:"SELF MATCHES %#", ibanRegEx)
return ibanTest.evaluate(with: text)
}
It's clean, and it works.

C to Lua conversion - weird result

I have a C function that I want to convert to LUA but I'm getting strange results out of Lua:
unsigned short crc16(const char* pstrCurrent, int iCount)
{
unsigned short wCRC = 0;
int iIndex = 0;
while(--iCount >= 0)
{
wCRC = wCRC ^ ((int)(*pstrCurrent++) << 8);
printf ("WCRC = %u\n", wCRC);
}
return (wCRC & 0xFFFF);
}
and here is how I started the Lua:
local function crc16(keyCurrent, byteCount)
wCRC = 0
byteIndex = 1
local crcInput = {}
while byteCount > 0 do
print ("BYTE COUNT= " .. byteCount)
wCRC=bit32.bxor(wCRC, bit32.lshift(keyCurrent[byteIndex], 8))
print ( "WCRC = " .. wCRC )
byteCount = byteCount-1
byteIndex = byteIndex+1
end
end
Yes, I know the C function is incomplete, I just want to compare what's causing issues.
The prints of the WCRC is C and Lua print completely different numbers for the same input.
Is my Lua conversion incorrect? It is my second or third time using Lua so not quite sure what I'm doing wrong.
***************** UPDATE ********************
So here is the full C and LUA and a quick little test code:
unsigned short crc16(const char* pstrCurrent, int iCount)
{
unsigned short wCRC = 0;
int iIndex = 0;
// Perform the following for each character in the buffer
while(--iCount >= 0)
{
// Get the byte information for the calculation and
// advance the pointer
wCRC = wCRC ^ ((int)(*pstrCurrent++) << 8);
for(iIndex = 0; iIndex < 8; ++iIndex)
{
if(wCRC & 0x8000)
{
wCRC = (wCRC << 1) ^ 0x1021;
}
else
{
wCRC = wCRC << 1;
}
}
}
return (wCRC & 0xFFFF);
}
and the LUA conversion:
function crc16 (keyCurrent, iCount)
wCRC = 0
byteIndex = 1
iIndex = 0
local crcInput = {}
while iCount >= 1 do
wCRC = bit32.bxor (wCRC, bit32.lshift(keyCurrent[byteIndex], 8))
for iIndex=0,8 do
if (bit32.band (wCRC, 0x8000) ~= nil ) then
wCRC = bit32.bxor (bit32.lshift (wCRC, 1), 0x1021)
else
wCRC = bit32.lshift (wCRC, 1)
end
end
iCount = iCount-1
byteIndex = byteIndex+1
end
return (bit32.band (wCRC, 0xFFFF))
end
local dKey = {}
dKey = {8, 210, 59, 0, 18, 166, 254, 117}
print ( "CRC = " .. crc16 (dKey ,8) )
In C, for the same array I get: CRC16 = 567
In LUA, I get: CRC = 61471
Can someone tell me what I'm doing wrong?
Thanks
It seems they yield the same results:
pure-C
WCRC = 18432
WCRC = 11520
WCRC = 16640
WCRC = 11520
pure-Lua
BYTE COUNT= 4
WCRC = 18432
BYTE COUNT= 3
WCRC = 11520
BYTE COUNT= 2
WCRC = 16640
BYTE COUNT= 1
WCRC = 11520
ASCII convertor:
What do you mean?
There's mistakes in altered Lua sample.
1. bit32.band() returns number. Number 0 not equals to 'nil', that's totally different type. You're trying to compare number with nil, and that check will fail always.2. for iIndex=0,8 do iterates 9 times, including final index 8.

scanf and gets buffer

im having a problem with scanf and gets. and I kno that its bound to errors but I couldn't find any other way. This way, the name is printing out but It doesn't print out the first letter of it.
Here's my code:
#include <stdio.h>
float calculations(int age, float highBP, float lowBP);
char option;
int counter, age;
char temp_name[50];
float highBP, lowBP, riskF, optimalH = 120.0, optimalL = 80.0;
typedef struct {
char name[50]; /*which represents the patient’s name*/
int age; /*which represents the patient’s age*/
float highBP; /*highBP, which represents the patient’s high (systolic) blood pressure*/
float lowBP; /*lowBP, which represents the patient’s low (diastolic) blood pressure*/
float riskF; /*riskFactor, which represents the patient’s risk factor for stroke due to hypertension.*/
}patient;/*end structure patient*/
patient *pRecords[30];
void printMenu()
{
printf("\n---------------------------------------------------------\n");
printf("|\t(N)ew record\t(D)isplay db\t(U)pdate record\t|\n");
printf("|\t(L)oad disk\t(W)rite disk\t(E)mpty disk\t|\n");
printf("|\t(S)ort db\t(C)lear db\t(Q)uit \t\t|\n");
printf("---------------------------------------------------------\n");
printf("choose one:");
}/*end print menu*/
void enter()
{
if(counter == 30)
printf("database full.");
else{
printf("name: ");
while(getchar()=='\n');
gets(temp_name);
strcpy(pRecords[counter]->name , temp_name);
printf("age: "); scanf("%d", &age);
pRecords[counter]->age = age;
printf("highBP: "); scanf("%f", &highBP);
pRecords[counter]->highBP = highBP;
printf("lowBP: "); scanf("%f", &lowBP);
pRecords[counter]->lowBP = lowBP;
float temp = calculations(age, highBP,lowBP);
pRecords[counter]->riskF = temp;
/*printf("name: %s, age: %d, highbp:%.1f, lowBP:%.1f\n", pRecords[counter]->name,pRecords[counter]->age,pRecords[counter]->highBP,pRecords[counter]->lowBP);
printf("risk factor: %.1f\n", pRecords[counter]->riskF);*/
counter ++;
}
}/*end of void enter function*/
memallocate(int counter){
pRecords[counter] = (patient *)malloc (sizeof(patient));
}/*end memallocate function*/
void display()
{
printf("===============================\n");
int i;
for(i=0; i<counter; i++)
{
printf("name: %s\n", pRecords[i]->name);
printf("age: %d\n", pRecords[i]->age);
printf("bp: %.2f %.2f\n", pRecords[i]->highBP, pRecords[i]->lowBP);
printf("risk: %.2f\n\n", pRecords[i]->riskF);
}/*end of for loop*/
printf("========== %d records ==========", counter);
}/*end of display method*/
float calculations(int age, float highBP, float lowBP)
{ float risk;
if((highBP <= optimalH) && (lowBP <= optimalL))
{ risk = 0.0;
if(age >=50)
risk = 0.5;
}
else if(highBP <= optimalH && (lowBP>optimalL && lowBP <=(optimalL+10)))
{ risk= 1.0;
if(age >=50)
risk = 1.5;
}
else if ((highBP >optimalH && highBP <= (optimalH+10))&& lowBP <=optimalL)
{ risk= 1.0;
if(age >=50)
risk= 1.5;
}
else if((highBP > optimalH && highBP <=(optimalH+10)) && (lowBP >optimalL && lowBP <= (optimalL+10)))
{ risk= 2.0;
if(age >=50)
risk = 2.5;
}
else if(highBP < optimalH && (lowBP >(optimalL+11) && lowBP<(optimalL+20)))
{ risk = 3.0;
if(age >=50)
risk = 3.5;
}
else if((lowBP < optimalL) && (highBP >(optimalH+11) && highBP<(optimalH+20)))
{ risk = 3.0;
if(age >=50)
risk = 3.5;
}
else if((highBP>=(optimalH+11) && highBP <= (optimalH+20))&& (lowBP>=(optimalL+11) && lowBP<=(optimalL+20)))
{ risk = 4.0;
if(age >=50)
risk = 4.5;
}
else
{ risk = 5.0;
if(age >=50)
risk = 5.5;
}
return risk;
}/*end of calculation function*/
main()
{
printMenu();
char option=getchar();
while(option != 'q' || option != 'Q'){
if(option == 'N' || option == 'n')
{
memallocate(counter);
enter();
printMenu();
}
if (option == 'L' || option == 'l')
{
printMenu();
}
if(option == 'S' || option == 's')
{
printMenu();
}
if(option == 'D' || option == 'd')
{
display();
printMenu();
}
if(option == 'W' || option == 'w')
{
printMenu();
}
if(option == 'C' || option == 'c')
{
printMenu();
}
if(option == 'U' || option == 'u')
{
printMenu();
}
if(option == 'E' || option == 'e')
{
printMenu();
}
if(option == 'Q' || option == 'q')
{
exit(0);
}
option = getchar();
}/*end while*/
system("pause");
}/*end main*/
sample output:
---------------------------------------------------------
| (N)ew record (D)isplay db (U)pdate record |
| (L)oad disk (W)rite disk (E)mpty disk |
| (S)ort db (C)lear db (Q)uit |
---------------------------------------------------------
choose one: n
name: judy
age: 30
high bp: 110
low bp: 88
3
---------------------------------------------------------
| (N)ew record (D)isplay db (U)pdate record |
| (L)oad disk (W)rite disk (E)mpty disk |
| (S)ort db (C)lear db (Q)uit |
---------------------------------------------------------
choose one: n
name: cindy white
age: 52
high bp: 100.7
low bp: 89.4
---------------------------------------------------------
| (N)ew record (D)isplay db (U)pdate record |
| (L)oad disk (W)rite disk (E)mpty disk |
| (S)ort db (C)lear db (Q)uit |
---------------------------------------------------------
choose one: d
===============================
name: udy
age: 30
bp: 110.00 88.00
risk: 1.0
name: indy white
age: 52
bp: 100.70 89.40
risk: 1.5
========== 2 records ==========
Your while loop and use of gets() is generally not good practice.
Try something like:
fflush(stdin);
fgets(pRecords[counter]->name, sizeof(pRecords[counter]->name), stdin);
Try
if (strlen(pRecords[counter]->name) > 0)
{
pRecords[counter]->name[strlen(pRecords[counter]->name) - 1] = '\0';
}
You lose the first character to while(getchar()=='\n');. I don't know why that statement is necessary, but it loops until it gets a character that is not '\n' (which is 'j' and 'c' in your case).
while (getchar() == '\n');
This line eats the newlines plus one character. When getchar() does not return a newline, it has already consumed the first character.
Look at ungetc() to write that character back onto the stream.
This:
while(getchar()=='\n');
loops until it gets a non-newline, which will be the first character of the name.
Try this instead:
do
c = getchar();
while(c == '\n');
ungetc(c, stdin);

Resources