Custom completion in shell - c

I need to develop a utility that will take command line arguments as follows:
$ lsm -g <group> -t <type> -d <device>
My project manager wants that when we type any argument like lsm -g and press Tab, then a function will be called to run a database query and fetch help for the user about the value of the option.
Similar to how terminal behaves in this case:
$ cd <tab>
.bash_history .local/
.bash_logout .log-report.log.swp
.bash_profile .macromedia/
.bashrc .mozilla/
.cache/ Music/
.config/ .mysql_history
.dbus/ .nautilus/
Desktop/ .opera/
Documents/ .orc/
Downloads/ .p2/
.eclipse/ Pictures/
I have tried these approaches:
Use fork() to create a child to call the help function.
Use execv() to run a help function using a seperate binary.
But both require that lsm be running.
I'm not sure how to proceed further.

As various commentators have noted, tab-completion is implemented in the shell, not in the program which is about to be executed.
Most shells have frameworks for implementing custom tab-completion. In the case of bash, it is implemented with the help of the readline library. There is extensive documentation in the bash manual and there are a variety of tutorials kicking around the internet, such as this one from Debian (not an endorsement, just the result of a quick Google search).

Related

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'")

C to NASM conversion

I'm trying to find a way to convert simple C code to NASM assembly. I have tried using objconv and downloaded and unzipped and built it since I am using a MAC; however, it doesn't seem to be working. I keep getting "-bash: objconv: command not found". Does anyone know another way or can help me solve the -bash error.
Bash is the program that takes the words you type in a terminal and launches other programs. If it is reporting an error, it is because it cannot find the program you want to run (at least in this case).
You need to either find a pre-packaged installation of objconv, or you need to do the work to "integrate" your copy of objconv yourself.
If you can identify the executable you want to run (probably called objconv) you need to add that to your path. The easiest way (if it is just for you) is to verify that your ~/.bashrc or ~/.bashprofile has a line that looks something like
PATH=$PATH:${HOME}/bin
Don't worry if it doesn't look exactly the same. Just make sure there's a ${HOME}/bin or ~/bin (~ is the short version of ${HOME}).
If you have that then type the commands
cd ~/bin
ln -fs ../path/to/objconv
and you will create a soft link (a type of file) in your home binary directory, and the program should be available to the command line.
If you create the file, and nothing above has any errors, but it is not available to the command line, you might need to set the executable bit on your "real" (not link) copy of objconv.
If this doesn't work, by now you should be well primed for a better, more specific question.
If you have gcc installed, try gcc -masm=intel -S source.c to generate assembly files in a syntax very similar to that of MASM.

How to know if a C program supports an option

I'm developing a script that checks the version of some installed C programs. The version check is performed with the --version option. However, this option may not be implemented in all the checked programs. When the option is implemented I use:
version=$(./$program_name --version)
But when it's not, the program just starts executing.
If I just execute the program in the background and the stop it if continues running, I can never get the version number. Is there a way to check whether the option is implemented without letting the program run?
Not completely waterproof but I nice start is using strings:
strings /usr/bin/git | grep -- --version
If you are referring to any arbitrary C program, no there is currently no way to reliably check if a program supports an option. You can try guessing with a mixture of ./program --help, ./program --usage, ./program -h and ./program --this-option-is-a-lie-or-some-other-bogus-option-to-give-usage-information. However, it all ultimately boils down to guesswork.
On RedHat and derivates using yum and rpm you can ask the rpm package manager:
$ rpm -q --whatprovides /bin/cat
coreutils-8.4-31.el6_5.1.i686
On Debian and derivated using apt-get you can ask the APT package manager:
$ dpkg-query -S /bin/bash
bash: /bin/bash
Back in the day we always included a what string with version info. Actually part of SCCS, if the program contained strings starting with "#(#)", it is displayed by the what command. C code would like like this:
static char prog_id[] = "#(#) my_program version 1.0 - 3/26/2014";
Anyway try doing a what on the program and see what you get. heh.

C: List aliases of current shell (not subshell!)

