Sistemi di Calcolo

Corso di Laurea in Ingegneria Informatica e Automatica - A.A. 2017-2018

HomePage | Avvisi | Diario lezioni | Programma | Materiale didattico | Esami | Forum | Login

Soluzione esercitazione 5


[ testo esercitazione ]

Esercizio 1

shell.c
#include <stdio.h>              // fgets, printf, puts
#include <sys/wait.h>           // wait
#include <unistd.h>             // fork, execvp
#include <string.h>             // strtok, strcpy, strrchr

#define MAX_LINE_SIZE 1024      // massima lunghezza riga di comando
#define MAX_NUM_ARGS  64        // massimo numero di token sulla riga di comando
#define PROMPT        "SC1> "   // prompt dei comandi


// ---------------------------------------------------------------------
//  parse_cmd_line
// ---------------------------------------------------------------------
// estrae argomenti dalla riga di comando
// parametri:
//   - cmd_line: [input] stringa di input contenente la riga di comando
//   - tok_buf: [output] array in cui scrivere i token della riga di comando
//   - tok_num_ptr: [output] puntatore a buffer in cui scrivere numero di token in tok_buf

void parse_cmd_line(char cmd_line[MAX_LINE_SIZE],
                    char tok_buf[MAX_NUM_ARGS][MAX_LINE_SIZE],
                    unsigned* tok_num_ptr) {
    unsigned tok_num = 0;                   // numero token estratti da cmd_line
    char buf[MAX_LINE_SIZE];                // buffer per non modificare cmd_line passata
    strcpy(buf, cmd_line);                  // copia cmd_line: strtok fa side-effect
    const char* delim = " \n\t";            // delimitatori tokenizzazione
    char* token = strtok(buf, delim);       // tokenizza buffer
    while (token != NULL) {
        strcpy(tok_buf[tok_num], token);    // copia token corrente in tok_buf
        token = strtok(NULL, delim);        // estrae token successivo
        tok_num++;
    }
    *tok_num_ptr = tok_num;                 // passa numero token estratti al chiamante
}


// ---------------------------------------------------------------------
//  do_internal_cmd
// ---------------------------------------------------------------------
// prova ad eseguire comando interno
// parametri:
//   - tok_buf: [input] array di token della riga di comando
//   - tok_num: [input] numero di token in tok_buf
// restituisce:
//    -1 se quit
//     0 se non e' un comando interno
//     1 se e' un comando interno

int do_internal_cmd(char tok_buf[MAX_NUM_ARGS][MAX_LINE_SIZE],
                    unsigned tok_num) {

    if (strcmp(tok_buf[0], "quit") == 0) return -1;

    // altri comandi interni qui...
    // if (strcmp(tok_buf[0], "miocomando") {
    //     do_mio_comando_interno(tok_buf, tok_num); // esegue cmd interno
    //     return 1;                                 // cmd eseguito
    // }

    return 0;
}


// ---------------------------------------------------------------------
//  _print_tok_buf
// ---------------------------------------------------------------------
// stampa token buffer (per scopi di debugging)

static void _print_tok_buf(char tok_buf[MAX_NUM_ARGS][MAX_LINE_SIZE],
                           unsigned tok_num) {
    int i;
    printf("--- tok_buf [len=%u]\n", tok_num);
    for (i=0; i<tok_num; i++) printf("%s\n", tok_buf[i]);
    printf("---\n");
}


// ---------------------------------------------------------------------
//  make_argv
// ---------------------------------------------------------------------
// crea array di puntatori argv per comando esterno
// parametri:
//   - argv: [output] buffer preallocato di puntatori agli argomenti del comando
//   - tok_buf: [input] array di token della riga di comando
//   - tok_num: [input] numero di token in tok_buf

void make_argv(char* argv[MAX_NUM_ARGS+1],
               char tok_buf[MAX_NUM_ARGS][MAX_LINE_SIZE],
               unsigned tok_num) {

    int i;

    // estrae nome file dal pathname e lo fa puntare da argv[0]
    char* s = strrchr(tok_buf[0], '/');
    if (s == NULL) argv[0] = tok_buf[0];
    else           argv[0] = s+1;

    // inizializza argv[i] per i>0
    for (i=1; i<tok_num; i++) argv[i] = tok_buf[i];

    // aggiunge terminatore
    argv[tok_num] = NULL;
}


// ---------------------------------------------------------------------
//  do_external_cmd
// ---------------------------------------------------------------------
// esegue comando esterno
// parametri:
//   - tok_buf: [input] array di token della riga di comando
//   - tok_num: [input] numero di token in tok_buf

void do_external_cmd(char tok_buf[MAX_NUM_ARGS][MAX_LINE_SIZE],
                     unsigned tok_num) {

    char* argv[MAX_NUM_ARGS+1];                  // argv da passare a processo
    make_argv(argv, tok_buf, tok_num);           // crea argv per processo

    pid_t pid = fork();                          // genera nuovo processo
    if (pid == -1) {                             // gestione errori
        perror("cannot spawn process");
        return;
    }

    if (pid == 0) {                              // processo figlio
        execvp(tok_buf[0], argv);                // carica programma
        perror("cannot exec program");           // loader fallisce
        _exit(1);                                // termina proc figlio
    }

    wait(NULL);                                  // shell attende term figlio
}


// ---------------------------------------------------------------------
//  do_cmd_loop
// ---------------------------------------------------------------------
// esegue il ciclo di lettura/esecuzione dei comandi

void do_cmd_loop() {
    char     cmd_line[MAX_LINE_SIZE];                    // buffer per contenere riga di cmd
    char     tok_buf[MAX_NUM_ARGS][MAX_LINE_SIZE];       // buffer token riga di comando
    unsigned tok_num;                                    // numero token riga di comano
    for (;;) {
        printf("%s", PROMPT);                            // stampa prompt
        if (fgets(cmd_line, MAX_LINE_SIZE, stdin)==NULL) // legge cmd line
            break;                                       // esce se NULL
        parse_cmd_line(cmd_line, tok_buf, &tok_num);     // estrae i token
        if (tok_num == 0) continue;                      // ripete se riga vuota
        int res = do_internal_cmd(tok_buf, tok_num);     // prova ad eseguire comando interno
        if (res == 0) do_external_cmd(tok_buf, tok_num); // comando esterno
        if (res == -1) break;                            // esce su quit
    }
}


// ---------------------------------------------------------------------
//  main
// ---------------------------------------------------------------------

int main() {
    do_cmd_loop();
    puts("\nsayonara.");
    return 0;
}



Esercizio 2

Estendere la soluzione dell'esercizio 1 con le seguenti funzioni:

// ---------------------------------------------------------------------
//  do_cd
// ---------------------------------------------------------------------
// esegue comando cd <path>
// parametri:
//   - tok_buf: [input] array di token della riga di comando
//   - tok_num: [input] numero di token in tok_buf

void do_cd(char tok_buf[MAX_NUM_ARGS][MAX_LINE_SIZE], unsigned tok_num) {
    if (tok_num < 2) {
        fprintf(stderr, "sintassi: cd <path>\n");
        return;
    }
    int res = chdir(tok_buf[1]);
    <