libisofs-legacy/libisofs/util.c

1265 lines
29 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 <iconv.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <limits.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;
}
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 */
setlocale(LC_CTYPE, "");
conv = iconv_open(nl_langinfo(CODESET), "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)));
}