I'm trying to find all aliases that the current shell possesses (in a C program). I've tried system("alias"), popen("alias", "r") and execvp("alias", ...), the latter of which doesn't work at all (because alias is a shell-specific command) and the first two of which run that command in a subshell (which is sh and not bash) -> there, aliases are disabled because they are defined in my ~/.bashrc. Executing bash and reading the output of alias isn't possible either because bash will only go to the alias definitions if it is in interactive mode.
If I do run bash in interactive mode, I get a huge delay time and a prompt output that I want to avoid.
Basically, what I want, is to have similar behaviour as time(1). It looks up current aliases without even executing any command! (it will fork only once, that is, for the passed command)
Crawling the Internet was to no avail.
Question: How do I look up all aliases in the current shell? Will there be any portability issues? If yes, how to avoid them?
Regards.
You can't. time is a built-in and it can access the aliases which are stored internally to the shell instance that is running. if you need to work out what the shell will execute, you need to run which or something similar.
time isn't doing anything clever or secret. It's just a prefix to the command to make the shell print out some timing information.
I just downloaded and built GNU time on my Mac. To my surprise and chagrin, it doesn't read aliases from the parent bash, whereas the built-in time does.
$ alias frodo=ls
$ ./time frodo
./time: cannot run frodo: No such file or directory
Command exited with non-zero status 127
0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata 819200maxresident)k
0inputs+0outputs (0major+59minor)pagefaults 0swaps
$ time frodo
AUTHORS INSTALL Makefile.in config.cache configure* error.o getopt.o getpagesize.h mkinstalldirs* resuse.h stamp-vti time.c time.texi version.texi
COPYING Makefile NEWS config.log configure.in getopt.c getopt1.c install-sh* port.h resuse.o texinfo.tex time.info version.c wait.h
ChangeLog Makefile.am README config.status* error.c getopt.h getopt1.o mdate-sh* resuse.c stamp-v time* time.o version.o
real 0m0.005s
user 0m0.002s
sys 0m0.002s
$
I suspect there's something undocumented is passing under the table to help the builtin time see aliases (or BSD provides something that GNU time won't or can't make use of).
EDIT: Prompted by Tom Tanner's answer (+1 Tom), I just realised that time frodo doesn't invoke /usr/bin/time (as suggested by which time), and that explicitly running /usr/bin/time frodo also fails, so it must be that entering time by itself invokes a bash builtin command.

gprof : How to generate call graph for functions in shared library that is linked to main program

I am working on Linux environment. I have two 'C' source packages train and test_train.
train package when compiled generates libtrain.so
test_train links to libtrain.so and generates executable train-test
Now I want to generate a call graph using gprof which shows calling sequence of functions in main program as well as those inside libtrain.so
I am compiling and linking both packages with -pg option and debugging level is o0.
After I do ./train-test , gmon.out is generated. Then I do:
$ gprof -q ./train-test gmon.out
Here, output shows call graph of functions in train-test but not in libtrain.so
What could be the problem ?
gprof won't work, you need to use sprof instead. I found these links helpful:
How to use sprof?
http://greg-n-blog.blogspot.com/2010/01/profiling-shared-library-on-linux-using.html
Summary from the 2nd link:
Compile your shared library (libmylib.so) in debug (-g) mode. No -pg.
export LD_PROFILE_OUTPUT=`pwd`
export LD_PROFILE=libmylib.so
rm -f $LD_PROFILE.profile
execute your program that loads libmylib.so
sprof PATH-TO-LIB/$LD_PROFILE $LD_PROFILE.profile -p >log
See the log.
I found that in step 2, it needs to be an existing directory -- otherwise you get a helpful warning. And in step 3, you might need to specify the library as libmylib.so.X (maybe even .X.Y, not sure) -- otherwise you get no warning whatsoever.
I'm loading my library from Python and didn't have any luck with sprof. Instead, I used oprofile, which was in the Fedora repositories, at least:
operf --callgraph /path/to/mybinary
Wait for your application to finish or do Ctl-c to stop profiling. Now let's generate a profile summary:
opreport --callgraph --symbols
See the documentation to interpret it. It's kind of a mess. In the generated report, each symbol is listed in a block of its own. The block's main symbol is the one that's not indented. The items above it are functions that call that function, and the ones below it are the things that get called by it. The percentages in the below section are the relative amount of time it spent in those callees.
If you're not on Linux (like me on Solaris) you simply out of luck as there is no sprof there.
If you have the sources of your library you can solve your problem by linking a static library and making your profiling binary with that one instead.
Another way I manage to trace calls to shared libraries, is by using truss. With the option -u [!]lib,...:[:][!]func, ... one can get a good picture of the call history of a run. It's not completely the same as profiling but can be very usefull in some scenarios.

Resources