libisofs/libisofs/util.c

1295 lines
30 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 openning it */
#ifndef HAVE_EACCESS
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
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;
iconv_t conv;
char *out;
char *src;
char *ret;
inbytes = strlen(str);
outbytes = (inbytes + 1) * MB_LEN_MAX;
out = alloca(outbytes);
if (out == NULL) {
return ISO_OUT_OF_MEM;
}
conv = iconv_open(ocharset, icharset);
if (conv == (iconv_t)(-1)) {
return ISO_CHARSET_CONV_ERROR;
}
src = (char *)str;
ret = (char *)out;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
if (n == -1) {
/* error */
iconv_close(conv);
return ISO_CHARSET_CONV_ERROR;
}
*ret = '\0';
iconv_close(conv);
*output = malloc(ret - out + 1);
if (*output == NULL) {
return ISO_OUT_OF_MEM;
}
memcpy(*output, out, ret - out + 1);
return ISO_SUCCESS;
}
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;
iconv_t conv;
char *out;
char *src;
char *ret;
inbytes = len;
outbytes = (inbytes + 1) * MB_LEN_MAX;
out = alloca(outbytes);
if (out == NULL) {
return ISO_OUT_OF_MEM;
}
conv = iconv_open(ocharset, icharset);
if (conv == (iconv_t)(-1)) {
return ISO_CHARSET_CONV_ERROR;
}
src = (char *)str;
ret = (char *)out;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
if (n == -1) {
/* error */
iconv_close(conv);
return ISO_CHARSET_CONV_ERROR;
}
*ret = '\0';
iconv_close(conv);
*output = malloc(ret - out + 1);
if (*output == NULL) {
return ISO_OUT_OF_MEM;
}
memcpy(*output, out, ret - out + 1);
return ISO_SUCCESS;
}
/**
* 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)
{
iconv_t conv;
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;
}
conv = iconv_open("WCHAR_T", icharset);
if (conv == (iconv_t)-1) {
return ISO_CHARSET_CONV_ERROR;
}
inbytes = strlen(input);
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;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
while (n == -1) {
if (errno == E2BIG) {
/* error, should never occur */
iconv_close(conv);
free(wstr);
return ISO_CHARSET_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;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
}
}
iconv_close(conv);
*( (wchar_t *)ret )='\0';
*output = wstr;
return ISO_SUCCESS;
}
int str2ascii(const char *icharset, const char *input, char **output)
{
int result;
wchar_t *wsrc_;
char *ret;
char *ret_;
char *src;
iconv_t conv;
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);
ret_ = malloc(numchars + 1);
if (ret_ == NULL) {
return ISO_OUT_OF_MEM;
}
outbytes = numchars;
ret = ret_;
/* initialize iconv */
conv = iconv_open("ASCII", "WCHAR_T");
if (conv == (iconv_t)-1) {
free(wsrc_);
free(ret_);
return ISO_CHARSET_CONV_ERROR;
}
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
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;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
}
iconv_close(conv);
*ret='\0';
free(wsrc_);
*output = ret_;
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_;
iconv_t conv;
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);
ret_ = malloc((numchars+1) * sizeof(uint16_t));
if (ret_ == NULL) {
return ISO_OUT_OF_MEM;
}
outbytes = numchars * sizeof(uint16_t);
ret = ret_;
/* initialize iconv */
conv = iconv_open("UCS-2BE", "WCHAR_T");
if (conv == (iconv_t)-1) {
free(wsrc_);
free(ret_);
return ISO_CHARSET_CONV_ERROR;
}
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
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;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
}
iconv_close(conv);
/* 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;
int lname, lext, lnname, lnext, pos, i;
char *dest = alloca(len + 1 + 1);
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 > 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) {
return NULL;
}
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';
return strdup(dest);
}
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;
iconv_t conv;
size_t n;
inbytes = len;
outbytes = (inbytes+1) * MB_LEN_MAX;
/* ensure enought space */
out = alloca(outbytes);
/* convert to local charset */
/* ??? ts Nov 25 2008 :
Shouldn't this go to library initialization or even to app ?
*/
setlocale(LC_CTYPE, "");
conv = iconv_open(iso_get_local_charset(0), "UCS-2BE");
if (conv == (iconv_t)(-1)) {
return NULL;
}
src = (char *)buf;
str = (char *)out;
n = iconv(conv, &src, &inbytes, &str, &outbytes);
if (n == -1) {
/* error */
iconv_close(conv);
return NULL;
}
iconv_close(conv);
*str = '\0';
/* remove trailing spaces */
for (len = strlen(out) - 1; out[len] == ' ' && len > 0; --len)
out[len] = '\0';
return strdup(out);
}
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)));
}