#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DELIMITER "."
#define NEWLINE_DELIM ".\n"
#define STATE_COMMAND 1
#define STATE_INPUT 2
#define STATE_EXIT 3
#define BUFFER_SIZE 1024
#define CMD_DELIMITER ";"
#define CMD_PRINT 'p'
#define CMD_QUIT 'q'
#define CMD_APPENDS 'a'
#define CMD_REPLACE 'r'
#define CMD_DELETE 'd'
#define CMD_INSERT 'i'
int main
(void) {
// Setup whatever we need.
char* buffer =
NULL;
int buffer_size =
0;
int buffer_length =
0;
int chars_read =
0;
int appState = STATE_COMMAND;
int lineNumber =
0;
int char_pos =
0;
char valid_cmds
[BUFFER_SIZE +
1] =
{'\0'};
char* all_cmds =
NULL;
char* cmd_line =
NULL;
int location =
0;
int locationB =
0;
char commandLetter;
char notModifier;
char rangeModifier;
char*
string =
"";
int skipPrint =
0;
// We're going to loop through here until we're all done. (ie, STATE_EXIT)
do {
// Read input into the buffer.
chars_read = getline
(&buffer, &buffer_size, stdin
);
// Make sure it exists.
if(chars_read <
1){
break;
}
// Delimiters make the state increase.
if(strcmp
(buffer, DELIMITER
) ==
0 || strcmp
(buffer, NEWLINE_DELIM
) ==
0){
appState = appState +
1;
continue;
}
// Command state.
if(appState == STATE_COMMAND
){
// Get some commands from the buffer.
// Go through each character of the command, and see if it matches a
// real command, if it does, put it in the valid commands buffer.
buffer_length = strlen
(buffer
);
for(char_pos =
0; char_pos < buffer_length; char_pos++
){
if(islower
(buffer
[char_pos
])){
if(buffer
[char_pos
] == CMD_PRINT ||
buffer
[char_pos
] == CMD_QUIT ||
buffer
[char_pos
] == CMD_APPENDS ||
buffer
[char_pos
] == CMD_REPLACE ||
buffer
[char_pos
] == CMD_DELETE ||
buffer
[char_pos
] == CMD_INSERT
){
strcat
(valid_cmds, buffer
);
strcat
(valid_cmds, CMD_DELIMITER
);
break;
}
appState = STATE_EXIT;
break;
}
}
}
// Text input state.
if(appState == STATE_INPUT
){
skipPrint =
0;
// This must must MUST get set to 0 on each loop, or everything will break.
lineNumber = lineNumber +
1;
// Increment the line number each time we go through.
all_cmds = strdup
(valid_cmds
);
// And now all_cmds is the same as valid_cmds.
// Get a command from the commands buffer.
for(cmd_line = strtok
(all_cmds, CMD_DELIMITER
); cmd_line !=
NULL; cmd_line = strtok
(NULL, CMD_DELIMITER
)){
// Match this one first, because it's the most specific - put the various bits of the command
// into the various variables. Also, make sure the range modifier is one of the valid ones,
// and that the not modifier is what we expect.
if(sscanf
(cmd_line,
"%d%c%d%c%c%s", &location, &rangeModifier, &locationB, ¬Modifier, &commandLetter,
string) ==
6
&&
(rangeModifier ==
',' || rangeModifier ==
'~')
&& notModifier ==
'!'){
// Now check the command issued. Appends? Yeah, check the range --
// For comma, make sure the line number isn't in the range specified. (Remember the '!'.)
// For tilde, check that A) the line number isn't the one specified, B) the line number minux
// the starting point divded by the second number has a remainder, C) the line number is less than
// the starting point. If A and B or A and C are true, we'll match.
if(commandLetter == CMD_APPENDS &&
(
(rangeModifier ==
',' &&
(location > lineNumber || lineNumber > locationB
))
||
(rangeModifier ==
'~' && location != lineNumber &&
((lineNumber - location
) % locationB >
0 || lineNumber < location
))
)){
// Append, stick the string to the end of the existing buffer and attach a newline.
// Append will use this same method for the rest off the programme.
strcat
(buffer,
string);
strcat
(buffer,
"\n");
}
// Same method for matching replace.
if(commandLetter == CMD_REPLACE &&
(
(rangeModifier ==
',' &&
(location > lineNumber || lineNumber > locationB
))
||
(rangeModifier ==
'~' && location != lineNumber &&
((lineNumber - location
) % locationB >
0 || lineNumber < location
))
)){
// Just replace the buffer with the new string and add a newline to the end.
// Replacement will be done the same way as this for the rest of the app.
strcpy
(buffer,
string);
strcat
(buffer,
"\n");
}
// And insert.
if(commandLetter == CMD_INSERT &&
(
(rangeModifier ==
',' &&
(location > lineNumber || lineNumber > locationB
))
||
(rangeModifier ==
'~' && location != lineNumber &&
((lineNumber - location
) % locationB >
0 || lineNumber < location
))
)){
// Add a newline to the end of the new string, stick the buffer on after that, and then
// move it all in to the buffer. We'll reuse this method for insert throughout the app.
strcat
(string,
"\n");
strcat
(string, buffer
);
strcpy
(buffer,
string);
}
// We're moving down in order of most specific matching regime to least specific. So this one's next.
}else if(sscanf
(cmd_line,
"%d%c%d%c%c", &location, &rangeModifier, &locationB, ¬Modifier, &commandLetter
) ==
5
&&
(rangeModifier ==
',' || rangeModifier ==
'~')
&& notModifier ==
'!'){
// We're using the same matching system as before to match the ranges for print, remembering that
// there's still an !.
if(commandLetter == CMD_PRINT &&
(
(rangeModifier ==
',' &&
(location > lineNumber || lineNumber > locationB
))
||
(rangeModifier ==
'~' && location != lineNumber &&
((lineNumber - location
) % locationB >
0 || lineNumber < location
))
)){
printf("%s", buffer
);
}
// And delete.
if(commandLetter == CMD_DELETE &&
(
(rangeModifier ==
',' &&
(location > lineNumber || lineNumber > locationB
))
||
(rangeModifier ==
'~' && location != lineNumber &&
((lineNumber - location
) % locationB >
0 || lineNumber < location
))
)){
skipPrint =
1;
}
// This is the next one in order, this time no !
}else if(sscanf
(cmd_line,
"%d%c%d%c%s", &location, &rangeModifier, &locationB, &commandLetter,
string) ==
5
&&
(rangeModifier ==
',' || rangeModifier ==
'~')){
// For appends, we're checking if the range is a comma or a tilde. If it's comma, is the line number
// in between the addresses specified? If it's a tilde, check A) is line number equal to the location
// specified, B) does the line number minus the location, divided by the second number,
// have NO remainder? and C) is the line number greater than the location specified?
// If A is true, or B and C are true, we match.
if(commandLetter == CMD_APPENDS &&
(
(rangeModifier ==
',' && location <= lineNumber && lineNumber <= locationB
)
||
(rangeModifier ==
'~' &&
(location == lineNumber ||
(((lineNumber - location
) % locationB
) ==
0 && lineNumber > location
)))
)){
strcat
(buffer,
string);
strcat
(buffer,
"\n");
}
if(commandLetter == CMD_REPLACE &&
(
(rangeModifier ==
',' && location == lineNumber
)
||
(rangeModifier ==
'~' &&
(location == lineNumber ||
(((lineNumber - location
) % locationB
) ==
0 && lineNumber > location
)))
)){
strcpy
(buffer,
string);
strcat
(buffer,
"\n");
}
// Using this to for the ranged replace for a comma-range only, once we've replaced the first line,
// We make sure that none of the lines after it print until we're out of the range.
// Ref: http://cnfolio.com/IntroComputingTutorial05#example05
if(commandLetter == CMD_REPLACE &&
(
(rangeModifier ==
',' && lineNumber > location && lineNumber <= locationB
)
)){
skipPrint =
1;
}
if(commandLetter == CMD_INSERT &&
(
(rangeModifier ==
',' && location <= lineNumber && lineNumber <= locationB
)
||
(rangeModifier ==
'~' &&
(location == lineNumber ||
(((lineNumber - location
) % locationB
) ==
0 && lineNumber > location
)))
)){
strcat
(string,
"\n");
strcat
(string, buffer
);
strcpy
(buffer,
string);
}
// And then this one goes next.
}else if(sscanf
(cmd_line,
"%d%c%d%c", &location, &rangeModifier, &locationB, &commandLetter
) ==
4
&&
(rangeModifier ==
',' || rangeModifier ==
'~')){
// Same method as before for dealing with the different ranges.
if(commandLetter == CMD_PRINT &&
(
(rangeModifier ==
',' && location <= lineNumber && lineNumber <= locationB
)
||
(rangeModifier ==
'~' &&
(location == lineNumber ||
(((lineNumber - location
) % locationB
) ==
0 && lineNumber > location
)))
)){
printf("%s", buffer
);
}
if(commandLetter == CMD_DELETE &&
(
(rangeModifier ==
',' && location <= lineNumber && lineNumber <= locationB
)
||
(rangeModifier ==
'~' &&
(location == lineNumber ||
(((lineNumber - location
) % locationB
) ==
0 && lineNumber > location
)))
)){
skipPrint =
1;
}
// This one's next but it doesn't have a range, just the not modifier.
}else if(sscanf
(cmd_line,
"%d%c%c%s", &location, ¬Modifier, &commandLetter,
string) ==
4
&& notModifier ==
'!'){
// So basically, because of the !, we only make sure that the line number DOESN'T match.
// We don't have to use the range detection here.
if(commandLetter == CMD_APPENDS && location != lineNumber
){
strcat
(buffer,
string);
strcat
(buffer,
"\n");
}
// And here.
if(commandLetter == CMD_REPLACE && location != lineNumber
){
strcpy
(buffer,
string);
strcat
(buffer,
"\n");
}
// And here.
if(commandLetter == CMD_INSERT && location == lineNumber
){
strcat
(string,
"\n");
strcat
(string, buffer
);
strcpy
(buffer,
string);
}
// We do have a not modifier here, so we need to not match everything.
}else if(sscanf
(cmd_line,
"%d%c%c", &location, ¬Modifier, &commandLetter
) ==
3
&& notModifier ==
'!'){
// Same here.
if(commandLetter == CMD_PRINT && location != lineNumber
){
printf("%s", buffer
);
}
// And here.
if(commandLetter == CMD_DELETE && location != lineNumber
){
skipPrint =
1;
}
// This time we don't have the not modifier.
}else if(sscanf
(cmd_line,
"%d%c%s", &location, &commandLetter,
string) ==
3){
// So we check that the line numbers DO match. Again, no worry about ranges.
if(commandLetter == CMD_APPENDS && location == lineNumber
){
strcat
(buffer,
string);
strcat
(buffer,
"\n");
}
// Same here.
if(commandLetter == CMD_REPLACE && location == lineNumber
){
strcpy
(buffer,
string);
strcat
(buffer,
"\n");
}
// And here.
if(commandLetter == CMD_INSERT && location == lineNumber
){
strcat
(string,
"\n");
strcat
(string, buffer
);
strcpy
(buffer,
string);
}
// We don't really have to make sure location > 0, but it's better to
// validate as much of the input as you can, to weed out falsely-matching
// input.
}else if(sscanf
(cmd_line,
"%d%c", &location, &commandLetter
) ==
2
&& location >
0){
// Again, it's a simple case of does the line number match?
if(commandLetter == CMD_PRINT && location == lineNumber
){
printf("%s", buffer
);
}
// Here too.
if(commandLetter == CMD_QUIT && location == lineNumber
){
appState = STATE_EXIT;
}
// This one as well.
if(commandLetter == CMD_DELETE && location == lineNumber
){
skipPrint =
1;
}
// We have a not modifier here.
}else if(sscanf
(cmd_line,
"%c%c", ¬Modifier, &commandLetter
) ==
2
&& notModifier ==
'!'){
// But no line numbers, so it matches every line.
if(commandLetter == CMD_PRINT
){
// well, if we DON'T (from the !) print every line an additional time,
// then it just does what it normally does so this doesn't
// actually need to do anything?
}
/// Same here.
if(commandLetter == CMD_DELETE
){
// likewise, if we don't delete every line, then it acts
// normally and again, this doesn't do anything.
// I don't actually understand the point in !p and !d.
}
// We're back to a simple command that effects every line, but not ! modifier.
}else if(sscanf
(cmd_line,
"%c%s", &commandLetter,
string) ==
2){
// So we don't need to worry about line matching.
if(commandLetter == CMD_APPENDS
){
strcat
(buffer,
string);
strcat
(buffer,
"\n");
}
// Or here.
if(commandLetter == CMD_REPLACE
){
strcpy
(buffer,
string);
strcat
(buffer,
"\n");
}
// Or here.
if(commandLetter == CMD_INSERT
){
strcat
(string,
"\n");
strcat
(string, buffer
);
strcpy
(buffer,
string);
}
// Very simple one letter commands.
}else if(sscanf
(cmd_line,
"%c", &commandLetter
) ==
1){
// Which match every line.
if(commandLetter == CMD_PRINT
){
printf("%s", buffer
);
}
// And here.
if(commandLetter == CMD_QUIT
){
appState = STATE_EXIT;
}
// Same here.
if(commandLetter == CMD_DELETE
){
skipPrint =
1;
}
}
// If nothing's told it not to print since the beginning of the loop,
// print the buffer. By this point, the buffer may have been modified
// by a replace, append, or insert. Doesn't matter, print it anyway as
// long as nothing set skipPrint to 1.
if(skipPrint ==
0){
printf("%s", buffer
);
}
}
}
} while(appState != STATE_EXIT
);
return 0;
}