How to create a virtual absolute path

Fri, Jan 19 2024 18:15:56 KST

This function creates a virtual absolute path.
One thing to consider is that the path does not exceed PATH_MAX.
If you input a relative path, it will be combined with the result of the getcwd() function.

  • .. is resolved.
  • Duplicate slashes are converted into one.
  • The trailing slash is removed.
/*
  To use nullptr , use the -std=c23 option.
  cc -std=c23 vpath.c -o vpath
*/
#include <stdio.h>
#include "c-str.h"
#include <stdlib.h>
#include "c-array.h"
#include <unistd.h>
#include <string.h>
#include <limits.h>

char* vpath (const char* path)
{
  if (path == nullptr || path[0] == '\0')
    return nullptr;

  CArray* array = c_array_new (nullptr, false);

  if (path[0] != '/')
  {
    char* cwd = getcwd (nullptr, 0);
    char** strv = c_str_split (cwd, '/');
    for (int i = 0; strv[i]; i++)
      c_array_add (array, strv[i]);

    free (strv);
    free (cwd);
  }

  char** strv = c_str_split (path, '/');

  for (int i = 0; strv[i]; i++)
  {
    if (strv[i][0] == '\0')
    {
      if (array->len == 0)
        c_array_add (array, strv[i]);
      else
        free (strv[i]);
    }
    else if (c_str_equal (strv[i], "."))
    {
      free (strv[i]);
    }
    else if (c_str_equal (strv[i], ".."))
    {
      free (strv[i]);
      if (array->len > 1)
      {
        free (array->data[array->len - 1]);
        c_array_remove_index (array, array->len - 1);
      }
    }
    else
    {
      c_array_add (array, strv[i]);
    }
  }

  c_array_add (array, nullptr);
  free (strv);
  strv = (char**) c_array_free (array);

  char* path2 = c_strv_join ((const char**) strv, "/");
  c_strv_free (strv);

  if (strlen (path2) > PATH_MAX)
  {
    free (path2);
    return nullptr;
  }

  return path2;
}

int main (int argc, char** argv)
{
  if (argc != 2)
  {
    printf ("Usage: vpath <path>\n");
    return 1;
  }

  printf ("input: %s\n", argv[1]);

  char* path = vpath (argv[1]);
  if (path)
  {
    printf ("output: %s\n", path);
    free (path);
  }

  return 0;
}