DocBook 文件製作進階(一) 文件模組化

老貢生

聯絡人
台灣企鵝寶寶工作隊
Revision History
Revision Revision: 1.1.2.9Date: 2004/03/08 14:11:53
第一次正式發佈

本文主要在探討 DocBook 如何利用 SGML 格式語言的實體(ENTITY),能達到類似像一般程式語言編譯時,那種常數宣告,以及含括插入外部模組檔案的功能,來達成文件的模組化。以及藉由此種模組化,使得文件分工,共同撰寫成為可能,文件基本片段的再利用,變得輕而易舉。最後,你會發覺,採用 DocBook 格式來製作文件,不管是新做或修正,都將大幅儉省組織的耗用時間,增加一個團隊的工作效率。

本文採用 GNU 組織的 GPL公開版權宣告,歡迎複製,改寫,散佈,並要求你應該以同樣的權利條款,授予文件的使用人。對 GPL 的進一步的解釋,請參考GNU 通用公共許可證


Table of Contents
1. 實體(ENTITY)的基本概念
1.1. ENTITY 宣告的語法
1.2. ENTITY 在那裡宣告
1.3. 實體 (ENTITY) 的使用
2. 各種 ENTITY 應用範例
2.1. 字串內容替換
2.2. 重複使用固定的文件片段
2.3. 過時連結修正機制
3. 共同寫作環境的佈置
3.1. 多人寫作的基本環境佈置
3.2. 主副文件結構
3.3. 在主文件中使用外部實體宣告檔
3.4. 子文件中含括延伸外部實體宣告檔案
3.5. 在子文件中插入子文件
3.6. 輸出整份文件
4. 依解譯器實作而不同的 ENTITY 功能
4.1. 條件編譯
5. 結語
6. DocBook 相關問答集
DocBook 參考字典

本文是系列 DocBook 格式文件探討的第三篇,也是比較艱深一點的課題。如果你以前從未接觸過 DocBook 格式文件,或不太熟悉的,建議你先讀一下 DocBook 解譯轉換環境的建立DocBook 文件寫作入門 這兩篇基礎文件。但如果你看過些 DocBook 文件,覺得它不過是某種類似的 HTML ,只不過換些標籤名稱,加了點自動編輯功能,大費周章之後,還不如 HTML 能對輸出樣式做更精準控制的話,那你就一定要看這篇文章。因為本篇文章將一一向你展示,那些只有 SGML 文件才做得到,不只 HTML ,就是你所熟知的其他樣式排版文件,也無法達成的功能,也就是這些才讓 SGML 有無可取代的理由。

DocBook 文件模組化牽涉很多 SGML 和 XML 相關語法,這兩門標記語法的專業領域我涉獵尚淺。文章中如有疏漏或錯誤,請來函 指正,我會盡快修訂並加以公佈。如果你想閱覽本文 SGML 原始格式,請 按此處下載


1. 實體(ENTITY)的基本概念

DocBook 可以一次輸入,多種格式輸出,這是透過內容資料與表現資料分開,及 DSSSL 或 XSLT 的樣式表技術達成。DocBook 可以作為文字型態的樹狀資料庫,供網路搜尋,因為 DocBook 的標記採內容意義分類,並有嚴謹的結構。DocBook 的所有功能就只有這些嗎?當然不是,DocBook 的最大功能就是作為組織與團隊利用的共同寫作與快速修訂系統。

談到共同寫作,相信常在 Unix 系統下工作的人都會想到 CVS Server 的架設,以及最近大行其道的 Wiki 網頁編輯系統。但不管是 CVS 還是 Wiki ,做的都是文件修訂的版本控制工作,他幫助一個團體組織不會因為大家的共同參與,而把文件改的不統一,一團亂。但是 DocBook 要負擔的共同寫作功能卻不是版本控制,他是負責把一個大文件拆成許多小部份,讓不同的人去負責不同的片段。每個人在他自己的片段裡,只要按著 DocBook 規矩把資料填進去,不需要去顧慮別人怎麼做,最後卻能把所有片段合在一起,編譯成一個完整一致的整體文件,完全看不出來有各自為政的不協調。

這種把一個大文件拆成小片段的功能,是如何達成的呢?答案就在 SGML 語言的實體(ENTITY)技術運用。透過實體(ENTITY),一個由 SGML 語言定義的格式文件可以達成類似程式語言的常數宣告,以及含括插入外部檔案的功能。

程式語言透過常數宣告和外部含括檔案來達成模組化,使得某些基本的程式敘述可以被重複利用,SGML 格式文件也透過實體(ENTITY)將文件資料模組化,達成拆卸重組的功能。所以實體的運用,是 SGML 語言的本有功能,DocBook 繼承了這個能力,而 HTML 放棄了這個功能。[1]換句話說,就是你可以在 DocBook 中使用 SGML 的實體功能(ENTITY),因為 DocBook 的 SGML 解析器會支援這項功能。[2]但一般的網頁瀏覽器則不支援 HTML 使用實體(ENTITY)的功能。


1.1. ENTITY 宣告的語法

ENTITY 就如同程式語言的常數宣告一樣,要先宣告後,才能在程式敘述中加以引用,而宣告一個 ENTITY 的完整敘述是:

Table 1. ENTITY 的宣告敘述

<!ENTITY%實體的名稱SYSTEM"實體內容字串">
 註一註二註三註四註五 

註一, 宣告新增實體

我們在 SGML prolog 區段中,在 "<!" 字串後鍵入 ENTITY 表示通知 SGML 解析器要新增一個實體來使用。

註二, 實體前置字元宣告

SGML 的 ENTITY 在使用方式的不同上,可分為參數實體(Parameter ENTITY)和一般實體(General ENTITY),我們將先講述 General ENTITY ,Parameter ENTITY 比較複雜,放在稍後講。General 和 Parameter 的分別就是 Parameter 採用 '%' 作為前置字元,而 General 不使用任何前置字元。要注意的是,Parameter 的前置字元 '%' 必須和前面的 ENTITY 和後面的實體名稱字串,都至少以一個空白鍵隔開。

註三, 實體名稱宣告

就是為你要使用的實體取個簡短好記得名字,這裡要注意,同名的實體如果重複宣告,解析器並不會發出錯誤通知,但會採用先佔策略,也就是同名的實體不管被宣告幾次,其內容都以第一次宣告時的設定為準。此外實體名稱前後不可以使用 '"' 字元做字串表示式的包夾。

註四, System identified 表示詞

當一個 ENTITY 在宣告實體名稱與實體內容字串之間有加一個 SYSTEM 描述詞時,就表示後面跟著的是一個 System Identifer 。什麼是 System Identifier ?講白一點就是檔案路徑位址啦,也就是你要在你的 DocBook 文件裡插入外部檔案,就必須先鍵入 SYSTEM 描述詞,然後跟著打外部檔案的路徑位址,可以是網路位址,也可以是本機位址,可以是絕對路徑,也可以是相對路徑。那沒打 SYSTEM 描述詞是什麼意思?就是實際顯示的內容字串啦。譬如:

<!ENTITY somename SYSTEM "/etc/passwd">
&somename;    顯示 /etc/passwd 這個檔案的內容

<!ENTITY somename "/etc/passwd">
&somename;    直接顯示 /etc/passwd 這個字串

所以有加 SYSTEM 描述詞的作用是含括外部檔案,稱為 external ENTITY ,沒加 SYSTEM 描述詞的,作用是做常數宣告,稱為 internal ENTITY 。

註五, 實體內容字串

如果明白了 external ENTITY 和 internal ENTITY 的差別,那實體內容字串代表的意義,就非常清楚了,不是檔案位址,就是時實際顯示的字串內容,要注意的是,不管是直接內容或檔案路徑,都必須在頭尾以 '"' 字符包夾。

Note

ENTITY 宣告的選項與選項之間,除了 "<!" 與 ENTITY 之間不要插入空白字符外,其他都必須插入一個以上的空白字符。


1.2. ENTITY 在那裡宣告

知道了 ENTITY 要怎麼宣告後,接著我們要知道 ENTITY 在那裡宣告,就如一般程式語言都有固定的宣告區,SGML 格式文件也有固定的 ENTITY 宣告區,不在宣告區宣告,就會被解析器判定語法錯誤。

一般說來,ENTITY 的宣告是屬於 SGML DTD 的一部分,是跟 ELEMENT,ATTLIST,..... 這些宣告組成綁一起的,也就是必須在一個 SGML 文件的 prolog 區完成的。prolog 區?那在那裡?其實不難判別,就是一個 SGML 文件的根標籤的開始標籤以前的區段,就叫 SGML prolog 。

你的第二個問題,天哪!難道要我去修改 DocBook 那個看都看不懂的天書,DTD 模組檔案嗎?嗯!原則上來說是可以的,但不鼓勵這麼做。第一:很少人能清楚掌握 DocBook 龐大 DTD 的組織結構,所以你不見得能找到正確的加入區段。第二:你很可能不經意的在宣告 ENTITY 時候,干擾了 DocBook DTD 原始的 ENTITY 宣告,縱使最後解析結果無誤,但套句 DocBook 官方的說法,如果你更改了 DocBook 的 ELEMENT or ATTLIST ,那就不能再叫 DocBook ,而變成 YourBook 了。


1.2.1. 開啟 prolog 的 subset 區段

所幸 SGML 語言早就考慮到 DTD 延展性的問題,我們可以藉由一些表示式來表示除了 DTD 檔案模組外,我們要開啟文件作者個人的 subset 區段(位於 prolog 區中),來放使用者自己定義的 ENTITY 宣告。例如:

Example 1. 延長 SGML 文件的 subset 區段

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.2//EN" [  <!-- 表示 subset 區開始 -->
<!ENTITY teamname "台灣企鵝寶寶工作隊">                      <!-- internal ENTITY -->
<!ENTITY authfile SYSTEM "/etc/passwd">                      <!-- external ENTITY -->
]>                                                           <!-- 表示 subset 結束 -->

Example 2. 延長 XML 文件的 subset 區段

<?xml version='1.0' encoding="Big5"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
                  "http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd"[
<!ENTITY teamname "台灣企鵝寶寶工作隊">
<!ENTITY authfile SYSTEM "/etc/passwd">
]>

