Stampf - implementazione ridotta della printf.
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