#include #include #include #include #include ssize_t read_from_fd(int fd, char * buffer, size_t count) { ssize_t bytes_read; size_t position = 0; while ( (bytes_read = read(fd, &buffer[position], count - position)) > 0) { position += bytes_read; if (position == count) { //printf("*1* position=%d\n", position); return position; } } if (bytes_read < 0) { perror("read()"); return -1; } else { //printf("*2*\n"); return position; } } ssize_t write_to_fd(int fd, char * buffer, size_t count) { ssize_t bytes_written; size_t position = 0; while ( (bytes_written = write(fd, &buffer[position], count - position)) > 0 ) { position += bytes_written; if (position == count) { //printf("+1+\n"); return position; } } if (bytes_written < 0) { perror("write()"); return -1; } else { //printf("+2+\n"); return position; } } /* scrivere la seguente funzione: int * gen_random_array(unsigned int seed, int use_seed, unsigned int number_elements); se use_seed è true, utilizza il parametro seed con la funzione srand() per impostare il "seed" della sequenza di interi pseudo-casuali. number_elements è il numero di celle dell'array che la funzione restituirà il valore di ogni elemento dell'array è ottenuto invocando rand() esempio: gen_random_array(-100, 1, 10) cioè con seed pari a -100 restituisce un array di 10 elementi pari a: (TODO!!!) */ int * gen_random_array(unsigned int seed, int use_seed, unsigned int number_elements) { if (use_seed) { srand(seed); } int * result; // result NON è un vettore, ma è un puntatore // la memoria di result è un intero lungo (8 bytes) perchè deve contenere un indirizzo result = malloc(number_elements * sizeof(int)); if (result == NULL) { return NULL; } /* * "regola": quando una funzione deve restituire un vettore (e la funzione ha il compito di "crearlo" o allocarlo), * allora è opportuno allocare il vettore con malloc (o calloc, o realloc) e quindi restituire il puntatore * * è OK restituire indirizzi a zone di memoria, purchè queste zone di memoria continuino ad esistere */ // ERRATO: // int result2[256]; // questa zona di memoria cessa di essere valida alla fine della funzione // // ... // return result2; // ERRORE // for (int i = 0; i < number_elements; i++) { result[i] = rand(); } return result; } typedef char * my_string; #define BUFFER_LEN 1024 int main(int argc, my_string argv[]) { // int * p; // // p = gen_random_array(100, 1, 1024); // // if (p == NULL) { // printf("problema!\n"); // exit(1); // } // // // // for (int i = 0; i < 1024; i++) { // printf("%d\n", p[i]); // } // // free(p); // // exit(0); // parametri passati a riga di comando // argc ha il numero di parametri passati a riga di comando, // ovvero argc è la dimensione dell'array argv // // argv[0] è il nome del programma da cui è istanziato il processo // // argv è un array di char * (ovvero un 'array di stringhe') // printf("tutti i parametri (argc = %i):\n", argc); // for (int i = 0; i < argc; i++) { // printf("argv[%i] = '%s'\n", i, argv[i]); // } /* └──╼ $Debug/tutoring-20210402 file_sorgente file_destinazione tutti i parametri (argc = 3): argv[0] = 'Debug/tutoring-20210402' argv[1] = 'file_sorgente' argv[2] = 'file_destinazione' */ // il nome del file sorgente si troverà in argv[1] // il nome del file destinazione si troverà in argv[2] // devo controllare che argc sia == 3 // se il file destinazione esiste, "tronca" a zero la sua dimensione (prima di scriverlo). // open con i "flag" corretti: vedi list 4.2 di Kerrisk pag. 73 (O_CREAT | O_TRUNC | O_RDWR) // il programma controlla i risultati di tutte le system call. // esempio: se il file sorgente non esiste, fornisce un messaggio opportuno. // potreste usare le seguenti system call: open, read, write, close // pseudocodice int fd_source; int fd_target; char * filename_source; char * filename_target; char * buffer; // leggere i nomi dei due file da riga di comando (controllare che si siano: argc == 3) if (argc != 3) { printf("questo programma si aspetta due parametri: nome del file sorgente e nome del file destinazione.\n"); exit(1); } filename_source = argv[1]; filename_target = argv[2]; // definire due file descriptor, perchè apriremo un file in lettura ed uno in scrittura // aprire in sola lettura il file sorgente // se il file non esiste (fd_source == -1), messaggio di errore ed uscire // aprire il file destinazione (O_CREAT | O_TRUNC | O_RDWR) fd_source = open(filename_source, O_RDONLY); if (fd_source == -1) { // se il file non esiste, finiamo qui perror("open()"); exit(1); } fd_target = open(filename_target, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); if (fd_target == -1) { perror("open()"); exit(1); } // riutilizzare https://github.com/marcotessarotto/exOpSys/blob/master/053flock/053flock.c // copiare: ssize_t read_from_fd(int fd, char * buffer, size_t count); // copiare: ssize_t write_to_fd(int fd, char * buffer, size_t count); // introduciamo un #define BUFFER_LEN 512 per indicare la dimensione della zona di memoria // che verrà usata per copiare il contenuto del file sorgente (un po' alla volta) // allocare una zona di memoria con malloc() buffer = malloc(BUFFER_LEN); if (buffer == NULL) { perror("malloc()"); exit(1); } int res; int bytes_totali_copiati = 0; int numero_cicli = 0; while ((res = read(fd_source, buffer, BUFFER_LEN)) > 0) { numero_cicli++; printf("ho letto %d bytes\n", res); bytes_totali_copiati += res; res = write(fd_target, buffer, res); if (res == -1) { perror("write()"); exit(1); } printf("ho scritto %d bytes\n", res); } if (res == -1) { perror("read()"); exit(1); } printf("TOTALE: ho copiato %d bytes in %d cicli\n", bytes_totali_copiati, numero_cicli); // ho finito // close(fd_source); // close(fd_target); // free(buffer); // // while (1) { // int res; // // res = read_from_fd(fd_source, buffer, BUFFER_LEN); // // if (res == 0) { // // EOF, non ci sono più dati da leggere dal file sorgente // // usciamo dal ciclo while // break; // } else if (res == -1) { // perror("read()"); // exit(1); // } // // // res contiene il numero di bytes letti // res = write_to_fd(fd_target, buffer, res); // // if (res == -1) { // perror("write()"); // exit(1); // } // // } // ciclo while (?) per: // leggere un po' di dati dal file sorgente // scrivere i dati nel file destinazione // fino a quando incontro EOF sul file sorgente // fine // potremmo chiudere i due file descriptor aperti, rilasciare la memoria allocata // oppure lasciare che li chiuda il kernel, dal momento che adesso il programma termina return 0; }