Extracting dos command output in C - c

Using system() or exec(), I can get any command to execute but they display the result into the Console. I want to execute the command and extract the output and process it, and then display it. How can I achieve this on Windows/DOS Platform?

There's nothing like that in standard C, but usually, for compatibility with POSIX, compilers implement popen (_popen in VC++), which starts a command (as it would do with system) and returns a FILE * to the stream you asked for (you can ask for stdout if you want to read the output or stdin if you want to give some input to the program).
Once you have the FILE *, you can read it with the usual fread/fscanf/..., like you would do on a regular file.
If you want to have both input and output redirection things start to get a bit more complicated, since Windows compilers usually do have something like POSIX pipe, but it isn't perfectly compatible (mostly because the Windows process creation model is different).
In this case (and in any case where you need more control on the started process than the plain popen gives you) I would simply go with the "real" way to perform IO redirection on Windows, i.e. with CreateProcess and the appropriate options; see e.g. here.

Matteo Italia's answer is awesome. But some compilers (especially older ones) don't support popen().
If popen() is not supported, here's a possible solution.
In DOS we can redirect the output to a temporary file using >.
Ex:
C:\> ipconfig > temp.txt
I can then open temp.txt in my C-code and then reprocess its content.
My C code for this will be something like:
system("ipconfig > temp.txt");
FILE *fp;
fp = fopen("temp.txt","r");
// //
//... Code for reprocessing can go here ...//
// //

Here's an alternative answer for those without popen that should work on most system. This code is not thread safe. I expect that this is not a significant limitation for most situation.
#include <stdio.h>
#include <process.h>
#include <io.h>
#include <sys\stat.h>
#include <assert.h>
int main()
{
int so = dup(1);
close(1);
int i = creat("output.txt", S_IWRITE);
assert(i == 1); // i should be 1...
system("dir");
close(i);
dup2(so, 1);
close(so);
}

Related

Print man contents as help prompt of a C program

