I need to make a program that will emulate the terminal of Linux. Since some system calls requires 1,2 or more arguments, I want to make sure that the number of parameters given are correct. I'm using strtok() to separate the call name from the arguments, but I need to know how many tokens strtok() created to compare it.
Here's and example code:
char *comand = (char*) malloc(sizeof(char)*100);
char *token;
char *path1 = (char*) malloc(sizeof(char)*100);
char *path2= (char*) malloc(sizeof(char)*100);
fgets(comand, 100, stdin);
printf( "\nYou entered: %s \n", comand);
token = strtok(comand ," ");
//Check the number of tokens and add a condition in each IF to match
if (strcmp("ls",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("cat",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("cp",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
token = strtok(NULL," ");
strcpy(path2,token);
}
else if (strcmp("mv",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
token = strtok(NULL," ");
strcpy(path2,token);
}
else if (strcmp("find",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("rm",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("mkdir",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("rmdir",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("quit",token) == 0) {
exit(0);
}
else print("Number of parameters do not match);
the only thing strtok() does is look for the next occurance of the delimiter and overwrite that character with a \0 and return the pointer with the offset added. the pointer is kept in a static variable that's why a subsequent call to it with a NULL for the char * will perform it on the last string used from the offset that the last delimiter was found.
this page has a very nice example:
http://en.cppreference.com/w/c/string/byte/strtok
If you only want to count the arguments it would be easier to use strchr(), this function searches for a character and returns a pointer to its location. you could use it like this.
unsigned int i = 0;
char *temp = token;
while ( (temp = strchr(temp, '') != NULL) ) {
++i;
}
this has the added benefit of not modifying your original char array while strtok() does!
I would handle this within the functions you create for each command.
you pass all options to the function and there you parse it. either with strtok() or whatever else you want to use.
This keeps it nice and clean within the sub-routines and you will always know what to expect.
if (strcmp("ls",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token); // I would maybe change the path variable name to args
ret = lscmd(path1);
if (ret == -1) {
// invalid input detected
}
}
then you would have a ls function
int lsdcmd(char *args) {
// parse args for argumants you are looking for, return -1 if it fails
// do whatever you want to do.
}
You can count the arguments using strtok this way:
Example:
const char* delimiter = ",";
char* tokens[MAX_NUM_OF_ARGS];
unsigned int index = 0;
char* temp = strtok(buff,delimiter);
while (temp!=NULL){
if(index<MAX_NUM_OF_ARGS){
tokens[index]=temp;
}
index++;
temp = strtok(NULL,delimiter);
}
Then later you can iterate through the array of pointers (tokens) and compare them...
Related
I am trying to figure out how to read input multiple ways. Input can look like this.
N:{-4,2,1}
E:{1,1,9}
W:{-2,5,3}
S:{7,1}
or like this
E:{9,1,1}N:{1,2,-4}W:{3,5,-2}S:{7,1}
I added into my code checking for word END. Its supposed to be working on EOF but on my windows machine it doesnt work. So input END to signal END of input. So far I managed to get them both working separatly... But I need them to both at the same time.
char *skip_whitespace(char *str)
{
//skip any leading whitespace characters
while (*str == ' ' || *str == '\t')
str++;
return str;
}
void read_tokens(int *north, int *west, int *east, int *south, int *north_size, int *west_size, int *east_size, int *south_size)
{
//buffer for reading in input
char buffer[MAX_TOKENS];
//read in the input line by line
while (fgets(buffer, MAX_TOKENS, stdin) != NULL)
{
//remove the newline character from the end of the line
buffer[strcspn(buffer, "\n")] = 0;
//check for the "END" string to end the input
if (strcmp(buffer, "END") == 0)
break;
//split the line at the curly brace character
char *direction_token = strtok(buffer, "{");
char *tokens = strtok(NULL, "}");
//get the direction
char direction = direction_token[0];
//skip any leading or trailing whitespace characters
tokens = skip_whitespace(tokens);
//split the tokens at each comma
char *token = strtok(tokens, ",");
//determine the direction and store the tokens in the appropriate array
if (direction == 'N')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
north[*north_size] = atoi(token);
(*north_size)++;
//find the next token
token = strtok(NULL, ",");
}
}
else if (direction == 'W')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
west[*west_size] = atoi(token);
(*west_size)++;
//find the next token
token = strtok(NULL, ",");
}
}
else if (direction == 'E')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
east[*east_size] = atoi(token);
(*east_size)++;
//find the next token
token = strtok(NULL, ",");
}
}
else if (direction == 'S')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
south[*south_size] = atoi(token);
(*south_size)++;
//find the next token
token = strtok(NULL, ",");
}
}
else
{
//invalid direction = error
printf("Nespravny vstup.\n");
}
}
}
Here is the main function. For anyone intrested here is calling and printing.
int main(void)
{
//field for token values
int north[MAX_TOKENS], west[MAX_TOKENS], east[MAX_TOKENS], south[MAX_TOKENS];
//sizes of token value fields
int north_size = 0, west_size = 0, east_size = 0, south_size = 0;
printf("Input:\n");
//fetch token values from input
read_tokens(north, west, east, south, &north_size, &west_size, &east_size, &south_size);
printf("N: { ");
for (int i = 0; i < north_size; i++)
printf("%d, ", north[i]);
printf("}\n");
printf("W: { ");
for (int i = 0; i < west_size; i++)
printf("%d, ", west[i]);
printf("}\n");
printf("E: { ");
for (int i = 0; i < east_size; i++)
printf("%d, ", east[i]);
printf("}\n");
printf("S: { ");
for (int i = 0; i < south_size; i++)
printf("%d, ", south[i]);
printf("}\n");
}
You have an input format that permits optional whitespace, including newlines, between tokens. Your two examples differ only in that one makes use of that option and the other doesn't. In the most basic terms, then, the solution is to make your parser ignore (all) whitespace between tokens, too. Such a parser handles both forms of input presented, and other variants, too.
I do think, however, that fgets() is more a liability here than a help. Your input is not fundamentally line-oriented, and with fgets() you need to (but do not presently) watch out for and handle cases where a long line is split over two or more reads. I suggest tokenizing the input directly from the stream instead of first reading it into an intermediate buffer.
I know that scanf() gets a lot of hate, but it might serve your purpose pretty well here. It already knows how to skip whitespace, to recognize integers, and to match specific characters. Something along these lines, maybe:
while (1) {
char direction;
char delim;
int result;
// scan first part:
result = scanf(" %c : %c", &direction, &delim);
if (result == EOF) {
// end of file
} else if (result != 2 || !is_valid_direction(direction) || delim != '{') {
// invalid input ...
// ... or maybe (part of) an "END" keyword if you decide to
// go ahead with that
}
// ... handle direction code ...
do {
// scan number
int num;
result = scanf("%d %c", &num, &delim);
if (result != 2 || (delim != ',' && delim != '}')) {
// invalid input ...
// unless empty number lists are allowed: N:{}
}
// store number ...
} while (delim == ',');
}
If you want to avoid scanf(), then you can do basically the same thing by reading one character at a time via getchar() or fgetc(). Or, yes, with fgets() too, provided you exercise sufficient care.
That's schematic, of course, not a full implementation of the needed parser
I'm making a custom C shell and currently splitting up commands using whitespace:
(Simplified example of what I'm doing currently)
char *buf[20];
char *tempVar;
tempvar = strtok(buf, " ");
For example:
sleep 5 would be split up into sleep and 5.
However, I'm wanting to split for & as well (to create background processes). So sleep 5& would be split into 3: sleep, 5 and &.
Any help would be greatly appreciated.
You can using a specific method to extract numbers, or just drop the character &. I've a thought here, maybe it's not the best solution. Hoping for further exchanges.
char buf[] = "sleep 5&";
char *cmd;
char *arg;
char *tmpArg;
int count = 0;
while (true) {
if (0 == count) {
cmd = strtok(buf, " ");
arg = strtok(NULL, " ");
} else {
cmd = strtok(NULL, " ");
arg = strtok(NULL, " ");
}
printf("%s\n", cmd);
if (NULL != cmd) {
if (strcmp("sleep", cmd) == 0) {
if (arg != NULL) {
// 1. introduce a method to extract digit or
// 2. using `&` to split the arg again, the shortcoming is that we omit character `&` here.
for (int i = 0; i < 2; ++i) {
if (0 == i) {
tmpArg = strtok(arg, "&");
} else {
tmpArg = strtok(NULL, "&");
}
printf("%s\n", tmpArg);
}
}
}
} else {
break;
}
++count;
}
I'm writing a compiler for assembler, and I need to do parsing to the text I get from a file, without making any changes in the original String. The function I used to copy the String was strcpyto a buffer, and to cut the String was strtok to cut the buffer.
Everything works perfect, but once I try to cut the original String after using the function addressingConstantIndex , I get null.
I tried to change the type of the buffer to a pointer of Character, it didn't really work. I guess the main problem it's in the way I copy the original String to the buffer.
int main(){
char desti[MAXCHAR];
char *temp;
char *token;
temp = "mov LIST[5] , r4";
strcpy(desti,temp);
printf("\ndest is : %s\n", desti);
token = strtok(desti," ");
printf("\nthe Token in Main is : %s \n", token);
token = strtok(NULL, ",");
printf("\nthe Token in Main is : %s\n", token);
printf("\nThe value is %d \n ",addressingConstantIndex(token));
token = strtok(NULL, " ,");
printf("\nthe Token in Main is : %s\n", token);
return 0;
}
int addressingConstantIndex(char * str) {
char buf[43];
char *token;
int ans;
strcpy(buf, str);
token = strtok(buf, "[");
printf("The string is %s\n",str);
if (token != NULL)
{
printf("the token is %s\n", token);
token = strtok(NULL, "]");
printf("the token is %s\n", token);
if(isOnlyNumber(token))
{
token = strtok(NULL, " ");
if (checkIfSpaces(token,0) == ERROR)
{
printf("ERROR: Extra characters after last bracket %s \n", str);
ans = ERROR;
} else
ans = OK;
} else {
printf("ERROR: Unknown string - %s - its not a macro & not a number.\n", token);
ans = ERROR;
}
} else {
printf("ERROR: %s , its not a LABEL", token);
ans = ERROR;
}
return ans;
}
int isOnlyNumber(char *str) {
int i, isNumber;
i = 0;
isNumber = 1;
if (!isdigit(str[i]) && !(str[i] == '-' || str[i] == '+'))
{
isNumber = ERROR;
}
i++;
while (i < strlen(str) && isNumber == 1)
{
if (!(isdigit(str[i]))) {
if (isspace(str[i]))
isNumber = checkIfSpaces(str, i);
else
isNumber = ERROR;
}
i++;
}
return isNumber;
}
int checkIfSpaces(char *str, int index) {
int i;
if (str == NULL)
{
return OK;
} else {
for (i = index; i < strlen(str); i++)
{
if (!isspace(str[i])) return ERROR;
}
}
return OK;
}
The expecting result:
dest is : mov LIST[5] , r4
the Token in Main is : mov
the Token in Main is : LIST[5]
The string is LIST[5]
the token is LIST
the token is 5
The value is 1
the Token in Main is : r4
The real result:
dest is : mov LIST[5] , r4
the Token in Main is : mov
the Token in Main is : LIST[5]
The string is LIST[5]
the token is LIST
the token is 5
The value is 1
the Token in Main is : (null)
The difference It's in the last row of the result.
The problem is that strtok() maintains a single static pointer to the current string location. So in addressingConstantIndex(), you start processing the local buf, so that when you return to main() it is no longer parsing desti but the now out of scope buf from addressingConstantIndex().
The simplest change to your existing code would be to use strtok_r() (or strtok_s() on Windows):
char* context = 0 ;
token = strtok_r( desti, " ", &context ) ;
printf("\nthe Token in Main is : %s \n", token);
token = strtok_r( NULL, ",", &context ) ;
...
Then similarly in addressingConstantIndex():
char* context = 0 ;
token = strtok_r(buf, "[", &context);
...
strtok replaces the separator in the string with a NULL on each call. Your code is finding the 'LIST[5]' token at the main level, at which point it has replaced the ',' with a NULL.
In addressingConstantIndex, strtok is reset with a new string and parses correctly (though your function is typed void instead of int).
At at the main level again, strtok is not being reset, so it is continuing to parse the string used in addressingConstantIndex.
To fix this you need to reset strtok again to continue. However you can't just call it with strtok(desti,",") as the desti has all the separators set to NULL from previous calls.
A quick solution is to copy the token to feed into addressingConstantIndex at the main level, and complete the main level before parsing at the next level.
int main(){
char desti[MAXCHAR];
char *temp;
char *token;
temp = "mov LIST[5] , r4";
strcpy(desti,temp);
printf("\ndest is : %s\n", desti);
token = strtok(desti," ");
printf("\nMnemonic : %s \n", token);
token = strtok(NULL, ",");
printf("\nLIst bit: %s\n", token);
char buf[80]; //Save the token
strcpy(buf, token);
token = strtok(NULL, " ,"); //Finish this level of processing
printf("\nRegister: %s\n", token);
//Continue at the next level with copy
printf("\nThe value is %d \n ",addressingConstantIndex(buf));
return 0;
}
Though a strtok_r solution might suit your needs better going forward
In here I am trying to tokenize the user input e.g. load sml.txt.
The load command works fine because it has 2 tokens, but if I try to use a single word input like display, it crashes and gives me a segfault. I assume its because the second token is NULL, but I have no idea how to circumvent this issue. Can you help?
For your reference COMMAND_LOAD = "load" and COMMAND_DISPLAY = "display".
int main(int argc, char **argv)
{
AddressBookList *addressBookList;
char input[BUFSIZ];
char load[BUFSIZ];
char fileN[BUFSIZ];
char *fileName;
char *token;
showStudentInformation();
do
{
printf("Enter your command: \n");
fgets(input, sizeof(input), stdin);
input[strlen(input) - 1] = '\0';
token = strtok(input, " ");
strcpy(load, token);
token = strtok(NULL, " ");
strcpy(fileN, token);
fileName = fileN;
if (strcmp(load, COMMAND_LOAD) == 0)
{
addressBookList = commandLoad(fileName);
}
else if (strcmp(load, COMMAND_UNLOAD) == 0)
{
/*commandUnload(fileName);*/
}
else if (strcmp(load, COMMAND_DISPLAY) == 0)
{
if (fileN == NULL)
{
printf("> No file loaded");
}
else
{
commandDisplay(addressBookList);
}
}
else
{
printf("> Invalid input\n\n");
}
} while (strcmp(load, COMMAND_QUIT) != 0);
return EXIT_SUCCESS;
}
strtok() returns NULL when there's no more tokens, you can check for this. If there's no token, I empty the target string by assigning '\0' to the first character, instead of calling strcpy().
do
{
printf("Enter your command: \n");
fgets(input, sizeof(input), stdin);
token = strtok(input, " \n");
if (token) {
strcpy(load, token);
} else {
load[0] = '\0';
}
token = strtok(NULL, " \n");
if (token) {
strcpy(fileN, token);
} else {
fileN[0] = '\0';
}
...
} while (strcmp(load, COMMAND_QUIT) != 0);
There's also no need to replace the last character in the string with \0. Just include \n in the strtok() delimiters, so it won't include the newline at the end in the token.
So I'm trying to add tokens to an array the if statement keeps verifying that the array, tokenHolder, is empty. My second while loop is where I try to input tokens into the array. However no tokens are inputted into the array and I don't understand why.
char* token;
int* bufflength = 0;
char* buffer = NULL;
char input[25000];
char *tokenHolder[2500];
int pos = 0;
while(1){
printf("repl> ");
getline(&buffer, &bufflength, stdin);
token = strtok(buffer, "");
//code to input tokens into array
while(token != NULL){
pos++;
token = strtok(NULL, "");
tokenHolder[pos] = token;
}
if(tokenHolder[0] == NULL){
printf("It's NULL");
}
}
You increment pos to 1 before you save any token, so nothing is ever assigned to tokenHolder[0].
Either use (note the use of blank rather than an empty string as the delimiter):
tokenHolder[0] = token = strtok(buffer, " ");
(or an equivalent) or do something like:
char *data = buffer;
while ((tokenHolder[pos++] = strtok(data, " ")) != NULL)
data = NULL;
char *tokenHolder[2500] = { NULL };
...
while(token != NULL){
tokenHolder[pos++] = token;
token = strtok(NULL, "");
}
if(tokenHolder[0] == NULL){//or if(pos == 0){
printf("It's NULL");
}