ProvaPratica 2013.07.18

From Sistemi Operativi
Revision as of 08:30, 9 May 2017 by FedericoB (talk | contribs) (Aggiunte soluzione es 3 di altri studenti, migliorata organizzazione pagina e formattato codice di Aula)
Jump to navigation Jump to search

Esercizio 1

Soluzione di Tommaso Ognibene

/*
Prova Pratica di Laboratorio di Sistemi Operativi
18 luglio 2013
Esercizio 1

URL: http://www.cs.unibo.it/~renzo/so/pratiche/2013.07.18.pdf

@author: Tommaso Ognibene
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

// Constants
#define MaxNumArguments 10
#define MaxLenArguments 50
#define MaxLenPath 100
#define MaxLenLine MaxLenArguments * MaxNumArguments
#define MaxLenCommands 500

// Structure for a process element
typedef struct processElem
{
	char CommandLine[MaxLenCommands];
	pid_t Pid;
	struct processElem *Next;
} ProcessElem;

// Function prototypes
ProcessElem *appendElement    (ProcessElem *tail);
char        *findCommand      (pid_t pid);
void         countTokens      (const char *string, const char delimiter, int *count, int *longest);
int          tokenize         (char *string, char delimiter, char ***tokens, int *count);
void         readMultipleLines(void);
void         runProcesses     (void);

// Global variables
static ProcessElem *ProcessList  = NULL;
int                 ProcessCount = 0;

// Given a PID retrieve the relative command
char *findCommand(pid_t pid)
{
	ProcessElem *process;
	for(process = ProcessList; process->Pid != pid; process = process->Next);
	return (process)? (process->CommandLine) : (NULL);
}

// Create a new element
ProcessElem *appendElement(ProcessElem *tail)
{
	ProcessElem *newElem = (ProcessElem *)malloc(sizeof(ProcessElem));
	newElem->Next = NULL;
	(tail)? (tail->Next = newElem) : (tail = newElem);
	return newElem;
}

/*
 * Count tokens and find the longest one
 * Example:
 *    Input:
 *       string = "ping -c 4 google.com"
 *       delimiter = ' '
 *    Output:
 *       count = 4
 *       longest = 10
 */
void countTokens(const char *string, const char delimiter, int *count, int *longest)
{
	int iterator, lenght;
	lenght = *longest = *count = 0;

	// Count the first (n - 1) tokens
	for(iterator = 0; string[iterator]; iterator++)
	{
		if(string[iterator] == delimiter)
		{
			if(*longest < lenght)
				*longest = lenght;
			lenght = 0;
			(*count)++;
		}
		lenght++;
	}

	// Count the last token
	if(string[iterator - 1] != delimiter)
	{
		lenght--;
		if(*longest < lenght)
			*longest = lenght;
		(*count)++;
	}
}

/*
 * Tokenize a string accordingly to a given delimiter
 * Example:
 *    Input:
 *       string = "ping -c 4 google.com"
 *       delimiter = ' '
 *    Output:
 *       tokens = ["ping", "-c", "4", "google.com"]
 *       count = 4
 */
int tokenize(char *string, char delimiter, char ***tokens, int *count)
{
	if(!(tokens && string && count)) return 0;

	int token, i, j, longest;
	*count = longest = 0;

	countTokens(string, delimiter, count, &longest);

	// Allocate space for array of strings
	*tokens = (char **)malloc(*count * sizeof(char *));

	if(!*tokens)
	{
		perror("malloc()");
		exit(EXIT_FAILURE);
	}

	// Allocate space for each string in the array and tokenize it
	i = 0;
	for(token = 0; token < *count; token++)
	{
		(*tokens)[token] = (char *)malloc(sizeof(char) * (longest + 1));

		j = 0;
		while(string[i] != delimiter && string[i])
		{
			(*tokens)[token][j] = string[i];
			j++;
			i++;
		}

		// Null-terminated string
		(*tokens)[token][j] = (char)0;
		i++;
	}

	return 1;
}

