Stampf - implementazione ridotta della printf.

From Sistemi Operativi
Jump to navigation Jump to search
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>

char *itos(int n, int *p); //int to string (base 10)
int slen(char *s); //string lenght
char *itos0x(int n, int *p); //int to string (base 16)

/*
 * stampf funziona come la printf (viene invocata allo stesso modo) ma riconosce solo alcuni parametri (int, char, stringhe,  void * (qualsiasi puntatore) e il %)
 * ma non vengono fatti controlli sui tipi, impossibili con va_list che non è in grado di riconosce il tipo (infatti lo prende in input),
 * per cui se il tipo passato non corrisponde a quello indicato con %, si possono verificare errori a run time di vario tipo
 * per stampare usa la system call write
 */
void stampf(char* s, ...) {
	int i=0;
	va_list ap;
	va_start(ap,s);
	while (s[i]!='\0'){
		while (s[i]!='\0' && s[i]!='%') i++; //raggiungo un % o la fine della stringa
		if (i>0) write( STDOUT_FILENO, s, i ); //stampo fino al punto raggiunto, escludendo l'eventuale % o \0
		if (s[i]=='%') {
			//var temporanee che non è detto vengano utilizzate
			int n; //usato per i parametri interi, per gli indirizzi e per contenere il num di carattere delle conversioni di questi in stringhe
			char *str; //usato per le stringhe di cui sopra e per quelle passate come parametro
			char c[6]="(nil)"; //nil già pronto (come usa printf) per i puntatori NULL, altrimenti c[0] viene usato per i parametri carattere
			switch(s[++i]){
				case 'd':
					n=va_arg(ap,int);
					str=itos(n,&n);
					write(STDOUT_FILENO, str, n );
					free(str);
					break;
				case 's':
					str=va_arg(ap,char*);
					write(STDOUT_FILENO, str, slen(str) );
					break;
				case 'c':
					c[0]=va_arg(ap,int); //int perchè va_arg converte i caratteri in interi prima di restituirli
					write(STDOUT_FILENO, &c[0], 1 );
					break;
				case 'p':
					n=(int)va_arg(ap,void*);
					if (n==0) write(STDOUT_FILENO, c, 5 ); //stampo (nil)
					else {
						str=itos0x(n,&n);
						write(STDOUT_FILENO, str, n );
						free(str);
					}
					break;
				case '%':
					write(STDOUT_FILENO, &s[i], 1 );
					break;
			}
			i++;
		}
		s=s+i; //faccio ricominciare la lettura di s o dal carattere successivo a quello dopo il % o dal '\0' e in quest'ultimo caso si uscirà dal ciclo
		i=0;
	}
	va_end(ap);
}

/*
 * itos converte un intero in una stringa allocata dinamicamente
 * prende in input il numero e un indirizzo (anche null) usato per restituire il numero di caratteri escluso lo '\0' 
 */
char *itos(int n, int *p) {
	int ncar=1,tmp=n/10,i,inizio=0;
	char *s=NULL;
	while (tmp) { //conto le cifre di n
		ncar++;
		tmp/=10;
	}
	if (n<0) { //metto il segno
		ncar++;
		s=(char*)malloc((ncar+1)*sizeof(char)); //+1 per il carattere terminatore
		*s='-';
		inizio=1;
		n*=-1;
	}
	else s=(char*)malloc((ncar+1)*sizeof(char));
	s[ncar]='\0';
	for (i=ncar-1; i>=inizio; n/=10, i--) s[i]=(char)(n%10 + 48); //converto la cifra in carattere ascii, 48 è lo zero
	if (p) *p=ncar;
	return s;
}

/*
 * itos0x converte un intero in num esadecimale e lo restituisce come stringa allocata dinamicamente
 * prende in input il numero e un indirizzo (anche null) usato per restituire il numero di caratteri escluso lo '\0' 
 */
char *itos0x(int n, int *p){
	int ncar=0, i,tmp,j;
	char v[sizeof(int *)*2]; //con un num esadecimale di 2 cifre posso rappresentare un byte, quindi per rappresentarne sizeof(int*) mi servirà al max un vettore grande il doppio
	char *s; //stringa in output
	do { //converto il num in esadecimale
		tmp=n%16;
		if (tmp<10) v[ncar]=tmp+48;
		else v[ncar]=tmp-10 + 97; //97 è la a minuscola nella tab ascii
		n/=16;
		ncar++;
	}while (n);
	s=(char*)malloc((ncar+3)*sizeof(char)); //+3 per il 0x iniziale e lo '\0' finale
	s[0]='0';
	s[1]='x';
	for (i=2, j=ncar-1; i<ncar+2; i++, j--) s[i]=v[j]; //copio v in s in ordine inverso
	s[i]='\0';
	if (p) *p=ncar+2;
	return s;
}

/*
 * slen restituisce la lunghezza di una stringa escluso lo '\0'
 */
int slen(char *s) {
	int l=0;
	while (*s++) l++;
	return l;
}

Daniele Cortesi