Thread: Filename only wanted

  1. #1
    Registered User
    Join Date
    Aug 2003
    Posts
    93

    Filename only wanted

    Hi,

    is there a standard function for removing paths from filenames ?

    printf("filename [%s]\n", argv[1]);
    /root/mydir/myfile.dat

    I only want the filename myfile.dat, so do I have to work it out manualy or can I call printf("filename [%s]\n", filename(argv[1]));

    tia,

  2. #2
    End Of Line Hammer's Avatar
    Join Date
    Apr 2002
    Posts
    6,231
    No standard function is available, just write your own.
    When all else fails, read the instructions.
    If you're posting code, use code tags: [code] /* insert code here */ [/code]

  3. #3
    root
    Join Date
    Sep 2003
    Posts
    232
    Here's a quickie example.
    Code:
    char *fname(const char *path, char *buffer, size_t limit) {
      char *start;
    
      if ((start = strrchr(path, '/')) == NULL)
        return NULL;
      strncpy(buffer, start+1, limit);
    
      return buffer;
    }
    Pretty straightforward really. Even for a C noob like me.
    The information given in this message is known to work on FreeBSD 4.8 STABLE.
    *The above statement is false if I was too lazy to test it.*
    Please take note that I am not a technical writer, nor do I care to become one.
    If someone finds a mistake, gleaming error or typo, do me a favor...bite me.
    Don't assume that I'm ever entirely serious or entirely joking.

  4. #4
    Registered User
    Join Date
    Aug 2003
    Posts
    93
    thanks Hammer I thought so,

    sorry twm, I cannot get your function to work

    Code:
    int main(int argc, char *argv[])
    {
    <snip>
    char * filename_only;
    char * buf;
    
       filename_only = fname(argv[1], buf, 9);
       <snip>
    
       return 0;
    }
    
    <snip>
    char *fname(const char *path, char *buffer, size_t limit)
    {
      char *start;
    
      if ((start = strrchr(path, '/')) == NULL)
        return NULL;
      strncpy(buffer, start+1, limit);
    
      return buffer;
    }

    /* OUTPUT */
    $ make
    gcc -DDEBUG -c eq_recon_mtn_cnx_format_eq.c -I../lib -I../common
    eq_recon_mtn_cnx_format_eq.c: In function `main':
    eq_recon_mtn_cnx_format_eq.c:54: warning: assignment makes pointer from integer without a cast
    eq_recon_mtn_cnx_format_eq.c: At top level:
    eq_recon_mtn_cnx_format_eq.c:261: warning: type mismatch with previous implicit declaration
    eq_recon_mtn_cnx_format_eq.c:54: warning: previous implicit declaration of `fname'
    eq_recon_mtn_cnx_format_eq.c:261: warning: `fname' was previously implicitly declared to return `int'
    gcc -DDEBUG -o eq_recon_mtn_cnx_format_eq eq_recon_mtn_cnx_format_eq.o \
    ../common/db_connect.o ../common/db_customer_control.o ../common/date_func.o ../common/error_func.o ../common/db_get_seq_no.o ../common/file_seq_no.o ../common/input_file_name.o \
    -L/product/oracle/oracle815/precomp/lib -L/product/oracle/oracle815/lib \
    `cat /product/oracle/oracle815/lib/sysliblist` \
    -lclntsh \
    -R/product/oracle/oracle815/lib -laio -lm -lthread
    $

  5. #5
    root
    Join Date
    Sep 2003
    Posts
    232
    You have a couple of problems with how you use it:

    >char * buf;
    First and foremost, this will cause a segfault because you don't have any memory allocated to buf and fname assumes you do.

    >filename_only = fname(argv[1], buf, 9);
    You shouldn't use a function before it's defined, try using a prototype:
    Code:
    #include <stdio.h>
    #include <string.h>
    
    char *fname(const char *, char *, size_t);
    
    int main(int argc, char *argv[])
    {
       char * filename_only;
       char buf[80];
    
       filename_only = fname(argv[1], buf, 9);
    
       return 0;
    }
    
    char *fname(const char *path, char *buffer, size_t limit)
    {
      char *start;
    
      if ((start = strrchr(path, '/')) == NULL)
        return NULL;
      strncpy(buffer, start+1, limit);
    
      return buffer;
    }
    The information given in this message is known to work on FreeBSD 4.8 STABLE.
    *The above statement is false if I was too lazy to test it.*
    Please take note that I am not a technical writer, nor do I care to become one.
    If someone finds a mistake, gleaming error or typo, do me a favor...bite me.
    Don't assume that I'm ever entirely serious or entirely joking.

  6. #6
    Registered User
    Join Date
    Aug 2003
    Posts
    93
    sorry twm, I put the declaration in the wrong header file and the char * buf was my schoolboy error of the day


    thanks

  7. #7
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    >strncpy(buffer, start+1, limit);

    I happened upon this earlier today.


    Also of note, 'path' loses its constness via the call to strchr -- you may want to declare 'start' as a const char *.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  8. #8
    root
    Join Date
    Sep 2003
    Posts
    232
    Will do. So it should be more like this:
    Code:
    char *fname(const char *path, char *buffer, size_t limit) {
      const char *start;
    
      if ((start = strrchr(path, '/')) == NULL)
        return NULL;
      buffer[0] = '\0'; /* Make sure the buffer is empty */
      strncat(buffer, start+1, limit);
    
      return buffer;
    }
    Thanks.
    The information given in this message is known to work on FreeBSD 4.8 STABLE.
    *The above statement is false if I was too lazy to test it.*
    Please take note that I am not a technical writer, nor do I care to become one.
    If someone finds a mistake, gleaming error or typo, do me a favor...bite me.
    Don't assume that I'm ever entirely serious or entirely joking.

  9. #9
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    buffer[0] = '\0'; /* Make sure the buffer is empty */
    On a side note, this does not make sure the buffer is empty. It simply changes the first character to a zero. All the others remain unchanged. Use memset or something similar if you really need to be sure. Or just make sure your modified string ends in a null when you stick it in the buffer.

    Quzah.
    Hope is the first step on the road to disappointment.

  10. #10
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    I think it was meant to read, "make sure the buffer is an empty string". The concatenation with an empty string would be the equivalent of the strncpy call, except that strncat would create a valid (null-terminated) string for a given correct 'limit' whereas strncpy might not.
    Last edited by Dave_Sinkula; 10-14-2003 at 08:55 PM.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  11. #11
    Registered User
    Join Date
    Aug 2003
    Posts
    93
    thanks Dave,

    I have added it too

    Code:
    #define BUF_SIZE         256
    #define SITA_FILE_LENGTH 46
    
    char * get_filename(const char * path, char * buffer, size_t limit)
    {
    const char * start;
    
       if((start = strrchr(path, '/')) == NULL)
          return NULL;
       strncpy(buffer, start + 1, limit);
    
       return buffer;
    }
    
    void eq_recon_process(char * input_file_name, FILE ** fp)
    {
    char * input_filename;
    char temporary[BUF_SIZE];
    
       input_filename = get_filename(input_file_name, temporary, SITA_FILE_LENGTH);
       input_filename[SITA_FILE_LENGTH] = '\0';
    
       <snip>

  12. #12
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Originally posted by darfader
    thanks Dave,

    I have added it too

    Code:
    #define SITA_FILE_LENGTH 46
    
    input_filename[SITA_FILE_LENGTH] = '\0';
    Make sure that's what you mean it to be. Usually if you have a defined size of an array, the last size you should be accessing is "SIZE -1".

    Quzah.
    Hope is the first step on the road to disappointment.

  13. #13
    Registered User
    Join Date
    Aug 2003
    Posts
    93
    the filename is guarranteed to be 46 characters long, [otherwise the program would have stopped before now] so the null is being place on the end,

    according to a printf statement I get the following results

    input_filename[SITA_FILE_LENGTH - 1] = '\0'; = [R3AP200307_CIRPTESTER_00001_20030701164512.SN]
    input_filename[SITA_FILE_LENGTH] = '\0'; = [R3AP200307_CIRPTESTER_00001_20030701164512.SND]
    input_filename[SITA_FILE_LENGTH + 1] = '\0'; = [R3AP200307_CIRPTESTER_00001_20030701164512.SND~]

    the tilde represents a strange character which I cannot reproduce,


    thanks quzah

  14. #14
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    >the filename is guarranteed to be 46 characters long, [otherwise the program would have stopped before now] so the null is being place on the end

    An issue being addressed is as follows.
    Code:
    #include <stdio.h>
    #include <string.h>
    
    #define SITA_FILE_LENGTH 46
    
    int main(void)
    {
       const char pathname[] = "R3AP200307_CIRPTESTER_00001_20030701164512.SND";
       char filename [ SITA_FILE_LENGTH ];
       strncpy(filename, pathname, SITA_FILE_LENGTH);
       filename [ SITA_FILE_LENGTH ] = '\0'; /* access out-of-bounds */
       printf("filename = \"%s\"\n", filename);
       return 0;
    }
    If you declare
    Code:
    char filename[46]; /* index from 0 - 45 */
    then trying to use
    Code:
    filename[46] = '\0';
    is one beyond the end of the array, and behavior is undefined. So at best, you would need to do this.
    Code:
    filename [ SITA_FILE_LENGTH - 1 ] = '\0'; /* null terminate last character */
    And if your filename needs to hold 46 significant characters, the buffer size must be 47 to make room for the terminating null.


    Now, the strncpy/strncat issue relates to the following.
    Code:
    #include <stdio.h>
    #include <string.h>
    
    void show(const char *s, size_t size)
    {
       size_t i;
       for(i = 0; i < size; ++i)
       {
          printf("s[%lu] = %d\n", (long unsigned)i, s[i]);
       }
    }
    
    void foo(char *s, const char *t, size_t size)
    {
       puts("strncpy");
       strncpy(s, t, size - 1);
       show(s, size);
    }
    
    void bar(char *s, const char *t, size_t size)
    {
       puts("strncat");
       s[0] = '\0';
       strncat(s, t, size - 1);
       show(s, size);
    }
    
    int main(void)
    {
       char a[] = {1,2,3,4,5}, b[] = "hello world";
       foo(a, b, sizeof a);
       bar(a, b, sizeof a);
       return 0;
    }
    
    /* my output
    strncpy
    s[0] = 104
    s[1] = 101
    s[2] = 108
    s[3] = 108
    s[4] = 5
    strncat
    s[0] = 104
    s[1] = 101
    s[2] = 108
    s[3] = 108
    s[4] = 0
    */
    Note that the strncpy version did not null terminate the string. You attempt to correct this by adding the null after the call to strncpy, but you may have attempted to change the wrong character.

    A final point about using strncpy is that if you have an array of 256 chars and you use strncpy to copy "hello" into the array (passing the maximum size of 256), all of the 256 elements of the array will be modified -- the remaining characters would be set to '\0'. This may be overkill, since a string needs only to end with a '\0'. Using strncat in this situation would only copy the five characters and add a null. (The "add a null" part is why one less than the buffer size should be passed.)


    Putting these couple of things together, here is an example with different sizes of strings.
    Code:
    #include <stdio.h>
    #include <string.h>
    
    #define BUF_SIZE         256
    #define SITA_FILE_LENGTH 46
    
    char * get_filename(const char * path, char * buffer, size_t limit)
    {
       const char * start = strrchr(path, '/');
       if ( start != NULL )
       {
          *buffer = '\0'; /* empty the string to concatenate to */
          strncat(buffer, start + 1/* after '/' */, limit - 1/* room for null*/);
          return buffer;
       }
       return NULL;
    }
    
    int main(void)
    {
       char filename [ SITA_FILE_LENGTH + 1 ]; /* 46 chars + null */
       const char pathname[] [ BUF_SIZE ] =
       {
          "/root/mydir/R3AP200307_CIRPTESTER_00001_20030701164512.SND",
          "/root/mydir/__R3AP200307_CIRPTESTER_00001_20030701164512.SND",
          "/root/mydir/R3AP200307_CIRPTESTER_00001_20030701164512.SND__",
          "/root/mydir/R3AP200307_CIRPTESTER_00001.SND",
       };
       size_t i;
       for(i = 0; i < sizeof pathname / sizeof *pathname; ++i)
       {
          printf("pathname = \"%s\"\n", pathname[i]);
          if ( get_filename(pathname[i], filename, sizeof filename) != NULL )
          {
             printf("filename = \"%s\"\n", filename);
          }
          putchar('\n');
       }
       return 0;
    }
    
    /* my output
    pathname = "/root/mydir/R3AP200307_CIRPTESTER_00001_20030701164512.SND"
    filename = "R3AP200307_CIRPTESTER_00001_20030701164512.SND"
    
    pathname = "/root/mydir/__R3AP200307_CIRPTESTER_00001_20030701164512.SND"
    filename = "__R3AP200307_CIRPTESTER_00001_20030701164512.S"
    
    pathname = "/root/mydir/R3AP200307_CIRPTESTER_00001_20030701164512.SND__"
    filename = "R3AP200307_CIRPTESTER_00001_20030701164512.SND"
    
    pathname = "/root/mydir/R3AP200307_CIRPTESTER_00001.SND"
    filename = "R3AP200307_CIRPTESTER_00001.SND"
    */
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  15. #15
    Registered User
    Join Date
    Aug 2003
    Posts
    93
    wow

    so it is perfectly obvious that the results I were getting was a fluke - not good

    if start is now a static, how does it know to traverse through the continuous cycle of slash delimiters, I always thought of static as keeping its value from one entrance in the function to the next ?

    Just out of interest, the last example is 31 character long, when the final null is placed, is it in filename[32] or filename[47] and if it is what is in bewteen ?


    thanks for your time Dave,


    [edit]
    after reading your post again, I realise that all the elements after the first null will also be null
    [/edit]

    Last edited by darfader; 10-16-2003 at 03:12 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 8
    Last Post: 04-11-2009, 01:49 AM
  2. Pass Filename to another function
    By awesmesk8er in forum C Programming
    Replies: 9
    Last Post: 10-24-2008, 01:43 PM
  3. adding line numbers and concatenating a filename
    By durrty in forum C Programming
    Replies: 25
    Last Post: 06-28-2008, 03:36 AM
  4. Time as filename
    By rkooij in forum C Programming
    Replies: 8
    Last Post: 03-02-2006, 09:17 AM
  5. Replies: 3
    Last Post: 01-25-2006, 10:30 PM