Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / bin / translate.cpp
blobc0ba867983dc9fbce11d33c4d074bcfa0c0d1169
1 /*
2 * Copyright 2003-2007, Haiku. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * jonas.sundstrom@kirilla.com
8 */
11 #include <TranslationKit.h>
12 #include <Application.h>
13 #include <String.h>
14 #include <File.h>
15 #include <Path.h>
16 #include <Entry.h>
17 #include <Mime.h>
18 #include <NodeInfo.h>
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <strings.h>
26 extern const char *__progname;
27 const char *gProgramName = __progname;
28 bool gVerbose = false;
31 class TypeList {
32 public:
33 void Add(uint32 type);
34 bool Remove(uint32 type);
35 bool FindType(uint32 type);
37 void SetTo(TypeList &types);
38 int32 Count();
39 uint32 TypeAt(int32 index);
41 private:
42 BList fList;
45 class RemovingFile : public BFile {
46 public:
47 RemovingFile(const char *path);
48 ~RemovingFile();
50 void Keep();
52 private:
53 BEntry fEntry;
56 class Translator {
57 public:
58 Translator(const char *inPath, const char *outPath, uint32 outFormat);
59 ~Translator();
61 status_t Translate();
63 private:
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);
70 private:
71 BTranslatorRoster *fRoster;
72 BPath fInputPath, fOutputPath;
73 uint32 fOutputFormat;
76 class TranslateApp : public BApplication {
77 public:
78 TranslateApp(void);
79 virtual ~TranslateApp();
81 virtual void ReadyToRun(void);
82 virtual void ArgvReceived(int32 argc, char **argv);
84 private:
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);
94 bool fGotArguments;
95 BTranslatorRoster *fTranslatorRoster;
96 int32 fTranslatorCount;
97 translator_id *fTranslatorArray;
101 static void
102 print_tupel(const char *format, uint32 value)
104 char tupel[5];
106 for (int32 i = 0; i < 4; i++) {
107 tupel[i] = (value >> (24 - i * 8)) & 0xff;
108 if (!isprint(tupel[i]))
109 tupel[i] = '.';
111 tupel[4] = '\0';
113 printf(format, tupel);
117 static void
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);
128 // #pragma mark -
131 void
132 TypeList::Add(uint32 type)
134 fList.AddItem((void *)(addr_t)type, 0);
138 bool
139 TypeList::Remove(uint32 type)
141 return fList.RemoveItem((void *)(addr_t)type);
145 bool
146 TypeList::FindType(uint32 type)
148 return fList.IndexOf((void *)(addr_t)type) >= 0;
152 void
153 TypeList::SetTo(TypeList &types)
155 fList.MakeEmpty();
157 for (int32 i = 0; i < types.Count(); i++)
158 fList.AddItem((void *)(addr_t)types.TypeAt(i));
162 int32
163 TypeList::Count()
165 return fList.CountItems();
169 uint32
170 TypeList::TypeAt(int32 index)
172 return (uint32)(addr_t)fList.ItemAt(index);
176 // #pragma mark -
179 RemovingFile::RemovingFile(const char *path)
180 : BFile(path, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE),
181 fEntry(path)
186 RemovingFile::~RemovingFile()
188 fEntry.Remove();
192 void
193 RemovingFile::Keep()
195 fEntry.Unset();
199 // #pragma mark -
202 Translator::Translator(const char *inPath, const char *outPath, uint32 outputFormat)
204 fRoster(BTranslatorRoster::Default()),
205 fInputPath(inPath),
206 fOutputPath(outPath),
207 fOutputFormat(outputFormat)
212 Translator::~Translator()
217 status_t
218 Translator::Translate()
220 // input file
221 BFile input;
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));
226 return status;
229 // find a direct translator
230 bool direct = true;
231 translator_info translator;
232 status = fRoster->Identify(&input, NULL, &translator, 0, NULL, fOutputFormat);
233 if (status < B_OK) {
234 // no direct translator found - let's try with something else
235 status = fRoster->Identify(&input, NULL, &translator);
236 if (status < B_OK) {
237 fprintf(stderr, "%s: identifying \"%s\" failed: %s\n",
238 gProgramName, fInputPath.Path(), strerror(status));
239 return status;
242 direct = false;
245 // output file
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));
250 return status;
253 if (direct)
254 status = Directly(input, translator, output);
255 else
256 status = Indirectly(input, output);
258 if (status == B_OK) {
259 output.Keep();
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
270 // translator
271 if (GetMimeTypeFromCode(fOutputFormat, mimeType) == B_OK)
272 info.SetType(mimeType);
274 } else {
275 fprintf(stderr, "%s: translating failed: %s\n",
276 gProgramName, strerror(status));
279 return status;
283 /** Converts the input file to the output file using the
284 * specified translator.
287 status_t
288 Translator::Directly(BFile &input, translator_info &info, BFile &output)
290 if (gVerbose)
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.
302 status_t
303 Translator::Indirectly(BFile &input, BFile &output)
305 TypeList translatorPath;
306 TypeList handledTypes;
307 double quality;
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
316 BMallocIO buffer[2];
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;
324 else
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);
331 if (status != B_OK)
332 return status;
334 // switch buffers
335 inputStream = &buffer[i % 2];
336 outputStream = &buffer[(i + 1) % 2];
338 if (gVerbose)
339 print_tupel(" '%s'\n", type);
342 return B_OK;
346 status_t
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;
352 int32 count;
353 uint32 inFormat = 0;
354 uint32 group = 0;
355 status_t status;
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;
365 if (gVerbose) {
366 puts("Indirect translation:");
367 print_tupel(" '%s', ", inFormat);
368 printf("%s\n", infos[0].name);
371 } else {
372 status = fRoster->GetAllTranslators(&ids, &count);
373 inFormat = format->type;
374 group = format->group;
376 if (status != B_OK || count == 0) {
377 delete[] infos;
378 delete[] ids;
379 return status;
382 // build the best path to get from here to fOutputFormat recursively
383 // (via depth search, best quality/capability wins)
385 TypeList bestPath;
386 double bestQuality = -1;
387 status = B_NO_TRANSLATOR;
389 for (int32 i = 0; i < count; i++) {
390 const translation_format *formats;
391 int32 formatCount;
392 bool matches = false;
393 int32 id = ids ? ids[i] : infos[i].translator;
394 if (fRoster->GetInputFormats(id, &formats, &formatCount) != B_OK)
395 continue;
397 // see if this translator is good enough for us
398 for (int32 j = 0; j < formatCount; j++) {
399 if (formats[j].type == inFormat) {
400 matches = true;
401 break;
405 if (!matches)
406 continue;
408 // search output formats
410 if (fRoster->GetOutputFormats(id, &formats, &formatCount) != B_OK)
411 continue;
413 typesSeen.Add(inFormat);
415 for (int32 j = 0; j < formatCount; j++) {
416 if (formats[j].group != group || typesSeen.FindType(formats[j].type))
417 continue;
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;
425 status = B_OK;
426 continue;
429 TypeList path;
430 double quality;
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);
436 status = B_OK;
441 typesSeen.Remove(inFormat);
444 if (status == B_OK) {
445 pathQuality = bestQuality;
446 path.SetTo(bestPath);
448 delete[] infos;
449 delete[] ids;
450 return status;
454 status_t
455 Translator::GetMimeTypeFromCode(uint32 type, char *mimeType)
457 translator_id *ids = NULL;
458 int32 count;
459 status_t status = fRoster->GetAllTranslators(&ids, &count);
460 if (status != B_OK)
461 return status;
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);
473 status = B_OK;
474 break;
479 delete[] ids;
480 return status;
484 // #pragma mark -
487 TranslateApp::TranslateApp(void)
488 : BApplication("application/x-vnd.haiku-translate"),
489 fGotArguments(false),
490 fTranslatorRoster(BTranslatorRoster::Default()),
491 fTranslatorCount(0),
492 fTranslatorArray(NULL)
494 fTranslatorRoster->GetAllTranslators(&fTranslatorArray, &fTranslatorCount);
498 TranslateApp::~TranslateApp()
500 delete[] fTranslatorArray;
504 void
505 TranslateApp::ArgvReceived(int32 argc, char **argv)
507 if (argc < 2
508 || !strcmp(argv[1], "--help"))
509 return;
511 if (!strcmp(argv[1], "--list")) {
512 fGotArguments = true;
514 uint32 type = B_TRANSLATOR_ANY_TYPE;
515 if (argc > 2)
516 type = GetTypeCodeFromString(argv[2]);
518 ListTranslators(type);
519 return;
522 if (!strcmp(argv[1], "--verbose")) {
523 fGotArguments = true;
524 argc--;
525 argv++;
526 gVerbose = true;
529 if (argc != 4)
530 return;
532 fGotArguments = true;
534 // get typecode of output format
535 uint32 outputFormat = 0;
536 BMimeType mime(argv[3]);
538 if (mime.IsValid() && !mime.IsSupertypeOnly()) {
539 // MIME-string
540 outputFormat = GetTypeCodeForOutputMime(argv[3]);
541 } else
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]);
547 exit(-1);
550 Translator translator(argv[1], argv[2], outputFormat);
551 status_t status = translator.Translate();
552 if (status < B_OK)
553 exit(-1);
557 void
558 TranslateApp::ReadyToRun(void)
560 if (fGotArguments == false)
561 PrintUsage();
563 PostMessage(B_QUIT_REQUESTED);
567 void
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",
572 gProgramName);
576 void
577 TranslateApp::ListTranslators(uint32 type)
579 for (int32 i = 0; i < fTranslatorCount; i++) {
580 const char *name = NULL;
581 const char *info = NULL;
582 int32 version = 0;
583 if (fTranslatorRoster->GetTranslatorInfo(fTranslatorArray[i], &name, &info, &version) != B_OK)
584 continue;
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) {
599 matches = true;
600 break;
604 for (int32 j = 0; j < outCount; j++) {
605 if (outputFormats[j].group == type || outputFormats[j].type == type) {
606 matches = true;
607 break;
611 if (!matches)
612 continue;
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++) {
622 printf(" input:\t");
623 print_translation_format(inputFormats[j]);
626 for (int32 j = 0; j < outCount; j++) {
627 printf(" output:\t");
628 print_translation_format(outputFormats[j]);
631 printf("\n");
636 uint32
637 TranslateApp::GetTypeCodeForOutputMime(const char *mime)
639 for (int32 i = 0; i < fTranslatorCount; i++) {
640 const translation_format *format = NULL;
641 int32 count = 0;
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;
650 return 0;
654 uint32
655 TranslateApp::GetTypeCodeFromString(const char *string)
657 size_t length = strlen(string);
658 if (length > 4)
659 return 0;
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);
670 // #pragma mark -
674 main()
676 new TranslateApp();
677 be_app->Run();
679 return B_OK;