第十五课

我们自己的文件类型

尽管已经存在了许多可用的不同文件类型,其涵盖了从图片到视频文件,以至邮件等所有内容,但是有时候,我们的程序还是需要使用自己独立的文件类型。尽管能够使用一些非标准的扩展来创建文件,实际上在Haiku中创建一个有用并且成熟的文件类型还涉及到其他一些工作。它并不困难,但是确实需要将存储套件(Storage Kit)中的一些东西组织起来,以便使之很好的运行。

创建新类型

首先,在创建我们新的文件类型之前,我们需要稍加思考。问自己三个问题:

  • 我的新文件类型的扩展是什么?
  • 新类型的MIME类型是什么?
  • 图标该如何设计?

在这里,我们希望为文档处理程序 MyWrite 新建一种文本文档类型。使用与我们的程序相关的文件扩展名很有帮助,因此我们将使用四个字符的扩展名:mywr。

MIME 类型并不复杂,但是它们需要一些思考。必须确定文档的超级类型。MyWrite 文件是文本文档,因此它们具有 text 超级类型。其他的选择包括 audio,video,image,以及 application。Application 类型通常用于与其他超级类型不匹配的格式。压缩档案,如 7-Zip 和 Bzip2 即属于此类。一旦设定新类型的超级类型,它的其他部分也就确定了。非标准的 MIME 类型前缀为 “x-”。供应商指定类型前缀为 “vnd.”。对于我们的 MyWrite,它完整的MIME类型为 text/x-myrite。

对于某些开发者,图标可能是最难的部分,因为他们擅长编写代码,但却对绘图不知所措。类型的图标不一定要非常漂亮,尽管专业的图标确实能够给你的程序增加人气。Haiku 可供使用的图标工具是 Icon-O-Matic,它和操作系统绑定在一起发布。为了不分散注意力,我们将不介绍使用 Icon-O-Matic 创建图标的细节,我们把它放在其他章节讲述。我们将继续其他部分,并且假定您已经有了一个可以使用的图标。

我们需要使用 BMimeType 类安装我们新的 MIME 类型。它是 Haiku 的 MIME 数据库官方接口。尽管我们可以使用文件类型首选项,但我们需要能够在程序中创建该类型。下面的代码介绍了安装基本类型的方法。

#include <Mime.h>           // BmimeType 的头文件

// 该头文件保存了转换为C++代码的图表数据 – 更多内容见后文。
#include “IconDefs.h”

void
InstallMyWriteType(void)
{
    BMimeType mime;
    mime.SetType(“text/x-MyWrite”);
    mime.SetShortDescription(“MyWrite text document”);
    mime.SetLongDescription(“MyWrite text document”);
    // 为该类型设置我们的IconDefs.h中定义的常量数据结构中保存的图标。
    // 由于该函数已经引入了 Haiku 的API,在 Be Book 中并没有相关说明。
    mime.SetIcon(kVectorIconBits, sizeof(kVectorIconBits));

    // 设置类型扩展名
    BMessage extMsg;
    extMsg.AddString(“extensions”, “mywr”);
    mime.SetFileExtensions(&extMsg);

    // 设置打开我们的文件类型的首选应用的署名。
    mime.SetPreferredApp(“application/x-vnd.test.MyWrite”);

    // 如果我们使用BeOS R5 或者 Zeta,我们需要调用
    // BMimeType::Install()使所有修改立即生效。Haiku并
    // 没有这种需要,在每个方法调用时就会更新类型。
}

上述的代码看起来都不是很特别,尤其当您曾经使用过文件类型首选项。每个方法都会设置新类型的相关信息。还有一个非常方便的方法调用:SetSnifferRule()。嗅探规则和空中胶水没有任何关系,它们反而都是关于新类型文件的自动识别。MIME嗅探规则的唯一难点是它的语法,它非常的灵活,让很简单的规则看起来非常的怪异,而复杂的规则更让人头疼。不过没有关系,它们带来的便利足以与它们的难度相抵,并且在多数情况下这种难度并不很高。

文件自动识别:MIME 嗅探

MIME 嗅探规则通常遵循以下格式:

rating [begin:end] ([begin:end] ‘pattern1’ | …)

从表面看,这段看起来古怪的文本包括优先级排序,,字节范围,以及一系列放在圆括号中的模式。但是这只是其中一部分。在规则的最底层是我们所谓的模式对(pattern pair)。它有文本匹配模式和可选的用于模式的字节范围组成。模式本身必须放在单引号中,一个或多个模式对可以放在圆括号里面,但是由管道字符(|)分开。下述示例显示了三个模式对:

([0:15] ‘Sample’ | ‘Another Sample’ | [10:] ‘Third example’)

前一对用于在文件的前15个字符中搜索 Sample。第二对仅用于匹配头部为 Another Sample 的文件。第三对在文件的第十字节至文件末尾之间搜索 Third example。

