Program printing Windows paths? - c

I can't figure out why my program is outputting seemingly random Windows paths as shown below.
\maven, : ORS=4 (id: 4677968)
m32\cmd.exe, ò: Æ (id: 5525087)
, : (id: 4653392)
It does this when looping through an array of structures I have and displaying the values inside. Funny thing is though that it works perfectly. If I add 5 entries, it still displays all of them accurately. Yet it prints the random paths shown above before everything else.
I'm going to attach the whole program because I honestly don't know where to narrow the problem down to. I'm new to C and especially new to manually handling memory. So that may be where the problem lies. Thanks ahead of time.
The program:
#include<stdio.h>
#include<string.h>
// Define structures
typedef struct Contact
{
int id;
char fname[24];
char lname[24];
char number[16];
} Contact;
typedef struct Phonebook
{
Contact* contacts;
int contacts_length;
int id_tracking;
} Phonebook;
// Define prototypes
Phonebook addContact(Phonebook);
Phonebook removeContact(Phonebook); // removeContact() prompts the user for information about whom they want to remove.
void viewContacts(Phonebook);
void exportContacts(Phonebook);
Contact findEntry(int, Phonebook);
Phonebook removeEntry(int, Phonebook); // removeEntry() can be called to explicitly remove the entry with the passed in integer id.
int main()
{
// Define variables
int menuChoice = 0, status = 1;
Phonebook phonebook;
phonebook.contacts = (Contact*) malloc(sizeof(Contact));
// Check memory allocation
if(phonebook.contacts == NULL)
{
printf("\nFatal error: Out of memory... now exiting.");
return;
}
// Handle the user
do
{
// Begin collecting and assigning data
printf("\nContact Menu\n");
printf("\n(1.) Add contact");
printf("\n(2.) Remove contact");
printf("\n(3.) View contacts");
printf("\n(4.) Export contacts");
printf("\n(5.) Exit");
printf("\n\nPlease choose a menu option (enter the number): ");
scanf("%d", &menuChoice);
// Interpret menu choice
switch(menuChoice)
{
case 1:
// Begin adding contact
phonebook = addContact(phonebook);
status = 1;
break;
case 2:
phonebook = removeContact(phonebook);
status = 1;
break;
case 3:
viewContacts(phonebook);
status = 1;
break;
case 4:
exportContacts(phonebook);
status = 1;
break;
case 5:
// Free memory
free(phonebook.contacts);
// See ya!
printf("\nGoodbye!");
status = 0;
break;
default:
printf("I'm sorry, I didn't quite understand that. Please try again.");
status = 1;
break;
}
}
while(status != 0);
// Return 0 for exit
return 0;
}
Phonebook addContact(Phonebook phonebook)
{
// Clear screen first
system("cls");
// Define contact and set random id
Contact entry;
entry.id = phonebook.id_tracking;
phonebook.id_tracking++;
// Obtain information
printf("First name (24 characters max): ");
scanf("%s", &entry.fname);
printf("Last name (24 characters max): ");
scanf("%s", &entry.lname);
printf("Telephone number (recommended format: ###-###-####): ");
scanf("%s", &entry.number);
// Handle memory allocation
if(phonebook.contacts_length > 1)
{
phonebook.contacts = (Contact*) realloc(phonebook.contacts, sizeof(Contact) * (phonebook.contacts_length + 1));
}
// Save the contact to the array and count up
phonebook.contacts[phonebook.contacts_length] = entry;
phonebook.contacts_length++;
printf("Contact saved!\n");
return phonebook;
}
Phonebook removeContact(Phonebook phonebook)
{
// First check to make sure they have saved contacts
if(phonebook.contacts_length < 1)
{
// No contacts saved, tell them
printf("\nYou haven't saved any contacts.\n");
return;
}
// Define variables
int i, chosenId = 0;
// Display contacts with their ids
for(i = 0; i < phonebook.contacts_length; i++)
{
Contact entry = phonebook.contacts[i];
printf("\n%s, %s (id: %d)", entry.lname, entry.fname, entry.id);
}
// Handle removal
printf("\n\nPlease enter the ID of the contact you would like to remove: ");
scanf("%d", &chosenId);
// Remove
Phonebook updatedPhonebook = removeEntry(chosenId, phonebook);
printf("Contact removed!\n");
return updatedPhonebook;
}
void viewContacts(Phonebook phonebook)
{
// First check to make sure they have saved contacts
if(phonebook.contacts_length < 1)
{
// No contacts saved, tell them
printf("\nYou haven't saved any contacts.\n");
return;
}
// Define variables
int i;
// Display contacts with their ids
for(i = 0; i < phonebook.contacts_length; i++)
{
Contact entry = phonebook.contacts[i];
printf("\n%s, %s: %s (id: %d)", entry.lname, entry.fname, entry.number, entry.id);
}
printf("\n");
return;
}
/*
* Experimenting with I/O in C
*/
void exportContacts(Phonebook phonebook)
{
// First check to make sure they have saved contacts
if(phonebook.contacts_length < 1)
{
// No contacts saved, tell them
printf("\nYou have no contacts to be exported.\n");
return;
}
// Define and initialize variables
int i;
char outputName[] = "contacts.txt";
FILE *contactFile = fopen(outputName, "w");
// Print message
printf("\nExporting contacts to .txt file... ");
// Print to the file
for(i = 0; i < phonebook.contacts_length; i++)
{
Contact entry = phonebook.contacts[i];
fprintf(contactFile, "%s, %s (id: %d): %s\n", entry.lname, entry.fname, entry.id, entry.number);
}
// Close the file
fclose(contactFile);
// Done
printf("Done!");
printf("\nData exported to contacts.txt located where this program was launched.");
}
Contact findEntry(int id, Phonebook phonebook)
{
int i;
for(i = 0; i < phonebook.contacts_length; i++)
{
Contact entry = phonebook.contacts[i];
if(entry.id == id)
{
return entry;
}
}
}
Phonebook removeEntry(int id, Phonebook phonebook)
{
// Define variables
int i, positionToFree;
// Search for the index of the entry to remove
for(i = 0; i < phonebook.contacts_length; i++)
{
Contact entry = phonebook.contacts[i];
if(entry.id == id)
{
positionToFree = i; // This is the position to be freed
}
// We've found what we need, break now
break;
}
// Loop starting at that entry and remove
for(i = positionToFree; i < phonebook.contacts_length; i++)
{
Contact temp = phonebook.contacts[i + 1];
phonebook.contacts[i] = temp;
}
// Count down for contacts_length
phonebook.contacts_length--;
// Return the updated contacts
return phonebook;
}

