Pseudo-terminaux, ca marche.

Top Page

Reply to this message
Author: Frederic Mantegazza
Date:  
To: ML Guilde
Subject: Pseudo-terminaux, ca marche.
Bonjour,

Rappel des faits. Je souhaitais utiliser Gnuplot comme traceur pour un
logiciel de calcul et de simulation d'enceintes acoustiques. Pour le
moment, j'utilise Gnuplot au travers de son shell, avec quelques petits
scripts. L'avantage de cette solution, c'est que le soft tourne sur
toute plateforme supportant Gnuplot.

Mais je compte egalement faire un front-end graphique pour rendre le
tout un peu plus convivial (sous Qt), avec comme contrainte d'avoir
toujours sous la main le shell de Gnuplot, et de pouvoir travailler
avec.

Bref, il me fallait un bout de code qui permettait a Gnuplot d'ecouter
a la fois son entree standard, et a la fois le front-end. Edgar Bonet
m'a donc aiguille sur les pseudo-terminaux. Avec son aide, on (enfin
surtout Edgar) a pu ecrire un petit utilitaire generique, 'tty4gui',
qui permet de faire tout cela.

Pour ceux que ca interesse, je le met en attachement (avec un bout de
script shell ecrit par Edgar pour le tester). Si vous avez des
remarques/suggestions, n'hesitez pas.

Pour compiler :

$ gcc tty4gui.c -o tty4gui -lutil

PS: J'ai egalement fait un petit essai avec un bout de code sous Qt. Ca
marche au poil.

-- 
    Frederic

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pty.h>

#define max(x, y) ((x)>(y) ? (x) : (y))

char usage[] = "Usage: %s fifo command [args]\n";

int main(int argc, char **argv)
{
    int                master,
                    child;
    struct termios    attributes;
    fd_set            rdfs;
    char            buffer[1024];
    int                pipeGUI;
    int                count;


    if (argc < 3) {
        fprintf(stderr, usage, argv[0]);
        return EXIT_FAILURE;
    }


    tcgetattr(0, &attributes);
    child = forkpty(&master, NULL, &attributes, NULL);
    if (child < 0) {
        perror("multi: fork");
        return EXIT_FAILURE;
    }
    if (child == 0) {


        /*    This is child    */
        execvp(argv[2], argv + 2);
        return EXIT_FAILURE;
    }


    /*    This is Parent    */


    /*    Open the pipe.    */
    pipeGUI = open(argv[1], O_RDONLY | O_NONBLOCK);
    if (pipeGUI == -1) {
        perror(argv[1]);
        close(master);
        return EXIT_FAILURE;
    }


    /*    Set our terminal to raw mode.    */
    cfmakeraw(&attributes);
    tcsetattr(0, TCSAFLUSH, &attributes);


    while (1) {
        FD_ZERO(&rdfs);                            /*    Init rdfs.                        */
        FD_SET(0, &rdfs);                        /*    Monitor stdin,                    */
        FD_SET(master, &rdfs);                    /*    the terminal from our child,    */
        FD_SET(pipeGUI, &rdfs);                    /*    and the pipe from the GUI.        */


        /*    Wait for some input.    */
        select(max(master, pipeGUI) + 1, &rdfs, NULL, NULL, NULL);


        if (FD_ISSET(pipeGUI, &rdfs)) {


            /*    Transfer from the pipe to child    */
            count = read(pipeGUI, buffer, sizeof buffer);
            if (count > 0)
                write(master, buffer, count);
        }
        if (FD_ISSET(0, &rdfs)) {


            /*    Transfer from stdin to child    */
            count = read(0, buffer, sizeof buffer);
            if (count > 0)
                write(master, buffer, count);
            else


                /*    Stdin was closed, let's make child know about that    */
                close(master);
        }
        if (FD_ISSET(master, &rdfs)) {


            /*    Transfer from child to stdout    */
            count = read(master, buffer, sizeof buffer);
            if (count > 0)
                write(1, buffer, count);
            else


                /* Child closed its stdout, then we are done */
                break;
        }
    }
    return EXIT_SUCCESS;
}


/* vim: set autoindent cindent tabstop=4 shiftwidth=4 noexpandtab: */
#!/bin/sh

fifo=/tmp/tty4gui-`date +%s`.$$
[ `which $TERM 2> /dev/null` ] || TERM=xterm
command=gnuplot

mkfifo $fifo
$TERM -e ./tty4gui $fifo $command &
echo "Type here or in the new window. Echo will be in the new window."
stty raw -echo
cat >> $fifo
stty sane
echo ""
rm -f $fifo