How to get the last line of a binary file in C? - c++

I want to get the last record in a binary file, extract its ID and add one to it to mimic an auto-incrementing ID feature. So for example, if the previous user had an ID of 1, then the next user should have an ID of 2, and so on. The issue is I can't get the last record of the binary file using the code below. How can I go about doing this? Here's my code so far:
#include <stdio.h>
struct Applicant
{
int id;
};
void increment()
{
char fileName = "data.dat";
FILE *file;
// Instantiate the applicant struct
struct Applicant applicant;
// Will contain details of the last applicant in the file
struct Applicant previousApplicant;
// Open the file in 'append binary' mode
file = fopen(fileName, "ab");
// If file does not exist, print error message and exit
if (!file)
{
printf("\nSorry, that file does not exist");
exit(1);
}
// Read the last applicant to get their id
while(1)
{
fread(&previousApplicant, sizeof(previousApplicant), 1, file);
if (feof(file)) { break; }
}
// If there's no previous applicant, set the ID to 100
if (previousApplicant.id == 0)
{
applicant.id = 100;
}
// Otherwise just increment the previous applicant's id and assign it to the new applicant
else
{
applicant.id = previousApplicant.id + 1;
}
fwrite(&applicant, sizeof(applicant), 1, file);
fclose(file);
}
To achieve this, I'm using structs

The way I'd suggest doing it is to call fseek() to seek directly to the last record in the file, read it in to RAM, update it, and write it out again. That way you don't have to read in every record in the file just to update the last one, which would be really inefficient if the file is large and/or you have to perform this operation often.
Something like this:
#include <stdio.h>
#include <stdlib.h>
struct Applicant
{
int id;
};
void increment()
{
const char fileName[] = "data.dat";
// Open the file in 'append binary' mode
FILE *file = fopen(fileName, "a+b");
if (file == NULL)
{
printf("Couldn't open file [%s]\n", fileName);
return;
}
// Seek to the end of the file
if (fseek(file, 0, SEEK_END) != 0)
{
printf("Couldn't seek to the end of the file!\n");
fclose(file);
return;
}
// Will contain details of the last applicant in the file
struct Applicant previousApplicant;
// Check to make sure the file is big enough to hold at least one record
const long fileLengthBytes = ftell(file);
if (fileLengthBytes < sizeof(previousApplicant))
{
printf("File is to short to contain a full record!?\n");
fclose(file);
return;
}
// Check to make sure the file length is an even multiple of the record-length
if ((fileLengthBytes % sizeof(previousApplicant)) != 0)
{
printf("File length (%li) is not a multiple of record size (%zu)!?\n", fileLengthBytes, sizeof(previousApplicant));
fclose(file);
return;
}
// Seek backwards to the start of the last record in the file
if (fseek(file, -sizeof(previousApplicant), SEEK_CUR) != 0)
{
printf("Couldn't seek to the start of the last record in the file!\n");
fclose(file);
return;
}
// Read in the last record in the file
if (fread(&previousApplicant, sizeof(previousApplicant), 1, file) != 1)
{
printf("Couldn't read the last record in the file!\n");
fclose(file);
return;
}
// Increment the record
printf("Incrementing ID of last record in the file from %i to %i\n", previousApplicant.id, previousApplicant.id+1);
previousApplicant.id++;
// Finally, write out the new record in the file with incremented ID
if (fwrite(&previousApplicant, sizeof(previousApplicant), 1, file) != 1)
{
printf("Couldn't write the last record in the file!\n");
fclose(file);
return;
}
// success!
fclose(file);
}
int main(int, char **)
{
increment();
return 0;
}

There were a few problems with your code. You were reading the applicant ID into the applicant struct variable rather than the previousApplicant variable and when reading and writing files it is best to separate out the tasks: first open the file for reading, then when you're ready to write, close it and open it for writing.
Here's the modified working code which will do what you want:
#include <stdio.h>
#include <stdlib.h>
struct Applicant
{
int id;
};
void increment()
{
char* fileName = "data.dat";
FILE *file;
// Instantiate the applicant struct
struct Applicant applicant;
// Will contain details of the last applicant in the file
struct Applicant previousApplicant;
// Open the file in 'append binary' mode
file = fopen(fileName, "rb");
// If file does not exist, print error message and exit
if (!file)
{
printf("\nSorry, that file does not exist");
exit(1);
}
// Read the last applicant to get their id
while(1)
{
fread(&previousApplicant, sizeof(applicant), 1, file);
if( feof(file) ) break;
}
// If there's no previous applicant, set the ID to 100
if (previousApplicant.id == 0)
{
applicant.id = 100;
}
// Otherwise just increment the previous applicant's id and assign it to the new applicant
else
{
applicant.id = previousApplicant.id + 1;
}
fclose(file);
FILE* out = fopen(fileName, "ab");
fwrite(&applicant, sizeof(applicant), 1, out);
printf("%d\n", applicant.id);
fclose(out);
}
void main() {
increment();
}

