views:

387

answers:

5

Lets say I have a function:

void split_path_file(char** p, char** f, char *pf)
{
    //malloc and set *p to file path, malloc and set *f to file name
    //pf is the pointer to the full file and path "C:\sponge\bob\square.pants"
    // edit: leave pf in its origional state
}

Whats the best way to accomplish this?

+2  A: 
void split_path_file(char** p, char** f, char *pf) {
    char *slash = pf, *next;
    while ((next = strpbrk(slash + 1, "\\/"))) slash = next;
    if (pf != slash) slash++;
    *p = strndup(pf, slash - pf);
    *f = strdup(slash);
}

(If pf == slash, then there is no directory component.)

ephemient
isn't !! the same as just leaving "!!" out?
No, because `strchr` returns a `char *` but we want 0/1.
ephemient
oh, cool trick...
It's a common C idiom in some circles, but I removed it for clarity.
ephemient
I don't like using `strdup()` because it's non-standard, but I _really_ don't like using `strndup()` because my compiler doesn't have it. :P
Chris Lutz
+1  A: 

Go backwards through the string until you reach the first '\\' then set *f to everything after it and *p to everything before and the '\\'.

Dan
You need to make at least one forward pass in order to find the terminating `'\0'` of a C string -- there's no reason not to scan for slashes while on the way forwards instead of starting a second pass in reverse.
ephemient
+1  A: 

The simplest way seems to be to start from the end and work towards the beginning, looking for the first delimiter character. You then have two cases: either you found one or you didn't. Something like this should do it for you:

#include <stdlib.h>
#include <string.h>

void split_path_file(char **p, char **f, char *pf) {
  char *newcopy = malloc(strlen(pf) + 1);
  strcpy(newcopy, pf);

  for (z=newcopy+strlen(newcopy); z>newcopy; z--) {
    if (*z == '/' || *z == '\\')
      break;
  }

  if (z > newcopy) {
    *p = newcopy;
    *z = '\0';
    *f = z+1;
  } else {
    *f = newcopy;
    *p = NULL;
  }
}

Update: @ephemient's comment below points out the above approach doesn't leave *p and *f suitable for calling free(). If this is important, something a little more complicated like this will be needed:

#include <stdlib.h>
#include <string.h>

void split_path_file(char **p, char **f, char *pf) {

  /* Find last delimiter. */
  char *z;
  for (z=pf+strlen(pf); z>=pf; z--) {
    if (*z == '/' || *z == '\\')
      break;
  }

  if (z >= pf) {
    /* There is a delimiter: construct separate
       path and filename fragments. */
    printf("--> %i\n", z-pf);
    *p = malloc(z-pf+1);
    strncpy(*p, pf, z-pf);
    (*p)[z-pf] = '\0';
    *f = malloc(strlen(z));
    strcpy(*f, z+1);
  } else {
    /* There is no delimiter: the entire
       string must be a filename. */
    *p = NULL;
    *f = malloc(strlen(pf)+1);
    strcpy(*f, pf);
  }
}
Tim
This probably violates OP's request that `*p` and `*f` be `malloc`ed: horrible failure ensues if the caller tries to `free(*f)` which is an offset into another `malloc`ed area.
ephemient
Absolutely right. Good call! Now fixed in answer. Thanks.
Tim
A: 

I came up with the following, of course this assumes that pf is malloced.

void split_path_file(char** p, char **f, char *pf)
{
    char *posp = strrchr(pf, '\\');
    *posp = '\0';
    *p = strdup(pf);
    *f = strdup(posp+1);
    *posp = '\\';
}

Not sure if this is a better approach than above answers.

This violates OP's request that `pf` not be modified, and segfaults if there is a filename without directory. Also, note that forward slash is still a valid path separation character on Windows.
ephemient
Nevermind on the first point: you restore the backslash. However, the other two may still be problematic.
ephemient
A: 
int
stripfilenameandpath (char *path, char *onlypath, char *onlyfilename)
{
/*
documentacao

path = path com path e arquivo
onlypath = somente o path
onlyfilename = somente o arquivo sem o path

*/
    int ret;
    int i;
    int p;
    char temp[255];

    char *fixo;
#ifdef WIN32
    const int separator = '\\';
#else
    const int separator = '/';
#endif

    fixo = path;

    if (path == NULL)
      {

       if (onlypath != NULL)
         {
          memset (onlypath, 0, 1);
         }

       if (onlyfilename != NULL)
         {
          memset (onlyfilename, 0, 1);
         }

       return 1;
      }

    ret = strlen (path);

    if (!ret)
      {

       if (onlypath != NULL)
         {
          memset (onlypath, 0, 1);
         }

       if (onlyfilename != NULL)
         {
          memset (onlyfilename, 0, 1);
         }

       return 0;
      }

    for (i = 0; i  -1; i--)
      {

       if (temp[i] == separator)
         {
          temp[i + 1] = 0;
          break;
         }
       p++;
      }

    p = ret - p;

    fixo += p + 1;

    if (onlypath != NULL)
      {
       strcpy (onlypath, temp);
      }

    if (onlyfilename != NULL)
      {
       strcpy (onlyfilename, fixo);
      }

    return 0;
}

Arabcoder