I am reading a book that has some difficult vocabulary and I, in order to understand the book thoroughly, decided to collect and lookup those words am not acquainted with, spend sometime learning them, and re-read the book.
I have created an interactive console application specifically for this purpose. The program's main objectives are:
- Open a file for reading and writing
- Load data from file into linked list, if any, and sort them alphabetically.
- Accept input from user, and search through the list for matches to display on screen.
- If right key, is pressed, discard input. If Return key is pressed, save input to file, and if Escape key is pressed, cleanup and terminate program.
The key of interactivity is to show the user whether he added a word to the list or not, to avoid duplicates.
However, I have noticed that my implementation is hefty on the CPU, because when I run it, after short time, the fans become noisy, which make it hard for me to read and concentrate. Please advise on how can I optimize it and what improvements can be made.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
struct G_list {
char word[100];
struct G_list *ptr;
};
typedef struct G_list G_list;
void add_and_sort(const char *str, G_list **head);
void free_list(G_list *head);
int cmp_at_begn(char *str, const char *substr);
void traverse_list(char *str, G_list *head);
void ClearScreen(void *hConsole);
int main(void)
{
/* open file for reading and writing */
const char filename[] = "Book.txt";
FILE *fp = fopen(filename, "a+");
if (!fp) {
perror(filename);
return 1;
}
/* ... */
char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
const size_t len = strlen(alphabet);
/* allocate memory */
G_list **wlist = (G_list **)malloc(sizeof(G_list *) * len);
for (size_t i = 0; i < len; i++) {
wlist[i] = (G_list *)malloc(sizeof(G_list));
wlist[i]->ptr = NULL;
wlist[i]->word[0] = 0;
}
/* load data from file */
int c;
char load[100];
while (fgets(load, sizeof(load), fp)) {
load[strcspn(load, "\n")] = 0;
c = *load - 'a';
if (c < 0 || c >= len) {
continue;
}
add_and_sort(load, wlist + c);
}
/* recieve input and display data */
int index = 0;
char buf[100] = { 0 };
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
while (1) {
// if backspace, delete character and traverse list
if (GetAsyncKeyState(VK_BACK) == -32767) {
if (index) {
buf[--index] = '\0';
ClearScreen(hStdout);
printf("%s\n............\n", buf);
if (index) {
traverse_list(buf, wlist[*buf - 'a']);
}
}
}
// if right-arrow key, discard input
else if (GetAsyncKeyState(VK_RIGHT) == -32767) {
index = 0;
*buf = '\0';
ClearScreen(hStdout);
continue;
}
// if Enter, save to file
else if (GetAsyncKeyState(VK_RETURN) == -32767) {
if ((*buf - 'a') < 0 || (*buf - 'a') >= len) {
continue;
}
add_and_sort(buf, &wlist[*buf - 'a']);
fprintf(fp, "%s\n", buf);
index = 0;
*buf = '\0';
ClearScreen(hStdout);
continue;
}
// if escape, terminate the program
else if (GetAsyncKeyState(VK_ESCAPE) == -32767) {
break;
}
// accept alphabet-input and display matches if any
for (short i = 'A'; i <= 'Z'; i++) {
if (GetAsyncKeyState(i) == -32767) {
buf[index++] = (char)(i + 32);
buf[index] = '\0';
ClearScreen(hStdout);
printf("%s\n............\n", buf);
traverse_list(buf, wlist[*buf - 'a']);
}
}
}
/* release memory */
fclose(fp);
for (size_t i = 0; i < len; i++) {
free_list(wlist[i]);
}
free(wlist);
}
void add_and_sort(const char *str, G_list **head)
{
/* create node */
G_list *node = (G_list *)malloc(sizeof(G_list));
/* initialize node members */
strcpy(node->word, str);
node->ptr = NULL;
/* ... */
if ((*head)->word[0] == '\0') {
*head = node;
return;
}
else {
while (1) {
if (strcmp(str, (*head)->word) < 0) {
G_list *tmp = *head;
*head = node;
node->ptr = tmp;
break;
}
else {
head = &((*head)->ptr);
if (!*head) {
*head = node;
(*head)->ptr = NULL;
break;
}
}
}
}
}
void free_list(G_list *head)
{
G_list *tmp;
while (head) {
tmp = head;
head = head->ptr;
free(tmp);
}
}
// compare at beginning of line
int cmp_at_begn(char *str,
const char *substr)
{
if (!*str || !*substr) {
return (*str == *substr);
}
else {
while (*str && *substr) {
if (*str++ != *substr++) {
return 0;
}
}
}
return !(*substr);
}
void ClearScreen(void *hConsole)
{
COORD coordScreen = { 0, 0 }; // home for the cursor
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
// Get the number of character cells in the current buffer.
if (!GetConsoleScreenBufferInfo(hConsole, &csbi))
{
return;
}
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
// Fill the entire screen with blanks.
if (!FillConsoleOutputCharacter(hConsole, // Handle to console screen buffer
(TCHAR) ' ', // Character to write to the buffer
dwConSize, // Number of cells to write
coordScreen, // Coordinates of first cell
&cCharsWritten))// Receive number of characters written
{
return;
}
// Get the current text attribute.
if (!GetConsoleScreenBufferInfo(hConsole, &csbi))
{
return;
}
// Set the buffer's attributes accordingly.
if (!FillConsoleOutputAttribute(hConsole, // Handle to console screen buffer
csbi.wAttributes, // Character attributes to use
dwConSize, // Number of cells to set attribute
coordScreen, // Coordinates of first cell
&cCharsWritten)) // Receive number of characters written
{
return;
}
// Put the cursor at its home coordinates.
SetConsoleCursorPosition(hConsole, coordScreen);
}
void traverse_list(char *str, G_list *head)
{
int v = 0;
while (head) {
if (cmp_at_begn(head->word, str)) {
puts(head->word);
v = 1;
}
else {
if (v) break;
}
head = head->ptr;
}
}
cmp_at_begn(char *str, const char *substr)
looks like it compares if the sub-stringsubstr
startsstr
. If so, what is theif (!*str || !*substr) { return (*str == *substr);
for that is not detected in the rest of code? \$\endgroup\$