2007年5月6日星期日

Firefox取证分析基础

Firefox配置目录

Firefox默认的配置文件目录为***.Default

操作系统

路径

Windows 95/98/Me

C:\Windows\Application Data\Mozilla\Firefox\Profiles\<Profile name>\

Window NT 4.x

C:\Winnt\Profiles\<Windows login/user name>\Application Data\Mozilla\Firefox\Profiles\<Profile name>\

Windows 2000/XP

C:\Documents and Settings\<Windows login/user name>\Application Data\Mozilla\Firefox\Profiles\<Profile name>\

即:

%APPDATA%\Mozilla\Firefox\Profiles\<Profile name>\

Windows Vista

C:\Users\<Windows login/user name>\AppData\Roaming\Mozilla\Firefox\Profiles\<Profile name>\

Unix

~/.mozilla/Firefox/<Profile name>/

Max OS X

~/Library/Mozilla/Firefox/Profiles/<Profile name>/

~/Library/Application Support/Firefox/Profiles/<Profile name>/

配置下的目录和文件

目        录

名 称

说 明

bookmarkbackups

每日循环备份书签(起始于Firefox 1.5)

Cache

缓存网络文件。Firefox 1.5以后用about:cache查看缓存文件夹位置及关于缓存文件的信息

chrome

"chrome"文件夹只用于保存 userChrome.css 和 userContent.css 文件

extensions

已安装的扩展

microsummary-generators

(始于Firefox 2.0)网页上最重要信息的简短摘要,摘要会定期更新

searchplugins

(始于Firefox 1.5)
包含搜索引擎插件及搜索引擎插件在搜索栏中的图标


 

文        件

名 称

说 明

.autoreg

用于通知已安装扩展发生变化的临时空文件

blocklist.xml

自动下载的危险扩展列表

bookmarks.bak

bookmarks.html 文件备份

bookmarks.html

书签

bookmarks.html.moztmp

临时书签文件。如果存在该文件,请去掉它的"只读"属性,否则会导致创建多个编号的 bookmarks-n.html 文件

bookmarks-(date).html

(在 bookmarkbackups 文件夹)

书签的每日循环备份

cert8.db

安全证书

compatibility.ini

存储上次使用过本配置文件的应用程序的版本和路径。当一个不同版本/路径的应用程序载入这个配置文件时,将触发 XPCOM 组件注册过程。该文件自动生成,可以安全删除

components.ini

以 XPCOM 组件列出扩展文件夹。在 1.5 中被 extensions.ini 文件替代。

compreg.dat

列出注册的 XPCOM 组件。触发 XPCOM 注册过程时自动生成。

Cookies.txt

Cookies

cookies.txt.moztmp

临时 cookies 文件。如果存在该文件,要么删除它要么去掉它的"只读"属性,否则会导致生成多个编号的 cookies-n.txt 文件

defaults.ini

在 Firefox 1.5 之后被 extensions.ini 文件替代

downloads.rdf

下载历史。可以删除该文件以解决程序缓慢或挂起问题

extensions.cache

列出已安装的扩展、它们的 ID 和安装文件夹,以及最后更改时间。例如将文件夹拖放到已知的安装位置,该文件用于注册以这种方式安装的扩展。该文件自动生成,可以删除该文件以解决某些问题。

extensions.ini

列出已安装扩展和主题的文件夹。该文件由 nsExtensionManager 自动生成,低级代码用以检测 chrome 包以及已安装的附加软件提供的 XPCOM 组件。可以删除该文件以解决某些问题

extensions.rdf

已安装扩展的信息。在 Firefox 1.5 或 Thunderbird 1.5 中可以删除该文件以清除一些卸载后仍在扩展列表中"卸载不掉"的扩展项目,也可以解决其他一些问题。

extensions-startup.manifest

该文件曾在 Deer Park alphas 中使用,以后改称 extensions.cache

Formhistory.dat

保存的表单数据

history.dat

浏览历史。可以删除该文件以解决某些问题

hostperm.1

针对每个站点是否允许 cookies 及弹出窗口等的设置。

key3.db

密钥数据库

kf.txt

(始于Firefox 2.0)反钓鱼网站密钥数据库

localstore.rdf

工具栏和窗口大小/位置的设置。可以删除该文件以解决某些问题。

localstore-safe.rdf

(始于Firefox 1.5)
在安全模式中使用的特殊版本的 localstore.rdf 文件,用以将工具栏和窗口重置为默认设置