這裡要來澄清幾個觀念問題,我們曾在 ENTITY 宣告語法一節說,在 SGML 預設的選項下,同名稱的實體被重複宣告不算語法錯誤,只是採先佔策略,先宣告的先贏。但是 SGML 對 prolog 區的執行順序卻是,先執行我們 user 自己在 subset 的宣告敘述,再去執行 PUBLIC Identifier 指到的 DTD 檔案模組,所以如果我們在 subset 不長眼睛,剛好宣告了 DTD 模組中會用到的 ENTITY ,由於先佔先贏,你的 ENTITY 設定值會取代 DocBook ENTITY 的設定值,結果當然是天下大亂,解析器報告錯誤一定連篇累牘。

那怎樣避開一個 DocBook 格式 DTD 的重複 ENTITY 宣告呢,查清楚 DocBook 的保留字嗎?很抱歉,這辦法不通。因為除了 DTD 中 DocBook 使用了大量的 ENTITY 宣告來協助他構築文件結構外,他還引用了一大堆 ISO 組織定義的 ENTITY 特殊字元表,數量之龐大,不要說一般人難以記憶,而且每次改版,ENTITY 宣告名稱一定大幅改變,讓你記不勝記。[3]

情況真是那麼絕望嗎?其實換條路走,採用名稱空間的命名方式,就能保證你不發生 ENTITY 宣告衝突。例如前面兩個範例,你如果覺得 teamname 或 authfile 還是不夠安全的話,就為他加個前置字串吧。例如我喜歡用 twbaby 當作我自己組織的前置字串,那我就可以改用:

<!ENTITY twbaby.teamname "台灣企鵝寶寶工作隊">
<!ENTITY twbaby.authfile SYSTEM "/etc/passwd">

來宣告我自己專用的 ENTITY,雖然增加點麻煩,但可以保證不會有發生毀壞 DTD 結構的狀況。

同時觀念澄清到這裡,也可以順便回答一個附帶的問題。為何 HTML 同樣是 SGML 格式文件,卻不能使用 SGML 的 ENTITY 宣告功能?因為你沒有辦法編輯 HTML 的 prolog 區,他的 prolog 訊息及設定,寫死到瀏覽器裡去了。


1.3. 實體 (ENTITY) 的使用

ENTITY 既經宣告後,就可以在 SGML 的文件敘述中使用,但因為 ENTITY 的分類不同,也有使用地點和方式的差異。譬如:

Example 3. ENTITY 的使用方式及場合

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
<!ENTITY teamname "台灣企鵝寶寶工作隊">
<!ENTITY authfile SYSTEM "/etc/passwd">
<!ENTITY % twbaby.include SYSTEM "test.ent">        <!-- 標誌外部宣告檔 -->
%twbaby.include;                                    <!-- 使外部檔案的宣告生效 -->
]>
<article><title>&teamname; SGML 文件範例</title>    <!-- 常數宣告展開 -->
  <para>我們可以用 ENTITY 的 SYSTEM 描述詞來插入外部檔案:</para>
<literallayout>
&authfile;                                          <!-- 外部檔案展開 -->
</literallayout>
</article>

由上面範例我們可以看出一些規則,那就是實體名稱(ENTITY)前有 '%' 字符的 Parameter ENTITY ,主要用在 prolog 的 subset 宣告區段,目的是幫助 subset 宣告模組化。他的展開表示式是實體名稱前加一 '%' 字符,而在實體名稱後加一 ';' 收尾字符,如 %twbaby.include;。

至於實體名稱前沒有 '%' 前置字符的稱為 General ENTITY 。General ENTITY 主要是供 SGML 文件的撰寫者,在根標籤區段內,做字串取代和展開使用的,他只能在根標籤區內使用,不可以用在 prolog 的 subset 區。他的展開表示式是在實體名稱前加 '&' 前置字符,在實體名稱後加 ';' 收尾字符,如 &teamname; or &authfile;。

不管是 Parameter 或 General 型態,ENTITY 都可以兼用 internal 和 external 的方式來取得字串內容。只有 Parameter 和 General 的屬性會造成 ENTITY 使用場合的不同,internal 和 external 的屬性不會。

Note

Parameter ENTITY 並非絕對不可用在根標籤的本文文件區段內,只是那種使用方式很特別,應該算是一種特例,而且 XML 語法並不支援,只有 SGML 有這種用法。


2. 各種 ENTITY 應用範例

經過上一節對實體(ENTITY)基本概念的解說後,本節我們將示範一些 SGML 和 XML 通用的 ENTITY 使用技巧。首先假設本節各範例都是在 user 家目錄的 Document/sgml 路徑編輯的,所以我們先執行一下指令:

$ mkdir -p $HOME/Document/sgml

對於使用 XML 語言定義的 DocBook 文件轉譯,我們使用 Red Hat 公司的 xmlto 套件。而對於使用 SGML 語言定義的 DocBook 文件,則使用 openjade 套件。雖然 openjade 也可以解譯 XML 語言定義的文件格式,但那是以 SGML 的規則去模擬 XML 的行為,常常會不小心擴大了 XML 的解釋,而那些擴大解釋,一般標準的 XML 解譯器是作不到的[4]。所以針對不同語法選擇正確的解譯器,是正確了解標記語言效能的必要途徑。不清楚怎樣安裝使用上面兩個解譯器的,請參考 DocBook 解譯轉換環境的建立 一文。


2.1. 字串內容替換

一如我們在許多程式語言的常數宣告一樣,對於一些內容敘述可能會有更動的字串或檢驗值,為了保持彈性,我們先在程式的一開始為此預設值取個代名,再到實際要顯示預設值的地方打上代名,而不是直接打預設值。好處是如果那天我們有必要更該此預設值時,只要更該該代名後的預設值即可,不需要到繁複的程式敘述中,一一去把這些預設值改過來,因為只要重新編譯一遍,編譯器就會根據新值,幫我們自動更正過來了。


2.1.1. 直接替換

SGML 的語法表示中,internal ENTITY 的意義,幾乎就和程式語言的常數宣告一樣。例如:

Example 4. 簡單 internal ENTITY範例

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
  <!ENTITY teamname "台灣企鵝寶寶工作隊">
]>
<article><title>&teamname;組織章程</title>
  <para>......</para>
</article>

這是 SGML 與 XML 最簡單的一種 ENTITY 使用範例,你在 prolog 的 subset 區宣告 teamname 這個常數的預設值是 "台灣企鵝寶寶工作隊"。然後你在 article,title 後插入 teamname 這個常數宣告,那麼經過解譯器的編譯,article 的 title 最後會顯示 "台灣企鵝寶寶工作隊組織章程" ,如果你把 teamname 預設值改成 "酷學園",那當然 title 就顯示 "酷學園組織章程"。

上面這個範例可以說簡單到不值得多做說明,下面我們舉個更有用的範例。譬如定型化契約,立約人都是甲方乙方的稱呼,到簽約時再把當事人真實姓名填入。如果為了範本文件和簽約文件的當事人署名不同,而要準備兩份檔案,那也有點太沒效率了。下面的範例把契約立約人改用實體輸入,你就會發覺,不同的人簽約,不需要改整份文件,prolog 區改 ENTITY 即可。

Example 5. 連續替換字串值範例

<?xml version='1.0' encoding="Big5"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
                  "http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd"[
  <!ENTITY site.org "出租方">
  <!ENTITY site.user "承租方">
  <!ENTITY sign.date "中華民國 xx 年 xx 月 xx 日">
]>
<article><title>網頁空間租用合約書</title>
  <para>今有雙方為承租網頁空間,特立約規定雙方權利義務如下:</para>
  <orderedlist>
    <listitem><para>今有承租人&site.user;向網站機構&site.org;承租網頁空間作為網頁發佈使用。
      </para></listitem>
    <listitem><para>&site.user;自本合約生效日起,擁有所承租網頁空間完整權利,所發佈網頁的
      言論責任,亦由&site.user;自負。</para></listitem>
    <listitem><para>&site.user;應按月向&site.org;繳納租金新台幣 200 元。</para></listitem>
    <listitem><para>&site.user;如欠繳三個月以上的租金,&site.org;有權視為合約終止,停止
      &site.user;使用權利。</para></listitem>
    <listitem><para>合約期間,&site.org;應對&site.user;寄放的網頁,盡善良管理人之責任,
      並在合約終止後,代為保存&site.user;網頁三個月。</para></listitem>
  </orderedlist>
  <para>以上條款,皆經立約雙方同意,特此書面為證。立約人:</para>
  <simplelist>
    <member>網頁空間出租人:&site.org;</member>
    <member>網頁空間承租人:&site.user;</member>
  </simplelist>
  <para>立約日期:&sign.date;</para>
</article>

上例是一個定型化契約的範本,供客戶簽約前的參考,實際簽約時,改換一下 site.org, site.user 和 sign.date 三個 internal ENTITY 的設定值,就可以重編譯成一份實際的正式合約,你可以分別改這三個實體後重編譯一番,以驗證效果。


2.1.2. 組合替換

前一小節是 ENTITY 字串替換的最簡單用法,因為不管某個 ENTITY 在文件中被插入幾次,他都只是把 ENTITY 直接換成預先設定的字串而已。本節要介紹的是字串替換更進一步的利用,就是一個 ENTITY 的設定值裡還包含了其他的 ENTITY,這裡我們稱他是組合形式的實體。譬如:

Example 6. 組合實體的使用

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
  <!ENTITY cool.school "http://www.study-area.org/">        <!-- (1) 網站位址 -->
  <!ENTITY cool.subject"tip/">                              <!-- (2) 分類位址 -->
  <!ENTITY dbook.write "&cool.school;&cool.subject;docw/docwrite.html">    <!-- (3) 檔案位址 -->
  <!ENTITY dbook.trans "&cool.school;&cool.subject;doctrans/doctrans.html"><!-- (4) 檔案位址 -->
]>
<article><title>DocBook 簡介</title>
  <para>需要了解如何建立 DocBook 解譯環境的請參考 <ulink url="&dbook.trans;">
  &dbook.trans;</ulink>,要了解如何寫作 DocBook 文件的請參考
   <ulink url="&dbook.write;">&dbook.write;</ulink> 。</para>
