Coder Social home page Coder Social logo

likle / cwalk Goto Github PK

View Code? Open in Web Editor NEW
231.0 231.0 39.0 327 KB

Path library for C/C++. Cross-Platform for Linux, FreeBSD, Windows and MacOS. Supports UNIX and Windows path styles on those platforms.

Home Page: https://likle.github.io/cwalk/

License: MIT License

CMake 8.73% C 90.58% Meson 0.70%
c c-plus-plus c-plus-plus-11 cpp cross-platform directory file file-path file-system filesystem library linux macos osx path path-manipulation path-parsing unc windows

cwalk's People

Contributors

0xh0b0 avatar angelofrangione avatar davidebeatrici avatar dkowl avatar falsycat avatar forgge avatar foxieflakey avatar freevryheid avatar ilya33 avatar jwerle avatar likle avatar mupfdev avatar noodlecollie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cwalk's Issues

consider provide two set of APIs?

Sometime processing paths with all styles is needed, and switch between them is not convenient(not thread safe too), can you separated two set of APIs(with CPP or something) so that user can use them both without switch frequently?

Conan package

Hi, I'm trying to use your lib inside my project which is based on conan package manager.

Are you planning to release your work to the conan package center?

I have some difficulties to use embed cwalk downloaded directly via CMakeLists ExternalProject

[Bug] cwk_path_get_absolute can produce incorrect results when reusing base as buffer

Hello! First real quick, I've been using libcwalk for a few days so far and it's done basically everything I need with no fuss. I love it!

Okay, so in an application I'm working on now on an embedded platform, I'm using libcwalk to traverse my folder hierarchy as the user navigates. I have a single buffer that I'm using and have been trying to use cwk_path_get_absolute to build the active path as the user pushes and pops directories. The issue I've run into is that attempting to append a path after a relative path has been appended results in an incorrect result. I've created a small example here to demonstrate the behavior and my expected outcome:

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

#include "cwalk.h"

#define PATHSIZE 256

void path_push(char* path, const char* add, const char* expected)
{
	cwk_path_get_absolute(path, add, path, PATHSIZE);

	const char* result = strcmp(path, expected) == 0 ? "PASS" : "FAIL";
	printf("%s: '%s', expected: '%s'\r\n", result, path, expected);
}

int main(int argc, char** argv)
{
	char path[PATHSIZE] = { 0 };
	cwk_path_set_style(CWK_STYLE_UNIX);
	path_push(path, "/", "/");
	path_push(path, "see", "/see");
	path_push(path, "dog", "/see/dog");
	path_push(path, "run", "/see/dog/run");
	path_push(path, "..", "/see/dog");
	path_push(path, "..", "/see");
	path_push(path, "cat", "/see/cat");
	path_push(path, "run", "/see/cat/run");
}

Using the relative ".." to navigate up seems to somehow cause the next time a new fragment is added, append to the end of the original, pre-"up'd" directory.

Is this behavior expected when reusing the base path as the result like that?

Thanks for taking a look!

get_absolute creates only UNIX paths if base is not absolute

on line 651

  // The basename should be an absolute path if the caller is using the API
  // correctly. However, he might not and in that case we will append a fake
  // root at the beginning.
  if (cwk_path_is_absolute(base)) {
    i = 0;
  } else {
    paths[0] = "/";
    i = 1;
  }

The function produces a weird Unix path if the base is not absolute in windows.
There could be some thought about trying to see if the appended path has a root in those cases
since assuming c:\ as a root is not the best option.

BTW, I stumbled on this because I tried path_join and could not produce absolute paths with it.

Documentation isn't clearly states that size excludes NUL terminator

One of such problems is cwk_path_normalize's documentation

The function returns the total number of characters the complete buffer would have

Problem: "complete buffer" full buffer to store whole thing which in this case a string which in C includes the NUL terminator which also implicitly make the return value also includes the NUL terminator

Solution: Documentation should clearly express that it excludes NUL terminator. Like below example

The function returns the total number of characters the complete buffer would have excluding NUL terminator

Additional better suggestion:
The wording could be changed to this which is shorter and directly express what it means

The function returns the size of its full output excluding NUL terminator

Pssst a little story, this unclear documentation problem caused me to debug some nasty off by one bugs which leads to buffer overflow which in my program writes/reads to the file path I passed in but includes the overflow when the program opens the file (like i gave "./whatever_dir/output.txt" but my program opens "/home/fox/project/whatever_dir/output.txt<insert garbage from overflow>" instead).

