configure: Add --with-extra flag to allow setting the extra version string.
[libguestfs.git] / df / output.c
1 /* virt-df
2  * Copyright (C) 2010-2011 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
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include <inttypes.h>
26 #include <xvasprintf.h>
27 #include <math.h>
28 #include <assert.h>
29
30 #ifdef HAVE_LIBVIRT
31 #include <libvirt/libvirt.h>
32 #include <libvirt/virterror.h>
33 #endif
34
35 #include "c-ctype.h"
36 #include "human.h"
37 #include "progname.h"
38
39 #include "guestfs.h"
40 #include "options.h"
41 #include "virt-df.h"
42
43 static void write_csv_field (const char *field);
44
45 void
46 print_title (void)
47 {
48   const char *cols[6];
49
50   cols[0] = _("VirtualMachine");
51   cols[1] = _("Filesystem");
52   if (!inodes) {
53     if (!human)
54       cols[2] = _("1K-blocks");
55     else
56       cols[2] = _("Size");
57     cols[3] = _("Used");
58     cols[4] = _("Available");
59     cols[5] = _("Use%");
60   } else {
61     cols[2] = _("Inodes");
62     cols[3] = _("IUsed");
63     cols[4] = _("IFree");
64     cols[5] = _("IUse%");
65   }
66
67   if (!csv) {
68     /* ignore cols[0] in this mode */
69     printf ("%-36s%10s %10s %10s %5s\n",
70             cols[1], cols[2], cols[3], cols[4], cols[5]);
71   }
72   else {
73     size_t i;
74
75     for (i = 0; i < 6; ++i) {
76       if (i > 0)
77         putchar (',');
78       write_csv_field (cols[i]);
79     }
80     putchar ('\n');
81   }
82 }
83
84 static void canonical_device (char *dev, int offset);
85
86 void
87 print_stat (const char *name, const char *uuid_param,
88             const char *dev_param, int offset,
89             const struct guestfs_statvfs *stat)
90 {
91   /* First two columns are always 'name' and 'dev', followed by four
92    * other data columns.  In text mode the 'name' and 'dev' are
93    * combined into a single 'name:dev' column.  In CSV mode they are
94    * kept as two separate columns.  In UUID mode the name might be
95    * replaced by 'uuid', if available.
96    */
97 #define MAX_LEN (LONGEST_HUMAN_READABLE > 128 ? LONGEST_HUMAN_READABLE : 128)
98   char buf[4][MAX_LEN];
99   const char *cols[4];
100   int64_t factor, v;
101   float percent;
102   int hopts = human_round_to_nearest|human_autoscale|human_base_1024|human_SI;
103   size_t i, len;
104
105   /* Make the device canonical. */
106   len = strlen (dev_param) + 1;
107   char dev[len];
108   strcpy (dev, dev_param);
109   if (offset >= 0)
110     canonical_device (dev, offset);
111
112   if (!inodes) {                /* 1K blocks */
113     if (!human) {
114       factor = stat->bsize / 1024;
115
116       v = stat->blocks * factor;
117       snprintf (buf[0], MAX_LEN, "%" PRIi64, v);
118       cols[0] = buf[0];
119       v = (stat->blocks - stat->bfree) * factor;
120       snprintf (buf[1], MAX_LEN, "%" PRIi64, v);
121       cols[1] = buf[1];
122       v = stat->bavail * factor;
123       snprintf (buf[2], MAX_LEN, "%" PRIi64, v);
124       cols[2] = buf[2];
125     } else {
126       cols[0] =
127         human_readable ((uintmax_t) stat->blocks, buf[0],
128                         hopts, stat->bsize, 1);
129       v = stat->blocks - stat->bfree;
130       cols[1] =
131         human_readable ((uintmax_t) v, buf[1], hopts, stat->bsize, 1);
132       cols[2] =
133         human_readable ((uintmax_t) stat->bavail, buf[2],
134                         hopts, stat->bsize, 1);
135     }
136
137     if (stat->blocks != 0)
138       percent = 100. - 100. * stat->bfree / stat->blocks;
139     else
140       percent = 0;
141   }
142   else {                        /* inodes */
143     snprintf (buf[0], MAX_LEN, "%" PRIi64, stat->files);
144     cols[0] = buf[0];
145     snprintf (buf[1], MAX_LEN, "%" PRIi64, stat->files - stat->ffree);
146     cols[1] = buf[1];
147     snprintf (buf[2], MAX_LEN, "%" PRIi64, stat->ffree);
148     cols[2] = buf[2];
149
150     if (stat->files != 0)
151       percent = 100. - 100. * stat->ffree / stat->files;
152     else
153       percent = 0;
154   }
155
156   if (!csv)
157     /* Use 'ceil' on the percentage in order to emulate what df itself does. */
158     snprintf (buf[3], MAX_LEN, "%3.0f%%", ceil (percent));
159   else
160     snprintf (buf[3], MAX_LEN, "%.1f", percent);
161   cols[3] = buf[3];
162
163 #undef MAX_LEN
164
165   if (uuid && uuid_param)
166     name = uuid_param;
167
168   if (!csv) {
169     len = strlen (name) + strlen (dev) + 1;
170     printf ("%s:%s", name, dev);
171     if (len <= 36) {
172       for (i = len; i < 36; ++i)
173         putchar (' ');
174     } else {
175       printf ("\n                                    ");
176     }
177
178     printf ("%10s %10s %10s %5s\n", cols[0], cols[1], cols[2], cols[3]);
179   }
180   else {
181     write_csv_field (name);
182     putchar (',');
183     write_csv_field (dev);
184
185     for (i = 0; i < 4; ++i) {
186       putchar (',');
187       write_csv_field (cols[i]);
188     }
189
190     putchar ('\n');
191   }
192 }
193
194 /* /dev/vda1 -> /dev/sda, adjusting the device offset. */
195 static void
196 canonical_device (char *dev, int offset)
197 {
198   if (STRPREFIX (dev, "/dev/") &&
199       (dev[5] == 'h' || dev[5] == 'v') &&
200       dev[6] == 'd' &&
201       c_isalpha (dev[7]) &&
202       (c_isdigit (dev[8]) || dev[8] == '\0')) {
203     dev[5] = 's';
204     dev[7] -= offset;
205   }
206 }
207
208 /* Function to quote CSV fields on output without requiring an
209  * external module.
210  */
211 static void
212 write_csv_field (const char *field)
213 {
214   size_t i, len;
215   int needs_quoting = 0;
216
217   len = strlen (field);
218
219   for (i = 0; i < len; ++i) {
220     if (field[i] == ' ' || field[i] == '"' ||
221         field[i] == '\n' || field[i] == ',') {
222       needs_quoting = 1;
223       break;
224     }
225   }
226
227   if (!needs_quoting) {
228     printf ("%s", field);
229     return;
230   }
231
232   /* Quoting for CSV fields. */
233   putchar ('"');
234   for (i = 0; i < len; ++i) {
235     if (field[i] == '"') {
236       putchar ('"');
237       putchar ('"');
238     } else
239       putchar (field[i]);
240   }
241   putchar ('"');
242 }