</article>

如上例,檔案位址 (3) 和 (4) 都內涵 (1) 網站位址和 (2) 分類位址這兩個實體,因為他們設定的內部不是單純的字串,還包含了其他實體插入敘述,所以稱為組合實體。組合實體本身被插入時,會將所有內涵實體一起替換展開,合成一整個字串。

使用這種方式來組織實體,最大的功用就是讓實體(ENTITY)階層化。以上例來看,當我們更改 (1)(2) 這兩個實體時,(3)(4) 會跟著連動,所以當文件依原組織結構搬移到新地點後,只要改變相關的階層實體設定值,就可以達成全體實體一起改變的作用。這在網頁搬家或移目錄時特別有用,因為你只要更改 (1) 或 (2) 的實體設定址,就會引起其他同結構實體的相配合改變,而不必一個又一個的去相關實體更改。


2.2. 重複使用固定的文件片段

2.2.1. 簡單型

舉個我最常使用的版權宣告方式,我先使用:

Example 7. 新增外部含括檔範例

cat << EOF > $HOME/Document/sgml/legal.inc
<?xml encoding="Big5"?>
<para>本文採用 GNU 組織的 GPL公開版權宣告,歡迎複製,改寫,散佈,
並要求你應該以同樣的權利條款,授予文件的使用人。對 GPL 的進一步的解釋,
請參考<ulink url="http://www.linux.org.tw/CLDP/OLD/doc/GPL.html">
GNU 通用公共許可證</ulink>。</para>
EOF

Important

legal.inc 這個含括檔的第一行 <?xml encoding="Big5"?> 敘述對 SGML 而言毫無作用,但對 XML 而言卻關乎解析是否合法的重要關鍵。凡是該文件不是採用 UTF-8 (us ascii 算是 UTF 的共同支援)或 UTF-16 編碼的,通通必須在文件最開頭使用此 XML 敘述,並註明正確的 encoding 編碼。所以為了確保你的含括檔可以在 SGML 和 XML 兩邊的解析器通用,建議都加上這個 XML 敘述。

上面指令執行完畢,我就多了一個 legal.inc ,接著我就可以在我的 SGML 本文裡引用他了。譬如我在 $HOME/Document/sgml 寫了這樣一個 DocBook 文件:

Example 8. 含括外部文件片段

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
<!ENTITY twbaby.include SYSTEM "legal.inc">
]>
<article><title>SGML ENTITY 含括外部檔案範例</title>
  <para>我們可以用 ENTITY 的 SYSTEM 描述詞來插入外部檔案:</para>
  <sect1><title>版權宣告</title>
    &twbaby.include;
  </sect1>
</article>

編譯後的結果輸出是:

SGML ENTITY 含括外部檔案範例

我們可以用 ENTITY 的 SYSTEM 描述詞來插入外部檔案:

版權宣告. 本文採用 GNU 組織的 GPL公開版權宣告,歡迎複製,改寫,散佈,並要求你應該以同樣的權利條款,授予文件的使用人。對 GPL 的進一步的解釋,請參考GNU 通用公共許可證

我個人幾乎每篇在網路上發表的電腦技術文章,都會做這段 GPL 版權宣告,因此我把它作成含括檔,就可以一再的插入我的 DocBook 文件中。不必每次都重新改寫,也不必把一個檔案 copy 好多份放不同文件中,徒然增加檔案的混亂,這種文件模組化,真是好用啊。

在主文件中插入外部含括檔雖然方便,但文件結構組織的配合仍不能錯亂,要遵守 DocBook DTD 原有的架構,重點如下:

  • 插入的含括檔只是主文件的一個小片段,並不是一個完整的 DocBook 文件。

  • 一份 SGML 文件只可以有一個 DOCTYPE 宣告,一個 Subset 宣告區,和一對根標籤,因此含括檔中不可以出現 DOCTYPE 宣告,Subset 宣告區以及文件根標籤。

  • 呼叫含括檔的主文件,最少要有完整的 DOCTYPE 文件宣告,subset 宣告區段和一對根標籤。

  • 外部含括檔不是插入在 SGML prolog 的 Subset 宣告區,就是插在本文根標籤內。

  • 插入到 Subset 區的含括檔內容必須是 DTD 宣告式或者 DTD 宣告式的集合。

  • 插入到本文根標籤區的含括檔,除了一般文字,也可以有標籤的階層結構,但他的結構必須配合所插入區段,符合 DocBook DTD 的定義結構。講的比較淺顯一點,就是含括檔中如果有標籤結構,他只能插在父標籤區段中。

  • 含括檔裡可以再插其他含括檔,但同樣必須符合前述各條原則。

其實在主文件中插入含括檔的規則並不難理解,把他想成其實就是遵守原來 DocBook 文件的規則,只是把中間某些片段資料,存在另外的檔案裡,編譯時再把他通通拼湊回來而已。


2.2.2. 進階型

隨著你使用 DocBook 格式撰寫文件,資料愈來愈大,固定的共用資料片段也愈來愈多,這時再在文件的 subset 區段中利用 SI 敘述來一一指明外部資料路徑位址,以供在本文敘述中能夠插入,就會變得十分繁瑣。這時我們是否能把所有使用 SI 敘述的 external ENTITY 宣告,集合寫在一個檔案中,那麼以後所有需要宣告這些外部資料的文件,只要在 subset 區執行一次插入動作,就等於宣告了所有的共通外部資料?答案是肯定的,利用前一節我們學會的外部資料含括的基本知識,我們就可以演伸出更進階的檔案含括功能。

首先我們假定所有外部含括資料都集中放置在 common 這個子目錄裡,因此我們執行下列指令來做一個路徑出來:

          $ mkdir -p $HOME/Document/sgml/common
        

接著我們假定下列三個外部資料是每篇在網路發表網頁時,都會引用的資料,我們先一一製作,存放在 $HOME/Document/sgml/common 路徑下。

 

檔案名稱:legal.inc

<?xml encoding="Big5"?>
<legalnotice><para>本文採用 GNU 組織的 GPL公開版權宣告,歡迎複製,改寫,散佈,
並要求你應該以同樣的權利條款,授予文件的使用人。對 GPL 的進一步的解釋,
請參考<ulink url="http://www.linux.org.tw/CLDP/OLD/doc/GPL.html">
GNU 通用公共許可證</ulink>。</para></legalnotice>
           

 
--版權宣告 
 

檔案名稱:author-oldguy.inc

<?xml encoding="Big5"?>
<author>
  <honorific>發起人</honorific>
  <othername>老貢生</othername>
  &twbaby.org.inc;
</author>
           

 
--文件作者資料 
 

檔案名稱:twbaby.org.inc

<?xml encoding="Big5"?>
<affiliation>
  <orgname>台灣企鵝寶寶工作隊</orgname>
  <orgdiv>編輯出版組</orgdiv>
  <address>
    <otheraddr>
      <ulink url="http://3ybaby.v-club.net/">企鵝寶寶網站</ulink>
    </otheraddr>
  </address>
</affiliation>
           

 
--文件發佈單位資料 

接著我們在 common 路徑下作個 index.ent 的 SI 宣告集合檔如下:

 

檔案名稱:index.ent

<?xml encoding="Big5"?>
<!ENTITY twbaby.legal.inc SYSTEM "legal.inc">
<!-- GPL 版權宣告含括檔 -->
<!ENTITY twbaby.authur.oldguy SYSTEM "author-oldguy.inc">
<!-- 老貢生資料含括檔 -->
<!ENTITY twbaby.org.inc SYSTEM "twbaby.org.inc">
<!-- 企鵝寶寶工作隊資料含括檔 -->
           

 
--ENTITY 集合宣告檔 

要注意的是:

  • 因為這些 external ENTITY 將來是要插在文件編寫區的(根標籤區段中),所以使用 General ENTITY 宣告方式,實體名稱前不加 % 字符。

  • 這些 SYSTEM 描述詞後是採用相對路徑指出外部含括檔路徑,這裡的相對,是指 ENTITY 宣告檔和其指定含括檔的相對位置,而不是指主文件與含括檔的相對位置。

  • 最好為每個 ENTITY 宣告檔的內容或功用做個簡短備註說明,否則,不出兩個禮拜,你會忘了那些含括檔是做什麼用的。

  • 哪怕中文資料只在備註字符中,哪怕真正的宣告敘述都是英文,只要文件中有一個中文字,都請你使用 xml 敘述設定好 encoding 屬性,因為 xmlto 只要看到文件中有非 ascii 或 utf-8 字元,一律視為錯誤而終止執行,不只 xmlto 如此,許多其他的 XML 解析器也一樣嚴格的近乎龜毛(如果你確定你的所有含括檔都只在 SGML 下解譯,就不必如此麻煩,SGML parser 不 care 那個 encoding。)

最後我們編輯一份測試功能的主文件 $HOME/Document/sgml/test.sgml(or test.xml),首先宣告文件型態,並打開 prolog 的 subset 區,SGML 格式是:

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [

XML 格式:

<?xml version="1.0" encoding="Big5"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
                  "http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd" [

接著我們把剛剛那個 ENTITY 宣告集合檔插入到 prolog 的 subset 區,產生逐一宣告 ENTITY 的效果:

  <!ENTITY % twbaby.include SYSTEM "common/index.ent">
  <!-- 由於 ENTITY 必須於 subset 區宣告,subset 區必須使用 Parameter
       ENTITY ,所以實體名稱前加 % 字符。 -->
  %twbaby.include;    <!-- 把 index.ent 檔案插入宣告區,產生宣告效果。 -->

關上 subset 區,並開始根標籤,進入本文編輯區:

]]>
<article>

在本文編輯區中使用 subset 區宣告的 External ENTITY:

  <title>固定資料片段重複使用範例</title>
  <articleinfo>
    &twbaby.legal.inc;
    &twbaby.authur.oldguy;
  </articleinfo>
  <para>你可以用一個外部 ENTITY 宣告集合檔,來統合所有的常用資料片段,插入本文中來重複使用。</para>
</article>

現在你可以把所有這些都湊在一起,用 openjade or xmlto 編譯成 HTML 看看效果。

Note