Provide some handling examples / rethink interface

My problem was to receive an absolute path ("C:\bla\ble\bli\blo\blu") and create it.
Since the directories in the middle may not exist, either, I have to iterate through the path and create the directories as they appear.

At the end, the solution ended like this:

// This is a co-routine for the create Dir function
// it uses a double buffer approach to concatenate the paths
// because the cwalk library requires using a path1, path2, output_buffer architecture
char * co_append_path(char * path , char *path_end, bool initialize)
{
	static char buf1[PATH_BUF_SIZE];
	static char buf2[PATH_BUF_SIZE];
	static char *ind[2] = { buf1,buf2 };
	static int src_buf;

#define dest_buf (1-src_buf)

	if (initialize)
	{
		int rootLen = 0;
		src_buf = 0;
		buf1[0] = 0;
		buf2[0] = 0;

		cwk_path_get_root(path, &rootLen);
		strncpy_s(ind[src_buf], PATH_BUF_SIZE, path, rootLen);
	}
	else
	{
		char sep = *path_end;
		*path_end = 0;
		cwk_path_get_absolute(ind[src_buf], path, ind[dest_buf], PATH_BUF_SIZE); 
		*path_end = sep;
		src_buf = dest_buf;
	}

	return ind[src_buf];
}

// newDir must be in absolute format
int createDir(char *newDir)
{

	char *p;
	struct cwk_segment segment;

	p = co_append_path(newDir, NULL, true);

	if (!cwk_path_get_first_segment(newDir, &segment))
	{
		return -1;
	}
	
	do { 
		p = co_append_path((char*)segment.begin,(char*)segment.end,false);
		if (dirExists(p))
		{
			printf("dir Exists [%s]\n",p);
		}
		else
		{
			printf("dir does not Exist [%s]\n", p);
			//create it
		}
	} while (cwk_path_get_next_segment(&segment));

	return 0;
}

I needed to create this strange co-routine in order to deal with the source1,source2, target API style that you created.
Also, what #9 required makes sense, specially because you return indexes some times, so adding something from another path with begin+length, makes sense on your API.

How would you solve my problem?
Is there something that I'm missing?

Windows path root separator is normalized

Hi, thanks for making this library, I've been using it for my side project pocketlang.

On windows cwk_path_normalize does not seems to change the path separator of the root from / to \\ (An example provided below). Is this behavior intended or should be patched?

>>> import path
>>> path.normpath('foo/bar/baz')
"foo\\bar\\baz"
>>> path.normpath('C:/foo/bar/baz')
"C:/foo\\bar\\baz"

cwk_path_get_relative also seems to fail because of the separator of the root

>>> path.relpath('C:\\foo/bar/file.txt', 'C:\\foo\\bar\\baz\\')
"..\\file.txt"
>>> path.relpath('C:/foo/bar/file.txt', 'C:\\foo\\bar\\baz\\')
""

Generate pkgconfig files

It'd be useful if running meson build would generate pkg-config files.

You can do it with

pkg = import ('pkgconfig')
pkg.generate (cwalk)

I'm not sure whether you already do this in your cmake files -- I don't know cmake (and barely know meson).

I'm aware that the build instructions say use cmake, but I saw a meson.build and thought, hey, why not? And
it worked, sans pkg-config files.

useless set of variable is_device_path

Hi!

I'm using cwalk in my project and i use the clang static analyzer tool for scanning my code and it scans cwalk too.
And he finds an useless set of a variable in the code since multiple commits.

../../../subprojects/cwalk/src/cwalk.c:471:3: warning: Value stored to 'is_device_path' is never read
  is_device_path = false;
  ^                ~~~~~

That means it is useless to define it to false in the present code. Because you set it to anything else before using it.
Do you intend to use it in the future ?
It's nice that it's the only problem found by this tool, usually there are a ton !

Weird behavior in cwk_path_get_relative when child directory have same name as its parents

Example:

cwk_path_set_style(::CWK_STYLE_UNIX);

char buffer[FILENAME_MAX];

cwk_path_get_relative(
	"/dir/dir/",
	"/dir/dir3/file",
	buffer,
	sizeof(buffer));

printf("The relative path is: %s\n", buffer);

Will return
The relative path is: file

This is wrong. Expected results should be
../dir3/file

If i change base dir to something else for example from "/dir/dir/" to "/dir/dir2/"

cwk_path_set_style(::CWK_STYLE_UNIX);

char buffer[FILENAME_MAX];

