Newsgroup: it.comp.lang.c
Titolo: File di Log.

Salve a tutti.
Sono nuovo di programmazione C e in uno dei miei tanti tentativi ho
scritto:
char *logfile;
logfile = malloc(sizeof(getenv("HOME"))+sizeof("/file.log"));
logfile = strcat((char *)getenv("HOME"),"/file.log");
Sembra funzionare. Serve per ricavare il percorso della home dir e
appendere il nome del file (scusate per l'ovvia spiegazione). E'
esatto scrivere così oppure ci sono errori formali o di sicurezza,
ecc.?
E' solo un dubbio, magari è corretto :)
Grazie.
Paolo.

 
Si, il dubbio era corretto :-)
C:\>iao Angelo
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

      malloc(strlen(homeNameP)+sizeof("/file.log") +1);
Non ho verificato il resto, ma sei sicuro che un char occupi sempre
solo un byte?
Se cosě fosse, tutto bene, ma nel dubbio IMHO č piů sicuro
	malloc(sizeof(char) * strlen(homeNameP)+sizeof("/file.log")
+1);
Cosě anche se un char occupa per esempio 2 byte, sei a posto. :-)

 Cosě anche se un char occupa per esempio 2 byte, sei a posto. :-)
Il char occupa *sempre* 1 byte sotto qualunque
piattaforma.  Il wchar_t e' il tipo previsto per 
Unicode.


 solo un byte?
Garantito, come da standard.
http://www.lysator.liu.se/c/rat/a.html#1-6
1.6  Definitions of terms
The definitions of object, bit, byte, and alignment reflect
a strong consensus, reached after considerable discussion,
about the fundamental nature of the memory organization of a C
environment: 
- All objects in C must be representable as a contiguous sequence of
  bytes, each of which is at least 8 bits wide.  
- A char (or signed char or unsigned char)  occupies exactly one byte. 
(grazie ad un post di Andrea Laforgia)


Unicode.
Si, lo pensavo anch'io, ma proprio l'altro ieri stavo leggendo un
libro preso in prestito in biblioteca: "Da C a C++" di Greg Perry.
E' un libro datato (1992 l'edizione in lingua Inglese), ma
m'interessava solamente per una panoramica sul C++.
C'era un punto in cui si evidenziava che spesso il compilatore
promuove i char ad int prima di metterli in memoria.
Probabilmente si tratta di un'informazione datata o peggio ancora
errata del tutto.
Meglio cosě. ;-)

 libro preso in prestito in biblioteca: "Da C a C++" di Greg Perry.
Se il libro che stai leggendo afferma che un dato di tipo 
char puo' occupare piu' di un byte, il libro che stai
leggendo e' in errore.  Lo standard del linguaggio C 
afferma che sizeof(char) e' uguale a 1.



anche in C avviene in casi particolari, per esempio nel passaggio di
parametri a funzioni che accettano parametri variabili.
Poi in generale nessuna funzione riceve parametri di dimensione inferiore
alla dimensione della parola macchina (cioe' il tipo int), ma questo rimane
normalmente nascosto al programmatore, che puo' tranquillamente definire
come tipo char i parametri delle proprie funzioni.
ciao
donCiccio

libro preso in prestito in biblioteca: "Da C a C++" di Greg Perry.
Il char occupa esattamente 1 byte. Quello di cui non puoi essere certo
e' che il char sia esattamente di 8 bit: lo standard ti garantisce
solo che sia ALMENO di 8 bit (ma puoi saperlo con esattezza andando a
leggere la macro CHAR_BIT contenuta in limits.h).
La problematica a cui a si riferiva il libro sulla conversione a int
e' (credo) questa: (che cito dal Koenig):
"But a compiler converting a char to an int has a choice: shoult it
treat the char as a signed or an unsigned quantity? In the former, it
should expand the char to an int by replicating the sign bit; if the
latter, it should fill the extra bit positions with zeroes.
[SNIP]
This, in turn, affects the way a programmer will design things like
hash tables and translate tables."
Mox :)

leggere la macro CHAR_BIT contenuta in limits.h).
Capito... non avevo considerato questa possibilità...
Ma il byte non è definito a 8 bit? Esistono sistemi in cui un byte
occupa un numero maggiore di bit?
Comunque dopo l'osservazione di Andrea Laforgia ho prontamente
riportato il libro alla biblioteca, consigliando di rinnovare il
reparto informatico di materiale meno inutile (tipo la guida di
office, che fra sei mesi sarà già obsoleta e nessuno leggerà più.
:-)))  ).
Non ci si può proprio più fidare di nulla, vero? :-)))

 occupa un numero maggiore di bit?
