Shell in C - how to read and execute user input? - c

My assignment is to write a very simple shell in C. I don't have many sources given and I have just started learning C, I have used only scanf() and printf() and coded simple functions. I only know it supposed to be written with fork() and different exec(). I spent a lot of time analyzing other shells, but I don't understand the structure of programs and some functions, like:
parsing, why do we need parsing? If I know I won't use arguments in commands, I would like only to compare input with my own functions like help() or date() and execute them.
reading user input. Using the fgets() and then strcmp()?
executing, how does it works? How execvp() knows that the user input is a command(function) in my main or program in my program folder?

First, let me say this sounds like a daunting task for a person just learning C, unless it's very carefully restricted. It does not make sense for you to need to analyze other shells. I'd advise you talk to a teaching assistant about the scope of the assignment, and what you're expected to do given your experience.
To answer your questions, though:
why do we need parsing?
Parsing is (in this context) taking a sequence of characters and producing a data structure which your proagram can work on. Now, this could be a rather simple structure if there are not supposed to be any arguments, no multiple commands per line etc. However, you at least have to make sure that the user has indeed not used arguments, not written down an invalid command line, closed their open parentheses and so on.
If I know I won't use arguments in commands
The program is not written for the perfect user whose behavior you can predict. You must accommodate any user, who may insert just about anything; you'll need to notice this case and report an error.
reading user input. Using the fgets() and then strcmp()?
Remember that fgets() may not get through the entire line - if the line is longer than your buffer length minus 1. But perhaps you are guaranteed a line length limit? Or are allowed to fail on overly long lines?
Also, it might be the case that the user is allowed to use extra white-space as part of the line, in which case strcmp() might not give you want you want.
executing, how does it works? How execvp() knows that the user input is a command(function) in my main or program in my program folder?
Have a look at the man page for execvp() (and friends). Basically, what happens when you call execvp() is that the binary at a specified location gets run, and its command-line is what you pass as the second argument of execvp(). Suppose you run
execvp("/path/to/foo", "/path/to/foo", "bar");
so, the program at /path/to/foo is run. Like any program, its argv[0] is the path to itself. Its argc will be 2 and its argv[1] will be "bar". Its working directory (and user and group id) will be the current directory, and user and group id, of the process which ran the execvp(), so - not necessarily /path/to/foo.
Continuing the previous example, you could do:
chdir("/path/to");
execvp("foo", "foo", "bar");
at which time foo would run with argv[0] being foo.

Related

How does main force arguments it takes (from command line) to become strings

