views:

269

answers:

7

I am creating a function that will split a full unix filename(like /home/earlz/test.bin) into its individual parts. I have got a function, and it works for the first two parts perfect, but after that it produces erroneous output...

strlcpy_char will copy a string using term as the terminator, as well as 0. If it is terminated with term, then term will be the last character of the string, then null.

returns trg string length...

int strlcpy_char(char *trg,const char *src,int max,char term){
    int i;
    if(max==0){return 0;}
    for(i=0;i<max-1;i++){
            if(*src==0){
                    *trg=0;
                    return i;
            }
            if(*src==term){
                    *trg=term;
                    trg++;
                    *trg=0; //null terminate
                    return i+1;
            }
            *trg=*src;
            src++;
            trg++;
    }
    *trg=0;
    return max; 
}

.

int get_path_part(char *file,int n,char *buf){
    int i;
    int current_i=0;
    //file is assumed to start with '/'so it skips the first character.
    for(i=0;i<=n;i++){
            current_i++;
            current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
            if(current_i<=1){ //zero length string..
                    kputs("!"); //just a debug message. This never happens with the example
                    return -1; //not enough parts to the path
            }
    }
    if(buf[current_i-1]=='/'){
            return 1; //is not the last part
    }else{
            return 0; //is the last part(the file part)
    }
}

I use this code to test it:

        kputs("test path: ");
  kgets(cmd);
  kputs("\n");
  char *tmp=malloc(256);
  int i=0;
  get_path_part(cmd,i,tmp);
  kputs(tmp);
  kputs("\n");
  i=1;
  get_path_part(cmd,i,tmp);
  kputs(tmp);
  kputs("\n");
  i=2;
  get_path_part(cmd,i,tmp);
  kputs(tmp);
  kputs("\n");

When I try something like "/home/test.bin" it works right outputting

/home
/test.bin

But when I try "/home/earlz/test.bin" I get

/home
/earlz
/arlz

Anyone see the problem in my code, as I've been looking but I just can't see any problem.

Also, before you say "but there is a library for that" I am doing this in an operating system kernel, so I barely have a standard library. I only have parts of string.h and really that's about it for standard.

A: 

Does your code have to be re-entrant? If not use strtok, it is in strings.h

STRTOK(P)

NAME
       strtok, strtok_r - split string into tokens

SYNOPSIS
       #include <string.h>

       char *strtok(char *restrict s1, const char *restrict s2);

       char *strtok_r(char *restrict s, const char *restrict sep,
              char **restrict lasts);

Sorry for not commenting on your code though :)

hhafez
I don't think I feel like implementing that myself(as I would have to, or port it from somewhere else to compile on my kernel)Also, my code must be re-entrant.. it's going to be part of the filesystem driver used by all sorts of processes at any unknown time...
Earlz
A: 

If you are using Glib, g_strsplit is very nice and easy to use.

Josh K
operating system kernel. No libraries I don't implement myself.
Earlz
+4  A: 

You overwrite current_i instead of adding it up as you walk through the path.

So

current_i++;
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');

should really be

current_i += strlcpy_char(buf,&file[current_i+1],MAX_PATH_PART_SIZE,'/');
Tobiesque
+2  A: 

I think you need to track your current_i for i>1 since the max value returned from the strlcpy has no idea of where you are in the overall file string. does it make sense?

current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
Ryan Oberoi
I think Tobiesque hit the same point while I was typing the answer. He is correct. +1 to him.
Ryan Oberoi
+2  A: 

Don't you need to do something like

tocurrent_i += strlcpy_char...

instead of

tocurrent_i = strlcpy_char...
Igor Krivokon
Ah. Too late. +1 to Tobuesque :)
Igor Krivokon
A: 

This is how I'd do it

char ** split_into_parts(char *path) {
  char ** parts = malloc(sizeof(char *) * 100);
  int i = 0;
  int j = 0;

  if (*path == '/') {
    path++;
  }

  parts[0] = 0;
  while (*path) {
    if (*path == '/') {
      parts[i][j] = 0;
      i++;
      parts[i] = 0;
      j = 0;
    } else {
      if (parts[i] == 0) {
        parts[i] = malloc(sizeof(char) * 100);
      }
      parts[i][j] = *path;
      j++;
    }
    path++;
  }
  parts[i+1] = 0;

  return parts;
}
swampsjohn
A: 

Try something like the code I have below.

If you need implementations of standard C functions (like strchr()) try koders.com or just google for strchr.c.

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

const char *NextToken(const char *pStart, char chSep, char *pToken, size_t nTokMax)
{
    const char *pEnd;
    size_t nLength;

    /* set output to empty */
    *pToken=0;

    /* make sure input is OK */
    if (!pStart || *pStart!=chSep)
        return NULL;

    /* find end of token */
    pEnd = strchr(pStart+1, chSep);
    if (pEnd)
        nLength = pEnd - pStart;
    else
        nLength = strlen(pStart);

    if (nLength >= nTokMax) /* too big */
        return NULL;

    strncpy(pToken, pStart, nLength);
    pToken[nLength] = 0;

    return pEnd;
}

int main()
{
    #define BUFFSIZE 256
    char cmd[BUFFSIZE];
    char tmp[BUFFSIZE];
    const char *pStart=cmd;
    int i=0;

    puts("test path: ");
    fgets(cmd, BUFFSIZE, stdin);
    puts("");

    do {
        pStart = NextToken(pStart, '/', tmp, BUFFSIZE);
        if (tmp[0])
            puts(tmp);
    } while (pStart);
    return 0;
}
Michael J