Put operands first in getopt() - c

Using the getopt() function in C, it is possible to do that:
program -a arg_for_a -b arg_for_b -c operand1 operand2
and it works with no problem.
But, how to make it work this way?:
program operand1 operand2 -a arg_for_a -b arg_for_b -c
In this case, every argument, including the -a, -b etc. are considered to be operands.
I'm trying to make just like like gcc or ssh does:
gcc code.c -o executable
ssh user#host -i file.pem
That is, no matter in what position the options and the operands come, they are recognized properly.
How to make options be recognized properly wherever they are, and every word that do not follow an option be recognized an operand?

If you use the GNU C library's implementation of getopt, then it will work like all GNU utilities do, because almost all of them use it. In particular, (quoting from man 3 getopt):
By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end.
That's not quite the same as gcc. gcc cares about the relative order of optional and positional arguments. (For example, it makes a difference where -l goes in the command line.) To do that, you'll have to tell GNU getopt to not permute the arguments. Then, every time getopt reports that it's done, optind will have the index of the next positional argument (if any). You can then use that argument, increment optind, and continue using getopt, which will continue at the next argument.

Related

Executable path as a variable parameter to exec

I've an application which needs to call a specific program 'mips64-unknown-linux-gcc' for linking all objects from a script with all required args for linking.
I am writing an exec function to call the compiler passed by script along with it's args. For this I wrote the code:
//prog.c : gcc prog.c -o prog
int main(int argc, char *argv[]) {
execvp("mips64-unknown-linux-gcc",argv);
}
This works, but the mips64-unknown-linux-gcc and argv are variables from script input.
I need execv first argument to be a variable which is compiler to be invoked. I can somehow (maybe) retrieve it by getenv(”CC”) but due to other dependencies my requirement is that exec shall accept the compiler and args at runtime (something like below). Is there any way I can do this?
./prog mips64-unknown-linux-gcc --sysroot=<<...>> -O3 -Wl -L <<...>> -L <<...>> -I <<...>> -L <<...>> abcd.o a1.o b2.o -o prog
I described my problem at my best. Please ask if anything is not clear.
From your example command line it seems that you want to take the first argument from command line as your command to execute and everything else should be passed to that command.
That is basically the same command line execpt for the first argument.
This makes things rather easy.
Looking at argv you will find these string:
char *argv[] = {"proc","mips64-unkown-linux-gcc", "--sysroot=<<...>>", ..., "-o", "prog", NULL};`
You can use that and call your command:
execvp(argv[1], argv+1);
Of course you should check whether you have at least one argument.
If you want do filter some options and handle in your own program instead of blindly passing it to execvp you must rebuild your own array of arguments where you do not include those options.

How do you execute a bash script with "system()" when there are spaces in the file path?

I created a simple bash script called "myscript.h" I gave it a .h extensions for reasons that I won't disclose here. This bash script lives in "/var/ftp/something with spaces".
From the terminal, I can type in "/var/ftp/something with spaces/myscript.h" and the script works perfectly.
However, from within my C program, I type in
system("/var/ftp/something with spaces/myscript.h")
and it complains that "/var/ftp/something" is not found. I've changed my system call to the following with forward slashes:
system("/var/ftp/something\ with\ spaces/myscript.h")
However, it still complains that "/var/ftp/something" is not found. Assuming I can't change the directory names, how can I get around this?
Thanks!
To run a single script, you might avoid the system(3) library function (and use lower level system calls like fork(2), execve(2), waitpid(2)... which are used by the implementation of system(3)), or you could quote the script name when passing it to system(3).
For more details, read Advanced Linux Programming.
On Linux, system(3) is documented to fork a /bin/sh -c process. See sh(1p). And that POSIX shell has some quoting rules. You might use double-quotes and backslashes appropriately. So you would construct (and perhaps check) the string passed to system(3) (perhaps using asprintf(3) or snprintf(3) with care). Be aware that the C compiler also has (different) quoting conventions for string literals.
In general, you should avoid code injection (imagine a naughty user giving some a; rm -rf $HOME &; input as a "directory" name; you don't want to run system(3) on the weird "/var/ftp/a; rm -rf $HOME &;/myscript.h" string)
In your particular case, I recommend using fork(2), execve(2) (perhaps thru some carefully choosen exec(3) function), waitpid(2)... appropriately. This has the slight advantage to avoid depending on and running /bin/sh so could be slightly faster (by a millisecond).
Understand more the role of an Unix shell; for example, read about the various shell expansions in bash (they are similar to those mandated by POSIX sh) and be aware of globbing. See glob(7)
Note that you're adding quotes when running from the shell. You need to do the same here. Add quotes to the path name you're sending to system:
system("\"/var/ftp/something with spaces/myscript.h\"")
This should work with gcc version 5.4.0
system("\'\'/var/ftp/something\\ with\\ spaces/myscript.h\'\'");
Just put the filename inside single quotes
system("rm '/var/ftp/something with spaces/myscript.h'")

Handle command line arguments? [duplicate]

This question already has an answer here:
Pass File As Command Line Argument
(1 answer)
Closed 8 years ago.
So, I can make my program run and all, but if I'm given
$ ./a.out -f Text.txt
I'm just not sure how to get the program to make the connection that -f indicates a file. What is the logic for doing this?
The main function has signature int main(int argc, char**argv); so you can use the argc (which is positive) & argv arguments. The argv array is guaranteed to have argc+1 elements. The last is always NULL. The others are non-nil, non-aliased zero-byte terminated strings. Notice that often some shell is globbing the arguments before your program is started by execve(2): see glob(7).
For example, if you type (in a Linux terminal) myprog -i a*.c -o foo.txt and if at the moment you type that the shell has expanded (by globbing) a*.c into a1.c and a2.c (because these are the only files whose name start with a and have a .c suffix in the current directory), your myprog executable main program is called with
argc==6
argv[0] containing "myprog" (so you could test that strcmp(argv[0],"myprog") == 0)
argv[1] containing "-i"
argv[2] containing "a1.c"
argv[3] containing "a2.c"
argv[4] containing "-o"
argv[5] containing "foo.txt"
argv[6] being the NULL pointer
In addition you are guaranteed (by the kernel doing the execve(2)) that all the 6 argv pointers are distinct, non-aliasing, and non-overlapping.
GNU libc gives you several ways to parse these arguments: getopt & argp. getopt is standardized in POSIX (but GNU gives you also the very useful getopt_long(3))
I strongly suggest you to follow GNU conventions: accept at least --help and --version
The fact that e.g. -f is for some option and not a file name is often conventional (but see use of -- in program arguments). If you happen to really want a file named -f (which is a very bad idea), use ./-f
Several shells have autocompletion. You need to configure them for that (and you might configure them even for your own programs).

Why is libargtable behaving strangely with optional arguemnts?

On the off-chance that anyone uses argtable as a command line argument parser for C-Code, here is my question:
My Intention
I'm programming in C on a Linux platform using the most recent version of the argtable2 library.
What I want to archive is have a program that takes multiple input files and an optional option (let's call it -o). If -o is not provided as an option in a shell call, no output is written by the program whatsoever. If -o is provided by itself the program's output is written to a default file called "output.txt". If the option is provided together with a file name, e.g. -o other.txt, the output should be written to the file name that was given - in this case to "other.txt".
The Problem
In all my tries argtable misbehaved. It interprets the optional value given along with -o as an input file. So ./program -o other.txt inputfile1.dat inputfile2.dat inputfile3.dat would be interpreted as having four inputfiles - the three "inputfile*.dat"s and "other.txt" which is supposed to be the output file.
Reproduce the problem
Here is a shell session to illustrate, what I mean. It uses a minimal example that produces the problem. I'm not sure if I did something wrong or if it is a bug in libargtable2:
confus#confusion:~$ gcc -o argbug argbug.c -largtable2
confus#confusion:~$ ./argbug
Error: missing option INPUT-FILES
Usage:
./argbug [-o [<file>]] INPUT-FILES
-o [<file>] File to write output to. Default is 'output.txt'.
Omit whole option to discard all output
INPUT-FILES Input files
confus#confusion:~$ ./argbug inputfile1.dat inputfile2.dat inputfile3.dat -o other.txt
inputfile[0] = inputfile1.dat # this is okay output
inputfile[1] = inputfile2.dat # as is this line
inputfile[2] = inputfile3.dat # also okay output
inputfile[3] = other.txt # "other.txt" is falsely recognized as an input file
outputfile = output.txt # should be "other.txt" instead of the default "output.txt"
Either way neither I nor Steward, the author of argtable seem to have time to really look into my problem. Any ideas?
I ran your test and found the same problem. Looking into the source, it seems libargtable is handling it correctly, but it boils down to getopt behavior.
If you look beginning at line 647 in getopt.c, you can see that getopt first checks if there is an argument attached without any space in between the option and the argument (e.g. -oother.txt). If that is the case, it handles it. That is the only case in which it will notice an optional argument.
To test this, try
void *argtable[] = { argOutput, end };
in your testcase, and then
./argbug -o other.txt
You will see that it gives an error.
However, it then has an additional piece of code which checks if there is a required option. If so, is will perform an additional search for options to satisfy this even if there is a space between the flag and argument.
Hint for looking at the code: has_arg is an enum with 0=No argument 1=Required arguemnt 2=Optional argument
Short Answer
libargtable will not process optional arguments if there is a space between the flag and the argument. Remove the space and it should work.
I might consider this a bug, but perhaps some people like this behavior.

double-dash command line options instead of getopt

How can I accept a command line argument this way:
./a.out --printall
so that inside my program, I have something like
if (printall) {
// do something
}
I don't want to do this:
if (argc == 2)
//PRINTALL exists
since my program can have multiple command line options:
./a.out --printread
./a.out --printwrite
Secondly, I don't want to use getopt , such that the command becomes
./a.out -printall 1
I just find ./a.out --printall cleaner than ./a.out -printall 1
Edit:
I have seen programs that do this:
./a.out --help
I wonder how they work.
(About the argument parsing part of the question:)
You will need getopt_long() from <unistd.h>. This is a GNU extension.
For greater portability, you might consider Boost program options, though that's a compiled library.
Command line arguments cannot be used to trigger conditional compilation. The program has already been compiled before the program is run.
This is a very pedantic answer. For command-line options in general, see Kerrek SB's answer.

Resources