Add file properties dialog.
[guestfs-browser.git] / filetree_markup.ml
1 (* Guestfs Browser.
2  * Copyright (C) 2010 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *)
18
19 open ExtString
20 open ExtList
21 open Unix
22
23 open Utils
24 open Slave_types
25
26 open Printf
27
28 module CL = CalendarLib
29
30 (* Base colours. XXX Should be configurable somewhere. *)
31 let file_color = 0x20, 0x20, 0xff  (* regular file *)
32 let dir_color = 0x80, 0x80, 0x20   (* directory *)
33 let symlink_color = file_color     (* symlink *)
34 let suid_color = 0x20, 0x20, 0x80  (* setuid bit set on regular file *)
35 let suid_bgcolor = 0xff, 0xc0, 0xc0
36 let sgid_color = suid_color        (* setgid bit set on regular file *)
37 let sgid_bgcolor = suid_bgcolor
38 let block_color = 0x00, 0x60, 0x60 (* block device *)
39 let char_color = block_color       (* char device *)
40 let fifo_color = 0x60, 0x00, 0x60  (* fifo *)
41 let socket_color = fifo_color      (* socket *)
42 let other_color = file_color       (* anything not one of the above *)
43
44 (* Mark up a filename for the name_col column.
45  *
46  * See also
47  * http://library.gnome.org/devel/pango/stable/PangoMarkupFormat.html
48  *)
49 let rec markup_of_name ?(visited = false) direntry =
50   let name = direntry.dent_name in
51   let mode = direntry.dent_stat.Guestfs.mode in
52   if is_directory mode then (           (* directory *)
53     let fg = if not visited then normal dir_color else darken dir_color in
54     sprintf "<span weight=\"bold\" fgcolor=\"%s\">%s</span>"
55       fg (markup_escape name)
56   )
57   else if is_symlink mode then (        (* symlink *)
58     let link = direntry.dent_link in
59     let fg =
60       if not visited then normal symlink_color else darken symlink_color in
61     sprintf "<span style=\"italic\" fgcolor=\"%s\">%s</span> %s <span style=\"italic\" fgcolor=\"%s\">%s</span>"
62       fg (markup_escape name) utf8_rarrow fg (markup_escape link)
63   )
64   else (                                (* not directory, not symlink *)
65     let fg, bg =
66       if is_regular_file mode then (
67         if is_suid mode then suid_color, Some suid_bgcolor
68         else if is_sgid mode then sgid_color, Some sgid_bgcolor
69         else file_color, None
70       )
71       else if is_block mode then block_color, None
72       else if is_char mode then char_color, None
73       else if is_fifo mode then fifo_color, None
74       else if is_socket mode then socket_color, None
75       else other_color, None in
76     let fg = if not visited then normal fg else darken fg in
77     let bg =
78       match bg with
79       | Some bg -> sprintf " bgcolor=\"%s\"" (normal bg)
80       | None -> "" in
81     sprintf "<span fgcolor=\"%s\"%s>%s</span>"
82       fg bg (markup_escape name)
83   )
84
85 (* Mark up a registry key. *)
86 and markup_of_regkey ?(visited = false) h node =
87   let name = Hivex.node_name h node in
88   let fg = if not visited then normal dir_color else darken dir_color in
89   sprintf "<span fgcolor=\"%s\">%s</span>" fg (markup_escape name)
90
91 (* Mark up a registry value. *)
92 and markup_of_regvalue ?(visited = false) h value =
93   let k = Hivex.value_key h value in
94   let k = if k = "" then "@" else k in
95   let t, v = Hivex.value_value h value in
96
97   (* Ignore long values. *)
98   let len = String.length v in
99   let v =
100     if len >= 512 then sprintf "&lt;%d bytes not printed&gt;" len
101     else markup_escape (printable_hivex_value ~split_long_lines:true t v) in
102
103   let fg = if not visited then normal file_color else darken file_color in
104   sprintf "<span fgcolor=\"%s\">%s</span>=<span fgcolor=\"%s\">%s</span>"
105     fg (markup_escape k) fg v
106
107 and normal (r, g, b) =
108   let r = if r < 0 then 0 else if r > 255 then 255 else r in
109   let g = if g < 0 then 0 else if g > 255 then 255 else g in
110   let b = if b < 0 then 0 else if b > 255 then 255 else b in
111   sprintf "#%02x%02x%02x" r g b
112
113 and darken (r, g, b) =
114   normal (r * 4 / 10, g * 4 / 10, b * 4 / 10)
115
116 (* Mark up mode. *)
117 let markup_of_mode mode =
118   let str = file_permissions_string mode in
119   "<span color=\"#222222\" size=\"small\">" ^ str ^ "</span>"
120
121 (* Mark up dates. *)
122 let markup_of_date t =
123   (* Guestfs gives us int64's, we want float which is OCaml's
124    * equivalent of time_t.
125    *)
126   let t = Int64.to_float t in
127
128   let show_full_date () =
129     let cal = CL.Calendar.from_unixfloat t in
130     let cal = CL.Calendar.convert cal CL.Time_Zone.UTC CL.Time_Zone.Local in
131     CL.Printer.Calendar.sprint
132       "<span color=\"#222222\" size=\"small\">%F %T</span>" cal
133   in
134
135   (* How long ago? *)
136   let now = time () in
137   let ago = now -. t in
138   if ago < 0. then (* future *)
139     show_full_date ()
140   else if ago < 60. then
141     "<small>now</small>"
142   else if ago < 60. *. 60. then
143     sprintf "<small>%.0f minutes ago</small>" (ago /. 60.)
144   else if ago < 60. *. 60. *. 24. then
145     sprintf "<small>%.0f hours ago</small>" (ago /. 60. /. 60.)
146   else if ago < 60. *. 60. *. 24. *. 28. then
147     sprintf "<small>%.0f days ago</small>" (ago /. 60. /. 60. /. 24.)
148   else
149     show_full_date ()
150
151 (* Mark up file sizes. *)
152 let markup_of_size bytes =
153   sprintf "<small>%s</small>" (human_size bytes)
154
155 (* Mark up registry value types. *)
156 let markup_of_regvaluetype h value =
157   let t, _ = Hivex.value_value h value in
158
159   match t with
160   | Hivex.REG_NONE -> "none(0)"
161   | Hivex.REG_SZ -> "str(1)"
162   | Hivex.REG_EXPAND_SZ -> "str(2)"
163   | Hivex.REG_BINARY -> "hex(3)"
164   | Hivex.REG_DWORD -> "dword(4)"
165   | Hivex.REG_DWORD_BIG_ENDIAN -> "dword(5)"
166   | Hivex.REG_LINK -> "link(6)"
167   | Hivex.REG_MULTI_SZ -> "multi string (7)"
168   | Hivex.REG_RESOURCE_LIST -> "resource list (8)"
169   | Hivex.REG_FULL_RESOURCE_DESCRIPTOR -> "full resource descriptor (9)"
170   | Hivex.REG_RESOURCE_REQUIREMENTS_LIST -> "resource requirements list (10)"
171   | Hivex.REG_QWORD -> "qword (11)"
172   | Hivex.REG_UNKNOWN i32 -> sprintf "type 0x%08lx" i32
173
174 (* Mark up registry value sizes. *)
175 let markup_of_regvaluesize h value =
176   let _, len = Hivex.value_type h value in
177   sprintf "%d" len