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.
Related
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. */
}
I want to get the status of command which i passed as a parameter to the system() function. suppose i pass "attrib +h E:/songs/lovingsong.mp3" as system("attrib +h E:/songs/lovingsong.mp3") since i know that the path give by me is not right , i.e there is no such folder songs in my E: drive so on console it says path not found.
How can i get "path not found" message in my c program or any status code ,so that i can know the command did not work.
/* system example : DIR */
#include <stdio.h> /* printf */
#include <stdlib.h> /* system, NULL, EXIT_FAILURE */
int main ()
{
int i;
printf ("Checking if processor is available...");
if (system(NULL)) puts ("Ok");
else exit (EXIT_FAILURE);
printf ("Executing command DIR...\n");
i = system ("attrib +h E:/songs/lovingsong.mp3");
//want to get status here
printf ("The value returned was: %d.\n",i);
return 0;
}
Notice that the C11 standard (read n1570) defines system in §7.22.4.8 (with its formal argument being called string below):
If string is a null pointer, the system function determines whether the host environment has a command processor. If string is not a null pointer, the system function passes the string pointed to by string to that command processor to be executed in a manner which the implementation shall document; this might then cause the program calling
system to behave in a non-conforming manner or to terminate
So there is no clear definition, according to the C11 standard, of what system does. You need to dive into the documentation of your particular C11 implementation.
On POSIX (e.g. Linux or MacOSX), system is required to run the POSIX shell /bin/sh with the -c argument followed by the string and there is a complex chapter describing what is a POSIX shell.
Perhaps you want to pass a command which is redirecting the stderr of attrib to stdout, and read that stdout produced by your attrib command. On POSIX you could use popen (with pclose) for that purpose. Maybe (and probably) your implementation has something similar: _popen. And you also need to read the documentation of your attrib command.
So your question is really operating system specific. You need to dive into the documentation of your particular operating system.
And you might simply test (before running system) that the file E:/songs/lovingsong.mp3 exists. Maybe use _access (with perhaps a race condition if that file gets removed before running attrib).
Be also aware of the $PATH variable.
PS. I don't know anything about Windows, but you did not tag your question with Windows.
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+");
How to move a particular file from one folder to another folder?
What I have tried,
#include <stdio.h>
int main() {
FILE *tFile;
if (tFile != NULL)
tFile = NULL;
if ((tFile = fopen("TempFile.txt", "rw")) == NULL) {
return -1;
}
mv("TempFile.txt", "../MST");
printf("Done Succesfully\n");
return 0;
}
Error :
test.c:17:2: warning: no newline at end of file
/tmp/ccKLWYNa.o(.text+0x5e): In function `main':
: undefined reference to `mv'
collect2: ld returned 1 exit status
Please guide me how can I do this.
You really should read Advanced Linux Programming and syscalls(2)
To move (from C) a file from one place to another in the same file system just use the rename(2) syscall.
At the very least, for your particular example, you'll need to code:
char* srcpath = "TempFile.txt"; // assume it is a variable path
char destpath[1024];
snprintf (destpath, sizeof(destpath), "../MST/%s", srcpath);
if (rename (srcpath, destpath)) {
// something went wrong
if (errno == EXDEV) {
// copy data and meta data
} else { perror("rename"); exit(EXIT_FAILURE); };
}
else { // the rename succeeded
}
If you really want to mv TempFile.txt ../MST/TempFile.txt specifically for TempFile.txt only you could just call rename("TempFile.txt", "../MST/TempFile.txt") and handle the error cases like I suggest. If you are sure that ../MST/lie in the same file system than . then EXDEV should not happen and you don't need to handle it particularly (but you do need to handle errors).
If you want to move a file between two different file systems, you have to copy the data (and perhaps some of the meta-data) yourself (and then remove e.g. with unlink(2)) the original source file). You could detect that situation by various means: you could just try the rename and if errno (see errno(3)) is EXDEV you need to copy the file. Or you could use stat(2) to query the source file(and the destination directory) meta-data -e.g. its size and its file system.
Of course, you need to understand what are files on Linux (or Posix), in particular what is an inode.... See inode(7) and credentials(7)
You could have used system with /bin/mv (but be careful about strange characters -like spaces or semicolons- in the file paths, you need to escape them to avoid code injection), apparently you don't want to.
You should play with strace(1) (or perhaps also ltrace) on mv in various situations to understand what it is doing. Also, study the source code of GNU coreutils which provides /bin/mv notably in mv.c ...
Some extra C or C++ libraries may provide you with functions to move files (in the same filesystem they should do a rename, in different file systems they copy the source file data and perhaps some meta-data and unlink the source, so cannot be atomic), e.g. in C g_file_move (from Gio with Glib from Gnome), or in C++ copy_file -followed by remove in Boost, etc etc....
PS. For temporary files see tmpfile(3), mkstemp(3), etc...
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);
}