lock (Linux)

同parent.lock

mimeTypes.rdf

在下载某些类型的文件时执行的动作。可以删除该文件以重置下载动作

parent.lock

用以标记当前正在使用的配置文件。可以删除该文件以解除配置文件锁定。

prefs.js

所有设置参数。参见: about:config

search.rdf

关于搜索插件的信息

search.sqlite

(始于Firefox 2.0) 关于搜索插件的信息

secmod.db

安全模块数据库

sessionstore.js

(始于Firefox 2.0)存储会话

signons.txt

加密保存的密码,需要与 key3.db 文件一起工作

urlclassifier.sqlite

(始于Firefox 2.0)反钓鱼数据库

webappsstore.sqlite

(始于Firefox 2.0)会话存储

xpti.dat

列出已注册的 XPCOM 接口。XPCOM 注册过程触发时自动生成。

ul.mfasl

参见 XUL.mfl

XUL.mfl

缓存用户接口数据。可以删除该文件以解决某些问题 (文件位置在 Firefox 1.5之后已经改变,位于C:\Documents and Settings\<user>\Local Settings\Application Data\Mozilla\Firefox\Profiles\***.default\目录下)

XUL FastLoad File

参见 XUL.mfl


 

配置文件夹之外的文件

名 称

说 明

pluginreg.dat

插件 Mimetypes 的注册信息。默认位置在包含配置文件夹的父文件夹。

profiles.ini

追踪配置文件位置。默认位置在包含配置文件夹的父文件夹。可以编辑该文件以指向已移动位置的配置文件夹。如果删除该文件,重新启动程序时将会重新生成一个新的默认配置文件夹和 profiles.ini 文件

registry.dat

在旧版的Firefox中用于追踪配置文件,现在被profiles.ini代替


 

History.dat

History.dat 存储在配置文件目录下,用于保存浏览历史:最近访问页面的记录。默认情况下Firefox存储最近9天的浏览历史。如果文件变得很大,将会拖慢Firefox的启动速度,可以通过清除历史或删除history.dat加快启动过程。

History.dat文件以一种称之为"Mork"复杂的格式保存。可以通过Firefox历史侧边拦编辑文件,但不推荐手工改动文件。

Mork术语

首先介绍描述Mork文档元素的术语(term)。每个术语描述了一个在Mork中使用的实体(Entry)。每个类型的实体有其自身的标记(markup)结构。我们在描述这些标记之前先定义每种类型的实体。每个实体包括一个斜体的简称,一个粗体的典型代号和蓝色的全称。有时我们更喜欢简称,例如用lit优先于使用literal。

  • val - (V) value: 单元格的值; 直接量(literal)或引用
  • lit - (L) literal: 八位一组的二进制序列; 例如文本值;例如行中的单元值
  • tab - (T) table: 稀疏矩阵; 行的集合; 无用存储单元收集根(a garbage collection root).
  • row - (R) row: 单元格的序列; 某个对象的属性; 表横向片断(注:即行).
  • col - (C) column: 命名一个属性的literal; 表纵向片断(注:即列).
  • cell - (.) cell: 表格中行和列相交的(col val)值对
  • id - (I) identity: 命名作用域内的不允许重复的二进制值, 命名直接量(literal)或对象(object).
  • oid - (O) object identity: 受作用域限制的对象/实体不重复的名称或id.
  • ref - (^) reference: 表示对象或literal Id的值, 代替直接量(literal).
  • dict - (D) dictionary: 在作用域内关联ids和literal的映射表.
  • atom - (A) atom: 没有作为列名使用的literal,例如文本值.
  • map - (M) hashmap: 映射键(key)和值(value)的哈希表 (e.g. ids to objects).
  • scope - (S) scope: 冒号后缀的名称,在其内部ids是不重复的(unique).
  • group - (G) group: a set of related changes applied atomically; a markup transaction.


 

模型 Model

基本的mork内容模型是包含单元格的行和列组成的表(稀疏矩阵),每个单元格隶属于特定的列(col)。每个单元格是行的一个属性。属性名称就是命名列的literal,属性的内容就是值。单元格的内容可以是literal(lit)也可以是引用(ref)。每个引用都可以指向literal(lit)、行(row)或则表(table),所以单元格可以通过引用"包含"另外一个共享对象(shared object)。