cwk_path_get_relative(
	"/dir/dir2/",
	"/dir/dir3/file",
	buffer,
	sizeof(buffer));

printf("The relative path is: %s\n", buffer);

it will return correct results.
The relative path is: ../dir3/file

Consider producing a new release

I am currently working on contributing a wrap file to wrapdb to be able to use cwalk as a meson dependency with a single command.

Latest release contains a meson.build file that produces warnings and that should be avoided. It seems though as those warnings have already been patched.

It would be awesome if a new release could be cut, containing those fixes.

Thanks!

Invalid pointer dereference with clang-analyzer

Hi,

the function cwk_path_guess_style triggers a warning with clang static analyzer, I'm attaching the full report here

report.zip

basically the call to cwk_path_get_last_segment at line 1386 does not initialize
segment.begin which is de-referenced the following line.

cwk_path_is_absolute returns an incorrect result

Hello.
Thanks for your library and putting a lot of effort into it.

I've made this simple unit test and 5 asserts have failed.

#ifdef _WIN32
  const bool is_unix = false;
  cwk_path_set_style(CWK_STYLE_WINDOWS);
#else
  const bool is_posix = true;
  cwk_path_set_style(CWK_STYLE_UNIX);
#endif

  assert( cwk_path_is_absolute("/") == is_unix  ); // fails
  assert( cwk_path_is_absolute("/dir") == is_unix  ); // fails
  assert( cwk_path_is_absolute("/dir/") == is_unix  ); // fails
  assert( cwk_path_is_absolute("/dir/tmp") == is_unix  ); // fails
  assert( cwk_path_is_absolute("/dir/tmp/") == is_unix  ); // fails
  assert( ! cwk_path_is_absolute("dir") );
  assert( ! cwk_path_is_absolute("dir/") );
  assert( ! cwk_path_is_absolute("dir/tmp") );
  assert( ! cwk_path_is_absolute("dir/tmp/") );
  assert( ! cwk_path_is_absolute("c:") );
  assert( ! cwk_path_is_absolute("c:dir") );
  assert( ! cwk_path_is_absolute("c:dir/") );
  assert( ! cwk_path_is_absolute("c:dir/tmp") );
  assert( ! cwk_path_is_absolute("c:dir/tmp/") );
  assert( cwk_path_is_absolute("c:/") == !is_unix  );
  assert( cwk_path_is_absolute("c:/dir") == !is_unix  );
  assert( cwk_path_is_absolute("c:/dir/") == !is_unix  );
  assert( cwk_path_is_absolute("c:/dir/tmp") == !is_unix  );
  assert( cwk_path_is_absolute("c:/dir/tmp/") == !is_unix  );

Windows also has a PathIsRelativeA/W function, which can be used as reference. However it has some limitations e.g MAX_PATH limit.

Add function that returns program location