在這範例中,雖然 article 根標籤只出現兩個 External ENTITY 插入,但其實是三個 ENTITY 都用上了,因為在 author-oldguy.inc 這個含括檔有做了插入另一個含括檔 twbaby.org.inc 的動作,所以一個被宣告過的 External Entity ,不只可以主文件中被插入,也可以在宣告效力所及的另一個含括檔中被插入。


2.3. 過時連結修正機制

在熟悉了 internal ENTITY 的常數宣告,以及 external ENTITY 的外部含括檔宣告的使用方法後,我們就可以善加變化,做出許多省時省力的附加機制出來。譬如下面介紹的,解決因外部連結檔位址改變,導致原連結失效問題的機制。

如果我說近年來風迷到世界各地每個角落的,World Wide Web 網頁,它最引人入勝,並獨具特色的,就是 Hyper Link(超連結)這項功能,應該大家不會有異議才對。但有過維護並更新網站經驗的人都知道,最令人頭疼,並且需要常常留意的,就是過時連結位址的更正。因為你可以靠著仔細和謹慎來盡可能降低自己撰寫網頁的錯誤資訊,但你沒辦法控制別人的網頁不搬家換地方,只要你的網頁中有向外連結其他網路資訊的敘述,你就要有此連結敘述可能隨時會過時的心理準備,你只能接受這個事實而一點辦法也沒有。

當然,就算用 DocBook 來轉譯產生網頁,就算用 SGML ENTITY 功能來協助,仍不能阻止別人的網頁不搬家。但可以藉由 SGML ENTITY 的協助來儉省你更新網頁中過時連結敘述的工作,讓你不必翻遍網頁的每一行段落,來找出連結敘述何在。

要達成這樣的功能,必須做到兩個重點:第一,所有在文件中的超連結敘述,一律由 internal ENTITY 取代;第二,所有會用到的超連結 ENTITY 宣告,集中寫到一個外部含括檔中。例如我會做一個 $HOME/Document/sgml/common/oururl.ent 如下:

<?xml encoding="Big5"?>
<!ENTITY addrlose.url "http://mysite.mydomain/addrlose.html">
<!-- 網路上無法再搜尋到原始檔案的錯誤通知網頁 --> 
<!ENTITY docbook.onlinebook.url "http://www.docbook.org/tdg/en/html/docbook.html">
<!-- DocBook 官方版線上手冊位址 -->
<!ENTITY openjade.download.url "http://openjade.sourceforge.net/">
<!-- openjade 原碼下載位址 -->
<!ENTITY install.jade.url "http://www.study-area.org/tips/doctrans/doctrans.html">
<!-- OpenJade 安裝說明文件位址 -->
<!-- 其他網路連結位址 ...... -->

接著我在 $HOME/Document/sgml/test.sgml 主文件中用 oururl.ent 的 ENTITY 來取代 Hyper Link 的 URL 敘述如下:

Example 9. 外部含括超連結宣告

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
  <!ENTITY % twbaby.hlink.ent SYSTEM "common/oururl.ent">
  %twbaby.hlink.ent;
]>
<article><title>DocBook 簡介</title>
  <para>想要取得 DocBook 解譯的程式模組,可以到 <ulink url="&openjade.download.url;">
    openjade 套件</ulink> 去下載。不知如何安裝程式模組,可以去 <ulink url="&install.jade.url;">
    查看安裝說明手冊</ulink>。至於不知如何撰寫 DocBook 文件,也可到<ulink url=
    "&docbook.onlinebook.url;">翻閱官方線上手冊</ulink>。</para>
</article>

當你做這樣安排,以後任何其他被連結網頁搬家,你只要更改 oururl.ent 這個宣告檔的相關 ENTITY 設定值後,再重新將文件編譯一次即可。不需要到網頁內容中一行一行去翻找,把相關的連結找出來。(當你的網頁數量十分龐大時,你會明白一行一行找是多恐怖的一件事。)而對於那個徹底從網路中消失的被連結原始檔,則用 addrlose.url 這個 ENTITY 所指示的網頁,取代原 ENTITY 設定值,來向瀏覽者報告,這個網路資源已退出網路,不復出現了。

此外如果你採用集中超連結宣告檔的機制,你不必等瀏覽者通知你網站中有壞連結發生,善於撰寫 server 端 script 的,可以用此檔資訊執行自動偵測程式,藉由回應偵測來發覺連結異常狀況。不會寫 script 的,就把所有宣告的超連結 ENTITY 寫成一大張 HTML ,找個人每天逐一按一下來測試,這都比你一一到網頁內文中去找,會有效率的多。你可以不必對網頁中過時連結那麼無力感,只要你善用 SGML 的 ENTITY 宣告功能。


3. 共同寫作環境的佈置

經過前面一連串基本觀念的介紹和基本語法的練習,現在總算可以開始我們使用 DocBook 格式文件的另一大理由,多人共同寫作。在開始之前我還是要再提醒一次,DocBook 能提供的功能是化整為零,分頭施工之後,再彙整成編。至於其他權限管制,版本修正歷程紀錄,則不在 DocBook 控制範圍內,不過兩者卻可以合併運作,相得益彰。


3.1. 多人寫作的基本環境佈置

不管你打算要透過網際網路,區域網路,或共用主機,來讓許多人共同編輯一份文件,你總要在放置文件的主機上,開放所有參與共同寫作的人,具有登入主機的權限,並讓他們在該主機上,擁有一個專屬的資料夾,來擺放他們被分配到編輯的子文件。

在這裡我們做一個假設,台灣企鵝寶寶工作隊的發起人老貢生,邀集了在台灣 BBS Linux 論壇的三大高手,小州,網中人,李果正來分別就系統,網路,排版列印部份,提供他們獨到見解和經驗,彙整成 Linux 密技彙編一篇,以供推廣提昇 Linux 社群使用經驗之用。我們現在就以這個做假設,來看看怎樣的環境安排,可以建構成一個多人共同撰寫的 DocBook 環境。

首先我們假設老貢生的那台網路主機名稱是 cvs.baby3y.org.tw,你可以讓他成為一個 cvs server ,telnet server or ssh server ,來讓使用者上下傳文件資料[5],下面的動作請先切換成 root 執行權限再做執行命令動作。首先我們需要建立一個 cvs group 來區別 DocBook 文件編輯者,或單純的 cvs.baby3y.org.tw 主機登入者。

# groupadd cvs

除非我們要建立 cvs server ,才要一同建立 cvs user ,否則光有 cvs group 即可。接著我們建立共同文件編寫區目錄並設定適當權限:

# mkdir /home/cvsroot
# chmod 775 /home/cvsroot
# chown root.cvs /home/cvsroot

OK,我們有了一個共同寫作資料夾,但你不會只寫一篇 Linux 密技彙編就滿足了,將來一定還有許多其他不同題目的共同文件會在此區編寫。你不會希望不同題目的文件都堆在一個目錄中亂成一團,因此我們給這次密技彙編文件一個單獨的目錄 tltp(Taiwan Linux Tip Project)。

# mkdir /home/cvsroot/tltp
# chmod 750 /home/cvsroot/tltp
# chown root.cvs /home/cvsroot/tltp

Note

如果你想讓一般登入者也可以進入及閱覽 /home/cvsroot/tltp 資料,你可以設定權限模式為 755 ,本範例設 750 表示非 tltp 文件編寫員不得進入本區。

現在我們知道 /home/cvsroot/tltp 就是我們密技彙編文件的最上層目錄,接著我們要在這文件根目錄下分別建立各個編寫員自己文件放置的單獨資料夾。當然首先你要在 cvs.baby3y.org.tw 主機上建立 小州(kenduest),網中人(netman),李果正(edward) 三人的帳號,建立的方法請自行參考 useradd 命令,此處不再贅述,接著你要編輯 /etc/group 群組檔,做下列的更改:

cvs:x:nnn
改成
cvs:x:nnn:kenduest,netman,edward

如此才能讓 kenduest,netman,edward 三人取得進入 tltp 的權限。接著你要建立 kenduest 等人的分別資料夾:

# mkdir /home/cvsroot/tltp/kenduest
# chmod 750 /home/cvsroot/tltp/kenduest
# chown kenduest.cvs /home/cvsroot/tltp/kenduest

其他兩人也依同樣的法則,在 tltp 下分別建立 netman 和 edward 子資料夾,最後你應該建立一個全體資料共用區 common 如下:

# mkdir /home/cvsroot/tltp/common
# chmod 770 /home/cvsroot/tltp/common
# chown root.cvs /home/cvsroot/tltp/common

經過以上這些歷程,共同文件寫作的系統環境規劃就算完備,接著,就是 DocBook 文件配置該上場的時候了。


3.2. 主副文件結構

有了 external ENTITY 宣告的概念後,如何在主文件中含括外部文件想必是輕而易舉的事,現在我們就在 /home/cvsroot/tltp/ 路徑編輯一個 main.sgml(xml)當主文件來插入 kenduest,netman,edward 三個人編寫的副文件:

Example 10. SGML 主文件範例

<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
  <!ENTITY kenduest.main SYSTEM "kenduest/mainchap">
  <!ENTITY netman.main SYSTEM "netman/mainchap">
  <!ENTITY edward.main SYSTEM "edward/mainchap">
]>
<book><title>Linux 密技問答彙編</title>
  <chapter><title>導論</title>
    <para>本文集合在台灣 BBS 論壇上,常常熱心為網友排疑解惑,回答 Linux
    作業系統相關問題的三大高手,小州,網中人和李果正三位先生。分別就他們在
     Linux 上的專長所學,提供精闢的分析與獨到見解,薈萃成一篇完整文章。
    這真是 2004 年自由軟體社群研究推展的一大勝事,希望大家能藉此相互交流,
    進步一日千里。</para>
  </chapter>
  &kenduest.main;
  &netman.main;
  &edward.main;
</book>

上面是 SGML 範例,XML 範例見下列:

Example 11. XML 主文件範例