I plan to use pandoc to generate the man files from markdown for a very small C program.
I do not want to repeat myself and type the help prompt (command [ -h | --help ]) into the source code of the program.
Question
Q: Could I permanently include man content from a file at compile time of a C program?
OR
Q: Is there a way in C to print the contents of the installed man page to stdout?
[WIP] Solution
Based on #KamilCuk's answer here's what i am doing:
In terminal:
file command.1
# OUTPUT --> command.1: troff or preprocessor input, ASCII text
xxd -i command.1 > command.hex
Then in main.c
#include "command.hex"
printf("%s", command_1);
This is almost exactly what I am looking for except
I need the hexed troff to be parsed before printing. Or parsed before hexing. I am not sure which to do / how to do that.
No, I do not want to call man and pipe the output.
I would prefer using a library + a macro to generate the hex but I am not sure how it's done.
Clarifications
What I mean by help prompt:
The output of git --help which prints to stdout
NOT the output of git --help branch which opens the man for git-branch
However, please do share the latter too. It isn't what I want: my manual is very short; but I would be glad to learn an alternative if not the exact one I seek.
Considerations I have noted:
man pages are not always installed to the same location
man pages may not be installed in some systems; or there may be no man program
Could I permanently include man content from a file at compile time?
You can include any content of any file at compile time. The simplest is use xxd -i and compile the output.
Is there a way in C to print the contents of the installed man page?
You can troff output from man -k
You can get output from man by replacing pager, along MANPAGER=cat man.
In C: setenv("MANPAGER", "cat"); execp("man", (char*[]){"man", "something", 0}); or just system("MANPAGER=cat man some_command").
man pages are not always installed to the same location
And system administractors can use /etc/man_db.conf to configure the behavior of man command.
man pages may not be installed in some systems; or there may be no man program
So it seems users of such systems do not care about man pages, so there's no point in supporting such cases.
I believe your intention is to render a man page from your program, where you have a static troff input saved and want to use man to display it. You could save the input to a file, save it and pass the filename to man - man will parse troff and render it. Or you could the usual pipe()+fork()+exec() and from one process pass troff data to stdin to man - - the - will make man read troff input from stdin.
Is there a way in C to print the contents of the installed man page to stdout?
You could invoke the mancommand by using a pipe stream . Function popen is what you are looking for. More info here.
This is an fgets() solution, though you might want to be careful, in case you want to read more characters.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp = NULL;
char buffer[1000];
fp = popen("man man","r");
if (fp == NULL)
{
/* error running the command */
exit(EXIT_FAILURE);
}
while (fgets(buffer,sizeof(buffer),fp))
{
puts(buffer);
}
fclose(fp);
return 0;
}
getline() solution, in case you don't know how many bytes you are going to read.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp = NULL;
char *buffer = NULL;
size_t len = 0U;
ssize_t nread = 0;
fp = popen("man man","r");
if (fp == NULL)
{
/* error running the command */
exit(EXIT_FAILURE);
}
/* getline will set errno uppon failure, you might want to check that too */
while ((nread = getline(&buffer,&len,fp)) != -1)
{
buffer[nread] = 0; /* NULL terminate your buffer */
puts(buffer);
}
free(buffer);
fclose(fp);
return 0;
}
More on getline() here.
The man program is written in C and is open source. It is more complex than you believe, since it deals with terminal (and their width). See also termios(3) and tty(4). Be aware of ANSI escape codes and the ncurses library. Take into account that in 2021 UTF-8 is used everywhere, and that some man pages could contain § or ° characters. Be also aware of internationalization and localization issues, see locale(7). In France, a lot of Linux systems are installed with man pages in French.
So you could get the source code of the man-db package, study it, and incorporate it inside your program.
My opinion is that it would be silly to do so.
You could instead read Advanced Linux Programming, study various syscalls(2), and use fork(2), execve(2), waitpid(2) to run the existing and installed /usr/bin/man program (whose existence could be tested with test(1) or access(2) or stat(2) in C code; but if it does not exist your execve(2) will fail...)
Regarding display of --help, you could use (if so allowed) GNU libc program argument parsing facilities, and take inspiration from the source code of existing free software programs, such as GNU coreutils.
Of course, your build infrastructure (e.g. your Makefile if you build your software with GNU make) could run programs to share data and textual descriptions. You could (if so allowed) use open source tools to generate some C code (e.g. #included stuff) at build time, such as GNU m4, GNU gawk, GPP, etc..
Take also inspiration from the source code of git. It is open source (and useful for you): you can download its source code and study it.

Proper methods to Copy files/folders programmatically in C using POSIX functions

These terms may not be 100% accurate, but I'm using the GCC compiler and POSIX library. I have C code compiled with the SQLite amalgamation file to a single executable.
In the user interface that exchanges JSON messages with the C program, I'd like to make it possible for users to copy the SQLite database files they create through the C program, and copy a full directory/folder.
Thus far, I've been able to rename and move files and folders programmatically.
I've read many questions and answers here, at Microsoft's C runtime library, and other places but I must be missing the fundamental points. I'm using regular old C, not C++ or C#.
My question is are there POSIX functions similar to rename(), _mkdir(), rmdir(), remove(), _stat(), that allow for programmatic copying of files and folders in Windows and Linux?
If not, can one just make a new folder and/or file and fread/fwrite the bytes from the original file to the new file?
I am primarily concerned with copying SQLite database files, although I wouldn't mind knowing the answer in general also.
Is this answer an adequate method?
Is the system() function a poor method? It seems to work quite well. However, it took awhile to figure out how to stop the messages, such as "copied 2 files" from being sent to stdout and shutting down the requesting application since it's not well-formed JSON. This answer explains how and has a link to Microsoft "Using command redirection operators". A /q in xcopy may or may not be necessary also, but certainly didn't do the job alone.
Thank you very much for any direction you may be able to provide.
The question that someone suggested as an answer and placed the little submission box on this question is one that I had already linked to in my question. I don't mean to be rude but, if it had answered my question, I would not have written this one. Thank you whoever you are for taking the time to respond, I appreciate it.
I don't see how that would be a better option than using system() because with the right parameters all the sub-directories and files of a single parent folder can be copied in one statement without having to iterate through all of them manually. Is there any reason why it would not be better to use system() apart from the fact that code will need to be different for each OS?
Handling errors are a bit different because system() doesn't return an errno but an exit code; however, the errors can be redirected from stderr to a file and pulled from there, when necessary
rename(): posix
_mkdir(): not posix. You want mkdir which is. mkdir takes two arguments, the second of which should usually be 077.
rmdir(): posix
remove(): posix
_stat(): not posix, you want stat() which is.
_stat and _mkdir are called as such on the Windows C library because they're not quite compatible with the modern Unix calls. _mkdir is missing an argument, and _stat looks like a very old version of the Unix call. You'll have trouble on Windows with files larger than 2GB.
You could do:
#ifdef _WIN32
int mkdir(const char *path, int mode) { return _mkdir(path); } /* In the original C we could have #defined this but that doesn't work anymore */
#define stat _stat64
#endif
but if you do so, test it like crazy.
In the end, you're going to be copying stuff with stdio; this loop works. (beware the linked answer; it has bugs that'll bite ya.)
int copyfile(const char *src, const char *dst)
{
const int bufsz = 65536;
char *buf = malloc(bufsz);
if (!buf) return -1; /* like mkdir, rmdir, return 0 for success, -1 for failure */
FILE *hin = fopen(src, "rb");
if (!hin) { free(buf); return -1; }
FILE *hout = fopen(dst, "wb");
if (!hout) { free(buf); fclose(hin); return -1; }
size_t buflen;
while ((buflen = fread(buf, 1, bufsz)) > 0) {
if (buflen != fwrite(buf, 1, buflen)) {
fclose(hout);
fclose(hin);
free(buf);
return -1; /* IO error writing data */
}
}
free(buf);
int r = ferror(hin) ? -1 : 0; /* check if fread had indicated IO error on input */
fclose(hin);
return r | (fclose(hout) ? -1 : 0); /* final case: check if IO error flushing buffer -- don't omit this it really can happen; calling `fflush()` won't help. */
}

