2010-05-14 14 views
6

Ho una funzione irc_sendline che può essere chiamata come printf puòC di argomenti variabili refactoring

irc_sendline(s, "A strange game.\nThe only %s is not to play.", "winning move"); 

Funziona perfettamente, ma non sono felice con la sua attuazione:

int irc_sendline(irc *iobj, char *msg, ...) 
{ 
    char tmp_msg[BUFSIZE], fmsg[BUFSIZE]; 
    va_list args; 
    int len; 

    va_start(args, msg); 

    strncpy(tmp_msg, msg, BUFSIZE); 
    strncat(tmp_msg, "\r\n", BUFSIZE); 

    len = vsnprintf(fmsg, BUFSIZE, tmp_msg, args); 
    len = send(iobj->fd, fmsg, len, 0); 

    return len; 
} 

Vedete, Sto usando 2 buffer "temporanei" qui, perché devo prima copiare il messaggio originale dagli argomenti della funzione in un buffer temporaneo per aggiungere "\ r \ n" ad esso, e quindi copiare quel buffer temporaneo su un altro temporaneo Buffe r per fare la formattazione effettiva con gli argomenti forniti dalla chiamata di funzione, e solo THEN Posso inviare la roba sulla sua strada.

Come posso rendere questo detergente, meglio?


Grazie per tutti gli input qui, ho pensato che il mio unico problema era il caos in là, ma era in realtà un timebomb ticchettio! La mia nuova funzione assomiglia a questa:

int irc_sendline(irc *iobj, char *msg, ...) 
{ 
    char buffer[BUFSIZE]; 
    va_list args; 
    int res_str_len; 
    int sent; 

    va_start(args, msg); 

    res_str_len = vsnprintf(buffer, BUFSIZE, msg, args); 

    sent = send(iobj->fd, buffer, res_str_len, 0); 
    sent += send(iobj->fd, "\r\n", 2, 0); 

    return sent; 
} 

Se potessi, accetterei più risposte qui, ma meh.

+0

Il codice è rotto a causa di un uso improprio del 'strncpy' (vedi la mia risposta). – AnT

+0

Anche l'uso di 'strncat' è rotto. 'strncat' non fa quello che pensi che faccia. – AnT

risposta

5

Primo utilizzo vsnprintf per formattare i dati, quindi aggiungere "\ r \ n" al risultato. In alternativa, basta usare una seconda chiamata a send per inviare "\ r \ n".

1

Dal momento che l'\r\n sta per finire, alla fine della stringa formattata, perché non copiare in seguito:

va_start(args, msg); 
len = vsnprintf(fmsg, BUFSIZE, msg, args); 
strncat(fmsg, "\r\n", BUFSIZE - strlen(fmsg) - 1); 

Nota che ho anche fissato gli argomenti da strncat.

0

A meno che non si desideri utilizzare msg per lo strcat (non sicuro e malvagio perché non si conosce la dimensione della stringa), penso che si dovrà convivere con 2 buffer.

Per inciso, considererei strncpy (..., BUFSIZE-2) in modo che \ r \ n sia sempre presente nei messaggi e pertanto le stringhe si avvolgono sempre.

+0

+1 per 'BUFSIZE-2'. –

+0

Grazie per aver segnalato la faccenda di BUFSIZE! Non me ne ero accorto per niente, avrei potuto spaventarmi. – LukeN

+0

Si sta ancora dimenticando che 'stncpy' non aggiunge il terminatore null alla stringa se il buffer è troppo corto. 'BUFSIZE-2' non lo risolverà. Il tuo codice si bloccherà ogni volta che la fine del buffer viene colpita dalla stringa di formato. – AnT

3

In primo luogo, se sei interessato a "pulitore", smetti di usare strncpy. Nonostante il nome fuorviante (e contrariamente alla credenza popolare), questa non è una funzione di copia delle stringhe di lunghezza limitata. È sicuro dire che strncpy è una funzione che oggi non ha praticamente alcun utilizzo pratico. Quando vedi il codice strncpy utilizzato nel codice, il "pulitore" diventa immediatamente fuori questione (a parte il set di custodie che sta scomparendo in modo sottile) strncpy era effettivamente destinato a essere utilizzato per).

Infatti, il codice è rotto in particolare perché si è utilizzato strncpy: se la lunghezza della stringa di formato è maggiore della lunghezza del buffer, strncpynon aggiunge la terminazione carattere null al risultato, il che significa che il successiva chiamata strncat si bloccherà (nella migliore delle ipotesi).

La funzione di copia di lunghezza limitata non esiste nella libreria standard, ma viene spesso fornita dall'implementazione con il nome strlcpy. Se l'implementazione che stai usando non ne fornisce una - scrivi tu stesso e usala.

Anche il codice è stato interrotto a causa dell'errato utilizzo di strncat: strncat non occupa la lunghezza del buffer completo come ultimo argomento. Invece, strncat si aspetta la lunghezza del resto disponibile del buffer.. Quindi, se si desidera utilizzare strncat, è necessario calcolare prima quanto spazio è rimasto alla fine del buffer dopo la copia precedente. Anche in questo caso, anche se strncat è più utile di strncpy, è preferibile utilizzare la funzione non standard (ma spesso fornita dall'implementazione) strlcat, che funziona in realtà come si pensava che strncat funzionasse.

In secondo luogo, invece di aggiungere la parte \r\n in anticipo, perché non lo fai dopo? Utilizzare il fatto che vsnprintf restituisce il numero di caratteri scritti nel buffer di output e semplicemente aggiungere gli \r, i caratteri alla fine dopo il termine di vsnprintf terminato. Non è necessario utilizzare strncat per questo scopo. Basta scrivere direttamente i caratteri nel buffer, assicurandosi, ovviamente, che non si attraversi il confine.

+0

Grazie per l'avvertimento (3 di loro in realtà :), al momento sto cercando questo problema, e sembra che devo scrivere il mio strlcpy, perché Linux non lo fornisce. Sulla questione strncpy - quindi è rotto perché non aggiungerà \ NUL quando la stringa di destinazione è più lunga/fino a quando la stringa di destinazione? – LukeN

+0

In primo luogo, sì, non aggiunge '\ 0' se la stringa è più lunga. In secondo luogo, riempie l'intero resto del buffer con zero se la stringa è più corta, il che è completamente inutile. Ancora una volta, 'strncpy' non è una funzione creata per le stringhe con terminazione nulla. È una funzione creata per uno scopo completamente diverso: supportare le cosiddette stringhe "a larghezza fissa" in alcune vecchie versioni del file system Unix. Puoi leggere maggiori informazioni qui: http://stackoverflow.com/questions/1453876/why-does-strncpy-not-null-terminate – AnT

+0

Grazie per aver chiarito questo problema, ora ricordo perché ho terminato esplicitamente le mie stringhe con zero alcuni altri programmi che usavano strncpy. – LukeN

0

Uno dei principali problemi con il codice - vsnprintf restituisce il numero di caratteri che verrebbero inseriti nel buffer se fosse infinitamente grande, che potrebbe essere più grande di BUFSIZE se il buffer non è abbastanza grande. Quindi, se hai un messaggio che trabocca, finirai per inviare una spazzatura casuale dopo la fine del tuo buffer. È necessario aggiungere una riga

if (res_str_len >= BUFSIZE) res_str_len = BUFSIZE-1 

dopo il vprintf se si vuole realmente troncare il messaggio

Problemi correlati