發表文章

目前顯示的是 2014的文章

Ubuntu 12.04 安裝 graphviz 遇到 confclit

在 Ubuntu 12.04 裝 graphviz 遇到 liggd2-xpm 和 libgd2-noxpm 衝突的問題: $ sudo aptitude install graphviz The following NEW packages will be installed: fonts-liberation{a} graphviz libcdt4{a} libcgraph5{a} libgd2-noxpm{ab} libgraph4{a} libgvc5{a} libgvpr1{a} libpathplan4{a} ttf-liberation{a} 0 packages upgraded, 10 newly installed, 0 to remove and 427 not upgraded. Need to get 2,716 kB of archives. After unpacking 6,188 kB will be used. The following packages have unmet dependencies: libgd2-xpm:i386 : Conflicts: libgd2 which is a virtual package. Conflicts: libgd2-noxpm but 2.0.36~rc1~dfsg-6ubuntu2 is to be installed. libgd2-noxpm : Conflicts: libgd2:i386 which is a virtual package. Conflicts: libgd2-xpm:i386 but 2.0.36~rc1~dfsg-6ubuntu2 is installed. Internal error: the solver Install(fontconfig-config:amd64 2.8.0-3ubuntu9 {fontconfig-config:amd64 2.8.0-3ubuntu9}>) of a supposedly unresolved dependency is already installed in step 125 $ dpkg -l | gre…

善用 C++ istream 的 eof() 和 fail()

當函式需要讀入資料做處理時, 讓函式接收一個 std::istream* 會比直接呼叫 std::cin 好一些, 必要時可以改傳 std::istringstream 提供更多彈性, 比方說方便作測試。不過實測的結果, std::cin 和 std::istringstream 的行為略有不同。std::cin 讀到 EOF 後, 呼叫 eof() 才會回傳 true, 但是 std::istringstream 在讀完最後一個 byte 後, eof() 就回傳 true 了。以下是一個小範例, 可以正確處理兩者, 關鍵是讀完資料後用 fail() 檢查剛才的輸入是否有效。 #include <iostream> #include <sstream> void foo(std::istream* s) { int n; while (!s->eof()) { *s >> n; if (!s->fail()) { std::cout << n << std::endl; } } } int main(void) { std::istringstream ss("1 2 3 4 5"); foo(&ss); foo(&std::cin); return 0; } 另外附上別人截錄的官方說明。

備忘 Mac OS X 下的一些設定

加減寫下一些設定, 備忘在這篇裡。那天買新的 Mac 裝置時, 才知道要做那些設定iTerm2 ssh除 preference -> terminal 要設 UTF-8外,/etc/ssh_config 也要加上 SendEnv LANG LC_ALL=en.US.UTF-8參考資料: iterm2 ssh和openssh客户端中文乱码的解决方案 | 线筝

ph: python HTML generator without using any template syntax

偶而需要用 python 產生 HTML 報表, 自己手刻 HTML 頗麻煩的。寫個小 script 不會像寫網頁那頁使用 MVC 將資料和顯示分離, 不方便套 template (況且學 template 的語法也有點麻煩)。找了一下發現 pyh, 語法滿直覺的, 大致符合我的需求。於是自己重做了一個 ph, 改成更合自己習慣的語法, 順便練習自己設計一套簡單的 DSL, 寫起來像這樣: from ph import * doc = HTML() doc.body() << p('Hello, world!') << (p('What a ') << strong('wonderful') << ' world!') print unicode(doc) 輸出像這樣 (有手動排版過): <!DOCTYPE html> <html> <head><title>No Title</title></head> <body> <p>Hello, world!</p> <p>What a <strong>wonderful</strong> world!</p> </body> </html> 以前沒想過覆寫 operator, 覺得這種作法容易讓人困惑。但是以 HTML generator 的例子來說, 用 << 組合 HTML 元素挺方便的。

在 vim 內 indent JSON

太容易忘了,乾脆寫篇文章加強記憶: :%!python -m json.tool 是的,其實就是呼叫 python module 來處理