fprintf() is not working in ubuntu

I'm trying to learn File I/O concepts in C programming language. I'm using GNU / Linux ( Ubuntu 16.04 LTS ) and my IDE is eclipse 3.8. when I try to write in a file through fprintf() method, it doesn't create any files or if the file is even created, it doesn't write in it. I tried to fix the problem by using fflush() or setbuf(file_pointer, NULL) methods as is suggested here but still no change. I guess I'm writing the address of the file in a wrong way.
Here is the code:
#include <stdio.h>
int main(void){
FILE *file_pointer;
file_pointer=fopen("~/.textsfiless/test.txt","w+");
setbuf(file_pointer,NULL);
fprintf(file_pointer,"Testing...\n");
fclose(file_pointer);
return EXIT_SUCCESS;
}
Can someone explain what's wrong here?
On Linux, the ~ in ~/.textsfiless/test.txt is not expanded by the C library fopen... When you use ~ on the command line, it is expanded by your shell (but not by the program using it, started by the shell doing some execve(2)...) into your home directory; the expansion is called globbing. Read glob(7). You are very unlikely to have a directory named ~.
You should read Advanced Linux Programming
So you should check if fopen failed (it is very likely that it did fail). If you want to get a file in the home directory, you'll better use getenv(3) with "HOME" (or perhaps getpwuid(3) & getuid(2)...). See environ(7)
Perhaps a better code might be:
char*homedir = getenv("HOME");
if (!homedir) { perror("getenv HOME"); exit(EXIT_FAILURE); };
char pathbuf[512]; /// or perhaps PATH_MAX instead of 512
snprintf(pathbuf, sizeof(pathbuf),
"%s/.textsfiless/test.txt", homedir);
FILE *file_pointer = fopen(pathbuf, "r");
if (!file_pointer) { perror(pathbuf); exit(EXIT_FAILURE); };
and so on.
Notice that you should check against failures most C standard library (& POSIX) functions. The perror(3) function is useful to report errors to the user on stderr.
(pedantically, we should even test that snprintf(3) returns a length below sizeof(pathbuf) or use and test against failure asprintf(3) instead; I leave that test as an exercise to the reader)
More generally, read the documentation of every external function that you are using.
Beware of undefined behavior (your code is probably having some, e.g. fprintf to a NULL stream). Compile your code with all warnings & debug info (so gcc -Wall -g) and use the gdb debugger. Read What every C programmer should know about undefined behavior.
BTW, look into strace(1) and try it on your original (faulty) program. You'll learn a lot about the system calls used in it.
Most likely your call to fopen() fails. You don't have any checking in your program to ensure fopen even worked. It may not have, and this could be due to a variety of things, like you spelling the path wrong, wrong file or process permissions, etc.
To see what really happened, you should check fopen's return value:
#include <stdio.h>
int main(void){
FILE *file_pointer;
file_pointer=fopen("~/.textsfiless/test.txt","w+");
if (file_pointer == NULL) {
printf("Opening the file failed.");
return EXIT_FAILURE;
}
setbuf(file_pointer,NULL);
fprintf(file_pointer,"Testing...\n");
fclose(file_pointer);
return EXIT_SUCCESS;
}
Edit: Since your comment, you getting the path wrong is most certainly what happened. If you're executing your program from the current directory, and your file is in a folder called "textfiless" in your current directory and your file is called "test.txt", then you'd call fopen like this:
file_pointer=fopen("/textsfiless/test.txt","w+");

writing STDIN of bash application from c program

