I want to load an array from a file that I previously saved. I ran the code below, but for some reason, when I loaded the array, the array length that I loaded was not the same as the length of the array that I had saved. How would I change the length for my load a file code so that it would work for any array length?
intarr_t* intarr_load_binary( const char* filename )
{
unsigned int len = 0;
FILE *f = fopen (filename, "rb");
fscanf (f, "%d", len);
intarr_t* newia = malloc (sizeof(intarr_t));
assert (newia);
newia->data = malloc (len*sizeof(int));
assert(newia->data);
newia->len = len;
if (f != NULL)
{
while (!feof(f))
{
fscanf (f, "%d", newia->data);
}
}
else
{
return NULL;
}
fclose (f);
return newia;
}
The struct that I used for the save/load is here:
typedef struct {
int* data;
unsigned int len;
} intarr_t;
The code that I used to save the file is here:
int intarr_save_binary( intarr_t* ia, const char* filename )
{
unsigned int len = ia->len;
FILE *f;
f = fopen (filename, "wb");
if (fwrite (ia->data, sizeof(int), len, f) == len)
{
return 0;
}
else
{
return 1;
}
fclose (f);
}
the code is writing no length (len) value as the first data item to the file
yet the code is reading a length (len) value
as if it were the first data item in the file.
this code is full of errors and oversights:
int intarr_save_binary( intarr_t* ia, const char* filename )
{
unsigned int len = ia->len;
FILE *f;
f = fopen (filename, "wb");
if (fwrite (ia->data, sizeof(int), len, f) == len)
{
return 0;
}
else
{
return 1;
}
fclose (f);
}
suggest using code similar to this:
int intarr_save_binary( intarr_t* ia, const char* filename )
{
int returnValue = 0;
unsigned int len = ia->len;
FILE *f;
if( NULL == (f = fopen (filename, "wb") )
{
perror( "fopen failed" );
returnValue = 1;
}
else if ( fwrite ( &len, sizeof(int), 1, f) == 1 )
{ // then write of length successful
if (fwrite (ia->data, sizeof(int), len, f) == len)
{
returnValue = 0; // indicate success
}
else
{ // else, write of data failed
returnValue = 3;
}
}
else
{ // else, failed to write len value to file
returnValue = 4;
}
fclose( f ); // cleanup (writes last buffer to file)
return( returnValue );
} // end function: intarr_save_binary
this code needs some work.
for instance because assert should not enabled in production code
and error conditions not being checked
and cleanup not being properly performed
if the program is ok to continue after an I/O error
by always returning NULL
then you could change the following,
to return NULL rather than exiting the program
intarr_t* intarr_load_binary( const char* filename )
{
unsigned int len = 0;
FILE *f = fopen (filename, "rb");
fscanf (f, "%d", len);
intarr_t* newia = malloc (sizeof(intarr_t));
assert (newia);
newia->data = malloc (len*sizeof(int));
assert(newia->data);
newia->len = len;
if (f != NULL)
{
while (!feof(f))
{
fscanf (f, "%d", newia->data);
}
}
else
{
return NULL;
}
fclose (f);
return newia;
}
suggest:
intarr_t* intarr_load_binary( const char* filename )
{
unsigned int len = 0;
FILE *f = NULL;
intarr_t* newia = NULL;
if( NULL == fopen (filename, "rb") )
{ // then, fopen failed
perror( "fopen failed" );
exit( EXIT_FAILURE );
} // end if
// implied else, fopen successful
if( NULL == (newia = malloc (sizeof(intarr_t)) )
{ // then malloc failed
perror( "malloc failed" );
fclose(f);
exit( EXIT_FAILURE );
} // end if
// implied else, malloc successful
if( (fread (&len, sizeof(int), 1, f) != 1 ) )
{ // then fread failed
perror( "fread failed" );
fclose(f);
free( newia );
exit( EXIT_FAILURE );
} // end if
// implied else, fread for len successful
newis->len = len;
if( NULL == (newia->data = malloc (len*sizeof(int)) ) )
{ // then malloc failed
perror( "malloc failed" );
fclose(f);
free( newia );
exit( EXIT_FAILURE );
} // end if
// implied else, malloc successful
if( fread( newia->data, sizeof(int), len, f ) != len )
{ // then, fread failed
perror( "fread failed" );
fclose(f);
free(newia->data)l
free(newia);
exit( EXIT_FAILURE );
} // end if
// implied else, fread successful
fclose (f);
return newia;
} // end function: intarr_load_binary
Related
I've to read character by character from a file and put each line in a String.
The problem is that i don't know the size of each line so eventually I've to reallocate the memory. So If I try a reallocation my program return error. Am I doing something wrong?
FILE * file = fopen(input,"r");
if(file != NULL){
char temp;
char * line;
line = (char *) malloc(sizeof(char) * 10);
int i = 0;
while((temp = fgetc(file)) != EOF){
if(temp == '\n'){
i = 0;
}
else{
if(i > strlen(line) - 2){
line = (char *) realloc(line,sizeof(line) * 10);
}
line[i] = (char) temp;
i++;
}
}
free(line);
fclose(file);
}
else{
}
the following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
outputs user error messages to stderr
outputs the text reason the system thinks an error occurred to stderr
documents why each header file is included
shows an example of how to handle the case where the user failed to enter a command line parameter (in this case a input file name)
makes use of size_t rather than int when passing parameters to malloc() and realloc()
and now, the proposed code:
#include <stdio.h> // fopen(), perror(), fclose() fprintf()
#include <stdlib.h> // exit(), EXIT_FAILURE, malloc(), realloc(). free()
int main( int argc, char *argv[] )
{
if( argc != 2 )
{
fprintf( stderr, "USAGE: %s <fileName>\n", argv[0] );
exit( EXIT_FAILURE );
}
FILE * file = fopen( argv[1], "r" );
if( !file )
{
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
int ch;
char * line = malloc( 10 );
if( !line )
{
perror( "malloc failed" );
fclose( file ); // cleanup
exit( EXIT_FAILURE );
}
// implied else, malloc successful
size_t lineLen = 10;
size_t i = 0;
while( (ch = fgetc(file)) != EOF )
{
if( ch == '\n' )
{
line[i] = '\0';
// do something with contents of line
i = 0;
}
else
{
if(i >= lineLen )
{
lineLen *= 2;
char * temp = realloc( line, lineLen );
if( !temp )
{
perror( "realloc failed" );
// cleanup
fclose( file );
free( line );
exit( EXIT_FAILURE );
}
line = temp;
}
line[i] = (char)ch;
i++;
}
}
free(line);
fclose(file);
}
Why do I get 0 bytes after a block of size 7 alloc'd error when I am still leaving space for '\0'?
I tried allocating and reallocating 7 bytes and kept the size variable going up by 5 so that there would always be at least 2 bytes left at the end when I add the null terminator, but I am still getting the valgrind error:
Invalid write of size 1:
0 bytes after a block of size 7 alloc'd
whenever I read or write to token, for example I get it on this line:
token[i] = read;
void parse_file(char file[]) {
char read = 0;
int size = 5;
char *token = NULL;
int i = 0;
FILE *fp = NULL;
token = malloc(7 * sizeof(char));
fp = fopen(file, "r");
if(fp == NULL) {
fprintf(stderr, "%s: No such file or directory\n", file);
free(token);
fclose(fp);
return;
}
read = fgetc(fp);
while(read != EOF) {
if(i == size) {
token = realloc(token, 7 * sizeof(char));
size += 5;
}
if(isalpha(read)) {
read = (char) tolower(read);
token[i] = read;
}
else {
if(isalpha(token[0])) {
token[i] = '\0';
put(token);
}
else {
free(token);
}
token = calloc(7,sizeof(char));
size = 5;
i = 0;
read = fgetc(fp);
continue;
}
read = fgetc(fp);
i++;
}
free(token);
fclose(fp);
}
the following proposed code:
cleanly compiles
eliminates unnecessary code/logic
performs the desired functionality
properly checks for errors
incorporates the comments to the OPs question
incorporates the comments to this answer
and now the proposed code: (EDITED)
#include <stdlib.h> // exit(), EXIT_FAILURE, realloc(), free()
#include <stdio.h> // FILE, fprintf(), fopen(), fgetc(), perror()
#include <ctype.h> // isalpha(), tolower()
#include <errno.h> // errno
#include <string.h> // strerror()
// prototypes
void parse_file(char fileName[]);
void parse_file(char fileName[])
{
int byteRead = 0;
size_t size = 0;
char *token = NULL;
size_t i = 0;
FILE *fp = NULL;
fp = fopen(fileName, "r");
if( !fp )
{
fprintf(stderr, "Can't open %s: %s\n", fileName, strerror(errno));
exit( EXIT_FAILURE );
}
while( (byteRead = fgetc(fp) ) != EOF )
{
char *temp = NULL;
if(i >= size)
{
temp = realloc(token, 7 + size );
if( !temp )
{
perror( "realloc failed" );
free( token );
fclose( fp );
exit( EXIT_FAILURE );
}
// implied else, realloc successful
size += 7;
token = temp;
}
if( isalpha(byteRead) )
{
byteRead = tolower(byteRead);
token[i] = (char)byteRead;
i++;
}
else if( i )
{
token[i] = '\0';
puts(token);
free( token );
i = 0;
size = 0;
}
}
free(token);
fclose(fp);
}
The tasks are the following:
/* LAB 6 TASK A*/
/*
Save the entire array ia into a file called 'filename' in a binary
file format that can be loaded by intarr_load_binary(). Returns
zero on success, or a non-zero error code on failure. Arrays of
length 0 should produce an output file containing an empty array.
*/
/* LAB 6 TASK B*/
/*
Load a new array from the file called 'filename', that was
previously saved using intarr_save_binary(). Returns a pointer to a
newly-allocated intarr_t on success, or NULL on failure.
*/
For A, my code is the following:
int intarr_save_binary( intarr_t* ia, const char* filename )
{
int returnValue = 0;
unsigned int len = ia->len;
FILE *f;
if( NULL == (f = fopen (filename, "wb") ))
{
perror( "fopen failed" );
returnValue = 1;
}
else if ( fwrite ( &len, sizeof(int), 1, f) == 1 )
{ // then write of length successful
if (fwrite (ia->data, sizeof(int), len, f) == len)
{
returnValue = 0; // indicate success
}
else
{ // else, write of data failed
returnValue = 3;
}
}
else
{ // else, failed to write len value to file
returnValue = 4;
}
fclose( f ); // cleanup (writes last buffer to file)
return( returnValue );
}
And for B, my code is the following:
intarr_t* intarr_load_binary( const char* filename )
{
unsigned int len = 0;
FILE *f = NULL;
intarr_t* newia = NULL;
if( NULL == fopen (filename, "rb") )
{ // then, fopen failed
perror( "fopen failed" );
exit( EXIT_FAILURE );
} // end if
// implied else, fopen successful
if( NULL == (newia = malloc (sizeof(intarr_t)))){
perror( "malloc failed" );
fclose(f);
exit( EXIT_FAILURE );
} // end if
// implied else, malloc successful
if( (fread (&len, sizeof(int), 1, f) != 1 ) )
{ // then fread failed
perror( "fread failed" );
fclose(f);
free( newia );
exit( EXIT_FAILURE );
} // end if
// implied else, fread for len successful
newia->len = len;
if( NULL == (newia->data = malloc (len*sizeof(int)) ) )
{ // then malloc failed
perror( "malloc failed" );
fclose(f);
free( newia );
exit( EXIT_FAILURE );
} // end if
// implied else, malloc successful
if( fread( newia->data, sizeof(int), len, f ) != len )
{ // then, fread failed
perror( "fread failed" );
fclose(f);
free(newia->data);
free(newia);
exit( EXIT_FAILURE );
} // end if
// implied else, fread successful
fclose (f);
return newia;
} // end function: intarr_load_binary
Can anyone please tell me why my code results in a segmentation fault? Thank you very much.
In the code for B, NULL is passed to fread() in the line
if( (fread (&len, sizeof(int), 1, f) != 1 ) )
so it may lead to Segmentation Fault.
To fix this, assign the file pointer returned from fopen() to f:
Change
if( NULL == fopen (filename, "rb") )
to
if( NULL == (f = fopen (filename, "rb")) )
Also check if the parameters passed to the functions are valid.
I'm trying to build a gym program for a project in college.
We're doing it in C in a Linux environment.
I do not have problems reading from the file, but when I try to update the file, if I print to file with '\n' at the end, it puts double enters between line. And if I don't, it puts all the data in one line.
What should I do?
i.e. I've added an example of a function that reads from the file and one that updates it.
Employees** Init_Gym_emp(Employees** emp, int* num) {
FILE* f = fopen("Gym Employees.txt", "r");
if (f == NULL) {
printf("Failed opening the file. Exiting!\n");
exit(1);
}
char c = '\0';
while (fscanf(f, "%c", &c) == 1) {
if (c == '\n') num[0]++;
}
if (num[0] > 0) num[0]++;
fseek(f, 0, SEEK_SET);
Employees* tmp = (Employees*)malloc(sizeof(Employees));
if (tmp == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
emp = (Employees**)malloc(sizeof(Employees*)*(num[0]));
if (emp == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
int i = 0;
tmp->first_name = (char*)malloc(sizeof(char)* 20);
tmp->last_name = (char*)malloc(sizeof(char)* 20);
tmp->user_name = (char*)malloc(sizeof(char)* 20);
tmp->password = (char*)malloc(sizeof(char)* 20);
tmp->user_type = (char*)malloc(sizeof(char)* 20);
while (fscanf(f, "%20[^#]%*c%20[^#]%*c%ld%*c%20[^#]%*c%10[^#]%*c%20[^#]%*2c", tmp->first_name, tmp->last_name, &tmp->id, tmp->user_name, tmp->password, tmp->user_type) == 6) {
emp[i] = (Employees*)malloc(sizeof(Employees));
if (emp[i] == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
emp[i]->first_name = (char*)malloc(sizeof(char)* (strlen(tmp->first_name) + 1));
emp[i]->last_name = (char*)malloc(sizeof(char)* (strlen(tmp->last_name) + 1));
emp[i]->user_name = (char*)malloc(sizeof(char)* (strlen(tmp->user_name) + 1));
emp[i]->password = (char*)malloc(sizeof(char)* (strlen(tmp->password) + 1));
emp[i]->user_type = (char*)malloc(sizeof(char)* (strlen(tmp->user_type) + 1));
strcpy(emp[i]->first_name, tmp->first_name);
strcpy(emp[i]->last_name, tmp->last_name);
strcpy(emp[i]->user_name, tmp->user_name);
strcpy(emp[i]->password, tmp->password);
strcpy(emp[i]->user_type, tmp->user_type);
emp[i]->id = tmp->id;
i++;
}
free(tmp->first_name);
free(tmp->last_name);
free(tmp->user_name);
free(tmp->password);
free(tmp->user_type);
free(tmp);
fclose(f);
return emp;
}
void update_Gym_emp(Employees** emp, int* num) {
remove("Gym Employees.txt");
FILE* f = fopen("Gym Employees.txt", "w");
if (f == NULL) {
printf("Failed opening the file. Exiting!\n");
exit(1);
}
int i;
for (i = 0; i < num[0]; i++) {
fprintf(f, "%s#%s#%ld#%s#%s#%s#", emp[i]->first_name, emp[i]->last_name, emp[i]->id, emp[i]->user_name, emp[i]->password, emp[i]->user_type);
}
fclose(f);
}
here is one way the code could be written.
Notice that the first function reads from a file and creates a 2D list of Employee records
Notice that the second function writes to that same file from the 2D list of Employee records
Notice the string lengths for the call to fscanf() are one less than the related input buffer size because when scanning a string, the function will always append a NUL byte to the string
Strongly suggest defining the Employee struct as follows rather than the way it is currently defined:
struct Employee
{
char first_name[20];
char last_name[20];
long id;
char user_name[20];
char password[10];
char user_type[20];
};
Now, the proposed code
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Employee
{
char *first_name;
char *last_name;
long id;
char *user_name;
char *password;
char *user_type;
};
typedef struct Employee Employees;
// prototypes
void cleanup( Employees **emp );
Employees** Init_Gym_emp(Employees** emp, int* num)
{
FILE* f = fopen("Gym Employees.txt", "r");
if (f == NULL)
{
perror("fopen for Gym Employees.txt for input failed");
exit(1);
}
// implied else, fopen successful
char buffer[1024]; // input work area
// get count of records in file
while ( fgets(buffer, sizeof(buffer), f) )
{
num[0]++;
}
// step back to beginning of file to read/parse each record
if( fseek(f, 0, SEEK_SET) )
{ // then fseek failed
perror( "fseek to start of file failed" );
fclose( f );
exit( EXIT_FAILURE );
}
// implied else, fseek successful
// allocate array of pointers large enough for all records in input file
if( NULL == (*emp = malloc((size_t)num[0] * sizeof(Employees*) ) ) )
{
perror("malloc for array of pointers to Employee records failed");
cleanup( emp );
fclose( f );
exit(1);
}
// clear all pointers to NULL, to make cleanup easier
memset( *emp, '\0', (size_t)num[0] * sizeof( Employees* ));
char first_name[20];
char last_name[20];
long id;
char user_name[20];
char password[10];
char user_type[20];
int i = 0;
while( i < num[0] && fgets( buffer, sizeof(buffer), f ) )
{
if( 6 != sscanf(buffer,
"%19[^#]# %19[^#]# %ld# %19[^#]# %9[^#]# %19[^#]",
first_name,
last_name,
&id,
user_name,
password,
user_type) )
{ // then sscanf failed
perror( "sscanf for fields of emp record failed" );
cleanup( emp );
fclose( f );
exit( EXIT_FAILURE );
}
// implied else, sscanf successful
// get room for one employee record
if( NULL == (emp[i] = malloc(sizeof(Employees)) ) )
{
perror("malloc for new employee record failed");
cleanup( emp );
fclose( f );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
(*emp)[i].first_name = strdup( first_name );
(*emp)[i].last_name = strdup( last_name );
(*emp)[i].user_name = strdup( user_name );
(*emp)[i].password = strdup( password );
(*emp)[i].user_type = strdup( user_type );
(*emp)[i].id = id;
i++;
} // end while
fclose(f);
return emp;
} // end function: Init_Gym_emp
void update_Gym_emp(Employees** emp, int* num)
{
FILE* f = fopen("Gym Employees.txt", "w");
if (f == NULL)
{
perror("fopen for Gym Employees.txt for write failed");
cleanup( emp );
exit(1);
}
// implied else, fopen successful
for (int i = 0; i < num[0]; i++)
{
fprintf(f, "%s#%s#%ld#%s#%s#%s#",
(*emp)[i].first_name,
(*emp)[i].last_name,
(*emp)[i].id,
(*emp)[i].user_name,
(*emp)[i].password,
(*emp)[i].user_type);
}
fclose(f);
} // end function: update_Gym_emp
It may be a bit confusing trying to explain what I'm doing, but the context is: I code on the side for a MUD (multi user dungeon) and I created a struct for player kills when they occur - which write to a .sav file, and then load every time the mud reboots into that struct.
The struct is like so:
struct pkill_data
{
PKILL_DATA *next;
char *name;
char *victim;
sh_int wl;
sh_int location;
sh_int vclass;
sh_int vrace;
sh_int clevel;
sh_int vlevel;
time_t date;
};
And the create functions:
void update_pkill( CHAR_DATA* ch, CHAR_DATA* victim, int wl, int location, time_t date){
PKILL_DATA* pkill;
if (IS_NULLSTR( ch->name ) || IS_NULLSTR( victim->name ))
return;
pkill = new_pkill();
pkill->name = str_dup(ch->name);
pkill->victim= str_dup(victim->name);
pkill->vclass = victim->class;
pkill->vrace = victim->race;
pkill->date = date;
pkill->vlevel = victim->level;
pkill->clevel = ch->level;
pkill->wl = wl;
pkill->location = location;
add_pkill(pkill);
save_pkills();
}
PKILL_DATA* pkill_list;
void add_pkill( PKILL_DATA* pkill){
if (pkill_list == NULL){
pkill_list = pkill;
pkill->next = NULL;
}
else{
pkill->next = pkill_list;
pkill_list = pkill;
}
}
And the write/read functons:
void fwrite_pkill( FILE* fp, PKILL_DATA* pkill){
fprintf( fp, "#PKILL %s~ %s~ %d %d %d %d %d %d %ld\n",
pkill->name,
pkill->victim,
pkill->clevel,
pkill->vlevel,
pkill->vrace,
pkill->vclass,
pkill->wl,
pkill->location,
pkill->date);
}
void fread_pkill( FILE* fp, PKILL_DATA* pkill ){
pkill->name = fread_string( fp );
pkill->victim = fread_string( fp );
pkill->clevel = fread_number( fp );
pkill->vlevel = fread_number( fp );
pkill->vrace = fread_number( fp );
pkill->vclass = fread_number( fp );
pkill->wl = fread_number( fp );
pkill->location = fread_number( fp );
pkill->date = fread_number( fp );
}
void save_pkills(){
FILE *fp;
PKILL_DATA* pkill = pkill_list;
char path[128];
fclose( fpReserve );
sprintf( path, "%s%s", PKILL_DIR, PKILL_FILE);
if ( ( fp = fopen( path, "w" ) ) == NULL ){
fp = fopen( NULL_FILE, "r" );
fclose (fp);
perror( path );
return;
}
/* loop through pkills and save */
for (; pkill; pkill = pkill->next ){
fwrite_pkill( fp, pkill);
}
fprintf( fp, "#END\n" );
fclose( fp );
fpReserve = fopen( NULL_FILE, "r" );
}
And the load functions:
PKILL_DATA* get_pkill( char* name, char* victim ){
PKILL_DATA* pkill = pkill_list;
for (;pkill; pkill = pkill->next){
if (str_cmp(name, pkill->name))
continue;
else if (!IS_NULLSTR(victim) && str_cmp(pkill->victim, victim))
continue;
return pkill;
}
return NULL;
}
void load_pkills(){
PKILL_DATA* pkill;
FILE *fp;
char path[128];
char letter;
char* word;
fclose( fpReserve );
sprintf( path, "%s%s", PKILL_DIR, PKILL_FILE);
if ( ( fp = fopen( path, "r" ) ) == NULL ){
fp = fopen( NULL_FILE, "r" );
fclose (fp);
perror( path );
return;
}
for (;;){
letter = fread_letter( fp );
if ( letter != '#' ){
bug( "load_pkill: # not found.", 0 );
exit( 1 );
}
word = feof( fp ) ? "END" : fread_word( fp );
if ( !str_cmp(word, "END"))
break;
else if (!str_cmp(word, "PKILL")){
pkill = new_pkill();
fread_pkill( fp, pkill );
add_pkill( pkill );
}
}
fclose( fp );
fpReserve = fopen( NULL_FILE, "r" );
prune_pkall();
}
All of the above info may be too much info. Pretty much, the basic outline is I save everything to a file, which loads into a struct/table whenever the mud reboots or when a pkill is created, so I can pull from that list at any given time.
I want to be able to view these pkills by date created, (which is pkill->date), after they've been added into the buffer for viewing. I'm unsure how to do a bubble list with a buffer or how to sort a buffer before printing to the user.
below, add_buf is just a bool used elsewhere:
bool add_buf(BUFFER *buffer, char *string)
{
int len, oldsize = buffer->size;
char *oldstr = buffer->string;
if (buffer->state == BUFFER_OVERFLOW)
return FALSE;
len = strlen(buffer->string) + strlen(string) + 1;
while (len >= buffer->size)
{
buffer->size = get_size(buffer->size + 1);
{
if (buffer->size == -1)
{
buffer->size = oldsize;
buffer->state = BUFFER_OVERFLOW;
bug("buffer overflow past size %d",buffer->size);
return FALSE;
}
}
}
if (buffer->size != oldsize)
{
buffer->string = alloc_mem(buffer->size);
strcpy(buffer->string,oldstr);
free_mem(oldstr,oldsize);
}
strcat(buffer->string,string);
return TRUE;
}
And onto the ACTUAL question - how can I use a bubble sort within a string that's in a struct? This is what I have so far:
PKILL_DATA* pkill;
BUFFER* buffer;
char buf[MIL];
buffer = new_buf();
for (pkill = pkill_list ; pkill ; pkill = pkill->next ){
if (str_cmp(pkill->name, argument))
continue;
sprintf(buf, "%s %s %d %d %d %d %d %d %ld\n\r", pkill->name, pkill->victim, pkill->clevel, pkill->vlevel, pkill->vrace, pkill->vclass, pkill->wl, pkill->location, pkill->date);
}
page_to_char(buf_string(buffer),ch);
free_buf(buffer);
As one might could imagine, it's not sorted at all in any way, and lists them from top of file to bottom of file, or however which way they were loaded into the string/struct in the first place.