2010-05-14 3 views
6

У меня есть функция irc_sendline, которую можно назвать как printf можетC переменная аргумент рефакторинга

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

Он отлично работает, но я не доволен своей реализации:

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; 
} 

Вы видите, Я использую 2 «временных» буфера здесь, потому что сначала мне нужно скопировать исходное сообщение из аргументов функции в временный буфер, чтобы добавить к нему «\ r \ n», а затем скопировать этот временный буфер в еще Buffe r для фактического форматирования с аргументами, предоставленными из вызова функции, и только THEN Я могу отправить материал на своем пути.

Как я могу сделать это чище, лучше?


Спасибо за все входные здесь, я думал, что моя единственная проблема была неразбериха, но это было на самом деле тиканье бомба замедленного действия! Моя новая функция выглядит так:

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; 
} 

Если бы я мог, я бы принял несколько ответов здесь, но meh.

+0

Ваш код нарушается из-за неправильного использования 'strncpy' (см мой ответ). – AnT

+0

Ваше использование 'strncat' также нарушено. 'strncat' не делает то, что вы думаете. – AnT

ответ

5

Первое использование vsnprintf для форматирования данных, а затем добавить к нему результат «\ r \ n». Кроме того, просто используйте второй вызов send для отправки «\ r \ n».

1

Поскольку \r\n собирается закончить в конце отформатированной строки, то почему бы не скопировать впоследствии:

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

Обратите внимание, что я также фиксированные аргументы strncat.

0

Если вы не хотите использовать msg для strcat (небезопасно и злобно, потому что вы не знаете размер строки), я думаю, вам придется жить с двумя буферами.

Как в стороне, я бы рассмотрел strncpy (..., BUFSIZE-2), так что \ r \ n всегда делает это на ваших сообщениях, и поэтому строки всегда обертываются.

+0

+1 для 'BUFSIZE-2'. –

+0

Спасибо за указание вещи BUFSIZE! Я вообще этого не заметил, мог бы прорвать меня. – LukeN

+0

Вы все еще забываете, что 'stncpy' не добавляет нулевой ограничитель к строке, если буфер слишком короткий. 'BUFSIZE-2' не исправит это. Ваш код будет разбиваться каждый раз, когда конец буфера попадет в строку формата. – AnT

3

Во-первых, если вас интересует «очиститель», остановите использование strncpy. Несмотря на вводящее в заблуждение имя (и вопреки распространенному мнению), это не функция копирования строк с ограниченной длиной. Можно с уверенностью сказать, что strncpy - это функция, практически не использующая сегодня. Когда вы видите strncpy, используемый в коде, «очиститель» сразу же становится невозможен (кроме того, из-за исчезнувшего узкого набора корпусов strncpy на самом деле предназначался для использования).

В самом деле, ваш код разбивается именно потому, что вы использовали strncpy: если длина строки формата больше, чем длина буфера, strncpyне добавляет завершающий нулевой символ к результату, а это означает, что последующий strncat вызов вызовет крах (в лучшем случае).

Функция ограниченной длины копирования не существует в стандартной библиотеке, но она часто предоставляется реализацией под именем strlcpy. Если реализация, которую вы используете, не обеспечивает одно - напишите ее самостоятельно и используйте.

Ваш код также поврежден из-за неправильного использования strncat: strncat не принимает длину полного буфера в качестве последнего аргумента. Вместо этого strncat ожидает, что длина оставшегося остатка буфера. Итак, если вы хотите использовать strncat, вам нужно сначала рассчитать, сколько свободного места осталось в конце буфера после предыдущего копирования. Опять же, хотя strncat более полезен, чем strncpy, вам может быть лучше использовать нестандартную (но часто обеспечиваемую реализацией) функцию strlcat, которая фактически работает так, как вы думали strncat.

Во-вторых, вместо того, чтобы заранее добавить часть \r\n, почему бы вам не сделать это после? Используйте тот факт, что vsnprintf возвращает количество символов, записанных в выходной буфер, и просто добавляет \r, \n и \0 символов в конце после завершения vsnprintf. Для этого вам не нужно использовать strncat. Просто напишите символы в буфер непосредственно, убедившись, конечно, что вы не пересекаете границу.

+0

Спасибо за предупреждение (3 из них на самом деле :), я в настоящее время ищут эту проблему, и мне кажется, что я должен написать свою собственную strlcpy, потому что Linux ее не предоставляет. Что касается strncpy - так оно сломано, потому что оно не будет append \ NUL, если целевая строка длиннее/до тех пор, пока строка назначения? – LukeN

+0

Во-первых, да, он не добавляет '\ 0', если строка длиннее. Во-вторых, он заполняет всю оставшуюся часть буфера нулями, если строка короче, что совершенно не нужно. Еще раз, 'strncpy' не является функцией, которая была создана для строк с нулевым завершением. Это функция, созданная для совершенно другой цели - для поддержки так называемых строк фиксированной ширины в некоторой старой версии файловой системы Unix. Вы можете узнать больше об этом здесь: http://stackoverflow.com/questions/1453876/why-does-strncpy-not-null-terminate – AnT

+0

Спасибо, что прояснил это, теперь я помню, почему я явно отключил строки в строке некоторые другие программы, которые использовали strncpy. – LukeN

0

Одна из основных проблем с вашим кодом - vsnprintf возвращает количество символов, которое будет помещено в буфер, если оно было бесконечно большим, что может быть больше BUFSIZE, если буфер недостаточно велик. Поэтому, если у вас есть сообщение, которое переполняется, вы в конечном итоге отправляете случайный мусор после окончания вашего буфера. Вам нужно добавить строку

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

после vprintf если вы хотите на самом деле усечение сообщение

Смежные вопросы