Prepare for first binary release.
[guestfs-browser.git] / filetree_ops.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 Printf
20
21 open Utils
22
23 open Filetree_type
24
25 (* Get the basename of a file, using path conventions which are valid
26  * for libguestfs.  So [Filename.basename] won't necessarily work
27  * because it will use host path conventions.
28  *)
29 let basename pathname =
30   let len = String.length pathname in
31   try
32     let i = String.rindex pathname '/' in
33     let r = String.sub pathname (i+1) (len-i-1) in
34     if r = "" then "root" else r
35   with
36     Not_found -> pathname
37
38 (* Download a single file. *)
39 let rec download_file ({ model = model } as t) path () =
40   let row = model#get_iter path in
41   let src, pathname = get_pathname t row in
42   debug "download_file %s: showing dialog" pathname;
43
44   (* Put up the dialog. *)
45   let title = "Download file" in
46   let dlg = GWindow.file_chooser_dialog ~action:`SAVE ~title ~modal:true () in
47   dlg#add_button_stock `CANCEL `CANCEL;
48   dlg#add_select_button_stock `SAVE `SAVE;
49   dlg#set_current_name (basename pathname);
50
51   match dlg#run () with
52   | `DELETE_EVENT | `CANCEL ->
53       dlg#destroy ()
54   | `SAVE ->
55       match dlg#filename with
56       | None -> ()
57       | Some localfile ->
58           dlg#destroy ();
59
60           (* Download the file. *)
61           Slave.download_file src pathname localfile Slave.no_callback
62
63 (* Download a directory as a tarball. *)
64 let rec download_dir_tarball ({ model = model } as t) format path () =
65   let row = model#get_iter path in
66   let src, pathname = get_pathname t row in
67   debug "download_dir_tarball %s: showing dialog" pathname;
68
69   (* Put up the dialog. *)
70   let title = "Download directory to tar file" in
71   let dlg = GWindow.file_chooser_dialog ~action:`SAVE ~title ~modal:true () in
72   dlg#add_button_stock `CANCEL `CANCEL;
73   dlg#add_select_button_stock `SAVE `SAVE;
74
75   let extension = match format with
76     | Slave.Tar -> ".tar"
77     | Slave.TGZ -> ".tar.gz"
78     | Slave.TXZ -> ".tar.xz"
79   in
80   dlg#set_current_name (basename pathname ^ extension);
81
82   match dlg#run () with
83   | `DELETE_EVENT | `CANCEL ->
84       dlg#destroy ()
85   | `SAVE ->
86       match dlg#filename with
87       | None -> ()
88       | Some localfile ->
89           dlg#destroy ();
90
91           (* Download the directory. *)
92           Slave.download_dir_tarball src pathname format localfile
93             Slave.no_callback
94
95 let rec download_dir_find0 ({ model = model } as t) path () =
96   let row = model#get_iter path in
97   let src, pathname = get_pathname t row in
98   debug "download_dir_find0 %s: showing dialog" pathname;
99
100   (* Put up the dialog. *)
101   let title = "Download list of filenames" in
102   let dlg = GWindow.file_chooser_dialog ~action:`SAVE ~title ~modal:true () in
103   dlg#add_button_stock `CANCEL `CANCEL;
104   dlg#add_select_button_stock `SAVE `SAVE;
105   dlg#set_current_name (basename pathname ^ ".filenames.txt");
106
107   (* Notify that the list of strings is \0 separated. *)
108   let hbox =
109     let hbox = GPack.hbox () in
110     ignore (GMisc.image ~stock:`INFO ~packing:hbox#pack ());
111     let label = GMisc.label
112       ~text:"The list of filenames is saved to a file with zero byte separators, to allow the full range of characters to be used in the names themselves."
113       ~packing:hbox#pack () in
114     label#set_line_wrap true;
115     hbox in
116   dlg#set_extra_widget (hbox :> GObj.widget);
117
118   match dlg#run () with
119   | `DELETE_EVENT | `CANCEL ->
120       dlg#destroy ()
121   | `SAVE ->
122       match dlg#filename with
123       | None -> ()
124       | Some localfile ->
125           dlg#destroy ();
126
127           (* Download the directory. *)
128           Slave.download_dir_find0 src pathname localfile Slave.no_callback
129
130 let has_child_node_equals t row hdata =
131   try ignore (find_child_node_by_hdata t row hdata); true
132   with Not_found -> false
133
134 (* Calculate disk space used by a directory. *)
135 let rec disk_usage ({ model = model } as t) path () =
136   t.view#expand_row path;
137
138   let row = model#get_iter path in
139   let src, pathname = get_pathname t row in
140   debug "disk_usage %s" pathname;
141
142   (* See if this node already has an Info "disk_usage" child node.  If
143    * so they don't recreate it.
144    *)
145   let hdata = IsLeaf, Info "disk_usage" in
146   if not (has_child_node_equals t row hdata) then (
147     (* Create the child node first. *)
148     let row = model#insert ~parent:row 0 in
149     store_hdata t row hdata;
150     model#set ~row ~column:t.name_col "<i>Calculating disk usage ...</i>";
151
152     Slave.disk_usage src pathname (when_disk_usage t path)
153   )
154
155 and when_disk_usage ({ model = model } as t) path kbytes =
156   let row = model#get_iter path in
157
158   (* Find the Info "disk_usage" child node add above, and replace the
159    * text in it with the final size.
160    *)
161   try
162     let hdata = IsLeaf, Info "disk_usage" in
163     let row = find_child_node_by_hdata t row hdata in
164     let msg = sprintf "<b>Disk usage: %Ld KB</b>" kbytes in
165     model#set ~row ~column:t.name_col msg
166   with
167     Not_found -> ()