mirror of
https://github.com/libretro/RetroArch
synced 2025-03-03 04:14:00 +00:00
(glslang_util) Remove C++ 'isms' (where possible)
This commit is contained in:
parent
a4947780d9
commit
5ac152a5f3
@ -23,7 +23,6 @@
|
||||
#include <file/file_path.h>
|
||||
#include <file/config_file.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@ -38,105 +37,134 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool glslang_read_shader_file(const char *path, vector<string> *output, bool root_file)
|
||||
static void get_include_file(
|
||||
const char *line, char *include_file, size_t len)
|
||||
{
|
||||
char *start = NULL;
|
||||
char *end = NULL;
|
||||
|
||||
start = (char*)strchr(line, '\"');
|
||||
|
||||
if (!start)
|
||||
return;
|
||||
|
||||
start++;
|
||||
end = (char*)strchr(start, '\"');
|
||||
|
||||
if (!end)
|
||||
return;
|
||||
|
||||
*end = '\0';
|
||||
strlcpy(include_file, start, len);
|
||||
}
|
||||
|
||||
bool glslang_read_shader_file(const char *path, struct string_list *output, bool root_file)
|
||||
{
|
||||
vector<const char *> lines;
|
||||
char include_path[PATH_MAX_LENGTH];
|
||||
char tmp[PATH_MAX_LENGTH];
|
||||
char *ptr = NULL;
|
||||
char *buf = nullptr;
|
||||
int64_t len = 0;
|
||||
const char *basename = path_basename(path);
|
||||
union string_list_elem_attr attr;
|
||||
size_t i;
|
||||
const char *basename = NULL;
|
||||
uint8_t *buf = NULL;
|
||||
int64_t buf_len = 0;
|
||||
struct string_list *lines = NULL;
|
||||
|
||||
include_path[0] = tmp[0] = '\0';
|
||||
tmp[0] = '\0';
|
||||
attr.i = 0;
|
||||
|
||||
if (!filestream_read_file(path, (void**)&buf, &len))
|
||||
/* Sanity check */
|
||||
if (string_is_empty(path) || !output)
|
||||
return false;
|
||||
|
||||
basename = path_basename(path);
|
||||
|
||||
if (string_is_empty(basename))
|
||||
return false;
|
||||
|
||||
/* Read file contents */
|
||||
if (!filestream_read_file(path, (void**)&buf, &buf_len))
|
||||
{
|
||||
RARCH_ERR("Failed to open shader file: \"%s\".\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Remove Windows \r chars if we encounter them.
|
||||
* filestream_read_file() allocates one extra for 0 terminator. */
|
||||
auto itr = remove_if(buf, buf + len + 1, [](char c) {
|
||||
return c == '\r';
|
||||
});
|
||||
|
||||
if (itr < buf + len)
|
||||
*itr = '\0';
|
||||
|
||||
/* Cannot use string_split since it removes blank lines (strtok). */
|
||||
ptr = buf;
|
||||
|
||||
while (ptr && *ptr)
|
||||
if (buf_len > 0)
|
||||
{
|
||||
char *next_ptr = NULL;
|
||||
/* Remove Windows '\r' chars if we encounter them */
|
||||
string_remove_all_chars((char*)buf, '\r');
|
||||
|
||||
lines.push_back(ptr);
|
||||
|
||||
next_ptr = strchr(ptr, '\n');
|
||||
|
||||
if (next_ptr)
|
||||
{
|
||||
ptr = next_ptr + 1;
|
||||
*next_ptr = '\0';
|
||||
}
|
||||
else
|
||||
ptr = nullptr;
|
||||
/* Split into lines
|
||||
* (Blank lines must be included) */
|
||||
lines = string_separate((char*)buf, "\n");
|
||||
}
|
||||
|
||||
if (lines.empty())
|
||||
/* Buffer is no longer required - clean up */
|
||||
if (buf)
|
||||
free(buf);
|
||||
|
||||
/* Sanity check */
|
||||
if (!lines)
|
||||
return false;
|
||||
|
||||
if (lines->size < 1)
|
||||
goto error;
|
||||
|
||||
/* If this is the 'parent' shader file, ensure that first
|
||||
* line is a 'VERSION' string */
|
||||
if (root_file)
|
||||
{
|
||||
if (strstr(lines[0], "#version ") != lines[0])
|
||||
const char *line = lines->elems[0].data;
|
||||
|
||||
if (strncmp("#version ", line, STRLEN_CONST("#version ")))
|
||||
{
|
||||
RARCH_ERR("First line of the shader must contain a valid #version string.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
output->push_back(lines[0]);
|
||||
if (!string_list_append(output, line, attr))
|
||||
goto error;
|
||||
|
||||
/* Allows us to use #line to make dealing with shader errors easier.
|
||||
* This is supported by glslang, but since we always use glslang statically,
|
||||
* this is fine. */
|
||||
output->push_back("#extension GL_GOOGLE_cpp_style_line_directive : require");
|
||||
|
||||
if (!string_list_append(output, "#extension GL_GOOGLE_cpp_style_line_directive : require", attr))
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* At least VIM treats the first line as line #1,
|
||||
* so offset everything by one. */
|
||||
snprintf(tmp, sizeof(tmp), "#line %u \"%s\"", root_file ? 2 : 1, basename);
|
||||
output->push_back(tmp);
|
||||
if (!string_list_append(output, tmp, attr))
|
||||
goto error;
|
||||
|
||||
for (size_t i = root_file ? 1 : 0; i < lines.size(); i++)
|
||||
/* Loop through lines of file */
|
||||
for (i = root_file ? 1 : 0; i < lines->size; i++)
|
||||
{
|
||||
unsigned push_line = 0;
|
||||
const char *line = lines[i];
|
||||
if (strstr(line, "#include ") == line)
|
||||
const char *line = lines->elems[i].data;
|
||||
|
||||
/* Check for 'include' statements */
|
||||
if (!strncmp("#include ", line, STRLEN_CONST("#include ")))
|
||||
{
|
||||
char *closing = NULL;
|
||||
char *c = (char*)strchr(line, '"');
|
||||
char include_file[PATH_MAX_LENGTH];
|
||||
char include_path[PATH_MAX_LENGTH];
|
||||
|
||||
if (!c)
|
||||
include_file[0] = '\0';
|
||||
include_path[0] = '\0';
|
||||
|
||||
/* Build include file path */
|
||||
get_include_file(line, include_file, sizeof(include_file));
|
||||
|
||||
if (string_is_empty(include_file))
|
||||
{
|
||||
RARCH_ERR("Invalid include statement \"%s\".\n", line);
|
||||
goto error;
|
||||
}
|
||||
|
||||
c++;
|
||||
|
||||
closing = (char*)strchr(c, '"');
|
||||
|
||||
if (!closing)
|
||||
{
|
||||
RARCH_ERR("Invalid include statement \"%s\".\n", line);
|
||||
goto error;
|
||||
}
|
||||
|
||||
*closing = '\0';
|
||||
|
||||
fill_pathname_resolve_relative(include_path, path, c, sizeof(include_path));
|
||||
fill_pathname_resolve_relative(
|
||||
include_path, path, include_file, sizeof(include_path));
|
||||
|
||||
/* Parse include file */
|
||||
if (!glslang_read_shader_file(include_path, output, false))
|
||||
goto error;
|
||||
|
||||
@ -144,7 +172,8 @@ bool glslang_read_shader_file(const char *path, vector<string> *output, bool roo
|
||||
* to pull it back to current file. */
|
||||
push_line = 1;
|
||||
}
|
||||
else if (strstr(line, "#endif") || strstr(line, "#pragma"))
|
||||
else if (!strncmp("#endif", line, STRLEN_CONST("#endif")) ||
|
||||
!strncmp("#pragma", line, STRLEN_CONST("#pragma")))
|
||||
{
|
||||
/* #line seems to be ignored if preprocessor tests fail,
|
||||
* so we should reapply #line after each #endif.
|
||||
@ -152,52 +181,80 @@ bool glslang_read_shader_file(const char *path, vector<string> *output, bool roo
|
||||
* for the line after this one.
|
||||
*/
|
||||
push_line = 2;
|
||||
output->push_back(line);
|
||||
if (!string_list_append(output, line, attr))
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
output->push_back(line);
|
||||
if (!string_list_append(output, line, attr))
|
||||
goto error;
|
||||
|
||||
if (push_line != 0)
|
||||
{
|
||||
snprintf(tmp, sizeof(tmp), "#line %u \"%s\"", unsigned(i + push_line), basename);
|
||||
output->push_back(tmp);
|
||||
if (!string_list_append(output, tmp, attr))
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
string_list_free(lines);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
free(buf);
|
||||
|
||||
if (lines)
|
||||
string_list_free(lines);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static string build_stage_source(const vector<string> &lines, const char *stage)
|
||||
static string build_stage_source(const struct string_list *lines, const char *stage)
|
||||
{
|
||||
/* Note: since we have to return a std::string anyway,
|
||||
* there is nothing to be gained from trying to replace
|
||||
* this ostringstream with a C-based alternative
|
||||
* (would require a rewrite of deps/glslang/glslang.cpp) */
|
||||
ostringstream str;
|
||||
bool active = true;
|
||||
size_t i;
|
||||
|
||||
if (!lines)
|
||||
return "";
|
||||
|
||||
if (lines->size < 1)
|
||||
return "";
|
||||
|
||||
/* Version header. */
|
||||
str << lines.front();
|
||||
str << lines->elems[0].data;;
|
||||
str << '\n';
|
||||
|
||||
for (auto itr = begin(lines) + 1; itr != end(lines); ++itr)
|
||||
for (i = 1; i < lines->size; i++)
|
||||
{
|
||||
if (itr->find("#pragma stage ") == 0)
|
||||
const char *line = lines->elems[i].data;
|
||||
|
||||
/* Identify 'stage' (fragment/vertex) */
|
||||
if (!strncmp("#pragma stage ", line, STRLEN_CONST("#pragma stage ")))
|
||||
{
|
||||
if (stage)
|
||||
if (!string_is_empty(stage))
|
||||
{
|
||||
auto expected = string("#pragma stage ") + stage;
|
||||
active = itr->find(expected) != string::npos;
|
||||
char expected[128];
|
||||
|
||||
expected[0] = '\0';
|
||||
|
||||
strlcpy(expected, "#pragma stage ", sizeof(expected));
|
||||
strlcat(expected, stage, sizeof(expected));
|
||||
|
||||
active = strcmp(expected, line) == 0;
|
||||
}
|
||||
}
|
||||
else if (itr->find("#pragma name ") == 0 ||
|
||||
itr->find("#pragma format ") == 0)
|
||||
else if (!strncmp("#pragma name ", line, STRLEN_CONST("#pragma name ")) ||
|
||||
!strncmp("#pragma format ", line, STRLEN_CONST("#pragma format ")))
|
||||
{
|
||||
/* Ignore */
|
||||
}
|
||||
else if (active)
|
||||
str << *itr;
|
||||
str << line;
|
||||
|
||||
str << '\n';
|
||||
}
|
||||
|
||||
@ -288,20 +345,26 @@ static glslang_format glslang_find_format(const char *fmt)
|
||||
return SLANG_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
bool glslang_parse_meta(const vector<string> &lines, glslang_meta *meta)
|
||||
bool glslang_parse_meta(const struct string_list *lines, glslang_meta *meta)
|
||||
{
|
||||
char id[64];
|
||||
char desc[64];
|
||||
size_t i;
|
||||
|
||||
id[0] = desc[0] = '\0';
|
||||
id[0] = '\0';
|
||||
desc[0] = '\0';
|
||||
|
||||
*meta = glslang_meta{};
|
||||
if (!lines)
|
||||
return false;
|
||||
|
||||
for (auto &line : lines)
|
||||
*meta = glslang_meta{};
|
||||
|
||||
for (i = 0; i < lines->size; i++)
|
||||
{
|
||||
const char *line_c = line.c_str();
|
||||
const char *line = lines->elems[i].data;
|
||||
|
||||
if (line.find("#pragma name ") == 0)
|
||||
/* Check for shader identifier */
|
||||
if (!strncmp("#pragma name ", line, STRLEN_CONST("#pragma name ")))
|
||||
{
|
||||
const char *str = NULL;
|
||||
|
||||
@ -311,16 +374,18 @@ bool glslang_parse_meta(const vector<string> &lines, glslang_meta *meta)
|
||||
return false;
|
||||
}
|
||||
|
||||
str = line_c + STRLEN_CONST("#pragma name ");
|
||||
|
||||
str = line + STRLEN_CONST("#pragma name ");
|
||||
while (*str == ' ')
|
||||
str++;
|
||||
|
||||
meta->name = str;
|
||||
}
|
||||
else if (line.find("#pragma parameter ") == 0)
|
||||
/* Check for shader parameters */
|
||||
else if (!strncmp("#pragma parameter ", line, STRLEN_CONST("#pragma parameter ")))
|
||||
{
|
||||
float initial, minimum, maximum, step;
|
||||
int ret = sscanf(line_c, "#pragma parameter %63s \"%63[^\"]\" %f %f %f %f",
|
||||
int ret = sscanf(
|
||||
line, "#pragma parameter %63s \"%63[^\"]\" %f %f %f %f",
|
||||
id, desc, &initial, &minimum, &maximum, &step);
|
||||
|
||||
if (ret == 5)
|
||||
@ -331,19 +396,33 @@ bool glslang_parse_meta(const vector<string> &lines, glslang_meta *meta)
|
||||
|
||||
if (ret == 6)
|
||||
{
|
||||
auto itr = find_if(begin(meta->parameters), end(meta->parameters), [&](const glslang_parameter ¶m) {
|
||||
return param.id == id;
|
||||
});
|
||||
bool parameter_found = false;
|
||||
size_t parameter_index = 0;
|
||||
size_t j;
|
||||
|
||||
for (j = 0; j < meta->parameters.size(); j++)
|
||||
{
|
||||
/* Note: LHS is a std:string, RHS is a C string.
|
||||
* (the glslang_meta stuff has to be C++) */
|
||||
if (meta->parameters[j].id == id)
|
||||
{
|
||||
parameter_found = true;
|
||||
parameter_index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allow duplicate #pragma parameter, but only
|
||||
* if they are exactly the same. */
|
||||
if (itr != end(meta->parameters))
|
||||
if (parameter_found)
|
||||
{
|
||||
if ( itr->desc != desc ||
|
||||
itr->initial != initial ||
|
||||
itr->minimum != minimum ||
|
||||
itr->maximum != maximum ||
|
||||
itr->step != step
|
||||
const glslang_parameter *parameter = &meta->parameters[parameter_index];
|
||||
|
||||
if ( parameter->desc != desc ||
|
||||
parameter->initial != initial ||
|
||||
parameter->minimum != minimum ||
|
||||
parameter->maximum != maximum ||
|
||||
parameter->step != step
|
||||
)
|
||||
{
|
||||
RARCH_ERR("[slang]: Duplicate parameters found for \"%s\", but arguments do not match.\n", id);
|
||||
@ -355,11 +434,12 @@ bool glslang_parse_meta(const vector<string> &lines, glslang_meta *meta)
|
||||
}
|
||||
else
|
||||
{
|
||||
RARCH_ERR("[slang]: Invalid #pragma parameter line: \"%s\".\n", line_c);
|
||||
RARCH_ERR("[slang]: Invalid #pragma parameter line: \"%s\".\n", line);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (line.find("#pragma format ") == 0)
|
||||
/* Check for framebuffer format */
|
||||
else if (!strncmp("#pragma format ", line, STRLEN_CONST("#pragma format ")))
|
||||
{
|
||||
const char *str = NULL;
|
||||
|
||||
@ -369,12 +449,12 @@ bool glslang_parse_meta(const vector<string> &lines, glslang_meta *meta)
|
||||
return false;
|
||||
}
|
||||
|
||||
str = line_c + STRLEN_CONST("#pragma format ");
|
||||
|
||||
str = line + STRLEN_CONST("#pragma format ");
|
||||
while (*str == ' ')
|
||||
str++;
|
||||
|
||||
meta->rt_format = glslang_find_format(str);
|
||||
|
||||
if (meta->rt_format == SLANG_FORMAT_UNKNOWN)
|
||||
{
|
||||
RARCH_ERR("[slang]: Failed to find format \"%s\".\n", str);
|
||||
@ -382,37 +462,50 @@ bool glslang_parse_meta(const vector<string> &lines, glslang_meta *meta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(HAVE_GLSLANG)
|
||||
bool glslang_compile_shader(const char *shader_path, glslang_output *output)
|
||||
{
|
||||
vector<string> lines;
|
||||
struct string_list *lines = string_list_new();
|
||||
|
||||
if (!lines)
|
||||
return false;
|
||||
|
||||
RARCH_LOG("[slang]: Compiling shader \"%s\".\n", shader_path);
|
||||
|
||||
if (!glslang_read_shader_file(shader_path, &lines, true))
|
||||
return false;
|
||||
if (!glslang_read_shader_file(shader_path, lines, true))
|
||||
goto error;
|
||||
|
||||
if (!glslang_parse_meta(lines, &output->meta))
|
||||
return false;
|
||||
goto error;
|
||||
|
||||
if ( !glslang::compile_spirv(build_stage_source(lines, "vertex"),
|
||||
glslang::StageVertex, &output->vertex))
|
||||
{
|
||||
RARCH_ERR("Failed to compile vertex shader stage.\n");
|
||||
return false;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ( !glslang::compile_spirv(build_stage_source(lines, "fragment"),
|
||||
glslang::StageFragment, &output->fragment))
|
||||
{
|
||||
RARCH_ERR("Failed to compile fragment shader stage.\n");
|
||||
return false;
|
||||
goto error;
|
||||
}
|
||||
|
||||
string_list_free(lines);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
|
||||
if (lines)
|
||||
string_list_free(lines);
|
||||
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
bool glslang_compile_shader(const char *shader_path, glslang_output *output)
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <stdint.h>
|
||||
#include <retro_common_api.h>
|
||||
|
||||
#include <lists/string_list.h>
|
||||
|
||||
typedef enum glslang_format
|
||||
{
|
||||
SLANG_FORMAT_UNKNOWN = 0,
|
||||
@ -106,8 +108,8 @@ struct glslang_output
|
||||
bool glslang_compile_shader(const char *shader_path, glslang_output *output);
|
||||
|
||||
/* Helpers for internal use. */
|
||||
bool glslang_read_shader_file(const char *path, std::vector<std::string> *output, bool root_file);
|
||||
bool glslang_parse_meta(const std::vector<std::string> &lines, glslang_meta *meta);
|
||||
bool glslang_read_shader_file(const char *path, struct string_list *output, bool root_file);
|
||||
bool glslang_parse_meta(const struct string_list *lines, glslang_meta *meta);
|
||||
#endif
|
||||
|
||||
void *config_file_new_wrapper(const char *path);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include <compat/strl.h>
|
||||
#include <lists/string_list.h>
|
||||
|
||||
#include "../../verbosity.h"
|
||||
|
||||
@ -93,11 +94,23 @@ bool slang_preprocess_parse_parameters(const char *shader_path,
|
||||
struct video_shader *shader)
|
||||
{
|
||||
glslang_meta meta;
|
||||
vector<string> lines;
|
||||
bool ret = false;
|
||||
struct string_list *lines = string_list_new();
|
||||
|
||||
if (!glslang_read_shader_file(shader_path, &lines, true))
|
||||
return false;
|
||||
if (!lines)
|
||||
goto end;
|
||||
|
||||
if (!glslang_read_shader_file(shader_path, lines, true))
|
||||
goto end;
|
||||
if (!glslang_parse_meta(lines, &meta))
|
||||
return false;
|
||||
return slang_preprocess_parse_parameters(meta, shader);
|
||||
goto end;
|
||||
|
||||
ret = slang_preprocess_parse_parameters(meta, shader);
|
||||
|
||||
end:
|
||||
|
||||
if (lines)
|
||||
string_list_free(lines);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -88,6 +88,19 @@ bool string_list_find_elem_prefix(const struct string_list *list,
|
||||
*/
|
||||
struct string_list *string_split(const char *str, const char *delim);
|
||||
|
||||
/**
|
||||
* string_separate:
|
||||
* @str : string to turn into a string list
|
||||
* @delim : delimiter character to use for separating the string.
|
||||
*
|
||||
* Creates a new string list based on string @str, delimited by @delim.
|
||||
* Includes empty strings - i.e. two adjacent delimiters will resolve
|
||||
* to a string list element of "".
|
||||
*
|
||||
* Returns: new string list if successful, otherwise NULL.
|
||||
*/
|
||||
struct string_list *string_separate(char *str, const char *delim);
|
||||
|
||||
/**
|
||||
* string_list_new:
|
||||
*
|
||||
|
@ -129,6 +129,28 @@ char *string_trim_whitespace(char *const s);
|
||||
char *word_wrap(char *buffer, const char *string,
|
||||
int line_width, bool unicode, unsigned max_lines);
|
||||
|
||||
/* Splits string into tokens seperated by 'delim'
|
||||
* > Returned token string must be free()'d
|
||||
* > Returns NULL if token is not found
|
||||
* > After each call, 'str' is set to the position after the
|
||||
* last found token
|
||||
* > Tokens *include* empty strings
|
||||
* Usage example:
|
||||
* char *str = "1,2,3,4,5,6,7,,,10,";
|
||||
* char **str_ptr = &str;
|
||||
* char *token = NULL;
|
||||
* while((token = string_tokenize(str_ptr, ",")))
|
||||
* {
|
||||
* printf("%s\n", token);
|
||||
* free(token);
|
||||
* token = NULL;
|
||||
* }
|
||||
*/
|
||||
char* string_tokenize(char **str, const char *delim);
|
||||
|
||||
/* Removes every instance of character 'c' from 'str' */
|
||||
void string_remove_all_chars(char *str, char c);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -259,6 +259,59 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_separate:
|
||||
* @str : string to turn into a string list
|
||||
* @delim : delimiter character to use for separating the string.
|
||||
*
|
||||
* Creates a new string list based on string @str, delimited by @delim.
|
||||
* Includes empty strings - i.e. two adjacent delimiters will resolve
|
||||
* to a string list element of "".
|
||||
*
|
||||
* Returns: new string list if successful, otherwise NULL.
|
||||
*/
|
||||
struct string_list *string_separate(char *str, const char *delim)
|
||||
{
|
||||
char *token = NULL;
|
||||
char **str_ptr = NULL;
|
||||
struct string_list *list = NULL;
|
||||
|
||||
/* Sanity check */
|
||||
if (!str || string_is_empty(delim))
|
||||
goto error;
|
||||
|
||||
str_ptr = &str;
|
||||
list = string_list_new();
|
||||
|
||||
if (!list)
|
||||
goto error;
|
||||
|
||||
token = string_tokenize(str_ptr, delim);
|
||||
while (token)
|
||||
{
|
||||
union string_list_elem_attr attr;
|
||||
|
||||
attr.i = 0;
|
||||
|
||||
if (!string_list_append(list, token, attr))
|
||||
goto error;
|
||||
|
||||
free(token);
|
||||
token = NULL;
|
||||
|
||||
token = string_tokenize(str_ptr, delim);
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
error:
|
||||
if (token)
|
||||
free(token);
|
||||
if (list)
|
||||
string_list_free(list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_list_find_elem:
|
||||
* @list : pointer to string list
|
||||
|
@ -237,3 +237,84 @@ char *word_wrap(char* buffer, const char *string, int line_width, bool unicode,
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Splits string into tokens seperated by 'delim'
|
||||
* > Returned token string must be free()'d
|
||||
* > Returns NULL if token is not found
|
||||
* > After each call, 'str' is set to the position after the
|
||||
* last found token
|
||||
* > Tokens *include* empty strings
|
||||
* Usage example:
|
||||
* char *str = "1,2,3,4,5,6,7,,,10,";
|
||||
* char **str_ptr = &str;
|
||||
* char *token = NULL;
|
||||
* while((token = string_tokenize(str_ptr, ",")))
|
||||
* {
|
||||
* printf("%s\n", token);
|
||||
* free(token);
|
||||
* token = NULL;
|
||||
* }
|
||||
*/
|
||||
char* string_tokenize(char **str, const char *delim)
|
||||
{
|
||||
/* Taken from https://codereview.stackexchange.com/questions/216956/strtok-function-thread-safe-supports-empty-tokens-doesnt-change-string# */
|
||||
char *str_ptr = NULL;
|
||||
char *delim_ptr = NULL;
|
||||
char *token = NULL;
|
||||
size_t token_len = 0;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!str || string_is_empty(delim))
|
||||
return NULL;
|
||||
|
||||
str_ptr = *str;
|
||||
|
||||
/* Note: we don't check string_is_empty() here,
|
||||
* empty strings are valid */
|
||||
if (!str_ptr)
|
||||
return NULL;
|
||||
|
||||
/* Search for delimiter */
|
||||
delim_ptr = strstr(str_ptr, delim);
|
||||
|
||||
if (delim_ptr)
|
||||
token_len = delim_ptr - str_ptr;
|
||||
else
|
||||
token_len = strlen(str_ptr);
|
||||
|
||||
/* Allocate token string */
|
||||
token = (char *)malloc((token_len + 1) * sizeof(char));
|
||||
|
||||
if (!token)
|
||||
return NULL;
|
||||
|
||||
/* Copy token */
|
||||
strlcpy(token, str_ptr, (token_len + 1) * sizeof(char));
|
||||
token[token_len] = '\0';
|
||||
|
||||
/* Update input string pointer */
|
||||
*str = delim_ptr ? delim_ptr + strlen(delim) : NULL;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/* Removes every instance of character 'c' from 'str' */
|
||||
void string_remove_all_chars(char *str, char c)
|
||||
{
|
||||
char *read_ptr = NULL;
|
||||
char *write_ptr = NULL;
|
||||
|
||||
if (string_is_empty(str))
|
||||
return;
|
||||
|
||||
read_ptr = str;
|
||||
write_ptr = str;
|
||||
|
||||
while (*read_ptr != '\0')
|
||||
{
|
||||
*write_ptr = *read_ptr++;
|
||||
write_ptr += (*write_ptr != c) ? 1 : 0;
|
||||
}
|
||||
|
||||
*write_ptr = '\0';
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user