libburn/libisofs/util.c
2006-08-15 20:37:04 +00:00

711 lines
14 KiB
C
Executable File

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#include <ctype.h>
#include <iconv.h>
#include <stdlib.h>
#include <stdint.h>
#include <langinfo.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdio.h>
#include "util.h"
int valid_d_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_');
}
int valid_a_char(char c)
{
return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?') || (c >= 'A' &&
c <= 'Z')
|| (c == '_');
}
int valid_j_char(char msb, char lsb)
{
return !(msb == '\0' &&
(lsb < ' ' || lsb == '*' || lsb == '/' || lsb == ':' ||
lsb == ';' || lsb == '?' || lsb == '\\'));
}
int valid_p_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' &&
c <= 'z')
|| (c == '.') || (c == '_') || (c == '-');
}
char *iso_d_str(const char *src, int size, int pad)
{
char *dest = strlen(src) > size ||
pad ? malloc(size + 1) : malloc(strlen(src) + 1);
int i;
/* Try converting to upper-case before validating. */
for (i = 0; i < size && src[i]; i++) {
char c = toupper(src[i]);
dest[i] = valid_d_char(c) ? c : '_';
}
/* Optionally pad with spaces and terminate with NULL. */
if (pad)
while (i < size)
dest[i++] = ' ';
dest[i] = '\0';
return dest;
}
char *iso_a_str(const char *src, int size)
{
char *dest = malloc(size + 1);
int i;
for (i = 0; i < size && src[i]; i++) {
char c = toupper(src[i]);
dest[i] = valid_a_char(c) ? c : '_';
}
while (i < size)
dest[i++] = ' ';
dest[i] = '\0';
return dest;
}
uint16_t *iso_j_str(const char *src)
{
int size = strlen(src) * 2;
uint16_t *dest = malloc(size + 2);
char *cpy, *in, *out;
size_t inleft, outleft;
iconv_t cd;
/* If the conversion is unavailable, return NULL. Obviously,
nl_langinfo(..) requires the locale to be set correctly. */
cd = iconv_open("UCS-2BE", nl_langinfo(CODESET));
if (cd == (iconv_t) - 1) {
free(dest);
return NULL;
}
/* In, out, inleft and outleft will be updated by iconv, that's why we
need separate variables. */
cpy = strdup(src); /* iconv doesn't take const * chars, so we need our
* own copy. */
in = cpy;
out = (char*)dest;
inleft = strlen(cpy);
outleft = size;
iconv(cd, &in, &inleft, &out, &outleft);
/* Since we need to pad with NULLs, we can pad up to and including the
terminating NULLs. */
outleft += 2;
while (outleft) {
*out++ = '\0';
outleft--;
}
iconv_close(cd);
free(cpy);
return dest;
}
char *iso_1_fileid(const char *src)
{
char *dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2
(;1) + 1 (\0) */
char *dot = strrchr(src, '.'); /* Position of the last dot in the
filename, will be used to calculate
lname and lext. */
int lname, lext, pos, i;
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) {
free(dest);
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 : '_';
}
/* File versions are mandatory, even if they aren't used. */
dest[pos++] = ';';
dest[pos++] = '1';
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
return dest;
}
char *iso_2_fileid(const char *src)
{
char *dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2
(;1) + 1 (\0) */
char *dot = strrchr(src, '.');
int lname, lext, lnname, lnext, pos, i;
/* 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 == src || *(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) {
free(dest);
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++] = ';';
dest[pos++] = '1';
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
return dest;
}
uint16_t *iso_j_id(char *src)
{
char *dest = malloc(136); /* 136 = 128 (name + ext) + 2 (\0 .) +
4 (\0 ; \0 1) + 2 (\0 \0) */
char *dot = strrchr(src, '.');
int lname, lext, lcname, lcext, lnname, lnext, pos, i;
size_t inleft, outleft;
char *cname, *cext, *in, *out;
iconv_t cd;
if (dot == NULL || dot == src || *(dot + 1) == '\0') {
lname = strlen(src);
lext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
}
if (lname == 0 && lext == 0) {
free(dest);
return NULL;
}
cd = iconv_open("UCS-2BE", nl_langinfo(CODESET));
if (cd == (iconv_t) - 1) {
free(dest);
return NULL;
}
/* We need to convert the name and extension first, in order to
calculate the number of characters they have. */
cname = malloc(lname * 2);
in = src;
out = cname;
inleft = lname;
outleft = lname * 2;
iconv(cd, &in, &inleft, &out, &outleft);
lcname = (lname * 2) - outleft;
iconv_close(cd);
if (lext) {
cd = iconv_open("UCS-2BE", nl_langinfo(CODESET));
if (cd == (iconv_t) - 1) {
free(dest);
free(cname);
return NULL;
}
cext = malloc(lext * 2);
in = dot + 1;
out = cext;
inleft = lext;
outleft = lext * 2;
iconv(cd, &in, &inleft, &out, &outleft);
lcext = (lext * 2) - outleft;
iconv_close(cd);
} else
lcext = 0;
/* Again, divide the available characters over name and extension, but
keep a minimum of three characters for the new extension. */
lnext = (lcname + lcext > 128 &&
lcext > 6) ? (lcname < 122 ? 128 - lcname : 6) : lcext;
lnname = (lcname + lcext > 128) ? 128 - lnext : lcname;
pos = 0;
/* Copy up to lnname bytes from the converted filename. */
for (i = 0; i < lnname; i = i + 2)
if (valid_j_char(cname[i], cname[i + 1])) {
dest[pos++] = cname[i];
dest[pos++] = cname[i + 1];
} else {
dest[pos++] = '\0';
dest[pos++] = '_';
}
/* Dot is now a 16 bit separator. */
dest[pos++] = '\0';
dest[pos++] = '.';
/* Copy up to lnext bytes from the converted extension, if any. */
for (i = 0; i < lnext; i = i + 2)
if (valid_j_char(cext[i], cext[i + 1])) {
dest[pos++] = cext[i];
dest[pos++] = cext[i + 1];
} else {
dest[pos++] = '\0';
dest[pos++] = '_';
}
/* Again, 2 bytes per character. */
dest[pos++] = '\0';
dest[pos++] = ';';
dest[pos++] = '\0';
dest[pos++] = '1';
dest[pos++] = '\0';
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
free(cname);
if (lext)
free(cext);
/* Fill in the size in bytes (including the terminating NULLs) of the
destination string. */
return (uint16_t *) dest;
}
char *iso_p_filename(const char *src)
{
char *dest = malloc(251); /* We can fit up to 250 characters in
a Rock Ridge name entry. */
char *dot = strrchr(src, '.');
int lname, lext, lnname, lnext, pos, i;
if (dot == NULL || dot == src || *(dot + 1) == '\0') {
lname = strlen(src);
lnname = (lname > 250) ? 250 : lname;
lext = lnext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
lnext = (strlen(src) > 250 &&
lext > 3) ? (lname < 246 ? 249 - lname : 3) : lext;
lnname = (strlen(src) > 250) ? 249 - lnext : lname;
}
if (lnname == 0 && lnext == 0) {
free(dest);
return NULL;
}
pos = 0;
for (i = 0; i < lnname; i++)
dest[pos++] = valid_p_char(src[i]) ? src[i] : '_';
if (lnext) {
dest[pos++] = '.';
for (i = 0; i < lnext; i++)
dest[pos++] =
valid_p_char(src[lname + 1 + i]) ?
src[lname + 1 + i] : '_';
}
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
return dest;
}
char *iso_p_dirname(const char *src)
{
char *dest = strlen(src) > 250 ? malloc(251) : malloc(strlen(src) + 1);
int i;
if (strlen(src) == 0) {
free(dest);
return NULL;
}
for (i = 0; i < 250 && src[i]; i++)
dest[i] = valid_p_char(src[i]) ? src[i] : '_';
dest[i] = '\0';
return dest;
}
char *iso_pathname(const char *dir, const char *file)
{
char *path = malloc(strlen(dir) + strlen(file) + 2);
strcpy(path, dir);
path[strlen(dir)] = '/';
strcpy(path + strlen(dir) + 1, file);
return path;
}
void iso_a_strcpy(unsigned char *dest, int size, const char *src)
{
int i = 0, d = 0;
if (src)
for (; i < size && *src; ++i, ++src) {
char s = toupper(*src);
if (valid_a_char(s))
dest[d++] = s;
else
dest[d++] = '_';
}
for (; i < size; ++i) {
/* pad with spaces */
dest[d++] = ' ';
}
}
void iso_d_strcpy(unsigned char *dest, int size, const char *src)
{
int i = 0, d = 0;
if (src)
for (; i < size && *src; ++i, ++src) {
char s = toupper(*src);
if (valid_d_char(s))
dest[d++] = s;
else
dest[d++] = '_';
}
for (; i < size; ++i) {
/* pad with spaces */
dest[d++] = ' ';
}
}
char *iso_a_strndup(const char *src, int size)
{
int i, d;
char *out;
out = malloc(size + 1);
for (i = d = 0; i < size && *src; ++i, ++src) {
char s = toupper(*src);
if (valid_a_char(s))
out[d++] = s;
else
out[d++] = '_';
}
out[d++] = '\0';
out = realloc(out, d); /* shrink the buffer to what we used */
return out;
}
char *iso_d_strndup(const char *src, int size)
{
int i, d;
char *out;
out = malloc(size + 1);
for (i = d = 0; i < size && *src; ++i, ++src) {
char s = toupper(*src);
if (valid_d_char(s))
out[d++] = s;
else
out[d++] = '_';
}
out[d++] = '\0';
out = realloc(out, d); /* shrink the buffer to what we used */
return out;
}
char *iso_strconcat(char sep, const char *a, const char *b)
{
char *out;
int la, lb;
la = strlen(a);
lb = strlen(b);
out = malloc(la + lb + 1 + (sep ? 1 : 0));
memcpy(out, a, la);
memcpy(out + la + (sep ? 1 : 0), b, lb);
if (sep)
out[la] = sep;
out[la + lb + (sep ? 1 : 0)] = '\0';
return out;
}
char *iso_strdup(const char *src)
{
return src ? strdup(src) : NULL;
}
void iso_lsb(uint8_t *buf, uint32_t num, int bytes)
{
int i;
assert(bytes <= 4);
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;
assert(bytes <= 4);
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);
}
void iso_datetime_7(unsigned char *buf, time_t t)
{
static int tzsetup = 0;
static int tzoffset;
struct tm tm;
if (!tzsetup) {
tzset();
tzoffset = -timezone / 60 / 15;
if (tzoffset < -48)
tzoffset += 101;
tzsetup = 1;
}
localtime_r(&t, &tm);
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;
}
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 mktime(&tm) - buf[6] * 60 * 60;
}
void iso_datetime_17(unsigned char *buf, time_t t)
{
static int tzsetup = 0;
static int tzoffset;
struct tm tm;
char c[5];
if (t == (time_t) - 1) {
/* unspecified time */
memset(buf, '0', 16);
buf[16] = 0;
} else {
if (!tzsetup) {
tzset();
tzoffset = -timezone / 60 / 15;
if (tzoffset < -48)
tzoffset += 101;
tzsetup = 1;
}
localtime_r(&t, &tm);
/* year */
sprintf(c, "%04d", tm.tm_year + 1900);
memcpy(&buf[0], c, 4);
/* month */
sprintf(c, "%02d", tm.tm_mon + 1);
memcpy(&buf[4], c, 2);
/* day */
sprintf(c, "%02d", tm.tm_mday);
memcpy(&buf[6], c, 2);
/* hour */
sprintf(c, "%02d", tm.tm_hour);
memcpy(&buf[8], c, 2);
/* minute */
sprintf(c, "%02d", tm.tm_min);
memcpy(&buf[10], c, 2);
/* second */
sprintf(c, "%02d", MIN(59, tm.tm_sec));
memcpy(&buf[12], c, 2);
/* hundreths */
memcpy(&buf[14], "00", 2);
/* timezone */
buf[16] = tzoffset;
}
}
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 mktime(&tm) - buf[16] * 60 * 60;
}
void iso_split_filename(char *name, char **ext)
{
char *r;
r = strrchr(name, '.');
if (r) {
*r = '\0';
*ext = r + 1;
} else
*ext = "";
}
void iso_filecpy(unsigned char *buf, int size, const char *name, int version)
{
char v[6];
int nl, vl;
assert(version >= 0);
assert(version < 0x8000);
nl = strlen(name);
memcpy(buf, name, nl);
if (!version)
assert(size >= strlen(name));
else {
sprintf(v, "%d", version);
vl = strlen(v);
assert(size >= nl + vl + 1);
buf[nl] = ';';
memcpy(&buf[nl + 1], v, vl);
nl += vl + 1;
}
/* pad with spaces */
if (nl < size)
memset(&buf[nl], ' ', size - nl);
}
size_t ucslen(const uint16_t *str)
{
int i;
for (i=0; str[i]; i++)
;
return i;
}
int ucscmp(const uint16_t *s1, const uint16_t *s2)
{
int i;
for (i=0; 1; i++) {
if (s1[i] < s2[i]) {
return -1;
} else if (s1[i] > s2[i]) {
return 1;
} else if (s1[i] == 0 && s2[i] == 0) {
break;
}
}
return 0;
}
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)
{
uint32_t v1 = iso_read_lsb(buf, bytes);
uint32_t v2 = iso_read_msb(buf+bytes, bytes);
assert(v1 == v2);
return v1;
}