From 946b74aa0394684e8e6e6eff73ea542e9a93203a Mon Sep 17 00:00:00 2001 From: bfernhomberg Date: Sat, 14 Feb 2004 23:43:27 +0000 Subject: [PATCH] add uniconv tool to convert between volume encodings --- bin/uniconv/Makefile.am | 8 + bin/uniconv/iso8859_1_adapted.c | 153 +++++++++++ bin/uniconv/uniconv.c | 472 ++++++++++++++++++++++++++++++++ configure.in | 3 +- 4 files changed, 635 insertions(+), 1 deletion(-) create mode 100644 bin/uniconv/Makefile.am create mode 100644 bin/uniconv/iso8859_1_adapted.c create mode 100644 bin/uniconv/uniconv.c diff --git a/bin/uniconv/Makefile.am b/bin/uniconv/Makefile.am new file mode 100644 index 00000000..202e7227 --- /dev/null +++ b/bin/uniconv/Makefile.am @@ -0,0 +1,8 @@ +# Makefile.am for bin/aecho/ + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/sys + +bin_PROGRAMS = uniconv + +uniconv_SOURCES = uniconv.c iso8859_1_adapted.c +uniconv_LDADD = $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la diff --git a/bin/uniconv/iso8859_1_adapted.c b/bin/uniconv/iso8859_1_adapted.c new file mode 100644 index 00000000..e7829665 --- /dev/null +++ b/bin/uniconv/iso8859_1_adapted.c @@ -0,0 +1,153 @@ +/* + iso-8859-1.adapted conversion routines + Copyright (C) Bjoern Fernhomberg 2002,2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ +#include +#include +#include + +#include +#include +#include + +#include "../../libatalk/unicode/byteorder.h" + +static size_t iso8859_adapted_pull(void *,char **, size_t *, char **, size_t *); +static size_t iso8859_adapted_push(void *,char **, size_t *, char **, size_t *); + +struct charset_functions charset_iso8859_adapted = +{ + "ISO-8859-ADAPTED", + 0, + iso8859_adapted_pull, + iso8859_adapted_push, + CHARSET_MULTIBYTE | CHARSET_PRECOMPOSED +}; + + +/* ------------------------ + * from unicode to iso8859_adapted code page +*/ + +static size_t iso8859_adapted_push( void *cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + int len = 0; + + while (*inbytesleft >= 2 && *outbytesleft >= 1) { + ucs2_t inptr = SVAL((*inbuf),0); + if ( inptr == 0x2122) { + SSVAL((*outbuf),0,0xAA); + } + else if ( inptr == 0x0192) { + SSVAL((*outbuf),0,0xC5); + } + else if ( inptr == 0x2014) { + SSVAL((*outbuf),0,0xD1); + } + else if ( inptr == 0x201C) { + SSVAL((*outbuf),0,0xD2); + } + else if ( inptr == 0x201D) { + SSVAL((*outbuf),0,0xD3); + } + else if ( inptr == 0x2018) { + SSVAL((*outbuf),0,0xD4); + } + else if ( inptr == 0x2019) { + SSVAL((*outbuf),0,0xD5); + } + else if ( inptr == 0x25CA) { + SSVAL((*outbuf),0,0xD7); + } + else if ( inptr > 0x0100) { + errno = EILSEQ; + return -1; + } + else { + SSVAL((*outbuf), 0, inptr); + } + (*inbuf) +=2; + (*outbuf) +=1; + (*inbytesleft) -=2; + (*outbytesleft)-=1; + len++; + } + + if (*inbytesleft > 0) { + errno = E2BIG; + return -1; + } + + return len; +} + +/* ------------------------ */ +static size_t iso8859_adapted_pull ( void *cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + unsigned char *inptr; + size_t len = 0; + + while (*inbytesleft >= 1 && *outbytesleft >= 2) { + inptr = (unsigned char *) *inbuf; + + if ( *inptr == 0xAA) { + SSVAL((*outbuf),0,0x2122); + } + else if ( *inptr == 0xC5) { + SSVAL((*outbuf),0,0x0192); + } + else if ( *inptr == 0xD1) { + SSVAL((*outbuf),0,0x2014); + } + else if ( *inptr == 0xD2) { + SSVAL((*outbuf),0,0x201C); + } + else if ( *inptr == 0xD3) { + SSVAL((*outbuf),0,0x201D); + } + else if ( *inptr == 0xD4) { + SSVAL((*outbuf),0,0x2018); + } + else if ( *inptr == 0xD5) { + SSVAL((*outbuf),0,0x2019); + } + else if ( *inptr == 0xD7) { + SSVAL((*outbuf),0,0x25CA); + } + else { + SSVAL((*outbuf),0,(*inptr)); + } + (*outbuf) +=2; + (*outbytesleft)-=2; + (*inbuf) +=1; + (*inbytesleft) -=1; + len++; + } + + if (*inbytesleft > 0) { + errno = E2BIG; + return (size_t) -1; + } + return len; +} + diff --git a/bin/uniconv/uniconv.c b/bin/uniconv/uniconv.c new file mode 100644 index 00000000..1b3dcadc --- /dev/null +++ b/bin/uniconv/uniconv.c @@ -0,0 +1,472 @@ +/* + uniconv - convert volume encodings + Copyright (C) Bjoern Fernhomberg 2002,2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atalk/cnid.h" +#define MAXPATHLEN 4096 + +static struct _cnid_db *cdb; +static char curpath[MAXPATHLEN]; +static cnid_t cdir_id; +static char db_stamp[ADEDLEN_PRIVSYN]; + +static charset_t ch_from; +char* from_charset; +static charset_t ch_to; +char* to_charset; +static charset_t ch_mac; +char* mac_charset; +static int usedots = 0; +static u_int16_t conv_flags = 0; +static int dry_run = 0; +static int verbose=0; +char *cnid_type; + +char Cnid_srv[256] = "localhost"; +int Cnid_port = 4700; + +extern struct charset_functions charset_iso8859_adapted; + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + + +#define VETO "./../.AppleDB/.AppleDouble/.AppleDesktop/.Parent/" + +static int veto(const char *path) +{ + int i,j; + char *veto_str = VETO; + + + if ((path == NULL)) + return 0; + + for(i=0, j=0; veto_str[i] != '\0'; i++) { + if (veto_str[i] == '/') { + if ((j>0) && (path[j] == '\0')) + return 1; + j = 0; + } else { + if (veto_str[i] != path[j]) { + while ((veto_str[i] != '/') + && (veto_str[i] != '\0')) + i++; + j = 0; + continue; + } + j++; + } + } + return 0; +} + + +static int do_rename( char* src, char *dst, struct stat *st) +{ + char adsrc[ MAXPATHLEN + 1]; + struct stat tmp_st; + + if (!stat(dst, &tmp_st)) { + fprintf (stderr, "error: cannot rename %s to %s, destination exists\n", src, dst); + return -1; + } + + if ( rename( src, dst ) < 0 ) { + fprintf (stderr, "error: cannot rename %s to %s, %s\n", src, dst, strerror(errno)); + return -1; + } + + if (S_ISDIR(st->st_mode)) + return 0; + + strcpy( adsrc, ad_path( src, 0 )); + + if (rename( adsrc, ad_path( dst, 0 )) < 0 ) { + struct stat ad_st; + + if (errno == ENOENT) { + if (stat(adsrc, &ad_st)) /* source has no ressource fork, */ + return 0; + } + else { + fprintf (stderr, "failed to rename resource fork, error: %s\n", strerror(errno)); + return -1; + } + + } + return 0; +} + + +static char *convert_name(char *name, struct stat *st, cnid_t cur_did) +{ + static char buffer[MAXPATHLEN]; + size_t len = 0; + size_t outlen = 0; + unsigned char *p,*q; + int require_conversion = 0; + u_int16_t flags = conv_flags; + cnid_t id; + + p = name; + q = buffer; + + /* optimize for ascii case */ + while (*p != 0) { + if ( *p >= 0x80 || *p == ':') { + require_conversion = 1; + break; + } + p++; + } + + if (!require_conversion) { + if (verbose > 1) + fprintf(stdout, "no conversion required\n"); + return name; + } + + /* convert charsets */ + q=buffer; + p=name; + + outlen = convert_charset(ch_from, ch_to, ch_mac, p, strlen(p), q, sizeof(buffer)-len, &flags); + if ((size_t)-1 == outlen) { + fprintf(stderr, "conversion from '%s' to '%s' for '%s' in DID %u failed, ignoring\n", + from_charset, to_charset, name, ntohl(cur_did)); + return name; + } + buffer[len+outlen] = 0; + if (strcmp (name, buffer)) { + if (dry_run) { + fprintf(stdout, "dry_run: would rename %s to %s.\n", name, buffer); + } + else if (!do_rename(name, buffer, st)) { + if (CNID_INVALID != (id = cnid_add(cdb, st, cur_did, buffer, strlen(buffer), 0))) + fprintf(stdout, "converted '%s' to '%s' (ID %u, DID %u).\n", + name, buffer, ntohl(id), ntohl(cur_did)); + } + } + else if (verbose > 1) + fprintf(stdout, "no conversion required\n"); + + return (buffer); +} + +static int check_dirent(char** name, cnid_t cur_did) +{ + struct stat st; + int ret = 0; + + if (veto(*name)) + return 0; + + if (stat(*name, &st) != 0) { + switch (errno) { + case ELOOP: + case ENOENT: + return 0; + default: + return (-1); + } + } + + if (S_ISDIR(st.st_mode)){ + ret = 1; + } + + if (verbose > 1) + fprintf(stdout, "Checking: '%s' - ", *name); + + *name = convert_name(*name, &st, cur_did); + + return ret; +} + +static int check_adouble(DIR *curdir, char * path) +{ + DIR *adouble; + struct dirent* entry; + struct dirent* ad_entry; + int found = 0; + + strncat(curpath, "/", MAXPATHLEN); + strncat(curpath, ".AppleDouble", MAXPATHLEN); + + if (NULL == (adouble = opendir(curpath))) { + return(-1); + } + + while (NULL != (ad_entry=readdir(adouble)) ) { + if (veto(ad_entry->d_name)) + break; + found = 0; + rewinddir(curdir); + while (NULL != (entry=readdir(curdir)) ) { + if (!strcmp(ad_entry->d_name, entry->d_name)) { + found = 1; + break; + } + } + if (!found) { + fprintf (stderr, "found orphaned resource file %s", ad_entry->d_name); + } + } + + rewinddir(curdir); + closedir(adouble); + return (0); +} + +static cnid_t add_dir_db(char *name, cnid_t cur_did) +{ + cnid_t id, did; + struct stat st; + + if (CNID_INVALID != ( id = cnid_get(cdb, cur_did, name, strlen(name))) ) + return id; + + if (dry_run) { + fprintf( stderr, "dry_run: dir '%s' not in db and cannot add (dry run), skipping\n", name); + return 0; + } + + did = cur_did; + if (stat(name, &st)) { + fprintf( stderr, "dir '%s' cannot be stat'ed, error %u\n", name, errno); + return 0; + } + + id = cnid_add(cdb, &st, did, name, strlen(name), 0); + + fprintf (stderr, "added '%s' to DID %u as %u\n", name, ntohl(did), ntohl(id)); + return id; +} + +static int getdir(DIR *curdir, char ***names) +{ + struct dirent* entry; + char **tmp = NULL, **new=NULL; + char *name; + int i=0; + + while ((entry=readdir(curdir)) != NULL) { + new = (char **) realloc (tmp, (i+1) * sizeof(char*)); + if (new == NULL) { + fprintf(stderr, "out of memory"); + exit (-1); + } + tmp = new; + name = strdup(entry->d_name); + if (name == NULL) { + fprintf(stderr, "out of memory"); + exit (-1); + } + tmp[i]= (char*) name; + i++; + }; + + *names = tmp; + return i; +} + +static int checkdir(DIR *curdir, char *path, cnid_t cur_did) +{ + DIR* cdir; + int ret = 0; + cnid_t id; + char *name; + int n; + size_t len=strlen(curpath); + + char **names; + + chdir(path); + + check_adouble(curdir, path); + curpath[len] = 0; + + if (verbose) + fprintf( stdout, "\nchecking DIR '%s' with ID %u\n", path, ntohl(cur_did)); + + n = getdir(curdir, &names); + + while (n--) { + name = names[n]; + ret = check_dirent(&name, cur_did); + if (ret==1) { + id = add_dir_db(name, cur_did); + if ( id == 0) + continue; /* skip, no ID */ + strncat(curpath, "/", MAXPATHLEN); + strncat(curpath, name, MAXPATHLEN); + cdir = opendir(curpath); + checkdir(cdir, curpath, id); + closedir(cdir); + curpath[len] = 0; + chdir(path); + if (verbose) + fprintf( stdout, "returned to DIR '%s' with ID %u\n", path, ntohl(cur_did)); + } + free(names[n]); + } + free(names); + if (verbose) + fprintf( stderr, "leaving DIR '%s' with ID %u\n\n", path, ntohl(cur_did)); + + return 0; +} + +static int init(char* path) +{ + DIR* startdir; + + if (NULL == (cdb = cnid_open (path, 0, cnid_type, 0)) ) { + fprintf (stderr, "ERROR: cannot open CNID database in '%s'\n", path); + fprintf (stderr, "ERROR: check the logs for reasons, aborting\n"); + return -1; + } + cnid_getstamp(cdb, db_stamp, sizeof(db_stamp)); + cdir_id = htonl(2); + + startdir = opendir(path); + strncpy(curpath, path, MAXPATHLEN); + checkdir (startdir, path, cdir_id); + closedir(startdir); + + cnid_close(cdb); + + return (0); +} + + + +int main(int argc, char *argv[]) +{ + char path[MAXPATHLEN]; + int c; + + path[0]= 0; + conv_flags = CONV_UNESCAPEHEX | CONV_ESCAPEHEX | CONV_ESCAPEDOTS; + + while ((c = getopt (argc, argv, "f:m:p:t:nc:v")) != -1) + switch (c) + { + case 'f': + from_charset = strdup(optarg); + break; + case 't': + to_charset = strdup(optarg); + break; + case 'p': + strncpy(path, optarg, MAXPATHLEN); + break; + case 'm': + mac_charset = strdup(optarg); + break; + case 'd': + conv_flags &= !CONV_ESCAPEDOTS; + usedots = 1; + break; + case 'n': + fprintf (stderr, "doing dry run, volume will *not* be changed\n"); + dry_run = 1; + break; + case 'c': + cnid_type = strdup(optarg); + fprintf (stderr, "CNID backend set to: %s\n", cnid_type); + break; + case 'v': + verbose++; + break; + case 'h': + break; + default: + break; + } + + if ( from_charset == NULL || to_charset == NULL) { + fprintf (stderr, "charsets not specified\n"); + exit(-1); + } + + if ( mac_charset == NULL ) + mac_charset = "MAC_ROMAN"; + + if ( cnid_type == NULL) + cnid_type = DEFAULT_CNID_SCHEME; + + + if (path[0] == 0) { + if (NULL == (getcwd(path, MAXPATHLEN)) ) { + fprintf (stderr, "ERROR: path == NULL\n"); + return (-1); + } + } + + atalk_register_charset(&charset_iso8859_adapted); + + if ( (charset_t) -1 == ( ch_from = add_charset(from_charset)) ) { + fprintf( stderr, "Setting codepage %s as source codepage failed", from_charset); + exit (-1); + } + + if ( (charset_t) -1 == ( ch_to = add_charset(to_charset)) ) { + fprintf( stderr, "Setting codepage %s as destination codepage failed", to_charset); + exit (-1); + } + + if ( (charset_t) -1 == ( ch_mac = add_charset(mac_charset)) ) { + fprintf( stderr, "Setting codepage %s as mac codepage failed", mac_charset); + exit (-1); + } + + cnid_init(); + init(path); + + return (0); +} diff --git a/configure.in b/configure.in index cbc1845d..9074e368 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -dnl $Id: configure.in,v 1.179.2.3.2.18 2004-02-14 00:23:20 didg Exp $ +dnl $Id: configure.in,v 1.179.2.3.2.19 2004-02-14 23:43:47 bfernhomberg Exp $ dnl configure.in for netatalk AC_INIT(etc/afpd/main.c) @@ -885,6 +885,7 @@ AC_OUTPUT([Makefile bin/nbp/Makefile bin/pap/Makefile bin/psorder/Makefile + bin/uniconv/Makefile config/Makefile contrib/Makefile contrib/macusers/Makefile -- 2.39.2