<?xml version="1.0" encoding="big5"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
               "http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd" [
  <!ENTITY kenduest.main SYSTEM "kenduest/mainchap">
  <!ENTITY netman.main SYSTEM "netman/mainchap">
  <!ENTITY edward.main SYSTEM "edward/mainchap">
]>
<book><title>Linux 密技問答彙編</title>
  <chapter><title>導論</title>
    <para>本文集合在台灣 BBS 論壇上,常常熱心為網友排疑解惑,......(以下同上)<para>
  </chapter>
  &kenduest.main;
  &netman.main;
  &edward.main;
</book>

這樣在主文件的 subset 區插入副文件的使用方式相信沒啥大學問,也不需太多解釋,不過卻有些限制是要全體參與編寫人員遵守的:

  • external ENTITY 的外部檔案路徑宣告,要注意外部檔案和主文件的相對位置。

  • 就這個範例中,mainchap 變成各子文件編寫員最上層的文件,各子文件編寫員必須使用這個檔名,不得變更。

  • 為了將來每個人編寫的子文件能和在一塊,不造成文件結構衝突,主文件撰寫員必須告知各子文件編寫員被分配到的最上層標籤為何,子文件編寫員只能使用該最上層標籤的子標籤,並避免文件中有跳出最上層標籤段落之外的現象發生。例如本例,kenduest,netman,edward 分到的最上層標籤都是 chapter,所以三人都該在自己目錄中的 mainchap 檔以 <chapter> 為文件開頭,以 </chapter> 為文件結尾,並且只使用 chapter 的子標籤規劃文件。

  • 如果你是使用 XML 語法規則編譯 DocBook 文件,那麼所有的外部含括文件,只要編碼不是 UTF-8 or UTF-16 ,就一律必須在文件開頭加 <?xml encoding="code-name"?> 的宣告,否則會造成編譯錯誤。


3.3. 在主文件中使用外部實體宣告檔

在主文件插入外部實體(external ENTITY)宣告檔的用意有二:

  1. 讓文件的實體的宣告可以一次整批完成,維持主文件的簡單清楚。

  2. 藉由在外部實體宣告檔中含括其他的實體宣告檔,讓子文件編寫員也可以自行設定實體宣告。


3.3.1. 外部整批宣告實體檔案

我們先談主文件如何利用含括外部實體宣告檔來整批處理實體宣告,我們先做一個 /home/cvsroot/tltp/url.ent 的主文件網路資源宣告檔:

Example 12. 外部網路位址實體宣告檔

<?xml encoding="big5"?>
<!ENTITY common.oldguy.email "oldgen@pchome.com.tw">
<!-- 本文總編老貢生的 email 信箱 -->
<!ENTITY gnu.legal.url.tw "http://www.linux.org.tw/CLDP/OLD/doc/GPL.html">
<!-- GNU GPL 版權聲明中譯文件位址 -->

接著我們製作一份 /home/cvsroot/tltp/main.ent 檔案作為主文件的最上層外部實體宣告檔,並含括插入剛剛做好的網路位址宣告檔 url.ent:

Example 13. 主文件的最上層外部實體宣告檔

<?xml encoding="big5"?>
<!ENTITY % common.url.ent SYSTEM "url.ent">
<!-- 宣告網路資源連結位址宣告檔路徑何在 -->
%common.url.ent;
<!-- 宣告過的外部實體,要執行插入動作才會實際產生作用。 -->

最後我們在主文件 main.sgml(xml) 中引用插入主要外部宣告檔 main.ent:

Example 14. 主文件插入外部宣告檔

<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
  <!ENTITY % external.module SYSTEM "main.ent">
  %external.module;
                                                                                                 
  <!ENTITY kenduest.main SYSTEM "kenduest/mainchap">
  <!ENTITY netman.main SYSTEM "netman/mainchap">
  <!ENTITY edward.main SYSTEM "edward/mainchap">
]>
<book><title>Linux 密技問答彙編</title>
  <chapter><title>導論</title>
    <para>本文集合在台灣 BBS 論壇上,常常熱心為網友排疑解惑......</para>
    <para>對本篇文章有任何批評建議,請 email 到總編老貢生的電郵信箱
      <email>&common.oldguy.email;</email> ,對於各章主筆有進一步的請益,
      <!-- 插入 url.ent 中的宣告引用 -->
      請參考各章內主筆提供之聯絡方式及網路位址。</para>
  </chapter>
  &kenduest.main;
  &netman.main;
  &edward.main;
</book>


3.3.2. 子文件編寫員也可以自訂實體宣告

我們在前面的章節曾經說過,一整份 DocBook 只有一個 prolog 可以開啟 subset 插入實體宣告,而且只有主文件可以開啟 subset 區段。那麼作為被含括呼叫的子文件,是沒有 subset 區,也無法做各種實體宣告的。那麼子文件的編寫員如果要使用 ENTITY 好用的功能,就一定要拜託主文件編寫員幫他宣告,而不能自訂實體宣告嗎?

其實我們只要記住一個觀念,我在主文件宣告引用一個外部實體檔,這個實體檔就可以再去宣告引用其他實體檔,這麼一層一層引用下來,最後所有外部檔案的實體宣告,都會彙總到主文件的 subset 區去宣告。所以只要主文件撰寫員和子文件撰寫員協議好,在主文件插入一個由子文件撰寫員編寫的外部實體宣告檔,子文件編寫員就可以透過這個檔案中介,層層轉用其他自己編寫的外部實體宣告檔,並把它應用插入到自己文件中。

下面我們就把原來主文件的最上層外部實體宣告檔改寫一下,讓他會去含括 kenduest,netman,edward 三人目錄下的 main.ent 外部實體宣告檔,好讓三位子文件編寫員可以自訂實體宣告。此外我們也加了個共同資料區 common/main.ent 來引用共同區的相關資料。

Example 15. 主文件的最上層外部實體宣告檔(增補 A)

<?xml encoding="big5"?>
<!ENTITY % common.url.ent SYSTEM "url.ent">
%common.url.ent;
<!-- 主文件共用的網路資源連結位址 -->
<!ENTITY % common.module SYSTEM "common/main.ent">
%common.module;
<!-- 共同使用的外部實體(external ENTITY)宣告檔 -->
<!ENTITY % kenduest.module SYSTEM "kenduest/main.ent">
%kenduest.module;
<!-- 小州使用的外部實體(external ENTITY)宣告檔 -->
<!ENTITY % netman.module SYSTEM "netman/main.ent">
%netman.module;
<!-- 網中人使用的外部實體(external ENTITY)宣告檔 -->
<!ENTITY % edward.module SYSTEM "edward/main.ent">
%edward.module;
<!-- 李果正使用的外部實體(external ENTITY)宣告檔 -->

當我們在主文件的 main.ent 增加了對子文件區的外部實體宣告檔含括引用後,各子文件的編寫員就能夠在自己路徑的 main.ent 中,自訂實體宣告,或者進一步轉含括插入其他外部實體宣告檔。現在我們就利用 kenduest 編寫員為例來說明,首先我們編寫 kenduest 的 /home/cvsroot/tltp/kenduest/main.ent:[6]

Example 16. kenduest 的最上層外部實體宣告檔

<?xml encoding="big5"?>
<!ENTITY kenduest.email.url "kenduest@abc.123.com">
<!-- 插入 kenduest 的 email 位址 -->

接著我們編寫 kenduest 的最上層子文件 /home/cvsroot/tltp/kenduest/mainchap 來測試:

Example 17. kenduest 的最上層子文件

<?xml encoding="big5"?>
<chapter><title>系統架構與函式庫</title>
  <para>很榮幸有這個機會在這裡和大家分享有關開放作業系統的使用經驗,
  雖然 Linux 的系統架構與函式庫是我的興趣,並耗費不少心力去探索。
  但一個人的才力智慧有限,疏漏總是難免,所以文中有不當不妥或待商榷處,
  還希望不吝來函 <email>&kenduest.email.url;</email> ,
                 <!-- 插入 kenduest 的 main.ent 宣告,雖然沒有 subset 區,
                      但透過 main.ent 和主文件的 main.ent 連結關係,
                      kenduest 自己定義的實體宣告會被帶到主文件的 subset
                      區去宣告。 -->
  惠與指正,不勝感謝。</para>
</chapter>

Important

這裡有個要特別留意的就是做實體宣告時選用實體名稱的問題,因為每個子文件編寫員的子文件和外部實體宣告檔都是放在自己的目錄下,所以縱使檔名相同,也不會混淆。但是每個人宣告出來的實體,都會集中到主文件的 subset 區去集中宣告,那時如果兩個人宣告的實體名稱相同,就會產生宣告競合的問題,不管是 SGML or XML ,都不會分辨實體的來源而做分別的處理,他把所有的實體和外部資料視為一個整體,而採取一個固定值。為了避免編寫員宣告實體名稱的相互干擾,宣告實體名稱時應該留意避免和別人取一樣的實體名稱,像範例中的,把單純的 email.url 實體宣告名稱前,加上 kenduest 使用者帳號,作為實體名稱的一部分,就是一個不錯的方法。


3.4. 子文件中含括延伸外部實體宣告檔案

前一節範例我們已經練習了如何用主文件的外部實體宣告檔 main.ent 去含括插入子文件的 main.ent ,來達成子文件編寫員也能定義宣告自己的實體。這一節我們要練習的是透過子文件的外部實體宣告檔 main.ent 中介,再延伸插入更多其他的外部實體宣告檔。這些插入其他實體宣告檔的配合,只需修改自己目錄中的 main.ent 即可,不需要再麻煩主文件編寫員去做修改主文件的 main.ent 。

現在我們以子文件編寫員 netman 的文件結構來示範一下這個結構。首先我們做一個 netman 定義的 /home/cvsroot/tltp/netman/url.ent 作為外部實體宣告檔:

Example 18. netman 的外部實體宣告檔

<?xml encoding="big5"?>
<!ENTITY netman.site.url "http://myweb.mydomain.org.tw">
<!-- 酷學園討論區網址 -->

接著改一下 kenduest 的 main.ent 結構,來作為 netman 的 /home/cvsroot/tltp/netman/main.ent 使用:

Example 19. netman 的最上層外部實體宣告檔

<?xml encoding="big5"?>
<!ENTITY % netman.url.ent SYSTEM "url.ent">
%netman.url.ent;
<!-- 插入網中人網路位址實體宣告檔  -->