Si'.


Non ci si può proprio più fidare di nulla, vero? :-)))
Beh, se prima di prendere i libri provi a documentarti presso
http://www.accu.org
 sei piu' sicuro di cosa ti troverai davanti :)
Tra l'altro e' li che hai la prima intuizione che forse Shildt non sia
poi quel grande autore :P
Mox

 logfile = strcat((char *)getenv("HOME"),"/file.log");
Questo codice è un segfault in attesa di accadere...
1- non controlli il valore di ritorno di malloc e getenv. Entrambe
   ritornano NULL in caso di errore, che è un crash garantito
2- stai allocando spazio per due puntatori a stringa, non per due
   stringhe. Un puntatore tipicamente occupa appena 4 byte
3- mai usare strcat, non sa mai quando fermarsi. Usa strncat
4- le stringhe ritornate da getenv potrebbero essere di sola lettura
   e/o sovrascritte da successive chiamate a getenv
5- lo spazio allocato per logfile in realtà viene scartato
Insomma, il C è una brutta bestia da domare. Non sperare mai di 
cavartela in due righe se sono coinvolte delle stringhe :-)
Ecco come lo farei io (sì, tutti quei controlli sono necessari):
/*
nota 1: flame e/o battutine sulla notazione ungherese > /dev/null
nota 2: non l'ho provato, non so nemmeno se compila, ma ad occhio e
        croce direi che può andare
*/
/* globale */
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* immaginiamo che il nome del file di log venga specificato da un
   parametro di tipo const char * chiamato in_pszFileName e che il path 
   completo venga ritornato in un parametro di tipo char ** chiamato
   out_ppszLogFile passato dall'utente */
int getLogFullPath(const char *  in_pszFileName,
                         char ** out_ppszLogFile)
{
 char *pszLogFile = 0;
 char *pszHomeDir = 0;
 int   nStrLen = 0;
 int   nFileNameLen;
 /* validazione parametri */
 if(out_ppszLogFile == 0 || in_pszFileName == 0)
 {
  errno = EINVAL;
  return (-1);
 }
 /* se un nome di file inizia con '/', è convenzione accettata che sia
    considerato relativo alla root del processo */
 if(in_pszFileName[0] = '/')
 {
  /* calcola la lunghezza della stringa */
  nFileNameLen = strlen(in_pszFileName);
  /* nome troppo lungo */
  if(nFileNameLen > PATH_MAX)
  {
   errno = ENAMETOOLONG;
   return (-1);
  }
  /* fai spazio nel buffer per l'intera stringa */
  pszLogFile = malloc(nFileNameLen + 1);
  /* allocazione fallita */
  if(pszLogFile == 0)
   return (-1);
  /* copia il nome del file nel buffer */
  if(strncpy(pszLogFile, in_pszFileName, nFileNameLen) == 0)
   return (-1);
  /* se siamo arrivati qui, tutto va bene */
  *out_ppszLogFile = pszLogFile;
  return (0);
 }
 /* abbondiamo. Allochiamo il più possibile */
 pszLogFile = (char *)malloc(PATH_MAX + 1);
 /* abbiamo abbondato troppo :-) memoria esaurita */
 if(pszLogFile == 0)
  return (-1);
 /* ottieni la variabile - qui sarebbe utile mettere un bel mutex per
    impedire che più thread chiamino contemporaneamente getenv */
 pszHomeDir = getenv("HOME");
 if(pszHomeDir != 0)
  /* copia la home directory nel path completo */
  if(strncpy(pszLogFile, pszHomeDir, PATH_MAX - nStrLen) == 0)
  {
   /* copia fallita */
   /* qui il mutex andrebbe rilasciato */
   free(pszLogFile);
   return (-1);
  }
  else
  {
   /* aggiorna la lunghezza effettiva della stringa */
   nStrLen = strlen(pszLogFile);
   /* qui il mutex andrebbe rilasciato */
  }
 /* se l'ultimo carattere non è uno slash... */
 if(pszLogFile[nStrLen - 1] != '/')
  /* ... e se c'è spazio ... */
  if((PATH_MAX - nStrLen) >= 1)
  {
   /* ... aggiungi uno slash e sposta la fine della stringa */
   pszLogFile[nStrLen] = '/';
   pszLogFile[nStrLen + 1] = 0;
   nStrLen += 1;
  }
  else
  {
   /* altrimenti, esci e segnala il fallimento */
   free(pszLogFile);
   errno = ENAMETOOLONG;
   return (-1);
  }
 /* ricaviamo la lunghezza del nome del file */
 nFileNameLen = strlen(in_pszFileName);
 /* lo spazio ancora disponibile non basta */
 if((PATH_MAX - nStrLen) < nFileNameLen)
 {
  /* fallisci e segnala l'errore */
  free(pszLogFile);
  errno = ENAMETOOLONG;
  return (-1);
 }
 /* attacca il nome del file in coda */
 if(strncat(pszLogFile, in_pszFileName, PATH_MAX - nStrLen) == 0)
 {
  /* fallisci e segnala l'errore */
  free(pszLogFile);
  return (-1);
 }
 /* se siamo arrivati qui, tutto va bene */
 *out_ppszLogFile = pszLogFile;
 return (0);
}
-- 
 < 
