#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2013
    Posts
    3
    Rep Power
    0

    concatenate strings


    Hi,

    I use a mac os to play with c.
    I want to concatenate 2 strings, a command and a filename without using array. I don't want to use array, filename can take long chars as command path, it is impossible for me to know the max chars for them. So here it is :

    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main(void){
    
    char *command="/usr/local/mycommand ";
    char *file;
    printf("filename: ");
    scanf("%s",file);
    printf("Command: %s\n",command);
    printf("So, our command become: %s\n",strcat(command,file));
    }
    When i compile and execute the binary, i get error "Bus error: 10"
    Code:
    milo$ ./a.out 
    filename: test.md
    Command: /usr/local/mycommand 
    Bus error: 10
    Any idea to achieve that ?
    Thank you very much.

    Milo
    Last edited by milo974; December 27th, 2013 at 02:50 PM. Reason: more precisions
  2. #2
  3. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2013
    Posts
    3
    Rep Power
    0

    using arrays


    So, using arrays :

    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main(void){
    
    char command[256]="/usr/local/mycommand ";
    char file[128];
    printf("file:");
    scanf("%s",file);
    printf("Command : %s\n",command);
    printf("So, our command become : %s\n",strcat(command,file));
    }
    Now it works :
    Code:
    milo$ ./a.out 
    file:test.md
    Command : /usr/local/mycommand
    So, our command become : /usr/local/mycommand test.md
    Any advices ? Perhaps a better way to reach what i want??

    Thank you very much !
  4. #3
  5. Banned ;)
    Devshed Supreme Being (6500+ posts)

    Join Date
    Nov 2001
    Location
    Woodland Hills, Los Angeles County, California, USA
    Posts
    9,643
    Rep Power
    4248
    It is a good thing you're concerned about buffer overflows. There are a few ways to handle this problem:

    1. There is a limit for filename lengths, which is defined in sys/param.h and is called MAXPATHLEN.
    Code:
    #include <sys/param.h>
    ...
    char file[MAXPATHLEN];
    If you use this approach, you'll also need to use "safe string" functions to ensure you don't overflow your buffer. For instance:
    Code:
    #include <stdio.h>
    #include <string.h>
    #include <sys/param.h>
    
    int main(void) {
        char command[4096];
        char file[MAXPATHLEN];
        int  len;
    
        printf("File: ");
        fgets(file, sizeof(file), stdin);
        /* Get rid of trailing \n if needed */
        len = strlen(file);
        if (file[len - 1] == '\n') {
            file[len - 1] = '\0';
        }
    
        if ( (len = snprintf(command, sizeof(command), "/usr/local/command %s", file)) < sizeof(command)) {
            printf("So our command become: %s\n", command);
        } 
        else {
            printf("Our buffer is too small\n");
        }
    
        return 0;
    }
    Notice, I'm using fgets() instead of scanf(). You can use scanf() if you like, but use a format like "%.100s" instead of "%s" so that you can control how many characters are read in by scanf(), otherwise you're back to buffer overflow issues. However, this requires you to know the length of your buffer to build your format string appropriately. With fgets(), the second argument is the size of the buffer determined by the compiler at compile time, and I don't really need to know what the value of MAXPATHLEN is. Also, fgets() will read spaces in by default, whereas scanf() treats them as end of input, unless you use the appropriate format string. Too complicated for me and I prefer fgets() anyway.

    Also, note the use of snprintf() instead of strcat(). Again, snprintf() allows you to pass in the length of the string buffer, so it does not overflow the buffer. The return value of snprintf() is special[*]. While it does not write more than the # of bytes you passed in as the buffer size, if it truncates the data, it returns the number of characters which would have been written if your buffer was large enough (so if it can't write to your array fully, it tells you how big your array should be to write it out fully. That is very useful so you can reallocate your array if you are using dynamic memory allocation).

    The above code properly checks the array sizes and ensures that they are not being overflowed, so it will work correctly and will gracefully fail if the array sizes are not big enough.

    However, since the array sizes are fixed, the program fails gracefully if the arrays are not big enough. Also, if you enter a large number of characters, fgets() will only read in enough characters to fill the array, but will leave the rest behind for the next input operation. There are other ways to handle it to resize the arrays on the fly, to adjust in case the arrays are not big enough. Read on below for details.
    [*] Note: what I said about snprintf() is applicable to POSIX standards. However, Microsoft version of snprintf(), which is called _snprintf(), is somewhat non-standard and behaves a bit differently. For one, it returns a negative number if the string is not large enough and it also doesn't null terminate correctly, which snprintf() does.

    2. The second option is to use dynamic memory allocation using malloc(), realloc() and free() as needed, so as to set the size of command array appropriately. You can probably get to this when you start learning about pointers. I won't put that code here right now, but we can discuss it later.

    3. Use C++ instead of C, because C++ has already solved this by implementing string classes that automatically resize their array for you if needed. This is how the code is implemented in C++
    Code:
    #include <iostream>
    #include <string>
    
    int main(void) {
        std::string command = "/usr/local/command ";
        std::string file;
    
        std::cout << "File: ";
        std::getline(std::cin, file);
    
        std::cout << "Command: " << command << '\n';
        // concatenate the file at the end
        command += file;
        std::cout << "So, our command become : " << command << '\n';
    
        return 0;
    }
    See how clean the code looks in C++. Not only that, note that I've not specified the size of a single array anywhere, but the code is fully safe from buffer overflows. This is because C++ string classes automatically resize themselves as needed, but the fine details of using malloc()/realloc()/free() etc. are all hidden from you internally within the classes, leaving your code looking much cleaner. Also, you don't need to be concerned about details like MAXPATHLEN because the file variable will automatically resize itself to read your input, no matter what the size of the input is.
    Up the Irons
    What Would Jimi Do? Smash amps. Burn guitar. Take the groupies home.
    "Death Before Dishonour, my Friends!!" - Bruce D ickinson, Iron Maiden Aug 20, 2005 @ OzzFest
    Down with Sharon Osbourne

    "I wouldn't hire a butcher to fix my car. I also wouldn't hire a marketing firm to build my website." - Nilpo
  6. #4
  7. Contributing User

    Join Date
    Aug 2003
    Location
    UK
    Posts
    5,117
    Rep Power
    1803
    Write it in C++ and use a std::string. Much safer.
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2013
    Posts
    3
    Rep Power
    0

    Thanks a lot!


    Hi,

    Thank you very much for your explanations, i appreciate.
    You could write a book about C language, like a best practice ;)

    I just don't understand why file[len-1]='\0'
    if it detects newline what does it do ?

    Originally Posted by Scorpions4ever
    len = strlen(file);
    if (file[len - 1] == '\n') {
    file[len - 1] = '\0';
    }
    Can you explain me also how to use the code using malloc() and free() ? Then I will choose one way.

    (And it's true C++ is shorter than using C.
    But i started with C...since just 1 week. Perhaps i need to change. ;))

IMN logo majestic logo threadwatch logo seochat tools logo