Im beginner who encounters concept of command line arguments for first time and it really confuses me.
When we write at command line after the programs name anything it gets converted to a string.
But how is main able to do that? Does it interact with system itself and tells system (instructs system itself) everything written here in command line is now argument to my program? Can it do that because system itself is written in c too so it can interact with it outside of program it itself is part of?
Also is there equivalent of that in gui?
And i understand it can take ints and convert them to strings. But what if we wanted to type in other data type as argument in command line.Like boolean for example, is it error like data provided not matching argument required by function?
In typical implementations, command-line arguments are parsed by the command-line shell and then passed to the program being executed. In Unix systems, a new program is executed by calling a system routine in the “exec” family, and the arguments are passed to those routines as a list of pointers to character strings (in one form or another).
In any hosted C implementation that conforms to the C standard, the program arguments are prepared before main is called. main does not interact with the system to do that; it is done before main is called.
But what if we wanted to type in other data type as argument in command line.
You cannot type any other data type. On your keyboard, you can only press keys. There is no key on your keyboard that creates a boolean value or even an int value like 3. When you press a key1, a signal is sent to the computer, with information about which key you pressed. Software on the computer interprets the pressed key. Roughly, in a “normal” text-input terminal mode, if the pressed key represents some character, like “A” or “3” or “+”, that character will be added to a buffer that is being used to collect user input. When the user presses Return/Enter or does certain other actions, that buffer of characters will be sent to the current program attached to the terminal. (There may be other terminal modes available in which keypresses are processed differently, such as a sent directly to a program routine, which might interpret them as commands in a game being played.)
Footnote
1 The specifics of this may depend on the key and the hardware. Pressing a Shift key might not send a signal to the computer; the keyboard might merely use that to affect the signals it sends when you press another key on conjunction with Shift.
The system passes everything as a string, because it cannot know what kind of datatypes your program can handle or how it stores them. It does not know that your program is written in C. For all it knows, it could be Python, Java, JavaScript, Rust etc. and those all have and store different kinds of datatypes slightly differently, so the system cannot assume anything. Therefore programs just receive everything as a string and leave it to the programmer to convert it to the right datatypes if they so wish.
Does it [main()] interact with system itself and tells system (instructs system itself) everything written here in command line is now argument to my program?
The command line is always a sequence of characters, and the operating system cannot know what arguments actually mean or in which programming language a program was written in. So the program receives the command line as a sequence of characters.
Any program written in a high-level programming language (meaning, not assembly) has some kind of start-up code. This code makes sure that the first executed user code gets the environment prepared as specified for the programming language.
For example, the C standard promises that any static variables is initialized, implicitly or explicitly. And concerning your question, the C standard promises that the command line is processed in a way that main() receives the number of arguments, an array of pointers to them, and that all of the arguments are zero-terminated.
Such a start-up code is commonly written in assembly language, because only this is able to access any necessary resource like CPU registers or special functions of the operating system.
Can it do that because system itself is written in c too so it can interact with it outside of program it itself is part of?
The operating system can be written in any programming language. It "just" needs to fulfill the specified requirements, commonly called API, application programming interface.
Also is there equivalent of that in gui?
It depends on what you mean by "GUI".
If you prepare some preferences of, let's say, your IDE, you often see GUI dialogs offering text fields for command lines.
Additionally, many desktops allow to drag-and-drop files on a program's executable. The filenames (with full path) are given as arguments in the command line to the program. Astonishingly, this also works on Windows.
And i understand it can take ints and convert them to strings. But what if we wanted to type in other data type as argument in command line.Like boolean for example, is it error like data provided not matching argument required by function?
No command line takes integers. Such arguments are just sequences of digits and included in the command line as any other sequence of characters.
Each character is encoded in an integer, though. This encoding depends, well, on the encoding like UTF-8 or (extended) ASCII.
If your program needs to interpret specific arguments as integers, booleans, any type you want, it has to do the conversion itself. Fortunately, most programming languages provide libraries with lots of functions that help us doing so.

How can I reuse a text file in a C program without having a file pointer?