http://www.reactos.com/
 >  Open source clone of Windows NT. Current
     Don't stand, REACT!      version 0.0.18. C, C++ and ASM developers
                              and beta testers are welcome!

> Ma il byte non č definito a 8 bit? 
AL> E' *almeno* di 8 bit.
Ci sono stati byte piu` corti  se non sbaglio. Mi pare che sul CDC6600
fosse di 6  bit (la macchina aveva  una parola di 60 bit)  e credo che
tra le varie  architetture sulle quali aveva lavorato  Donald Knuth ce
ne fossero col byte piu` corto se no non si spiegherebbe la necessita`
di definire i byte del MIX 1009 come celle che garantiscono 0-100...
E'  proprio  perche'  il  byte  non  e` sempre  di  8  bit  che  nelle
telecomunicazioni si parla di ottetti....
-- 
Gian  | main(){printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
Uberto|                        David Korn, 1987 ioccc best one liner 
Lauri |-------------------------------------------------------------------
(aka saint) >>Vedere X-Nospam nell'header - See X-Nospam field in Header<<

 Ci sono stati byte piu` corti  se non sbaglio. 
Non importa.  Lo standard del linguaggio assume che 
un byte sia almeno di 8 bit, e questo perche' evidentemente
non ci sono piu' piattaforme in cui il byte sia piu' 
corto.


 [mega snip]
Non ti sembra di aver esagerato un po' con il codice? :-)
C:\>iao Angelo
[1] Della serie... vabbene essere paranoici, ma qui si sconfina
    nella schizofrenia piu' totale. :-)))))


 - dovrebbe essere solamente letta.
La  stringa  ritornata  potrebbe essere  sovrascritta  dalle  successive
chiamate a  getenv. Se la  chiamata successiva  avviene in  altro thread
mentre stai copiando  la stringa... C'e` comunque da dire  che di solito
gli  sviluppatori  delle  varie  librerie C  sono  abbastanza  furbi  da
inserire anche una versione thread-safe della chiamata.
ciao
Kronos
-- 
Home: 
http://kronoz.cjb.net
La vasca da bagno fu inventata nel 1850, il telefono nel 1875.
Se fossi vissuto nel 1850, avrei potuto restare in vasca per 25 anni
senza sentir squillare il telefono

 dalle  successive chiamate a  getenv.
????? Ma getenv non legge solamente? Una sovrascrittura
puo' avvenire solo se qualcuno si prende la briga di
andarla a sovrascrivere.
Se qualcuno e' tanto fesso da sovrascrivere il valore della
variabile HOME (che uno si aspetta punti a /home/nomeUtente)
non c'e' mutex che tenga.
Chi ti dice che la sovrascrittura non sia avvenuta prima?
Chi ti garantisce che il valore letto sia utilizzabile?
Scusa, ma alcuni punti fermi devono esserci. Uno di questi e'
che HOME punta a /home/nomeUtente e che nussuno la vada a
modificare.
A mio avviso non e' un errore presumere che HOME non venga
modificata (indipendentemente dal fatto che sia o meno thread
safe) e' un eccesso (sempre a mio avviso) presumere il contrario.
C:\>iao Angelo


 andarla a sovrascrivere.
Non ci siamo capiti: getenv potrebbe ritornare un puntatore ad un buffer
statico. Se fai una cosa del genere:
char *home = getenv("HOME");
char *foo = getenv("BAR");
La stringa puntata  da home potrebbe non essere piu`  la home directory,
visto che il buffer potrebbe essere stato sovrascritto.
Dallo standard [7.20.4.5]:
The getenv  function returns a pointer  to a string associated  with the
matched list member. The string pointed to  shall not be modified by the
program,  but may  be overwritten  by a  subsequent call  to the  getenv
function. If  the specified  name cannot  be  found, a  null pointer  is
returned.
Il  condizionale e`  d'obbligo,  visto  che un'implementazione  potrebbe
decidere di  allocare spazio per ogni  chiamata a getenv in  modo da non
sovrascrivere i risultati delle chiamate precedenti. Il rischio comunque
c'e`. In un programma normale bisognerebbe fare una cosa del genere:
if ((foo = getenv("BAR")) != NULL)
	strcpy(my_buff, foo);
Se il programma  e` multithreaded sarebbe bene inserire  anche una mutex
in modo che due thread non chiamino simultaneamente getenv.
ciao
Kronos
-- 
Home: 
http://kronoz.cjb.net
Software is like sex; it's better when it's free.
Linus Torvalds

 modificare.
mica vero. Non mi risulta sia un punto fermo. Un punto fermo semmai è 
che un thread che vuole settarla a /home/cacca e un secondo thread che 
vuole settarla a /home/pupu non si ritrovino alla fine con una $HOME 
contenente /home/cucu o /home/papa


     nella schizofrenia piu' totale. :-)))))
Vedi sopra :-)


 char *pszLogFile = 0;
p -> pointer
s -> string?
z -> ???
ciao
Kronos
-- 
Home: 
http://kronoz.cjb.net
"La Via prosegue senza fine."
	Bilbo Baggins - Il Signore degli Anelli - J.R.R. Tolkien

"Juliet::Juliet" ha scritto
:
:  /* se un nome di file inizia con '/', è convenzione accettata che sia
:     considerato relativo alla root del processo */
:  if(in_pszFileName[0] = '/')
:  {
Accetto la convenzione, ma il test io lo scriverei cosi`:
  if('/' == in_pszFileName[0])
in questo modo se ti dimenticassi un = il compilatore ti segnala
l'errore; volendo, puoi alzare al massimo il livello di warning
del tuo compilatore. (-;
:
:   /* fai spazio nel buffer per l'intera stringa */
:   pszLogFile = malloc(nFileNameLen + 1);
:
:   /* allocazione fallita */
:   if(pszLogFile == 0)
:    return (-1);
:
:   /* copia il nome del file nel buffer */
:   if(strncpy(pszLogFile, in_pszFileName, nFileNameLen) == 0)
:    return (-1);
Occhio e croce io dico che e` piu` sicuro
    strcpy(pszLogFile, in_pszFileName);
perche` strncpy non ti mette lo '\0' se non c'e` spazio, e qui
lo spazio c'e` ma tu gli impedisci di usarlo :-(
:
:     impedire che più thread chiamino contemporaneamente getenv */
:  pszHomeDir = getenv("HOME");
:
:  if(pszHomeDir != 0)
:   /* copia la home directory nel path completo */
:   if(strncpy(pszLogFile, pszHomeDir, PATH_MAX - nStrLen) == 0)
:   {
:    /* copia fallita */
che significa copia fallita? ti aspetti che strncpy restituisca
0 se la copia fallisce?
:    /* qui il mutex andrebbe rilasciato */
:    free(pszLogFile);
:    return (-1);
:   }
:   else
:   {
:    /* aggiorna la lunghezza effettiva della stringa */
:    nStrLen = strlen(pszLogFile);
nel caso strlen(pszHomeDir) >= PATH_MAX - nStrLen non temi che
la chiamata a strlen( pszLogFile ) possa rivelarsi avventurosa?
Semmai
nStrLen = strlen(pszHomeDir);
prima di copiarla.
ciao
Massimo

 prima di copiarla.
Sì, giusto. Stupide stringhe null-terminated


"Juliet::Juliet" ha scritto
:
: no, mi preoccupo terribilmente se strncpy mi ritorna zero, dato che non
: dovrebbe. Se succede, è un bug e se non causa danni a me li causerà a
: qualcuno che usa realmente il valore di ritorno di strncpy. Sbaglio
: solo a lasciar correre così, con un semplice return -1.
: Dovrebbe essere:
:
: assert(strncpy(pszLogFile, pszHomeDir, PATH_MAX - nStrLen));
:
Lo dico per scrupolo, perche` immagino che sia noto a tutti, assert non si
usa cosi`, decisamente meglio qualcosa di simile a:
temp = strncpy(pszLogFile, pszHomeDir, PATH_MAX - nStrLen);
assert( temp == pszLogFile );
1. assert e` una macro ed ha un comportamento diverso a seconda che sia
   definito o meno NDEBUG, in particolare nel caso sia definito si espande
   in una no-op, per cui e` bene non mettere chiamate a funzioni che devono
   essere eseguite comunque.[*]
2. se vuoi sincerarti che la libreria che usi non contenga bugs, devi
   confrontare il valore restituito con quello previsto e non solo con zero.
ciao
Massimo
[*] Provare per credere:
#define NDEBUG
#include <stdio.h>
#include <assert.h>
int main()
{
  /*
   * Quando e` definito NDEBUG
   * printf non viene chiamata
   */
  assert( printf( "Hello world\n" ) );
  return 0;
}

 in modo che due thread non chiamino simultaneamente getenv.
Il problema e' che nessuno ti garantisce che la cosa funzioni.
Il semaforo andrebbe messo sul buffer statico, non sulla funzione.
Nessuno vieta ad un'altro programmatore di scrivere una funzione che
chiama getenv() senza controllare il semaforo.
thread 1
fnc1()
{
   char* homeP = NULL;
   char  locHome[4096];
   if(!semaforo()) [1]
   {
      homeP = getenv()
      if(NULL != homeP)
         strcpy(locHome, homeP);
   }
}
thread 2
fnc2()
{
   char* homeP = NULL;
   char  locHome[4096];
   homeP = getenv();
   if(NULL != homeP)
      strcpy(locHome, homeP);
}
Il fatto di aver messo un semaforo, non credo risolva la situazione.
C:\>iao Angelo.
[1] Spero ti accontenterai di pseudo-codice e non di un esempio
    funzionante :-).


 Ho una sola cosa da dirti: GET /default.ida?NNNNNNNNNNNNNNNNNNNNNNNNNNN
Se e' solo per questo, basta stare lontani da mamma MS e non
si hanno problemi.
Comunque, la prossima volta la prima riga dei miei main sara:
   if(osIsNotEmulated())
   {
       ...
   }
   :-)
C:\>iao Angelo.


> Ci sono stati byte piu` corti  se non sbaglio. 
AL> Non importa.  Lo standard del linguaggio assume che 
AL> un byte sia almeno di 8 bit,
Non avevo colto questa restrizione al solo C...
AL> e questo perche' evidentemente non ci sono piu' piattaforme in cui
AL> il byte sia piu' corto.
Penso anche io che siano sparite, troppo custom :). 


 chiama getenv() senza controllare il semaforo.
In questo  caso il  programmatore andrebbe percosso  con un  crick sulle
gengive. Visto che il  programmatore sta lavorando ad un  programma MT e
sa che getenv potrebbe creare  problemi dovrebbe racchiudere il codice a
rischio tra mutex, oppure crearsi una funzione ad hoc:
pthread_mutex_t lock_getenv = PTHREAD_MUTEX_INITIALIZER;
char *my_getenv(char const * name) {
	char *p, *ret;
	int l;
	pthread_mutex_lock(&lock_getenv);
	p = getenv(name);
	if (p == NULL) {
		pthread_mutex_unlock(&lock_getenv);
		perror("getenv()");
 
		return NULL;
	}
	l = strlen(p) + 1;
	ret = malloc(l);
	assert(ret);
	memcpy(ret, p, l);
	pthread_mutex_unlock(&lock_getenv);
	return ret;
}
ciao
Kronos
-- 
Home: 
http://kronoz.cjb.net
Non sempre quello che viene dopo e` progresso.
Alessandro Manzoni

 rischio tra mutex, oppure crearsi una funzione ad hoc:
Questa e' buona soluzione solo se accompagnata dal crick sempre in bella
mostra sul tavolo del capo progetto :-)
C:\>iao Angelo