Mork提及的内容,不需要向前引用(forward references)和声明(declaration)。默认的,新的内容被认为是附加到已经存在的之前提及的内容之上的。Mork标记是面向更新(update oriented)的,其语法可以编辑已经存在的对象或则新定义一个对象。Mork更新可以组织成组(group),相关的修改必须作为事务(transaction)自动应用。典型的Mork文件也许是包含一系列的日志结构格式(log structure format)的组(group)。任何只有开始而没有正确结束的组必须被读取程序(reader)忽略。

根 Roots

当复制旧的Mork存储到新的文件时,未使用的(unused)内容将被省略。但是如何定义未使用呢?任何在表中没有提及的内容被认为是未使用的,未使用内容可以剪除以节省空间消耗。

Any table is a garbage collection root making referenced content reachable for preservation. Tables are never collected. To reduce table space consumption, one can only remove all row members.

Naive Mork implementations must avoid a subtle refcounting bug that can arise due to content written in nearly random hash based order. Markup might say to remove a row from one table early on, then add this same row to another table later. In between, the refcount might be zero, but the row's content must not be destroyed eagerly. A zero refcount only means dead content after all markup has been applied.

分析器 Parser

你期望自己动手写一个分析器,本文只是一个线索,不是标准。简单的无歧异德Hard part不是语法。Hard part意味着Mork标记。

开始 Start

1.4版的Mork文件以下面作为开始的第一行

    // <!-- <mdb:mork:z v="1.4"/> -->

你可以忽略这一行,因为这行没有任何意义,其只是声明文件格式而已,即表明这是一个Mork格式的文件。大概今后版本将和1.4版本看起来不一样。

即使你没有忽略这第一行,这个C++风格的注释在Mork中相当于空白,因此是无意义的。

    * start ::= magic (items | group)+


 

注释 Comments

你的Mork符号处理器要视C++风格的注释——以//开始到行结束——为空白字符。Mork认为任何CR或LF(0xD或0xA)的组和为行结束符,不管是单一出现的或则成对出现的。


 

空格 Space

白带(whitespace)是可选的,大多数时候Mork允许其出现。语法检查器趋向于用空格(space)替换在其可能出现的地方。

  • space ::= (white)*
  • white ::= ( #xA | #xD | #x9 | #x20 | comment | sp )
  • sp ::= /*other bytes you might wish to consider whitespace*/
  • comment ::= #xA not-endline endline
标题 Header

本实体小节可能被舍弃。1.4版的源代码好像没有处理1.1版程序放映的文件行。(下面是原文,因为可能没什么用,就不花时间翻译了)

This entire section might be obsolete. The 1.4 source code doesn't seem to process the 1.1 grammar showing a file row. So perhaps this was dropped before version 1.4. The parser in morkParser.cpp appears to recognize @ only as part of group syntax.

After the first line, Mork typically begins with exactly one file row which is metainfo about the Mork file itself, expressed as a row. This special purpose row is its own gc root, so it doesn't need to be a member of a table to persist. It looks different from any other row because it surrounds the normal row syntax with @ bytes. Here's an example with a single cell inside:

@[ (author=rys)]@

Rows are enclosed in [] square brackets. This row contains a single cell in column author with literal value rys. This example is arbitrary. Presumably the file row metainfo is a good place for apps to embed their own magic head info, without disturbing the other content of the file. If a file has more than one file row, or perhaps none, you might tolerate this to be lenient and permissive since no harm is done.


 

内容 Content

文件特征声明之后,剩下的Mork文件内容是条目序列(sequences of items)。对象(rows, dicts, tables)或则补充资料(updates)。条目随意的收集成组应用于基本事务。

  • items ::= (object | update)*
  • group ::= gather items (commit | abort)

组的语法非常复杂(most complex),所以我们首先陈述它。

组 Group

Mork组,是被一起自动处理单元的顶层内容集(A Mork group is a related set of top level content applied all together in one atomic unit),不管是否完全(either in its entirety or not at all),作为日志结构格式事务处理的机制(as a mechanism for handling transactions in a log structured format)。在1.4版中,@总是紧跟着$$作为组标记语法的一部分。

  • group ::= gather items (commit | abort)
  • gather ::= space @$${ id {@
  • commit ::= space @$$} id }@
  • abort ::= space @$$}~abort~ id }@