This sounds like undefined behaviour.
You never initialise phonebook.contacts_length. It could have any value. When you go to add an entry, it's quite possible that the realloc call fails. You don't check the return value, so you wouldn't know.
Bad juju.

Related

Losing struct values after assigning 'next' and 'prev' struct pointers

I am creating a Book Management system in which the user can search by the title or the author of the book. The problem I am running into is when I try to get the values from the books. When searching for the author, all I get are symbols and an infinite for loop, instead of any of the authors that are already saved in config.
I am more than certain that it is something wrong with the way I am storing these values, but am not sure.
main.c:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char * argv[]) {
struct Books books;
// Config works fine and dandy, don't need to worry about this
cfg = read_config(cfg, "test-cfg.cfg");
for (int size = 0; size < cfg.valuesSize; size++) {
// Gets values from the config and assigns the values to the struct Books
books.id = atoi(cfg.values[size][0][1]);
strcpy(books.name, cfg.values[size][1][1]);
strcpy(books.author, cfg.values[size][2][1]);
strcpy(books.description, cfg.values[size][3][1]);
// Assign prev and next in Books struct
struct Books nextBook;
books.next = &nextBook;
nextBook.prev = &books;
books = *books.next;
}
// This throws Frontend screens at the user
// While also pipelining to backend functions
while(true) {
int welcome = 1;
switch (welcome) {
case 1: {
struct Books booksFound[cfg.valuesSize];
int booksFoundPtr = 0;
int searchOption = 2;
getchar();
if (searchOption==2) {
printf("Enter the Author: ");
char bookAuthor[AUTHOR_MAX];
fgets(bookAuthor, AUTHOR_MAX, stdin);
// Here is where the code screws up.
// It gives me random symbols instead of the book author's name
for (; books.id >= 1; books = *books.prev) {
if (strncmp(books.author, bookAuthor, strlen(books.author))==0) {
booksFound[booksFoundPtr] = books;
booksFoundPtr++;
}
}
}
// Used for showing all matching book authors
for (int i = 0; i < booksFoundPtr; i++) {
printf("ID:%d\nTITLE:%s\nAUTHOR:%s\nDESCRIPTION:%s\n", booksFound[i].id, booksFound[i].name, booksFound[i].author, booksFound[i].description);
char option[2] = {'\0'};
fgets(option, 2, stdin);
switch(atoi(option)) {
case 1: {
if (i+1 == booksFoundPtr) {
i--;
}
break;
}
case 2: {
if (i-1 < 0) {
i--;
break;
}
else {
i-=2;
}
break;
}
default: {
break;
}
}
}
// Rewinds books for next use
while(books.id != cfg.valuesSize) {
books = *books.next;
}
continue;
}
default: {
continue;
}
}
break;
}
return 0;
}
books.h
#define NAME_MAX 60
#define AUTHOR_MAX 100
#define DESCRIPTION_MAX 240
struct Books {
int id;
char name[NAME_MAX];
char author[AUTHOR_MAX];
char description[DESCRIPTION_MAX];
struct Books * next;
struct Books * prev;
};

Searching for a string in a struct

The code below is a code that will track my product costs and remaining quantity.The problem I'm facing with is I can't search the code by
if(g[n].name == search[10])
The out put keep showing
"Item not found"
Im a beginner of c language and was hope to learn more. Please correct my code and send it here so that I can know why my code is wrong.
#include <stdio.h>
struct product
{
char name[10];
int quantity;
float costs;
};
void fn_search (struct product g[]);
int main ()
{
int n;
struct product g[4];
strcpy(g[0].name,"aa1");
g[0].quantity = 10;
g[0].costs = 1;
strcpy(g[1].name,"bb2");
g[1].quantity = 10;
g[1].costs = 2;
strcpy(g[2].name,"bb3");
g[2].quantity = 10;
g[2].costs = 3;
fn_search (g);
}
void fn_search (struct product g[10])
{
int n;
char search[10];
printf("Search>> ");
scanf("%s",&search[10]);
for (n=0;n<4;n++)
{
if(g[n].name == search[10])
{
printf ("\ncosts = NTD%.2f",g[n].costs);
printf ("\nquantity = %d\n",g[n].quantity);
}
else
{
printf("\nItem not found.");
break;
}
}
}
Two bugs:
Incorrect use of scanf :
scanf("%s",&search[10]); --> scanf("%9s", search);
Note: scanf("%9s", &search[0]); is also fine but the above is the common way.
Incorrect string compare :
if(g[n].name == search[10]) --> if(strcmp(g[n].name, search) == 0)
Also notice that you never initialized g[3] but fn_search checks it.
Then this part:
else
{
printf("\nItem not found.");
break;
}
means that you break the for loop as soon as an item doesn't match. In other words: Currently you only compare against g[0]
You don't want that! Check all items before printing "Item not found".
So the for loop should be more like:
for (n=0;n<4;n++)
{
if(strcmp(g[n].name, search) == 0)
{
printf ("\ncosts = NTD%.2f",g[n].costs);
printf ("\nquantity = %d\n",g[n].quantity);
return; // Exit function when match is found
}
}
// When execution arrives here, there was no matching element
printf("\nItem not found.");
Finally:
void fn_search (struct product g[10])
^^
why ??
Either do
void fn_search (struct product g[])
or
void fn_search (struct product *g)

