I agree with Sundialsvcs; you should seriously consider if C is suitable for you for this. That said, I'm assuming that you have good reasons why you are doing this in C.
Quote:
Originally Posted by Blackened Justice
1) How can I place the result of a command, in my case 'pwd' into a C string?
|
First, you create a pipe and a child process. Set the standard output of the child process to the pipe. Then, execute the external command in the child process. The
popen() function (see
man 3 popen for details) does all that for you.
Sometimes you need to not only retain the output, but also generate the input for the external command dynamically. The external command is a
coprocess in this case. Instead of popen(), you'd use pipe() (twice, for two separate pipes), fork(), one of the exec() family functions, and nonblocking low-level I/O (fcntl(), read(), write()) to feed input to the coprocess, and read its output, without deadlocking. (If the coprocessor needs some input before it can output, and your parent process wants to read something before it'll send input to the coprocess, nothing will happen: the two processes are deadlocked.) Since this case is quite complex to handle correctly, I'd suggest you avoid this case (for example by using a temporary file for input and/or output), until you are comfortable with C and low-level I/O operations in particular.
At this point, the input is available from a file handle or descriptor; it is not magically going to be transported to a string variable. You'll have to do that yourself.
In all practical cases, you'll want to use a loop, which reads the input into a dynamically allocated buffer, and continuously grows (reallocates) that buffer, until all input is read. Consider this example function using popen():
Code:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
char *run(const char *const command, int *const status)
{
char *data = NULL;
size_t size = 0;
size_t used = 0;
char *newdata;
size_t newsize, bytes;
FILE *cmd;
int result, saved_errno;
/* If we are successful, we retain original errno. */
saved_errno = errno;
/* Check that command string exists and is not empty. */
if (!command || !*command) {
errno = EINVAL; /* "Invalid argument" error */
return NULL;
}
/* Run the command. We wish to read the command output. */
errno = ENOMEM; /* Default if popen() does not set errno. */
cmd = popen(command, "r");
if (!cmd)
return NULL;
/* Read the command output. */
while (1) {
/* Make sure there is free space in the buffer. */
if (used >= size) {
/* Size to grow buffer to */
newsize = used + 65536;
/* Reallocate the buffer. */
newdata = realloc(data, newsize);
if (!newdata) {
/* Too much data. Release resouces and fail. */
if (data)
free(data);
fclose(cmd);
errno = ENOMEM;
return NULL;
}
/* Reallocation was successful. */
data = newdata;
size = newsize;
}
/* Read some of the output from the external command. */
bytes = fread(data + used, 1, size - used, cmd);
/* No more data? */
if (bytes == (size_t)0)
break;
/* No, we got some more data. Continue. */
used += bytes;
}
/* Did the command fail somehow? */
if (!feof(cmd) || ferror(cmd)) {
/* Failure. Free resources and fail. */
if (data)
free(data);
fclose(cmd);
/* We do not know the exact cause, so use "I/O error". */
errno = EIO;
return NULL;
}
/* Close the pipe. There may be delayed errors. */
errno = ENOENT; /* Default if pclose() does not set errno. */
result = pclose(cmd);
if (result == -1) {
saved_errno = errno;
if (data)
free(data);
errno = saved_errno;
return NULL;
}
/* If requested, save exit status (use WIFEXITED() et al on it) */
if (status)
*status = result;
/* Reallocate the output buffer, and add at least eight zero bytes.
* Just one EOS "\0" is required, but since the data is usually parsed,
* adding multiple EOS bytes helps avoid buffer overruns. */
newsize = (used | (size_t)7) + (size_t)9;
newdata = realloc(data, newsize);
if (!newdata) {
if (data)
free(data);
errno = ENOMEM;
return NULL;
}
data = newdata;
size = newsize;
/* Add the zero bytes to end the string. */
memset(data + used, 0, size - used);
/* Done! */
errno = saved_errno;
return data;
}
Quote:
Originally Posted by Blackened Justice
2) Let's say I want to request a path to a file from the user. Can I somehow place a default string in the stdin buffer, so that if the user wants to, he could backspace and gradually erase it?
|
Use the editline library for this. You could also use the GNU readline library for this, but the editline library has an easier interface, and seems to be installed at least in the recent Ubuntu variants by default. You do need to install the development version, if you wish to compile new programs using editline; in Debian variants, the package is named
libedit-dev .
Consider this simple example program:
Code:
#include <stdio.h>
#include <histedit.h>
/* basename() equivalent, returns the file name given path. */
const char *name(const char *path)
{
const char *result = path;
while (*path)
if (*(path++) == '/')
result = path;
return result;
}
/* Function that returns the prompt string. */
const char *promptfunc(EditLine *e)
{
return "Your input? ";
}
int main(int argc, char *argv[])
{
EditLine *e;
const char *input;
int arg, len;
/* Initialize the editline library.
* The name is used so that the user can set
* specific settings for editline for this exact
* application, without affecting others.
* You could also use a fixed name,
* this uses the name the user used.
*/
e = el_init(name(argv[0]), stdin, stdout, stderr);
/* If there is one or more command-line parameters,
* use the first one as the default. The user can
* then edit the value.
*/
if (argc > 1)
el_push(e, argv[1]);
/* Set the prompt function. */
el_set(e, EL_PROMPT, promptfunc);
/* Ask one line of input from the user. */
length = 0;
input = el_gets(e, &length);
/* Did we get anything? */
if (input) {
printf("You supplied %d bytes of input:\n", length);
fputs(input, stdout);
fputc('\n', stdout);
} else {
printf("No input.\n");
}
/* Release the resources dynamically allocated.
* Note that this includes the string in 'input' variable;
* this releases that too. If you want to keep the strings,
* make a copy using e.g. strdup().
*/
el_end(e);
return 0;
}
If you save the above as
example.c you can compile it using
Code:
gcc example.c -ledit -o example
Note that gcc-4.6 is sensitive about parameter order; using the same arguments in a wrong order will just give you "undefined reference to" errors. You can run the program after compiling it using for example
Code:
./example 'edit this string'
Note that
el_gets() function is very similar to my
run() function above, except that my function runs an external command instead of just asking the user. Both allocate the end result dynamically. My
run() function expects the user to
free() the result after they're done with it, but editline is a bit different: it records the pointer in its internal structures -- you shouldn't try to free() the pointer you get from el_gets()! -- and free()s it when el_end() is called (for that EditLine structure).
While the two approaches to memory management are different, they work equally well; the user of the library -- you, the programmer -- just has to realize which approach is used, and follow the paradigm. (Often programmers just "assume", and get frustrated when their program does not work, or crashes, often in a totally unrelated part of the code. It is their own fault. You need to understand how the library or libraries are intended to be used first, and then write your code using them. If you first write your code, you'll twist your brain trying to mate the two different approaches/paradigms/viewpoints together into a working program.)
Hope this helps.