I'm trying to create a simple shell program which execute the program specified in input. There are two main function: scanner() (use strtok to split the input in token) and execute() (fork the process and execute the program).
Unfortunately it doesn't work... I've tried to print string[0] at the end of scanner() and at the beginning of execute(). The first time the output is correct but the second time string[] seems to be modified in a sequence of random numbers so execvp() doesn't work...
I really can't figure out why the values of string[] changes, probably is a very stupid error but I can't see it. I really need your help! Thanks in advice.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define DIM 256
int scanner(char*[]);
int execute(char*[]);
int main()
{
char* string[DIM];
scanner(string);
execute(string);
}
/* scan: read the input in token*/
int scanner(char* string[])
{
char input[1024];
char delimit[]=" \t\r\n\v\f";
int i = 0;
if(fgets(input, sizeof input, stdin)) {
string[i] = strtok(input, delimit);
while(string[i]!=NULL){
i++;
string[i]=strtok(NULL,delimit);
}
return 0;
}
return 1;
}
/* execute: execute the command*/
int execute(char* string[])
{
int pid;
printf("%s\n", string[0]);
switch(pid = fork()){
case -1:
return 1;
case 0:
execvp(string[0], string);
return 1;
default:
wait((int*)0);
return 0;
}
}
The string variable input in scanner is a local variable, with storage class "auto". That means that when that function returns, that variable disappears, and the memory it occupied can be re-used for other things. That is unfortunate, since strtok returns pointers into that string variable.
Related
I input age as 30 but its not printing my if statement. Can i know why ? Thanks
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#define BUFFSIZE 512
int main()
{
int a;
char *buffer[BUFFSIZE];
write(1,"Please Enter your age: ",23);
a=read(0,*buffer,100);
if(a>21)
write(1,"You are an adult",16);
return 0;
}
This
char *buffer[BUFFSIZE];
declares an array of uninitialized pointers.
This
a=read(0,*buffer,100);
passes the first uninitialized pointer to read, so almost certain returns an error (probably EFAULT).
If you were to fix that (remove the * from both lines), it would still return 3 if you enter 30Enter on the keyboard (3 characters entered)
Fixing all that, you end up with something like:
int main() {
int len;
char buffer[BUFSIZE];
write(1,"Please Enter your age: ",23);
len=read(0,buffer,BUFSIZE-1);
if (len <= 0) {
write(1, "invalid input", 13);
} else {
buffer[len] = '\0';
char *end;
int age = strtol(buffer, &end, 0);
if (*end != '\n')
write(1, "input not a (just) a number", 27);
if(age > 21)
write(1,"You are an adult",16);
}
return 0;
}
There's more stuff you can do with error checking (for example, you might want to ignore spaces on the end of the line, or other questionable input), but this shows where to start.
If you consult the documentation for read you will see that the return value is the number of bytes read. You will need to read the characters in buffer and convert them from a string to an int instead of doing what you are doing.
The C standard library provides functions like atoi and scanf that you can use to convert the string into a number, but it's pretty easy to do it yourself and it's a good exercise for a new C programmer.
Try this out, which changes read for more commonly used fscanf:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a;
write(1,"Please Enter your age: ",23);
fscanf(stdin, "%d",&a);
if(a>21)
write(1,"You are an adult",16);
return 0;
}
I have been trying to write a C program where the user inputs a command-line argument and the program checks if it is all number digits or not. If that is true, the program simply reprints what the user put in. If that is false and there is at least one non-number mixed in, the program just prints the word "bad" once.
What I have come up so far consists of the program checking each character of the input individually. However, what ends up happening once I put in a mixture of numbers and letters in is that the program would still print the numbers, and then once it gets to printing a letter, it prints the word "bad".
Here's an example:
I put ./program 100x into the terminal
=> Expected result: bad
=> Actual result: 100bad
Here's the code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main (int argc, string argv[])
{
for(int i = 0; i < strlen(argv[1]); i++) {
if (isdigit(argv[1][i]) != 0) {
printf("%c", argv[1][i]);
return 0;
} else {
printf("bad");
return 1;
}
}
}
First, I don't think your program is doing what your describe because it returns at the first digit encountered. Then, as soon as you see a digit you print it, you want to first check that your input is right and then print it. Taking your implementation, you could modify it like that:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main (int argc, string argv[])
{
// checking input
for(int i = 0; i < strlen(argv[1]); i++) {
if (!(isdigit(argv[1][i]) != 0)) {
printf("bad\n");
return 1;
}
}
// here you know your input is good
// prints input
printf("%s\n", argv[1]);
return 0;
}
Try this one :
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
//for(int i = 0; i < strlen(argv[1]); i++)
{
//if (isdigit(argv[1][i]) != 0)
if(strspn(argv[1], "0123456789" )== strlen(argv[1]))
{
printf("%s", argv[1]);
return 0;
}
else
{
printf("bad");
return 1;
}
}
}
Your logical error is that your program checks one character and immediately prints a digit or a bad message, and so continues with each character. However, you need to try writing an algorithm that first checks the whole string for characters other than numbers. If the algorithm finds at least one character (the result is false), it can close loop by using "break" keyword and print "bad" after. OR if all characters are numbers, you can return the value true and only then print them on the screen.
I want to use read() and write() methods for reading from and writing to console instead of the original scanf() and printf(), as the first ones has system calls support using signals.
I have to make a mini Unix shell, which forks into children when performing a command. Here is my initial try for testing the reading and writing:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define COMMAND_LENGTH 1024
#define NUM_TOKENS (COMMAND_LENGTH / 2 + 1)
void read_command(char *buff, char *tokens[], _Bool *in_background) {
// to be implemented later
}
void createStr(char **str) {
if (*str != NULL) {
free(*str);
*str = NULL;
}
*str = (char*)malloc(sizeof(char) * COMMAND_LENGTH);
}
void delStr(char** str) {
if (*str != NULL) {
free(*str);
*str = NULL;
}
}
int main(int argc, char *argv[]) {
char input_buffer[COMMAND_LENGTH];
char *tokens[NUM_TOKENS];
char *inp = NULL;
while (true) {
write(STDOUT_FILENO, "> ", strlen("> "));
createStr(&inp);
read(STDIN_FILENO, input_buffer, sizeof(char) * COMMAND_LENGTH);
strcpy(inp, input_buffer);
write(STDOUT_FILENO, inp, strlen(inp));
_Bool in_background = false;
read_command(inp, tokens, &in_background);
}
delStr(&inp);
return 0;
}
My output for sample inputs are not the desired ones. Here is a sample output:
> Peterson
Peterson
��> Makr
Makr
son
��> Mark
Mark
son
��> Jon
Jon
son
��>
I don't know what is going on. Like why the special characters are showing up, as well as having parts of my last input in my new input. I need help in this.
You must store the return value of read, verify that read completed and null terminate the string before passing it to strcpy. read can indeed be interrupted by a signal and must be restarted in this case. read can also return a partial line, you should keep reading and concatenating into the command buffer, carefully reallocating the buffer if needed, until you receive either an end of file of the character '\n'.
Here is the general problem:
The program must fork() and wait() for the child to finish.
The child will exec() another program whose name is INPUT by the user.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
int status;
char input[BUFSIZ];
printf(">");
scanf("%s",input);
char *args[] = {"./lab1"};
pid_t pid = fork();
if(pid==0){
execvp(args[0],args);
}else if(pid<0){
perror("Fork fail");
}else{
wait(&status);
printf("My Child Information is: %d\n", pid);
}
return 0;
}
My problem is getting the user to input a program name to run (at the ">" prompt) and getting that input into execvp (or another exec() function if anyone has any ideas)
I'm going to hold off lambasting you for using scanf("%s") for now, though you should be aware it's really not robust code.
Your basic task here is going to be taking a character array entered by the user and somehow turning that into an array of character pointers suitable for passing to execvp.
You can use strtok to tokenise the input string into tokens separated by spaces, and malloc/realloc to ensure you have enough elements in an array to store the strings.
Alternatively, since you already have a potential buffer overflow issue, it may be good enough to just use a fixed size array.
For example, the following program shows one way of doing this, it uses a fixed string echo my hovercraft is full of eels and tokenises it to be suitable for execution:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static char *myStrDup (char *str) {
char *other = malloc (strlen (str) + 1);
if (other != NULL)
strcpy (other, str);
return other;
}
int main (void) {
char inBuf[] = "echo my hovercraft is full of eels";
char *argv[100];
int argc = 0;
char *str = strtok (inBuf, " ");
while (str != NULL) {
argv[argc++] = myStrDup (str);
str = strtok (NULL, " ");
}
argv[argc] = NULL;
for (int i = 0; i < argc; i++)
printf ("Arg #%d = '%s'\n", i, argv[i]);
putchar ('\n');
execvp (argv[0], argv);
return 0;
}
Then it outputs the tokenised arguments and executes it:
Arg #0 = 'echo'
Arg #1 = 'my'
Arg #2 = 'hovercraft'
Arg #3 = 'is'
Arg #4 = 'full'
Arg #5 = 'of'
Arg #6 = 'eels'
my hovercraft is full of eels
I am a C beginner and this is my C code:
#include <stdio.h>
#include <stdlib.h>
main()
{
printf("Hello, World!\n");
return 'sss';
}
That will show an error. So how can I return a string in C code?
If you are looking to return a string from a function (other than main), you should do something like this.
#include <stdio.h>
const char * getString();
int main()
{
printf("Hello, World!\n");
printf("%s\n", getString());
return 0;
}
const char * getString()
{
const char *x = "abcstring";
return x;
}
The magic is in the key word static which preserves the memory content of the string even after the function ends. (You can consider it like extending the scope of the variable.)
This code takes one character each time, then concatenates them in a string and saves it into a file:
#include <stdio.h>
#include <conio.h>
char* strbsmallah ()
{
static char input[50];
char position = 0, letter;
scanf("%c", &letter);
while (letter != '~') { // Press '~' to end your text
input[position] = letter;
++position;
scanf("%c", &letter);
}
input[position] = '\0';
char *y;
y = (char*) &input;
//printf("%s\n ", y);
return y;
}
int main() {
printf("\n");
FILE *fp;
fp = fopen("bsmallah.txt", "w+");
fprintf(fp, strbsmallah());
while (!_kbhit())
;
return 0;
}
You could do this in a way similar to scanf. In other words:
void foo(char **value_to_return) {
*value_to_return = malloc(256); // Store 256 characters
strcpy(*value_to_return, "deposited string");
}
int main() {
char *deposit;
foo(&deposit);
printf("%s", deposit);
return 0;
}
You don't return a string. Applications exit with an integer exit code.
Conventionally, exiting with a return of 0 will always show that your application exited without error / completed. You return an integer other than 0 to show that your application exited abnormally.
You could throw an exception and handle it with a logging method higher in the call stack, or you could just return something other than 0 and make sure you had it documented in your release notes as to what each error integer means.
Sadly there is no way to do that.
You could add something to the end of your C program like:
int main()
{
int err = 0; // 0 is "success" is most C programs
printf("Hello, World!!\n");
switch( err )
{
case 0:
printf("Program shutdown successfully!\n");
break;
case 1:
printf("We had an issue somewhere. Please fix your input data\n");
break;
//case 2, 3, etc...
};
return err;
}
You might be able to use environment variables for that. Not sure though.