2 * Copyright 2003-2007, Haiku. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
6 * Axel Dörfler, axeld@pinc-software.de
7 * jonas.sundstrom@kirilla.com
11 #include <TranslationKit.h>
12 #include <Application.h>
26 extern const char *__progname
;
27 const char *gProgramName
= __progname
;
28 bool gVerbose
= false;
33 void Add(uint32 type
);
34 bool Remove(uint32 type
);
35 bool FindType(uint32 type
);
37 void SetTo(TypeList
&types
);
39 uint32
TypeAt(int32 index
);
45 class RemovingFile
: public BFile
{
47 RemovingFile(const char *path
);
58 Translator(const char *inPath
, const char *outPath
, uint32 outFormat
);
64 status_t
Directly(BFile
&input
, translator_info
&info
, BFile
&output
);
65 status_t
Indirectly(BFile
&input
, BFile
&output
);
66 status_t
FindPath(const translation_format
*info
, BPositionIO
&stream
,
67 TypeList
&typesSeen
, TypeList
&path
, double &pathQuality
);
68 status_t
GetMimeTypeFromCode(uint32 type
, char *mimeType
);
71 BTranslatorRoster
*fRoster
;
72 BPath fInputPath
, fOutputPath
;
76 class TranslateApp
: public BApplication
{
79 virtual ~TranslateApp();
81 virtual void ReadyToRun(void);
82 virtual void ArgvReceived(int32 argc
, char **argv
);
85 void PrintUsage(void);
86 void ListTranslators(uint32 type
);
88 uint32
GetTypeCodeForOutputMime(const char *mime
);
89 uint32
GetTypeCodeFromString(const char *string
);
90 status_t
Translate(BFile
&input
, translator_info
&translator
, BFile
&output
, uint32 type
);
91 status_t
Translate(BFile
&input
, BFile
&output
, uint32 type
);
92 status_t
Translate(const char *inPath
, const char *outPath
, uint32 type
);
95 BTranslatorRoster
*fTranslatorRoster
;
96 int32 fTranslatorCount
;
97 translator_id
*fTranslatorArray
;
102 print_tupel(const char *format
, uint32 value
)
106 for (int32 i
= 0; i
< 4; i
++) {
107 tupel
[i
] = (value
>> (24 - i
* 8)) & 0xff;
108 if (!isprint(tupel
[i
]))
113 printf(format
, tupel
);
118 print_translation_format(const translation_format
&format
)
120 print_tupel("'%s' ", format
.type
);
121 print_tupel("(%s) ", format
.group
);
123 printf("%.1f %.1f %s ; %s\n", format
.quality
, format
.capability
,
124 format
.MIME
, format
.name
);
132 TypeList::Add(uint32 type
)
134 fList
.AddItem((void *)(addr_t
)type
, 0);
139 TypeList::Remove(uint32 type
)
141 return fList
.RemoveItem((void *)(addr_t
)type
);
146 TypeList::FindType(uint32 type
)
148 return fList
.IndexOf((void *)(addr_t
)type
) >= 0;
153 TypeList::SetTo(TypeList
&types
)
157 for (int32 i
= 0; i
< types
.Count(); i
++)
158 fList
.AddItem((void *)(addr_t
)types
.TypeAt(i
));
165 return fList
.CountItems();
170 TypeList::TypeAt(int32 index
)
172 return (uint32
)(addr_t
)fList
.ItemAt(index
);
179 RemovingFile::RemovingFile(const char *path
)
180 : BFile(path
, B_READ_WRITE
| B_CREATE_FILE
| B_ERASE_FILE
),
186 RemovingFile::~RemovingFile()
202 Translator::Translator(const char *inPath
, const char *outPath
, uint32 outputFormat
)
204 fRoster(BTranslatorRoster::Default()),
206 fOutputPath(outPath
),
207 fOutputFormat(outputFormat
)
212 Translator::~Translator()
218 Translator::Translate()
222 status_t status
= input
.SetTo(fInputPath
.Path(), B_READ_ONLY
);
223 if (status
!= B_OK
) {
224 fprintf(stderr
, "%s: could not open \"%s\": %s\n",
225 gProgramName
, fInputPath
.Path(), strerror(status
));
229 // find a direct translator
231 translator_info translator
;
232 status
= fRoster
->Identify(&input
, NULL
, &translator
, 0, NULL
, fOutputFormat
);
234 // no direct translator found - let's try with something else
235 status
= fRoster
->Identify(&input
, NULL
, &translator
);
237 fprintf(stderr
, "%s: identifying \"%s\" failed: %s\n",
238 gProgramName
, fInputPath
.Path(), strerror(status
));
246 RemovingFile
output(fOutputPath
.Path());
247 if ((status
= output
.InitCheck()) != B_OK
) {
248 fprintf(stderr
, "%s: Could not create \"%s\": %s\n",
249 gProgramName
, fOutputPath
.Path(), strerror(status
));
254 status
= Directly(input
, translator
, output
);
256 status
= Indirectly(input
, output
);
258 if (status
== B_OK
) {
261 // add filetype attribute
262 update_mime_info(fOutputPath
.Path(), false, true, false);
264 char mimeType
[B_ATTR_NAME_LENGTH
];
265 BNode
node(fOutputPath
.Path());
266 BNodeInfo
info(&node
);
267 if (info
.GetType(mimeType
) != B_OK
|| !strcasecmp(mimeType
, B_FILE_MIME_TYPE
)) {
268 // the Registrar couldn't find a type for this file
269 // so let's use the information we have from the
271 if (GetMimeTypeFromCode(fOutputFormat
, mimeType
) == B_OK
)
272 info
.SetType(mimeType
);
275 fprintf(stderr
, "%s: translating failed: %s\n",
276 gProgramName
, strerror(status
));
283 /** Converts the input file to the output file using the
284 * specified translator.
288 Translator::Directly(BFile
&input
, translator_info
&info
, BFile
&output
)
291 printf("Direct translation from \"%s\"\n", info
.name
);
293 return fRoster
->Translate(&input
, &info
, NULL
, &output
, fOutputFormat
);
297 /** Converts the input file to the output file by computing the best
298 * quality path between the input type and the output type, and then
299 * applies as many translators as needed.
303 Translator::Indirectly(BFile
&input
, BFile
&output
)
305 TypeList translatorPath
;
306 TypeList handledTypes
;
309 if (FindPath(NULL
, input
, handledTypes
, translatorPath
, quality
) != B_OK
)
310 return B_NO_TRANSLATOR
;
312 // The initial input stream is the input file which gets translated into
313 // the first buffer. After that, the two buffers fill each other, until
314 // the end is reached and the output file is the translation target
317 BPositionIO
*inputStream
= &input
;
318 BPositionIO
*outputStream
= &buffer
[0];
320 for (int32 i
= 0; i
< translatorPath
.Count(); i
++) {
321 uint32 type
= translatorPath
.TypeAt(i
);
322 if (type
== fOutputFormat
)
323 outputStream
= &output
;
325 outputStream
->SetSize(0);
327 inputStream
->Seek(0, SEEK_SET
);
328 // rewind the input stream
330 status_t status
= fRoster
->Translate(inputStream
, NULL
, NULL
, outputStream
, type
);
335 inputStream
= &buffer
[i
% 2];
336 outputStream
= &buffer
[(i
+ 1) % 2];
339 print_tupel(" '%s'\n", type
);
347 Translator::FindPath(const translation_format
*format
, BPositionIO
&stream
,
348 TypeList
&typesSeen
, TypeList
&path
, double &pathQuality
)
350 translator_info
*infos
= NULL
;
351 translator_id
*ids
= NULL
;
357 // Get a list of capable translators (or all of them)
359 if (format
== NULL
) {
360 status
= fRoster
->GetTranslators(&stream
, NULL
, &infos
, &count
);
361 if (status
== B_OK
&& count
> 0) {
362 inFormat
= infos
[0].type
;
363 group
= infos
[0].group
;
366 puts("Indirect translation:");
367 print_tupel(" '%s', ", inFormat
);
368 printf("%s\n", infos
[0].name
);
372 status
= fRoster
->GetAllTranslators(&ids
, &count
);
373 inFormat
= format
->type
;
374 group
= format
->group
;
376 if (status
!= B_OK
|| count
== 0) {
382 // build the best path to get from here to fOutputFormat recursively
383 // (via depth search, best quality/capability wins)
386 double bestQuality
= -1;
387 status
= B_NO_TRANSLATOR
;
389 for (int32 i
= 0; i
< count
; i
++) {
390 const translation_format
*formats
;
392 bool matches
= false;
393 int32 id
= ids
? ids
[i
] : infos
[i
].translator
;
394 if (fRoster
->GetInputFormats(id
, &formats
, &formatCount
) != B_OK
)
397 // see if this translator is good enough for us
398 for (int32 j
= 0; j
< formatCount
; j
++) {
399 if (formats
[j
].type
== inFormat
) {
408 // search output formats
410 if (fRoster
->GetOutputFormats(id
, &formats
, &formatCount
) != B_OK
)
413 typesSeen
.Add(inFormat
);
415 for (int32 j
= 0; j
< formatCount
; j
++) {
416 if (formats
[j
].group
!= group
|| typesSeen
.FindType(formats
[j
].type
))
419 double formatQuality
= formats
[j
].quality
* formats
[j
].capability
;
421 if (formats
[j
].type
== fOutputFormat
) {
422 // we've found our target type, so we can stop the path here
423 bestPath
.Add(formats
[j
].type
);
424 bestQuality
= formatQuality
;
431 if (FindPath(&formats
[j
], stream
, typesSeen
, path
, quality
) == B_OK
) {
432 if (bestQuality
< quality
* formatQuality
) {
433 bestQuality
= quality
* formatQuality
;
434 bestPath
.SetTo(path
);
435 bestPath
.Add(formats
[j
].type
);
441 typesSeen
.Remove(inFormat
);
444 if (status
== B_OK
) {
445 pathQuality
= bestQuality
;
446 path
.SetTo(bestPath
);
455 Translator::GetMimeTypeFromCode(uint32 type
, char *mimeType
)
457 translator_id
*ids
= NULL
;
459 status_t status
= fRoster
->GetAllTranslators(&ids
, &count
);
463 status
= B_NO_TRANSLATOR
;
465 for (int32 i
= 0; i
< count
; i
++) {
466 const translation_format
*format
= NULL
;
467 int32 formatCount
= 0;
468 fRoster
->GetOutputFormats(ids
[i
], &format
, &formatCount
);
470 for (int32 j
= 0; j
< formatCount
; j
++) {
471 if (type
== format
[j
].type
) {
472 strcpy(mimeType
, format
[j
].MIME
);
487 TranslateApp::TranslateApp(void)
488 : BApplication("application/x-vnd.haiku-translate"),
489 fGotArguments(false),
490 fTranslatorRoster(BTranslatorRoster::Default()),
492 fTranslatorArray(NULL
)
494 fTranslatorRoster
->GetAllTranslators(&fTranslatorArray
, &fTranslatorCount
);
498 TranslateApp::~TranslateApp()
500 delete[] fTranslatorArray
;
505 TranslateApp::ArgvReceived(int32 argc
, char **argv
)
508 || !strcmp(argv
[1], "--help"))
511 if (!strcmp(argv
[1], "--list")) {
512 fGotArguments
= true;
514 uint32 type
= B_TRANSLATOR_ANY_TYPE
;
516 type
= GetTypeCodeFromString(argv
[2]);
518 ListTranslators(type
);
522 if (!strcmp(argv
[1], "--verbose")) {
523 fGotArguments
= true;
532 fGotArguments
= true;
534 // get typecode of output format
535 uint32 outputFormat
= 0;
536 BMimeType
mime(argv
[3]);
538 if (mime
.IsValid() && !mime
.IsSupertypeOnly()) {
540 outputFormat
= GetTypeCodeForOutputMime(argv
[3]);
542 outputFormat
= GetTypeCodeFromString(argv
[3]);
544 if (outputFormat
== 0) {
545 fprintf(stderr
, "%s: bad format: %s\nformat is 4-byte type code or full MIME type\n",
546 gProgramName
, argv
[3]);
550 Translator
translator(argv
[1], argv
[2], outputFormat
);
551 status_t status
= translator
.Translate();
558 TranslateApp::ReadyToRun(void)
560 if (fGotArguments
== false)
563 PostMessage(B_QUIT_REQUESTED
);
568 TranslateApp::PrintUsage(void)
570 printf("usage: %s { --list [type] | input output format }\n"
571 "\t\"format\" can expressed as 4-byte type code (ie. 'TEXT') or as MIME type.\n",
577 TranslateApp::ListTranslators(uint32 type
)
579 for (int32 i
= 0; i
< fTranslatorCount
; i
++) {
580 const char *name
= NULL
;
581 const char *info
= NULL
;
583 if (fTranslatorRoster
->GetTranslatorInfo(fTranslatorArray
[i
], &name
, &info
, &version
) != B_OK
)
586 const translation_format
*inputFormats
= NULL
;
587 const translation_format
*outputFormats
= NULL
;
588 int32 inCount
= 0, outCount
= 0;
589 fTranslatorRoster
->GetInputFormats(fTranslatorArray
[i
], &inputFormats
, &inCount
);
590 fTranslatorRoster
->GetOutputFormats(fTranslatorArray
[i
], &outputFormats
, &outCount
);
592 // test if the translator has formats of the specified type
594 if (type
!= B_TRANSLATOR_ANY_TYPE
) {
595 bool matches
= false;
597 for (int32 j
= 0; j
< inCount
; j
++) {
598 if (inputFormats
[j
].group
== type
|| inputFormats
[j
].type
== type
) {
604 for (int32 j
= 0; j
< outCount
; j
++) {
605 if (outputFormats
[j
].group
== type
|| outputFormats
[j
].type
== type
) {
615 printf("name: %s\ninfo: %s\nversion: %" B_PRId32
".%" B_PRId32
".%"
616 B_PRId32
"\n", name
, info
,
617 B_TRANSLATION_MAJOR_VERSION(version
),
618 B_TRANSLATION_MINOR_VERSION(version
),
619 B_TRANSLATION_REVISION_VERSION(version
));
621 for (int32 j
= 0; j
< inCount
; j
++) {
623 print_translation_format(inputFormats
[j
]);
626 for (int32 j
= 0; j
< outCount
; j
++) {
627 printf(" output:\t");
628 print_translation_format(outputFormats
[j
]);
637 TranslateApp::GetTypeCodeForOutputMime(const char *mime
)
639 for (int32 i
= 0; i
< fTranslatorCount
; i
++) {
640 const translation_format
*format
= NULL
;
642 fTranslatorRoster
->GetOutputFormats(fTranslatorArray
[i
], &format
, &count
);
644 for (int32 j
= 0; j
< count
; j
++) {
645 if (!strcmp(mime
, format
[j
].MIME
))
646 return format
[j
].type
;
655 TranslateApp::GetTypeCodeFromString(const char *string
)
657 size_t length
= strlen(string
);
661 uint8 code
[4] = {' ', ' ', ' ', ' '};
663 for (uint32 i
= 0; i
< length
; i
++)
664 code
[i
] = (uint8
)string
[i
];
666 return B_HOST_TO_BENDIAN_INT32(*(uint32
*)code
);