Разделение сообщения на две строки, если его длина больше заданной в C

#c

#c

Вопрос:

Итак, я пытался повторно реализовать старую технику из sudo для разделения большого сообщения на две строки, исходный код взят из sudo logging.c:

 static void
do_syslog(pri, msg)
    int pri;
    char *msg;
{
    int count;
    char *p;
    char *tmp;
    char save;

    /*
     * Log the full line, breaking into multiple syslog(3) calls if necessary
     */
    for (p = msg, count = 0; count < strlen(msg) / MAXSYSLOGLEN   1; count  ) {
    if (strlen(p) > MAXSYSLOGLEN) {
        /*
         * Break up the line into what will fit on one syslog(3) line
         * Try to break on a word boundary if possible.
         */
        for (tmp = p   MAXSYSLOGLEN; tmp > p amp;amp; *tmp != ' '; tmp--)
        ;
        if (tmp <= p)
        tmp = p   MAXSYSLOGLEN;

        /* NULL terminate line, but save the char to restore later */
        save = *tmp;
        *tmp = '';

        if (count == 0)
        SYSLOG(pri, "%8.8s : %s", user_name, p);
        else
        SYSLOG(pri, "%8.8s : (command continued) %s", user_name, p);

        *tmp = save;            /* restore saved character */

        /* Eliminate leading whitespace */
        for (p = tmp; *p != ' '; p  )
        ;
    } else {
        if (count == 0)
        SYSLOG(pri, "%8.8s : %s", user_name, p);
        else
        SYSLOG(pri, "%8.8s : (command continued) %s", user_name, p);
    }
    }
}
  

Однако, когда попробуйте тот же код, просто заменив syslog на printf , и MAXSYSLOGLEN на 20 вместо 960

 #include <stdio.h>
#include <string.h>

#define MAXSYSLOGLEN 20

void remake(char *msg){

    int count;
    char *p; 
    char *tmp;
    char save;

    
    for (p = msg, count=0; count < strlen(msg)/MAXSYSLOGLEN   1; count  ){

        if( strlen(p) > MAXSYSLOGLEN) { 

            printf("[*] The message is greater than MAXSYSLOGLENn");

            for (tmp = p   MAXSYSLOGLEN; tmp > p amp;amp; *tmp != ' '; tmp-- );

        if( tmp <= p )
            tmp = p   MAXSYSLOGLEN;

        save = *tmp;
        *tmp = '';

        if (count == 0)
            printf("%sn",p);
        else
            printf("(command continued) %sn",p );

            *tmp = save;

            for (p = tmp; *p != ' '; p  );
        } else { 
            if (count == 0) 
                printf("%sn",p);
            else 
                printf("(command continued) %sn",p );  
        }
    }
}

int main(int argc, char* argv[])
{
    remake(argv[1]);
}
  

Теперь к результату:

 gcc test.c -o test
./test "this is a command ....dklckdsmcsklsmdcdd"
  
 [*] The message is greater than MAXSYSLOGLEN
this is a command
[*] The message is greater than MAXSYSLOGLEN
(command continued)  ....dklckdsmcsklsmd
(command continued)  ERROR;JS LOG
  

последняя строка странная, откуда вообще взялась эта строка? ERROR;JS LOG это что-то с памятью?

Заранее спасибо.

Ответ №1:

TLDR: Выбросьте этот ужасный исходный код и напишите свой собственный код из scrath.

Описание

Ошибка возникает из исходного кода — здесь:

     /* Eliminate leading whitespace */
    for (p = tmp; *p != ' '; p  )
    ;
  

В случае, если в оставшейся части строки нет пробела, p оно будет увеличиваться снова и снова и в конечном итоге будет указывать за пределы строки. Как только p он выходит за пределы строки, и вы разыменовываете его (т. Е. *p ), У вас неопределенное поведение, т. Е. Ваша программа может делать что угодно, она может печатать что угодно, аварийно завершать работу, выключать ваш компьютер… что угодно.

Теперь посмотрите на комментарий:

     /* Eliminate leading whitespace */
  

Хммм… но это не то, что делает код! Оно увеличивается p до тех пор, пока нет пробела. Таким образом, код скорее «устраняет все, что не является пробелом», и он не останавливается, когда строка заканчивается. Ужасно.

Вероятно, они намеревались:

     /* Eliminate leading whitespace */
    for (p = tmp; *p == ' '; p  )
    ;
  

Это исправит многие случаи ошибок.
Но даже это приведет к сбою для некоторого ввода… Попробуйте с:

 "aaa                                               aa"
           ^^^^^^^^
           Lots of spaces, e.g. 100 spaces
  

Пробелы будут устранены, но цикл, т.е.

 for (p = msg, count = 0; count < strlen(msg) / MAXSYSLOGLEN   1; count  ) {
  

не отрегулировано, поэтому будет напечатано несколько «непреднамеренных» строк.

В качестве второстепенного: обратите внимание, что в комментарии указано «пробел», но код проверяет только наличие «обычных» пробелов. В коде должна была использоваться isspace функция.

Единственное, что нужно сделать, это выбросить этот ужасный исходный код и написать свой собственный код из scrath.