libisofs/libisofs/util.c

1656 lines
37 KiB
C

/*
* Copyright (c) 2007 Vreixo Formoso
* Copyright (c) 2007 Mario Danic
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "util.h"
#include "libisofs.h"
#include "../version.h"
#include <stdlib.h>
#include <wchar.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <limits.h>
#include <iconv.h>
#include <locale.h>
#include <langinfo.h>
#include <unistd.h>
/* if we don't have eaccess, we check file access by opening it */
#ifndef HAVE_EACCESS
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#ifdef Libisofs_with_iso_iconV
/* Produce possibly inflationary error messages directly to stderr */
static int iso_iconv_debug = 0;
struct iso_iconv_handle {
int status; /* bit0= open , bit1= identical mapping */
iconv_t descr;
};
/*
@param flag bit0= shortcut by identical mapping is not allowed
*/
static
int iso_iconv_open(struct iso_iconv_handle *handle,
char *tocode, char *fromcode, int flag)
{
handle->status = 0;
handle->descr = (iconv_t) -1;
if (strcmp(tocode, fromcode) == 0 && !(flag & 1)) {
handle->status = 1 | 2;
return 1;
}
handle->descr = iconv_open(tocode, fromcode);
if (handle->descr == (iconv_t) -1) {
if (strlen(tocode) + strlen(fromcode) <= 160 && iso_iconv_debug)
fprintf(stderr,
"libisofs_DEBUG: iconv_open(\"%s\", \"%s\") failed: errno= %d %s\n",
tocode, fromcode, errno, strerror(errno));
return 0;
}
handle->status = 1;
return 1;
}
static
size_t iso_iconv(struct iso_iconv_handle *handle,
char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft, int flag)
{
size_t ret;
if (!(handle->status & 1)) {
if (iso_iconv_debug)
fprintf(stderr,
"libisofs_DEBUG: iso_iconv(): iso_iconv_handle not in open state\n");
return (size_t) -1;
}
if (handle->status & 2) {
if (inbuf == NULL || outbuf == NULL) {
null_buf:;
if (iso_iconv_debug)
fprintf(stderr,
"libisofs_DEBUG: iso_iconv(): NULL buffers not allowed in shortcut mapping\n");
return (size_t) -1;
}
if (*inbuf == NULL || *outbuf == NULL)
goto null_buf;
while (*inbytesleft > 0 && *outbytesleft > 0) {
*((*outbuf)++) = *((*inbuf)++);
(*inbytesleft)--;
(*outbytesleft)--;
}
if (*inbytesleft > 0 && *outbytesleft <= 0)
return (size_t) -1;
return (size_t) 0;
}
ret = iconv(handle->descr, inbuf, inbytesleft, outbuf, outbytesleft);
if (ret == (size_t) -1) {
if (iso_iconv_debug)
fprintf(stderr, "libisofs_DEBUG: iconv() failed: errno= %d %s\n",
errno, strerror(errno));
return (size_t) -1;
}
return ret;
}
static
int iso_iconv_close(struct iso_iconv_handle *handle, int flag)
{
int ret;
if (!(handle->status & 1)) {
if (iso_iconv_debug)
fprintf(stderr,
"libisofs_DEBUG: iso_iconv_close(): iso_iconv_handle not in open state\n");
return -1;
}
handle->status &= ~1;
if (handle->status & 2)
return 0;
ret = iconv_close(handle->descr);
if (ret == -1) {
if (iso_iconv_debug)
fprintf(stderr,
"libisofs_DEBUG: iconv_close() failed: errno= %d %s\n",
errno, strerror(errno));
return -1;
}
return ret;
}
#endif /* Libisofs_with_iso_iconV */
int int_pow(int base, int power)
{
int result = 1;
while (--power >= 0) {
result *= base;
}
return result;
}
/* This static variable can override the locale's charset by its getter
function which should be used whenever the local character set name
is to be inquired. I.e. instead of calling nl_langinfo(CODESET) directly.
If the variable is empty then it forwards nl_langinfo(CODESET).
*/
static char libisofs_local_charset[4096]= {""};
/* API function */
int iso_set_local_charset(char *name, int flag)
{
if(strlen(name) >= sizeof(libisofs_local_charset))
return(0);
strcpy(libisofs_local_charset, name);
return 1;
}
/* API function */
char *iso_get_local_charset(int flag)
{
if(libisofs_local_charset[0])
return libisofs_local_charset;
return nl_langinfo(CODESET);
}
int strconv(const char *str, const char *icharset, const char *ocharset,
char **output)
{
size_t inbytes;
size_t outbytes;
size_t n;
#ifdef Libisofs_with_iso_iconV
struct iso_iconv_handle conv;
int conv_ret;
#else
iconv_t conv;
#endif
char *out = NULL;
char *src;
char *ret;
int retval;
inbytes = strlen(str);
outbytes = (inbytes + 1) * MB_LEN_MAX;
out = calloc(outbytes, 1);
if (out == NULL) {
retval = ISO_OUT_OF_MEM;
goto ex;
}
#ifdef Libisofs_with_iso_iconV
conv_ret = iso_iconv_open(&conv, (char *) ocharset, (char *) icharset, 0);
if (conv_ret <= 0) {
#else
conv = iconv_open(ocharset, icharset);
if (conv == (iconv_t)(-1)) {
#endif
retval = ISO_CHARSET_CONV_ERROR;
goto ex;
}
src = (char *)str;
ret = (char *)out;
#ifdef Libisofs_with_iso_iconV
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0);
if (n == -1) {
/* error */
iso_iconv_close(&conv, 0);
#else
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
if (n == -1) {
/* error */
iconv_close(conv);
#endif
retval = ISO_CHARSET_CONV_ERROR;
goto ex;
}
*ret = '\0';
#ifdef Libisofs_with_iso_iconV
iso_iconv_close(&conv, 0);
#else
iconv_close(conv);
#endif
*output = malloc(ret - out + 1);
if (*output == NULL) {
retval = ISO_OUT_OF_MEM;
goto ex;
}
memcpy(*output, out, ret - out + 1);
retval = ISO_SUCCESS;
ex:;
if (out != NULL)
free(out);
return retval;
}
int strnconv(const char *str, const char *icharset, const char *ocharset,
size_t len, char **output)
{
size_t inbytes;
size_t outbytes;
size_t n;
#ifdef Libisofs_with_iso_iconV
struct iso_iconv_handle conv;
int conv_ret;
#else
iconv_t conv;
#endif
char *out = NULL;
char *src;
char *ret;
int retval;
inbytes = len;
outbytes = (inbytes + 1) * MB_LEN_MAX;
out = calloc(outbytes, 1);
if (out == NULL) {
retval = ISO_OUT_OF_MEM;
goto ex;
}
#ifdef Libisofs_with_iso_iconV
conv_ret = iso_iconv_open(&conv, (char *) ocharset, (char *) icharset, 0);
if (conv_ret <= 0) {
#else
conv = iconv_open(ocharset, icharset);
if (conv == (iconv_t)(-1)) {
#endif
retval = ISO_CHARSET_CONV_ERROR;
goto ex;
}
src = (char *)str;
ret = (char *)out;
#ifdef Libisofs_with_iso_iconV
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0);
if (n == -1) {
/* error */
iso_iconv_close(&conv, 0);
#else
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
if (n == -1) {
/* error */
iconv_close(conv);
#endif
retval = ISO_CHARSET_CONV_ERROR;
goto ex;
}
*ret = '\0';
#ifdef Libisofs_with_iso_iconV
iso_iconv_close(&conv, 0);
#else
iconv_close(conv);
#endif
*output = malloc(ret - out + 1);
if (*output == NULL) {
retval = ISO_OUT_OF_MEM;
goto ex;
}
memcpy(*output, out, ret - out + 1);
retval = ISO_SUCCESS;
ex:;
if (out != NULL)
free(out);
return retval;
}
/**
* Convert a str in a specified codeset to WCHAR_T.
* The result must be free() when no more needed
*
* @return
* 1 success, < 0 error
*/
static
int str2wchar(const char *icharset, const char *input, wchar_t **output)
{
#ifdef Libisofs_with_iso_iconV
struct iso_iconv_handle conv;
int conv_ret;
#else
iconv_t conv;
#endif
/* That while loop smells like a potential show stopper */
size_t loop_counter = 0, loop_limit = 3;
size_t inbytes;
size_t outbytes;
char *ret;
char *src;
wchar_t *wstr;
size_t n;
if (icharset == NULL || input == NULL || output == NULL) {
return ISO_NULL_POINTER;
}
#ifdef Libisofs_with_iso_iconV
conv_ret = iso_iconv_open(&conv, "WCHAR_T", (char *) icharset, 0);
if (conv_ret <= 0) {
#else
conv = iconv_open("WCHAR_T", icharset);
if (conv == (iconv_t)-1) {
#endif
return ISO_CHARSET_CONV_ERROR;
}
inbytes = strlen(input);
loop_limit = inbytes + 3;
outbytes = (inbytes + 1) * sizeof(wchar_t);
/* we are sure that numchars <= inbytes */
wstr = malloc(outbytes);
if (wstr == NULL) {
return ISO_OUT_OF_MEM;
}
ret = (char *)wstr;
src = (char *)input;
#ifdef Libisofs_with_iso_iconV
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0);
#else
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
#endif
while (n == -1) {
if (errno == E2BIG) {
/* error, should never occur */
goto conv_error;
} else {
wchar_t *wret;
/*
* Invalid input string charset.
* This can happen if input is in fact encoded in a charset
* different than icharset.
* We can't do anything better than replace by "_" and continue.
*/
inbytes--;
src++;
wret = (wchar_t*) ret;
*wret++ = (wchar_t) '_';
ret = (char *) wret;
outbytes -= sizeof(wchar_t);
if (!inbytes)
break;
/* Just to appease my remorse about unclear loop ends */
loop_counter++;
if (loop_counter > loop_limit)
goto conv_error;
#ifdef Libisofs_with_iso_iconV
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0);
#else
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
#endif
}
}
#ifdef Libisofs_with_iso_iconV
iso_iconv_close(&conv, 0);
#else
iconv_close(conv);
#endif
*( (wchar_t *)ret )='\0';
*output = wstr;
return ISO_SUCCESS;
conv_error:;
#ifdef Libisofs_with_iso_iconV
iso_iconv_close(&conv, 0);
#else
iconv_close(conv);
#endif
free(wstr);
return ISO_CHARSET_CONV_ERROR;
}
int str2ascii(const char *icharset, const char *input, char **output)
{
int result;
wchar_t *wsrc_;
char *ret;
char *ret_;
char *src;
#ifdef Libisofs_with_iso_iconV
struct iso_iconv_handle conv;
int conv_ret;
#else
iconv_t conv;
#endif
/* That while loop smells like a potential show stopper */
size_t loop_counter = 0, loop_limit = 3;
/* Fallback in case that iconv() is too demanding for system */
unsigned char *cpt;
size_t numchars;
size_t outbytes;
size_t inbytes;
size_t n;
if (icharset == NULL || input == NULL || output == NULL) {
return ISO_NULL_POINTER;
}
/* convert the string to a wide character string. Note: outbytes
* is in fact the number of characters in the string and doesn't
* include the last NULL character.
*/
result = str2wchar(icharset, input, &wsrc_);
if (result < 0) {
goto fallback;
}
src = (char *)wsrc_;
numchars = wcslen(wsrc_);
inbytes = numchars * sizeof(wchar_t);
loop_limit = inbytes + 3;
ret_ = malloc(numchars + 1);
if (ret_ == NULL) {
return ISO_OUT_OF_MEM;
}
outbytes = numchars;
ret = ret_;
/* initialize iconv */
#ifdef Libisofs_with_iso_iconV
conv_ret = iso_iconv_open(&conv, "ASCII", "WCHAR_T", 0);
if (conv_ret <= 0) {
#else
conv = iconv_open("ASCII", "WCHAR_T");
if (conv == (iconv_t)-1) {
#endif
free(wsrc_);
free(ret_);
goto fallback;
}
#ifdef Libisofs_with_iso_iconV
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0);
#else
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
#endif
while (n == -1) {
/* The destination buffer is too small. Stops here. */
if (errno == E2BIG)
break;
/* An incomplete multi bytes sequence was found. We
* can't do anything here. That's quite unlikely. */
if (errno == EINVAL)
break;
/* The last possible error is an invalid multi bytes
* sequence. Just replace the character with a "_".
* Probably the character doesn't exist in ascii like
* "é, è, à, ç, ..." in French. */
*ret++ = '_';
outbytes--;
if (!outbytes)
break;
/* There was an error with one character but some other remain
* to be converted. That's probably a multibyte character.
* See above comment. */
src += sizeof(wchar_t);
inbytes -= sizeof(wchar_t);
if (!inbytes)
break;
/* Just to appease my remorse about unclear loop ends */
loop_counter++;
if (loop_counter > loop_limit)
break;
#ifdef Libisofs_with_iso_iconV
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0);
#else
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
#endif
}
#ifdef Libisofs_with_iso_iconV
iso_iconv_close(&conv, 0);
#else
iconv_close(conv);
#endif
*ret='\0';
free(wsrc_);
*output = ret_;
return ISO_SUCCESS;
fallback:;
/* Assume to have a single byte charset with ASCII as core.
Anything suspicious will be mapped to '_'.
*/
*output = strdup(input);
for (cpt = (unsigned char *) *output; *cpt; cpt++) {
if (*cpt < 32 || *cpt > 126)
*cpt = '_';
}
return ISO_SUCCESS;
}
static
void set_ucsbe(uint16_t *ucs, char c)
{
char *v = (char*)ucs;
v[0] = (char)0;
v[1] = c;
}
/**
* @return
* -1, 0, 1 if *ucs <, == or > than c
*/
static
int cmp_ucsbe(const uint16_t *ucs, char c)
{
char *v = (char*)ucs;
if (v[0] != 0) {
return 1;
} else if (v[1] == c) {
return 0;
} else {
return (uint8_t)c > (uint8_t)v[1] ? -1 : 1;
}
}
int str2ucs(const char *icharset, const char *input, uint16_t **output)
{
int result;
wchar_t *wsrc_;
char *src;
char *ret;
char *ret_;
#ifdef Libisofs_with_iso_iconV
struct iso_iconv_handle conv;
int conv_ret;
#else
iconv_t conv;
#endif
/* That while loop smells like a potential show stopper */
size_t loop_counter = 0, loop_limit = 3;
size_t numchars;
size_t outbytes;
size_t inbytes;
size_t n;
if (icharset == NULL || input == NULL || output == NULL) {
return ISO_NULL_POINTER;
}
/* convert the string to a wide character string. Note: outbytes
* is in fact the number of characters in the string and doesn't
* include the last NULL character.
*/
result = str2wchar(icharset, input, &wsrc_);
if (result < 0) {
return result;
}
src = (char *)wsrc_;
numchars = wcslen(wsrc_);
inbytes = numchars * sizeof(wchar_t);
loop_limit = inbytes + 3;
ret_ = malloc((numchars+1) * sizeof(uint16_t));
if (ret_ == NULL) {
return ISO_OUT_OF_MEM;
}
outbytes = numchars * sizeof(uint16_t);
ret = ret_;
/* initialize iconv */
#ifdef Libisofs_with_iso_iconV
conv_ret = iso_iconv_open(&conv, "UCS-2BE", "WCHAR_T", 0);
if (conv_ret <= 0) {
#else
conv = iconv_open("UCS-2BE", "WCHAR_T");
if (conv == (iconv_t)-1) {
#endif
free(wsrc_);
free(ret_);
return ISO_CHARSET_CONV_ERROR;
}
#ifdef Libisofs_with_iso_iconV
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0);
#else
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
#endif
while (n == -1) {
/* The destination buffer is too small. Stops here. */
if (errno == E2BIG)
break;
/* An incomplete multi bytes sequence was found. We
* can't do anything here. That's quite unlikely. */
if (errno == EINVAL)
break;
/* The last possible error is an invalid multi bytes
* sequence. Just replace the character with a "_".
* Probably the character doesn't exist in UCS */
set_ucsbe((uint16_t*) ret, '_');
ret += sizeof(uint16_t);
outbytes -= sizeof(uint16_t);
if (!outbytes)
break;
/* There was an error with one character but some other remain
* to be converted. That's probably a multibyte character.
* See above comment. */
src += sizeof(wchar_t);
inbytes -= sizeof(wchar_t);
if (!inbytes)
break;
/* Just to appease my remorse about unclear loop ends */
loop_counter++;
if (loop_counter > loop_limit)
break;
#ifdef Libisofs_with_iso_iconV
n = iso_iconv(&conv, &src, &inbytes, &ret, &outbytes, 0);
#else
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
#endif
}
#ifdef Libisofs_with_iso_iconV
iso_iconv_close(&conv, 0);
#else
iconv_close(conv);
#endif
/* close the ucs string */
set_ucsbe((uint16_t*) ret, '\0');
free(wsrc_);
*output = (uint16_t*)ret_;
return ISO_SUCCESS;
}
static int valid_d_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_');
}
static int valid_a_char(char c)
{
return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?') ||
(c >= 'A' && c <= 'Z') || (c == '_');
}
static int valid_j_char(uint16_t c)
{
return cmp_ucsbe(&c, ' ') != -1 && cmp_ucsbe(&c, '*') && cmp_ucsbe(&c, '/')
&& cmp_ucsbe(&c, ':') && cmp_ucsbe(&c, ';') && cmp_ucsbe(&c, '?')
&& cmp_ucsbe(&c, '\\');
}
static
char *iso_dirid(const char *src, int size)
{
size_t len, i;
char name[32];
len = strlen(src);
if (len > size) {
len = size;
}
for (i = 0; i < len; i++) {
char c= toupper(src[i]);
name[i] = valid_d_char(c) ? c : '_';
}
name[len] = '\0';
return strdup(name);
}
char *iso_1_dirid(const char *src)
{
return iso_dirid(src, 8);
}
char *iso_2_dirid(const char *src)
{
return iso_dirid(src, 31);
}
char *iso_1_fileid(const char *src)
{
char *dot; /* Position of the last dot in the filename, will be used
* to calculate lname and lext. */
int lname, lext, pos, i;
char dest[13]; /* 13 = 8 (name) + 1 (.) + 3 (ext) + 1 (\0) */
if (src == NULL) {
return NULL;
}
dot = strrchr(src, '.');
lext = dot ? strlen(dot + 1) : 0;
lname = strlen(src) - lext - (dot ? 1 : 0);
/* If we can't build a filename, return NULL. */
if (lname == 0 && lext == 0) {
return NULL;
}
pos = 0;
/* Convert up to 8 characters of the filename. */
for (i = 0; i < lname && i < 8; i++) {
char c= toupper(src[i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
/* This dot is mandatory, even if there is no extension. */
dest[pos++] = '.';
/* Convert up to 3 characters of the extension, if any. */
for (i = 0; i < lext && i < 3; i++) {
char c= toupper(src[lname + 1 + i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
dest[pos] = '\0';
return strdup(dest);
}
char *iso_2_fileid(const char *src)
{
char *dot;
int lname, lext, lnname, lnext, pos, i;
char dest[32]; /* 32 = 30 (name + ext) + 1 (.) + 1 (\0) */
if (src == NULL) {
return NULL;
}
dot = strrchr(src, '.');
/*
* Since the maximum length can be divided freely over the name and
* extension, we need to calculate their new lengths (lnname and
* lnext). If the original filename is too long, we start by trimming
* the extension, but keep a minimum extension length of 3.
*/
if (dot == NULL || *(dot + 1) == '\0') {
lname = strlen(src);
lnname = (lname > 30) ? 30 : lname;
lext = lnext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
lnext = (strlen(src) > 31 && lext > 3) ? (lname < 27 ? 30 - lname : 3)
: lext;
lnname = (strlen(src) > 31) ? 30 - lnext : lname;
}
if (lnname == 0 && lnext == 0) {
return NULL;
}
pos = 0;
/* Convert up to lnname characters of the filename. */
for (i = 0; i < lnname; i++) {
char c= toupper(src[i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
dest[pos++] = '.';
/* Convert up to lnext characters of the extension, if any. */
for (i = 0; i < lnext; i++) {
char c= toupper(src[lname + 1 + i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
dest[pos] = '\0';
return strdup(dest);
}
/**
* Create a dir name suitable for an ISO image with relaxed constraints.
*
* @param size
* Max len for the name
* @param relaxed
* 0 only allow d-characters, 1 allow also lowe case chars,
* 2 allow all characters
*/
char *iso_r_dirid(const char *src, int size, int relaxed)
{
size_t len, i;
char *dest;
len = strlen(src);
if (len > size) {
len = size;
}
dest = malloc(len + 1);
for (i = 0; i < len; i++) {
char c= src[i];
if (relaxed == 2) {
/* all chars are allowed */
dest[i] = c;
} else if (valid_d_char(c)) {
/* it is a valid char */
dest[i] = c;
} else {
c= toupper(src[i]);
if (valid_d_char(c)) {
if (relaxed) {
/* lower chars are allowed */
dest[i] = src[i];
} else {
dest[i] = c;
}
} else {
dest[i] = '_';
}
}
}
dest[len] = '\0';
return dest;
}
/**
* Create a file name suitable for an ISO image with relaxed constraints.
*
* @param len
* Max len for the name, without taken the "." into account.
* @param relaxed
* 0 only allow d-characters, 1 allow also lowe case chars,
* 2 allow all characters
* @param forcedot
* Whether to ensure that "." is added
*/
char *iso_r_fileid(const char *src, size_t len, int relaxed, int forcedot)
{
char *dot, *retval = NULL;
int lname, lext, lnname, lnext, pos, i;
char *dest = NULL;
dest = calloc(len + 1 + 1, 1);
if (dest == NULL)
goto ex;
if (src == NULL) {
goto ex;
}
dot = strrchr(src, '.');
/*
* Since the maximum length can be divided freely over the name and
* extension, we need to calculate their new lengths (lnname and
* lnext). If the original filename is too long, we start by trimming
* the extension, but keep a minimum extension length of 3.
*/
if (dot == NULL || *(dot + 1) == '\0') {
lname = strlen(src);
lnname = (lname > len) ? len : lname;
lext = lnext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
lnext = (strlen(src) > len + 1 && lext > 3) ?
(lname < len - 3 ? len - lname : 3)
: lext;
lnname = (strlen(src) > len + 1) ? len - lnext : lname;
}
if (lnname == 0 && lnext == 0) {
goto ex;
}
pos = 0;
/* Convert up to lnname characters of the filename. */
for (i = 0; i < lnname; i++) {
char c= src[i];
if (relaxed == 2) {
/* all chars are allowed */
dest[pos++] = c;
} else if (valid_d_char(c)) {
/* it is a valid char */
dest[pos++] = c;
} else {
c= toupper(src[i]);
if (valid_d_char(c)) {
if (relaxed) {
/* lower chars are allowed */
dest[pos++] = src[i];
} else {
dest[pos++] = c;
}
} else {
dest[pos++] = '_';
}
}
}
if (lnext > 0 || forcedot) {
dest[pos++] = '.';
}
/* Convert up to lnext characters of the extension, if any. */
for (i = lname + 1; i < lname + 1 + lnext; i++) {
char c= src[i];
if (relaxed == 2) {
/* all chars are allowed */
dest[pos++] = c;
} else if (valid_d_char(c)) {
/* it is a valid char */
dest[pos++] = c;
} else {
c= toupper(src[i]);
if (valid_d_char(c)) {
if (relaxed) {
/* lower chars are allowed */
dest[pos++] = src[i];
} else {
dest[pos++] = c;
}
} else {
dest[pos++] = '_';
}
}
}
dest[pos] = '\0';
retval = strdup(dest);
ex:;
if (dest != NULL)
free(dest);
return retval;
}
uint16_t *iso_j_file_id(const uint16_t *src)
{
uint16_t *dot;
size_t lname, lext, lnname, lnext, pos, i;
uint16_t dest[66]; /* 66 = 64 (name + ext) + 1 (.) + 1 (\0) */
if (src == NULL) {
return NULL;
}
dot = ucsrchr(src, '.');
/*
* Since the maximum length can be divided freely over the name and
* extension, we need to calculate their new lengths (lnname and
* lnext). If the original filename is too long, we start by trimming
* the extension, but keep a minimum extension length of 3.
*/
if (dot == NULL || cmp_ucsbe(dot + 1, '\0') == 0) {
lname = ucslen(src);
lnname = (lname > 64) ? 64 : lname;
lext = lnext = 0;
} else {
lext = ucslen(dot + 1);
lname = ucslen(src) - lext - 1;
lnext = (ucslen(src) > 65 && lext > 3) ? (lname < 61 ? 64 - lname : 3)
: lext;
lnname = (ucslen(src) > 65) ? 64 - lnext : lname;
}
if (lnname == 0 && lnext == 0) {
return NULL;
}
pos = 0;
/* Convert up to lnname characters of the filename. */
for (i = 0; i < lnname; i++) {
uint16_t c = src[i];
if (valid_j_char(c)) {
dest[pos++] = c;
} else {
set_ucsbe(dest + pos, '_');
pos++;
}
}
set_ucsbe(dest + pos, '.');
pos++;
/* Convert up to lnext characters of the extension, if any. */
for (i = 0; i < lnext; i++) {
uint16_t c = src[lname + 1 + i];
if (valid_j_char(c)) {
dest[pos++] = c;
} else {
set_ucsbe(dest + pos, '_');
pos++;
}
}
set_ucsbe(dest + pos, '\0');
return ucsdup(dest);
}
uint16_t *iso_j_dir_id(const uint16_t *src)
{
size_t len, i;
uint16_t dest[65]; /* 65 = 64 + 1 (\0) */
if (src == NULL) {
return NULL;
}
len = ucslen(src);
if (len > 64) {
len = 64;
}
for (i = 0; i < len; i++) {
uint16_t c = src[i];
if (valid_j_char(c)) {
dest[i] = c;
} else {
set_ucsbe(dest + i, '_');
}
}
set_ucsbe(dest + len, '\0');
return ucsdup(dest);
}
size_t ucslen(const uint16_t *str)
{
size_t i;
for (i = 0; str[i]; i++)
;
return i;
}
uint16_t *ucsrchr(const uint16_t *str, char c)
{
size_t len = ucslen(str);
while (len-- > 0) {
if (cmp_ucsbe(str + len, c) == 0) {
return (uint16_t*)(str + len);
}
}
return NULL;
}
uint16_t *ucsdup(const uint16_t *str)
{
uint16_t *ret;
size_t len = ucslen(str);
ret = malloc(2 * (len + 1));
if (ret != NULL) {
memcpy(ret, str, 2 * (len + 1));
}
return ret;
}
/**
* Although each character is 2 bytes, we actually compare byte-by-byte
* (thats what the spec says).
*/
int ucscmp(const uint16_t *s1, const uint16_t *s2)
{
const char *s = (const char*)s1;
const char *t = (const char*)s2;
size_t len1 = ucslen(s1);
size_t len2 = ucslen(s2);
size_t i, len = MIN(len1, len2) * 2;
for (i = 0; i < len; i++) {
if (s[i] < t[i]) {
return -1;
} else if (s[i] > t[i]) {
return 1;
}
}
if (len1 < len2)
return -1;
else if (len1 > len2)
return 1;
return 0;
}
uint16_t *ucscpy(uint16_t *dest, const uint16_t *src)
{
size_t n = ucslen(src) + 1;
memcpy(dest, src, n*2);
return dest;
}
uint16_t *ucsncpy(uint16_t *dest, const uint16_t *src, size_t n)
{
n = MIN(n, ucslen(src) + 1);
memcpy(dest, src, n*2);
return dest;
}
int str2d_char(const char *icharset, const char *input, char **output)
{
int ret;
char *ascii;
size_t len, i;
if (output == NULL) {
return ISO_OUT_OF_MEM;
}
/** allow NULL input */
if (input == NULL) {
*output = NULL;
return 0;
}
/* this checks for NULL parameters */
ret = str2ascii(icharset, input, &ascii);
if (ret < 0) {
*output = NULL;
return ret;
}
len = strlen(ascii);
for (i = 0; i < len; ++i) {
char c= toupper(ascii[i]);
ascii[i] = valid_d_char(c) ? c : '_';
}
*output = ascii;
return ISO_SUCCESS;
}
int str2a_char(const char *icharset, const char *input, char **output)
{
int ret;
char *ascii;
size_t len, i;
if (output == NULL) {
return ISO_OUT_OF_MEM;
}
/** allow NULL input */
if (input == NULL) {
*output = NULL;
return 0;
}
/* this checks for NULL parameters */
ret = str2ascii(icharset, input, &ascii);
if (ret < 0) {
*output = NULL;
return ret;
}
len = strlen(ascii);
for (i = 0; i < len; ++i) {
char c= toupper(ascii[i]);
ascii[i] = valid_a_char(c) ? c : '_';
}
*output = ascii;
return ISO_SUCCESS;
}
void iso_lsb(uint8_t *buf, uint32_t num, int bytes)
{
int i;
for (i = 0; i < bytes; ++i)
buf[i] = (num >> (8 * i)) & 0xff;
}
void iso_msb(uint8_t *buf, uint32_t num, int bytes)
{
int i;
for (i = 0; i < bytes; ++i)
buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff;
}
void iso_bb(uint8_t *buf, uint32_t num, int bytes)
{
iso_lsb(buf, num, bytes);
iso_msb(buf+bytes, num, bytes);
}
uint32_t iso_read_lsb(const uint8_t *buf, int bytes)
{
int i;
uint32_t ret = 0;
for (i=0; i<bytes; i++) {
ret += ((uint32_t) buf[i]) << (i*8);
}
return ret;
}
uint32_t iso_read_msb(const uint8_t *buf, int bytes)
{
int i;
uint32_t ret = 0;
for (i=0; i<bytes; i++) {
ret += ((uint32_t) buf[bytes-i-1]) << (i*8);
}
return ret;
}
uint32_t iso_read_bb(const uint8_t *buf, int bytes, int *error)
{
uint32_t v1 = iso_read_lsb(buf, bytes);
if (error) {
uint32_t v2 = iso_read_msb(buf + bytes, bytes);
if (v1 != v2)
*error = 1;
}
return v1;
}
void iso_datetime_7(unsigned char *buf, time_t t, int always_gmt)
{
static int tzsetup = 0;
int tzoffset;
struct tm tm;
if (!tzsetup) {
tzset();
tzsetup = 1;
}
memset(&tm, 0, sizeof(tm));
tm.tm_isdst = -1; /* some Linuxes change tm_isdst only if it is -1 */
localtime_r(&t, &tm);
#ifdef HAVE_TM_GMTOFF
tzoffset = tm.tm_gmtoff / 60 / 15;
#else
if (tm.tm_isdst < 0)
tm.tm_isdst = 0;
tzoffset = ( - timezone / 60 / 15 ) + 4 * tm.tm_isdst;
#endif
if (tzoffset > 52 || tzoffset < -48 || always_gmt) {
/* absurd timezone offset, represent time in GMT */
gmtime_r(&t, &tm);
tzoffset = 0;
}
buf[0] = tm.tm_year;
buf[1] = tm.tm_mon + 1;
buf[2] = tm.tm_mday;
buf[3] = tm.tm_hour;
buf[4] = tm.tm_min;
buf[5] = tm.tm_sec;
buf[6] = tzoffset;
}
void iso_datetime_17(unsigned char *buf, time_t t, int always_gmt)
{
static int tzsetup = 0;
static int tzoffset;
struct tm tm;
if (t == (time_t) - 1) {
/* unspecified time */
memset(buf, '0', 16);
buf[16] = 0;
return;
}
if (!tzsetup) {
tzset();
tzsetup = 1;
}
memset(&tm, 0, sizeof(tm));
tm.tm_isdst = -1; /* some Linuxes change tm_isdst only if it is -1 */
localtime_r(&t, &tm);
localtime_r(&t, &tm);
#ifdef HAVE_TM_GMTOFF
tzoffset = tm.tm_gmtoff / 60 / 15;
#else
if (tm.tm_isdst < 0)
tm.tm_isdst = 0;
tzoffset = ( - timezone / 60 / 15 ) + 4 * tm.tm_isdst;
#endif
if (tzoffset > 52 || tzoffset < -48 || always_gmt) {
/* absurd timezone offset, represent time in GMT */
gmtime_r(&t, &tm);
tzoffset = 0;
}
sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900);
sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1);
sprintf((char*)&buf[6], "%02d", tm.tm_mday);
sprintf((char*)&buf[8], "%02d", tm.tm_hour);
sprintf((char*)&buf[10], "%02d", tm.tm_min);
sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec));
memcpy(&buf[14], "00", 2);
buf[16] = tzoffset;
}
#ifndef HAVE_TIMEGM
static
time_t timegm(struct tm *tm)
{
time_t ret;
char *tz;
tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
}
#endif
time_t iso_datetime_read_7(const uint8_t *buf)
{
struct tm tm;
tm.tm_year = buf[0];
tm.tm_mon = buf[1] - 1;
tm.tm_mday = buf[2];
tm.tm_hour = buf[3];
tm.tm_min = buf[4];
tm.tm_sec = buf[5];
return timegm(&tm) - ((int8_t)buf[6]) * 60 * 15;
}
time_t iso_datetime_read_17(const uint8_t *buf)
{
struct tm tm;
sscanf((char*)&buf[0], "%4d", &tm.tm_year);
sscanf((char*)&buf[4], "%2d", &tm.tm_mon);
sscanf((char*)&buf[6], "%2d", &tm.tm_mday);
sscanf((char*)&buf[8], "%2d", &tm.tm_hour);
sscanf((char*)&buf[10], "%2d", &tm.tm_min);
sscanf((char*)&buf[12], "%2d", &tm.tm_sec);
tm.tm_year -= 1900;
tm.tm_mon -= 1;
return timegm(&tm) - ((int8_t)buf[6]) * 60 * 15;
}
/**
* Check whether the caller process has read access to the given local file.
*
* @return
* 1 on success (i.e, the process has read access), < 0 on error
* (including ISO_FILE_ACCESS_DENIED on access denied to the specified file
* or any directory on the path).
*/
int iso_eaccess(const char *path)
{
int access;
/* use non standard eaccess when available, open() otherwise */
#ifdef HAVE_EACCESS
access = !eaccess(path, R_OK);
#else
int fd = open(path, O_RDONLY);
if (fd != -1) {
close(fd);
access = 1;
} else {
access = 0;
}
#endif
if (!access) {
int err;
/* error, choose an appropriate return code */
switch (errno) {
case EACCES:
err = ISO_FILE_ACCESS_DENIED;
break;
case ENOTDIR:
case ENAMETOOLONG:
case ELOOP:
err = ISO_FILE_BAD_PATH;
break;
case ENOENT:
err = ISO_FILE_DOESNT_EXIST;
break;
case EFAULT:
case ENOMEM:
err = ISO_OUT_OF_MEM;
break;
default:
err = ISO_FILE_ERROR;
break;
}
return err;
}
return ISO_SUCCESS;
}
char *strcopy(const char *buf, size_t len)
{
char *str;
str = malloc((len + 1) * sizeof(char));
if (str == NULL) {
return NULL;
}
strncpy(str, buf, len);
str[len] = '\0';
/* remove trailing spaces */
for (len = len-1; str[len] == ' ' && len > 0; --len)
str[len] = '\0';
return str;
}
/**
* Copy up to \p max characters from \p src to \p dest. If \p src has less than
* \p max characters, we pad dest with " " characters.
*/
void strncpy_pad(char *dest, const char *src, size_t max)
{
size_t len, i;
if (src != NULL) {
len = MIN(strlen(src), max);
} else {
len = 0;
}
for (i = 0; i < len; ++i)
dest[i] = src[i];
for (i = len; i < max; ++i)
dest[i] = ' ';
}
char *ucs2str(const char *buf, size_t len)
{
size_t outbytes, inbytes;
char *str, *src, *out = NULL, *retval = NULL;
#ifdef Libisofs_with_iso_iconV
struct iso_iconv_handle conv;
int conv_ret;
#else
iconv_t conv;
#endif
size_t n;
inbytes = len;
outbytes = (inbytes+1) * MB_LEN_MAX;
/* ensure enought space */
out = calloc(outbytes, 1);
/* convert to local charset */
#ifdef Libisofs_with_iso_iconV
conv_ret = iso_iconv_open(&conv, iso_get_local_charset(0), "UCS-2BE", 0);
if (conv_ret <= 0) {
#else
conv = iconv_open(iso_get_local_charset(0), "UCS-2BE");
if (conv == (iconv_t)(-1)) {
#endif
goto ex;
}
src = (char *)buf;
str = (char *)out;
#ifdef Libisofs_with_iso_iconV
n = iso_iconv(&conv, &src, &inbytes, &str, &outbytes, 0);
iso_iconv_close(&conv, 0);
#else
n = iconv(conv, &src, &inbytes, &str, &outbytes);
iconv_close(conv);
#endif
if (n == -1) {
/* error */
goto ex;
}
*str = '\0';
/* remove trailing spaces */
for (len = strlen(out) - 1; out[len] == ' ' && len > 0; --len)
out[len] = '\0';
retval = strdup(out);
ex:;
if (out != NULL)
free(out);
return retval;
}
void iso_lib_version(int *major, int *minor, int *micro)
{
*major = LIBISOFS_MAJOR_VERSION;
*minor = LIBISOFS_MINOR_VERSION;
*micro = LIBISOFS_MICRO_VERSION;
}
int iso_lib_is_compatible(int major, int minor, int micro)
{
int cmajor, cminor, cmicro;
/* for now, the rule is that library is compitable if requested
* version is lower */
iso_lib_version(&cmajor, &cminor, &cmicro);
return cmajor > major
|| (cmajor == major
&& (cminor > minor
|| (cminor == minor && cmicro >= micro)));
}
int iso_init_locale(int flag)
{
setlocale(LC_CTYPE, "");
return 1;
}