* Fix some unused result warnings.
* Handle folder art a little differently than file album art.
This commit is contained in:
parent
d42f8fc193
commit
875f0e2351
112
albumart.c
112
albumart.c
@ -36,22 +36,26 @@
|
|||||||
#include "image_utils.h"
|
#include "image_utils.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
int
|
static int
|
||||||
art_cache_exists(const char * orig_path, char ** cache_file)
|
art_cache_exists(const char *orig_path, char **cache_file)
|
||||||
{
|
{
|
||||||
asprintf(cache_file, "%s/art_cache%s", db_path, orig_path);
|
if( asprintf(cache_file, "%s/art_cache%s", db_path, orig_path) < 0 )
|
||||||
|
{
|
||||||
|
*cache_file = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
strcpy(strchr(*cache_file, '\0')-4, ".jpg");
|
strcpy(strchr(*cache_file, '\0')-4, ".jpg");
|
||||||
|
|
||||||
return (!access(*cache_file, F_OK));
|
return (!access(*cache_file, F_OK));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
static char *
|
||||||
save_resized_album_art(image_s * imsrc, const char * path)
|
save_resized_album_art(image_s *imsrc, const char *path)
|
||||||
{
|
{
|
||||||
int dstw, dsth;
|
int dstw, dsth;
|
||||||
image_s * imdst;
|
image_s *imdst;
|
||||||
char * cache_file;
|
char *cache_file;
|
||||||
char * cache_dir;
|
char cache_dir[MAXPATHLEN];
|
||||||
|
|
||||||
if( !imsrc )
|
if( !imsrc )
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -59,9 +63,8 @@ save_resized_album_art(image_s * imsrc, const char * path)
|
|||||||
if( art_cache_exists(path, &cache_file) )
|
if( art_cache_exists(path, &cache_file) )
|
||||||
return cache_file;
|
return cache_file;
|
||||||
|
|
||||||
cache_dir = strdup(cache_file);
|
strncpyt(cache_dir, cache_file, sizeof(cache_dir));
|
||||||
make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
|
make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
|
||||||
free(cache_dir);
|
|
||||||
|
|
||||||
if( imsrc->width > imsrc->height )
|
if( imsrc->width > imsrc->height )
|
||||||
{
|
{
|
||||||
@ -88,7 +91,8 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Simple, efficient hash function from Daniel J. Bernstein */
|
/* Simple, efficient hash function from Daniel J. Bernstein */
|
||||||
unsigned int DJBHash(const char * str, int len)
|
static unsigned int
|
||||||
|
DJBHash(const char *str, int len)
|
||||||
{
|
{
|
||||||
unsigned int hash = 5381;
|
unsigned int hash = 5381;
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
@ -103,19 +107,22 @@ unsigned int DJBHash(const char * str, int len)
|
|||||||
|
|
||||||
/* And our main album art functions */
|
/* And our main album art functions */
|
||||||
void
|
void
|
||||||
update_if_album_art(const char * path)
|
update_if_album_art(const char *path)
|
||||||
{
|
{
|
||||||
char * dir;
|
char *dir;
|
||||||
char * match = NULL;
|
char *match;
|
||||||
char * file = NULL;
|
char file[MAXPATHLEN];
|
||||||
|
char fpath[MAXPATHLEN];
|
||||||
|
char dpath[MAXPATHLEN];
|
||||||
int ncmp = 0;
|
int ncmp = 0;
|
||||||
int album_art;
|
int album_art;
|
||||||
DIR * dh;
|
DIR *dh;
|
||||||
struct dirent *dp;
|
struct dirent *dp;
|
||||||
enum file_types type = TYPE_UNKNOWN;
|
enum file_types type = TYPE_UNKNOWN;
|
||||||
sqlite_int64 art_id = 0;
|
sqlite_int64 art_id = 0;
|
||||||
|
|
||||||
match = strdup(basename((char *)path));
|
strncpyt(fpath, path, sizeof(fpath));
|
||||||
|
match = basename(fpath);
|
||||||
/* Check if this file name matches a specific audio or video file */
|
/* Check if this file name matches a specific audio or video file */
|
||||||
if( ends_with(match, ".cover.jpg") )
|
if( ends_with(match, ".cover.jpg") )
|
||||||
{
|
{
|
||||||
@ -128,7 +135,8 @@ update_if_album_art(const char * path)
|
|||||||
/* Check if this file name matches one of the default album art names */
|
/* Check if this file name matches one of the default album art names */
|
||||||
album_art = is_album_art(match);
|
album_art = is_album_art(match);
|
||||||
|
|
||||||
dir = dirname(strdup(path));
|
strncpyt(dpath, path, sizeof(dpath));
|
||||||
|
dir = dirname(dpath);
|
||||||
dh = opendir(dir);
|
dh = opendir(dir);
|
||||||
if( !dh )
|
if( !dh )
|
||||||
return;
|
return;
|
||||||
@ -141,9 +149,8 @@ update_if_album_art(const char * path)
|
|||||||
break;
|
break;
|
||||||
case DT_LNK:
|
case DT_LNK:
|
||||||
case DT_UNKNOWN:
|
case DT_UNKNOWN:
|
||||||
asprintf(&file, "%s/%s", dir, dp->d_name);
|
snprintf(file, sizeof(file), "%s/%s", dir, dp->d_name);
|
||||||
type = resolve_unknown_type(file, ALL_MEDIA);
|
type = resolve_unknown_type(file, ALL_MEDIA);
|
||||||
free(file);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
type = TYPE_UNKNOWN;
|
type = TYPE_UNKNOWN;
|
||||||
@ -156,27 +163,25 @@ update_if_album_art(const char * path)
|
|||||||
(album_art || strncmp(dp->d_name, match, ncmp) == 0) )
|
(album_art || strncmp(dp->d_name, match, ncmp) == 0) )
|
||||||
{
|
{
|
||||||
DPRINTF(E_DEBUG, L_METADATA, "New file %s looks like cover art for %s\n", path, dp->d_name);
|
DPRINTF(E_DEBUG, L_METADATA, "New file %s looks like cover art for %s\n", path, dp->d_name);
|
||||||
asprintf(&file, "%s/%s", dir, dp->d_name);
|
snprintf(file, sizeof(file), "%s/%s", dir, dp->d_name);
|
||||||
art_id = find_album_art(file, NULL, 0);
|
art_id = find_album_art(file, NULL, 0);
|
||||||
if( sql_exec(db, "UPDATE DETAILS set ALBUM_ART = %lld where PATH = '%q'", art_id, file) != SQLITE_OK )
|
if( sql_exec(db, "UPDATE DETAILS set ALBUM_ART = %lld where PATH = '%q'", art_id, file) != SQLITE_OK )
|
||||||
DPRINTF(E_WARN, L_METADATA, "Error setting %s as cover art for %s\n", match, dp->d_name);
|
DPRINTF(E_WARN, L_METADATA, "Error setting %s as cover art for %s\n", match, dp->d_name);
|
||||||
free(file);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(dh);
|
closedir(dh);
|
||||||
|
|
||||||
free(dir);
|
free(dir);
|
||||||
free(match);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
check_embedded_art(const char * path, const char * image_data, int image_size)
|
check_embedded_art(const char *path, const char *image_data, int image_size)
|
||||||
{
|
{
|
||||||
int width = 0, height = 0;
|
int width = 0, height = 0;
|
||||||
char * art_path = NULL;
|
char *art_path = NULL;
|
||||||
char * cache_dir;
|
char *cache_dir;
|
||||||
FILE * dstfile;
|
FILE *dstfile;
|
||||||
image_s * imsrc;
|
image_s *imsrc;
|
||||||
static char last_path[PATH_MAX];
|
static char last_path[PATH_MAX];
|
||||||
static unsigned int last_hash = 0;
|
static unsigned int last_hash = 0;
|
||||||
static int last_success = 0;
|
static int last_success = 0;
|
||||||
@ -269,14 +274,28 @@ end_art:
|
|||||||
return(art_path);
|
return(art_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
static char *
|
||||||
check_for_album_file(const char * dir, const char * path)
|
check_for_album_file(const char *path)
|
||||||
{
|
{
|
||||||
char file[MAXPATHLEN];
|
char file[MAXPATHLEN];
|
||||||
struct album_art_name_s * album_art_name;
|
char mypath[MAXPATHLEN];
|
||||||
image_s * imsrc = NULL;
|
struct album_art_name_s *album_art_name;
|
||||||
|
image_s *imsrc = NULL;
|
||||||
int width=0, height=0;
|
int width=0, height=0;
|
||||||
char * art_file;
|
char *art_file;
|
||||||
|
const char *dir;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if( stat(path, &st) != 0 )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if( S_ISDIR(st.st_mode) )
|
||||||
|
{
|
||||||
|
dir = path;
|
||||||
|
goto check_dir;
|
||||||
|
}
|
||||||
|
strncpyt(mypath, path, sizeof(mypath));
|
||||||
|
dir = dirname(mypath);
|
||||||
|
|
||||||
/* First look for file-specific cover art */
|
/* First look for file-specific cover art */
|
||||||
snprintf(file, sizeof(file), "%s.cover.jpg", path);
|
snprintf(file, sizeof(file), "%s.cover.jpg", path);
|
||||||
@ -302,7 +321,7 @@ check_for_album_file(const char * dir, const char * path)
|
|||||||
if( imsrc )
|
if( imsrc )
|
||||||
goto found_file;
|
goto found_file;
|
||||||
}
|
}
|
||||||
|
check_dir:
|
||||||
/* Then fall back to possible generic cover art file names */
|
/* Then fall back to possible generic cover art file names */
|
||||||
for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
|
for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
|
||||||
{
|
{
|
||||||
@ -333,30 +352,16 @@ found_file:
|
|||||||
}
|
}
|
||||||
|
|
||||||
sqlite_int64
|
sqlite_int64
|
||||||
find_album_art(const char * path, const char * image_data, int image_size)
|
find_album_art(const char *path, const char *image_data, int image_size)
|
||||||
{
|
{
|
||||||
char * album_art = NULL;
|
char *album_art = NULL;
|
||||||
char * sql;
|
char *sql;
|
||||||
char ** result;
|
char **result;
|
||||||
int cols, rows;
|
int cols, rows;
|
||||||
sqlite_int64 ret = 0;
|
sqlite_int64 ret = 0;
|
||||||
char * mypath;
|
|
||||||
const char * dir;
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if( stat(path, &st) == 0 && S_ISDIR(st.st_mode) )
|
|
||||||
{
|
|
||||||
mypath = NULL;
|
|
||||||
dir = path;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mypath = strdup(path);
|
|
||||||
dir = dirname(mypath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( (image_size && (album_art = check_embedded_art(path, image_data, image_size))) ||
|
if( (image_size && (album_art = check_embedded_art(path, image_data, image_size))) ||
|
||||||
(album_art = check_for_album_file(dir, path)) )
|
(album_art = check_for_album_file(path)) )
|
||||||
{
|
{
|
||||||
sql = sqlite3_mprintf("SELECT ID from ALBUM_ART where PATH = '%q'", album_art ? album_art : path);
|
sql = sqlite3_mprintf("SELECT ID from ALBUM_ART where PATH = '%q'", album_art ? album_art : path);
|
||||||
if( (sql_get_table(db, sql, &result, &rows, &cols) == SQLITE_OK) && rows )
|
if( (sql_get_table(db, sql, &result, &rows, &cols) == SQLITE_OK) && rows )
|
||||||
@ -372,7 +377,6 @@ find_album_art(const char * path, const char * image_data, int image_size)
|
|||||||
sqlite3_free(sql);
|
sqlite3_free(sql);
|
||||||
}
|
}
|
||||||
free(album_art);
|
free(album_art);
|
||||||
free(mypath);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -242,38 +242,41 @@ image_get_jpeg_resolution(const char * path, int * width, int * height)
|
|||||||
unsigned char buf[8];
|
unsigned char buf[8];
|
||||||
u_int16_t offset, h, w;
|
u_int16_t offset, h, w;
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
|
size_t nread;
|
||||||
long size;
|
long size;
|
||||||
|
|
||||||
|
|
||||||
img = fopen(path, "r");
|
img = fopen(path, "r");
|
||||||
if( !img )
|
if( !img )
|
||||||
return(-1);
|
return -1;
|
||||||
|
|
||||||
fseek(img, 0, SEEK_END);
|
fseek(img, 0, SEEK_END);
|
||||||
size = ftell(img);
|
size = ftell(img);
|
||||||
rewind(img);
|
rewind(img);
|
||||||
|
|
||||||
fread(&buf, 2, 1, img);
|
nread = fread(&buf, 2, 1, img);
|
||||||
if( (buf[0] != 0xFF) || (buf[1] != 0xD8) )
|
if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) )
|
||||||
{
|
{
|
||||||
fclose(img);
|
fclose(img);
|
||||||
return(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
memset(&buf, 0, sizeof(buf));
|
memset(&buf, 0, sizeof(buf));
|
||||||
|
|
||||||
while( ftell(img) < size )
|
while( ftell(img) < size )
|
||||||
{
|
{
|
||||||
while( buf[0] != 0xFF && !feof(img) )
|
while( nread > 0 && buf[0] != 0xFF && !feof(img) )
|
||||||
fread(&buf, 1, 1, img);
|
nread = fread(&buf, 1, 1, img);
|
||||||
|
|
||||||
while( buf[0] == 0xFF && !feof(img) )
|
while( nread > 0 && buf[0] == 0xFF && !feof(img) )
|
||||||
fread(&buf, 1, 1, img);
|
nread = fread(&buf, 1, 1, img);
|
||||||
|
|
||||||
if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) )
|
if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) )
|
||||||
{
|
{
|
||||||
fread(&buf, 7, 1, img);
|
nread = fread(&buf, 7, 1, img);
|
||||||
*width = 0;
|
*width = 0;
|
||||||
*height = 0;
|
*height = 0;
|
||||||
|
if( nread < 1 )
|
||||||
|
break;
|
||||||
memcpy(&h, buf+3, 2);
|
memcpy(&h, buf+3, 2);
|
||||||
*height = SWAP16(h);
|
*height = SWAP16(h);
|
||||||
memcpy(&w, buf+5, 2);
|
memcpy(&w, buf+5, 2);
|
||||||
@ -284,7 +287,9 @@ image_get_jpeg_resolution(const char * path, int * width, int * height)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
offset = 0;
|
offset = 0;
|
||||||
fread(&buf, 2, 1, img);
|
nread = fread(&buf, 2, 1, img);
|
||||||
|
if( nread < 1 )
|
||||||
|
break;
|
||||||
memcpy(&offset, buf, 2);
|
memcpy(&offset, buf, 2);
|
||||||
offset = SWAP16(offset) - 2;
|
offset = SWAP16(offset) - 2;
|
||||||
if( fseek(img, offset, SEEK_CUR) == -1 )
|
if( fseek(img, offset, SEEK_CUR) == -1 )
|
||||||
@ -305,13 +310,14 @@ image_get_jpeg_date_xmp(const char * path, char ** date)
|
|||||||
struct NameValueParserData xml;
|
struct NameValueParserData xml;
|
||||||
char * exif;
|
char * exif;
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
|
size_t nread;
|
||||||
|
|
||||||
img = fopen(path, "r");
|
img = fopen(path, "r");
|
||||||
if( !img )
|
if( !img )
|
||||||
return(-1);
|
return(-1);
|
||||||
|
|
||||||
fread(&buf, 2, 1, img);
|
nread = fread(&buf, 2, 1, img);
|
||||||
if( (buf[0] != 0xFF) || (buf[1] != 0xD8) )
|
if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) )
|
||||||
{
|
{
|
||||||
fclose(img);
|
fclose(img);
|
||||||
return(-1);
|
return(-1);
|
||||||
@ -320,11 +326,11 @@ image_get_jpeg_date_xmp(const char * path, char ** date)
|
|||||||
|
|
||||||
while( !feof(img) )
|
while( !feof(img) )
|
||||||
{
|
{
|
||||||
while( buf[0] != 0xFF && !feof(img) )
|
while( nread > 0 && buf[0] != 0xFF && !feof(img) )
|
||||||
fread(&buf, 1, 1, img);
|
nread = fread(&buf, 1, 1, img);
|
||||||
|
|
||||||
while( buf[0] == 0xFF && !feof(img) )
|
while( nread > 0 && buf[0] == 0xFF && !feof(img) )
|
||||||
fread(&buf, 1, 1, img);
|
nread = fread(&buf, 1, 1, img);
|
||||||
|
|
||||||
if( feof(img) )
|
if( feof(img) )
|
||||||
break;
|
break;
|
||||||
@ -332,7 +338,9 @@ image_get_jpeg_date_xmp(const char * path, char ** date)
|
|||||||
if( buf[0] == 0xE1 ) // APP1 marker
|
if( buf[0] == 0xE1 ) // APP1 marker
|
||||||
{
|
{
|
||||||
offset = 0;
|
offset = 0;
|
||||||
fread(&buf, 2, 1, img);
|
nread = fread(&buf, 2, 1, img);
|
||||||
|
if( nread < 1 )
|
||||||
|
break;
|
||||||
memcpy(&offset, buf, 2);
|
memcpy(&offset, buf, 2);
|
||||||
offset = SWAP16(offset) - 2;
|
offset = SWAP16(offset) - 2;
|
||||||
|
|
||||||
@ -347,7 +355,9 @@ image_get_jpeg_date_xmp(const char * path, char ** date)
|
|||||||
break;
|
break;
|
||||||
data = newdata;
|
data = newdata;
|
||||||
|
|
||||||
fread(data, 29, 1, img);
|
nread = fread(data, 29, 1, img);
|
||||||
|
if( nread < 1 )
|
||||||
|
break;
|
||||||
offset -= 29;
|
offset -= 29;
|
||||||
if( strcmp(data, "http://ns.adobe.com/xap/1.0/") != 0 )
|
if( strcmp(data, "http://ns.adobe.com/xap/1.0/") != 0 )
|
||||||
{
|
{
|
||||||
@ -359,7 +369,9 @@ image_get_jpeg_date_xmp(const char * path, char ** date)
|
|||||||
if( !newdata )
|
if( !newdata )
|
||||||
break;
|
break;
|
||||||
data = newdata;
|
data = newdata;
|
||||||
fread(data, offset, 1, img);
|
nread = fread(data, offset, 1, img);
|
||||||
|
if( nread < 1 )
|
||||||
|
break;
|
||||||
|
|
||||||
ParseNameValue(data, offset, &xml);
|
ParseNameValue(data, offset, &xml);
|
||||||
exif = GetValueFromNameValueList(&xml, "DateTimeOriginal");
|
exif = GetValueFromNameValueList(&xml, "DateTimeOriginal");
|
||||||
@ -378,7 +390,9 @@ image_get_jpeg_date_xmp(const char * path, char ** date)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
offset = 0;
|
offset = 0;
|
||||||
fread(&buf, 2, 1, img);
|
nread = fread(&buf, 2, 1, img);
|
||||||
|
if( nread < 1 )
|
||||||
|
break;
|
||||||
memcpy(&offset, buf, 2);
|
memcpy(&offset, buf, 2);
|
||||||
offset = SWAP16(offset) - 2;
|
offset = SWAP16(offset) - 2;
|
||||||
fseek(img, offset, SEEK_CUR);
|
fseek(img, offset, SEEK_CUR);
|
||||||
|
@ -402,8 +402,7 @@ GetAudioMetadata(const char * path, char * name)
|
|||||||
if( !getenv("LANG") )
|
if( !getenv("LANG") )
|
||||||
strcpy(lang, "en_US");
|
strcpy(lang, "en_US");
|
||||||
else
|
else
|
||||||
strncpy(lang, getenv("LANG"), 5);
|
strncpyt(lang, getenv("LANG"), sizeof(lang));
|
||||||
lang[5] = '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( readtags((char *)path, &song, &file, lang, type) != 0 )
|
if( readtags((char *)path, &song, &file, lang, type) != 0 )
|
||||||
@ -595,11 +594,11 @@ GetImageMetadata(const char * path, char * name)
|
|||||||
e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_MAKE);
|
e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_MAKE);
|
||||||
if( e )
|
if( e )
|
||||||
{
|
{
|
||||||
strncpy(make, exif_entry_get_value(e, b, sizeof(b)), sizeof(make));
|
strncpyt(make, exif_entry_get_value(e, b, sizeof(b)), sizeof(make));
|
||||||
e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_MODEL);
|
e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_MODEL);
|
||||||
if( e )
|
if( e )
|
||||||
{
|
{
|
||||||
strncpy(model, exif_entry_get_value(e, b, sizeof(b)), sizeof(model));
|
strncpyt(model, exif_entry_get_value(e, b, sizeof(b)), sizeof(model));
|
||||||
if( !strcasestr(model, make) )
|
if( !strcasestr(model, make) )
|
||||||
snprintf(model, sizeof(model), "%s %s", make, exif_entry_get_value(e, b, sizeof(b)));
|
snprintf(model, sizeof(model), "%s %s", make, exif_entry_get_value(e, b, sizeof(b)));
|
||||||
m.creator = strdup(model);
|
m.creator = strdup(model);
|
||||||
|
7
utils.c
7
utils.c
@ -45,6 +45,13 @@ strcatf(struct string_s *str, const char *fmt, ...)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
strncpyt(char *dst, const char *src, size_t len)
|
||||||
|
{
|
||||||
|
strncpy(dst, src, len);
|
||||||
|
dst[len-1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ends_with(const char * haystack, const char * needle)
|
ends_with(const char * haystack, const char * needle)
|
||||||
{
|
{
|
||||||
|
3
utils.h
3
utils.h
@ -27,6 +27,9 @@
|
|||||||
int
|
int
|
||||||
strcatf(struct string_s *str, char *fmt, ...);
|
strcatf(struct string_s *str, char *fmt, ...);
|
||||||
|
|
||||||
|
void
|
||||||
|
strncpyt(char *dst, const char *src, size_t len);
|
||||||
|
|
||||||
int
|
int
|
||||||
ends_with(const char * haystack, const char * needle);
|
ends_with(const char * haystack, const char * needle);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user