現在 netman 把外部實體檔 url.ent 含括在 main.ent 的設定都做好了,我們就可以使用子文件 /home/cvsroot/tltp/netman/mainchap 來試試引用延伸外部實體宣告檔的功能如何:

Example 20. netman 的子文件測試

<?xml encoding="big5"?>
<chapter><title>網路通訊與安全</title>
  <para>今天很高興有這個機會,在酷學園之外和大家談談網路。如果本章說得不周全的地方,
    各位可以繼續來 <ulink url="&netman.site.url;">酷學園討論區</ulink>
                    <!-- 這是在 netman 路徑下 url.ent 宣告的實體,
                         我們可由此觀察設定是否正確。 -->
    和我們進一步交流。</para>
</chapter>

Note

在子文件的 main.ent 裡含括其他外部檔案,還是要注意 main.ent 與被含括檔的相對路徑。要注意!!在範例中 url.ent 的相對路徑,是指和子文件 main.ent 的相對路徑,而不是和主文件 main.ent 的相對路徑,縱使他們最後會被帶到主文件去宣告。最簡單的原則是,被宣告檔案的相對路徑,是指與宣告他的檔案的相對路徑。


3.5. 在子文件中插入子文件

子文件既然可以插入另外的外部實體宣告檔,當然也可以插入更細分的子文件片段資料,我們就用 edward 的資料夾來試試。首先我們先製作 /home/cvsroot/tltp/edward/secta 和 sectb 這兩個簡短的子文件:

Example 21. edward 的第二層子文件(a)

<?xml encoding="big5"?>
<sect1><title>Tex/Latex</title>
  <para>本節將介紹在 UNIX 系統下,首推一指的文件排版軟體,Tex/Latex。</para>
</sect1>

Example 22. edward 的第二層子文件(b)

<?xml encoding="big5"?>
<sect1><title>Post Script</title>
  <para>本節將介紹在資訊處理中,最風行的印表機列印命令描述檔,Post Script。</para>
</sect1>

接著我們來製作 /home/cvsroot/tltp/edward/main.ent 這個外部實體宣告檔案:

Example 23. edward 的外部實體宣告檔

<?xml encoding="big5"?>
<!ENTITY edward.sect.a SYSTEM "secta">
<!-- 李果正的第一區段的含括檔 -->
<!ENTITY edward.sect.b SYSTEM "sectb">
<!-- 李果正的第二區段的含括檔 -->

最後我們編寫 /home/cvsroot/tltp/edward/mainchap:

Example 24. edward 的最上層子文件

<?xml encoding="big5"?>
<chapter><title>Linux 系統中格式文件的使用狀況</title>
  &edward.sect.a;
  &edward.sect.b;
</chapter>


3.6. 輸出整份文件

不管是測試整份文件效果如何,或者終於要結稿正式對外發佈,你的編譯對象都是 /home/cvsroot/tltp/main.sgml or main.xml 。不管結構多複雜,多少人參與,最後都只用一個命令,針對一個最上層主文件。如果你是使用 SGML 語法的 DocBook ,你的編譯命令應該是:

$ openjade -t sgml -d style-sheet-filename /home/cvsroot/tltp/main.sgml

如果用的是 XML 的 DocBook ,則編譯命令是:

$ xmlto html /home/cvsroot/tltp/main.xml

4. 依解譯器實作而不同的 ENTITY 功能

前面各節所介紹的,不管是 XML 或 SGML ,都是有定義規範的,一般的解譯器也都支援這些功能。本節則將介紹一些:或者是 SGML 與 XML 有所分別的差異,或者是特定解譯器對規範實作的支援度不同,總之,我會特別指出那個解譯器有支援,而不能運用在別種解譯器上。


4.1. 條件編譯

不管是 SGML 和 XML ,在他們正式對外發佈的規格建議書上,都有議定 ENTITY 透過 IGNORE 和 INCLUDE 值的設定,可以達成類似程式語言那種 #ifdef 語法的條件編譯效果。但是經我實地測試,負責解譯 SGML 的 openjade 和負責解譯 XML 的 xmlto,在語法使用上有差異,而 xmlto 的限制比較多,功能也不如 openjade 完整,現在分述如後。


4.1.1. openjade 的 ENTITY 條件編譯

所以先說 openjade ,是因為語法限制少,功能多,比較容易讓你了解什麼叫 ENTITY 的條件編譯。首先我們先示範一個 subset 區的 ENTITY 條件編譯:

Example 25. SGML 的 Subset 區條件編譯

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
  <!ENTITY % site.choice "IGNORE">              <!-- 註1 -->
  <![%site.choice;[                              <!-- 註2 -->
    <!ENTITY site.name "企鵝寶寶工作隊">        <!-- 註3 -->      
  ]]>                               <!-- 註4 -->
  <!ENTITY site.name "酷學園">                  <!-- 註5 -->
]>
<article><title>ENTITY 的條件編譯</title>
  <para>歡迎光臨[&site.name;]網站。</para>      <!-- 註6 -->
</article>

這個範例若不是對 SGML 語法稍有涉略的人,猛一看還真會有點難於理解,所以請耐心看完下面的解釋:

註1, 設定條件標示實體初值

任何 ENTITY 的條件編譯運作,都需要宣告一個條件標示實體來掌控條件設定。一個條件標示實體必須有 '%' 字符為前置字符,表示只有 Parameter ENTITY 才能當條件標示實體。條件標示實體只可以有 INCLUDE(引用) 或 IGNORE(忽略)兩種選項值,並且必須以 '"' 字符包夾於選項值前後。這裡的設定是條件實體的名稱是 site.choice ,他的選項值是 IGNORE(忽略)。

註2, site.choice 條件區段開始

在 subset 區段中可以有很多個條件編譯區段,但只有 site.choice 區段,是受 site.choice 條件編譯實體設定值控制的。區段開始的表示式是:

<![%條件實體名稱;[

註3, site.choice 條件區段內容

在一個條件編譯區段中,可以有很多的宣告,這裡為了解說方便,只設定一個常數宣告(internal ENTITY) site.name 。但由於條件標示實體 site.choice 的設定值是 IGNORE,所以在這個範例裡,這裡的宣告示被忽略不產生作用的。

註4, 條件區段結束

不管條件編譯實體名稱是什麼,區段結束符號一律是 ]]>

註5, 條件區段外的對應設定

我們通常會在某個條件區段之後,把條件區段中的各宣告再次宣告,並給予不同的設定值,達到選擇切換的目標。

註6, 條件編譯結果顯示處

openjade 最後會依據條件編譯宣告設定值把結果插在作者要求的位置。在這個範例中,由於 site.choice 是 IGNORE ,所以 site.choice 區段中 site.name="台灣企鵝寶寶工作隊" 的宣告被忽略,而由區段後的 site.name="酷學園" 決定 site.name 的最後設定值。但當我們把 site.choice 設定值改成 INCLUDE 時,那麼條件編譯區段中 site.name="台灣企鵝寶寶工作隊" 的宣告就會被執行,依據 SGML 同名 ENTITY 重複宣告的處理原則是先佔先贏,所以後面的 site.name="酷學園" 就會失去作用,所以最後設定值就會是 "台灣企鵝寶寶工作隊" 。

光有 if 宣告語法好像還是有那麼點缺憾,最好是能像一般程式語言那樣,有個 if ... else ... endif 這種分流式語法。原則上還是可以,只是語法上比較費解釋一些,如果有興趣,下面的例子還要仔細想一下才會通曉。

Example 26. SGML If ... Else 語法

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
  <!ENTITY % site.else.choice "IGNORE">    <!-- 註1 -->
  <![%site.else.choice;[
    <!ENTITY % site.if.choice "IGNORE">    <!-- 註2 -->
    <!ENTITY site.name "企鵝寶寶工作隊">
  
  ]]>
  <!ENTITY % site.if.choice "INCLUDE">     <!-- 註3 -->
  <![%site.if.choice;[
    <!ENTITY site.name "酷學園">
  
  ]]>
]>
<article><title>ENTITY 的條件編譯</title>
  <para>歡迎光臨[&site.name;]網站。</para>
</article>

註1, else 條件標示實體

我們先假設 site.else.choice 為 else 選項,預設值為 IGNORE,則 site.else.choice 區段不會去執行。

註2, if 區段關閉設定

site.if.choice 設為 IGNORE ,則因為先佔原則,會壓制後面 site.if.choice 的其他設定,使得 site.if.choice 區段內的宣告無法執行。

註3, if 條件標示實體

如果前面沒有任何 site.if.choice 的先行設定,那麼 site.if.choice 將依此處設定為 INCLUDE ,site.if.choice 區段內的宣告也將被執行。

所以我們可以知道, site.if.choice 區段能否執行,是要看 site.else.choice 區段中的 site.if.choice 為 IGNORE 的宣告是否執行來決定。這樣就能達成 site.else.choice 被 INCLUDE ,site.if.choice 就會 IGNORE ,而 site.else.choice IGNORE ,site.if.choice INCLUDE 的這種二者擇一的效果。

條件編譯既然可以在 subset 中使用,那是否也能使用在本文敘述中呢,答案是肯定的,下面的範例你可以將 site.else.choice 分別設為 IGNORE 和 INCLUDE 後,再編譯轉換看看,就會發覺本文會產生整段出現或消失的情況出現。

Example 27. 在本文中使用條件編譯

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
  <!ENTITY % site.else.choice "IGNORE">
  <![%site.else.choice;[
    <!ENTITY % site.if.choice "IGNORE">
  ]]>
  <!ENTITY % site.if.choice "INCLUDE">
]>
<article><title>ENTITY 的條件編譯</title>
  <para>在自由軟體的開放社區中,
  <![%site.if.choice;[
  酷學園以自由軟體線上的學習與討論而聞名。
  ]]>
  
  <![%site.else.choice;[
  台灣企鵝寶寶工作隊希望研發輕便易攜帶的作業環境,來
  推廣自由軟體的使用與發展。
  ]]>
  
  </para></article>

最後,還要給你個驚喜,openjade 可以讓你直接從命令參數中改變被編譯文件的條件編譯值。例如上面的例子,如果我們要把條件編譯實體 site.else.module 設定值改為 INCLUDE ,我們不必要去改檔案內容,我們只要在使用 openjade 編譯文件時,加上 -i 參數即可,例如:

