2 uniconv - convert volume encodings
3 Copyright (C) Bjoern Fernhomberg 2004
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #endif /* HAVE_CONFIG_H */
34 #include <sys/types.h>
35 #include <sys/param.h>
38 #include <atalk/afp.h>
39 #include <atalk/unicode.h>
40 #include <atalk/util.h>
41 #include <atalk/logger.h>
43 #include "atalk/cnid.h"
45 #define MAXPATHLEN 4096
48 static struct _cnid_db *cdb;
49 static char curpath[MAXPATHLEN];
50 static cnid_t cdir_id;
51 static char db_stamp[ADEDLEN_PRIVSYN];
53 static charset_t ch_from;
55 static charset_t ch_to;
57 static charset_t ch_mac;
59 static int usedots = 0;
60 static u_int16_t conv_flags = 0;
61 static int dry_run = 0;
65 char Cnid_srv[256] = "localhost";
68 extern struct charset_functions charset_iso8859_adapted;
71 #define MAX(a,b) (((a)>(b))?(a):(b))
75 #define VETO "./../.AppleDB/.AppleDouble/.AppleDesktop/.Parent/"
77 static int veto(const char *path)
80 char *veto_str = VETO;
86 for(i=0, j=0; veto_str[i] != '\0'; i++) {
87 if (veto_str[i] == '/') {
88 if ((j>0) && (path[j] == '\0'))
92 if (veto_str[i] != path[j]) {
93 while ((veto_str[i] != '/')
94 && (veto_str[i] != '\0'))
106 static int do_rename( char* src, char *dst, struct stat *st)
108 char adsrc[ MAXPATHLEN + 1];
111 if (!stat(dst, &tmp_st)) {
112 fprintf (stderr, "error: cannot rename %s to %s, destination exists\n", src, dst);
116 if ( rename( src, dst ) < 0 ) {
117 fprintf (stderr, "error: cannot rename %s to %s, %s\n", src, dst, strerror(errno));
121 if (S_ISDIR(st->st_mode))
124 strcpy( adsrc, ad_path( src, 0 ));
126 if (rename( adsrc, ad_path( dst, 0 )) < 0 ) {
129 if (errno == ENOENT) {
130 if (stat(adsrc, &ad_st)) /* source has no ressource fork, */
134 fprintf (stderr, "failed to rename resource fork, error: %s\n", strerror(errno));
143 static char *convert_name(char *name, struct stat *st, cnid_t cur_did)
145 static char buffer[MAXPATHLEN];
148 int require_conversion = 0;
149 u_int16_t flags = conv_flags;
155 /* optimize for ascii case */
157 if ( *p >= 0x80 || *p == ':') {
158 require_conversion = 1;
164 if (!require_conversion) {
166 fprintf(stdout, "no conversion required\n");
170 /* convert charsets */
174 outlen = convert_charset(ch_from, ch_to, ch_mac, p, strlen(p), q, sizeof(buffer), &flags);
175 if ((size_t)-1 == outlen) {
176 if ( ch_to == CH_UTF8) {
177 /* maybe name is already in UTF8? */
181 outlen = convert_charset(ch_to, ch_to, ch_mac, p, strlen(p), q, sizeof(buffer), &flags);
182 if ((size_t)-1 == outlen) {
183 /* it's not UTF8... */
184 fprintf(stderr, "ERROR: conversion from '%s' to '%s' for '%s' in DID %u failed!!!\n",
185 from_charset, to_charset, name, ntohl(cur_did));
190 if (!strcmp(buffer, name)) {
194 fprintf(stderr, "ERROR: conversion from '%s' to '%s' for '%s' in DID %u failed. Please check this!\n",
195 from_charset, to_charset, name, ntohl(cur_did));
199 if (strcmp (name, buffer)) {
201 fprintf(stdout, "dry_run: would rename %s to %s.\n", name, buffer);
203 else if (!do_rename(name, buffer, st)) {
204 if (CNID_INVALID != (id = cnid_add(cdb, st, cur_did, buffer, strlen(buffer), 0)))
205 fprintf(stdout, "converted '%s' to '%s' (ID %u, DID %u).\n",
206 name, buffer, ntohl(id), ntohl(cur_did));
209 else if (verbose > 1)
210 fprintf(stdout, "no conversion required\n");
215 static int check_dirent(char** name, cnid_t cur_did)
223 if (stat(*name, &st) != 0) {
233 if (S_ISDIR(st.st_mode)){
238 fprintf(stdout, "Checking: '%s' - ", *name);
240 *name = convert_name(*name, &st, cur_did);
245 static int check_adouble(DIR *curdir, char * path)
248 struct dirent* entry;
249 struct dirent* ad_entry;
252 strlcat(curpath, "/", sizeof(curpath));
253 strlcat(curpath, ".AppleDouble", sizeof(curpath));
255 if (NULL == (adouble = opendir(curpath))) {
259 while (NULL != (ad_entry=readdir(adouble)) ) {
260 if (veto(ad_entry->d_name))
264 while (NULL != (entry=readdir(curdir)) ) {
265 if (!strcmp(ad_entry->d_name, entry->d_name)) {
271 fprintf (stderr, "found orphaned resource file %s", ad_entry->d_name);
280 static cnid_t add_dir_db(char *name, cnid_t cur_did)
285 if (CNID_INVALID != ( id = cnid_get(cdb, cur_did, name, strlen(name))) )
293 if (stat(name, &st)) {
294 fprintf( stderr, "dir '%s' cannot be stat'ed, error %u\n", name, errno);
298 id = cnid_add(cdb, &st, did, name, strlen(name), 0);
300 fprintf (stderr, "added '%s' to DID %u as %u\n", name, ntohl(did), ntohl(id));
304 static int getdir(DIR *curdir, char ***names)
306 struct dirent* entry;
307 char **tmp = NULL, **new=NULL;
311 while ((entry=readdir(curdir)) != NULL) {
312 new = (char **) realloc (tmp, (i+1) * sizeof(char*));
314 fprintf(stderr, "out of memory");
318 name = strdup(entry->d_name);
320 fprintf(stderr, "out of memory");
323 tmp[i]= (char*) name;
331 static int checkdir(DIR *curdir, char *path, cnid_t cur_did)
338 size_t len=strlen(curpath);
344 check_adouble(curdir, path);
348 fprintf( stdout, "\nchecking DIR '%s' with ID %u\n", path, ntohl(cur_did));
350 n = getdir(curdir, &names);
355 ret = check_dirent(&name, cur_did);
357 id = add_dir_db(name, cur_did);
358 if ( id == 0 && !dry_run )
359 continue; /* skip, no ID */
362 strlcat(curpath, "/", sizeof(curpath));
363 strlcat(curpath, name, sizeof(curpath));
364 cdir = opendir(curpath);
366 fprintf( stderr, "ERROR: cannot open DIR '%s' with ID %u\n", curpath, ntohl(cur_did));
369 checkdir(cdir, curpath, id);
374 fprintf( stdout, "returned to DIR '%s' with ID %u\n", path, ntohl(cur_did));
381 fprintf( stdout, "leaving DIR '%s' with ID %u\n\n", path, ntohl(cur_did));
386 static int init(char* path)
390 if (NULL == (cdb = cnid_open (path, 0, cnid_type, 0)) ) {
391 fprintf (stderr, "ERROR: cannot open CNID database in '%s'\n", path);
392 fprintf (stderr, "ERROR: check the logs for reasons, aborting\n");
395 cnid_getstamp(cdb, db_stamp, sizeof(db_stamp));
398 startdir = opendir(path);
399 strlcpy(curpath, path, sizeof(curpath));
400 checkdir (startdir, path, cdir_id);
408 static void usage( char * name )
410 fprintf( stderr, "usage:\t%s [-ndv] -c cnid -f fromcode -t tocode [-m maccode] path\n", name );
411 fprintf( stderr, "Try `%s -h' for more information.\n", name );
415 static void print_version ()
417 fprintf( stderr, "uniconv - Netatalk %s\n", VERSION );
422 fprintf (stdout, "\nuniconv, a tool to convert between various Netatalk volume encodings\n");
423 fprintf (stdout, "\nUsage: uniconv [-ndv] -c cnid -f fromcode -t tocode [-m maccode] path\n\n");
424 fprintf (stdout, "Examples:\n");
425 fprintf (stdout, " uniconv -c dbd -f ASCII -t UTF8 -m MAC_ROMAN /path/to/share\n");
426 fprintf (stdout, " uniconv -c cdb -f ISO-8859-1 -t UTF8 -m MAC_ROMAN /path/to/share\n");
427 fprintf (stdout, " uniconv -c cdb -f ISO-8859-ADAPTED -t ASCII -m MAC_ROMAN /path/to/share\n");
428 fprintf (stdout, " uniconv -f UTF8 -t ASCII -m MAC_ROMAN /path/to/share\n\n");
429 fprintf (stdout, "Options:\n");
430 fprintf (stdout, "\t-f\tencoding to convert from, use ASCII for CAP encoded volumes\n");
431 fprintf (stdout, "\t-t\tvolume encoding to convert to, e.g. UTF8.\n");
432 fprintf (stdout, "\t-m\tMacintosh client codepage, required for CAP encoded volumes.\n");
433 fprintf (stdout, "\t\tDefaults to `MAC_ROMAN'\n");
434 fprintf (stdout, "\t-n\t`dry run', don't change anything.\n");
435 fprintf (stdout, "\t-d\tDon't CAP encode leading dots (:2e).\n");
436 fprintf (stdout, "\t-c\tCNID backend used on this volume, usually cdb or dbd.\n");
437 fprintf (stdout, "\t\tIf not specified, the default cnid backend `%s' is used\n", DEFAULT_CNID_SCHEME);
438 fprintf (stdout, "\t-v\tVerbose output, use twice for maximum logging.\n");
439 fprintf (stdout, "\t-V\tPrint version and exit\n");
440 fprintf (stdout, "\t-h\tThis help screen\n\n");
441 fprintf (stdout, "WARNING:\n");
442 fprintf (stdout, " Setting the wrong options might render your data unusable!!!\n");
443 fprintf (stdout, " Make sure you know what you are doing. Always backup your data first.\n\n");
444 fprintf (stdout, " It is *strongly* recommended to do a `dry run' first and to check the\n");
445 fprintf (stdout, " output for conversion errors.\n");
446 fprintf (stdout, " USE AT YOUR OWN RISK!!!\n\n");
451 int main(int argc, char *argv[])
453 char path[MAXPATHLEN];
457 conv_flags = CONV_UNESCAPEHEX | CONV_ESCAPEHEX | CONV_ESCAPEDOTS;
459 #ifdef HAVE_SETLINEBUF
463 while ((c = getopt (argc, argv, "f:m:t:c:nvVh")) != -1)
467 from_charset = strdup(optarg);
470 to_charset = strdup(optarg);
473 mac_charset = strdup(optarg);
476 conv_flags &= !CONV_ESCAPEDOTS;
480 fprintf (stderr, "doing dry run, volume will *not* be changed\n");
484 cnid_type = strdup(optarg);
485 fprintf (stderr, "CNID backend set to: %s\n", cnid_type);
502 if ( argc - optind != 1 ) {
506 set_processname("uniconv");
508 if ( from_charset == NULL || to_charset == NULL) {
509 fprintf (stderr, "required charsets not specified\n");
513 if ( mac_charset == NULL )
514 mac_charset = "MAC_ROMAN";
516 if ( cnid_type == NULL)
517 cnid_type = DEFAULT_CNID_SCHEME;
521 strlcpy(path, argv[optind], sizeof(path));
523 /* deal with relative path */
525 fprintf (stderr, "ERROR: cannot chdir to '%s'\n", path);
529 if (NULL == (getcwd(path, sizeof(path))) ) {
530 fprintf (stderr, "ERROR: getcwd failed\n");
535 atalk_register_charset(&charset_iso8859_adapted);
537 if ( (charset_t) -1 == ( ch_from = add_charset(from_charset)) ) {
538 fprintf( stderr, "Setting codepage %s as source codepage failed", from_charset);
542 if ( (charset_t) -1 == ( ch_to = add_charset(to_charset)) ) {
543 fprintf( stderr, "Setting codepage %s as destination codepage failed", to_charset);
547 if ( (charset_t) -1 == ( ch_mac = add_charset(mac_charset)) ) {
548 fprintf( stderr, "Setting codepage %s as mac codepage failed", mac_charset);