You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

561 lines
19 KiB
C

//cmdtypist: the main of cmdtypist
/*
Program title: CMDTYPIST
Author: Chiatiah Calson
License: GPL 3 or later versions
Date and Time: 5 July 2017 @ 10:40PM
Program Size: 2.8MB
*/
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<ctype.h>
#include <unistd.h>
//cmdtypist.c: Implementing the main
#include"functions_for_cmd_typist.h"//function prototypes and global variables.
#include"display.h"//display fixing
#include"utils.h"//useful functions
#include"files.h"//file manipulations
#include"config.h"//configuration
#include"terminal.h"//manipulating the terminal
char argv[5][18];
int main(int argc, char **argv)//argc=command line counter, argv=pointer to pointer to character(command line arguments)
{
redirect();
lmt_pg_size();
name_display();
read_message_conf();//welcome message for first time users.
int lesson_choice=1;//global variable to hold the number corresponding to the chosen lesson.
//char commands[][10]={"0ls","1edituser","2myown","3--help","4man","5mkuser","6mkrand","7mkstd", "8select","9chblk","10sound","11--off","12--on","13cch","14reset","15timeset","16atv","17raw"};
char commands[][10]={"ls","edituser","myown","--help","man","mkuser","mkrand","mkstd", "select","chblk","sound","--off","--on","cch","reset","timeset","atv","raw"};
if(argc<1||argc>3)
{
fprintf(stderr, "%s\n", "Invalid number of arguments to cmdtypist");
exit(EXIT_FAILURE);
}
switch(argc)//switching command depending on the number of command line arguments.
{
case 1:
if(read_myown_config()!=0)
{
lesson_list();//list all lessons present.
select_lesson(argc,&lesson_choice);
}
main_play(argc,&lesson_choice);
break;
case 2:
if(strcmp(argv[1],commands[2])==0)
{
write_myown_config(0);
main_play(argc,&lesson_choice);
}
else if(strcmp(argv[1],commands[13])==0)
{
write_myown_config(1);
lesson_list();
select_lesson(argc,&lesson_choice);
system("clear");
main_play(argc,&lesson_choice);
}
else if(strcmp(argv[1],commands[3])==0)
{
FILE *fp;
if((fp=fopen("help.md","r"))==NULL)
{
fprintf(stderr, "%s\n", "Fatal Error, Some files are missing");
exit(EXIT_FAILURE);
}
while((ch=getc(fp))!=EOF)
printf("%c", ch);
puts("");
if(fclose(fp))
{
fprintf(stderr, "%s\n", "Fatal Error, Unable to close some files\n");
exit(EXIT_FAILURE);
}
}
else if(strcmp(argv[1],commands[4])==0)
{
FILE *fp;
if((fp=fopen("Readme.txt","r"))==NULL)
{
fprintf(stderr, "%s\n", "Fatal Error, Some files are missing");
exit(EXIT_FAILURE);
}
while((ch=getc(fp))!=EOF)
printf("%c", ch);
}
else if(strcmp(argv[1],commands[6])==0)
write_conf_mode(0);
else if(strcmp(argv[1],commands[7])==0)
write_conf_mode(1);
else if(strcmp(argv[1],commands[14])==0)
{
printf("%s","Will reset to default; continue? [y/n]:");
if(get_only_char()=='n')
exit(EXIT_SUCCESS);
reset_default_config(argv[2],argc);
printf("%s\n","Settings reset to default");
exit(EXIT_SUCCESS);
}
else if(strcmp(argv[1],commands[16])==0)
{
adapt_to_ver();
exit(EXIT_SUCCESS);
}
else if(strcmp(argv[1],commands[0])==0)
{
lesson_list();
select_lesson(argc,&lesson_choice);
main_play(argc,&lesson_choice);
}
else fprintf(stderr, "%s\n", "Ensure the second argument is corrrectly spelled");
break;
case 3:
if(strcmp(argv[1],commands[5])==0)
{
test_new_user(argv[2]);
lesson_list();
select_lesson(argc,&lesson_choice);
main_play(argc,&lesson_choice);
}
else if(strcmp(argv[1],commands[14])==0&&strcmp(argv[2],commands[17])==0)
{
reset_default_config(argv[2],argc);
exit(EXIT_SUCCESS);
}
else if(strcmp(argv[1],commands[8])==0)
{
if(read_myown_config()==1)
lesson_choice = range_verifier(is_integral(argv[2],argc));
else
{
lesson_choice=1;
fprintf(stderr, "%s\n\n", "You have been redirected here because you are typing in \"myown\"");
}
main_play(argc,&lesson_choice);
}
/*
else if((strcmp(argv[1],"sound")==0)&&strcmp(argv[2],"--on")==0)//modifying system sound
{
sound_config_write(1);
exit(EXIT_SUCCESS);
}
else if((strcmp(argv[1],"sound")==0)&&strcmp(argv[2],"--off")==0)
{
sound_config_write(0);
exit(EXIT_SUCCESS);
}*/
else if((strcmp(argv[1],commands[10])==0))//modifying system sound
{
if(strcmp(argv[2],commands[12])==0)
{
sound_config_write(1);
exit(EXIT_SUCCESS);
}
else if(strcmp(argv[2],commands[11])==0)
{
sound_config_write(0);
exit(EXIT_SUCCESS);
}
else
{
fprintf(stderr, "%s\n", "Check argument 3 for errors, can be \"--on or --off\"");
exit(EXIT_SUCCESS);
}
}
else if(strcmp(argv[1],commands[9])==0)
{
write_conf_block_read(is_integral(argv[2],argc));
exit(EXIT_SUCCESS);
}
break;
/*case 4://later update
if((strcmp(argv[1],"timeset")==0))//testing if user wants to play for specific amount of time.
{
if(strcmp(argv[2],"--on")==0)
{
time_set=1;
main_play();
}
else
{
fprintf(stderr, "%s\n", "Argument 3 invalid or not recognized");
exit(EXIT_SUCCESS);
}
}*/
default:
fprintf(stderr, "%s\n", "Argument is invalid, use \"help\" to find out more");
break;
}
return 0;
}
void select_lesson(int argc_cmd, int* user_choice)
{
char firstarg[81];//
if(argc_cmd>0&&argc_cmd<4)//checking on the command line argument.
{
char ch;//
printf("%s", "Enter command >>");
while(scanf("%s",firstarg)!=1||(scanf("%d",&*user_choice))!=1||*user_choice<1||*user_choice>15||strncmp(firstarg,"select",6)!=0)//Ensuring that "select"
{ //is entered accurately and the selected value is within the correct range.
if((strncmp(firstarg,"se",2)==0||strcmp(firstarg,"sel")==0||strcmp(firstarg,"sele")==0||strcmp(firstarg,"selec")==0)&&strcmp(firstarg,"select")!=0)
//Making suggestion to help user prevent errors.
fprintf(stderr, "\n%s\n%s", "Did you mean \"select usernumber\"","Enter command >>");
else if(ch!=1&&strcmp(firstarg,"select")==0)
printf("%s%s", "Lesson number cannot contain symbols or alphas\n","Enter command >>");
else if((*user_choice<1||*user_choice>20)&&strcmp(firstarg,"select")==0)
fprintf(stderr, "%s %d\n", "No lesson entry for ",*user_choice);
else printf("%s\nEnter command >>", "Command not found");
while(ch=getchar()!='\n');//disposing off wrong input string.
}
}
else
{
fprintf(stderr, "%s\n", "Invalid number of arguments, consult \"cmdtypist --help\" for more");
exit(EXIT_FAILURE);
}
/*
if(argc_cmd==3)
if(strncmp(argv[2],"select",6)!=0||lesson_choice<1||lesson_choice>20)
{
fprintf(stderr, "%s\n", "Command not found\n");
if(strncmp(argv[2],"se",2))
fprintf(stderr, "%s\n", "Did you mean \"select\"");
else if(lesson_choice<1||lesson_choice>20)
fprintf(stderr, "%s %d\n", "No lesson entry for ",lesson_choice);
//else if(ch!=1)
printf("%s", "Lesson number cannot contain symbols or alpha letters.\n");
exit(EXIT_FAILURE);
}*/
printf("\n");
}
void urs_or_cchl(void)
{
if(read_myown_config()==0)
{
strcpy(file_to_read,"my_own.txt");
mode=1;
}
else if(read_myown_config()==1)
strcpy(file_to_read,"noslaclessons.txt");
else
{
fprintf(stderr, "%s\n", "Fatal Error, lesson file corrupted or does not exist");
exit(EXIT_FAILURE);
}
}
void lesson_position(long *read_this_length,long *point_to,int *my_choice)//setting up the pointer in a position of the file to start reading.
{
FILE *lesson_point;
urs_or_cchl();
if((lesson_point=fopen(file_to_read,"r+"))==NULL)
{
fprintf(stderr, "%s\n", "Fatal Error, Some files are missing");
exit(EXIT_FAILURE);
}
if(read_myown_config()==0)
{
rewind(lesson_point);//return to beginning
*read_this_length=read_file_size(lesson_point);
*point_to=0;
}
else
switch(*my_choice)
{
case 1:
*point_to=0;
if(read_myown_config()==1)
*read_this_length=25510;
break;
case 2:
*point_to=25512L;
*read_this_length=21660;
break;
case 3:
*point_to=39326L;
*read_this_length=397417;
break;
case 4:
*point_to=444591L;
*read_this_length=11142;
break;
case 5:
*point_to=455733L;
*read_this_length=98588;
break;
case 6:
*point_to=554321L;
*read_this_length=19564;
break;
case 7:
*point_to=573885L;
*read_this_length=79999;
break;
case 8:
*point_to=653884L;
*read_this_length=327523;
break;
case 9:
*point_to=981407L;
*read_this_length=208614;
break;
case 10:
*point_to=1190021L;
*read_this_length=400980;
break;
case 11:
*point_to=1591001L;
*read_this_length=625353;
break;
case 12:
*point_to=2216354L;
*read_this_length=1132581;
break;
default:
*point_to=0;
}
if(fclose(lesson_point))
{
fprintf(stderr, "%s\n", "Unable to close lesson file");
exit(EXIT_FAILURE);
}
}
void main_play(int argc_cmd,int *lesson_choice)
{
//lmt_pg_size();
char terminate=0;
long length_to_read;//holds information on how long the text to be read is.
long move_lesson_to;
lesson_position(&length_to_read,&move_lesson_to,lesson_choice);
unsigned short block_count=0;
user_test();//test if a user already exists or not.
long num_of_chars_typed=0;
block_length=read_conf_block_read();
mode=read_conf_mode();
urs_or_cchl();//selects file to read from
remove_ext_ascii();//removes any non ascii 7 bits characters from lesson file.
unsigned int start_time,elapsed_time=0;//elapsed_time: time used during the typing session measured from start time.
unsigned short number_of_lines_count=1;//used to count the total number of lines to print.
const unsigned short chars_to_read=77;//total number of characters to read for each line.
unsigned int i=0;//counter variable for loop counting.
char linetype[150];//char array to hold the total number of characters to to read from file per line.
//lesson_list();
//fseek(noslac_lessonsp,25531L,SEEK_SET);//places the pointer to the position of the file to read.
int wrong_letters=0;//sums up the total number of wrong characters entered during program run.
srand((unsigned)time(NULL));//randomizing seed
FILE *noslac_lessonsp;//lesson pointer
if((noslac_lessonsp=fopen(file_to_read,"r"))==NULL)
{
fprintf(stderr, "%s\n", "Fatal Error, Some files are missing");
exit(EXIT_FAILURE);
}
fseek(noslac_lessonsp,move_lesson_to,SEEK_SET);
while(block_count <= (int)(length_to_read/((chars_to_read+1) * block_length)))//testing inorder to read the entire lesson chosen.
{
num_of_chars_typed=0;
char time_checker=0;//changes back to zero after every block typing
/*goes =reads a certain number of characters in the file using a loop determined
by the random generator and places the pointer at the end of it's reading. thereby
making the lesson each time to be random*/
if(mode==0)
{
int u=0;//counter
while(u<=rand()%(length_to_read-((chars_to_read+1)*block_length))&&(ch=getc(noslac_lessonsp))!=EOF)//program feels new
u++;
do
fseek(noslac_lessonsp,-2L,SEEK_CUR);
while((ch=getc(noslac_lessonsp))!=' ');//moving backwards from where it is placed
//to start reading from where there is a space or has found an uppercase letter.
}
number_of_lines_count=1;
while(number_of_lines_count<=block_length)//testing for number of lines to read.
{
i=0;
char endl = guess(14, 33);//endl holds the char to end a line in place of usual '\n'
char startl = guess(14, 33); //guess generates a random char
while(i <= chars_to_read)//test on i to get 77 characters. the screen size is exactly 77 characters.
{
linetype[i] = getc(noslac_lessonsp);//getting characters and placing in the linetype array.
if(linetype[0] == ' ')//prevent a the start of a line from ever being a space character
linetype[0] = startl; //replace with random char
if(linetype[chars_to_read] == ' ')//ensuring a line does not end with a space character.
linetype[chars_to_read] = '-';//replacing space character at the end of a line with a -
if(i > 1)
if(linetype[i-1] == ' ' && linetype[i] == ' ')//preventing two consecutive space characters since text read is random.
i -= 2;
//checking and eliminating newlines to prevent brakes.
if(linetype[i]=='\n'){
linetype[i] = endl;
linetype[++i] = ' ';
}
if(linetype[i]==EOF)//making sure a line does not contain any end of file character by any chance
{
fprintf(stderr, "%s\n", "Closed unexpectedly, <possibly a corrupt cmdtypist file OR you haven't placed any text in myown.txt>");
exit(EXIT_FAILURE);
}
i++;
}
linetype[i]='\0';//Adding string terminator and subtracting the number of spaces removed.
if((number_of_lines_count % (block_length)) == 0 && number_of_lines_count != 0)
printf(""LAST_LINE_BLUE"");
else
printf(""RESET"");
puts(linetype);//using puts inorder to print the a line and move to the next for the user to follow
number_of_lines_count++;
i=0;//setting i to 0 to begin new counting.
unsigned short error_store[3000], j=0;//error_store: array of ints to note the index of a wrong character.
while(i <= chars_to_read+1)//adding 1 for the extra enter key after the 77 letters are entered.
{
int u=0;//loop counter
if((ch=getche())!='\n'&&ch!=EOF)//using getche to prevent printing of enter key.
{
putchar(ch);
if(time_checker==0)//Making sure time is initialized only once
{
time_checker=1;
wrong_letters=0;//setting errors to 0 to start next typing session
start_time=(unsigned)time(NULL);//to start timing.
}
}
if(ch==EOF)
{
fprintf(stderr, "%s\n", "Closed unexpectedly, <an unexpected character keyed in>");
exit(EXIT_FAILURE);
}
if(ch == 27 || ch == '\t')/*testing for ESC character or a tab to exit program.
iscntrl ensures a control character is entered to exit the program*/
{
terminate=1;
letter_clear(1);
puts("\n");
break;
}
if((ch==127 || ch == 8)&& i == 0)//not using '\b' since most terminals are 'cooked' (keys like backspace are handled by terminal driver) //checking for both delete and backspace.
letter_clear(adapt_to_ver_read());
else if((ch == 127 || ch == 8) && i > 0)//testing for delete of backspace
{
i--;//decremting the number of characters entered when backspaced is pressed.
letter_clear(adapt_to_ver_read());
j=wrong_letters;
while(j>u)//counting from u to j, to find if there is a wrong character stored in the error_store array of ints.
{
//printf("j=%d and u=%d\n", j,u);
if(error_store[j]==i)//checking through the array for errased wrong charactes initially entered.
{ //also ensuring before any decrement, wrong_letters>0
wrong_letters--;//decrementing the number of wrong letters.
/*if(wrong_letters<0)
{
printf("finally got a case %d\n",wrong_letters);
wrong_letters=0;
}*/
error_store[j]=-99;//-99 is a value which will never be reached.
//this is to mark the erased index as no longer wrong.
break;//Ensuring that immediately there is a match, the while loop is escaped for speed.
}
j--;
}
}
else if(i==78&&ch!='\n')
letter_clear(1);
else if(ch!=linetype[i])
{
if(ch!='\n')//testing for ENTER to prevent its printing by printf which will cause a newline.
{
num_of_chars_typed++;
letter_clear(1);//clearing the wrong character
//printf("\a"RED"%c"RESET"",ch );//to print again with color RED. \a is used to include a beep for wrong charater
printf("%s%c",(sound_config_read())==1? "\a"RED"":""RED"",ch);//\a is used to include a beep for wrong character entries
wrong_letters++;
error_store[wrong_letters]=i;//recording the index of a wrong letter
u=0;
j++; //Incrementing the backspace counter.
i++; //incrementing the number of wrong characters entered.
}
}
else
{
if(ch!='\n')//Preventing printing of newline which causes an escape from current typing position in the console.
{
letter_clear(1);
num_of_chars_typed++;
printf(""GREEN"%c"RESET"", ch);//changing color of correct character to green.
i++;
}
}
if(ch=='\n'&&i==78)
break;//escaping loop when the user keys in an Enter.
}
printf("%s","\n" ); //Prints two spaces to ensure the two console spaces left are used, so next printing goes to next line. Game console is 80 and 78 is being used.
if(terminate==1||argc_cmd==4)
break;
}
elapsed_time = (unsigned)time(NULL) - start_time;/*getting the final time and subtracting from the initial
to get the elapsed time*/
block_count++;
//printf("lines=%d block = %d\n",number_of_lines_count,block_length );
if(terminate==1)//exiting on tabs and other systme keys
{
char user_name[81];
if(elapsed_time <= 10)
{
fprintf(stderr, "%s\n", "Speed not recorded");
printf(""RESET"\n");
exit(EXIT_SUCCESS);
}
get_user_name(user_name);
printf(""GREEN" ");
printf("%s", user_name);
if(wrong_letters<0)//optional statement to reduce proberbility of ever having a -ve wrong_letters.
wrong_letters=0;
write_user_speed(elapsed_time,wrong_letters,num_of_chars_typed);
session_style(elapsed_time,wrong_letters,num_of_chars_typed);
exit(EXIT_SUCCESS);//display current typing speed and error
}
if(((number_of_lines_count-1)%block_length)==0)
{
char user_name[81];
if(wrong_letters<0)//optional statement to reduce proberbility of ever having a -ve wrong_letters.
wrong_letters=0;
printf(""GREEN" ");
get_user_name(user_name);//reading user name from file to display in session
printf("%s", user_name);
session_style(elapsed_time,wrong_letters,num_of_chars_typed);//printing session speed details
write_user_speed(elapsed_time,wrong_letters,num_of_chars_typed);//writing user speed to speed file
}
}
}
/*Wrong letters algorithm:
if a user is at the first position of the line and presses backspace, then, that backspace is simply cleared and i not incremented
the array error_store[] keeps track of the index(the i position of the wrong character) and increments a counter variable j, which which be used as
A stop point in a for loop when this stored inex is searched.
When ever backspace keyed in(i!=0), i is first decremented and a search is done through out the loop to see if the decremented i was stored in error store,
if so, then the user is erasing a wrong character, so the wrong_letters is decremented.*/
/*solving the case where wrong_letters shows a messy value
changing it's type to int and making sure it's always greater than 0
*/