在实践中,分析器会期望在@之后立即看见$$,{表示一个事务组开始,id是事务的十六进制标识(identity)。例如,假设id等于FACE,同样值的id必须出现在commit或则abort中,以标记组的结束。分析器(parser)如何确保commit或abort中的内容自动应用的(全部或则根本没有)?

主要的,分析器记得紧跟着gather的字节位置,所以可以在定位组结束后重新定位(reseek)这个位置。然后向前移动查找@$$}FACE}@或则@$$}~abort~FACE}@(即commit或abort)。如果位置找到,分析器可以定位保存的紧跟在gather之后的位置并分析group当中的所有内容。否则,group应该被忽略。

如果分析器在发现旧的组结束之前发现了新的组开始,这将被视作第一组的终止(即没有及时发现组结束的组)。这种解释允许增加到被生成程序(writer)截断的存储之后。

注:the choice of group markup syntax seen here and literal escape metachar syntax shown later are related。格式良好的Mork文档应该不能再literal中插入$$,because $ is a metachar which only escapes hex and not itself。这是故意的,这样就不会因为literal的内容仅仅含有@$$就意外的终止事务。格式良好的literal会用@$\$替换@$$。这是jwz的问题(why there is more than one escape metachar)答案。因为他有助于避免用户数据的讹误。

As a result, a parser can generally get along with one non-space character lookahead. Seeing @ means group markup is expected. Then a parser looks for $$ which can't appear inside literals when Mork writers follow the rules. This is exactly why $$ was chosen here

符号 Tokens

你也许期望在此看见在组之后的条目(item)的资料。但在此我们将改变战术,自底向上查看组成更高层次结构的符号和原始数据表示法。

大多数符号都是但字符的, 或则可以被看作是单字符。一些非literal的复杂的多字节符号有一个同义的单字符#x20 whitespace byte after having whatever parsing effect is needed.

例如,一旦表明一个组的标记服务于组内部的内容时,其相当于一个空白字符。

注意一行的条件,行尾的反斜杠(\),当其出现在literal中间时根本不是符号。相反,符号解析器应该忽略它并假设其从来没有出现过似的。

另外,Mork使用下面的单字节符号集。

  • < - open angle – 开始一个字典(dict)(inside a dict, begins metainfo row)
  • > - close angle – 结束一个字典
  • [ - open bracket – 开始一行(row) (inside a row, begins metainfo row)
  • ] - close bracket – 结束一行
  • { - open brace – 开始一张表(table) (inside a table, begins metainfo row)
  • } - close brace – 结束一张表
  • ( - open paren – 开始一个单元格(cell)
  • ) - close paren – 结束一个单元格
  • ^ - up arrow – 用紧接着的id所指向的literal替换id
  • r - lower r – 用紧接着的行(row)oid引用替换行(row)oid
  • t - lower t -用紧接着的表(table)oid引用替换表(table)oid
  •  : - 冒号(colon )- 下一个值是当前处理id的作用域名称
  • = - equals – 在单元格中开始一个直接量(literal)
  • + - plus - add update: insert content
  • - - minus - cut update: remove content
  •  ! - bang - put update: clear and set content
Ids

在Mork中,对象标示(object identities)以十六进制书写。所以一个id标示仅仅是一个十六进制字符序列,没有大小写区分。因为被解释为整型,大小写是没有意义的。

有些名称总是所有id都可见的(Some namespace is always understood as the scope for every id), 但这种情况并不出现在id符号本身(but this does not appear in the id token itself)。 作用域总是紧跟在id后面的冒号后,详见oid的描述。

  • id ::= hex+.
  • hex ::= [0-9a-fA-F]
Oids

Mork oid指对象id(object id),包括十六进制id和作用域名称。 当没有明确的规定时,作用域隐含的标示为默认作用域(When not explicitly stated, the scope is implicitly understood as some default for each context)。

  • oid ::= id | id:scope
  • scope ::= literal | ^id | ^oid

注意scope的第三个选项有可能在实际应用中并不支持或使用,因为其可能导致oid和scope递归。你也许期望看见^id:literal 和 ^id:^id,但并不期望^id:^id:literal。

直接量 Literals

Mork literal是一组明码标示的八位一组的二进制序列。每个字节都变成literal的一部分,知道发现有特定含义的元字符为止。(Why is there more than one metachar? Because it might shrink markup. Complexity here is worth some compression.)

  • ) - close paren - end of literal
  • $ - dollar - escape the next two hex bytes which encode a single octet
  • \ - backslash – 如果下一个字节是#xA或#xD,literal忽略行结束;否则转义下一个字节。例如, \去掉紧接着的\、$、)元字符(metachar)状态。即起到转义字符的作用。

