]> arthur.barton.de Git - netatalk.git/commitdiff
add uniconv tool to convert between volume encodings
authorbfernhomberg <bfernhomberg>
Sat, 14 Feb 2004 23:43:27 +0000 (23:43 +0000)
committerbfernhomberg <bfernhomberg>
Sat, 14 Feb 2004 23:43:27 +0000 (23:43 +0000)
bin/uniconv/Makefile.am [new file with mode: 0644]
bin/uniconv/iso8859_1_adapted.c [new file with mode: 0644]
bin/uniconv/uniconv.c [new file with mode: 0644]
configure.in

diff --git a/bin/uniconv/Makefile.am b/bin/uniconv/Makefile.am
new file mode 100644 (file)
index 0000000..202e722
--- /dev/null
@@ -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 (file)
index 0000000..e782966
--- /dev/null
@@ -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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <netatalk/endian.h>
+#include <atalk/unicode.h>
+#include <atalk/logger.h>
+
+#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 (file)
index 0000000..1b3dcad
--- /dev/null
@@ -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 <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <dirent.h>
+#include <atalk/afp.h>
+#include <atalk/unicode.h>
+
+#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);
+}
index cbc1845d93fdeeb37f263ecd09c5e929ed17ef18..9074e3681bb9b0a86dfe4141cc11c839623800f3 100644 (file)
@@ -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