wmic: Windows Management Instrumentation Command-line

Windows 方面的技能毫無長進, 這就是典型的同樣的事做個十年, 沒有學習的話也不會有所長進...。最近重灌電腦才發覺 wmic 這個好東西, 可以取得硬體資訊 (如主機版型號), 管理 process 等。搜尋 "wmic tips" 會看到很多介紹它的好處和用法。寫這篇廢文只是想降低我忘掉 wmic 的機率...。Wikipedia 有相關介紹, 其中有幾個關鍵字等要用的時候再深入研究一下, 像是 CIM, DMI, SNMP, 概念是對硬體裝置定好統一的 API, 方便本機或網路查詢、更新設定。

找出 dangle pointer 的方法

有時候 C++ 程式會莫明地 crash 在呼叫 method 的時候, 像是 obj->method()。檢查 obj 的值有幾種情況: NULL: 一看就知道有問題某種規律的數字, 像是 0x23232323, 0xCDCDCDCD: 99% 有問題, 還沒猜錯過特定的位置, 如 0xDEADBEEF: 看了也知道有問題, 這是 debugger 特意覆寫的值看起來像個正常的指標, 可能是 dangle pointer需要注意的是, 透過 dangle pointer 呼叫 method, 不一定會 crash。不過呼叫 virtual method 時一定會 crash, 因為 destructor 會重置 virtual method table 內的值 (相關文章見《C++ 執行後掛在 __cxa_pure_virtual》)。以下是一個測試例子: $ cat a.cpp #include class Rect { public: Rect(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) {} int area() const { return w * h; }; virtual int area2() const { return w * h; }; private: int x, y, w, h; }; int main(void) { int w, h; scanf("%d%d", &w, &h); Rect *r = new Rect(10, 20, w, h); printf("area: %d\n", r->area()); printf("area2: %d\n", r->area2()); delete r; printf("area: %d\n", r->area()); printf("area2: %d\n", r->area2()); return 0; } $ ./a 30 40 area: 1200…

用 regexp 的 lookahead 尋找符合 pattern A 但不符合 pattern B 的字串

偶而會需要找內含字串 A 但不含字串 B 的字串, 若剛好得用 regexp 表示的話, 會有點麻煩 (像是用 Android logcat filter 的時候)。查了一下, 發現 regular expression 有個強大的語法叫作 lookaround, 用它可相對容易地達到此需求, 還可以應付各種情況。詳見 Regex Lookarounds: Lookahead and Lookbehind 的介紹。關鍵在於 lookaround 的語法只是從「目前的位置」往前或往後「看看是否符合目標 pattern」,不會實際占去符合的字串。看文件的例子會比較好理解。 以下是用 Python RE 比對「內含 abc 但 abc 之後不含 def 的字串」:In [70]: re.search('(?!.*def)abc', 'abcdef') (?!.*def)abc 的意思是每一個位置都做以下的事: 先往後找看看有沒有符合 .*def, 找不到才算成立。(?!...) 的意思是 negative lookahead negative lookahead 成立後, 看看目前位置是否能找到 abc 反之, 下面這個寫法是錯的, 比對 abcdef 仍會有結果:In [69]: re.search('(?!def)abc', 'abcdef') Out[69]: <_sre.SRE_Match at 0x2f2fd98> 因為它的意思是在目前位置看看是否不符合 def, 於是一開始比對 abc 時就成立了, 然後再比對成功 abc, 於是回傳比對成功的結果。 (?!.*def)abc 看起來很美好, 但它無法避開 defabc, 也就是 abc 之前有 def 的情況。雖然有 lookbehind 的語法, 但它只能比對固定長度的 pattern, 無法應付 xxxdefxxxabc, 所以得換個方式表示。 regex - Regular expression to match string not containing a word? 說明如何用 regexp 表示不含目標字串的字串, 並有圖解說明運作的過程。了解之後, 可運用同樣的技巧表示「內含 abc 但 abc 之前不含 d…

讓 git diff 顯示 utf-16 (或其它binary) 檔案的差異