Function casts itself c, after it has been casted twice

I've got a university project where I have to write a database of workers. I decided to use dynamic array of structures:
struct data
{
char id[50];
char name[50];
char surname[50];
char account_num[50];
double net_pension,taxed_pension;
};
int main()
{
int current_size=0;
struct data *database;//creating a table
database=(struct data*)malloc(1*sizeof(struct data));//memory allocation
menu(database);//running menu
return 0;
}
function menu
void menu(struct data *database)
{
int current_size=1;
int input=0;
char inpt[512];
do
{
printf("Input function or input help for list of avaible commands \n");
fgets(inpt,511,stdin);
input=mod_input(inpt);
if(input==404)
{
printf("Function does not exist \n");
}
else if(input==1)
{
print_result(database,current_size);
}
else if(input==2)
{
add_element(database,&current_size);
}
else if(input==3)
{
modify_element(database,current_size);
}
else if(input==4)
{
sort_table(database, current_size);
}
else if(input==5)
{
search(database,current_size);
}
else if(input==6)
{
hilfe();
}
else if(input==7)
{
search_by_col(database,current_size);
}
input=8;
}
while(input!=0);
}
decides what we want to do, for example writing "add" will start my problematic function, which is supposed to add new records
void add_element(struct data *database,int *size)
{
int subflag=0;
char inpt[50];
int place=((*size)-1);
int pass=(*size);
printf("%i",pass);
if((*size)!=1)
{
modify_element(database,(*size));
}
do
{
printf("Input unical ID \n");
fgets(inpt,50,stdin);
if(does_exist(inpt,database,pass)==1)
{
subflag=1;
strncpy(database[place].id,inpt,50);
}
else
{
printf("ID exists");
}
}
while(subflag==0);
subflag=0;
do
{
printf("Input name \n");
fgets(inpt,50,stdin);
if(is_word(inpt)==1)
{
subflag=1;
strcpy(database[place].name,inpt);
}
}
while(subflag==0);
subflag=0;
do
{
printf("Input surname \n");
fgets(inpt,50,stdin);
if(is_word(inpt)==1)
{
subflag=1;
strcpy(database[place].surname,inpt);
}
}
while(subflag==0);
subflag=0;
do
{
printf("Input account number \n");
fgets(inpt,50,stdin);
if(is_accnum(inpt)==1)
{
subflag=1;
strcpy(database[place].account_num,inpt);
}
}
while(subflag==0);
subflag=0;
do
{
printf("Input net gain \n");
fgets(inpt,50,stdin);
if(is_num(inpt)==true)
{
printf("%d",atof(inpt));
subflag=1;
database[place].net_pension=atof(inpt);
}
}
while(subflag==0);
subflag=0;
do
{
printf("Input taxed gain \n");
fgets(inpt,50,stdin);
if(is_num(inpt)==true)
{
subflag=1;
database[place].taxed_pension=atof(inpt);
}
}
while(subflag==0);
printf("record added \n");
if((*size)==1)
(*size)++;
}
function modify_size reallocs memory, does_exist ensures, that id's are unique, is acc_num, num and word checks input for given rules. They all work perfectly when you use function first time. But after you try to add second one "record added" does not display and function add runs from the beginning. I ahve no idea why. That is the main problem. Secondary one is menu, because when you input "print" it runs add_element. Code that converts input is:
int mod_input(char function[])
{
printf(function);
if(strcmp(function,"modify")==1)
return 3;
else if(strcmp(function,"sort")==1)
return 4;
else if(strcmp(function,"search")==1)
return 5;
else if(strcmp(function,"help")==1)
return 6;
else if(strcmp(function,"add")==1)
return 2;
else if(strcmp(function,"print")==1)
return 1;
else if(strcmp(function,"search_by_column")==1)
return 7;
return 404;
}
Thank you in advance for help. Also I know that some parts could be done better, but for now, I try to just force it to work.
whole programme
lab3.c and header
Intuitively, what you need to do is pass around a pointer to a pointer to your array, not just a pointer to it. And that's what BLUEPIXY was trying to get at in a comment.
The problem is that if you do x = realloc(y, new_size), there's no guarantee that x will be equal to y.
In particular, database = realloc(database, new_size) inside a subroutine may leave the subroutine with a different value for database than the one passed in as an argument. The caller still has the old value for database.
The results are undefined, and may include worse than what you got.
What you want is something like
struct data *datap;
struct data **database = &datap;
*database = malloc(sizeof(struct data))
With corresponding changes all the way down - basically pass database, and set *database = realloc(...) when you get to that stage.
Also, make sure you update *size appropriately at the same time.

