1/*-
2 * SPDX-License-Identifier: Beerware
3 *
4 * ----------------------------------------------------------------------------
5 * "THE BEER-WARE LICENSE" (Revision 42):
6 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
7 * can do whatever you want with this stuff. If we meet some day, and you think
8 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
9 * ----------------------------------------------------------------------------
10 *
11 */
12
13#include <sys/capsicum.h>
14#include <sys/ioctl.h>
15#include <sys/queue.h>
16#include <sys/ttycom.h>
17
18#include <assert.h>
19#include <capsicum_helpers.h>
20#include <ctype.h>
21#include <err.h>
22#include <errno.h>
23#include <math.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28
29#define NSTUDENT 100
30#define NCONF 6
31static double const studentpct[] = { 80, 90, 95, 98, 99, 99.5 };
32static double const student[NSTUDENT + 1][NCONF] = {
33/* inf */	{	1.282,	1.645,	1.960,	2.326,	2.576,	3.090  },
34/* 1. */	{	3.078,	6.314,	12.706,	31.821,	63.657,	318.313  },
35/* 2. */	{	1.886,	2.920,	4.303,	6.965,	9.925,	22.327  },
36/* 3. */	{	1.638,	2.353,	3.182,	4.541,	5.841,	10.215  },
37/* 4. */	{	1.533,	2.132,	2.776,	3.747,	4.604,	7.173  },
38/* 5. */	{	1.476,	2.015,	2.571,	3.365,	4.032,	5.893  },
39/* 6. */	{	1.440,	1.943,	2.447,	3.143,	3.707,	5.208  },
40/* 7. */	{	1.415,	1.895,	2.365,	2.998,	3.499,	4.782  },
41/* 8. */	{	1.397,	1.860,	2.306,	2.896,	3.355,	4.499  },
42/* 9. */	{	1.383,	1.833,	2.262,	2.821,	3.250,	4.296  },
43/* 10. */	{	1.372,	1.812,	2.228,	2.764,	3.169,	4.143  },
44/* 11. */	{	1.363,	1.796,	2.201,	2.718,	3.106,	4.024  },
45/* 12. */	{	1.356,	1.782,	2.179,	2.681,	3.055,	3.929  },
46/* 13. */	{	1.350,	1.771,	2.160,	2.650,	3.012,	3.852  },
47/* 14. */	{	1.345,	1.761,	2.145,	2.624,	2.977,	3.787  },
48/* 15. */	{	1.341,	1.753,	2.131,	2.602,	2.947,	3.733  },
49/* 16. */	{	1.337,	1.746,	2.120,	2.583,	2.921,	3.686  },
50/* 17. */	{	1.333,	1.740,	2.110,	2.567,	2.898,	3.646  },
51/* 18. */	{	1.330,	1.734,	2.101,	2.552,	2.878,	3.610  },
52/* 19. */	{	1.328,	1.729,	2.093,	2.539,	2.861,	3.579  },
53/* 20. */	{	1.325,	1.725,	2.086,	2.528,	2.845,	3.552  },
54/* 21. */	{	1.323,	1.721,	2.080,	2.518,	2.831,	3.527  },
55/* 22. */	{	1.321,	1.717,	2.074,	2.508,	2.819,	3.505  },
56/* 23. */	{	1.319,	1.714,	2.069,	2.500,	2.807,	3.485  },
57/* 24. */	{	1.318,	1.711,	2.064,	2.492,	2.797,	3.467  },
58/* 25. */	{	1.316,	1.708,	2.060,	2.485,	2.787,	3.450  },
59/* 26. */	{	1.315,	1.706,	2.056,	2.479,	2.779,	3.435  },
60/* 27. */	{	1.314,	1.703,	2.052,	2.473,	2.771,	3.421  },
61/* 28. */	{	1.313,	1.701,	2.048,	2.467,	2.763,	3.408  },
62/* 29. */	{	1.311,	1.699,	2.045,	2.462,	2.756,	3.396  },
63/* 30. */	{	1.310,	1.697,	2.042,	2.457,	2.750,	3.385  },
64/* 31. */	{	1.309,	1.696,	2.040,	2.453,	2.744,	3.375  },
65/* 32. */	{	1.309,	1.694,	2.037,	2.449,	2.738,	3.365  },
66/* 33. */	{	1.308,	1.692,	2.035,	2.445,	2.733,	3.356  },
67/* 34. */	{	1.307,	1.691,	2.032,	2.441,	2.728,	3.348  },
68/* 35. */	{	1.306,	1.690,	2.030,	2.438,	2.724,	3.340  },
69/* 36. */	{	1.306,	1.688,	2.028,	2.434,	2.719,	3.333  },
70/* 37. */	{	1.305,	1.687,	2.026,	2.431,	2.715,	3.326  },
71/* 38. */	{	1.304,	1.686,	2.024,	2.429,	2.712,	3.319  },
72/* 39. */	{	1.304,	1.685,	2.023,	2.426,	2.708,	3.313  },
73/* 40. */	{	1.303,	1.684,	2.021,	2.423,	2.704,	3.307  },
74/* 41. */	{	1.303,	1.683,	2.020,	2.421,	2.701,	3.301  },
75/* 42. */	{	1.302,	1.682,	2.018,	2.418,	2.698,	3.296  },
76/* 43. */	{	1.302,	1.681,	2.017,	2.416,	2.695,	3.291  },
77/* 44. */	{	1.301,	1.680,	2.015,	2.414,	2.692,	3.286  },
78/* 45. */	{	1.301,	1.679,	2.014,	2.412,	2.690,	3.281  },
79/* 46. */	{	1.300,	1.679,	2.013,	2.410,	2.687,	3.277  },
80/* 47. */	{	1.300,	1.678,	2.012,	2.408,	2.685,	3.273  },
81/* 48. */	{	1.299,	1.677,	2.011,	2.407,	2.682,	3.269  },
82/* 49. */	{	1.299,	1.677,	2.010,	2.405,	2.680,	3.265  },
83/* 50. */	{	1.299,	1.676,	2.009,	2.403,	2.678,	3.261  },
84/* 51. */	{	1.298,	1.675,	2.008,	2.402,	2.676,	3.258  },
85/* 52. */	{	1.298,	1.675,	2.007,	2.400,	2.674,	3.255  },
86/* 53. */	{	1.298,	1.674,	2.006,	2.399,	2.672,	3.251  },
87/* 54. */	{	1.297,	1.674,	2.005,	2.397,	2.670,	3.248  },
88/* 55. */	{	1.297,	1.673,	2.004,	2.396,	2.668,	3.245  },
89/* 56. */	{	1.297,	1.673,	2.003,	2.395,	2.667,	3.242  },
90/* 57. */	{	1.297,	1.672,	2.002,	2.394,	2.665,	3.239  },
91/* 58. */	{	1.296,	1.672,	2.002,	2.392,	2.663,	3.237  },
92/* 59. */	{	1.296,	1.671,	2.001,	2.391,	2.662,	3.234  },
93/* 60. */	{	1.296,	1.671,	2.000,	2.390,	2.660,	3.232  },
94/* 61. */	{	1.296,	1.670,	2.000,	2.389,	2.659,	3.229  },
95/* 62. */	{	1.295,	1.670,	1.999,	2.388,	2.657,	3.227  },
96/* 63. */	{	1.295,	1.669,	1.998,	2.387,	2.656,	3.225  },
97/* 64. */	{	1.295,	1.669,	1.998,	2.386,	2.655,	3.223  },
98/* 65. */	{	1.295,	1.669,	1.997,	2.385,	2.654,	3.220  },
99/* 66. */	{	1.295,	1.668,	1.997,	2.384,	2.652,	3.218  },
100/* 67. */	{	1.294,	1.668,	1.996,	2.383,	2.651,	3.216  },
101/* 68. */	{	1.294,	1.668,	1.995,	2.382,	2.650,	3.214  },
102/* 69. */	{	1.294,	1.667,	1.995,	2.382,	2.649,	3.213  },
103/* 70. */	{	1.294,	1.667,	1.994,	2.381,	2.648,	3.211  },
104/* 71. */	{	1.294,	1.667,	1.994,	2.380,	2.647,	3.209  },
105/* 72. */	{	1.293,	1.666,	1.993,	2.379,	2.646,	3.207  },
106/* 73. */	{	1.293,	1.666,	1.993,	2.379,	2.645,	3.206  },
107/* 74. */	{	1.293,	1.666,	1.993,	2.378,	2.644,	3.204  },
108/* 75. */	{	1.293,	1.665,	1.992,	2.377,	2.643,	3.202  },
109/* 76. */	{	1.293,	1.665,	1.992,	2.376,	2.642,	3.201  },
110/* 77. */	{	1.293,	1.665,	1.991,	2.376,	2.641,	3.199  },
111/* 78. */	{	1.292,	1.665,	1.991,	2.375,	2.640,	3.198  },
112/* 79. */	{	1.292,	1.664,	1.990,	2.374,	2.640,	3.197  },
113/* 80. */	{	1.292,	1.664,	1.990,	2.374,	2.639,	3.195  },
114/* 81. */	{	1.292,	1.664,	1.990,	2.373,	2.638,	3.194  },
115/* 82. */	{	1.292,	1.664,	1.989,	2.373,	2.637,	3.193  },
116/* 83. */	{	1.292,	1.663,	1.989,	2.372,	2.636,	3.191  },
117/* 84. */	{	1.292,	1.663,	1.989,	2.372,	2.636,	3.190  },
118/* 85. */	{	1.292,	1.663,	1.988,	2.371,	2.635,	3.189  },
119/* 86. */	{	1.291,	1.663,	1.988,	2.370,	2.634,	3.188  },
120/* 87. */	{	1.291,	1.663,	1.988,	2.370,	2.634,	3.187  },
121/* 88. */	{	1.291,	1.662,	1.987,	2.369,	2.633,	3.185  },
122/* 89. */	{	1.291,	1.662,	1.987,	2.369,	2.632,	3.184  },
123/* 90. */	{	1.291,	1.662,	1.987,	2.368,	2.632,	3.183  },
124/* 91. */	{	1.291,	1.662,	1.986,	2.368,	2.631,	3.182  },
125/* 92. */	{	1.291,	1.662,	1.986,	2.368,	2.630,	3.181  },
126/* 93. */	{	1.291,	1.661,	1.986,	2.367,	2.630,	3.180  },
127/* 94. */	{	1.291,	1.661,	1.986,	2.367,	2.629,	3.179  },
128/* 95. */	{	1.291,	1.661,	1.985,	2.366,	2.629,	3.178  },
129/* 96. */	{	1.290,	1.661,	1.985,	2.366,	2.628,	3.177  },
130/* 97. */	{	1.290,	1.661,	1.985,	2.365,	2.627,	3.176  },
131/* 98. */	{	1.290,	1.661,	1.984,	2.365,	2.627,	3.175  },
132/* 99. */	{	1.290,	1.660,	1.984,	2.365,	2.626,	3.175  },
133/* 100. */	{	1.290,	1.660,	1.984,	2.364,	2.626,	3.174  }
134};
135
136#define	MAX_DS	8
137static char symbol[MAX_DS] = { ' ', 'x', '+', '*', '%', '#', '@', 'O' };
138
139struct dataset {
140	char *name;
141	double	*points;
142	size_t lpoints;
143	double sy, syy;
144	size_t n;
145};
146
147static struct dataset *
148NewSet(void)
149{
150	struct dataset *ds;
151
152	ds = calloc(1, sizeof *ds);
153	assert(ds != NULL);
154	ds->lpoints = 100000;
155	ds->points = calloc(sizeof *ds->points, ds->lpoints);
156	assert(ds->points != NULL);
157	ds->syy = NAN;
158	return(ds);
159}
160
161static void
162AddPoint(struct dataset *ds, double a)
163{
164	double *dp;
165
166	if (ds->n >= ds->lpoints) {
167		dp = ds->points;
168		ds->lpoints *= 4;
169		ds->points = calloc(sizeof *ds->points, ds->lpoints);
170		assert(ds->points != NULL);
171		memcpy(ds->points, dp, sizeof *dp * ds->n);
172		free(dp);
173	}
174	ds->points[ds->n++] = a;
175	ds->sy += a;
176}
177
178static double
179Min(const struct dataset *ds)
180{
181
182	return (ds->points[0]);
183}
184
185static double
186Max(const struct dataset *ds)
187{
188
189	return (ds->points[ds->n -1]);
190}
191
192static double
193Avg(const struct dataset *ds)
194{
195
196	return(ds->sy / ds->n);
197}
198
199static double
200Median(const struct dataset *ds)
201{
202	const size_t m = ds->n / 2;
203
204	if ((ds->n % 2) == 0)
205		return ((ds->points[m] + (ds->points[m - 1])) / 2);
206	return (ds->points[m]);
207}
208
209static double
210Var(struct dataset *ds)
211{
212	size_t z;
213	const double a = Avg(ds);
214
215	if (isnan(ds->syy)) {
216		ds->syy = 0.0;
217		for (z = 0; z < ds->n; z++)
218			ds->syy += (ds->points[z] - a) * (ds->points[z] - a);
219	}
220
221	return (ds->syy / (ds->n - 1.0));
222}
223
224static double
225Stddev(struct dataset *ds)
226{
227
228	return sqrt(Var(ds));
229}
230
231static void
232VitalsHead(void)
233{
234
235	printf("    N           Min           Max        Median           Avg        Stddev\n");
236}
237
238static void
239Vitals(struct dataset *ds, int flag)
240{
241
242	printf("%c %3zu %13.8g %13.8g %13.8g %13.8g %13.8g", symbol[flag],
243	    ds->n, Min(ds), Max(ds), Median(ds), Avg(ds), Stddev(ds));
244	printf("\n");
245}
246
247static void
248Relative(struct dataset *ds, struct dataset *rs, int confidx)
249{
250	double spool, s, d, e, t;
251	double re;
252	size_t z;
253
254	z = ds->n + rs->n - 2;
255	if (z > NSTUDENT)
256		t = student[0][confidx];
257	else
258		t = student[z][confidx];
259	spool = (ds->n - 1) * Var(ds) + (rs->n - 1) * Var(rs);
260	spool /= ds->n + rs->n - 2;
261	spool = sqrt(spool);
262	s = spool * sqrt(1.0 / ds->n + 1.0 / rs->n);
263	d = Avg(ds) - Avg(rs);
264	e = t * s;
265
266	re = (ds->n - 1) * Var(ds) + (rs->n - 1) * Var(rs) *
267	    (Avg(ds) * Avg(ds)) / (Avg(rs) * Avg(rs));
268	re *= (ds->n + rs->n) / (ds->n * rs->n * (ds->n + rs->n - 2.0));
269	re = t * sqrt(re);
270
271	if (fabs(d) > e) {
272		printf("Difference at %.1f%% confidence\n", studentpct[confidx]);
273		printf("	%g +/- %g\n", d, e);
274		printf("	%g%% +/- %g%%\n", d * 100 / Avg(rs), re * 100 / Avg(rs));
275		printf("	(Student's t, pooled s = %g)\n", spool);
276	} else {
277		printf("No difference proven at %.1f%% confidence\n",
278		    studentpct[confidx]);
279	}
280}
281
282struct plot {
283	double		min;
284	double		max;
285	double		span;
286	int		width;
287
288	double		x0, dx;
289	size_t		height;
290	char		*data;
291	char		**bar;
292	int		separate_bars;
293	int		num_datasets;
294};
295
296static struct plot plot;
297
298static void
299SetupPlot(int width, int separate, int num_datasets)
300{
301	struct plot *pl;
302
303	pl = &plot;
304	pl->width = width;
305	pl->height = 0;
306	pl->data = NULL;
307	pl->bar = NULL;
308	pl->separate_bars = separate;
309	pl->num_datasets = num_datasets;
310	pl->min = 999e99;
311	pl->max = -999e99;
312}
313
314static void
315AdjPlot(double a)
316{
317	struct plot *pl;
318
319	pl = &plot;
320	if (a < pl->min)
321		pl->min = a;
322	if (a > pl->max)
323		pl->max = a;
324	pl->span = pl->max - pl->min;
325	pl->dx = pl->span / (pl->width - 1.0);
326	pl->x0 = pl->min - .5 * pl->dx;
327}
328
329static void
330DimPlot(struct dataset *ds)
331{
332	AdjPlot(Min(ds));
333	AdjPlot(Max(ds));
334	AdjPlot(Avg(ds) - Stddev(ds));
335	AdjPlot(Avg(ds) + Stddev(ds));
336}
337
338static void
339PlotSet(struct dataset *ds, int val)
340{
341	struct plot *pl;
342	int i, x;
343	size_t m, j, z;
344	size_t n;
345	int bar;
346	double av, sd;
347
348	pl = &plot;
349	if (pl->span == 0)
350		return;
351
352	if (pl->separate_bars)
353		bar = val-1;
354	else
355		bar = 0;
356
357	if (pl->bar == NULL) {
358		pl->bar = calloc(sizeof(char *), pl->num_datasets);
359		assert(pl->bar != NULL);
360	}
361
362	if (pl->bar[bar] == NULL) {
363		pl->bar[bar] = malloc(pl->width);
364		assert(pl->bar[bar] != NULL);
365		memset(pl->bar[bar], 0, pl->width);
366	}
367
368	m = 1;
369	i = -1;
370	j = 0;
371	/* Set m to max(j) + 1, to allocate required memory */
372	for (n = 0; n < ds->n; n++) {
373		x = (ds->points[n] - pl->x0) / pl->dx;
374		if (x == i) {
375			j++;
376			if (j > m)
377				m = j;
378		} else {
379			j = 1;
380			i = x;
381		}
382	}
383	m += 1;
384	if (m > pl->height) {
385		pl->data = realloc(pl->data, pl->width * m);
386		assert(pl->data != NULL);
387		memset(pl->data + pl->height * pl->width, 0,
388		    (m - pl->height) * pl->width);
389	}
390	pl->height = m;
391	i = -1;
392	for (n = 0; n < ds->n; n++) {
393		x = (ds->points[n] - pl->x0) / pl->dx;
394		if (x == i) {
395			j++;
396		} else {
397			j = 1;
398			i = x;
399		}
400		pl->data[j * pl->width + x] |= val;
401	}
402	av = Avg(ds);
403	sd = Stddev(ds);
404	if (!isnan(sd)) {
405		x = ((av - sd) - pl->x0) / pl->dx;
406		m = ((av + sd) - pl->x0) / pl->dx;
407		pl->bar[bar][m] = '|';
408		pl->bar[bar][x] = '|';
409		for (z = x + 1; z < m; z++)
410			if (pl->bar[bar][z] == 0)
411				pl->bar[bar][z] = '_';
412	}
413	x = (Median(ds) - pl->x0) / pl->dx;
414	pl->bar[bar][x] = 'M';
415	x = (av - pl->x0) / pl->dx;
416	pl->bar[bar][x] = 'A';
417}
418
419static void
420DumpPlot(void)
421{
422	struct plot *pl;
423	int i, j, k;
424	size_t z;
425
426	pl = &plot;
427	if (pl->span == 0) {
428		printf("[no plot, span is zero width]\n");
429		return;
430	}
431
432	putchar('+');
433	for (i = 0; i < pl->width; i++)
434		putchar('-');
435	putchar('+');
436	putchar('\n');
437	for (z = 1; z < pl->height; z++) {
438		putchar('|');
439		for (j = 0; j < pl->width; j++) {
440			k = pl->data[(pl->height - z) * pl->width + j];
441			if (k >= 0 && k < MAX_DS)
442				putchar(symbol[k]);
443			else
444				printf("[%02x]", k);
445		}
446		putchar('|');
447		putchar('\n');
448	}
449	for (i = 0; i < pl->num_datasets; i++) {
450		if (pl->bar[i] == NULL)
451			continue;
452		putchar('|');
453		for (j = 0; j < pl->width; j++) {
454			k = pl->bar[i][j];
455			if (k == 0)
456				k = ' ';
457			putchar(k);
458		}
459		putchar('|');
460		putchar('\n');
461	}
462	putchar('+');
463	for (i = 0; i < pl->width; i++)
464		putchar('-');
465	putchar('+');
466	putchar('\n');
467}
468
469static int
470dbl_cmp(const void *a, const void *b)
471{
472	const double *aa = a;
473	const double *bb = b;
474
475	if (*aa < *bb)
476		return (-1);
477	else if (*aa > *bb)
478		return (1);
479	else
480		return (0);
481}
482
483static struct dataset *
484ReadSet(FILE *f, const char *n, int column, const char *delim)
485{
486	char buf[BUFSIZ], *p, *t;
487	struct dataset *s;
488	double d;
489	int line;
490	int i;
491
492	s = NewSet();
493	s->name = strdup(n);
494	assert(s->name != NULL);
495	line = 0;
496	while (fgets(buf, sizeof buf, f) != NULL) {
497		line++;
498
499		i = strlen(buf);
500		while (i > 0 && isspace(buf[i - 1]))
501			buf[--i] = '\0';
502		for (i = 1, t = strtok(buf, delim);
503		     t != NULL && *t != '#';
504		     i++, t = strtok(NULL, delim)) {
505			if (i == column)
506				break;
507		}
508		if (t == NULL || *t == '#')
509			continue;
510
511		d = strtod(t, &p);
512		if (p != NULL && *p != '\0')
513			errx(2, "Invalid data on line %d in %s", line, n);
514		if (*buf != '\0')
515			AddPoint(s, d);
516	}
517	if (s->n < 3) {
518		fprintf(stderr,
519		    "Dataset %s must contain at least 3 data points\n", n);
520		exit (2);
521	}
522	qsort(s->points, s->n, sizeof *s->points, dbl_cmp);
523	return (s);
524}
525
526static void
527usage(char const *whine)
528{
529	int i;
530
531	fprintf(stderr, "%s\n", whine);
532	fprintf(stderr,
533	    "Usage: ministat [-C column] [-c confidence] [-d delimiter(s)] [-Anqs] [-w width] [file [file ...]]\n");
534	fprintf(stderr, "\tconfidence = {");
535	for (i = 0; i < NCONF; i++) {
536		fprintf(stderr, "%s%g%%",
537		    i ? ", " : "",
538		    studentpct[i]);
539	}
540	fprintf(stderr, "}\n");
541	fprintf(stderr, "\t-A : print statistics only. suppress the graph.\n");
542	fprintf(stderr, "\t-C : column number to extract (starts and defaults to 1)\n");
543	fprintf(stderr, "\t-d : delimiter(s) string, default to \" \\t\"\n");
544	fprintf(stderr, "\t-n : print summary statistics only, no graph/test\n");
545	fprintf(stderr, "\t-q : suppress printing summary-statistics headers and data-set names\n");
546	fprintf(stderr, "\t-s : print avg/median/stddev bars on separate lines\n");
547	fprintf(stderr, "\t-w : width of graph/test output (default 74 or terminal width)\n");
548	exit (2);
549}
550
551int
552main(int argc, char **argv)
553{
554	const char *setfilenames[MAX_DS - 1];
555	struct dataset *ds[MAX_DS - 1];
556	FILE *setfiles[MAX_DS - 1];
557	int nds;
558	double a;
559	const char *delim = " \t";
560	char *p;
561	int c, i, ci;
562	int column = 1;
563	int flag_s = 0;
564	int flag_n = 0;
565	int flag_q = 0;
566	int termwidth = 74;
567	int suppress_plot = 0;
568
569	if (isatty(STDOUT_FILENO)) {
570		struct winsize wsz;
571
572		if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
573			termwidth = atoi(p);
574		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsz) != -1 &&
575			 wsz.ws_col > 0)
576			termwidth = wsz.ws_col - 2;
577	}
578
579	ci = -1;
580	while ((c = getopt(argc, argv, "AC:c:d:snqw:")) != -1)
581		switch (c) {
582		case 'A':
583			suppress_plot = 1;
584			break;
585		case 'C':
586			column = strtol(optarg, &p, 10);
587			if (p != NULL && *p != '\0')
588				usage("Invalid column number.");
589			if (column <= 0)
590				usage("Column number should be positive.");
591			break;
592		case 'c':
593			a = strtod(optarg, &p);
594			if (p != NULL && *p != '\0')
595				usage("Not a floating point number");
596			for (i = 0; i < NCONF; i++)
597				if (a == studentpct[i])
598					ci = i;
599			if (ci == -1)
600				usage("No support for confidence level");
601			break;
602		case 'd':
603			if (*optarg == '\0')
604				usage("Can't use empty delimiter string");
605			delim = optarg;
606			break;
607		case 'n':
608			flag_n = 1;
609			break;
610		case 'q':
611			flag_q = 1;
612			break;
613		case 's':
614			flag_s = 1;
615			break;
616		case 'w':
617			termwidth = strtol(optarg, &p, 10);
618			if (p != NULL && *p != '\0')
619				usage("Invalid width, not a number.");
620			if (termwidth < 0)
621				usage("Unable to move beyond left margin.");
622			break;
623		default:
624			usage("Unknown option");
625			break;
626		}
627	if (ci == -1)
628		ci = 2;
629	argc -= optind;
630	argv += optind;
631
632	if (argc == 0) {
633		setfilenames[0] = "<stdin>";
634		setfiles[0] = stdin;
635		nds = 1;
636	} else {
637		if (argc > (MAX_DS - 1))
638			usage("Too many datasets.");
639		nds = argc;
640		for (i = 0; i < nds; i++) {
641			setfilenames[i] = argv[i];
642			if (!strcmp(argv[i], "-"))
643				setfiles[0] = stdin;
644			else
645				setfiles[i] = fopen(argv[i], "r");
646			if (setfiles[i] == NULL)
647				err(2, "Cannot open %s", argv[i]);
648		}
649	}
650
651	if (caph_limit_stdio() < 0)
652		err(2, "capsicum");
653
654	for (i = 0; i < nds; i++)
655		if (caph_limit_stream(fileno(setfiles[i]), CAPH_READ) < 0)
656			err(2, "unable to limit rights for %s",
657			    setfilenames[i]);
658
659	/* Enter Capsicum sandbox. */
660	if (caph_enter() < 0)
661		err(2, "unable to enter capability mode");
662
663	for (i = 0; i < nds; i++) {
664		ds[i] = ReadSet(setfiles[i], setfilenames[i], column, delim);
665		if (setfiles[i] != stdin)
666			fclose(setfiles[i]);
667	}
668
669	if (!flag_q) {
670		for (i = 0; i < nds; i++)
671			printf("%c %s\n", symbol[i+1], ds[i]->name);
672	}
673
674	if (!flag_n && !suppress_plot) {
675		SetupPlot(termwidth, flag_s, nds);
676		for (i = 0; i < nds; i++)
677			DimPlot(ds[i]);
678		for (i = 0; i < nds; i++)
679			PlotSet(ds[i], i + 1);
680		DumpPlot();
681	}
682	if (!flag_q)
683		VitalsHead();
684	Vitals(ds[0], 1);
685	for (i = 1; i < nds; i++) {
686		Vitals(ds[i], i + 1);
687		if (!flag_n)
688			Relative(ds[i], ds[0], ci);
689	}
690	exit(0);
691}
692