兩種作法: 透過 difftoolunicode - Can I make git recognize a UTF-16 file as text? - Stack Overflow$ git difftool commit1 commit2 我直接跑 difftool, 沒設定的情況會問我要不要用 vimdiff。 用 textconvTextconv - Git SCM Wiki 這個作法比較好, 適用 git diff, show, blame。 比方說對於 iOS L10N 的檔案, 可以這麼設: 編輯 PROJECT/.git/info/attributes: 加入 *.strings diff=localizablestrings 編譯 /.gitconfig: 加入 [diff "localizablestrings"] textconv = "iconv -f utf-16 -t utf-8"

使用 OAuth2 refresh token 的好處

初次看到 refresh token, 想說這東西真礙事, 原本 OAuth2 基本的流程已將使用者的密碼和授權分離, 看起來很完美了, 為什麼授權的 access token 之外, 還要多一個 refresh token? 看了 security - Why Does OAuth v2 Have Both Access and Refresh Tokens? 才明白 refresh token 更進一步提升安全性。 access token 解決了: 使用者不需告訴應用程式密碼 使用者可以細分授權項目 使用者可隨時撤消授權 當 access token 外洩的時候, 只要使用者立即撤消授權, 就可以中止傷害, 相較於修改密碼方便許多 (一組密碼可能用在一到多個帳號上, 甚至可能忘了那些帳號使用同一密碼)。 但是為了減少使用者的認證的麻煩, 一個 access token 通常會授權使用很長一段時間 (比方說一個月)。使用者不容易知道什麼時候發出去的 access token 外洩了, 傷害期可能會太長。外洩的原因可能是使用 access token 取存服務時被竊聽破解, 或是在裝置端的應用程式 (PC、smartphone) 存放的 access token 直接外洩。 有沒有辦法可以兼顧減少使用者認證, 又降低 access token 外洩的傷害期呢? 答案是加上 refresh token。設定授權的 access token 在很短的時間內會過期 (如一小時), 然後 client(應用程式) 要用 refresh token + client id + client secret 取得新的 access token。 client 不需要透過使用者就可以取得新的 access token, 不會打擾到使用者。沒有 client secret 的情況下, 單是外洩 access token, 別人用一小段時間後, token 就過期了, 傷害時間很短。 不過要達到上面的好處, client 必須在不同地方存下 refresh token 和 client secret, 偷懶存在同一地方, 一但破台也是一樣。 security - Why Does OAuth v2 Have Both Access and Refresh Tokens?

讓 Eclipse 連接到小米3 的方法

過濾 iOS log 的方法

平常開發 Linux 程式需要即時觀察 log, 又有太多 log 要過濾的時候, 會用$ tail -f LOG_FILE | grep PATTERN # 只留符合 PATTERN 的 log 或是 $ tail -f LOG_FILE | grep -v PATTERN # 去掉符合 PATTERN 的 log command line 的 pipe 可以無限接, 方便做很多延伸運用。 開發 Android 的時候可以透過命令列的 adb logcat, 或是 Eclipse 也有提供介面過濾 log。 開發 iOS 時比較困擾, 通俗是用 NSLog 寫到 console, 但是沒有找到用 XCode 過濾的方法。 轉念一想, 既然 iOS 裝置接上 XCode 後可以看到過去的 console log, 表示 log 存在檔案裡。直接取 log 的原始檔案, 比照在 Linux 的習慣直接對檔案操作應該比較簡單。找了一陣子找到檔案放在 $HOME/Library/Application Support/iPhone Simulator/7.1/Library/Logs/system.log, 接著套上在 Linux 的習慣, 就方便多了。

Ctrl+C 運作的原理以及 session, terminal 和 process group