$ openjade -t sgml -i site.else.module -d <style-sheet filename> SgmlFile[7]

-i site.else.choice 的效果就等同於修改 site.else.choice 設定為 INCLUDE。


4.1.2. xmlto 的條件編譯

根據 W3 組織在 XML 1.0 三版 中有提到,XML 支援經由 ENTITY 設定 IGNORE 和 INCLUDE 值,來達成條件選項宣告的功能。但實際上如果你使用 xmlto 來編譯前一小節的範例如下:

<?xml version="1.0" encoding="big5"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
               "http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd"[
  <!ENTITY % site.choice "IGNORE">
  <![%site.choice;[
    <!ENTITY site.name "企鵝寶寶工作隊">
  ]]>
  
  <!ENTITY site.name "酷學園">
]>
<article><title>ENTITY 的條件編譯</title>
  <para>歡迎光臨[&site.name;]網站。</para>
</article>

則你會得到 error: xmlParseInternalSubset: error detected in Markup declaration 這樣的錯誤訊息。你應該採用將原本的條件編譯區段另存成外部檔案,例如:

filename:extmod.ent

<?xml encoding="big5"?>
  <!ENTITY % site.choice "INCLUDE">
  <![%site.choice;[
    <!ENTITY site.name "企鵝寶寶工作隊">
  ]]>

接著再在主文件的 subset 區中使用 external ENTITY 把 extmod.ent 這個外部實體宣告檔含括進來如下範例:

Example 28. XML 的條件編譯語法

<?xml version="1.0" encoding="big5"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
               "http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd"[
  <!ENTITY % external.module SYSTEM "extmod.ent">
  %external.module;
  <!ENTITY site.name "酷學園">
]>
<article><title>ENTITY 的條件編譯</title>
  <para>歡迎光臨[&site.name;]網站。</para>
</article>

遵照範例的方式就可以達成 XML 文件的條件編譯功能。至於為何要這樣轉個彎,我是完全不清楚,由於時間匆促,我沒能好好研究 XML 規格和 xmlto 命令的說明文件,所以不知是 XML 規定如此,或是 xmlto 背後的核心程式自己的策略選擇,我完全沒概念,我只是從實做中知道, SGML 不管是直接在 subset 使用,或透過 external ENTITY 間接引用都可,但 xmlto 卻限制只能透過 external ENTITY 間接引用。

至於不管是單純的 ifdef 一段的用法,或 if ... else 兩段用法,SGML 和 XML 都支援,但本文敘述的條件編譯,和以命令參數選項改變條件編譯設定值,我只在 SGML 下的 openjade 試成功過,xmlto 找不到相關功能。礙於作者學養有限,只能知其然,而無法告訴大家所以然,如果有能明瞭 XML 與 SGML 解譯器表現不同的原因,或者某種 Markup 語言另有蹊徑,都請 mail 到本文序言的作者信箱指導我一下,我一定馬上進行整理,公佈出來,讓大家分享你的慷慨。


5. 結語

看完本文各章節範例,利用 SGML 定義的實體功能,所能做到的常數宣告,含括外部檔案,以及條件編譯,相信你對 SGML 格式文件模組化能力印象深刻。以 SGML 靈活的組合性,加上 DocBook 標籤定義的豐富完整,難怪他成為各軟體開發團隊共同文件交換格式的首選。對這些優點,我們不用再說一遍,但有些在標記語言使用時該注意的事項,在結語中提醒大家一下:


6. DocBook 相關問答集

DocBook 寫作問答

6.1. 我該用 DocBook 取代其他格式文件嗎?
6.2. 我的資料消失了?
6.3. 應該注意使用的編碼字元
6.4. DocBook 的文件型態定義的名稱有分大小寫嗎?
6.5. PI(Public Identifier) 和 SI(System Identifier)那個好呢?

這裡嘗試回答一些你在製作 DocBook 文件時,可能碰到的問題。

6.1. 我該用 DocBook 取代其他格式文件嗎?

既然 DocBook 可以一次輸入,然後轉成 HTML,PS,RTF 多種格式輸出,那是否意味著以後我只要學 DocBook 一種格式就好,反正其他的格式可以用轉換的。

我個人的答案是否定的。第一:DocBook 本身不是被設計來取代任何其他可供閱覽的格式文件用的,他存在目的不是要改變你的顯示方法和行為,他存在的目的是整理組織你的各種文件,也就是他把一堆雜亂的資料,彙整到你想表達的模式中去。所以 DocBook 不能取代 HTML,PS,RTF ,因為那是不同功能的兩種東西,你應該同時學習一種你喜歡的閱覽格式文件再加學一個 DocBook ,用 DocBook 幫你組織和交換你的資料來源。

第二:DocBook 並未包含所有的文件主題,就像本手冊中一再強調的,DocBook 的目的在製作出論理性的,技術性的文件。對於一些文藝性的,甚至多媒體格式,DocBook 並不適合。所以在寫理論文章時,你可以用 DocBook 加一個其他閱覽格式當良好的組合方案,但如果你要表達詩詞歌賦的意境,多媒體的藝術感動,你必需為你使用的閱覽格式文件,尋求其他的配套組合。

6.2. 我的資料消失了?

為何我在 DocBook 文件中某些標籤的內容資料,轉換成其他格式文件後,並未出現在新的格式文件中?我標籤的語法和內容資料格式都合法,並通過解析器的合法檢測,並無任何錯誤顯示,請問錯誤出在那裡呢?

非常有可能是毫無錯誤,因為負責設定轉換樣式的 DSSSL 文件不只能決定一個標籤輸出成什麼樣子,更可以決定那些標籤資料要輸出,那些標籤不輸出。所以如果你參照 http://www.study-area.org/tips/doctrans/doctrans.html 中譯的說明,分別在 -d 選項使用 Norman Walsh's DSSSL 的 docbook.dsl 和 TLDP 的 ldp.dsl 編譯一次,就會明白某些部份 docbook.dsl 和 ldp.dsl 會有選擇輸出的不同。所以,DocBook 的某些標籤內容資料,在某些 DSSSL 檔案下不會轉到新文件中,不僅正常,而且正是 DSSSL 要發揮的選擇功能。

6.3. 應該注意使用的編碼字元

DocBook 中除了 '<','>','&' 負有特殊功能,有使用限制外,還有那些字元的使用有限制呢?

'_' 底線字元

底線字元不該出現在實體(ENTITY)設定字串,或屬性設定字串裡。

6.4. DocBook 的文件型態定義的名稱有分大小寫嗎?

DocBook 那麼多的標籤和屬性名稱,以及定義的特殊 ISO 實體字元,除了不能拼錯字外,有沒有大小寫之分呢?

這問題有個模凝兩可的答案,如果希望你的 DocBook 文件由 XML 語法來解析,那麼就標籤和屬性名稱來說,是有大小寫的分別,但如果由 SGML 語法來解析,沒有大小寫分別,也就是 "BOOK" 和 "book" 代表一樣的意思。但這只是就標籤及屬性名稱而言,如果一個由實體定義的名稱,不管是 XML or SGML ,都是有大小寫分別的,所以代表 '<' 字符的永遠只能是 &lt; ,&LT; 絕對是錯誤。最後的建議是,為了一個 source 有可能經過 XML 和 SGML 兩種語法解析器的轉換,所以還是採取大小寫有區別的寫法比較好,如果不確定,多瀏覽一下 DocBook 官方版線上手冊,跟著標準走,是保證交流無礙的唯一法門。

6.5. PI(Public Identifier) 和 SI(System Identifier)那個好呢?

SGML 採用 PI 的方式搜尋 DTD 模組檔案,XML 則採用 SI 的方式搜尋 DTD 模組檔案,這兩種 DTD 資源定位法的工作原理有何不同?倒底那個比較好呢?

先說說 SI 方式好了,如果一個標記格式文件的 DTD 模組已經龐大到不適合和本文內容資料放在一個檔案時,他就會將標記格式文件的 DTD 模組資料部份與本文分離,存成另一個檔案,來供標記本文內容呼叫。既然 DTD 模組是另外一個檔案,本文要搜尋到他,當然要紀錄他的路徑位址,所以最開始的 DTD 模組搜尋定位,是採用 SI 的方式,就是 SYSTEM 表述詞後面加 DTD 檔案路徑位址字串。

這在標記文件只在一台機器中運作時是 OK 的,但如果你把一份標記格式文件從一台電腦搬到另一台電腦中解譯時,煩麻就來了。因為 A 電腦的某種 DTD 檔案位址有可能和 B 電腦的 DTD 檔案位址不同(尤其是像 DocBook 這種文件本體與 DTD 模組分別安裝的狀況特別明顯),那麼你根據 A 電腦的 DTD 位址設定標記文件中的 SI 位址,很可能到了 B 電腦,DTD 卻在另一個路徑,導致你的 SI 設定失效。

為了解決這個不同機器,檔案路徑規劃不同的問題,SGML 發表 PI 的規格,就是類似 PUBLIC "-//OASIS//DTD DocBook V4.2//EN" 這樣的東西,它不直接指明 DTD 相關檔案路徑何在,只是給每個不同的 DTD 模組一個唯一的,與所有其他種的 DTD 不同的識別字串。然後由 catalog 這個檔案,把 PI 識別字串和這台機器的 DTD 檔案路徑,做一個兩兩對應,當 SGML 的解析器讀到文件型態宣告中的 PI 字串時,就不是去找文件內的 SI 設定,而是到 catalog 這個檔,查明這個 PI 字串所代表的 DTD 模組,是在這台機器的什麼路徑上,就可以不受不同機器,路徑不同的困擾了。所以我們在建立 SGML 文件解譯環境時,必須先安裝好各個可能用到的 DTD 模組檔案,然後用一個 catalog 檔,紀錄每個 DTD 模組的 PI 字串和在本台機器的檔案路徑對應表。那麼當使用 SGML 解析器時,或透過 SGML_CATALOG_FILES 環境變數指明系統的 SGML catalog 檔,或者用參數命令標明 catalog 檔所在,就可以找到所有在這台機器上的各種 DTD 檔案了。這就是 SGML PI 運做的基本原理。

