First problem:
Suppose we write a simple program which takes in command line arguments and prints to a file. If the user enters
writetofile Hello!0\n w%orl\t!##y
bash replies with
!0: event not
found.
Without the user knowing things like using quotes ('') or escape characters ('\'), how do I handle this stuff instead of bash understanding it as a command?
Second problem:
Once I get these arguments, how do I interpret them as special characters and not sequences of characters. (ie. \t is tab, not '\''t')
That is, how do make sure that the program writes this to the file:
Hello!0
w%orl !##y
and not
Hello!0\n w%orl\t!##y
You can't get bash to stop parsing a command line as a command line. In your particular case, set +H would disable history expansion, thus allowing !0 as a literal, but a command would still fail if you included (parenthesis), $variables, #comments, ampersand & and a dozen other elements of shell syntax.
Note that this only applies to the parsing stage of the shell, not for any data that is passed literally into execve or similar.
Either make the user quote the data ('single quotes' will take care of everything except other single quotes), or make your program read the data itself so that the interaction goes
$ writetofile
Please enter your gibberish and press enter: Hello!0\n w%orl\t!##y
Thank you for your eloquent contribution.
$
Regarding the second problem: as #Jims said, you could use printf(1) to print the string. Good idea, but be aware that that would work only because the escapes you (appear to) want to recognise, like \t and \n, are the same as the ones that printf recognises.
These escapes are common, but there's nothing fundamental about them, so the general answer to your question – and the answer if you want to recognise an escape that printf doesn't – is for your program to interpret them. The C-like code to do that would be something like:
char *arg = "foo\\tbar\\n"; /* the doubled backslashes are to make this valid C */
int arglen = strlen(arg);
for (i=0; i<arglen; i++) {
if (arg[i] == '\\') { // we've spotted an escape
// interpret a backslash escape
i++; // we should check for EOS here...
switch (arg[i]) {
case 'n': putchar('\n'); break;
case 't': putchar('\t'); break;
// etc
}
} else {
// ordinary character: just output it
putchar(arg[i]);
}
}
(insert error handling and good style, to taste).
First problem: Use single quotes. ie: writetofile 'Hello!0\n w%orl\t!##y'
Second problem: Use printf.
Related
I am trying to create a pattern like below, for ,when enter button is pressed twice, the program should terminate, but if user enters something in the line, the program shouldn't terminate.
[\n\n] {
if( strcmp(yytext,"?")){
return 0;
}
}
I try to check with strcmp for yytex is null space, but what should ı put inside " " ?
[\n\n] doesn't match two consecutive line breaks. It's a character class matching a single character that's either a line break or ... a line break. It's equivalent to just \n - duplicate elements in a character class have no effect.
To match two consecutive line breaks, what you want is \n\n without the bracket.
As for strcmp, there is absolutely no reason to check the contents of yytext here. Using the pattern [\n\n], yytext can only ever be equal to "\n" and using \n\n, it can only ever be equal to "\n\n". Either way there's no reason to compare it to anything when you already know what its value is going to be.
This is the solution for this situation guys
^\n yyterminate();
I need to create a method that get's commands from users using scanf and runs a function. The command can be simple as help or list but it can also be a command that has an argument like look DIRECTION or take ITEM. What is the best way to go about this? I could just loop through the characters of a single given string and check it manually but I was wondering there was a better way of doing this.
scanf("%s %s", command, argument);
This won't work if there's no argument. Is there a way around this?
There is a 'method' that may work. In fact, two come to mind.
Both rely on whitespace chars (in plain-english, '\n', ' 'and '\t') separating the arguments , and I assume this is good enough.
1
First, the relatively easy one - using main(int argc,char *argv[]) as most CLI programs do.
Then, running a long string of if()s/else if()s which check if the input string matched valid arguments , by testing if strcmp(argv[x],expected_command) returns 0.
You may not yet have been taught about how to use this, and it may appear scary, but its quite easy if you are familiar with string.h, arrays and pointers already.
Google searches and YouTube videos may be of help, and it won't take more than 20 or so minutes.
2
Second, if you have your program with a real CLU 'UI' and the program is in a loop and doesn't just terminate once output is generated - unlike say cat or ls , then you take input of 'command' strings within the program.
This means you will have to, apart from and before the if-ed strcmp()s , ensure that you take input with scanf() safely, and that you are able to take multiple strings as input, since you talk of sub-arguments like look DIRECTION.
The way I have done this myself (in the past) is as follows :
1. Declare a command string, say char cmd[21] = ""; and (optionally) initialise it to be empty , since reading an uninitialised string is UB (and the user may enter EOF).
2. Declare a function (for convenience) to check scanf() say like so:
int handle_scanf(int returned,int expected){
if(returned==expected)
return 0;
if(returned==EOF){
puts("\n Error : Input Terminated Immaturely.");
/* you may alternatively do perror() but then
will have to deal with resetting errno=0 and
including errno.h */
return -1;
}
else{
puts("\n Error : Insufficient Input.");
return -2;
}
}
Which can be used as : if(handle_scanf(scanf(xyz,&xyz),1)==0) {...}
As scanf() returns number of items 'taken' (items that matched with expected format-string and were hence saved) and here there is only 1 expected argument.
3. Declare a function (for convenience) to clear/flush stdin so that if and when unnecessary input is left in the input stream , (which if not dealt with, will be passed to the next place where input is taken) it can be 'eaten'.
I do it like so :
void eat()
{
int eat; while ((eat = getchar()) != '\n' && eat != EOF);
}
Essentially clears input till a newline or EOF is read. Since '\n' and EOF represent End Of Line and End Of File , and modern I/O is line buffered and performed through the stdin file , it makes sense to stop upon reading them.
EDIT : You may alternatively use a macro, for slightly better performance.
4. Print a prompt and take input, like so :
fputs("\n >>> ",stdout);
int check = handle_scanf(scanf("%20s",cmd),1);
Notice what I did here ?
"%20s" does two things - stops buffer overflow (because more than 20 chars won't be scanned into cmd) and also stops scanning when a whitespace char is encountered. So, your main command must be one-word.
5. Check if the the command is valid .
This is to be done with the aforementioned list of checking if strcmp(cmd,"expected_cmd")==0 , for all possible expected commands.
If there is no match, with an else , display an error message and call eat();(arguments to invalid command can be ignored) but only if(check != -1).
If check==-1 , this may mean that the user has sent an EOF signal to the program, in which case, calling eat() within a loop will result in an infinite loop displaying the error message, something which you don't want.
6. If there is a match, absorb the whitespace separating char and then scanf() into a char array ( if the user entered, look DIRECTION, DIRECTION is still in the input stream and will only now be saved to said char array ). This can be done like so :
#define SOME_SIZE 100 // use an appropriate size
if(strcmp(cmd,"look")==0 && check==0){ // do if(check==0) before these ifs, done here just for my convenience)
getchar(); // absorb whitespace seperator
char strbuff[SOME_SIZE] = ""; // string buffer of appropriate size
if(handle_scanf(scanf("%99[^\n]",strbuff),1)==0){
eat();
/* look at DIRECTION :) */
}
// handle_scanf() generated appropriate error msg if it doesn't return 0
}
Result
All in all, this code handles scanf mostly safely and can indeed be used in a way that the user will only type , say :
$ ./myprogram
>>> look DIRECTION
# output
>>> | #cursor
If it is all done within a big loop inside main() .
Conclusion
In reality, you may end up needing to use both together if your program is complex enough :)
I hope my slightly delayed answer is of help :)
In case of any inaccuracies , or missing details, please comment and I will get back to you ASAP
Here's a good way to parse an inputted string using strtok and scanf with a limit of 99 characters
#include <string.h>
char command[99];
scanf("%[^\n]%*c", command); //This gets the entire string and spaces
char *token;
token = strtok(command, " "); //token = the first string separated by a " "
if (strcmp(token, "help") == 0){
//do function
}
else if (strcmp(token, "go") == 0){ //if the command has an argument, you have to get the next string
token = strtok(NULL, " "); //this gets the next string separated by a space
if (strcmp(token, "north") == 0){
//do function
}
}
You can keep using token = strtok(NULL, " "); until token = NULL signifying the end of a string
I have a method execfile which takes the path of an executable file and then executes it, I'm now trying to add the option for the user to enter arguments to the command being executed. I currently have it implemented like so:
int execfile(char *file) {
printf("Enter any arguments to %s: ", file);
char *arg = (char *) calloc(ARG_MAX, sizeof(char));
scanf("%s", arg);
execl(file, file, arg, NULL);
return -1;
}
This does function crudely in that I can execute /usr/bin/ps and enter el or -el at the argument prompt and the function will execute with both arguments as intended.
Ideally though it would be more elegant to be able to enter arguments as you traditionally would when executing a C program directly, so say enter -e -l at the prompt and have it correctly interpreted by the program (currently this wont work), or just immediately press enter to skip the prompt (currently have to enter at least some character).
I thought the best way to do this would be to declare an array char *argv[], set arg[0] = file then scan the rest of the input and place each separate argument in an array cell, then calling execv(file, argv).
I'm very new to C however and am unsure of how this can be implemented in the most efficient way, for example I was considering reading in the whole string first then using a loop to iterate through it character by character to recognise arguments to add to argv, but it seems a bit longwinded? Is there no way to read these arguments into the array directly from stdin?
Additionally I'm unsure what to set ARG_MAX to as I don't know what the limit on number of arguments is within C, and I don't know how to get it to recognise that the enter key has been pressed so to skip immediately.
Is there no way to read these arguments into the array directly from stdin?
In one go, without knowing how many whitespace delimited argument will be passed, and without additional parsing?
No.
I'm trying to write a quine program for the follow C source code:
#include<stdio.h>
char name[] = "Jacob Stinson";
int main(){
char *c="#include<stdio.h> char name[] = \"Jacob Stinson\"; int main(){char *c=%c%s%c; prinf(c,34,c,34);}";
printf(c,34,c,34);
}
I need to include the backslash before the " in the string in order to properly print out line 3, however, when I print out *c, I want those backslashes to be present, as to correctly copy the source code. Currently it omits the backslashes from the output.
Wanted to see if anyone knows how to go about doing this.
As the compiler interprets escape sequences in only one direction (deescaping them) I think there's no possibility to include an escape sequence in the code and make it appear as such in the listing. The compiler will always eliminate one of the backslashes on input of the source file, making it appear different on output. The printf uses %s format to allow for the recursive part of the problem and allow you to shelf print, and, as you have guessed correctly, you have to use integer versions of delimiting chars for the " delimiting chars. Why to use %c to be able to delimit the strings in your program if there's an alternative method to include escape sequences? By the same reason, I was not able to include any end of line delimiter, so I wrote the same problem in one line (without using the #include <stdio.h> line) My solution was a one line (without the final end of line.)
I'm working with Arduino.
I want to send Ctrl+z after a string in C. I tried truncating ^Z but that didn't work. So how to do that ?
Ctrl+Z = 26 = '\032' = '\x1A'. Either of the backslash escape sequences can be written in a string literal (but be careful with the hex escape as if it is followed by a digit or A-F or a-f, that will also be counted as part of the hex escape, which is not what you want).
However, if you are simulating terminal input on a Windows machine (so you want the character to be treated as an EOF indication), you need to think again. That isn't how it works.
It may or may not do what you want with Arduino, either; in part, it depends on what you think it is going to do. It also depends on whether the input string will be treated as if it came from a terminal.
I hacked this up as I needed similar
#include <stdio.h>
#define CTRL(x) (#x[0]-'a'+1)
int main (void)
{
printf("hello");
printf("%c", CTRL(n));
printf("%c", CTRL(z));
}
hope it helps 8)
I assume by "truncating" you actually meant appending.
In ASCII, CTRL+z is code point 26 so you can simply append that as a character, something like:
#define CTRL_Z 26
char buffer[100];
sprintf (buffer, "This is my message%c", CTRL_Z);
The sprintf method is only one of the ways of doing this but they all basically depend on you putting a single byte at the end with the value 26.
The following should work:
whatever you are trying to write append \032 at the end
For example:
strcpy(InputCommand,"hi\032");
GetSerialData(InputCommand,......); //this is my own function which uses serialPuts()