I have to restrict when the user gives "--" as an option.
For example:
./test --
should thrown an error.
For the sake of parsing I am using getopt
How to achieve this using getopt?
getopt is intended for implementing programs which conform to the POSIX Utility Syntax Guidelines where -- has a special meaning of causing all subsequent arguments to be treated as non-options even if they have the form of options (e.g. rm -- -f to remove a file named -f). There's no explicit way to suppress this behavior. You could try checking whether argv[optind-1] is "--" after getopt finishes, but this would give false positives in cases where the "--" was the argument to an option that takes an argument (something like -f -- where -f needs an argument) which you might need to work around.
If you really want argument handling that does not conform to the Utility Syntax Guidelines you might be better off rolling your own from scratch.
Related
I have a bash command
"cp -rf "TEMP_DIRECTORY"/* "USER_DIR"/";
I am using that inside a C program.
TEMP_DIRECTORY and USER_DIR are defined using #define.
I want to get the list of files inside TEMP_DIRECTORY. Can I do that with the above approach?
If we treat the provided line of code as the actual code fragment you intend to use to perform the desired task, the answer to your query is no.
Assuming:
#define TEMP_DIRECTORY "/tmp"
#define USER_DIR "/home/joe"
Then the provided statement is simply an expression representing a string constant, and by itself performs no action. GCC with sufficient warnings will tell you that:
.code.tio.c: In function ‘main’:
.code.tio.c:5:1: warning: statement with no effect [-Wunused-value]
"cp -rf "TEMP_DIRECTORY"/* "USER_DIR"/";
^~~~~~~~~
To make the command take effect in your C code, you will need to pass the string to a function, such as system() or popen().
const char *cmd = "cp -rf "TEMP_DIRECTORY"/* "USER_DIR"/";
system(cmd);
However, since system() and popen() use the shell to execute the commands, they are susceptible to command injection attacks. If your TEMP_DIRECTORY and USER_DIR actually contain shell environment variables that you want expanded, it is better that you expand them yourself with getenv(), form the complete paths, and then invoke the command via fork() followed by one of the exec() variants. If you need the glob expansion, there is the POSIX utility glob(), or you can walk the directory yourself using functions from <dirent.h>.
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'")
I'm writing a program that uses termcaps and I need to know which kind of terminal I am using.
I am aware I can get TERM variable via getenv("TERM"), but I can launch my program with "$ env -i ./myprog" and TERM will not be set.
So how can I determine which terminal type must I use?
May I safely set TERM variable to xterm/xterm-256color in my application?
Will it cause non-portability issues?
Is there a method to do such (get termtype) safely?
I've red many manuals (getty - getttab - tty - ttys) and posts but I can't spot any solution.
I'm also worried because if I launch a shell (like zsh or tcsh) I have issues with some keys.
For example launching zsh like so:
$env -i zsh
will cause troubles with arrows and any keys implying termcaps (even Ctr-d).
Instead bash and tcsh will behave normally on many keys but not all.
If you're actually using termcap (and not some minimal implementation such as busybox), you're likely using a system that provides tset, which can offer the user a default choice for TERM that can be modified.
Something like this:
eval `tset -s vt100`
in the shell initialization would work.
Actually tset isn't limited to termcap-systems, but that's where it started.
Further reading:
tset, reset -- terminal initialization (FreeBSD)
tset, reset - terminal initialization (ncurses)
tset, reset - terminal initialization (NetBSD)
It is (somewhat) safe to set TERM=vt100 as default (Ctx's suggestion), as most terminal emulators are set to emulate that anyway. I'd recommend you to print a warning in this case, though.
Is there a way to make getopt() or getopt_long() recognise double character option?
example: ./a.out -my argument where my is single command.
You can use getopt_long_only, which will try to process options as long ones even if there is only one - sign before them.
This function is GNU extension, as well as getopt_long.
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.