11
#ifdef HAVE_SYS_STAT_H
12
# include <sys/stat.h>
13
#endif /* HAVE_SYS_STAT_H */
14
#ifdef HAVE_SYS_FILE_H
15
# include <sys/file.h>
16
#endif /* HAVE_SYS_FILE_H */
17
#ifdef HAVE_SYS_TYPES_H
18
# include <sys/types.h>
19
#endif /* HAVE_SYS_TYPES_H */
22
#endif /* HAVE_DIRENT_H */
26
#endif /* HAVE_GETOPT_H */
30
#ifndef RCC_OPTION_TRANSLATE_SKIP_PARENT
31
# define RCC_OPTION_TRANSLATE_SKIP_PARENT RCC_OPTION_TRANSLATE_SKIP_PARRENT
41
int mode = MODE_STDIN;
45
OPT_ENCODING_IN = 'e',
48
OPT_LANGUAGE_IN = 'l',
62
static struct option long_options[] = {
63
{"config", required_argument, 0, OPT_CONFIG },
64
{"from", required_argument, 0, OPT_FROM },
65
{"to", required_argument, 0, OPT_TO },
66
{"force-encoding", required_argument, 0, OPT_ENCODING_IN },
67
{"force-language", required_argument, 0, OPT_LANGUAGE_IN },
68
{"force-target-encoding", required_argument, 0, OPT_ENCODING_OUT },
69
{"force-target-language", required_argument, 0, OPT_LANGUAGE_OUT },
70
{"language-detection", required_argument, 0, OPT_AUTODETECT },
71
{"translation", optional_argument, 0, OPT_TRANSLATION },
72
{"caching", optional_argument, 0, OPT_CACHING },
73
{"cache", required_argument, 0, OPT_CACHE },
74
{"timeout", required_argument, 0, OPT_TIMEOUT },
75
{"force", no_argument, 0, OPT_YES },
76
{"allow-offline-processing",no_argument, 0, OPT_OFFLINE },
77
{"disable-subdirs", no_argument, 0, OPT_SUBDIRS },
78
{"stdin", no_argument, &mode, MODE_STDIN },
79
{"directory", no_argument, &mode, MODE_DIRECTORY },
80
{"file", no_argument, &mode, MODE_FILE },
81
{"filelist", no_argument, &mode, MODE_FILELIST },
82
{"help", no_argument, 0, OPT_HELP },
86
void Usage(int argc, char *argv[]) {
89
" %s [options] [mode] [file|directory]\n"
91
" --stdin - Convert stdin to stdout\n"
92
" --directory - Convert file names in specified directory\n"
93
" --file - Convert specified file\n"
94
" --filelist - Convert all files writed on stdin\n"
95
" --help - Help message\n"
98
" -c <config> - Specify configuration name\n"
99
" -f <class> - Source class ('in' is default)\n"
100
" -t <class> - Output class ('out' is default)\n"
101
" -e <enc> - Force specified source encoding (autodetection)\n"
102
" -l <lang> - Force specified source language (from LC_CTYPE)\n"
103
" --force-target-encoding=<enc>\n"
104
" - Convert to the specified encoding\n"
105
" --force-target-language=<enc>\n"
106
" - Translate to the specified language\n"
107
" --caching=[mode]\n"
108
" - Use recodings cache. Following modes are supported\n"
110
" use - Use cached values (default)\n"
111
" add - Add new recodings to cache\n"
112
" replace - Replace encodings in cache\n"
114
" - Use specified cache database instead of default one\n"
115
" --translation=[mode]\n"
116
" - Enable translation. Following modes are supported:\n"
118
" skip_parent - Skip translation to parent lang\n"
119
" skip_related - Skip translation between related langs\n"
120
" english - Translate to english (default)\n"
121
" transliterate - Transliterate\n"
122
" --language-detection=[mode]\n"
123
" - Lanuage autodetection. Following modes are supported:\n"
124
" off - Current language is considered\n"
125
" on - Use only configured langs (default)\n"
126
" all - Try everything (slow)\n"
128
" - Specify recoding timeout in microseconds (1s default)\n"
130
" -y - Do not ask any question\n"
131
" --disable-subdirs\n"
132
" - Do not descend into the sub directories\n"
134
" Language Relations:\n"
135
" To prevent unneccesary translations the concept of related/parent languages is\n"
136
" introduced. For each language you can specify a parent language.\n"
137
" skip_parent translation option will turn off translation to parent language\n"
138
" skip_related translation option will additionaly turn off translation from\n"
139
" parent language.\n"
141
" For example, in the default configuration Russian is parent of Ukrainian, and\n"
142
" English is parent of all other languages. With \"skip_parrent\" option the\n"
143
" translation from Russian to Ukrainian would be turned off, but translation\n"
144
" from Ukrainian to Russian would operate. With \"skip_related\" option the\n"
145
" translation in both directions would be disabled\n"
147
" Language Detection:\n"
148
" Current version uses aspell dictionaries to autodetect language. Therefore,\n"
149
" only languages with aspell available in the system aspell dictionaries are\n"
150
" autodected. Beware, if your system contains a lot of installed languages,\n"
151
" the autodection may take considerable amount of time.\n"
157
fs: is a standard class here, we do not need fs detecting here
159
static rcc_class classes[] = {
160
{ "unicode", RCC_CLASS_TRANSLATE_CURRENT, "UTF-8", NULL, "Dummy", 0 },
161
{ "in", RCC_CLASS_STANDARD, NULL, NULL, "Input Encoding", 0 },
162
{ "out", RCC_CLASS_TRANSLATE_CURRENT, "LC_CTYPE", NULL, "Output Encoding", 0 },
163
{ "id3", RCC_CLASS_STANDARD, "in", NULL, "ID3 Encoding", 0 },
164
{ "id3v2", RCC_CLASS_STANDARD, "id3", NULL, "ID3 v.2 Encoding", 0},
165
{ "pl", RCC_CLASS_STANDARD, "id3", NULL, "PlayList Title Encoding", 0},
166
{ "plfs", RCC_CLASS_STANDARD, "pl", NULL, "PlayList File Encoding", 0 },
167
{ "fs", RCC_CLASS_STANDARD, "LC_CTYPE", NULL, "FileSystem Encoding", 0 },
168
{ "arc", RCC_CLASS_STANDARD, "in", NULL, "Archives Encoding", 0 },
169
{ "oem", RCC_CLASS_STANDARD, "in", NULL, "Zip OEM Encoding", 0 },
170
{ "iso", RCC_CLASS_STANDARD, "in", NULL, "Zip ISO Encoding", 0 },
171
{ "ftp", RCC_CLASS_STANDARD, "in", NULL, "FTP Encoding", 0 },
172
{ "http", RCC_CLASS_STANDARD, "in", NULL, "HTTP Encoding", 0 },
173
{ "ssh", RCC_CLASS_STANDARD, "in", NULL, "SSH Encoding", 0 },
177
rcc_class_id GetClass(const char *name) {
180
for (i = 1; classes[i].name; i++) {
181
if ((!strcasecmp(name, classes[i].name))||(!strcasecmp(name, classes[i].fullname)))
184
return (rcc_class_id)-1;
188
static char process_subdirs = 1;
189
static rcc_language_id source_language_id, target_language_id;
190
static rcc_class_id source_class_id = 1, target_class_id = 2;
191
static char *efrom = NULL, *eto = NULL;
193
static int translate = RCC_OPTION_TRANSLATE_OFF;
196
char *Translate(const char *source);
197
int Stdin(const char *arg);
198
int Directory(const char *arg);
200
int main(int argc, char *argv[]) {
201
rcc_language_id language_id, current_language_id, english_language_id;
207
char *config_name = NULL;
208
char *cache_name = NULL;
213
unsigned char from_forced = 0;
214
unsigned char to_forced = 0;
219
int cache = RCC_OPTION_LEARNING_FLAG_USE;
223
int ldetect_force = 0;
225
unsigned long timeout = 0;
228
int option_index = 0;
229
while ((c = getopt_long(argc, argv, "yhe:f:l:t:", long_options, &option_index)) != -1) {
238
config_name = optarg;
250
case OPT_ENCODING_IN:
253
case OPT_ENCODING_OUT:
256
case OPT_LANGUAGE_IN:
259
Selects main language, but for translation we can switch on
260
autodetection. Should do it manualy.
262
if (!ldetect_force) {
268
case OPT_LANGUAGE_OUT:
271
case OPT_TRANSLATION:
273
translate = RCC_OPTION_TRANSLATE_TO_ENGLISH;
274
else if (!strcasecmp(optarg, "full"))
275
translate = RCC_OPTION_TRANSLATE_FULL;
276
else if (!strcasecmp(optarg, "skip_parent"))
277
translate = RCC_OPTION_TRANSLATE_SKIP_PARENT;
278
else if (!strcasecmp(optarg, "skip_related"))
279
translate = RCC_OPTION_TRANSLATE_SKIP_RELATED;
280
else if (!strcasecmp(optarg, "english"))
281
translate = RCC_OPTION_TRANSLATE_TO_ENGLISH;
282
else if (!strcasecmp(optarg, "transliterate"))
283
translate = RCC_OPTION_TRANSLATE_TRANSLITERATE;
284
else if (!strcasecmp(optarg, "off"))
285
translate = RCC_OPTION_TRANSLATE_OFF;
287
fprintf(stderr, "*** Unknown translation mode: %s\n\n", optarg);
292
if (!ldetect_force) {
293
if (!strcasecmp(optarg, "off"))
301
cache = RCC_OPTION_LEARNING_FLAG_USE;
302
else if (!strcasecmp(optarg, "off"))
304
else if (!strcasecmp(optarg, "use"))
305
cache = RCC_OPTION_LEARNING_FLAG_USE;
306
else if (!strcasecmp(optarg, "add"))
307
cache = RCC_OPTION_LEARNING_FLAG_USE|RCC_OPTION_LEARNING_FLAG_LEARN;
308
else if (!strcasecmp(optarg, "replace"))
309
cache = RCC_OPTION_LEARNING_FLAG_LEARN;
311
fprintf(stderr, "*** Unknown caching mode: %s\n\n", optarg);
319
if (!optarg) ldetect = 1;
320
else if (!strcasecmp(optarg, "off")) {
323
} else if (!strcasecmp(optarg, "on")) {
327
} else if (!strcasecmp(optarg, "all")) {
334
timeout = atoi(optarg);
352
if ((optind + 1) < argc) {
353
fprintf(stderr, "*** Invalid non-option arguments:\n");
354
for (;optind < argc;optind++) {
357
fprintf(stderr, "\n\n");
366
if (!from_forced) from = "fs";
367
if (!to_forced) to = "fs";
373
setlocale(LC_ALL, "");
378
rccInitDefaultContext(NULL, 0, 0, classes, 0);
379
rccInitDb4(NULL, cache_name, 0);
381
if (timeout) rccSetOption(NULL, RCC_OPTION_TIMEOUT, timeout);
383
if (config_name) rccLoad(NULL, config_name);
386
rccSetOption(NULL, RCC_OPTION_LEARNING_MODE, cache);
388
if (translate != RCC_OPTION_TRANSLATE_OFF)
389
rccSetOption(NULL, RCC_OPTION_TRANSLATE, translate);
392
rccSetOption(NULL, RCC_OPTION_AUTODETECT_LANGUAGE, 1);
394
rccSetOption(NULL, RCC_OPTION_CONFIGURED_LANGUAGES_ONLY, 0);
398
// DS: More checks, sometimes we can skip that.
399
if ((lfrom)||(lto)) {
400
// if (lfrom) rccSetOption(NULL, RCC_OPTION_AUTODETECT_LANGUAGE, 1);
401
rccSetOption(NULL, RCC_OPTION_CONFIGURED_LANGUAGES_ONLY, 0);
405
rccSetOption(NULL, RCC_OPTION_OFFLINE, 1);
409
source_class_id = GetClass(from);
410
if (source_class_id == (rcc_class_id)-1) {
412
fprintf(stderr, "*** Invalid source class (%s) specified\n", from);
417
target_class_id = GetClass(to);
418
if (target_class_id == (rcc_class_id)-1) {
420
fprintf(stderr, "*** Invalid target class (%s) specified\n", to);
425
current_language_id = rccGetCurrentLanguage(NULL);
426
english_language_id = rccGetLanguageByName(NULL, "en");
429
source_language_id = rccGetLanguageByName(NULL, lfrom);
430
if (source_language_id == (rcc_language_id)-1) {
432
fprintf(stderr, "*** Invalid source language (%s) specified\n", lfrom);
435
} else source_language_id = current_language_id;
438
target_language_id = rccGetLanguageByName(NULL, lto);
439
if (target_language_id == (rcc_language_id)-1) {
441
fprintf(stderr, "*** Invalid target language (%s) specified\n", lto);
444
} else target_language_id = current_language_id;
446
if (source_language_id == target_language_id) {
447
language_id = source_language_id;
449
if (language_id != current_language_id) {
450
if ((rccSetLanguage(NULL, language_id))||(!rccGetCurrentLanguageName(NULL))) {
452
fprintf(stderr, "*** Unable to set the specified language (%s)\n", rccGetLanguageName(NULL, language_id));
457
if (!rccGetCurrentLanguageName(NULL)) {
458
if (current_language_id != english_language_id) {
459
language_id = english_language_id;
460
rccSetLanguage(NULL, english_language_id);
463
if (!rccGetCurrentLanguageName(NULL)) {
465
fprintf(stderr, "*** Default language (%s) is not configured\n", rccGetLanguageName(NULL, current_language_id));
472
language_id = (rcc_language_id)-1;
474
// Checking if languages are selectable
475
if ((rccSetLanguage(NULL, source_language_id))||(!rccGetCurrentLanguageName(NULL))) {
477
fprintf(stderr, "*** Unable to set source language (%s)\n", rccGetLanguageName(NULL, source_language_id));
480
if ((rccSetLanguage(NULL, target_language_id))||(!rccGetCurrentLanguageName(NULL))) {
482
fprintf(stderr, "*** Unable to set target language (%s)\n", rccGetLanguageName(NULL, target_language_id));
495
fprintf(stderr, "*** Mode (FILE) is not supported in current version\n");
498
fprintf(stderr, "*** Mode (FILELIST) is not supported in current version\n");
508
// DS. Dynamicaly raise string length?
509
int Stdin(const char *arg) {
513
while (fgets(buf,16384,stdin)) {
514
res = Translate(buf);
515
fprintf(stdout, res?res:buf);
522
char *Fullname(const char *path, const char *name) {
525
res = (char*)malloc(strlen(path) + strlen(name) + 2);
527
if (path[strlen(path)-1] == '/')
528
sprintf(res, "%s%s",path,name);
530
sprintf(res, "%s/%s",path,name);
535
// DS: We do not follow symbolic links (add option?)
536
// DS: Skipping everything begining with point (system files)
537
int Directory(const char *arg) {
542
struct dirent *entry;
551
printf("Processing directory: %s\n", arg);
555
fprintf(stderr, "*** Failed to process directory: %s\n", arg);
559
entry = readdir(dir);
561
if (entry->d_name[0] == '.') {
562
entry = readdir(dir);
566
res = Translate(entry->d_name);
568
if (strcmp(res, entry->d_name)) {
570
printf("Rename \"%s\" to \"%s\" (y/[n]) ", entry->d_name, res);
571
scanf("%c", &answer);
572
if (answer != '\n') fgets(stmp, 255, stdin);
573
answer = ((answer=='y')||(answer=='Y'))?1:0;
579
fn = Fullname(arg, entry->d_name);
580
nfn = Fullname(arg, res);
582
if (!lstat(nfn, &st)) {
584
printf("Trying rename \"%s\" to \"%s\"\n", entry->d_name, res);
587
if (S_ISDIR(st.st_mode)) {
588
printf("*** Directory with that name exists, skipping\n");
591
printf("*** File exists, overwrite (y/[n]) ");
592
scanf("%c", &answer);
593
if (answer != '\n') fgets(stmp, 255, stdin);
594
answer = ((answer=='y')||(answer=='Y'))?1:0;
598
err = rename(fn, nfn);
606
printf("*** Renaming \"%s\" to \"%s\" is failed (errno: %u)\n", entry->d_name, res, errno);
608
printf("Rename completed: \"%s\" to \"%s\"\n", entry->d_name, res);
614
entry = readdir(dir);
618
if (process_subdirs) {
622
entry = readdir(dir);
624
if (entry->d_name[0] == '.') {
625
entry = readdir(dir);
629
fn = Fullname(arg, entry->d_name);
631
if ((!lstat(fn, &st))&&((S_ISDIR(st.st_mode)))) {
636
entry = readdir(dir);
645
char *Translate(const char *source) {
646
rcc_string rccstring;
647
char *recoded, *stmp;
649
if (strlen(source)<2) return NULL;
651
if (source_language_id != target_language_id) {
652
rccSetLanguage(NULL, source_language_id);
655
if (efrom) rccstring = rccFromCharset(NULL, efrom, source);
656
else rccstring = rccFrom(NULL, source_class_id, source);
658
if (!rccstring) return NULL;
660
if (source_language_id != target_language_id)
661
rccSetLanguage(NULL, target_language_id);
664
if (translate = RCC_OPTION_TRANSLATE_OFF) {
665
stmp = rccTo(NULL, target_class_id, rccstring);
667
recoded = rccRecodeCharsets(NULL, "UTF-8", eto, stmp);
668
if (recoded) free(stmp);
670
} else recoded = NULL;
673
recoded = rccToCharset(NULL, eto, rccstring);
675
} else recoded = rccTo(NULL, target_class_id, rccstring);