First pinch of libisofs rewrite

This commit is contained in:
Mario Danic
2006-08-24 19:23:37 +00:00
parent 176e1654ff
commit 48d76e844e
16 changed files with 1610 additions and 3065 deletions

View File

@ -1,136 +1,190 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
/* vim: set noet ts=8 sts=8 sw=8 : */
#include <ctype.h>
/**
* Utility functions for the Libisofs library.
*/
#include <wchar.h>
#include <iconv.h>
#include <stdlib.h>
#include <stdint.h>
#include <langinfo.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include "util.h"
int valid_d_char(char c)
int div_up(int n, int div)
{
return (n + div - 1) / div;
}
int round_up(int n, int mul)
{
return div_up(n, mul) * mul;
}
wchar_t *towcs(const char *str)
{
size_t len = strlen(str);
wchar_t *ret = malloc(sizeof(wchar_t) * (len + 1));
mbstate_t ps;
size_t n;
memset(&ps, 0, sizeof(ps));
n = mbsrtowcs(ret, &str, len, &ps);
ret[len] = '\0';
if (n != len) {
free(ret);
return NULL;
}
return ret;
}
char *wcstoascii(const wchar_t *wsrc)
{
if (!wsrc)
return NULL;
iconv_t conv = iconv_open("ASCII", "WCHAR_T");
size_t outbytes = wcslen(wsrc);
size_t inbytes = outbytes * sizeof(wchar_t);
char src_[inbytes + sizeof(wchar_t)];
char *src = src_;
char *ret_, *ret;
size_t n;
if (conv == (iconv_t)-1)
return NULL;
memcpy(src, wsrc, inbytes + sizeof(wchar_t));
ret = malloc(outbytes+1);
ret[outbytes] = '\0';
ret_ = ret;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
if (n == -1) {
free(ret_);
return NULL;
}
return ret_;
}
/* FIXME: C&P */
uint16_t *wcstoucs(const wchar_t *wsrc)
{
if (!wsrc)
return calloc(2, 1); /* empty UCS string */
iconv_t conv = iconv_open("UCS-2BE", "WCHAR_T");
size_t outbytes = wcslen(wsrc) * 2;
size_t inbytes = outbytes * sizeof(wchar_t) / 2;
char src_[inbytes + sizeof(wchar_t)];
char *src = src_;
char *ret_, *ret;
size_t n;
if (conv == (iconv_t)-1)
return calloc(2, 1);
memcpy(src, wsrc, inbytes + sizeof(wchar_t));
ret = malloc(outbytes + sizeof(wchar_t));
ret[outbytes] = 0;
ret[outbytes+1] = 0;
ret_ = ret;
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
if (n == -1) {
perror ("error in iconv conversion");
free(ret_);
return calloc(2, 1);
}
return (uint16_t*)ret_;
}
static int valid_d_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_');
}
int valid_a_char(char c)
static int valid_a_char(char c)
{
return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?') || (c >= 'A' &&
c <= 'Z')
|| (c == '_');
return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?')
|| (c >= 'A' && c <= 'Z')
|| (c == '_');
}
int valid_j_char(char msb, char lsb)
static int valid_j_char(uint16_t c)
{
return !(msb == '\0' &&
(lsb < ' ' || lsb == '*' || lsb == '/' || lsb == ':' ||
lsb == ';' || lsb == '?' || lsb == '\\'));
return !(c < (uint16_t)' ' || c == (uint16_t)'*' || c == (uint16_t)'/'
|| c == (uint16_t)':' || c == (uint16_t)';'
|| c == (uint16_t)'?' || c == (uint16_t)'\\');
}
int valid_p_char(char c)
/* FIXME: where are these documented? */
static int valid_p_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' &&
c <= 'z')
|| (c == '.') || (c == '_') || (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)
static char *iso_dirid(const wchar_t *src, int size)
{
char *dest = strlen(src) > size ||
pad ? malloc(size + 1) : malloc(strlen(src) + 1);
int i;
char *ret = wcstoascii(src);
size_t len, 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);
if (!ret)
return NULL;
len = strlen(ret);
if (len > size) {
ret[size] = '\0';
len = size;
}
for (i = 0; i < len; i++) {
char c = toupper(ret[i]);
ret[i] = valid_d_char(c) ? c : '_';
}
/* 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;
return ret;
}
char *iso_1_fileid(const char *src)
char *iso_1_dirid(const wchar_t *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
return iso_dirid(src, 8);
}
char *iso_2_dirid(const wchar_t *src)
{
return iso_dirid(src, 31);
}
char *iso_1_fileid(const wchar_t *wsrc)
{
char *src = wcstoascii(wsrc);
char *dest;
char *dot; /* Position of the last dot in the
filename, will be used to calculate
lname and lext. */
int lname, lext, pos, i;
if (!src)
return NULL;
dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2
(;1) + 1 (\0) */
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) {
free(src);
free(dest);
return NULL;
}
@ -156,16 +210,24 @@ char *iso_1_fileid(const char *src)
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
free(src);
return dest;
}
char *iso_2_fileid(const char *src)
char *iso_2_fileid(const wchar_t *wsrc)
{
char *dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2
(;1) + 1 (\0) */
char *dot = strrchr(src, '.');
char *src = wcstoascii(wsrc);
char *dest;
char *dot;
int lname, lext, lnname, lnext, pos, i;
if (!src)
return NULL;
dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2
(;1) + 1 (\0) */
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
@ -177,12 +239,13 @@ char *iso_2_fileid(const char *src)
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
lnext = (strlen(src) > 31 &&
lext > 3) ? (lname < 27 ? 30 - lname : 3) : lext;
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(src);
free(dest);
return NULL;
}
@ -206,276 +269,43 @@ char *iso_2_fileid(const char *src)
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
free(src);
return dest;
}
uint16_t *iso_j_id(char *src)
char *
iso_p_fileid(const wchar_t *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;
char *ret = wcstoascii(src);
size_t i, len;
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);
if (!ret)
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;
len = strlen(ret);
for (i = 0; i < len; i++) {
if (!valid_p_char(ret[i])) {
ret[i] = (uint16_t)'_';
}
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;
}
return ret;
}
char *iso_p_filename(const char *src)
uint16_t *
iso_j_id(const wchar_t *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;
uint16_t *j_str = wcstoucs(src);
size_t len = ucslen(j_str);
size_t n;
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 (len > 128) {
j_str[128] = '\0';
len = 128;
}
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;
for (n = 0; n < len; n++)
if (!valid_j_char(j_str[n]))
j_str[n] = '_';
return j_str;
}
void iso_lsb(uint8_t *buf, uint32_t num, int bytes)
@ -551,7 +381,6 @@ 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 */
@ -570,27 +399,13 @@ void iso_datetime_17(unsigned char *buf, time_t t)
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 */
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);
/* timezone */
buf[16] = tzoffset;
}
}
@ -611,47 +426,6 @@ time_t iso_datetime_read_17(const uint8_t *buf)
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;
@ -661,20 +435,30 @@ size_t ucslen(const uint16_t *str)
return i;
}
/**
* 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)
{
int i;
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; 1; i++) {
if (s1[i] < s2[i]) {
for (i=0; i < len; i++) {
if (s[i] < t[i]) {
return -1;
} else if (s1[i] > s2[i]) {
} else if (s[i] > t[i]) {
return 1;
} else if (s1[i] == 0 && s2[i] == 0) {
break;
}
}
if (len1 < len2)
return -1;
else if (len1 > len2)
return 1;
return 0;
}