// Read multiple input lines
void readMultipleLines(void)
{
	ProcessElem *process = NULL;
	char input[MaxLenLine];
	char *iterator;

	printf("Enter commands. Press enter on blank line to start them.\n");
	while(1)
	{
		// Get input string
		if(!fgets(input, MaxLenLine, stdin))
		{
			perror("fgets()");
			exit(EXIT_FAILURE);
		}

		// Skip blank lines
		for(iterator = input; *iterator == ' '; iterator++);

		// Check if enter was pressed on blank line
		if(*iterator == '\n') break;

		// Append a new input element
		if(!(ProcessList))
			process = ProcessList = appendElement(ProcessList);
		else
		{
			process->Next = appendElement(process);
			process = process->Next;
		}
		ProcessCount++;

		// Eliminate "\n" from the input string
		iterator = strtok(iterator, "\n");

		strcpy(process->CommandLine, iterator);
	}
}

/*
 * Run child processes ->
 * Print when a child process terminates
 */
void runProcesses(void)
{
	pid_t pid;
	int status, terminated, count;
	char path[MaxLenPath];
	ProcessElem *process = ProcessList;
	char **command = NULL;
	terminated = count = 0;

	// Start processes
	while(process)
	{
		// Tokenize command line
		tokenize(process->CommandLine, ' ', &command, &count);

		// Create path
		snprintf(path, sizeof path, "%s%s", "/bin/", command[0]);

		// Create child process
		pid = fork();

		// Check PID
		switch(pid)
		{
		// Error
		case -1:
			perror("fork()");
			exit(EXIT_FAILURE);
		// Child process
		case 0:
			// Execute command
			if (execv(path, command) == -1)
			{
				perror("execv()");
				exit(EXIT_FAILURE);
			}
			break;
		// Parent process
		default:
			process->Pid = pid;
		}
		process = process->Next;
	}

	// Wait processes
	do
	{
		// Check if a child process has terminated
		pid = waitpid(-1, &status, WNOHANG);
		switch(pid)
		{
		// Error
		case -1:
			perror("waitpid()");
			exit(EXIT_FAILURE);
		// All child processes are still running
		case 0:
			break;
		// A child process has terminated
		default:
			terminated++;
			printf("Finished #%d: %s\n", terminated, findCommand(pid));
		}
	}
	while(terminated < ProcessCount);

	exit(EXIT_SUCCESS);
}

// Entry point
int main(int argc, char *argv[])
{
	// Get input
	readMultipleLines();

	// Set output
	runProcesses();

	return EXIT_SUCCESS;
}

Soluzione di Aula

#include < stdio.h > 
#include < unistd.h >

#define CHAR 1
#define SPACE 0

execs2(char * s, int argc) {
    char * argv[argc + 1];
    char * t = s;
    int status = SPACE;
    argc = 0;
    while ( * t) {
        switch ( * t) {
        case ' ':
        case '\t':
        case '\n':
        case 0:
            if (status == CHAR) argc++; * t = 0;
            status = SPACE;
            break;
        default:
            if (status == SPACE) printf("%s\n", t);
            if (status == SPACE) argv[argc] = t;
            status = CHAR;
            break;
        }
        t++;
    }
    argc++;
    argv[argc] = 0;
    int i;
    for (i = 0; i < argc + 1; i++)
        printf("%d %p\n", i, argv[i]);
    execvp(argv[0], argv);
}

execs(char * s) {
    char * t = s - 1;
    int argc = 0;
    int status = SPACE;
    do {
        t++;
        switch ( * t) {
        case ' ':
        case '\t':
        case '\n':
        case 0:
            if (status == CHAR) argc++;
            status = SPACE;
            break;
        default:
            status = CHAR;
            break;
        }
    } while ( * t);
    execs2(s, argc);
}

int main(int argc, char * argv[]) {
    char buf[1024];
    fgets(buf, 1024, stdin);
    execs(buf);
}

Esercizio 3