It would be really useful to me (and others, I'm sure) if your project had a function that returns the absolute path to the executable that's calling the function. For example, if you were running a program whose binary exists at /usr/bin/myprog, and arvg[0] contains an alias to that program, or a symlink, or some other thing I haven't thought of, the function would return /usr/bin/myprog.

I have found a couple of other libraries available on Conan that have this functionality, including Corrade::Utility::Directory::executableLocation() in Corrade (https://github.com/mosra/corrade) and boost::dll::fs::path::program_location() in Boost (https://github.com/boostorg/dll), but it's impossible to install either of them without installing a whole lot of other dependencies that I'm not interested in.

Your library would be a perfect fit for this kind of function: it's very small, and includes a lot of other functions that would be very useful in client code after calling program_location().

I could write the program_location() function for you if you're interested. The implementation is around 5-10 SLOC for each platform you want to target, plus some preprocessor directives. I hate having to copy-paste this code whenever I need it; it really belongs in a library that has a good CI test suite, like yours.

The only tricky part I can foresee is writing cross-platform tests for this function. I think that you would want one process that calls the program_location() function, and another process that can verify the first process' output under different conditions (aliased, symlinked, etc) where argv[0] could get you the wrong answer. I would be tempted to write this in bash script, iff you can do that on Windows.

Please let me know if you're interested. I really like your project.

Dangerous fault in the behaviour of cwk_path_join

The behaviour of cwk_path_join should be similar to running cd on the first argument, and cd on the last argument directly afterwards.

We can see this works with simple statements, like joining together hello and world:
Output of cwk_path_join: hello/world
Output of os.path.join from Python: hello/world
Output of two cds: hello/world

But, if we try to do something more subtle, like joining together /hello and /world, cwalk fails catastrophically:
Output of cwk_path_join: /hello/world (See https://likle.github.io/cwalk/reference/cwk_path_join.html - This flaw is even demonstrated in the documentation!)
Output of os.path.join from Python: /world
Output of two cds: /world

So, why is this happening? It seems that cwk_path_join doesn't actually "walk" or "separate cd" as it is intended to, but rather word-for-word smashes the two paths together.

Unless there is an alternative function that does this properly, cwk_path_join should NOT operate like this. Every argument to cwk_path_join should operate like a separate, sequenced cd.

If I run cd /hello and then cd /world, I end up in /world, because of the /. I do not end up in /hello/world. If I used /hello and then ./world (or just world), it would be /hello/world, but I did not do this.

Unreliable behaviour of cwk_path_join makes this library completely unusable for many people. I would suggest testing your function against Python's os.path functions.

make cwk_path_change_extension more like std::filesystem::path

std::filesystem::path::replace_extension vs cwalk_path_change_extension

"/foo/bar.jpg" + ".png"
expected: "/foo/bar.png", result: "/foo/bar.png"
Result: Success!

"/foo/bar.jpg" + "png"
expected: "/foo/bar.png", result: "/foo/bar.png"
Result: Success!

"/foo/bar.jpg" + "."
expected: "/foo/bar.", result: "/foo/bar."
Result: Success!

"/foo/bar.jpg" + ""
expected: "/foo/bar", result: "/foo/bar."
Result: Error!

"/foo/bar." + "png"
expected: "/foo/bar.png", result: "/foo/bar.png"
Result: Success!

"/foo/bar" + ".png"
expected: "/foo/bar.png", result: "/foo/bar.png"
Result: Success!

"/foo/bar" + "png"
expected: "/foo/bar.png", result: "/foo/bar.png"
Result: Success!

"/foo/bar" + "."
expected: "/foo/bar.", result: "/foo/bar."
Result: Success!

"/foo/bar" + ""
expected: "/foo/bar", result: "/foo/bar."
Result: Error!

"/foo/." + ".png"
expected: "/foo/..png", result: "/foo/.png"
Result: Error!

"/foo/." + "png"
expected: "/foo/..png", result: "/foo/.png"
Result: Error!

"/foo/." + "."
expected: "/foo/..", result: "/foo/."
Result: Error!

"/foo/." + ""
expected: "/foo/.", result: "/foo/."
Result: Success!

"/foo/" + ".png"
expected: "/foo/.png", result: "/foo.png/"
Result: Error!

"/foo/" + "png"
expected: "/foo/.png", result: "/foo.png/"
Result: Error!

Wide string support

When working with path sometimes Wide-string is a must.

Do you think adding Wide string support such as wchar_t will worth a effort?

Suggestion: functions to determine buffer size

Add support to just return absolute path length so i can allocate exact amount of memory in an array

size_t cwk_path_get_absolute_length(const char *base, const char *path)

this would return 6
then i would allocate 6 + 1 bytes to a char array
example usage:

size_t length = cwk_path_get_absolute_length("/hello/", "/world") + 1; // 6 + 1 = 7
char *buffer = malloc(length);
cwk_path_get_absolute("/hello/", "/world", buffer, length);
printf("Path: %s", buffer); // "/world"

Same for relative

cwk_path_get_relative segfaulted

cwk_path_get_relative is buggy if:

  • two input path are not the same type, e.g. cwk_path_get_relative("/foo", "./bar", ...), in this case cwk_path_get_relative return garbage result.
  • the second input path is a root, e.g. cwk_path_get_relative("...", "/", ...), in this case cwk_path_get_relative segfaulted.

incorrect basename

Thanks for the library, got a question:

Perhaps I'm misunderstanding the concept of basename - the following returns a basename of 'my' with length 2 - I was expecting a null string with length 0.

#include <cwalk.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  const·char·*basename;
  size_t length;
  cwk_path_set_style(CWK_STYLE_UNIX);  
  cwk_path_get_basename("/my/", &basename, &length);
  printf("The basename is: '%.*s'", length, basename);
  return EXIT_SUCCESS;
}

Possibility of sized variants of functions?

Hey there. Is there a possibility of function variants that take the input string's length as a parameter instead of relying on them being null-terminated?

I have an application that makes heavy use of path manipulation and all of the string sizes are known beforehand. Further, not all places where this will be needed will have null terminators, so I'd rather avoid a copy in such cases.

Just curious :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.