第一个元字符是闭合的括号。Literal总是出现在单元格的最后,所以在实践中literal总是被)终止。得到literal中的)的唯一方法是某种方法的转义。

第二个元字符是$符号,$允许编码任何八位组字节表示十六进制。有些Mork写入程序(writers)可能会用这种方法编码所有非ascii值,2000年版本的Mozilla就是这么做的,但这不是必须的。写入时,你不需要使用$转义字节,但读取者必须转义$后紧跟着的十六进制(当$本身没有被\转义)。 (为什么需选择$呢?因为Mork内容通常是URL,所以选择一个在URL当中不常用的字符。)

第三个元字符时反斜杠\,引入\允许用类似C的语法转义元字符,并且允许像C风格以行的将很长的行放在不同的行中显示。

(If I was going to extend Mork for base 64, I'd probably extend the meaning of the $ metachar to understand a following non-hex byte was the start of a base 64 sequence. For example, ${ABC} might mean ABC was base 64 content. I've seen folks online bitch about Mozilla's verbose encoding of unicode under Mork using $ to encode every null byte. Why didn't you speak up during the year I discussed it online? In five years, why did no one tweak Mork so version 1.5 would do better? Why not just write unicode as raw binary, since Mork supports that? Why does Mork suck because no one spends an hour making changes? Whatever.)

条目 Items

现在我们最后看看条目(items),which is where all interesting Mork content is actually found.

  • items ::= (object | update)*
  • object ::= (dict | row | table)
  • edit ::= (+ | - | !)
  • update ::= edit (row | table)

(注意dict 没有出现在update当中仅仅是因为dict没有标示(identity),所以因此不能被更新。)