SGML 的 PI 設計,並沒有獲得 XML 青睞,XML 仍然是採用 SI 的方式去定位尋找 DTD 檔案位置。不過由於 XML 強調跨網路的資料運作,所以他的 SI 不是指本機上的 DTD 資源路徑,而是指網路上的 URL 位址。這樣的設計有他強大的好處,首先 SGML 文件要運作前,一定必須在本機安裝相關 DTD 模組檔案,但 XML 不必,因為它不是從本機取得 DTD 設定,而是透過網路傳送,取得 DTD 模組檔案。所以要看某種 DTD 定義文件,不必在每台機器裝相關的 DTD ,只要在網路的某處有設定好的 DTD 模組資源即可。

再其次,最後 XML 採用的文件定義,是由 SI 指定的網路 DTD 決定的,那麼所謂的 PI 就不再那麼具有權威性和標準性,反而變成一種備註說明而已。所以萬一 DTD 的維護單位對規則定義有所更動,XML 網路 SI 的方式,馬上更改,馬上生效,不必像 SGML 的 PI 方式,還要通知 User 下載更新 DTD 到自己機器裡安裝才行。

XML 網路 SI 功能這麼好,那缺點呢?當然就是網路要通啊,其次就是萬一原來發佈 DTD 的單位改了原來的 DTD URI ,那麼 XML 都會因為找不到 DTD 而解譯失敗。當然 XML 的 SI 也可以指向本機的 DTD 路徑位址,但一個指向本機 DTD 的 SI ,當然比 SGML 的 PI 還不如。

其次 XML 改版更新快速的優點也可能造成不良後果,那就是如果我用 SGML 的 PI ,只要我本機的 DTD 模組檔案不變,我寫出來的 SGML 文件就可以永遠得到同樣的結果。但 XML SI 的 DTD 檔,卻是由 DTD 發佈單位控制的。萬一主事者沒概念,更改 DTD 定義不用新版本號另開一個 DTD 網路位址,而直接向原來位址的 DTD 檔案下手的話,那麼就會有你前兩天製作的合法的良好 XML ,過兩天卻變得不合法,格式不良了。所以使用 XML SI ,你最好必須先確定,那個 DTD 的發佈者,會把他的 DTD 維護得很穩定。

以上就是 SGML PI 和 XML SI 的運作原理大略分析,可說兩方各有利弊,但如果網路暢通,DTD 發佈者又維護的很好的話,網路 SI 無疑是比較能發揮即時功效的系統。

DocBook 參考字典

DocBook 相關字詞解釋

D

Document Style Semantics and Specification Language
(DSSSL)

文件樣式表示及設定語言:一種以 SGML 標記語法定義的標記語言,他被定義的目的是要替其他以 SGML 語法定義的格式文件,決定一個輸出的樣式。簡單的說法 DSSSL 是其他 SGML 文件的型版或樣式表,透過他 SGML 能轉換成其他各種不同格式的文件。

See Also: Standard Genralized Markup Language.

Document Type Definitions
(DTD)

文件型態定義:使用在 SGML 文件中,一種用來描述某特定格式的文件分類和階層結構的說明文件,是 SGML 文件的基礎和起點。

See Also: Standard Genralized Markup Language.

E

Element
(tag)

分類元素:這是使用在 SGML DTD 文件中的一個基本組成。首先我們將某特殊格式文件做分類,然後用標記加以區隔,並給不同的類別一個特定的名稱,我們稱一個類別就是一個 Element (其實也就是一個標籤的另一種說法),而一份文件就是由不同的 Element 有組織有架構的組織起來的。

See Also: Document Type Definitions.

ENTITY
(ent)

實體,這個名詞很難望文生義,就其實際代表的含意是指:本文部份片段的代名詞。本文片段小到一個字符,一個字串,大到一個檔案,都可以取一個名稱來代表它,而最後解譯的結果,顯示的也是它代表意義,而非那個代名

O

Organization for the Advancement of Structured Information Standards
(OASIS)

結構化資訊標準宣導組織(如果你願意,也可以叫它綠洲組織,這樣的縮寫名稱應該是有意的安排吧)。這是非營利的推廣組織,主要是在負責推動一些產業界資訊標準的研究,資料蒐集與開發,尤其是 SGML 和 XML 的推廣和介紹。本文介紹的 DocBook 格式文件標準,即由 OASIS 負責制定,維護,更新。

P

Public Identifier
(PI)

公共識別字串:就是每個不同種類及版本的文件格式標準製作者,為自己的格式標準取一個獨特且唯一的識別字串,並公告周知。讓別人或解譯程式看到這樣的字串,就知道此文件採用某種特定格式的標準,而據以處理。一般他的格式是:

"prefix//owner-identifier//text-class text-description//language//display-version"

例如 DocBook 4.2 DTD 的 PI 是:

"-//OASIS//DTD DocBook V4.2//EN"

各部份組成意義如下:

prefix, 前置符號

分為 '+' 和 '-' 兩個字符,'+' 表示有經 ISO 組織正式註冊,'-' 則是未向 ISO 組織申請註冊手續。

owner-identifier, 擁有者識別字串

表明此一標準是由那個個人或組織負責訂定及維護的,例如 DocBook 4.2 DTD 是由 OASIS 這個組織制定維護的。

text-class, 文件類別

在 SGML 中較常見的分類有:DOCUMENT,DTD,ELEMENTS,ENTITIES,NONSGML。

text-description, 文件描述

制定標準者可以為其制定的格式標準使用一個獨特的描述詞,如 DocBook V4.2。

language, 語言區域標識

表示這個文件標準是使用何種地區的語言文字製作的,通常使用 ISO 組織兩字符區域語言標準協定。

display-version, 版本編號標識

如果同樣的類別,同樣描述的格式標準,先後出了幾個有修正的版本,可以使用此項識別。這是一個選項,不一定要使用。有些 PI 字串在 text-description 中就包含了版本分別,所以沒有使用到 display-version 這項識別。

See Also: System Identifier.

S

Standard Genralized Markup Language
(SGML)

通用標準標記語言:一種描述文件分類及組織架構的表示語法,這種語法是透過在文件中插入標記資料,來做文件內容分類和組織。透過共同的 SGML 語法標準,不同的閱覽者及閱覽程式,能夠準確的掌握文件的結構並加以有效的處理。制定 SGML 的 ISO 組織並努力制定不同機器平台和作業環境的資料交換格式標準,藉由這些附加標準,遵照 SGML 標準寫作的文件,都能享有跨平台交換資料的可攜性便利。

SGML Declaration
(SGMLDECL)

SGML 宣告:提供編譯 DTD所需的全部資訊及剖析文件實例所需的部份資訊。例如,它定義了文件的字元(Characters Set)、標籤的界限符號 (Delimiters)、處理文件時所需的儲存容量(Storage Capacity)、文件使用的具體語法(Concrete Syntax)、具體語法適用的範圍(Scope)、文件使用的SGML特徵(SGML Features)等等。

See Also: Document Type Definitions.

System Identifier
(SI)

系統識別:這是和 Public Identifier 為了同一目的的另一種作法,都是為了含括外部檔案,達成文件模組化的機制。一開始 SGML 含括外部檔案都是採用 System Identifier 的作法,就是前面加一個 SI 的表示詞,後面就是被含括檔案的絕對或相對路徑位址字串。例如:

somename SYSTEM "被含括檔的路徑字串"

somename 可以是作者自己選用的任一名稱,因為 SYSTEM 的識別,我們就可以在需要插入檔案的地方用 &somename; 這樣的表示式,來插入被宣告的檔案。

雖然這是很有用的工具,但當一個文件有很龐大數量的含括檔必須被經常性引用時,就會在跨平台傳遞遭遇不同機器,檔案路徑佈置差異,所導致的含括檔案位址錯誤。所以才有 Public Identifier 和 CATALOG 的新方法,有關 Public Identifier 相關解說,請參考本辭典 PI 項解說。

See Also: Public Identifier.

X

Extensible Markup Language
(XML)

可延伸式標記語言:為了在 Web 網站上運作,便於資料交換傳遞專門設計的 SGML 精簡指令集。他基本上延續了 SGML 的精神和運作模式,但採用了更嚴格的標記表示法,並刪除了一些 SGML 上與網路運作沒有直接關聯的指令和功能,目的是讓應用程式能更簡便而快速的達成解析轉譯的工作。

See Also: Standard Genralized Markup Language.

Extensible Stylesheet Language
(XSL)

延伸式樣式表語言:就如同 DSSSL 之於 SGML 的負責格式及輸出轉換,XSL 也負責 XML 的格式及輸出轉換。

See Also: Extensible Markup Language, Document Style Semantics and Specification Language.

Extensible Stylesheet Language Transformations
(XSLT)

XSL 的變體,又稱為新一代的 XSL 。

See Also: Extensible Markup Language, Extensible Stylesheet Language.

Notes

[1]

XML 語言同樣有常數宣告與含括外部檔案的功能,但不如 SGML 定義的完整,功能比較少,也不像 SGML 那麼方便。

[2]

例如我們最常使用的 SGML 解析器 OpenSP

[3]

如果你的 DocBook 解譯環境是採用類似DocBook 解譯轉換環境的建立方式建立的,那你應該有 openjade 和 onsgmls 這兩個解析程式可以用,以命令模式輸入:

onsgmls -s -w duplicate sgmlfile
openjade -s -w duplicate sgmlfile

就可以查出整份文件是否有重複宣告同名 ENTITY。不過一定要有個概念,重複宣告同名 ENTITY 在 SGML 中是合法的,它用這個特性製作出有延展性可塑性的 DTD 模組架構。

[4]

openjade 是個不死鳥的解譯器,他會盡一切可能得出理想的輸出結果,縱使你的語法錯誤得很嚴重。

[5]

如何建立各種可登入,並上下傳資料的伺服器,請依各伺服器相關說明辦理,本文不另做說明。

[6]

你可以用 # su - kenduest的方式來模擬 kenduest 的登入主機。

[7]

有關 openjade 使用命說明請參考 openjade 安裝說明文件 。如果你是用的 red hat 公司的 docbook-utils 工具,則可以使用 docbook2html SgmlFile -i site.else.choice。