位于 MIME 嗅探规则的模式对之上的是模式集(pattern set)。一个模式集包括一个可选的字节范围以及一个或多个放在圆括号中的模式对。模式集中的字节范围可以认为是备用的范围,仅用于未指定字节范围的模式对。

在最高层面上,MIME 嗅探规则是一个优先级排序和一个或多个模式集。优先级是一个位于 0.0 到 1.0 之间的浮点数,用于将其与 MIME 类型的其他规则进行优先级排序。模式集在逻辑上都可以认为是由逻辑与操作符链接起来 – 所有的模式集都必须和相应规则匹配。下面是一个实例:

.5 (‘foo’) ([20:] ‘BAR’) ([0:10] ‘baz’)

该规则的优先级为 0.5,并且需要 foo 位于文件头,BAR 需要位于文件的第 19 字节之后,而 baz 必须位于文件的前 10 个字节之中。

除了所有这些可能产生的疑惑,还有一些其他的方便的选项可供构建模式。首先是,-i 可放在模式集的第一个模式对之前,这样可以让模式集中所有的模式大小写不敏感。第二,可以对任何的模式使用掩码来指定某些字节(无关)紧要,这里在模式对中使用引用符号(&)。第三,可以使用前缀 和 x 来指定八进制和十六进制数。第四,浮点数可以使用科学计数法表示。在下面的示例中用到了这些选项,示例来源于 Haiku 的私有头文件 Parser.h。

200e-3 (-i ‘ab’)
0.70 ( “8BPS \000\000\000\000” & 0xffffffff0000ffffffff )

第一个示例的优先级为0.2,其用于在文件头部查找ab,并且忽略大小写。第二个稍微复杂一些:在文件的前 10 个字节中,前四个字节希望是 “8BPS”,忽略第五和第六个字节,而最后四个字节希望是0。忽略的字节由掩码中的 0 字节指定。Haiku 中使用第二个规则来识别 Adobe Photoshop 文件,它通常以如下字节作为开头:

38 42 50 53 00 01 00 00 00 00 00 00

前四个字节的字符串表示是 8BPS,而之后的两个字节值为 1,最后六个是保留字节,通常值为 0。

类型特别属性

一些文件类型具有与自身相关的特别属性。例如,Ogg Vorbis 和 MP3 文件使用属性 Audio:album,Audio:artist,Audio:title 来保存通常保存在标签里的信息。如果您的新类型也需要使用额外的属性,这些属性的添加操作也非常简单。你将有关的信息保存在 BMessage ,然后将该消息传递给 BMimeType::SetAttrInfo() 。例如,我们希望定制联系人( Person )文件类型以便为每个联系人的姓氏和名字保存为独立的苏醒。下面是您需要编写的设置这两个自定义属性的代码。

#include <Message.h>
#include <Mime.h>

int
main(void)
{
    BMimeType mime(“application/x-person”);
    BMessage attrMsg;

    // 我们需要获取联系人文件已有的信息,除非我们
    // 希望将其替换为下面编写的两个属性。但我们只
    // 是希望为联系人文件添加这两个属性,而不是替
    // 换已有的属性。
    mime.GetAttrInfo(&attrMsg);

    // 需要为消息添加一些内容以使自定义属性更为实
    // 用。
    attrMsg.AddString(“attr:public_name”, “First Name”);
    attrMsg.AddString(“attr:name”, “META:firstname”);
    attrMsg.AddInt32(“attr:type”, B_STRING_TYPE);
    attrMsg.AddBool(“attr:viewable”, true);
    attrMsg.AddBool(“attr:editable”, true);

    // 以下的三个内容在 Be Book 中并未说明,但是在
    // Tracker 中用于确定信息在 Tracker 窗口中的显示。
    attrMsg.AddInt32(“attr:width”, 120);
    attrMsg.AddInt32(“attr:alignment”, B_ALIGN_LEFT);
    attrMsg.AddBool(“attr:extra”, false);

    attrMsg.AddString(“attr:public_name”, “Last Name”);
    attrMsg.AddString(“attr:name”, “META:lastname”);
    attrMsg.AddInt32(“attr:type”, B_STRING_TYPE);
    attrMsg.AddBool(“attr:viewable”, true);
    attrMsg.AddBool(“attr:editable”, true);
    attrMsg.AddInt32(“attr:width”, 120);
    attrMsg.AddInt32(“attr:alignment”, B_ALIGN_LEFT);
    attrMsg.AddBool(“attr:extra”, false);

    mime.SetAttrInfo(&attrMsg);
}

课后思考

我们的新类型现在已经可以使用了。为了将一个文件设置为我们的新类型,我们需要一下选项:

  • BNode::WriteAttr()
  • BNode::WriteAttrString()
  • BNodeInfo::SetType()

以上三个方法对 BeOS R5 和 Zeta 都适用,但是在本书编写时,由于系统中存在未解决的问题,只有第三个方法能够正常使用。您的新文件类型将会出现在文件类型首选项应用,并且和其他标准的系统文件类型一样。