I am not sure I got the logic of what you are trying to do. As pointed above, you can use just fseek() to move the current pointer for the file.
from the [documentation][1]
int fseek(
FILE *stream,
long offset,
int origin
);
int _fseeki64(
FILE *stream,
__int64 offset,
int origin
);
As for the offset: you can use this pre-defined constants: SEEK_CUR meaning the current position in file, SEEK_SET meaning the beginning and SEEK_END pointing, well, to the END of the file. The current offset from the beginning of file you can get by calling fseek()'s sister, ftell(), that returns a long with the position. First byte is 0. You can for sure seek for any position.
An example:
At first consider this code:
int create(const char* f_name, unsigned int n_rec)
{
Applicant one = {100};
FILE* f = fopen(f_name, "wb");
if (f == NULL) return -1;
for (unsigned i = 0; i < n_rec; i += 1, one.id+= 1)
if (fwrite(&one, sizeof(one), 1, f) != 1)
return 1 + i;
fclose(f);
return n_rec;
}; // create()
This function creates the file and writes down the supplied number of IDs. e.g.
create( "x.dat", 300 )
will go as expected and create x.dat and write 300 IDs starting at 100 --- inclusive --- in the file.
Then the following function in the example below reopens the file, get the last ID used and returns it, and you can use it as a prototype:
int get_last_id(const char* f_name)
{
Applicant one = {0};
int res = 0;
FILE* f = fopen(f_name, "rb+");
if (f == NULL) return -1;
res = fseek(f, SEEK_END, SEEK_SET);
if (res != 0) return -2;
res = fseek(f, -(long) (sizeof(one)), SEEK_END);
if (res != 0)
{
printf("res = %d\n", res);
perror("Error in seek()");
return -3;
}
printf("(file position: byte #%ld)\n", ftell(f));
if (fread(&one, sizeof(one), 1, f) != 1) return -4;
fclose(f);
return one.id;
}
The lines you want are these:
Applicant one = {0};
fseek(f, SEEK_END, SEEK_SET);
fseek(f, -(long) (sizeof(one)), SEEK_END);
fread(&one, sizeof(one), 1, f);
You go to the end if file, then goes back 1 record and read it.
sample output
5 Ids written to "sample.dat"
now open file and get the last ID used
(file position: byte #16)
Last ID on "sample.dat" is 104
For this
C code
#include <stdio.h>
typedef struct
{ int id;
} Applicant;
int create(const char*,unsigned);
int get_last_id(const char*);
int main(void)
{
const char* f_name = "sample.dat";
const unsigned initial_size = 5;
int res = 0;
// a few ids are written to file
if ( (res = create(f_name, initial_size)) < 0) return -1;
printf("\
%d Ids written to \"%s\"\n\
now open file and get the last ID used\n",
res, f_name);
// get the last id on file
res = get_last_id(f_name);
if (res < 0)
{
printf("Could not get id from file (%d)\n", res);
return -1;
}
else
printf("Last ID on \"%s\" is % d\n", f_name, res);
return 0;
}
int get_last_id(const char* f_name)
{
Applicant one = {0};
int res = 0;
FILE* f = fopen(f_name, "rb+");
if (f == NULL) return -1;
res = fseek(f, SEEK_END, SEEK_SET);
if (res != 0) return -2;
res = fseek(f, -(long) (sizeof(one)), SEEK_END);
if (res != 0)
{
printf("res = %d\n", res);
perror("Error in seek()");
return -3;
}
printf("(file position: byte #%ld)\n", ftell(f));
if (fread(&one, sizeof(one), 1, f) != 1) return -4;
fclose(f);
return one.id;
}
int create(const char* f_name, unsigned int n_rec)
{
Applicant one = {100};
FILE* f = fopen(f_name, "wb");
if (f == NULL) return -1;
for (unsigned i = 0; i < n_rec; i += 1, one.id+= 1)
if (fwrite(&one, sizeof(one), 1, f) != 1)
return 1 + i;
fclose(f);
return n_rec;
}; // create()
```
[1]: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fseek-fseeki64?view=msvc-160

Related

libzip zip_fread on image stops at null byte - corrupt images - c++

When trying to unzip an image file using libzip, I have run across the issue where in the image data, I hit a null byte and libzip zip_fread sees this as EOF and stops reading the file, resulting in a corrupted image. What is the best way to get handle the null byte when reading an image and extract the full image?
To clarify, text only files extract perfectly fine.
Below is the code used:
int FileHandler::ExtractFiles(std::string& path, std::string& file, bool is_test)
{
int err = 0;
std::string fullPath = path + "\\" + file;
zip* za = zip_open(fullPath.c_str(), 0, &err);
struct zip_stat st;
zip_stat_init(&st);
int number_of_entries = zip_get_num_entries(za, NULL);
for (zip_uint64_t i = 0; i < number_of_entries; ++i)
{
const char* name = zip_get_name(za, i, NULL);
std::string s_name = name;
size_t pos;
std::string backsl = "\\";
while ((pos = s_name.find(47)) != std::string::npos)
{
s_name.replace(pos, 1, backsl);
}
std::string fullFilePath = path + "\\" + s_name;
if(!is_test)
printf("Extracting: %s...\n", s_name.c_str());
std::string fullDir;
size_t found;
found = fullFilePath.find_last_of("\\");
if (found != std::string::npos)
{
fullDir = fullFilePath.substr(0, found);
}
struct zip_stat ist;
zip_stat_init(&ist);
zip_stat(za, name, 0, &ist);
char* contents = new char[ist.size];
zip_file* f = zip_fopen(za, name, 0);
// zip_fread to contents buffer
zip_fread(f, contents, ist.size);
if (CreateDirectory(fullDir.c_str(), NULL) || ERROR_ALREADY_EXISTS == GetLastError())
{
// writing buffer to file
if (!std::ofstream(fullFilePath).write(contents, ist.size))
{
return EXIT_FAILURE;
}
}
zip_fclose(f);
}
zip_close(za);
return EXIT_SUCCESS;
}
gerum was able to point me in the right direction. For anyone that is wondering or has the same issue, I had to open the ofstream in binary mode and that resolved the issue.
Original code:
// writing buffer to file
if (!std::ofstream(fullFilePath).write(contents, ist.size))
{
return EXIT_FAILURE;
}
Solution:
// writing buffer to file
if (!std::ofstream(fullFilePath, std::ios::binary).write(contents, ist.size))
{
return EXIT_FAILURE;
}

EOF sign in the middle of a textfile [duplicate]

I am writing a XOR encryption program which works fine during encryption but during decryption
the
char ca2=fgetc(f);
gets stuck at one point and no decryption takes place after that my best guess about the problem is (the encrypted file contains all sorts of characters ) as soon as fgetc reaches EOF mark which can be present before the actual end of the file it gets stuck there and stop reading the next characters .
is this some kind of limitation of getc() ? here is my rubbish code
int get_file_size(char filename[])
{
FILE *p_file = NULL;
p_file = fopen(filename,"rb");
fseek(p_file,0,SEEK_END);
int size = ftell(p_file);
fclose(p_file);
return size;
}
int endec(char filename[],char psdw[])
{
FILE *f;
int hashed=0,ed=0;
int inphash=inhash(psdw);
inphash=inphash%50;
f=fopen(filename,"r");
if(f==NULL)
printf("failed");
char temps[999999];
long int crs=0,j=0;
int filesz=get_file_size(filename);
printf("file size = %d\n\n",filesz);
while(1){
inphash=inphash+2;
char ca=(char)inphash;
char ca2=fgetc(f);
printf("%c\n",ca2);
if(crs>=filesz)
break;
temps[crs]= ca2 ^ ca;
crs++;
}
fclose(f);
printf("%d",strlen(temps));
FILE *fp;
fp=fopen(filename,"wt");
for(j=0;j<crs;j++){
putc (temps[j] , fp);
printf("%c",temps[j]);
}
fclose(fp);
}
Your problem is right here:
f=fopen(filename,"r");
You open the file for text reading, not for binary. Your file size function gets it right, but your decoder function does not.
The idiomatic way to read a file character by character using the C-style IO routines is like this:
f = fopen(filename, "rb");
if (!f)
// handle error
int c; // NOTE: int, not char!
while ( (c = fgetc(f)) != EOF )
{
// do something with 'c'
}
This idiom does not require you to get the file size as a separate operation. You can rewrite your XOR "encryption" routine with a simple loop of the above form. It will be much clearer and more concise.
Your entire decoder function could be rewritten as follows: (minus the debug code)
int endec(char filename[], char psdw[])
{
int inphash = inhash(psdw) % 50;
char temp[999999]; // really, should be std::vector<char>
FILE *f;
if ( (f = fopen(filename, "rb")) == NULL )
{
printf("opening for read failed\n");
return -1;
}
size_t crs = 0;
int c;
while ( (c = fgetc(f)) != EOF )
{
inphash += 2;
temp[crs++] = (char)(inphash ^ c);
}
fclose(f);
if ( (f = fopen(filename, "wt")) == NULL )
{
printf("opening for write failed\n");
return -1;
}
if (fwrite(temp, crs, 1, f) != crs)
{
printf("short write\n");
fclose(f);
return -1;
}
fclose(f);
return 0;
}
Not stellar error handling, but it is error handling.

Enabling C code to run as C++ code

I have a C program that finds duplicate files within a directory. The program is executed on the command line and passed 2 arguments. One is the parent directory, and argument two is the file name. It is working code in c, but I have a GUI and other files for "microservices" written in c++.
How would one call this C code from a c++ file?
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
//Compile: gcc dreamduplicatefinder.c -o dreamduplicatefinder.exe
//Run: ./dreamduplicateFinder.exe parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//printf(dir); //error could becuase trying to open shortcut or corrupt folder.
printf("%s\n",path);
perror("Failed to open directory");
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("%s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
//Step 2...
//size matches, files can be same; confirm it by matching both file contents...
int fdOriginal = open(originalFile, O_RDONLY);
int fdCurr = open(currFile, O_RDONLY);
if (fdOriginal == -1 || fdCurr == -1)
return false; //error occurred, not sure if file is duplicate...
//we will read file in small chunks and compare...
int chunkSize = 1024, bytesRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
while (true)
{
//read file in chunk...
bytesRead = read(fdOriginal, bufferOriginal, chunkSize);
if (bytesRead <= 0)
break; //end of file...
bytesRead = read(fdCurr, bufferCurr, bytesRead);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
My errors include: (from compareFiles function)
2x 'open' identifier not found
2x 'read' identifier not found
The working code for those curious.
Thank you #MarcusMüller & #JesperJuhl
#include "stdafx.h" //there is nothing in this header
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Compile: gcc <name of this file>.cpp -o <nameOfThisFile>.exe
//Run: <nameOfThisFile> parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//possibly trying to open shortcut or corrupt folder typically.
printf("Failed to open directory %s \n",path);
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("This is a duplicate! %s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
FILE* fdOriginal;
if (fdOriginal = fopen(originalFile, "r")) {
if (fdOriginal == NULL) { fputs("File error", stderr); return false; }
}
else return false; //error occurred, not sure if duplicate
FILE* fdCurr;
if (fdCurr = fopen(currFile, "r")) {
if (fdCurr == NULL) { fputs("File error", stderr); return false; }
}
else return false;
int chunkSize = 1024, objsRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
if (bufferOriginal == NULL) { fputs("Memory error for buff orig", stderr); exit(2); }
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
if (bufferCurr == NULL) { fputs("Memory error for buff curr", stderr); exit(2); }
while (true)
{
//read file in chunk...
//std::size_t fread( void* buffer, std::size_t size, std::size_t count, std::FILE* stream );
objsRead = fread(bufferOriginal, sizeof(char), chunkSize , fdOriginal);
if (objsRead <= 0)
break; //end of file...
objsRead = fread(bufferCurr, sizeof(char), objsRead, fdCurr);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
You usually just wouldn't do that. You'd wrap it in a C function, and compile it to an object file.
Then you'd include your C header with extern "C" {…}, and just call that function from C++.
When building your executable, you'd link in the object file containing your C function. Done!
Note: C isn't C++, and albeit your code not being illegal in C++ (as far as I can instantly tell), it does very "ugly" things (like #defineing true and false – ugh, that would already be a bad idea in C, to be honest). So, deal with it like you would deal with code in Fortran, or Java, or any other language that has a calling convention that you can use from C++ (which, usually, is the C calling convention): Just use it as an extern object.
Using ::open and ::read should cause the functions to be found.
You may also want to replace the C headers (like "string.h") with their C++ equivalent versions (like "cstring").
Your defines for true and false should also go. In C++ those are proper bools, not integers. This means the return type of CompareFiles should be changed to bool.
And you should wrap duplicateCount in an anonymous namespace - or return it from the function that updates it (either by returning a small struct with two ints, or by using a std::pair or std::tuple) - global variables are evil.

How to get page count of spl file

I'm working on a project that aims at monitoring print jobs. I'm copying spl and shd files from spool folder to a temporary folder and trying to parse the to get data I want such as number of printed pages, owner, date and time...
I'm using the following code to parse spooler spl files (get the emf file structure)
#include "stdafx.h"
#include <windows.h>
#include <winspool.h>
#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <iostream>
using namespace std;
BOOL AnalyseFile(const char* pszFileName);
void UPFM(const wchar_t pszInfo[])
{
wprintf(L"%s\n",pszInfo);
}
static char* ID_Func[] =
{
"EMF_HEADER", "EMF_POLYBEZIER", /*....*/
};
int main()
{
setlocale(LC_ALL,"");
const char* pszFileName = "~MyTempFolder\\00031.SPL";
if(!AnalyseFile(pszFileName))
printf("Analyse File Failed!");
else
printf("Analyse File Successed Completed!");
return 0;
}
BOOL AnalyseFile(const char* pszFileName)
{
BOOL bRet = FALSE;
DWORD dwStartPos = 0;
FILE * f ;
f = fopen("log.txt", "w");
FILE* pFile = fopen(pszFileName,"rb");
if(!pFile)
{
fprintf(f,"Open File Failed!");
return bRet;
}
/* =======================Headers================================ */
DWORD dwTmp = 0;
fseek(pFile,0,0);
fread(&dwTmp,sizeof(DWORD),1,pFile);
fread(&dwTmp,sizeof(DWORD),1,pFile);
dwStartPos = dwTmp;
fread(&dwTmp,sizeof(DWORD),1,pFile);
long pos = ftell(pFile);
fseek(pFile,dwTmp,SEEK_SET);
wchar_t pszInfo[256] = {0};
pszInfo[0] = L'(';
WORD wTmp;
int i;
for( i = 1;;i++)
{
fread(&wTmp,sizeof(wTmp),1,pFile);
if(!wTmp)
break;
memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));
}
pszInfo[i] = L')';
UPFM(pszInfo);
fseek(pFile,pos,SEEK_SET);
fread(&dwTmp,sizeof(DWORD),1,pFile);
fseek(pFile,dwTmp,SEEK_SET);
memset(pszInfo,0,sizeof(wchar_t)*256);
pszInfo[0] = L'(';
for(i = 1;;i++)
{
fread(&wTmp,sizeof(wTmp),1,pFile);
if(!wTmp)
break;
memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));
}
pszInfo[i] = L')';
UPFM(pszInfo);
/* ======================== Unknown datas ================================= */
fseek(pFile,dwStartPos,SEEK_SET);
fread(&dwTmp,sizeof(DWORD),1,pFile);
fread(&dwTmp,sizeof(DWORD),1,pFile);
/* ======================== Record datas ================================= */
DWORD dwTmp2 = 0;
for(int i=0;;i++)
{
pos = ftell(pFile);
fread(&dwTmp,sizeof(DWORD),1,pFile);
fread(&dwTmp2,sizeof(DWORD),1,pFile);
FILE *f;
f = fopen("log.txt", "a");
fprintf(f,"index: (%04d) type: 0x%04X size: %04d 0x%08X (%s)\n",i,dwTmp,dwTmp2,pos,ID_Func[dwTmp-1]);
fclose (f);
if(dwTmp == 0x0E)
{
break;
}
fseek(pFile,pos+dwTmp2,SEEK_SET);
}
fclose (f);
if(pFile) fclose(pFile);
bRet = TRUE;
return bRet;
}
Is there a way to calculate page count of the spl file with this method?
To calculate pages you have to count PAGE headers.
Here is a part of code which does it
SpoolFilename = Path.ChangeExtension(SpoolFilename, ".SPL")
'\\ Open a binary reader for the spool file
Dim SpoolFileStream As New System.IO.FileStream(SpoolFilename, FileMode.Open, FileAccess.Read)
Dim SpoolBinaryReader As New BinaryReader(SpoolFileStream, System.Text.Encoding.UTF8)
'Read the spooler records and count the total pages
Dim recNext As EMFMetaRecordHeader = NextHeader(SpoolBinaryReader)
While recNext.iType <> SpoolerRecordTypes.SRT_EOF
If recNext.iType = SpoolerRecordTypes.SRT_PAGE Then
_Pages += 1
End If
'SpoolfileReaderPerformaceCounter.Increment()
Call SkipAHeader(recNext, SpoolBinaryReader)
recNext = NextHeader(SpoolBinaryReader)
End While
Code gets filename, opens binary stream for it, and read header after header. If header code is SRT_PAGE, increment pages counter.
more details and source here
Here is how you do it in C++
struct EMFMetaRecordHeader {
long Seek;
SpoolerRecordTypes iType;
int Size;
};
if (splfile)
{
EMFMetaRecordHeader recNext = NextHeader(splfile);
while (recNext.iType != SpoolerRecordTypes::SRT_EOF)
{
if (recNext.iType == SpoolerRecordTypes::SRT_PAGE)
{
nPages++;
}
recNext = NextHeader(splfile);
}
nEmf_record_code = 0;
}
EMFMetaRecordHeader EMFSpoolfileReader::NextHeader(FILE* splfile){
EMFMetaRecordHeader recRet;
recRet.Seek = ftell(splfile);
fread(&recRet.iType, sizeof(int), 1, splfile);
if (feof(splfile))
{
recRet.iType = SpoolerRecordTypes::SRT_EOF;
return recRet;
}
fread(&recRet.Size, sizeof(int), 1, splfile);
return recRet;
}
splfile is a spool file pointer
more details and source here

How to read past EOF from getc?

I am writing a XOR encryption program which works fine during encryption but during decryption
the
char ca2=fgetc(f);
gets stuck at one point and no decryption takes place after that my best guess about the problem is (the encrypted file contains all sorts of characters ) as soon as fgetc reaches EOF mark which can be present before the actual end of the file it gets stuck there and stop reading the next characters .
is this some kind of limitation of getc() ? here is my rubbish code
int get_file_size(char filename[])
{
FILE *p_file = NULL;
p_file = fopen(filename,"rb");
fseek(p_file,0,SEEK_END);
int size = ftell(p_file);
fclose(p_file);
return size;
}
int endec(char filename[],char psdw[])
{
FILE *f;
int hashed=0,ed=0;
int inphash=inhash(psdw);
inphash=inphash%50;
f=fopen(filename,"r");
if(f==NULL)
printf("failed");
char temps[999999];
long int crs=0,j=0;
int filesz=get_file_size(filename);
printf("file size = %d\n\n",filesz);
while(1){
inphash=inphash+2;
char ca=(char)inphash;
char ca2=fgetc(f);
printf("%c\n",ca2);
if(crs>=filesz)
break;
temps[crs]= ca2 ^ ca;
crs++;
}
fclose(f);
printf("%d",strlen(temps));
FILE *fp;
fp=fopen(filename,"wt");
for(j=0;j<crs;j++){
putc (temps[j] , fp);
printf("%c",temps[j]);
}
fclose(fp);
}
Your problem is right here:
f=fopen(filename,"r");
You open the file for text reading, not for binary. Your file size function gets it right, but your decoder function does not.
The idiomatic way to read a file character by character using the C-style IO routines is like this:
f = fopen(filename, "rb");
if (!f)
// handle error
int c; // NOTE: int, not char!
while ( (c = fgetc(f)) != EOF )
{
// do something with 'c'
}
This idiom does not require you to get the file size as a separate operation. You can rewrite your XOR "encryption" routine with a simple loop of the above form. It will be much clearer and more concise.
Your entire decoder function could be rewritten as follows: (minus the debug code)
int endec(char filename[], char psdw[])
{
int inphash = inhash(psdw) % 50;
char temp[999999]; // really, should be std::vector<char>
FILE *f;
if ( (f = fopen(filename, "rb")) == NULL )
{
printf("opening for read failed\n");
return -1;
}
size_t crs = 0;
int c;
while ( (c = fgetc(f)) != EOF )
{
inphash += 2;
temp[crs++] = (char)(inphash ^ c);
}
fclose(f);
if ( (f = fopen(filename, "wt")) == NULL )
{
printf("opening for write failed\n");
return -1;
}
if (fwrite(temp, crs, 1, f) != crs)
{
printf("short write\n");
fclose(f);
return -1;
}
fclose(f);
return 0;
}
Not stellar error handling, but it is error handling.