Soluzione di Tommaso Ognibene

[Python 3]

'''
Prova Pratica di Laboratorio di Sistemi Operativi
18 luglio 2013
Esercizio 3

URL: http://www.cs.unibo.it/~renzo/so/pratiche/2013.07.18.pdf

@author: Tommaso Ognibene
'''

import os, sys

def Main(argv):
    # Check number of arguments
    if len(argv) != 3:
        print("The function requires two arguments to be passed in.")
        return
    
    # Check parameters
    srcDir = str(argv[1])
    dstDir = str(argv[2])
    if not os.path.isdir(srcDir):
        print("First argument should be an existing directory.")
        return
    if not os.path.isdir(dstDir):
        print("Second argument should be an existing directory.")
        return
    
    # Build a dictionary with key-value pair {file base name - occurrences}
    nameFreq = { }
    for dirPath, dirNames, fileNames in os.walk(srcDir):
        for fileName in fileNames:
            fileBaseName, _ = os.path.splitext(fileName)
            nameFreq[fileBaseName] = nameFreq.get(fileBaseName, -1) + 1
            
            # Create a soft link
            freq = nameFreq[fileBaseName]
            linkName = "{0}{1}".format(fileBaseName, str(freq) if freq > 0 else "")
            srcPath = os.path.join(os.path.abspath(dirPath), fileName)
            dstPath = os.path.join(dstDir, linkName)
            if not os.path.lexists(dstPath):
                os.symlink(srcPath, dstPath)
        
    print("Done!")

if __name__ == "__main__":
    sys.exit(Main(sys.argv))

Soluzione di Fede

import os, sys

def collectfiles(arg1,arg2): 
	fcd = os.listdir('{0}'.format(arg1))
	while fcd != []:
		B=str(fcd.pop())
		C='{0}/{1}'.format(arg1,B)
		if os.path.isdir('{0}'.format(C)):
			collectfiles(C,arg2)
		elif os.path.isfile('{0}'.format(C)):
			try:
				os.symlink('{0}'.format(C), '{0}/{1}'.format(arg2,B))
			except OSError:
				i=1
				while True:
					try:
						os.symlink('{0}'.format(C), '{0}/{1}{2}'.format(arg2,B,i))
						break
					except OSError:
						i=i+1

try:
	collectfiles(str(sys.argv[1]),str(sys.argv[2]))
except OSError:
		print("Invalid Directory!")

Altra soluzione di Tommaso Ognibene

Leggendo la tua versione ho pensato che in effetti se i file sono """pochi""" l'hash-table non e' necessaria. E' sufficiente un controllo iterativo.

'''
Prova Pratica di Laboratorio di Sistemi Operativi
18 luglio 2013
Esercizio 3

URL: http://www.cs.unibo.it/~renzo/so/pratiche/2013.07.18.pdf

@author: Tommaso Ognibene
'''

import os, sys

def Main(argv):
    # Check number of arguments
    if len(argv) != 3:
        print("The function requires two arguments to be passed in.")
        return
    
    # Check parameters
    srcDir = str(argv[1])
    dstDir = str(argv[2])
    if not os.path.isdir(srcDir):
        print("First argument should be an existing directory.")
        return
    if not os.path.isdir(dstDir):
        print("Second argument should be an existing directory.")
        return
    
    # Traverse the directory tree and create a soft link for each file
    for dirPath, _, fileNames in os.walk(srcDir):
        for fileName in fileNames:
            # 'example.pdf' -> 'example'
            # 'example.xml' -> 'example'
            fileBaseName, _ = os.path.splitext(fileName)
            linkName = fileBaseName
            srcPath = os.path.join(os.path.abspath(dirPath), fileName)
            dstPath = os.path.join(dstDir, linkName)
            i = 0
            while os.path.isfile(dstPath):
                # 'example' will point to 'example.pdf'
                # 'example1' will point to 'example.xml'
                i += 1
                linkName = fileBaseName + str(i)
                dstPath = os.path.join(dstDir, linkName)
            if not os.path.lexists(dstPath):
                os.symlink(srcPath, dstPath)
        
    print("Done!")

