diff --git a/src/util.c b/src/util.c index 58dfbdd..88d366f 100644 --- a/src/util.c +++ b/src/util.c @@ -507,6 +507,108 @@ char *iso_2_fileid(const char *src) return strdup(dest); } +/** + * Create a file/dir 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_id(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; diff --git a/src/util.h b/src/util.h index 7967db7..4e013a4 100644 --- a/src/util.h +++ b/src/util.h @@ -106,6 +106,19 @@ char *iso_1_fileid(const char *src); */ char *iso_2_fileid(const char *src); +/** + * Create a file/dir 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_id(const char *src, size_t len, int relaxed, int forcedot); + /** * Create a Joliet file identifier that consists of name and extension. The * combined name and extension length will not exceed 128 bytes, and the diff --git a/test/test_util.c b/test/test_util.c index 2e030c9..398bba5 100644 --- a/test/test_util.c +++ b/test/test_util.c @@ -374,6 +374,140 @@ static void test_iso_2_fileid() free(file); } +static void test_iso_r_id() +{ + char *file; + + /* force dot */ + file = iso_r_id("file1", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + + /* and not */ + file = iso_r_id("file1", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE1"); + free(file); + + /* allow lowercase */ + file = iso_r_id("file1", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, "file1"); + free(file); + file = iso_r_id("file1", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, "file1"); + free(file); + + /* force d-char and dot */ + file = iso_r_id("fILe1", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + /* force d-char but not dot */ + file = iso_r_id("fILe1", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE1"); + free(file); + /* allow lower case but force dot */ + file = iso_r_id("fILe1", 30, 1, 1); + CU_ASSERT_STRING_EQUAL(file, "fILe1."); + free(file); + + file = iso_r_id("FILE1", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE1."); + free(file); + file = iso_r_id(".EXT", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, ".EXT"); + free(file); + file = iso_r_id(".EXT", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, ".EXT"); + free(file); + + file = iso_r_id("file.ext", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + + /* not force dot is the same in this case */ + file = iso_r_id("fiLE.ext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_r_id("fiLE.ext", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, "fiLE.ext"); + free(file); + + file = iso_r_id("file.EXt", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + file = iso_r_id("FILE.EXT", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "FILE.EXT"); + free(file); + + file = iso_r_id("31 characters filename.extensio", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "31_CHARACTERS_FILENAME.EXTENSIO"); + free(file); + file = iso_r_id("32 characters filename.extension", 30, 0, 1); + CU_ASSERT_STRING_EQUAL(file, "32_CHARACTERS_FILENAME.EXTENSIO"); + free(file); + + /* allow lowercase */ + file = iso_r_id("31 characters filename.extensio", 30, 1, 1); + CU_ASSERT_STRING_EQUAL(file, "31_characters_filename.extensio"); + free(file); + + /* and all characters */ + file = iso_r_id("31 characters filename.extensio", 30, 2, 1); + CU_ASSERT_STRING_EQUAL(file, "31 characters filename.extensio"); + free(file); + + file = iso_r_id("more than 30 characters filename.extension", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FIL.EXT"); + + /* incrementing the size... */ + file = iso_r_id("more than 30 characters filename.extension", 35, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FILENAME.EXT"); + file = iso_r_id("more than 30 characters filename.extension", 36, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "MORE_THAN_30_CHARACTERS_FILENAME.EXTE"); + + free(file); + file = iso_r_id("file.bigext", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, "file.bigext"); + free(file); + file = iso_r_id(".bigext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, ".BIGEXT"); + + /* "strange" characters */ + file = iso_r_id("file<:a.ext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE__A.EXT"); + free(file); + file = iso_r_id("file<:a.ext", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, "file__a.ext"); + free(file); + file = iso_r_id("file<:a.ext", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, "file<:a.ext"); + free(file); + + /* multiple dots */ + file = iso_r_id("fi.le.a.ext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FI_LE_A.EXT"); + free(file); + file = iso_r_id("fi.le.a.ext", 30, 1, 0); + CU_ASSERT_STRING_EQUAL(file, "fi_le_a.ext"); + free(file); + file = iso_r_id("fi.le.a.ext", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, "fi.le.a.ext"); + + file = iso_r_id("file.<:a", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE.__A"); + free(file); + file = iso_r_id("file<:a.--a", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "FILE__A.__A"); + free(file); + + file = iso_r_id(".file.bigext", 30, 0, 0); + CU_ASSERT_STRING_EQUAL(file, "_FILE.BIGEXT"); + free(file); + + file = iso_r_id(".file.bigext", 30, 2, 0); + CU_ASSERT_STRING_EQUAL(file, ".file.bigext"); + free(file); +} + static void test_iso_rbtree_insert() { int res; @@ -509,6 +643,7 @@ void add_util_suite() CU_add_test(pSuite, "iso_2_dirid()", test_iso_2_dirid); CU_add_test(pSuite, "iso_1_fileid()", test_iso_1_fileid); CU_add_test(pSuite, "iso_2_fileid()", test_iso_2_fileid); + CU_add_test(pSuite, "iso_r_id()", test_iso_r_id); CU_add_test(pSuite, "iso_rbtree_insert()", test_iso_rbtree_insert); CU_add_test(pSuite, "iso_htable_put/get()", test_iso_htable_put_get); }