簡短版說明前陣子遇到一個神祕的問題: 執行程式 P 的時候, 可以用 ^C 中止它。但透過 shell script S 執行 P (S 內只有該程式名稱和參數而已), 就無法用 ^C 中止它。^\ 也是一樣情況, 不過 ^Z 可以正常 suspend。 檢查其它的環境因素發現: kill -INT PID 可以中止 P, 表示程式沒有攔截 SIGINT。 stty -a -F TTY_DEVICE 顯示 intr = ^C 且 isig, 表示輸入 ^C 後會被轉譯成 SIGINT。按 ^C 後螢幕上有顯示 ^C。 綜合以上的資訊, 無法理解還有什麼原因會讓 ^C 轉送 SIGINT 失效。 卡了一陣子後, 回頭翻 TLPI ch34 Process Groups, Sessions, and Job Control 以及 ch62 Teriminals 才找到答案。 答案是: P 有用 setpgid() 產生新的 process group, 於是 P 就不是 terminal foreground process group, 而 ^C 只會送給 terminal foreground process group。 詳細說明在沒有透過 shell script 執行 P 的情況, P 會自成一個新的 process group, 同時也是 terminal foreground process group (這是 shell fork process 後設定的)。之後即使程式有呼叫 setpgid(), 也不會改變 pgid, 因為 P 本來就是自己 process group leader。這時, P 仍然是 terminal foreground process group, 收得到傳給 terminal 的 ^C。 另一方面, 透過 shell script S 執行的情況, S 自成一個新的 process group 也會是 terminal foreground process group (同樣的, 是 shell 設定的)。S 執行 P, P 又用 setpgid() 自己成立一個新的 process group, 這個新的 process group 不是 terminal foreground process group, 也…

如何找出 iOS crash log 中的 symbol name

從 Sam 那看到的參考文章: 别用symbolicatecrash来解析crash log了 - Wonderffee's Blog 若 binary 是別人產生, crash log 也是別人產生時, 可以照以下的方式對回 symbols: 取得產生 crash log 對應 app binary 的 archive 目錄下的 dSYMs 目錄。 在命令列下輸入: $ mdimport dSYMs。 將 crash log 存成 xxx.crash (副檔名必須是 .crash)。 打開 XCode 的 Oraganizer, 點選 Library 下的 Devices Logs, 點選 Import, 選擇 xxx.crash。 結果會顯示在畫面右側。

lldb to gdb command

官方連結: LLDB to GDB Command Map 在這備忘自己常用的指令: return EXPR -> thread return EXPR顯示 step out 後的傳回值出處:#/usr/bin/env python import lldb # Put _this_ file into ~/Library/lldb/thread_return.py # Put the following line into ~/.lldbinit # command script import ~/Library/lldb/thread_return.py def __lldb_init_module(debugger, internal_dict): debugger.HandleCommand('command script add -f thread_return.thread_return thread_return') def thread_return(debugger, command, result, internal_dict): '''Prints the return value of the last function you stepped out from. This is very useful if the return was a complex expression. This lldb command prevents you from needing to create a temporary variable just to inspect the return value''' # If anyone knows a way of doing this without using needing to script lldb in python... target = debugger.GetSelectedTarget() process = target.GetProcess() thread = process.GetSelectedThread…

用 vim tab 批次處理多個檔案

