This repository has been archived by the owner on Oct 17, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
cmd_typist.c
561 lines (538 loc) · 18.8 KB
/
cmd_typist.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
//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
*/