if __name__ == "__main__":
    sys.exit(Main(sys.argv))

Soluzione di Fede in bash

Ecco la mia versione in bash

#! /bin/bash
 
BornAgainFede () {
for f in "$1"/*; do
	bn=$(basename "$f")
	if [[ -f $f ]] ; then
		if [[ -h "$2"/"$bn" ]] ; then
			i=1
			while [[ -h "$2"/"$bn$i" ]] ; do
			let "i += 1" 
			done
			ln -s "$1"/"$bn" "$2"/"$bn$i"
		else
			ln -s "$1"/"$bn" "$2"/"$bn"
		fi
	fi
	if [[ -d $f ]] ; then
		BornAgainFede "$1"/"$bn" "$2"
	fi
done
}
 
BornAgainFede "$1" "$2"

Versione by Daniele Cortesi

from sys import argv
import os

#controllo se il programma e' stato chiamato correttamente
if len(argv)<3:
	print "uso: programma cartella1 cartella2"
	exit(1)
	
dir1=argv[1]
dir2=argv[2]

#controllo che gli argomenti siano effettivamente cartelle
if not os.path.isdir(dir1) or  not os.path.isdir(dir2):
	print "cartelle non valide"
	exit(1)

cartelle=[os.getcwd()+"/"+dir1] #lista che conterra' mano a mano tutte le sottocartelle
files={} #dizionario con nomefile: [lista con tutti i file che si chiamano "nomefile"]

while len(cartelle)>0:
	c=cartelle.pop() 
	for f in os.listdir(c): #analizzo tutti i file nella cartella c
		#se f non e' una cartella, allora lo inserisco nel dizionario se non c'e', altrimenti aggiungo il suo percorso alla lista
		if not os.path.isdir(c+"/"+f):
			if not files.has_key(f):
				files[f]=[c+"/"+f]
			else: files[f].append(c+"/"+f)
		#se f invece e' una cartella, la aggiungo alla lista cosi' alla prossima iterazione (o dopo in caso di piu' cartelle) verra' analizzata
		else: cartelle.append(c+"/"+f)
		
#creo i link simbolici	
for k in files.keys():
	for i,f in enumerate(files[k]):
		link=k
		if (i>0): link+=str(i)
		os.symlink(f, dir2+"/"+link)

exit(0)

Osservazioni di Amonaldini

Per prima cosa, bel lavoro!
Di seguito riporto alcune osservazioni che mi sono venute in mente, prendetele assolutamente con soli fini "costruttivi"!

  • Se non sbaglio, i due programmi (quello di Tommaso e quello di Fede) hanno un comportamento diverso per quanto concerne l'interpretazione del nome file e, in particolare, dell'estensione:
    • Per Tommaso:
      • dir1/example.pdf --> example
      • dir1/example.txt --> example1
      • dir1/dirX/example.pdf --> example2
    • Per Fede:
      • dir1/example.pdf --> example.pdf
      • dir1/example.txt --> example.txt
      • dir1/dirX/example.pdf --> example.pdf1
    • interpretando in modo "stringente" del testo dell'esercizio avrei ipotizzato un comportamento come quello di Fede.
  • Nel codice sorgente c'è in generale poco "commento". Commentare il codice è essenziale: permette ad altri di capire immediatamente le scelte implementative fatte e permette a voi di capire al volo il perché di queste scelte nel caso dobbiate riprendere in mano il vostro codice dopo un po' di tempo. Quindi: commentate!!!
  • Bash si presta molto all'esercizio

Soluzione di fede&pirata

/*
ESERCIZIO 3.
*/
#include<unistd.h>  
#include<stdio.h> 
#include<stdlib.h> 
#include<string.h> 
#include<dirent.h> 
#include <sys/types.h> 
#include <sys/stat.h> 

void collectandlink(char *arg1, char *carg2)
{ 
	struct dirent **namelist; 
	int n; 
	n = scandir( arg1 , &namelist, 0, alphasort); 

	if (n < 0) 
		perror("scandir"); 
	else 
	{ 
		while(n--)
		{ 
			int i;
			char* istr;
			int datest;
			char *pathnametmp;
			char *pathwithname=(char *)malloc(512);
			char *pathwithname2=(char *)malloc(512);
			strcpy(pathwithname,arg1);
			strcpy(pathwithname2,carg2);
			strcat(pathwithname,"/");
			strcat(pathwithname2,"/");
			strcat(pathwithname,namelist[n]->d_name);
			strcat(pathwithname2,namelist[n]->d_name);
			struct stat s; 
			if( stat(pathwithname,&s) == 0 ) 
			{  
				if( s.st_mode & S_IFDIR ) //è un dir
				{ 
					if ( strcmp( namelist[n]->d_name , ".." ) == 0 )//si deve escludere ".."
					{ 
						continue; 
					} 
					if( strcmp( namelist[n]->d_name , "." ) == 0 )//si deve escludere "."
					{ 
						continue; 
					}  
					else 
					{
						collectandlink(pathwithname,carg2);
					}
				} 
				else 
				{
					if( s.st_mode & S_IFREG ) //è un file
					{ 
						datest=symlink(pathwithname,pathwithname2);//torna 0 al successo -1 altrimenti
						if(datest==-1) //se simlink fallisce => link esiste
						{
							i=1;
							while(datest!=0)//finchè simlink non ha successo
							{
								istr=(char *)malloc(32);
								pathnametmp=(char *)malloc(544);
								strcpy(pathnametmp,pathwithname2);
								sprintf(istr, "%d", i);
								strcat(pathnametmp,istr);
								datest=symlink(pathwithname,pathnametmp);
								free(istr);
								free(pathnametmp);
								i=i+1;
							}
						}
					} 
				} 
			}
			free(pathwithname);
			free(pathwithname2);
		} 
	}
	free(namelist);
}

int main(int argc, char *argv[]){ 
collectandlink(argv[1],argv[2]); 
return 1; 
}

Soluzione di Krusty

import os, sys
from stat import *

exist={}

def Scan(pathname):

	for f in os.listdir(pathname):
		path = os.path.join(pathname,f)
		s = os.stat(path)
		if S_ISREG(s.st_mode):
			if f not in exist:
				exist[f] = 1
				os.symlink(path,f)	
			else:
				n = exist[f]
				exist[f] = exist[f] + 1
				os.symlink(path,(f+str(n)))	
				
		elif S_ISDIR(s.st_mode):	
			Scan(path)

#main

if not os.path.isdir(sys.argv[2]):
    os.makedirs(sys.argv[2])

os.chdir(sys.argv[2])
Scan(sys.argv[1])

int main(int argc, char * argv[]) {

   char buf[1024];
   fgets(buf, 1024, stdin);
   execs(buf);

} </source>

Soluzione di MV

#! /bin/bash

if (( $# != 2 ))
then
	echo "Usage: nameindex dir1 dir2"
	exit 1
fi

if [[ $1 = /* ]] # verifico se $1 è un path assoluto o relativo (è necessario assoluto per i link)
then 
	dir1=$1
else
	dir1=`pwd`/$1
fi

for i in `find $dir`	# scandisco l'albero
do
	if [ -f $i ]
	then
		filename=`basename $i`		# ricavo il nome del file senza percorso
		
		if [ ! -f $2/$filename ] 	# se è il primo file trovato con quel nome
		then						# creo il link con lo stesso nome
			ln -s $dir1/$i $2/$filename
		else			# altrimento cerco il primo numero n>0 non ancora usato come suffisso
			n=1
			while [ -f $2/${filename}${n} ]
			do
				(( n=$n+1 ))
			done
			ln -s $dir1/$i $2/$filename$n
		fi
	fi
done