Process exited with return value 255, w/pointers to structures

I have some functions that should allow me to manage a structure which was allocated dynamically. The allocation of the memory and the input of data in those is no real problem, though my program stops when it reaches a certain line of code: (No warning or problems detected)
if(codeV == p_vendite[ctrl_j].p_venditore[ctrl_i].codVenditore)
This line is in the function called VenditeProdotto(Vendite *p_vendite).
Here's the important part of the code (defining structures)
typedef struct _Venditore {
int codVenditore;
int codProdotto;
int qty;
} Venditore;
typedef struct _Vendite{
int mmGG;
Venditore *p_venditore;
} Vendite;
void AggiungiVendita (Vendite *p_vendite);
void VenditeProdotto(Vendite *p_vendite);
void VenditeVenditore(Vendite *p_vendite);
...
Here's main():
int main() {
int check, i, count, flag, choice;
Vendite *p_Vendite;
...
...
p_Vendite = (Vendite*) calloc(numVenditori,sizeof(Vendite));
...
...
p_Vendite->p_venditore = (Venditore*)calloc(numVenditori,sizeof(Venditore));
/*menu*/
flag = TRUE;
do{
choice = menu();
switch (choice) {
case 1 : AggiungiVendita(p_Vendite); break;
...
case 3 : VenditeProdotto(p_Vendite); break;
case 4 : VenditeVenditore(p_Vendite); break;
...
}
} while (flag == TRUE);
return 0;
}
And here are the functions:
void AggiungiVendita (Vendite *p_vendite) {
int flag, check, answer;
i = 0;
do{
/*input of struct - codVenditore,codProdotto,qty*/
...
check = scanf("%d", &(p_vendite[j].p_venditore[i].codVenditore));
...
/*input*/
check = scanf("%d", &(p_vendite[j].p_venditore[i].codProdotto) );
...
/*controllo sull'input*/
check = scanf("%d", &(p_vendite[j].p_venditore[i].qty) );
...
...
//asking to redo or quit
} while(flag == TRUE && i < numVenditori);
return;
}
int menu() {
//just a standard menu, no problem here
...
return choice;
}
void VenditeProdotto(Vendite *p_vendite) {
int check = 0, codeP = 0, ctrl_i = 0, ctrl_j = 0; //ctrl_i,ctrl_j are increasing variables and I use them to search among the structures
...//input, continues after
Where I find the debug error: (line 3 after this)
for(ctrl_j = 0; ctrl_j < numVendite; ctrl_j++) {
for(ctrl_i = 0; ctrl_i < numVenditori; ctrl_i++) {
if (codeP == p_vendite[ctrl_j].p_venditore[ctrl_i].codProdotto)
printf("\nSeller %d, quantity sold: %d in day %d", p_vendite[ctrl_j].p_venditore[ctrl_i].codVenditore, p_vendite[ctrl_j].p_venditore[ctrl_i].qty, ctrl_j+1);
else
continue;
}
}
return;
}
Basically I don't know if it's really legit to use the first line of code that I've talked about, with . instead of ->, but if I try to change the syntax I get detected errors. Any ideas?
At first I thought about something like (p_vendite+ctrl_j)->(p_venditore+ctrl_i)->codProdotto, since it's a pointer but it doesn't seem working.
There are a couple of obvious bugs:
Allocation of Vendite
You're allocating numVenditori elements for p_Vendite, but later on you are iterating numVendite times over the same pointer:
p_Vendite = (Vendite*) calloc(numVenditori,sizeof(Vendite));
...
for(ctrl_j = 0; ctrl_j < numVendite; ctrl_j++) {
for(ctrl_i = 0; ctrl_i < numVenditori; ctrl_i++) {
if (codeP == p_vendite[ctrl_j].p_venditore[ctrl_i].codProdotto)
The allocation should read:
p_Vendite = (Vendite*) calloc(numVendite,sizeof(Vendite));
or as I would prefer it:
p_Vendite = calloc (numVendite, sizeof *p_Vendite);
Allocation of Venditore
p_Vendite->p_venditore = (Venditore*)calloc(numVenditori,sizeof(Venditore));
You're only allocating the p_venditore element for one of your Vendite structs. You need to allocate all of them in a loop:
for (int j = 0; j < numVendite; j++) {
p_Vendite[j].p_venditore = (Venditore*)calloc(numVenditori,sizeof(Venditore));
// And check for allocation errors
}

Sending "cls" to dos causes exception

Due to a problem posting, my last question (duplicate of this) was closed.
Background is provided at the end so you can get straight to the problem.
I have a text-based program to help learn vocabulary or anything else (basically simulates flash cards, but flashes up the ones you don't know more often). It seemed to work fine while I was testing it, until I got fed up of the constant backlog of text on the screen, so I implemented a (somewhat unportable) clear screen routine.
Then it started throwing up exceptions, and I put in all sorts of debugging code to try and track it down.
Well... I managed to narrow it down to the following command on line 445:
system("cls");
How can this command cause an exception? Does anyone know a workaround?
I've run this in command prompts on both Windows Vista and Windows 7 with the same result.
Complete source in case anyone wants to compile it themselves or take a look through:
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <process.h>
#include <ctype.h>
#include <string.h>
#define DINPUTFILENAME "vtdb.~sv"
#define DOUTPUTFILENAME "vtdb.~sv"
#define MAXINTVALUE 2147483647
#define MAXTEXTLENGTH 256
#define N2LTONORM 5
#define NORMTON2L 3
#define NORMTOKNOWN 5
#define KNOWNTONORM 2
#define KNOWNTOOLD 2
#define OLDTONORM 1
struct vocab
{
int index; //identifies the entry in the list, allowing it to be selected by use of a random number
char * question;//pointer to question text
char * answer;//pointer to the answer text, which is required for the response to be considered correct
char * info;//pointer to optional extra text giving advice such as to how to format the response
char * hint;//pointer to optional text giving a clue to the answer
int right;//indicates whether counter is counting correct or incorrect responses
int counter;//counts how many times in a row the answer has been correct/incorrect
int known;//indicates to what level the vocab is known, and thus to which list it belongs (when loading/saving)
struct vocab * next;//pointer to next in list
};
struct listinfo//struct holds head, tail and the number of entries for the n2l, norm, known and old lists
{
struct vocab * head;
int entries;
struct vocab * tail;
};
struct listinfo n2l, norm, known, old;
int n2l_flag; //Prevents 'need to learn's coming up twice in a row
int maxtextlength = MAXTEXTLENGTH; //allows use of this #define within text strings
FILE * inputfile;
FILE * outputfile;
void getrecordsfromfile(char * inputfilename,char separator);//load
char * readtextfromfile(int maxchars,char separator);//get text field from file
int readnumberfromfile(int maxvalue,char separator);//get integer field from file
struct vocab * addtolist(struct vocab * newentry, struct listinfo * list);//add given (already filled in) vocab record to given list
int removefromlist(struct vocab * entry, struct listinfo * list,int freeup);//remove given entry from given list. Also destroy record if freeup is true
void reindex (struct listinfo * list);//necessary to stop gaps in the numbering system, which could cause random vocab selection to fail
int writeliststofile();//save
void testme();//main code for learning vocab, including options menu
char * gettextfromkeyboard(char * target,int maxchars);//set given string (char pointer) from keyboard, allocating memory if necessary
int getyesorno();//asks for yes or no, returns true (1) if yes
void testrandom();//code keeps causing exceptions, and as it's so random, I'm guessing it's to do with the random numbers
void getrecordsfromfile(char * inputfilename,char separator)
{
int counter = 0;
struct vocab * newvocab;
struct listinfo * newvocablist;
if (!(inputfile = fopen(inputfilename, "r")))
{
printf("Unable to read input file. File does not exist or is in use.\n");
}
else
{
printf("Opened input file %s, reading contents...\n",inputfilename);
while (!feof(inputfile))
{
newvocab = (struct vocab *)malloc(sizeof(struct vocab));
if (!newvocab)
{
printf("Memory allocation failed!\n");
return;
}
else
{
newvocab->question=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->answer=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->info=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->hint=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->right=readnumberfromfile(1,separator);
newvocab->counter=readnumberfromfile(0,separator);
newvocab->known=readnumberfromfile(3,separator);
switch (newvocab->known)
{
case 0: newvocablist = &n2l;break;
case 1: newvocablist = &norm;break;
case 2: newvocablist = &known;break;
case 3: newvocablist = &old;break;
}
addtolist(newvocab,newvocablist);
if (newvocab->question==NULL||newvocab->answer==NULL)
{
printf("Removing empty vocab record created from faulty input file...\n");
removefromlist(newvocab,newvocablist,1);
}
else counter++;
}
}
fclose(inputfile);
printf("...finished.\n%i entries read from %s.\n\n",counter,inputfilename);
}
return;
}
char * readtextfromfile(int maxchars,char separator)
{
int i=0;
char ch;
char * target = (char *)malloc(maxchars+1); //allocate memory for new string
if (!target) {printf("Memory allocation failed!\n");return 0;}//return 0 and print error if alloc failed
ch=getc(inputfile);
if (ch==separator){free(target);return NULL;}//if field is blank (zero-length), return null pointer
while (isspace(ch))
{
ch = getc(inputfile);//cycle forward until you reach text
if (ch == separator||ch=='\n'||ch==EOF) {free(target);return NULL;}//if no text found(reached ~ before anything else), return null pointer
}
if (ch=='"') //Entry is in quotes (generated by excel when exporting to .csv and field contains a comma)
{
ch=getc(inputfile);//move to next character after the quotes
while (i<maxchars && ch!='"' && ch!='\n')//stop when you reach the end quotes, end of line, or when text too long
{
target[i++]=ch;
ch = getc(inputfile); //copy name from file to target, one char at a time
}
}
else //entry is not in quotes, so char is currently first letter of string
{
while (i<maxchars && ch!=separator && ch!='\n')//stop when you reach separator, end of line, or when text too long
{
target[i++]=ch;
ch = getc(inputfile); //copy name from file to target, one char at a time
}
}
target[i] = '\0';//terminate string
return target;
}
int readnumberfromfile (int maxvalue,char separator)
{
int number, i=0;
char ch;
char * buff = (char *)malloc(11);
if (!buff) {printf("Memory allocation failed!\n");return 0;}//return 0 and print error if alloc failed
if (!maxvalue) maxvalue=MAXINTVALUE;
ch=getc(inputfile);
while (!isdigit(ch))
{
ch = getc(inputfile);//cycle forward until you reach a digit
if (ch == separator||ch=='\n'||ch==EOF) {printf("Format error in file\n");return 0;}//if no number found(reached ~ before digit), print error and return 0
}
while (i<11 && ch!=separator && ch!='\n')//stop when you reach '~', end of line, or when number too long
{
buff[i++]=ch;
ch = getc(inputfile); //copy number from file to buff, one char at a time
}
buff[i] = '\0';//terminate string
number = atoi(buff)<=maxvalue ? atoi(buff) : maxvalue;//convert string to number and make sure it's in range
free(buff);
return number;
}
struct vocab * addtolist(struct vocab * newentry, struct listinfo * list)
{
if (!list->head)//if head is null, there is no list, so create one
{
list->head = list->tail = newentry;//this is the new head and tail
list->entries = newentry->index = 1;
newentry->next = NULL;// FISH! not sure if this is necessary, but just be sure...
}
else//just appending to the list
{
list->tail->next = newentry;//adjust current tail to point to new entry
list->tail = newentry;//make the new entry the new tail
newentry->index=++list->entries;
newentry->next = NULL;
}
return newentry;
}
int removefromlist(struct vocab * entry, struct listinfo * list,int freeup)
{
struct vocab * prev;
if (list->head == entry) //if entry being deleted is first in the list
{
if (list->tail == entry) //if entry is only item in the list
{
list->head = list->tail = NULL;
}
else //if first in list, but not last
{
list->head = entry->next;
}
}
else //entry is not first in list, so set prev to point to previous entry
{
prev = list->head;
while (prev->next!=entry)
{
prev=prev->next;
if (!prev)
{
printf("Trying to delete an entry from a list it's not in!!\n");
return 0;
}
}
if (list->tail == entry)//if entry is at the end of the list
{
list->tail = prev;
list->tail->next = NULL;
}
else //if entry is somewhere in middle of list
{
prev->next=entry->next;
}
}//this entry is now not pointed to in any list
list->entries--;
/*following line removed because it could theoretically break a list if the entry was removed from a list after it had been added to another
entry->next = NULL;//and doesn't point to anything either*/
reindex(list);
if (freeup) //if freeup is set, this also wipes the record and frees up the memory associated with it
{
free(entry->question);
free(entry->answer);
free(entry->info);
free(entry->hint);
free(entry);
}
return 1;
}
void reindex (struct listinfo * list)
{
int counter = 1;
struct vocab * workingentry = list->head;
while (workingentry)
{
workingentry->index = counter++;
workingentry=workingentry->next;
}
if (list->entries!=counter-1) printf("Reindexing Error!\n");
}
int writeliststofile()
{
int i,counter=0;
struct listinfo * list;
struct vocab * entry;
if (!(outputfile = fopen(DOUTPUTFILENAME, "w")))
{
printf ("Error accessing output file!\n");
return 0;
}
else
{
printf("Saving...\n");
for (i=0;i<=3;i++)
{
switch (i)
{
case 0: list = &n2l;break;
case 1: list = &norm;break;
case 2: list = &known;break;
case 3: list = &old;break;
default: printf("Loop Error!\n");break;
}
entry=list->head;
while (entry!=NULL)
{
if (counter) fprintf(outputfile,"\n");
fprintf(outputfile,"%s~%s~%s~%s~%i~%i~%i",entry->question,entry->answer,entry->info,entry->hint,entry->right,entry->counter,i);
entry=entry->next;
counter++;
}
}
fclose(outputfile);
printf("...finished. %i entries saved.\n",counter);
return 1;
}
}
void testme()
{
int list_selector, entry_selector, bringupmenu = 0, testagain=1;
char testmenuchoice = '\n';
char * youranswer = (char *)malloc(MAXTEXTLENGTH+1);
struct listinfo * currentlist;
struct vocab * currententry;
if (!youranswer) {printf("Memory allocation error!\n");return;}
while (testagain)
{
fprintf(stderr,"Start of 'testagain' loop\nClearing screen...\n");
system("cls");
//select a list at random, using the percentage probabilities in the if statements. FISH! Can this be done with a switch and ranges?
fprintf(stderr,"Assigning list selector to random value...");
list_selector = (((float)rand() / 32768) * 100)+1;
fprintf(stderr,"assigned list selector value %i\nAssigning list pointer...",list_selector);
if (list_selector<33) currentlist = &n2l;
if (list_selector>32&&list_selector<95) {n2l_flag=0;currentlist=&norm;} //use norm list and cancel n2l flag (not cancelled with other lists)
if (list_selector>94&&list_selector<100) currentlist = &known;
if (list_selector==100) currentlist = &old;
fprintf(stderr,"assigned list pointer %x\nModifying pointer...",currentlist);
//do a little control over random selection
if (currentlist==&n2l && n2l_flag) {currentlist=&norm; n2l_flag=0;} //if n2l list was used last time as well (flag is set), use entry from the norm list instead
if (currentlist==&n2l) n2l_flag = 1; //is using n2l this time, set flag so it won't be used next time as well
if (currentlist->entries==0) currentlist = &norm;//if current list is empty, default to normal list
if (currentlist->entries==0 && !n2l_flag) currentlist = &n2l;//if normal list is empty, try n2l list if it wasn't used last time
if (currentlist->entries==0 && list_selector%10==5) currentlist = &old;//if list is still empty, in 10% of cases try old list
if (currentlist->entries==0) currentlist = &known;//in the other 90% of cases, or if old is empty, use the known list
if (currentlist->entries==0) currentlist = &old;//if known list is empty, try the old list
if (currentlist->entries==0) {currentlist = &n2l;n2l_flag=1;}//if old list is empty, use n2l list EVEN if it was used last time
if (currentlist->entries==0) {printf("No entries in list!");return;} //if list is STILL empty, abort
fprintf(stderr,"modified list pointer\nAssigning entry selector...");
//we now have the desired list of words with at least one entry, let's select an entry at random from this list
entry_selector = (((float)rand() / 32767) * currentlist->entries)+1;
fprintf(stderr,"assigned entry selector value %i\nAssignig pointer...",entry_selector);
currententry = currentlist->head;
fprintf(stderr,"set entry pointer to head, going to loop to it...\n");
while (currententry->index!=entry_selector)
{
currententry = currententry->next;//move through list until index matches the random number
if (currententry==NULL) {printf("Indexing error!\nCurrent list selector: %i, entries: %i, entry selector: %i\n",list_selector,currentlist->entries,entry_selector);return;}//in case not found in list
}
fprintf(stderr,"Looped, testing.\n");
printf("Translate the following:\n\n\t%s\n\n",currententry->question);
if (!currententry->info) printf("There is no additional information for this entry.\n");
else printf("Useful Info: %s\n\n",currententry->info);
printf("Your Translation:\n\n\t");
gettextfromkeyboard(youranswer,MAXTEXTLENGTH);
if (!strcmp(youranswer,currententry->answer))//if you're right
{
printf("Yay!\n");
if(currententry->right) currententry->counter++;
else currententry->right = currententry->counter = 1;
if (currententry->counter>2) printf("You answered correctly the last %i times in a row!\n",currententry->counter);
//make comments based on how well it's known, and move to a higher list if appropriate
if (currentlist==&n2l && currententry->counter>=N2LTONORM)
{
removefromlist(currententry,currentlist,0);
printf("Looks like you know this one a little better now!\nIt will be brought up less frequently.\n");
currententry->counter = 0;
addtolist(currententry,&norm);
}
if (currentlist==&norm && currententry->counter>=NORMTOKNOWN)
{
removefromlist(currententry,currentlist,0);
printf("Looks like you know this one now!\nIt will be brought up much less frequently.\n");
currententry->counter = 0;
addtolist(currententry,&known);
}
if (currentlist==&known && currententry->counter>=KNOWNTOOLD)
{
removefromlist(currententry,currentlist,0);
printf("OK! So this one's well-learnt.\nIt probably won't be brought up much any more.\n");
currententry->counter = 0;
addtolist(currententry,&old);
}
}
else //if you're wrong
{
printf("\nSorry, the correct answer is:\n\n\t%s\n\n",currententry->answer);
if(!currententry->right) currententry->counter++;
else {currententry->right = 0; currententry->counter = 1;}
if (currententry->counter>1) printf("You've got this one wrong the last %i times.\n",currententry->counter);
if (currentlist==&norm && currententry->counter>=NORMTON2L)
{
removefromlist(currententry,currentlist,0);
printf("This one could do with some learning...\n");
currententry->counter = 0;
addtolist(currententry,&n2l);
}
if (currentlist==&known && currententry->counter>=KNOWNTONORM)
{
removefromlist(currententry,currentlist,0);
printf("OK, perhaps you don't know this one as well as you once did...\n");
currententry->counter = 0;
addtolist(currententry,&norm);
}
if (currentlist==&old && currententry->counter>=OLDTONORM)
{
removefromlist(currententry,currentlist,0);
printf("This old one caught you out, huh? It will be brought up a few more times to help you remember it.\n");
currententry->counter = 0;
addtolist(currententry,&norm);
}
}
fprintf(stderr,"Tested, options menu?\n");
printf("Type 'o' for options or strike enter for another question\n");
testmenuchoice = getchar();
fprintf(stderr,"Got choice\n");
if (tolower(testmenuchoice)=='o') bringupmenu = 1;
fprintf(stderr,"set menuflag\n");
if (testmenuchoice!='\n') while (getchar()!='\n')getchar();
fprintf(stderr,"cleared getchar\n");
while (bringupmenu)
{
system("cls");
printf("Current Entry:\n\nQuestion: %s\nAnswer: '%s'\n",currententry->question,currententry->answer);
if (currententry->info) printf("Info: %s\n",currententry->info); else printf("No info.\n");
if (currententry->hint) printf("Hint: %s\n\n",currententry->hint); else printf("No hint.\n\n");
printf("Options Menu:\n\nType q to modify the question phrase displayed for translation.\nType a to change the answer phrase you must provide.\nType i to add/modify additional info for this entry.\nType h to add/modify the hint for this entry.\nType p to mark this entry as high priority to learn.\nType d to delete this entry from the database.\nType x to end testing and return to the main menu.\n\n");
testmenuchoice=getchar();
while (getchar()!='\n') getchar();
switch (testmenuchoice)
{
case 'q': printf("Enter new question text for this entry (max %i chars):\n",maxtextlength);
currententry->question=gettextfromkeyboard(currententry->question,MAXTEXTLENGTH);
break;
case 'a': printf("Enter new answer text for this entry (max %i chars):\n",maxtextlength);
currententry->answer=gettextfromkeyboard(currententry->answer,MAXTEXTLENGTH);
break;
case 'i': printf("Enter new info for this entry (max %i chars):\n",maxtextlength);
currententry->info=gettextfromkeyboard(currententry->info,MAXTEXTLENGTH);
break;
case 'h': printf("Enter new hint for this entry (max %i chars):\n",maxtextlength);
currententry->hint=gettextfromkeyboard(currententry->hint,MAXTEXTLENGTH);
break;
case 'p': if(currentlist=&n2l)printf("Already marked as priority!\n");
else
{
removefromlist(currententry,currentlist,0);
currententry->counter = 0;
currentlist=&n2l;
addtolist(currententry,currentlist);
printf("Entry will be brought up more often\n");
}
break;
case 'd': printf("Are you sure you want to delete this entry?\nOnce you save, this will be permanent!(y/n)");
if (getyesorno()) {removefromlist(currententry,currentlist,1);printf("Entry deleted!\n");bringupmenu=0;}
else printf("Entry was NOT deleted.\n");
break;
case 'x': bringupmenu = testagain = 0;
break;
default: printf("Invalid choice.\n");
}
if (bringupmenu)
{
printf("Select again from the options menu? (y/n)");
bringupmenu = getyesorno();
}
if (!bringupmenu&&testagain)
{
printf("Continue testing? (y/n)");
testagain = getyesorno();
}
}
fprintf(stderr,"End of 'testagain' loop.\n Clearing Screen...");
system("cls");
}
free(youranswer);
// getchar();
return;
}
char * gettextfromkeyboard(char * target,int maxchars)
{
int i =0;
char ch;
if (!target)//if no memory already allocated (pointer is NULL), do it now
{
target=(char *)malloc(maxchars+1);
if (!target) {printf("Memory allocation failed!");return NULL;} //return null if failed
}
ch = getchar();
if (ch=='\n') {free(target);return NULL;}//if zero length, free mem and return null pointer
while (!isalnum(ch))//cycle forward past white space
{
ch=getchar();
if (ch=='\n') {free(target);return NULL;}//if all white space, free mem and return null pointer
}
while (ch!='\n' && i<maxchars)
{
target[i++]=ch;
ch=getchar();
}
target[i]='\0';
return target;
}
int getyesorno()
{
char yesorno = '\n';
while (toupper(yesorno)!='Y'&&toupper(yesorno)!='N')
{
yesorno=getchar();
if (toupper(yesorno)!='Y'&&toupper(yesorno)!='N') printf("Invalid choice. You must enter Y or N:\n");
}
while (getchar()!='\n') getchar();
if (toupper(yesorno)=='Y') return 1;
else return 0;
}
void testrandom()
{
return;
}
int main(int argc, char* argv[])
{
char * inputfilename = DINPUTFILENAME;
char * outputfilename = DOUTPUTFILENAME;
char separator = '~';
char menuchoice = '\0';
n2l.entries = norm.entries = known.entries = old.entries = 0;
srand((unsigned)time(NULL));
fprintf(stderr,"Start...\n");
printf("Loading...\nLoad default database? (y/n)");
if (!getyesorno())
{
printf("Default file type is .~sv. Import .csv file instead? (y/n)");
if (getyesorno())
{
separator = ',';
printf("Enter name of .csv file to import:\n");
}
else
{
printf("Enter name of .~sv file to load:\n");
}
inputfilename = gettextfromkeyboard(inputfilename,256);
}
getrecordsfromfile(inputfilename,separator);
while (menuchoice!='x')
{
printf("Welcome to the Vocab Test, version C!\n\nMain menu:\n\n\tt: Test Me!\n\ts: Save\n\tx: Exit\n\n");
menuchoice = getchar();
while (getchar()!='\n') getchar();
switch (tolower(menuchoice))
{
case 'x': break;
case 't': testme(); break;
case 's': writeliststofile();break;
case 'w': testrandom(); break;
default: printf("Invalid choice. Please try again.\n"); break;
}
system("cls");
}
system("cls");
printf("Bye for now!\n\nPress enter to exit.");
getchar();
fprintf(stderr,"Successfully closed\n");
return 0;
}
I tried adding the output of stderr on a typical run, but it makes the body too long. Also tried adding it as an answer, but:
Users with less than 100 reputation can't answer their own question for 8 hours after asking. You may self-answer in 7 hours. Until then please use comments, or edit your question instead.
Background: I made my first foray into programming earlier this year, and decided I wanted to start with C before moving on to C++, Java, and perhaps Python and C#.
To get me started in C, after the obligatory hello world, I wrote a small game (text based, also including the "cls" command), and then moved onto this little vocab tester, which was to help me learn Indonesian while I was away in Austria speaking German :-D. I eventually got exasperated at the cls crash and haven't programmed since. I really want to pick it back up, so I'm starting here with this question.
Have you tried printing '\f'? That's the "formfeed" character.
EDIT: I've had a closer look at your code, and there's some stuff going on that I don't like. :-)
For example, in gettextfromkeyboard, if you enter only whitespace, it'll free target, even if that was non-NULL on entry.
In this line:
inputfilename = gettextfromkeyboard(inputfilename,256);
it passes inputfilename, which points to a constant string, into gettextfromkeyboard. Trying to free that is a bad idea.
I also have my doubts about while (getchar()!='\n') getchar();.
Suppose the input is "ABC\n".
The condition will consume and return 'A', the body of the loop will consume 'B', the condition will consume and return 'C', and the body of the loop will consume '\n'.
Try one of the many curses, ncurses, etc. packages around. There should be one somewhere on the web for your version of C, if it is not too uncommon. It should handle all kinds of text screen functionality, including clearing the screen and it is pretty portable.
Possibly system('cls') has been deprecated? Here is another way of doing it.
The code runs fine for me and the CLS command works. Non-reproduceable crashes often indicate memory corruption. I'd say a good place to look first is your readtextfromfile function since it will overwrite the input buffer if a file contains 256 chars.

Resources