最近才發覺 vim 有強大的批次處理功能, 相見恨晚啊。 在命令列同時開啟多個檔案到多個 tabvim -p module/*.h 開啟 module 目錄下全部 .h 檔, 用一個 tab 編輯一個檔案。 需配合 tabpagemax 使用 (set tabpagemax=1000), 預設 tabpagemax=10, 不太夠用。 在 vim 內開啟多個檔案到多個 tab:args module/*.h :argdo :tabe 效果同上一個作法。但 argdo 為了加速讀檔會先關掉 syntax, 要自己加指令開啟 syntax::args module/*.h | execute 'argdo tabe %' | syntax on 在 vim 內更改全部 tab 內容並存檔:tabdo :% s/xxx/ooo/ :tabdo :w 第一行對全部 tab 逐一執行 :% s/xxx/ooo/, 第二行則是逐一執行 :w。一但有 tab 無法執行指令, 就會停下來。 :tabdo 可用的場合很多, 比方說我習慣用 vim -p 一次開啟多個 core dump 的 backtrace, 然後用:tabdo normal zR unfold 全部檔案, 還有用 :tabdo 下指令 highlight 關鍵字: :tabdo hi KeywordTemp ctermfg=red :tabdo syn match KeywordTemp /SOME_KEYWORD/ 方便閱讀不同 core dump 中共通的部份。

git 的入門文件

記錄一下我學 git 的過程。這些文件看起來都滿不錯的, 應該也適用沒接觸過 DVCS 的人 (我先前有用 mercurial 的經驗)。 Pro Git先在網路查些基礎指令 (介紹愈短愈好), 還有抄別人的 git config 簡化成自己的版本, 就這樣上工一陣子。 然後需要 merge branch 時, 看這本第三章講 branch 的部份, 有許多圖示說明 git 內部資料的變化, 一目了然。看不懂的時候就回頭翻第二章, 滿容易上手的。 Git Tutorials and Training | Atlassian平時要用什麼指令就 google 一下, 再點進 stackoverflow 看別人的說明。這樣瞎用了一陣子後, 回來快速掃一遍這裡的說明, 才比較有系統地記下常用指令。 Btw, 以前學 mercurial 的時候是先有系統地掃一遍常用指令, 再開始用。這回學 git 反過來, 覺得這樣學也不錯。或許基礎比較紮實後, 學類似的新東西時, 採取要用再補的方式會比較有效率。

從 third-party library 的 console 錯誤訊息找出更多線索

最近遇到一個不錯的小例子, 可以比較完整地記錄用到的相關指令。更多關於 gdb 的說明, 見 gdb 初步心得。 問題Ubuntu 下執行程式後看到 "Gtk-WARNING **: cannot open display:", 然後程式就掛了。 解決過程為了獲取更多線索, 我想找出是那段程式輸出這個錯誤訊息。由 "Gtk-WARNING" 可知是 Gtk+ 函式庫輸出的錯誤訊息。所以得先取得 Gtk+ 的 debug symbol 和原始碼: $ dpkg -l | grep gtk ... ii libgtk2.0-0 ... 找出已安裝的 Gtk+ 函式庫叫做 libgtk2.0, 接著找出它對應的 debug symbol package 和原始碼:$ aptitude search libgtk2.0 i libgtk2.0-0 - GTK+ graphical user interface library ... i libgtk2.0-0-dbg - GTK+ libraries and debugging symbols ... i libgtk2.0-dev - development files for the GTK+ library ... 然後安裝 debug symbol, 還有取得 Gtk+ 的原始碼:$ aptitude install libgtk2.0-0-dbg $ apt-get source libgtk2.0-dev 從取得的原始碼找看看有沒有 "cannot open display":$ cd gtk+2.0-2.24.10/ $ grep "cannot open display" -R . ./gtk/gtkmain.c: g_warning ("cannot open display: %s", display_name_arg ? display_name_arg : ""); ./gdk/gdk.c: g_warning ("cannot open display: %s&quo…

初次使用 paper prototyping 做使用性測試

圖片
雖然時常看到有人推薦用紙筆畫 app 介面做使用性測試, 但是要自己動手畫重覆的東西, 實在是提不起勁。前陣子聽阿修介紹 Paper Prototype, 也有同事動手畫了幾頁, 實際跟著流程走一次, 感覺還不錯。剛好我有在 iPad 裡裝 POP, 當場用 POP 結合同事做的紙張雛型, 互動效果出乎意料的好。 最近想幫爸媽做一款「待做事項」的軟體, 順便練習從頭開發一款 app 要經過的流程。中午花了大概一小時畫草稿加上用 POP 制作雛型, 過程滿順暢的。下面是部份原稿和 POP Android 版的畫面。 和爸媽分別測試後有找到一些小問題, 不過確定大方向應該可行。後來找我哥測試, 原以為應該會更順暢, 但是因為我哥使用 iPhone, 我是針對 Android 畫的草圖, 結果不容易融入情境。再加上我哥不是我的目標對象, 主要操作流程和他的期望不同, 也影響測試的流暢度。 歸納一下目前對使用 POP 做 paper prototyping 的看法。 目的: 做使用性測試。了解使用者是否了解如何操作。功能是否有滿足使用者的需求。 注意事項: 需要篩選目標使用者。 測試前要做情境描述, 使用者才能進入狀況 (看著草圖想像要做的事)。 盡量觀察使用者的反應, 少開口提醒。 優點: 制作速度快, 適合早期討論, 甚至能在開會中當場做。 易於專注在操作流程, 不會討論配色、字型、畫面排版等細節。 任何人都可以製作, 工程師和設計師以外的人也可藉此展示自己的想法。 限制: 只適合能融入情境的使用者。也許可透過幾次體驗後克服。 還是有可能插題討論圖示, 所以也不能畫得太糟。 總結來說, 現在我覺得用 POP 做 paper prototyping 滿快的, 但若是只用紙張互動, 一來我做得比較慢, 二來使用者也比較難聯想。我自己不適合用純紙張版本的 paper prototyping。 附帶一提, 做得過程中要自己用紙筆畫出概要, 才留意到一些 Android UI 的小細節。還有實際測試流程不像影片裡那樣順。看了再多資料, 還是得親身走一遍才行。

C++ 下 thread-safe 的 lazy initialization

《clang 避免 non-local static 物件初始化順序的方法》提到可用 static local variable, 然後用 method 傳回的方式避免產生 global static variable (藉此避免不同編譯單元的初始化問題)。以下是一個例子:const struct Point* center() { static Point* s_center = CreateCenterPoint(); return s_center; } 但是, 在 multi-thread 的環境下, s_center 可能被初始化兩次。假設 CreateCenterPoint() 會傳回不同的值, 或是外界可以改變取得的 Point, 有兩份 Point 會造成問題。 好消息是:gcc 預設有幫 local static variable 加入 thread-safe 的保護。 若 compiler 有正確實作 C++11, 在 C++11 的規範下, 有保證靜態變數的初始化是 thread-safe 的。 所以情況比想像中的安全。 不過, 其它 lazy initialization 的實作方式有可能出錯。更一般化的 lazy initialization 會用 Double-Checked Lock Pattern, 但是這個作法有不易察覺的漏洞。 直覺的作法如下:Singleton* Singleton::instance() { if (pInstance == 0) { // 1st test Lock lock; if (pInstance == 0) { // 2nd test pInstance = new Singleton; } } return pInstance; } 注意初始化 singleton 是三個步驟組成的: 配置一塊新記憶體 初始化新記憶體 將新記憶體的位置指向目標指標 (即 pInstance) compiler 有可能更動三者的順序。最壞的情況下, thread A 執行了 1, 3, 還沒執行 2, 這時 thread B 發覺 pInstance 不是 0, 於是回傳尚未初始化的 pInstance。解法是要使用 memory barr…

尋找 memory error 的強力工具: Address Sanitizer (ASan)

官網有很詳細的介紹, ASan 可以偵測出以下的問題: Use after free (dangling pointer dereference) Heap buffer overflow Stack buffer overflow Global buffer overflow Use after return 都是出錯時很難察覺, 之後會造成奇異的行為, 或是讓程式掛在莫明奇妙的地方。 clang 3.1 和 gcc 4.8 開始內建 ASan 的功能。號稱執行速度平均慢兩倍, 比起 Valgrind 的 20 倍, 實在相當誘人。這裡有說明 ASan 怎麼做的。這樣在 Linux 上也有不錯的 memory error detector 了。不知什麼時候能追上 OS X 的腳步。 除了 Chromium 有用之外, Firefox 也有使用

clang 避免 non-local static 物件初始化順序的方法

C++ 沒有定義 non-local static 物件在不同編譯單元 (translation unit) 之間初始化的順序, 所以要極力避免 non-local static 物件相互間的存取。 今天試用 clang 編譯程式, 發覺它有個不錯的選項: -Wglobal-constructors。用這選項編譯, 遇到有 non-local static 物件會依賴其它函式 (包含 constructor) 設值時, 會輸出 warning。 這裡引用 Address Sanitizer 提供的例子: $ cat a.cc #include <stdio.h> extern int extern_global; static int __attribute__((noinline)) read_extern_global() { return extern_global; } int x = read_extern_global() + 1; int main() { printf("%d\n", x); return 0; } $ cat b.cc int foo(); int foo() { return 42; } int extern_global = foo(); $ clang++ a.cc b.cc && ./a.out 1 $ clang++ b.cc a.cc && ./a.out 43 $ clang++ a.cc b.cc -Weverything a.cc:6:5: warning: declaration requires a global constructor [-Wglobal-constructors] int x = read_extern_global() + 1; ^ ~~~~~~~~~~~~~~~~~~~~~~~~ 1 warning generated. b.cc:3:5: warning: declaration requires a global constructor [-Wglobal-constructors] int extern_global = foo(); ^ ~~~~~ …

使用 CSS position 組合 div 的佈局小技巧

圖片
這篇談的方法在舊的瀏覽器大概會出包, 沒有實際研究。跨瀏覽器的議題太複雜, 希望在我不得不面對這議題前, 跨瀏覽器的痛苦可以減少很多 (大概要等 Windows XP 絕跡吧)。 以下圖為例, 這篇討論兩個使用 CSS position 的小技巧。 使用 relative position 重疊版型 上圖的書本和內文, 實際上是兩個 div 組成的, 還原後如下圖: 組合的方式如下: HTML<div class="wrapper"> <div class="content"></div> <div class="book"></div> </div> CSS.wrapper { height: 750px; overflow: hidden; } .content { height: 500px; position: relaltive; z-index: 1; } .book { height: 700px; width: 500px; position: relative; top: -600px; } 數據是我大概填的, 示意用。 幾個要點: .book 用 position: relative 從原有的位置往上移到 .content 做為背景。 為了讓 .content 在上方顯示, 設 z-index: 1。由於 z-index 只有在 non-static position 下才有效, 所以改一下 position。 .book 使用 position: relative 往上移後, 仍會在原位置留下空間, 就像 visibility: hidden 一樣。為了避免占用原位置的空間, 限製 .wrapper 的 height。 這樣做的好處是內文和背景分離, 兩者各自依需求配置 div 結構, 不會互相影響。不然要顧及內文的排版而調整背景書本的 div, 切版型會滿辛苦的。 缺點是必須限制高度, 不像一般網頁彈性地隨內文增加而自動增加高度。 使用 absolute position 疊出下方的書緣 下方展開後, 中間長這個樣子, 我隱藏了左側一個 div, 方…

Ubuntu 安裝 package 出現 conflicts 時的解法

這篇只討論我自己常遇到的一個情境: 明明只是升級某個套件或加裝相關套件, aptitude 卻發瘋似的說有些相關套件的相依版本不合, 要移掉一大堆套件才能繼續。解法是下指令時同時安裝目標套件和 aptitude 抱怨不合的相依套件。 實例如下: $ sudo aptitude install git-svn [100%] Reading package lists ... The following NEW packages will be installed: git-svn libsvn-perl{a} libterm-readkey-perl{a} The following packages will be upgraded: perl perl-base perl-modules 3 packages upgraded, 3 newly installed, 0 to remove and 793 not upgraded. Need to get 10.3 MB of archives. After unpacking 4,449 kB will be used. The following packages have unmet dependencies: libperl5.14 : Depends: perl-base (= 5.14.2-6ubuntu2.1) but 5.14.2-6ubuntu2.3 is to be installed. Internal error: the solver Install(libc-bin:amd64 2.15-0ubuntu10.5 <libc6:i386 2.15-0ubuntu10.5 -> {libc-bin:amd64 2.15-0ubuntu10.5 libc-bin:i386 2.15-0ubuntu10.5}>) of a supposedly unresolved dependency is already installed in step 153 Internal error: the solver Install(libc-bin:amd64 2.15-0ubuntu10 <libc6:i386 2.15-0ubuntu10 -> {libc…