//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 #include #include #include #include #include #include //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, "); 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, "); 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 */