当分析器没有在组的顶层发现@,其首先期望发现下面当中的某一个:

  • + : the next object adds or inserts content listed to existing content.
  • - : the next object cuts or removes content listed from existing content.
  •  ! : the next object adds content listed, after clearing or deleting old existing content.
  • < : begin a dict.
  • [ : begin a row.
  • { : begin a table.

因为下面的小节中会描述dict、row和table,本节剩下的部分将不再多描述他们了。The + for add is really Mork's default state because it is implied when missing before a row or table.如果row或table已经存在,那么新的内容将简单增加到已经存在内容后。(当然,向已经存在的列添加单元格将替换旧的值。)

书写器(writer)会发现重写一个对象比增加或裁减的效率高多了,可以使用!清除旧的内容重新开始。

当书写器发现增量裁减比从头开始构建一个巨大的对象花的代价要小时,可以在对象前使用 !以表明内容被删除了而不是插入的。

字典 Dicts

字典的目的是枚举所有和id关联的literal。分析器期望使用映射表(hashmap)关联每个id和literal,任何在单元格中引用给定id的地方都视作对lit值得引用。

Mork实现也许会使用不止一个映射表(map)——每个作用域都需要一个。但1.4版的Mork仅仅使用两个作用域:a 表示 atom literals 、 c 表示 column literals. 前者, a,是字典中的默认作用域(scope),除非通过包含(atomScope=c)单元格的元字典(metadict)改变为后者。

(注意:为简化语法,每个literal符号之前可选的空白被省略了,那些值中间的空白除外。因为鼓励空白是效率低下的,所以我们不希望在值之前看见可选的空白,但你可以选择容忍多余的空白。)

  • dict ::= < (metadict | alias)* >
  • metadict ::= < (cell)* >
  • alias ::= ( id value )
  • value ::= ^oid | =literal
  • oid ::= id | id:scope

这里的语法没有显示对id的约束,id可以使不低于80的十六进制值。 所有低于80的值为保留定义:any single ascii byte literal with ascii value 0xNN is defined to have id NN. Mork实现从80开始升序的给id赋值(或则从已经存在的最大值开始赋值).

原则上,元字典(metadict)也许包含任何类型的单元格。但在1.4版Mork,仅仅等于(atomScope=a)和(atomScope=c)有意义。他们改变后来发现的id的作用域。但在元字典(metadict)代替使用(atomScope=a) 返回默认作用域到a,你可以简单的使用><以关闭或再打开一个新的字典,只需要几个字节。

< <(atomScope=c)> (80=cards)(81=dn)(82=modifytimestamp)(83=cn)

(84=givenname)(85=mail)(86=xmozillausehtmlmail)(87=sn)>


 

<(90=cn=John Hackworth,mail=jhackworth@atlantis.com)(91=19981001014531Z)

(92=John Hackworth)(93=John)(94=jhackworth@atlantis.com)(95=FALSE)

(96=Hackworth)>

分析上面的例子,oid 85:c的值为mail,oid 96:a的值为Hackworth。使用^ 语法以表明一个oid,当列作用域c为默认是通常写作^85,而当atom作用域a为默认时写作^96。

别名 Alias

Dict中的别名语法几乎等同于cell;他们也使用圆括号界定边界。但值对的第一个元素是id,而cell的第一个元素是col。加之,cell允许第二个元素有更多变化,而alias约束为literal或引用literal的oid。

  • alias ::= ( id value )
  • value ::= ^oid | =literal

书写器在此写入非必须的(optional)空格可不是好主意,因为这将降低空间效率。但是阅读去应该是可以容许以外的空格的。

是否因该允许向前引用还没有定义的alias ids?也许,这将有些许损害。It would be consistent with the system of making things exist as soon as they are mentioned. 所以没有定义的oid通常指向一个空值。 这就是为什么在row和table中使用literal还不如使用oid呢!

行 Rows

行是在逻辑上无序的单元格。任何物理顺序并不意味有语义含义。所有的事情就是col出现在单元格中,literal或其引用为col的值。

(为简化这里的语法,token之前非必须的空格没有出现。)

  • row ::= [ roid (metarow | cell)* ]
  • roid ::= oid /*default scope is rowScope from table*/
  • metarow ::= [ (cell)* ]
  • cell ::= ( col slot )
  • col ::= ^oid /*default scope is c*/ | name
  • slot ::= ^oid /*default scope is a*/ | =literal

在col位置,id的默认作用域是c,在slot位置,默认的作用域是a。roid的默认作用域依赖于上下文—— 表的默认作用域。If the scope is itself an oid for a literal, the default scope is c; so when roid is 1:^80 this means 1:^80:c.

< <(atomScope=c)> (80=cards)(81=dn)(82=modifytimestamp)(83=cn)

(84=givenname)(85=mail)(86=xmozillausehtmlmail)(87=sn)>


 

<(90=cn=John Hackworth,mail=jhackworth@atlantis.com)(91=19981001014531Z)

(92=John Hackworth)(93=John)(94=jhackworth@atlantis.com)(95=FALSE)

(96=Hackworth)>


 

[1:^80 (^81^90)(^82^91)(^83^92)(^84^93)(^85^94)(^86^95)(^87^96)]

示例行用实体的oid写成,这不怎么具有可读性。所以我们用literal替换id重写一下本行。注意所有的col的默认作用域为c而所有的slot的默认作用域为a。

[ 1:cards (dn=cn=John Hackworth,mail=jhackworth@atlantis.com)

(modifytimestamp=19981001014531Z)(cn=John Hackworth)(givenname=John)

(mail=jhackworth@atlantis.com)(^xmozillausehtmlmail=FALSE)(sn=Hackworth)]

改写之后的内容和原先是一致的,但更具有可读性,代价是从其他行内复制内容相同的值。

单元格 Cells

单元格基本上就是被命名的值,其中名称就是列的literal内容,即列名。如果排列整理表中相同的列,单元格将组成稀疏矩阵的列。

  • cell ::= ( col slot )
  • col ::= ^oid /*default scope is c*/ | name
  • name ::= [a-zA-Z:_] [a-zA-Z:_+-?!]*
  • slot ::= ^oid /*default scope is a*/ | =literal
  • oid ::= id | id:scope

注意单元格也能通过oid指向row或table,甚至这个特性还没有被任何Mork程序使用过。

实际上,读取者也许会允许列名包含除^、=和)外的任何字节。注意,在sn列空的literal值将被写为:(sn=)。

表格 Tables

表示row的集合。表不能被检索,无论其是否有序。我认为表是row的映射(hashmap),映射roid到row,row可以以任何顺序出现。

但是如果表是拍过序的,采用数组访问表也许是个更好的方法。在任何时候,Mork语法不涉及这个方面的讨论。

  • table ::= { toid (metatable | row | roid )* }
  • toid ::= oid /*default scope is c*/
  • metatable ::= { (cell)* }

{ 1:cards {(rowScope=cards)(tableKind=Johns)} 1 2 }

metatable包含metainfo单元格,但Mork认为仅有很少的可能性。在此,我们看见roid的默认作用域被(rowScope=cards)设置为cards。这表明row id 1和2被解释为1:cards和2:cards,或则1:^80和2:^80(^80指代cards)。

表的角色(或目的或类型)被(tableKind=Johns)指定。这支持运行时检查表是否是toid的一个替代。

{ 1:^80 {(rowScope^80:c)(tableKind=Johns)} 1:^80 2:^80 } // with oids

在此表使用oid代替字符串literal重写了。但这张表被认为等同最近在dict中定义的80:c表。

{ 1:^80 {(rowScope^80:c)(tableKind=Johns)} // with explicit row cells

  [ 1:^80 (^81^90)(^82^91)(^83^92)(^84^93)(^85^94)(^86^95)(^87^96)]

  [ 2 (mail=galtj@atlantis.com)(cn=John Galt)]

}

This last version of the same table actually defines contents of the member rows using explicit [] notation. You need only define a row in one place, and any other use of that same row can include the row as a member by reference, using only the row's oid. The Mork writer for Mozilla in 2000 made an effort to write the actual contents of any entity one time only, by keeping track of whether it was "clean" in the sense of having been written once already in the current printing task.

标识 Identity

每个对象都有一个唯一的称之为oid的标识 (object identity),由两部分组成:id和scope。

注意scope是namespace的同义替代。请避免对namespacce使用的混淆。 (这里和XML中的namesapce没有任何关系。)

Mork颠倒了传统的命名习惯,名称出现在id之后。Mork使用id:scope代替namespace:name。但他们的含义是相同的。

Scope namespace的目的是允许不同的应用程序可以处理同一份Mork文件而不至于造成标识(identity)冲突,只要使用不同的namespace就行了。非常长的scope names以确保唯一性是可以的,因为scope可以同id替换名称。

Atoms

Mork是基于interned-string octet sequence值原语的,通常称之为atoms. (David Bienvenu 更喜欢atomized strings而不是interned strings.)这个术语给Mork实现设定了一个假定: hash each value namespace scope should have a map hashing string keys to atom values, so every instance of the same string can be shared as the same atom instance. You needn't do this, but it's less confusing if you do. You also want a map from id to atom, to resolve val hex oid references.

Unicode

Mork并不知道或关心unicode等宽字符 。所有的Mork内容被解释为比特序列,可以包含应用程序喜欢的一切内容。所有看起来像二进制的东西都被转义,所以不会干扰Mork标记,Mork标记使用的是基本的ascii码(或等价的字符)。Mork简单的嵌入内容语法不强迫包含数据类型。Mork转义二进制内容的效率并不高,典型的使用三个字节以表示难以使用的字节:$00表示十六进制零。(选择$是任意的,就是选择一个不大经常出现在Mork内容中的字符。Space efficiency for binary was never high priority in whimsical demands from managment.)

Brevity

为什么要为字符串val使用多于一个的namespace?列名称有其自己的namespace,所以列id的长度通常不会超过两个十六进制数字,程序通常不会有数量巨大的属性名称。

如果列和其val出现在同一namespace,如果普通的列名称和id比较长Mork文件将会更大。所以其实是压缩策略是的文件格式变得复杂了。

引导 Bootstrap

Mork的部分内容是自描述的,以使得其反射(reflective)。这使得自检问题成为可能。For this reason, Mork defines the ids of single byte scope names specially: the integer value of a single octet name is defined to be the id of that name. This means all scope name ids less than 0x80 are already assigned. A Mork implementation must dynamically assign ids increasing from 0x80.

Default scope names for col's and ordinary atom val's are defined as follows:

  • col - c (for col) with id 0x63 (equal to 'c')
  • val - a (for atom) with id 0x61 (equal to 'a')
对象 Objects

Mork模型使得应用程序对象成为一行(row),分离的单元格对应对象的属性。属性名称就是列(名)。对象的集合形成一张表(稀疏矩阵)。

Mork也把meatinfo表示为row中的属性,so the anotation of a table or row with metainfo appears as another row, distinguished by privileged syntax. Primary parts of metainfo include object identity, kind or type, and default scope for contained objects.

没有评论: