dirconv update
dirconv: 轉換系統裡文件名,目錄名的編碼。
==================================================================
Changes:
Reorganize a bit.
Fix a couple of mem leak on symlinks.
Louder error report.
dirconv is a program to convert encoding of a give directory/file to
another encoding.
WARNING: The action is highly dangerous. You might have your system
converted to something you can not read/recognize. And even worse, the
program has not been intensively tested. Never tested on a vfat system.
Use it on your own risk. Read the file first.
Why it is writen? Because it can be writen and it might be useful
in the future.
--
hashao| 知人者智,自知者明。
hashao| 勝人者有力,自勝者強。
hashao| 知足者富。強行者有志。
hashao| 不失其所者久。死而不亡者壽。
--
| This message was re-posted from debian-chinese-gb@lists.debian.org
| and converted from gb2312 to big5 by an automatic gateway.
/* dirconv: encoding convert for directries. Ver: 20020920.
* Released under the Gnu Public License (GPL).
* It might make your file system unusable. Use it on your own risk.
* -- hashao
*
* Please visit: http://www.debian.org/intl/zh/
*/
/* Convert the encoding of a directory name recursively.
* + content of symbolic links are also converted. (good/bad?)
*/
/* Usage: dirconv -f encode -t encode [-w|-m] filename
* -w: Warn off. Do not warn at the beginning.
* -m: Also follow other mounted file system (horror!)
*/
/* To compile:
* gcc -lreadline -o dirconv dirconv.c
* You need libreadline-dev to compile it, of course.
*/
/* ChangeLog:
*
* 2002-09-20:
* + Make config structure global so we can use it. C++ is better here.
* + Add -m option to also do mounted file system if asked.
* + Plug a couple of mem leak for rename symlink. (do_symlink())
* + Better error display. Many perror()'s, printf()'s.
*
* 2002-09-15:
* + first release. Only limited test.
* + content of symbolic links are also converted. (good/bad?)
* + Don't know what will happen on vfat systems. I will not
* try it on my own vfat system!
* + Use it on your own risk. (hashao)
*/
#define _GNU_SOURCE 1 /* Need for nftw() in ftw.h */
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <ftw.h>
#include <iconv.h>
#include <stdio.h>
#include <readline/readline.h>
#define SYMLEN 4096 /* Symbolic link buffer length. */
static iconv_t icd = (iconv_t)(-1); /* global iconv handler. */
/* buff hold the converted file name. Avoid too much malloc. */
static char *convstr = NULL;
static int maxlen = 256; /* max len of the convstr. */
/* Hold configuration for this program. */
struct conf {
char* from;
char* to;
char* root; /* Root diretory to convert. */
int warn; /* warning flag. */
int mount;
};
/* Gobal variable holding our configuration. */
static struct conf gcf;
/* Handy iconv function use a global output buffer. */
int do_iconv(const char *filename)
{
char *inbuf, *outbuf;
size_t inleft, outleft;
size_t retval;
memset(convstr, 0, maxlen);
inbuf = (char*)filename;
outbuf = convstr;
inleft = strlen(inbuf);
outleft = maxlen;
retval = iconv(icd, NULL, NULL, NULL, NULL);
retval = iconv(icd, &inbuf, &inleft, &outbuf, &outleft);
/* convert ok. */
if (retval != (size_t)(-1))
return retval;
/* encoding error. */
if (errno != E2BIG){
perror(filename);
return retval;
}
/* No enough output space. expend and tail recursive. */
free(convstr);
maxlen = maxlen + MB_LEN_MAX*inleft;
convstr = (char*)malloc(maxlen);
return do_iconv(filename);
}
/* Function to re-encode plain filename. */
int do_rename(const char* file, char* newfile, struct FTW *s)
{
int retval;
char *newfullpath;
/* Construct the new fullpath. */
newfullpath = (char*)malloc(sizeof(newfile)+ s->base + 1);
memset(newfullpath, 0, sizeof(newfile) + s->base + 1);
strncat(newfullpath, file, s->base);
strcat(newfullpath, newfile);
retval = rename(file, newfullpath);
if (!retval){
printf("Converted %s to %s\n", file, newfullpath);
}
else {
printf("!!! Failed to convert %s\n", file);
perror(file);
}
free(newfullpath);
}
/* Convert a symbolic link and its link content. */
int do_symlink(const char* file, char* newfile, struct FTW *s)
{
int retval;
char *newfullpath;
char symbuf[SYMLEN];
char *newlink;
/* Construct the new fullpath. */
newfullpath = (char*)malloc(sizeof(newfile)+ s->base + 1);
memset(newfullpath, 0, sizeof(newfile) + s->base + 1);
strncat(newfullpath, file, s->base);
strcat(newfullpath, newfile);
memset(symbuf, 0, SYMLEN);
/* Get the content of symbolic link and convert it too. */
retval = readlink(file, symbuf, SYMLEN);
if (retval == -1){
printf("!!! Failed to read symbolic link: %s\n", file);
perror(file);
}else{
retval = do_iconv(symbuf);
if (retval == (size_t)(-1)){
printf("!!! Cannot iconv symlink content %s for %s\n",
symbuf, file);
}else{
/* Save convstr. */
newlink = strdup(convstr);
/* Relink to the new symlink. */
retval = symlink(newlink, newfullpath);
if (retval == -1){
printf("!!! Cannot create symlink %s to %s\n",
newfullpath, newlink);
perror(newlink);
}else {
printf("Converted symlink %s to %s points to \n", file,
newfullpath, newlink);
retval = unlink(file);
if (retval == -1){
printf("!!! Cannot unlink symlink %s\n", file);
perror(file);
}
}
free(newlink);
}
}
free(newfullpath);
return retval;
}
/* Function past to nftw(). called for every file. */
int walk_func(const char* file, const struct stat *sb, int flag, struct FTW *s)
{
int retval;
char *basefile;
char *newfile;
if(flag & FTW_DNR) {
printf("!!! %s is a directory but its content cannot be read.\n", file);
}
/* Only convert the basename. we do depth first. */
basefile = (char*)(file + s->base);
retval = do_iconv(basefile);
if (retval == (size_t)(-1)){
printf("!!! Cannot iconv %s, skip.\n", basefile);
if (S_ISDIR(sb->st_mode))
printf("In dir %s\n", file);
return 0;
}
/* Save a copy of converted string. */
newfile = strdup(convstr);
if S_ISLNK(sb->st_mode) {
do_symlink(file, newfile, s);
}else {
do_rename(file, newfile, s);
}
if (S_ISDIR(sb->st_mode))
printf("In dir %s\n", file);
free(newfile);
/* Return non-0 will stop ftw(). */
return 0;
}
/* Start waling a dir tree from the 'root'. */
int do_walk(char* root, struct conf *cf)
{
int retval;
int flag;
/* Do not |FTW_CHDIR. It will stop when hit by permission denies. */
/* Do depth first, do no follow symbolic directory. */
flag = FTW_DEPTH|FTW_PHYS;
if (cf->mount){
flag |= FTW_MOUNT;
}
retval = nftw(root, walk_func, 100, flag);
if (retval == -1) {
perror("nftw Error");
}
return retval;
}
int do_init(struct conf *cf)
{
int retval = 0;
convstr = (char*)malloc(maxlen);
if (!convstr)
{
perror("convstr");
retval = -1;
}
icd = iconv_open(cf->to, cf->from);
if(icd == (iconv_t) -1) {
printf("Cannot convert from %s to %s.\n", cf->from, cf->to);
perror("Iconv");
retval = -1;
}
return retval;
}
/* Read command line options. */
int do_opt(int argc, char* argv[], struct conf *cf)
{
int retval;
int c;
cf->to = cf->from = cf->root = NULL;
cf->warn = 1;
cf->mount = 0;
while(1) {
int curoptind = optind ? optind : 1;
c = getopt(argc, argv, "-wmt:f:");
if (c == -1)
break;
switch (c) {
case 1:
if (!cf->root)
cf->root = optarg;
break;
case 't':
cf->to = optarg;
break;
case 'f':
cf->from = optarg;
break;
case 'w':
cf->warn = 0;
break;
case 'm':
cf->mount = 1;
break;
case '?':
return -1;
break;
}
}
if(!(cf->to && cf->from && cf->root)) {
printf("Usage: %s [-w|-m] -f encode -t encode filename \n", argv[0]);
return -1;
}
return 0;
}
/* confirm if user want to continue. */
int print_confirm(struct conf *cf)
{
/* Warn the user. */
char* c;
int retval;
retval = 0;
printf ("I am going to convert \"%s\" and its subdirectory (if any) \n"
"from [%s] to [%s]. It is **dangerous**!!! It might mess \n"
"up all your file system!\n\n"
"=== \"%s:\" [%s] -> [%s] ===\n",
cf->root, cf->from, cf->to, cf->root, cf->from, cf->to);
if (cf->mount){
printf("Will follow links in other mounted file systems!!!\n");
}else{
printf("Will stay in the same mounted file systems.\n");
}
c = readline("Are you sure?! [N/y]: ");
if (!strcasecmp(c, "y")) {
free(c);
c = readline("Really? [N/y]: ");
if (!strcasecmp(c, "y"))
retval = 1;
}
free(c);
return retval;
}
int main(int argc, char* argv[])
{
int retval;
struct stat *myst;
struct conf *cf;
cf = &gcf;
/* parse command line. */
retval = do_opt(argc, argv, cf);
if(retval != 0)
return 1;
/* init global stuff. */
retval = do_init(cf);
if(retval == -1)
{
printf("Failed to initialize.\n");
return 2;
}
/* Make sure the 'root' is a valid file or directory. */
myst = (struct stat*) malloc(sizeof(struct stat));
retval = stat(cf->root, myst);
free(myst);
if (retval == -1) {
printf("%s cannot be converted.\n", cf->root);
perror(cf->root);
return 3;
}
/* Warning user of the danger. */
if (cf->warn){
retval = print_confirm(cf);
if(!retval) /* not confirmed. */
return 0;
}
/* Real action. */
retval = do_walk(cf->root, cf);
return retval;
}
Reply to: