LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 12-08-2011, 04:30 PM   #1
Blackened Justice
Member
 
Registered: Jun 2011
Posts: 82

Rep: Reputation: Disabled
A couple of questions about C


Hey everyone,

I have a couple of questions about C.

1) How can I place the result of a command, in my case 'pwd' into a C string?

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?
 
Old 12-08-2011, 04:44 PM   #2
kbp
Senior Member
 
Registered: Aug 2009
Posts: 3,790

Rep: Reputation: 653Reputation: 653Reputation: 653Reputation: 653Reputation: 653Reputation: 653
I just wanted to comment because I liked the Metallica reference not because of my l33t coding skillz ... I think you would need to push the string to stdout (console) without a CRLF then when the user modified and pressed enter it would be sent back via stdin (not that I've tried it).

In regard to your first point, you don't need to grab output from external commands.. the c standard library has all the code you need to obtain the current working directory (getcwd)

Last edited by kbp; 12-08-2011 at 04:47 PM.
 
Old 12-08-2011, 06:40 PM   #3
sundialsvcs
LQ Guru
 
Registered: Feb 2004
Location: SE Tennessee, USA
Distribution: Gentoo, LFS
Posts: 10,659
Blog Entries: 4

Rep: Reputation: 3940Reputation: 3940Reputation: 3940Reputation: 3940Reputation: 3940Reputation: 3940Reputation: 3940Reputation: 3940Reputation: 3940Reputation: 3940Reputation: 3940
First:

Forget About "C" !!

Now:

Think (only...!) about what you want to accomplish ... not about ...

... how you (incorrectly!!!) assume that "you must do it!"

Think outside the box. (Just like your predecessors did, uhh, in the 1970's on a PDP-7.)

You want: "to run a command," and ... "to capture the results thereof."

So ... "where does 'C' necessarily figure into all of this?"

Answer: (think about it !! !! !!)

Nowhere.
 
Old 12-09-2011, 11:33 AM   #4
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
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 View Post
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 View Post
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.

Last edited by Nominal Animal; 12-09-2011 at 11:35 AM.
 
Old 12-09-2011, 03:03 PM   #5
Blackened Justice
Member
 
Registered: Jun 2011
Posts: 82

Original Poster
Rep: Reputation: Disabled
Thank you for your answers

Sorry about not mentioning it in my initial post, but I have to make it ANSI-compliant, so using popen and getwd is pretty much out of the question.

I do have to use C, it's for a project for my programming class. I just wanted, when asking the user for a file, to show the path to the current working directory and to have a default path already in the stdin buffer. If that isn't possible using standard ANSI libraries and functions, I'll just not implement like so.

Cheers
 
Old 12-09-2011, 03:22 PM   #6
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by Blackened Justice View Post
Sorry about not mentioning it in my initial post, but I have to make it ANSI-compliant, so using popen and getwd is pretty much out of the question.
I think you're defining your program out of existence.

You asked for two behaviors that are outside the definition of the C language, into the boundary between the OS and your program. Those behaviors are necessarily OS specific.
 
Old 12-09-2011, 06:42 PM   #7
SigTerm
Member
 
Registered: Dec 2009
Distribution: Slackware 12.2
Posts: 379

Rep: Reputation: 234Reputation: 234Reputation: 234
Quote:
Originally Posted by Blackened Justice View Post
Sorry about not mentioning it in my initial post, but I have to make it ANSI-compliant, so using popen and getwd is pretty much out of the question.
Then you can't do it. If I remember correctly, there are no directory management functions at all, and the only thing you can get from os is an integer return code using "system" call.
 
Old 12-10-2011, 03:03 AM   #8
millgates
Member
 
Registered: Feb 2009
Location: 192.168.x.x
Distribution: Slackware
Posts: 852

Rep: Reputation: 389Reputation: 389Reputation: 389Reputation: 389
Quote:
Originally Posted by SigTerm View Post
Then you can't do it. If I remember correctly, there are no directory management functions at all, and the only thing you can get from os is an integer return code using "system" call.
You could probably redirect the "system" call into a temporary file and read the result from the file afterwards.
 
Old 12-10-2011, 06:41 AM   #9
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
Just a quick note: there is no "PWD" command in WinDos, so the program wouldn't be platform-independent anyway.
 
Old 12-10-2011, 07:29 AM   #10
millgates
Member
 
Registered: Feb 2009
Location: 192.168.x.x
Distribution: Slackware
Posts: 852

Rep: Reputation: 389Reputation: 389Reputation: 389Reputation: 389
Quote:
Originally Posted by NevemTeve View Post
Just a quick note: there is no "PWD" command in WinDos, so the program wouldn't be platform-independent anyway.
in Windows, you can use CD instead.
 
Old 12-10-2011, 07:35 AM   #11
Cedrik
Senior Member
 
Registered: Jul 2004
Distribution: Slackware
Posts: 2,140

Rep: Reputation: 244Reputation: 244Reputation: 244
Another diff is files path char delimiter AFAIK (windows: '\'; unix,linux: '/')
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Couple of questions alaxulas Linux - Newbie 5 04-05-2005 12:14 AM
A couple questions breider565 Linux - Newbie 3 06-04-2004 01:47 PM
couple of questions ph0n1x Linux - Software 7 03-19-2004 12:58 PM
Couple of questions... p3ngu!n Linux - Newbie 2 11-11-2003 05:41 AM
A couple of questions. jhansman Linux - Newbie 3 08-26-2003 09:05 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 02:37 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration