Add a “/” at the end of each string in an array of strings

Viewed 92

I have an array of double pointers where each string represents a path.

I would like to add a “/” at the end of each path in my array so that I can then execute them in my program, but I'm having trouble designing the loop.

Any ideas ?

Scheme

Here is my way of doing it for the moment but as you can see it is not at all optimal...

char    **ft_extract_and_delimit_path(char **envp)
{
    char    **array_of_paths;
    int     i;
    int     j;

    array_of_paths = NULL;
    i = 0;

    while(ft_strnstr(envp[i], "PATH", 4) == 0)
        i++;

    array_of_paths = ft_split(envp[i] + 5, ':');
    
    i = 0;
    j = 0;

    while (array_of_paths[i][j] != '\0')
        j++;
    array_of_paths[i][j] = ft_strjoin(array_of_paths[j], "/");

    i++;
    j = 0;

    while (array_of_paths[i][j] != '\0')
        j++;
    array_of_paths[i][j] = ft_strjoin(array_of_paths[j], "/");

    i++;
    j = 0;

    while (array_of_paths[i][j] != '\0')
        j++;
    array_of_paths[i][j] = ft_strjoin(array_of_paths[j], "/");

    i++;
    j = 0;

    ...
}
2 Answers

Assuming that your string has enough space to accommodate the new char:

char *addCharToString(char *str, char c)
{
    if(*str)
    {
        size_t len = strlen(str);
        str[len] = c;
        str[len + 1] = 0;
    }
    return str;
}

If not you need to create new string.

char *allocateAndAddCharToString(char *str, char c)
{
    char *newstr = NULL;
    if(*str)
    {
        size_t len = strlen(str);
        newstr = malloc(len + 2);
        if(newstr)
        {
            newstr[len] = c;
            newstr[len + 1] = 0;
        }
    }
    return newstr;
}

Then call the function from the place in your code where you need to add the char. Always split task into smaller bits and use functions instead of "monotonic" coding in the main function

You also mention "problems with designing your loops" I will address that topic first.

You repeat all the code again and again and just increment i in between. That is candidate #1 for creating your loop.

DISCLAIMER I assume you are using libft.

ft_split from libft indicates the end of the pointer array by array_of_paths[i]==NULL. Let's use that as condition for your loop.

char    **ft_extract_and_delimit_path(char **envp)
{
    char    **array_of_paths;
    int     i;
    int     j;

    array_of_paths = NULL;
    i = 0;

    while(ft_strnstr(envp[i], "PATH", 4) == 0)
        i++;

    array_of_paths = ft_split(envp[i] + 5, ':');
    
    for (int i = 0; array_of_paths[i] != NULL; i++)
    {
      j = 0;
      while (array_of_paths[i][j] != '\0')
        j++;
      array_of_paths[i][j] = ft_strjoin(array_of_paths[j], "/");
    }
}

That is the loop about all the paths you have in that array.

Now we need to look at your inner loop.

You are using ft_strjoin which returns a pointer to a string holding both input strings concatenated. You try to assign this into a single character which cannot work. The string you put into this function uses index j which you used for finding the length of the string but you use it as index into the array of pointers. That will likely cause out of bounds access. You also don't free the pointer that you had stored there before, causing a memory leak.

It should be like this:

      char *tmp = ft_strjoin(array_of_paths[j], "/");
      if (tmp != NULL)
      {
        free(array_of_paths[i]);
        array_of_paths[i] = tmp;
      }
      else
        ;  // Do some error handling

Together your code could look like this (not tested):

char    **ft_extract_and_delimit_path(char **envp)
{
    char    **array_of_paths = NULL;
    int     i = 0;

    while(ft_strnstr(envp[i], "PATH", 4) == 0)
        i++;

    array_of_paths = ft_split(envp[i] + 5, ':');

    for (int i = 0; array_of_paths[i] != NULL; i++)
    {
      char *tmp = ft_strjoin(array_of_paths[j], "/");
      if (tmp != NULL)
      {
        free(array_of_paths[i]);
        array_of_paths[i] = tmp;
      }
      else
        ;  // Do some error handling
    }
}

Finally, I did some improvements (use standard C lib functions, add = in search string as pointed out by Craig Estey,check if there is already a trailing '/' in the string, etc.) and created a complete example with extra printouts for better visibility:

#include <stdio.h>
#include <string.h>
#include "libft/libft.h"


char **ft_extract_and_delimit_path(char **envp)
{
  char **array_of_paths = NULL;
  int    i = 0;

  while(strncmp(envp[i], "PATH=", 5) != 0)
    i++;

  array_of_paths = ft_split(envp[i] + 5, ':');

  printf("PATH entries as found in environment:\n");
  for (int i = 0; array_of_paths[i] != NULL; i++)
  {
    printf("%02d: %s\n", i, array_of_paths[i]);
  }

  printf("\n\nAdd trailing '/' if not already present\n");
  for (int i = 0; array_of_paths[i] != NULL; i++)
  {
    if (array_of_paths[i][strlen(array_of_paths[i])-1] != '/')
    {
      char *tmp = ft_strjoin(array_of_paths[i], "/");
      if (tmp != NULL)
      {
        free(array_of_paths[i]);
        array_of_paths[i] = tmp;
      }
      else
      {
        ;  // Do some error handling
      }
    }
    else
    {
      printf("Skip %s as it already contains trailing /\n", array_of_paths[i]);
    }
  }

  printf("\n\nPATH entries after adding '/':\n");
  for (int i = 0; array_of_paths[i] != NULL; i++)
  {
    printf("%02d: %s\n", i, array_of_paths[i]);
  }
  return array_of_paths;
}


char *env[] = 
{
  "PATH=/usr/sbin:"
  "/usr/bin:"
  "/sbin:"
  "/bin:"
  "/mnt/c/Program Files/dotnet/:"
  "/mnt/c/Users/X/AppData/Local/Programs/Python/Python39/",
  NULL
};

int main(void)
{
  (void)ft_extract_and_delimit_path(env);
  return 0;
}

For demonstration I used a reduced environment replacement.

The output is:

PATH entries as found in environment:
00: /usr/sbin
01: /usr/bin
02: /sbin
03: /bin
04: /mnt/c/Program Files/dotnet/
05: /mnt/c/Users/X/AppData/Local/Programs/Python/Python39/


Add trailing '/' if not already present
Skip /mnt/c/Program Files/dotnet/ as it already contains trailing /
Skip /mnt/c/Users/X/AppData/Local/Programs/Python/Python39/ as it already contains trailing /


PATH entries after adding '/':
00: /usr/sbin/
01: /usr/bin/
02: /sbin/
03: /bin/
04: /mnt/c/Program Files/dotnet/
05: /mnt/c/Users/X/AppData/Local/Programs/Python/Python39/
Related