I would like to open an bash application(prog1) and send command to that application with C program.
I tried and wrote the following code.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
FILE * fp;
char message[10] = "command1\n";
fp = popen("sudo prog1","r");
write(STDIN_FILENO, message, 10);
pclose(fp);
execlp("sudo prog1", "sudo prog1", "-l", NULL);
return 0;
}
This code gives an output:
Linux:~$ ./ prog 2 // running the code
command // prints "command"
prog1-> // Then opens "prog1" (prog1 waits for user commands)
But I want it to be
Linux:~$ ./ prog 2 // running the code
prog1-> command // open "prog1" and write command (instead of getting it from user)
It either writes after quitting prog1 or before starting prog1. Please let me know how to write the "command" in prog1, after opening the application prog1. Thank you.
Note: If I make
fp = popen("sudo prog1","w"); //to write
It throws the following error
tcgetattr : Inappropriate ioctl for device
Your main bug is thinking that popen() somehow associates your child process with your STDIN_FILENO. It doesn't. STDIN_FILENO is not associated with your "sudo prog1" child. You'd have to create a pipe, dup the descriptors to stdin/stdout, and fork+exec to do that. But you used popen() so don't do that either.
Instead, you should be writing and reading from fp.
Something like:
fprintf(fp, message);
fgets(response, 100, fp);
Since fprintf() is buffered, you should use \n at the end of the line, or fflush().
Also, there is no point is using exec/execlp at the end when you've already called popen(). Looks like you may be mixing two approaches that you've seen by example.
popen() essentially does a combination of (pipe, dup stdin/stdout/stderr, fork, execl) to take care of redirecting a child process to a file stream connected to the parent. So no need to reimplement unless you need different semantics than popen().
You technically are implementing "expect" functionality, so you might want to look into expect, or expect modules for different languages. Expect is included with Linux distributions but is usually optional.
http://expect.sourceforge.net/
http://search.cpan.org/~rgiersig/Expect-1.21/Expect.pod
And not to mention, Perl has a Sudo module already.
http://search.cpan.org/~landman/Sudo-0.21/lib/Sudo.pm

C , linebreak without linefeed?

Silly question but,
Is it possible to break a line on stdout without the line feed using printf();? If not, any tips on how I would overwrite 2+ lines, if possible?
I'm trying to generate sort of a progress bar but on multiple lines.
Any ideas?
EDIT:
So yeah I accepted the below answer although it won't work for my specific case.
I'm trying to overwrite 2+lines
rather than a single line.
printf("12345\r");
fflush(stdout);
printf("67890\n");
The result of which is $ ./a.out 67890
But what I'm trying to achieve is have 2+ lines be overwritten with new data.
Similar to a progress bar but on 2+ lines except I have a percentage number for some data.
To rewrite all (or part) of a line, you need to use the correct number of backspace characters. Eg:
printf("some text");
printf("\b\b\b\bstuff");
Will output:
some stuff
This is fine for simple stuff; for something more complex you should use ncurses which uses ANSI-escape cleverness to manipulate the cursor around the screen.
If your terminal (or, much more likely, terminal emulator) supports VT100-style escape sequences, you can print specific code sequences to control the cursor position, clear some or all of the screen/window, etc.
For example, to move the cursor up 1 line:
printf("\x1b[A");
fflush(stdout);
To move the cursor up 2 lines, either do that twice or:
printf("\x1b[2A"});
fflush(stdout);
These are commonly referred to as ANSI escape codes; the link is to a Wikipedia article that lists many of them. They were first implemented by the old DEC VT-100 terminal, which is emulated by most modern terminals and emulators.
And this:
printf("\x1b[J");
fflush(stdout);
will clear part of the screen, from the current cursor position to the bottom.
These sequences should be enough to do what you need. (They might not work in a Windows command window.)
More portably, if your system supports it, you can use termcap or terminfo to determine the proper command sequences for your current terminal (as determined by the $TERM environment variable). The tput command lets you do this on the command line; man tput for more information. In practice, you're unlikely to find a system these days that supports termcap or terminfo with a terminal that's not VT100-compatible; printing raw escape sequences is strictly not portable, but probably good enough.
A suggestion: your program should probably have an option to inhibit any such control sequences; for example, if a user who wants to redirect the output to a file won't want to have those escape sequences in the file. Some programs use control sequences only if they can determine that stdout is a terminal, but an explicit option is also a good idea.
*UPDATE: *
Here's a program I threw together that demonstrates how to do this with the terminfo. It should work on just about any Unix-like system.
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <term.h>
#include <unistd.h>
int main(void) {
const char *term = getenv("TERM");
if (term == NULL) {
fprintf(stderr, "TERM environment variable is not set\n");
exit(EXIT_FAILURE);
}
setterm(term);
for (int i = 0; i < 10; i ++) {
putp(tparm(clr_eos));
printf("%d\n%d\n", i, i+1);
sleep(1);
putp(tparm(parm_up_cursor, 2));
}
return 0;
}

Resources