uniroma1
.*_main.c
.cognome.nome
. Sulle postazioni del laboratorio sarà /home/biar/Desktop/cognome.nome/
.cognome.nome.zip
(zip -r cognome.nome.zip cognome.nome/
).cognome.nome.zip
.rm -rf cognome.nome
).Per maggiori informazioni fate riferimento al regolamento delle esercitazioni.
stdin
in C)Questo è un esercizio di mera conoscenza del linguaggio C. Il canale stdin
(di tipo FILE*
) modella in C la sorgente di caratteri ASCII che proviene come input, salvo diversamente specificato, dal terminale. Si chiede di scrivere nel file E1-get-cmd-line/e1.c
una funzione void get_cmd_line(char* argv[]);
che legge la prossima linea di testo da stdin
, estrae ciascun token (sequenza consecutiva di caratteri, esclusi spazi, tab \t
e ritorni a capo \n
) e produce un array di al più n<=MAX_TOKENS=64
stringhe come segue:
malloc
;NULL
, fungendo da “terminatore”.Assumere che la linea di testo letta da stdin
contenga al più 1024 caratteri compreso il ritorno a capo \n
.
Esempio: se la linea letta da stdin
è rm -f .DS_store
, la chiamata get_cmd_line(argv)
restituisce in argv
l’array di stringhe {"rm", "-f". ".DS_Store", NULL}
.
Suggerimenti. Basandosi sul comando man
(oppure la documentazione online cercando funzione
opengroup
) usare:
fgets
per leggere una linea da stdio
di al più MAX_LINE=1024
caratteri terminata dal ritorno a capo \n
;strtok
per tokenizzare la linea una volta letta da stdio.L’array di stringhe prodotto deve essere deallocabile con la semplice funzione:
void free_args(char* argv[]) {
while (*argv) free(argv++);
}
Usare il main di prova nella directory di lavoro E1-parse-line
compilando con `gcc e1_main.c e1.c -o e1´.
[Svolgere questo esercizio solo dopo aver svolto l’Esercizio 1]
Una shell
è un programma che chiede all’utente di eseguire altri programmmi sotto forma di nuovi processi, passandogli eventuali argomenti specificati dall’utente. Una shell fornisce normalmente un prompt
, vale a dire un breve testo (es. $, >, ecc.) che segnala all’utente che la shell è in attesa di ricevere comandi.
Si chiede di scrivere nel file E2-shell/e2.c
una semplice shell sotto forma di una funzione int do_shell(const char* prompt);
che prende come parametro la stringa di prompt e si comporta come segue;
stdin
un comando seguito dai suoi eventuali argomenti (es: ls -l
, dove ls
è il comando e -l
è il suo unico argomento). Per ottenere comando e argomenti da stdin
usare il risultato dell’esercizio 2;quit
terminare con successo la shell;fork
un nuovo processo che esegua il comando con gli argomenti dati usando execvp
;unknown command
seguito dal nome del comando e tornare al punto 1;wait
la terminazione del processo e tornare al punto 1.Per valutare il corretto funzionamento della shell, effettuare i seguenti quattro test:
ls -l
e verificare che listi la directory correnteecho hello
e verificare che venga stampato hellosergente hartman
e verificare che venga stampato un messaggio di errorequit
e verificare la terminazione della shellIl risultatao di ogni system call deve essere controllato e in caso di errore segnalato con perror
e terminazione EXIT_FAILURE.
Rispondi alle seguenti domande, tenendo conto che una risposta corretta vale 1 punti, mentre una risposta errata vale 0 punti.
Domanda 1 Relativamente al seguente codice C (supponendo che la fork
non generi un errore), quale tra le seguenti affermazioni è VERA?
Padre: 1
” e poi “Figlio: 2
”Figlio: 2
” e poi “Padre: 1
”Figlio: 1
” e poi “Padre: 1
”Figlio: 1
” e poi “Padre: 2
”Figlio: 2
” e poi “Padre: 2
”Domanda 2 Relativamente al seguente codice C (supponendo che la fork
non generi un errore), qual è l’output atteso?
Figlio 1: x=10
”, “Figlio 2: x=20
”, “Figlio 3: x=30
”, “Padre: x=40
”Figlio 1: x=10
”, “Figlio 2: x=10
”, “Figlio 3: x=10
”, “Padre: x=30
”Figlio 1: x=10
”, “Figlio 2: x=10
”, “Figlio 3: x=10
”, “Padre: x=40
”Figlio 1: x=10
”, “Figlio 2: x=20
”, “Figlio 3: x=30
”, “Padre: x=30
”Figlio 1: x=10
”, “Figlio 2: x=30
”, “Figlio 3: x=60
”, “Padre: x=100
”Domanda 3 Relativamente al seguente codice C (supponendo che la execv
non generi un errore), qual è l’output atteso?
--- started ---
”, “Hello
”, “NULL
, “--- finished ---
”--- started ---
”, “Hello
”, “--- finished ---
”--- started ---
”, “Hello
”--- started ---
”, “--- finished ---
”stdin
in C)e1.c
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "e1.h"
char* dup_string(const char* in) {
size_t n = strlen(in);
char* out = malloc(n + 1);
strcpy(out, in);
return out;
}
void get_cmd_line(char* argv[MAX_TOKENS]) {
int argc = 0;
char line[MAX_LINE];
fgets(line, MAX_LINE, stdin);
char* token = strtok(line, " \t\n");
argc = 0;
while (argc < MAX_TOKENS && token != NULL) {
argv[argc++] = dup_string(token);
token = strtok(NULL, " \t\n");
}
argv[argc] = NULL;
}
e2.c
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "e2.h"
#define MAX_LINE 1024
#define MAX_TOKENS 64
void do_cmd(char* argv[MAX_TOKENS]) {
int res;
pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(EXIT_FAILURE);
}
if (pid == 0) {
res = execvp(argv[0], argv);
if (res == -1) {
printf("unkwnown command %s\n", argv[0]);
_exit(EXIT_FAILURE);
}
}
res = wait(NULL);
if (pid == -1) {
perror("wait error");
exit(EXIT_FAILURE);
}
}
void deallocate_cmd(char* argv[MAX_TOKENS]) {
while (*argv != NULL)
free(*argv++);
}
char* dup_string(const char* in) {
size_t n = strlen(in);
char* out = malloc(n + 1);
strcpy(out, in);
return out;
}
void get_cmd_line(char* argv[MAX_TOKENS]) {
int argc = 0;
char line[MAX_LINE];
fgets(line, MAX_LINE, stdin);
char* token = strtok(line, " \t\n");
argc = 0;
while (argc < MAX_TOKENS && token != NULL) {
argv[argc++] = dup_string(token);
token = strtok(NULL, " \t\n");
}
argv[argc] = NULL;
}
int do_shell(const char* prompt){
for (;;) {
printf("%s", prompt);
char* argv[MAX_TOKENS];
get_cmd_line(argv);
if (argv[0] == NULL) continue;
if (strcmp(argv[0], "quit") == 0) break;
do_cmd(argv);
deallocate_cmd(argv);
}
return EXIT_SUCCESS;
}
Figlio: 1
” e poi “Padre: 1
”Figlio 1: x=10
”, “Figlio 2: x=20
”, “Figlio 3: x=30
”, “Padre: x=40
”--- started ---
”, “Hello
”