My program has uses a text file to input data, but can't use a file pointer in the program itself. I'm supposed to use the < file.txt in the Linux terminal. I can't do it any other way because it's a college assignment, so please don't waste my time with rewind or other functions that require a pointer. I just need to be able to basically restart the text file that I already have open.
The C library provides a FILE pointer for standard input, stdin from <stdio.h>. However, it might not support all the functions you want, since it can be connected to another command (if you pipe your input from somewhere else) or the terminal (if you don't use input redirection). If you need to be able to support these, which you probably do, you won't be able to successfully call fseek or any of the related functions.
If that's the case, then this is fundamentally impossible. The computer doesn't store all the data which was sent to your program, so there is no way to go back and get it because there's nowhere to get it from. Instead, you either need to store the input yourself, or rework your algorithm to only need a single pass over the input data.

System function in C language - creating user from CodeBlocks (C) program

I have a problem, i wanted to do a program which will be used to make an account on PC. I write from keyboard name and password which is used to create new user. Everything was okey to the time when i wanted to write command in code, I used "system("net user xxx /add")" where xxx == name of the new user, yes it is working like that, but i want to change xxx to the variable which is written from the keyboard. I mean that, the name of the new user which is creating, the user of program will choose. Any help?
int main()
{
char login;
printf("Login of the new user:");
scanf("%s",&login);
system("net user xxx /add");
return 0;
}
You can use the function sprintf to build a string. It works similar to printf but produces its output in a character array rather than as output to stdout.
For example:
char buffer[100], login[100];
printf("Login of the new user: ");
scanf("%s", login); // notice there is no & operator here
sprintf(buffer, "net user %s /add", login);
system(buffer);
Safer code would use snprintf, which also takes the length of the destination character array as a parameter to ensure that it does not overflow. Also, I'd use fgets rather than scanf as it is inherently safer for the same reason that snprintf is safer than sprintf.
As a side note, for what you're trying to do (add a new user on a Windows system), there are Windows APIs specifically designed for that purpose that you should use rather than calling the net command from system, because these methods will indicate success and failure of attempting to add the user. I concede that there is a very steep learning curve to the Windows API, but if it lets you avoid using the system function, it really is the way to go.
Firstly, you do notscanf towards a variable which is only 1 byte (login). This will just result in buffer overflow and application crash. Since I have the feeling that this is a homework kind of assignment I recommend using an array of chars with help from Since we have snprintf, why we don't have a snscanf?. Otherwise, this answer: C - scanf() vs gets() vs fgets() will give some good directions on how to read (more) safely from the stdin.
Secondly you will need another buffer for creating the net add ... command. For this I recommend using snprintf (https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx) in order to achieve the desired result in a safe manner.
Intentionally I do not post code here to solve this problem, in order to not to hinder your learning, feel free to ask more questions.

Basic C operations

I have this task that I'm stumbling upon. I'll first go ahead with the description and requirements for it and then point out what I'm having trouble coping with.
Build a block scheme and a program that reads another C-based program and finds:
Number of lines in the program
Number of conditional operators if and if/else
The program needs to start with a menu disposing with the following options:
Reading the program from a file and storing the result in a separate file (the user has to input the names of both files whilst the program
file has to end in “.C”)
Reading the program from a file and outputting the result on the screen (the user selected file has to end in “.C”).
Reading of the program from the keyboard and inserting a file selected by the user;
Reading of the program from the keyboard and outputting on the screen; The program has to be written in different functions for each
operation.
Reading of the file has to be done line by line.
First question is what is a block scheme and what 'reading from a keyboard' refers to?
Thanks in advance
A block scheme is another, albeit less used, term for a block diagram, or a flow chart.
A block diagram is:
a diagram of a system in which the principal parts or functions are represented by blocks connected by lines that show the relationships of the blocks.
Or in other words, it's a way of using pen and paper to construct what will be the flow of your program prior to writing any code. Typically they used a set of shapes to mean certian things, circles/ovals can show states/starts/ends, a rectangle may repersent a function, a diamond could be used for a decision point, etc.
As you'er asking about a specific homework problem it is best to clairify what your instructor expects, but a quick example of a block scheme for a progrm could be something like:
This maybe too algorithimic for what your instcutor wants, they may just want to see blocks only stating "get input from user", "open a file" showing the flow at a moduler level and ignorning the decision details as in checking error conditions.
Now as far as "•Reading of the program from the keyboard", as I didn't write the assignment, it's again speculation, but I'm pretty sure your instructor is simply saying get input via stdin. The mechanism would be dependent on what you're learning in class, but in C, just something like scanf() or fgets() or whatever you know can get the input from the keyboard.

CommandLineToArgvW equivalent on Linux

I'm looking for an equivalent function to Windows' CommandLineToArgvW.
I have a string, which I want to break up exactly the way bash does it, with all the corner cases - i.e. taking into account single and double quotes, backslashes, etc., so splitting a b "c'd" "\"e\"f" "g\\" h 'i"j' would result into:
a
b
c'd
"e"f
g\
h
i"j
Since such a function already exist and is used by the OS/bash, I'm assuming there's a way to call it, or at least get its source code, so I don't need to reinvent the wheel.
Edit
To answer why I need it, it has nothing to do with spawning child processes. I want to make a program that searches text, watching for multiple regular expressions to be true in whatever order. But all the regular expressions would be input in the same text field, so I need to break them up.
GNU/Linux is made of free software and bash is free software, so you can get the source code and improve it (and you should publish your improving patches under GPL license).
But there is no common library doing that, because it is the role of the shell to expand the command line to arguments to the execve(2) syscall (which then go to the main of the invoked program).
(this was different in MS-DOS, where the called program had to expand its command line)
The function wordexp(3) is close to what you may want.
You may want to study the source code of simpler shells, e.g. download sash-3.7.tar.gz
If you want it to expand a string exactly the way Bash does, you will need to run Bash. Remember, Bash does parameter expansion, command substitution, and the like. If it really needs to act exactly like Bash, just call Bash itself.
FILE *f = popen("bash", "r+");
fprintf(f, "echo %s", your_string);
fgets(buffer, sizeof(buffer), f);
pclose(f);
Note that real code would need to handle errors and possibly allocating a bigger buffer if your original is not large enough.
Given your updated requirements, it sounds like you don't want to parse it exactly like Bash does. Instead, you just want to parse space-separated strings with quoting and escaping. I would recommend simply implementing this yourself; I do not know of any off the shelf library that will parse strings exactly the way that you specify. You don't have to write it entirely by hand; you can use a lexical scanner generator like flex or Ragel for this purpose.

Resources