0

I’m developing an IDE kind of software for C/C++ using java (although there are lots of available, but I want my own) that can compile and execute C or C++ program. So I tried a simple program to compile and execute the C program in java using Process and ProcessBuilder.

Here is my simple java program which compiles and execute C program:

public class RunProgram {

    public static void main(String[] args) throws Exception {
        new ProcessBuilder("gcc", "-o", "first", "first.c").start().waitFor(); //To Compile the source file using gcc and wait for compilation


/*
    Although I've to handle error-stream but 
for now, my assumption is that there is no error 
in program.
*/


        ProcessBuilder run = new ProcessBuilder("./first");

        execute.redirectErrorStream(true);

        Process runProcess = run.start();

        StreamReader sr = new StreamReader(runProcess.getInputStream());

        new Thread(sr).start(); //A new thread to handle output of program . 

    //rest of coding to provide input using OutputStream of 'runProcess' and to close the stream.

    }
}

class StreamReader implements Runnable {

    private InputStream reader;

   public StreamReader(InputStream inStream) {
      reader = inStream;

   }

   @Override
   public void run() {

      byte[] buf = new byte[1024];

        int size = 0;
        try {

            while ((size = reader.read(buf)) != -1) {

                System.out.println(new String(buf));
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
   }
}

And here is my first.c program.

#include<stdio.h>

int main() {
    int a;
    int k;

    printf("input a: ");

    scanf("%d", &a);
    for(k = 0; k < a; k++)
        printf("k = %d\n", k);
    return 0;
}

I want to create interactive IO console just like most of the IDEs or command terminals(Terminal in Linux bases OS and command prompt in Windows based OS). For above example: firstly, it should print “Input a: “and then wait for input to be provided and then rest of program. But It won’t work as I thought, as it doesn’t print the result of printf statement appeared before scanf until I provide input possibly using OutputStream.

I googled for my problem and visited many links but didn't get solution. Mean while, I found this link which suggest to append fflush after every printf statement or use setbuf or setvbuf methods (from some other sub-links) to clear the buffer. But a new person (who is going to learn C) might not be aware of fflush or these functions and he/she will never use it, as it doesn’t require in other IDEs or not even on terminals. How can I solve this problem and can build integrated console for my IDE

Here is a glimpse of what I want:

animation of Interactive IO

Sachin Kumar
  • 781
  • 1
  • 9
  • 28
  • This [answer](https://stackoverflow.com/a/50386656/3545273) of mine gives a more in depth explaination of what happens under the hood and why it should be handled in callee program. The only alternative I can imagine would be pseudo-terminals in Unix-Linux but it is a rather advanced (and non portable) way. – Serge Ballesta May 18 '18 at 07:29
  • @SergeBallesta I've a look upon your answer, but I didn't understand that how your answer would help me to solve this problem. – Sachin Kumar May 18 '18 at 07:51
  • Unfortunately, there is no way to solve it (except with pseudo-terminals)... – Serge Ballesta May 18 '18 at 08:09
  • @SergeBallesta so does eclipse and other IDEs uses pseudo-terminals – Sachin Kumar May 18 '18 at 08:12

2 Answers2

2

From the comments above, think adding a little bit of explanation of how buffering for I/O streams works makes sense here.

What happens behind the scenes when calling printf(3) and the like is that the data is written to a buffer until the buffer fills up or some trigger happens. The content of the buffer is then copied from the buffer to the actual output device/another output buffer ... The trigger is usually encountering a line end (\n under Linux/Unix). Thus a crude version of this buffering is:

struct buffered_file_t {

    char* buffer;
    size_t capacity;
    size_t current_char;
    FILE* file;

};

void flush_buffered(struct buffered_file_t* file) {

    assert(0 != file);
    assert(0 != file->buffer);

    fwrite(file->buffer, file->current_char, 1, file->file);
    file->current_char = 0;

}


void print(struct buffered_file_t* file, const char* str) {

    assert(0 != file);
    assert(0 != file->buffer);
    assert(0 != str);

    for(size_t i = 0;  0 != str[i]; ++i) {

        if(file->current_char >= file->capacity - 1) flush_buffered(file);
        file->buffer[file->current_char++] = str[i];
        if('\n' == str[i]) flush_buffered(file);

    }

}

Now, if you invoke print like

const size_t BUFSIZE = 100;

struct buffered_file_t stdout_buffered = {
    .buffer = calloc(1, BUFSIZE),
    .capacity = BUFSIZE,
    .current_char = 0,
    .file= stdout,
};

print(&stdout_buffered, "Naglfar\n");
print(&stdout_buffered, "Surthur");

You won't see Surthur appear onstdout ever. In order to have it written from the buffer to stdout, you have to either

  • call flush_buffered explicitly
  • Disable buffering by reducing the buffer size (buffered_file.capacity = 1 in the example above)

In your case, you cannot invoke fflush(3) explicitly (that's what you stated as requirement). thus the only means left is disabling buffering.

How to do this is OS dependent, IMHO. For Linux, look at stdbuf(1) from the Coreutils package to find out how to diable buffering for certain streams of foreign processes.

Michael Beer
  • 853
  • 5
  • 12
0

Under GNU/Linux, for switching off buffering for the standard I/O streams, you could use stdbuf(1)like so:

 ....
 ProcessBuilder run = new ProcessBuilder("stdbuf", "-o0", "./first");
 ....

Add -e0 and -i0 options if you want to turn off buffering for stderr and stdin as well.

Of course, it would be nicer if you would not have to rely upon external tools but could do switching off buffering in your own code - simplest thing is to have a look at the source of stdbuf, but I guess it would end up in you having to use the JNI, and then, I guess, I would just stick to stdbuf ...

Michael Beer
  • 853
  • 5
  • 12
  • I've tried your code and it produces exeception: can't run program 'stdbuf' as studbuf is not an executable program – Sachin Kumar May 18 '18 at 07:48
  • Of course, `stdbuf` must be in path - install `coreutils`. My intention was to give you a starter were to look, if you plan to stick to precisely `stdbuf` for solving your problem, I recommend distributing it along with your software. – Michael Beer May 18 '18 at 08:00
  • Bear Actually I don't want to stick with `stdbuf` rather I want a solution, so I'm trying everything. As I've searched a lot on internet, I didn't find any first step solution – Sachin Kumar May 18 '18 at 08:03
  • From Your point, I've to distribute it along with my IDE which is little cumbersome. And I think, setting buffer to 0 is not a good practice as program uses buffer for fastening the execution and for some other purpose. – Sachin Kumar May 18 '18 at 08:08
  • Hi again, so, there are two points I want to reply to: As I said, I intended to give you `stdbuf` as a hint were to start looking. You know C, thus look at its source code. – Michael Beer May 18 '18 at 09:12
  • Second point: You realize that having `stdout` of your `./first` buffered is the root cause of your problem? Then either you disable buffering `stdout` live on with your problem, as far as I can see. Unfortunately, you can't have the cake and eat it, too. Moreover, `stdout` and the likes are mainly buffered in order to provide e.g. line-by-line manipulations. Switching off buffering for `stdout` in your case won't inflict heavy performance penalties. Just compare https://docs.oracle.com/javase/7/docs/api/java/io/BufferedInputStream.html , which is buffered for the same ends. – Michael Beer May 18 '18 at 09:17