New sources layout

Change-Id: Ic716f336b7071063997cf5b4dae6d50e0b4631e9
This commit is contained in:
2014-07-28 21:19:37 +02:00
parent 428aa25dc6
commit 433d6423c3
3138 changed files with 693 additions and 606 deletions

View File

@@ -36,14 +36,6 @@ SUBDIR= asa \
.if !defined(__MINIX)
SUBDIR+= ../external/zlib/pigz/bin/pigz
.else
# Non-NetBSD imports
SUBDIR+= ministat
# Minix commands
SUBDIR+= top
SUBDIR+= toproto
# LSC FIXME For now we compile those unconditionnaly
SUBDIR+= apropos whatis bdes soelim
.endif # defined(__MINIX)

View File

@@ -1,17 +0,0 @@
# $FreeBSD$
PROG= ministat
DPADD= ${LIBM}
LDADD= -lm
# BJG
WARNS=
.include <bsd.prog.mk>
test: ${PROG}
./${PROG} < ${.CURDIR}/chameleon
./${PROG} ${.CURDIR}/chameleon
./${PROG} ${.CURDIR}/iguana ${.CURDIR}/chameleon
./${PROG} -c 80 ${.CURDIR}/iguana ${.CURDIR}/chameleon
./${PROG} -s -c 80 ${.CURDIR}/chameleon ${.CURDIR}/iguana
./${PROG} -s -c 80 ${.CURDIR}/chameleon ${.CURDIR}/iguana ${.CURDIR}/iguana

View File

@@ -1,50 +0,0 @@
$FreeBSD$
A small tool to do the statistics legwork on benchmarks etc.
Prepare your data into two files, one number per line
run
./ministat data_before data_after
and see what it says.
You need at least three data points in each data set, but the more
you have the better your result generally gets.
Here are two typical outputs:
x _1
+ _2
+--------------------------------------------------------------------------+
|x + x+ x x x + ++ |
| |_________|______AM_______________|__A___________M_______________||
+--------------------------------------------------------------------------+
N Min Max Median Avg Stddev
x 5 36060 36138 36107 36105.6 31.165686
+ 5 36084 36187 36163 36142.6 49.952978
No difference proven at 95.0% confidence
Here nothing can be concluded from the numbers. It _may_ be possible to
prove something if many more measurements are made, but with only five
measurements, nothing is proven.
x _1
+ _2
+--------------------------------------------------------------------------+
| + |
| x + +|
|x x x x + +|
| |_______________A_____M_________| |_M___A____| |
+--------------------------------------------------------------------------+
N Min Max Median Avg Stddev
x 5 0.133 0.137 0.136 0.1354 0.0015165751
+ 5 0.139 0.14 0.139 0.1394 0.00054772256
Difference at 95.0% confidence
0.004 +/- 0.00166288
2.95421% +/- 1.22812%
(Student's t, pooled s = 0.00114018)
Here we have a clearcut difference, not very big, but clear and unambiguous.

View File

@@ -1,6 +0,0 @@
# $FreeBSD$
150
400
720
500
930

View File

@@ -1,8 +0,0 @@
# $FreeBSD$
50
200
150
400
750
400
150

View File

@@ -1,130 +0,0 @@
.\"
.\" Copyright (c) 2007 Poul-Henning Kamp
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd June 28, 2010
.Dt MINISTAT 1
.Os
.Sh NAME
.Nm ministat
.Nd statistics utility
.Sh SYNOPSIS
.Nm
.Op Fl ns
.Op Fl C Ar column
.Op Fl c Ar confidence_level
.Op Fl d Ar delimiter
.Op Fl w Op width
.Op Ar
.Sh DESCRIPTION
The
.Nm
command calculates fundamental statistical properties of numeric data
in the specified files or, if no file is specified, standard input.
.Pp
The options are as follows:
.Bl -tag -width Fl
.It Fl n
Just report the raw statistics of the input, suppress the ASCII-art plot
and the relative comparisons.
.It Fl s
Print the average/median/stddev bars on separate lines in the ASCII-art
plot, to avoid overlap.
.It Fl C Ar column
Specify which column of data to use.
By default the first column in the input file(s) are used.
.It Fl c Ar confidence_level
Specify desired confidence level for Student's T analysis.
Possible values are 80, 90, 95, 98, 99 and 99.5 %
.It Fl d Ar delimiter
Specifies the column delimiter characters, default is SPACE and TAB.
See
.Xr strtok 3
for details.
.It Fl w Ar width
Width of ASCII-art plot in characters, default is 74.
.El
.Pp
A sample output could look like this:
.Bd -literal -offset indent
$ ministat -s -w 60 iguana chameleon
x iguana
+ chameleon
+------------------------------------------------------------+
|x * x * + + x +|
| |________M______A_______________| |
| |________________M__A___________________| |
+------------------------------------------------------------+
N Min Max Median Avg Stddev
x 7 50 750 200 300 238.04761
+ 5 150 930 500 540 299.08193
No difference proven at 95.0% confidence
.Ed
.Pp
If
.Nm
tells you, as in the example above, that there is no difference
proven at 95% confidence, the two data sets you gave it are for
all statistical purposes identical.
.Pp
You have the option of lowering your standards by specifying a
lower confidence level:
.Bd -literal -offset indent
$ ministat -s -w 60 -c 80 iguana chameleon
x iguana
+ chameleon
+------------------------------------------------------------+
|x * x * + + x +|
| |________M______A_______________| |
| |________________M__A___________________| |
+------------------------------------------------------------+
N Min Max Median Avg Stddev
x 7 50 750 200 300 238.04761
+ 5 150 930 500 540 299.08193
Difference at 80.0% confidence
240 +/- 212.215
80% +/- 70.7384%
(Student's t, pooled s = 264.159)
.Ed
.Pp
But a lower standard does not make your data any better, and the
example is only included here to show the format of the output when
a statistical difference is proven according to Student's T method.
.Sh SEE ALSO
Any mathematics text on basic statistics, for instances Larry Gonicks
excellent "Cartoon Guide to Statistics" which supplied the above example.
.Sh HISTORY
The
.Nm
command was written by Poul-Henning Kamp out of frustration
over all the bogus benchmark claims made by people with no
understanding of the importance of uncertainty and statistics.
.Pp
From
.Fx 5.2
it has lived in the source tree as a developer tool, graduating
to the installed system from
.Fx 8.0 .

View File

@@ -1,631 +0,0 @@
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*
*/
#include <sys/cdefs.h>
#if 0
__FBSDID("$FreeBSD$");
#endif
#include <stdio.h>
#include <math.h>
#include <err.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/ttycom.h>
#define NSTUDENT 100
#define NCONF 6
double const studentpct[] = { 80, 90, 95, 98, 99, 99.5 };
double student [NSTUDENT + 1][NCONF] = {
/* inf */ { 1.282, 1.645, 1.960, 2.326, 2.576, 3.090 },
/* 1. */ { 3.078, 6.314, 12.706, 31.821, 63.657, 318.313 },
/* 2. */ { 1.886, 2.920, 4.303, 6.965, 9.925, 22.327 },
/* 3. */ { 1.638, 2.353, 3.182, 4.541, 5.841, 10.215 },
/* 4. */ { 1.533, 2.132, 2.776, 3.747, 4.604, 7.173 },
/* 5. */ { 1.476, 2.015, 2.571, 3.365, 4.032, 5.893 },
/* 6. */ { 1.440, 1.943, 2.447, 3.143, 3.707, 5.208 },
/* 7. */ { 1.415, 1.895, 2.365, 2.998, 3.499, 4.782 },
/* 8. */ { 1.397, 1.860, 2.306, 2.896, 3.355, 4.499 },
/* 9. */ { 1.383, 1.833, 2.262, 2.821, 3.250, 4.296 },
/* 10. */ { 1.372, 1.812, 2.228, 2.764, 3.169, 4.143 },
/* 11. */ { 1.363, 1.796, 2.201, 2.718, 3.106, 4.024 },
/* 12. */ { 1.356, 1.782, 2.179, 2.681, 3.055, 3.929 },
/* 13. */ { 1.350, 1.771, 2.160, 2.650, 3.012, 3.852 },
/* 14. */ { 1.345, 1.761, 2.145, 2.624, 2.977, 3.787 },
/* 15. */ { 1.341, 1.753, 2.131, 2.602, 2.947, 3.733 },
/* 16. */ { 1.337, 1.746, 2.120, 2.583, 2.921, 3.686 },
/* 17. */ { 1.333, 1.740, 2.110, 2.567, 2.898, 3.646 },
/* 18. */ { 1.330, 1.734, 2.101, 2.552, 2.878, 3.610 },
/* 19. */ { 1.328, 1.729, 2.093, 2.539, 2.861, 3.579 },
/* 20. */ { 1.325, 1.725, 2.086, 2.528, 2.845, 3.552 },
/* 21. */ { 1.323, 1.721, 2.080, 2.518, 2.831, 3.527 },
/* 22. */ { 1.321, 1.717, 2.074, 2.508, 2.819, 3.505 },
/* 23. */ { 1.319, 1.714, 2.069, 2.500, 2.807, 3.485 },
/* 24. */ { 1.318, 1.711, 2.064, 2.492, 2.797, 3.467 },
/* 25. */ { 1.316, 1.708, 2.060, 2.485, 2.787, 3.450 },
/* 26. */ { 1.315, 1.706, 2.056, 2.479, 2.779, 3.435 },
/* 27. */ { 1.314, 1.703, 2.052, 2.473, 2.771, 3.421 },
/* 28. */ { 1.313, 1.701, 2.048, 2.467, 2.763, 3.408 },
/* 29. */ { 1.311, 1.699, 2.045, 2.462, 2.756, 3.396 },
/* 30. */ { 1.310, 1.697, 2.042, 2.457, 2.750, 3.385 },
/* 31. */ { 1.309, 1.696, 2.040, 2.453, 2.744, 3.375 },
/* 32. */ { 1.309, 1.694, 2.037, 2.449, 2.738, 3.365 },
/* 33. */ { 1.308, 1.692, 2.035, 2.445, 2.733, 3.356 },
/* 34. */ { 1.307, 1.691, 2.032, 2.441, 2.728, 3.348 },
/* 35. */ { 1.306, 1.690, 2.030, 2.438, 2.724, 3.340 },
/* 36. */ { 1.306, 1.688, 2.028, 2.434, 2.719, 3.333 },
/* 37. */ { 1.305, 1.687, 2.026, 2.431, 2.715, 3.326 },
/* 38. */ { 1.304, 1.686, 2.024, 2.429, 2.712, 3.319 },
/* 39. */ { 1.304, 1.685, 2.023, 2.426, 2.708, 3.313 },
/* 40. */ { 1.303, 1.684, 2.021, 2.423, 2.704, 3.307 },
/* 41. */ { 1.303, 1.683, 2.020, 2.421, 2.701, 3.301 },
/* 42. */ { 1.302, 1.682, 2.018, 2.418, 2.698, 3.296 },
/* 43. */ { 1.302, 1.681, 2.017, 2.416, 2.695, 3.291 },
/* 44. */ { 1.301, 1.680, 2.015, 2.414, 2.692, 3.286 },
/* 45. */ { 1.301, 1.679, 2.014, 2.412, 2.690, 3.281 },
/* 46. */ { 1.300, 1.679, 2.013, 2.410, 2.687, 3.277 },
/* 47. */ { 1.300, 1.678, 2.012, 2.408, 2.685, 3.273 },
/* 48. */ { 1.299, 1.677, 2.011, 2.407, 2.682, 3.269 },
/* 49. */ { 1.299, 1.677, 2.010, 2.405, 2.680, 3.265 },
/* 50. */ { 1.299, 1.676, 2.009, 2.403, 2.678, 3.261 },
/* 51. */ { 1.298, 1.675, 2.008, 2.402, 2.676, 3.258 },
/* 52. */ { 1.298, 1.675, 2.007, 2.400, 2.674, 3.255 },
/* 53. */ { 1.298, 1.674, 2.006, 2.399, 2.672, 3.251 },
/* 54. */ { 1.297, 1.674, 2.005, 2.397, 2.670, 3.248 },
/* 55. */ { 1.297, 1.673, 2.004, 2.396, 2.668, 3.245 },
/* 56. */ { 1.297, 1.673, 2.003, 2.395, 2.667, 3.242 },
/* 57. */ { 1.297, 1.672, 2.002, 2.394, 2.665, 3.239 },
/* 58. */ { 1.296, 1.672, 2.002, 2.392, 2.663, 3.237 },
/* 59. */ { 1.296, 1.671, 2.001, 2.391, 2.662, 3.234 },
/* 60. */ { 1.296, 1.671, 2.000, 2.390, 2.660, 3.232 },
/* 61. */ { 1.296, 1.670, 2.000, 2.389, 2.659, 3.229 },
/* 62. */ { 1.295, 1.670, 1.999, 2.388, 2.657, 3.227 },
/* 63. */ { 1.295, 1.669, 1.998, 2.387, 2.656, 3.225 },
/* 64. */ { 1.295, 1.669, 1.998, 2.386, 2.655, 3.223 },
/* 65. */ { 1.295, 1.669, 1.997, 2.385, 2.654, 3.220 },
/* 66. */ { 1.295, 1.668, 1.997, 2.384, 2.652, 3.218 },
/* 67. */ { 1.294, 1.668, 1.996, 2.383, 2.651, 3.216 },
/* 68. */ { 1.294, 1.668, 1.995, 2.382, 2.650, 3.214 },
/* 69. */ { 1.294, 1.667, 1.995, 2.382, 2.649, 3.213 },
/* 70. */ { 1.294, 1.667, 1.994, 2.381, 2.648, 3.211 },
/* 71. */ { 1.294, 1.667, 1.994, 2.380, 2.647, 3.209 },
/* 72. */ { 1.293, 1.666, 1.993, 2.379, 2.646, 3.207 },
/* 73. */ { 1.293, 1.666, 1.993, 2.379, 2.645, 3.206 },
/* 74. */ { 1.293, 1.666, 1.993, 2.378, 2.644, 3.204 },
/* 75. */ { 1.293, 1.665, 1.992, 2.377, 2.643, 3.202 },
/* 76. */ { 1.293, 1.665, 1.992, 2.376, 2.642, 3.201 },
/* 77. */ { 1.293, 1.665, 1.991, 2.376, 2.641, 3.199 },
/* 78. */ { 1.292, 1.665, 1.991, 2.375, 2.640, 3.198 },
/* 79. */ { 1.292, 1.664, 1.990, 2.374, 2.640, 3.197 },
/* 80. */ { 1.292, 1.664, 1.990, 2.374, 2.639, 3.195 },
/* 81. */ { 1.292, 1.664, 1.990, 2.373, 2.638, 3.194 },
/* 82. */ { 1.292, 1.664, 1.989, 2.373, 2.637, 3.193 },
/* 83. */ { 1.292, 1.663, 1.989, 2.372, 2.636, 3.191 },
/* 84. */ { 1.292, 1.663, 1.989, 2.372, 2.636, 3.190 },
/* 85. */ { 1.292, 1.663, 1.988, 2.371, 2.635, 3.189 },
/* 86. */ { 1.291, 1.663, 1.988, 2.370, 2.634, 3.188 },
/* 87. */ { 1.291, 1.663, 1.988, 2.370, 2.634, 3.187 },
/* 88. */ { 1.291, 1.662, 1.987, 2.369, 2.633, 3.185 },
/* 89. */ { 1.291, 1.662, 1.987, 2.369, 2.632, 3.184 },
/* 90. */ { 1.291, 1.662, 1.987, 2.368, 2.632, 3.183 },
/* 91. */ { 1.291, 1.662, 1.986, 2.368, 2.631, 3.182 },
/* 92. */ { 1.291, 1.662, 1.986, 2.368, 2.630, 3.181 },
/* 93. */ { 1.291, 1.661, 1.986, 2.367, 2.630, 3.180 },
/* 94. */ { 1.291, 1.661, 1.986, 2.367, 2.629, 3.179 },
/* 95. */ { 1.291, 1.661, 1.985, 2.366, 2.629, 3.178 },
/* 96. */ { 1.290, 1.661, 1.985, 2.366, 2.628, 3.177 },
/* 97. */ { 1.290, 1.661, 1.985, 2.365, 2.627, 3.176 },
/* 98. */ { 1.290, 1.661, 1.984, 2.365, 2.627, 3.175 },
/* 99. */ { 1.290, 1.660, 1.984, 2.365, 2.626, 3.175 },
/* 100. */ { 1.290, 1.660, 1.984, 2.364, 2.626, 3.174 }
};
#define MAX_DS 8
static char symbol[MAX_DS] = { ' ', 'x', '+', '*', '%', '#', '@', 'O' };
struct dataset {
char *name;
double *points;
unsigned lpoints;
double sy, syy;
unsigned n;
};
static struct dataset *
NewSet(void)
{
struct dataset *ds;
ds = calloc(1, sizeof *ds);
ds->lpoints = 100000;
ds->points = calloc(sizeof *ds->points, ds->lpoints);
return(ds);
}
static void
AddPoint(struct dataset *ds, double a)
{
double *dp;
if (ds->n >= ds->lpoints) {
dp = ds->points;
ds->lpoints *= 4;
ds->points = calloc(sizeof *ds->points, ds->lpoints);
memcpy(ds->points, dp, sizeof *dp * ds->n);
free(dp);
}
ds->points[ds->n++] = a;
ds->sy += a;
ds->syy += a * a;
}
static double
Min(struct dataset *ds)
{
return (ds->points[0]);
}
static double
Max(struct dataset *ds)
{
return (ds->points[ds->n -1]);
}
static double
Avg(struct dataset *ds)
{
return(ds->sy / ds->n);
}
static double
Median(struct dataset *ds)
{
return (ds->points[ds->n / 2]);
}
static double
Var(struct dataset *ds)
{
return (ds->syy - ds->sy * ds->sy / ds->n) / (ds->n - 1.0);
}
static double
Stddev(struct dataset *ds)
{
return sqrt(Var(ds));
}
static void
VitalsHead(void)
{
printf(" N Min Max Median Avg Stddev\n");
}
static void
Vitals(struct dataset *ds, int flag)
{
printf("%c %3d %17.12g %17.12g %17.12g %17.12g %17.12g", symbol[flag],
ds->n, Min(ds), Max(ds), Median(ds), Avg(ds), Stddev(ds));
printf("\n");
}
static void
Relative(struct dataset *ds, struct dataset *rs, int confidx)
{
double spool, s, d, e, t;
int i;
i = ds->n + rs->n - 2;
if (i > NSTUDENT)
t = student[0][confidx];
else
t = student[i][confidx];
spool = (ds->n - 1) * Var(ds) + (rs->n - 1) * Var(rs);
spool /= ds->n + rs->n - 2;
spool = sqrt(spool);
s = spool * sqrt(1.0 / ds->n + 1.0 / rs->n);
d = Avg(ds) - Avg(rs);
e = t * s;
if (fabs(d) > e) {
printf("Difference at %.1f%% confidence\n", studentpct[confidx]);
printf(" %g +/- %g\n", d, e);
printf(" %g%% +/- %g%%\n", d * 100 / Avg(rs), e * 100 / Avg(rs));
printf(" (Student's t, pooled s = %g)\n", spool);
} else {
printf("No difference proven at %.1f%% confidence\n",
studentpct[confidx]);
}
}
struct plot {
double min;
double max;
double span;
int width;
double x0, dx;
int height;
char *data;
char **bar;
int separate_bars;
int num_datasets;
};
static struct plot plot;
static void
SetupPlot(int width, int separate, int num_datasets)
{
struct plot *pl;
pl = &plot;
pl->width = width;
pl->height = 0;
pl->data = NULL;
pl->bar = NULL;
pl->separate_bars = separate;
pl->num_datasets = num_datasets;
pl->min = 999e99;
pl->max = -999e99;
}
static void
AdjPlot(double a)
{
struct plot *pl;
pl = &plot;
if (a < pl->min)
pl->min = a;
if (a > pl->max)
pl->max = a;
pl->span = pl->max - pl->min;
pl->dx = pl->span / (pl->width - 1.0);
pl->x0 = pl->min - .5 * pl->dx;
}
static void
DimPlot(struct dataset *ds)
{
AdjPlot(Min(ds));
AdjPlot(Max(ds));
AdjPlot(Avg(ds) - Stddev(ds));
AdjPlot(Avg(ds) + Stddev(ds));
}
static void
PlotSet(struct dataset *ds, int val)
{
struct plot *pl;
int i, j, m, x;
unsigned n;
int bar;
pl = &plot;
if (pl->span == 0)
return;
if (pl->separate_bars)
bar = val-1;
else
bar = 0;
if (pl->bar == NULL) {
pl->bar = malloc(sizeof(char *) * pl->num_datasets);
memset(pl->bar, 0, sizeof(char*) * pl->num_datasets);
}
if (pl->bar[bar] == NULL) {
pl->bar[bar] = malloc(pl->width);
memset(pl->bar[bar], 0, pl->width);
}
m = 1;
i = -1;
j = 0;
for (n = 0; n < ds->n; n++) {
x = (ds->points[n] - pl->x0) / pl->dx;
if (x == i) {
j++;
if (j > m)
m = j;
} else {
j = 1;
i = x;
}
}
m += 1;
if (m > pl->height) {
pl->data = realloc(pl->data, pl->width * m);
memset(pl->data + pl->height * pl->width, 0,
(m - pl->height) * pl->width);
}
pl->height = m;
i = -1;
for (n = 0; n < ds->n; n++) {
x = (ds->points[n] - pl->x0) / pl->dx;
if (x == i) {
j++;
} else {
j = 1;
i = x;
}
pl->data[j * pl->width + x] |= val;
}
if (!isnan(Stddev(ds))) {
x = ((Avg(ds) - Stddev(ds)) - pl->x0) / pl->dx;
m = ((Avg(ds) + Stddev(ds)) - pl->x0) / pl->dx;
pl->bar[bar][m] = '|';
pl->bar[bar][x] = '|';
for (i = x + 1; i < m; i++)
if (pl->bar[bar][i] == 0)
pl->bar[bar][i] = '_';
}
x = (Median(ds) - pl->x0) / pl->dx;
pl->bar[bar][x] = 'M';
x = (Avg(ds) - pl->x0) / pl->dx;
pl->bar[bar][x] = 'A';
}
static void
DumpPlot(void)
{
struct plot *pl;
int i, j, k;
pl = &plot;
if (pl->span == 0) {
printf("[no plot, span is zero width]\n");
return;
}
putchar('+');
for (i = 0; i < pl->width; i++)
putchar('-');
putchar('+');
putchar('\n');
for (i = 1; i < pl->height; i++) {
putchar('|');
for (j = 0; j < pl->width; j++) {
k = pl->data[(pl->height - i) * pl->width + j];
if (k >= 0 && k < MAX_DS)
putchar(symbol[k]);
else
printf("[%02x]", k);
}
putchar('|');
putchar('\n');
}
for (i = 0; i < pl->num_datasets; i++) {
if (pl->bar[i] == NULL)
continue;
putchar('|');
for (j = 0; j < pl->width; j++) {
k = pl->bar[i][j];
if (k == 0)
k = ' ';
putchar(k);
}
putchar('|');
putchar('\n');
}
putchar('+');
for (i = 0; i < pl->width; i++)
putchar('-');
putchar('+');
putchar('\n');
}
static int
dbl_cmp(const void *a, const void *b)
{
const double *aa = a;
const double *bb = b;
if (*aa < *bb)
return (-1);
else if (*aa > *bb)
return (1);
else
return (0);
}
static struct dataset *
ReadSet(const char *n, int column, const char *delim)
{
FILE *f;
char buf[BUFSIZ], *p, *t;
struct dataset *s;
double d;
int line;
int i;
if (n == NULL) {
f = stdin;
n = "<stdin>";
} else if (!strcmp(n, "-")) {
f = stdin;
n = "<stdin>";
} else {
f = fopen(n, "r");
}
if (f == NULL)
err(1, "Cannot open %s", n);
s = NewSet();
s->name = strdup(n);
line = 0;
while (fgets(buf, sizeof buf, f) != NULL) {
line++;
i = strlen(buf);
if (buf[i-1] == '\n')
buf[i-1] = '\0';
for (i = 1, t = strtok(buf, delim);
t != NULL && *t != '#';
i++, t = strtok(NULL, delim)) {
if (i == column)
break;
}
if (t == NULL || *t == '#')
continue;
d = strtod(t, &p);
if (p != NULL && *p != '\0')
err(2, "Invalid data on line %d in %s\n", line, n);
if (*buf != '\0')
AddPoint(s, d);
}
fclose(f);
if (s->n < 3) {
fprintf(stderr,
"Dataset %s must contain at least 3 data points\n", n);
exit (2);
}
qsort(s->points, s->n, sizeof *s->points, dbl_cmp);
return (s);
}
static void
usage(char const *whine)
{
int i;
fprintf(stderr, "%s\n", whine);
fprintf(stderr,
"Usage: ministat [-C column] [-c confidence] [-d delimiter(s)] [-ns] [-w width] [file [file ...]]\n");
fprintf(stderr, "\tconfidence = {");
for (i = 0; i < NCONF; i++) {
fprintf(stderr, "%s%g%%",
i ? ", " : "",
studentpct[i]);
}
fprintf(stderr, "}\n");
fprintf(stderr, "\t-C : column number to extract (starts and defaults to 1)\n");
fprintf(stderr, "\t-d : delimiter(s) string, default to \" \\t\"\n");
fprintf(stderr, "\t-n : print summary statistics only, no graph/test\n");
fprintf(stderr, "\t-s : print avg/median/stddev bars on separate lines\n");
fprintf(stderr, "\t-w : width of graph/test output (default 74 or terminal width)\n");
exit (2);
}
int
main(int argc, char **argv)
{
struct dataset *ds[7];
int nds;
double a;
const char *delim = " \t";
char *p;
int c, i, ci;
int column = 1;
int flag_s = 0;
int flag_n = 0;
int termwidth = 74;
if (isatty(STDOUT_FILENO)) {
struct winsize wsz;
if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
termwidth = atoi(p);
else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsz) != -1 &&
wsz.ws_col > 0)
termwidth = wsz.ws_col - 2;
}
ci = -1;
while ((c = getopt(argc, argv, "C:c:d:snw:")) != -1)
switch (c) {
case 'C':
column = strtol(optarg, &p, 10);
if (p != NULL && *p != '\0')
usage("Invalid column number.");
if (column <= 0)
usage("Column number should be positive.");
break;
case 'c':
a = strtod(optarg, &p);
if (p != NULL && *p != '\0')
usage("Not a floating point number");
for (i = 0; i < NCONF; i++)
if (a == studentpct[i])
ci = i;
if (ci == -1)
usage("No support for confidence level");
break;
case 'd':
if (*optarg == '\0')
usage("Can't use empty delimiter string");
delim = optarg;
break;
case 'n':
flag_n = 1;
break;
case 's':
flag_s = 1;
break;
case 'w':
termwidth = strtol(optarg, &p, 10);
if (p != NULL && *p != '\0')
usage("Invalid width, not a number.");
if (termwidth < 0)
usage("Unable to move beyond left margin.");
break;
default:
usage("Unknown option");
break;
}
if (ci == -1)
ci = 2;
argc -= optind;
argv += optind;
if (argc == 0) {
ds[0] = ReadSet("-", column, delim);
nds = 1;
} else {
if (argc > (MAX_DS - 1))
usage("Too many datasets.");
nds = argc;
for (i = 0; i < nds; i++)
ds[i] = ReadSet(argv[i], column, delim);
}
for (i = 0; i < nds; i++)
printf("%c %s\n", symbol[i+1], ds[i]->name);
if (!flag_n) {
SetupPlot(termwidth, flag_s, nds);
for (i = 0; i < nds; i++)
DimPlot(ds[i]);
for (i = 0; i < nds; i++)
PlotSet(ds[i], i + 1);
DumpPlot();
}
VitalsHead();
Vitals(ds[0], 1);
for (i = 1; i < nds; i++) {
Vitals(ds[i], i + 1);
if (!flag_n)
Relative(ds[i], ds[0], ci);
}
exit(0);
}

View File

@@ -1,7 +0,0 @@
PROG= top
MAN=
DPADD+= ${LIBCURSES} ${LIBTERMINFO}
LDADD+= -lcurses -lterminfo
.include <bsd.prog.mk>

View File

@@ -1,714 +0,0 @@
/* Author: Ben Gras <beng@few.vu.nl> 17 march 2006 */
/* Modified for ProcFS by Alen Stojanov and David van Moolenbroek */
#define _MINIX_SYSTEM 1
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <curses.h>
#include <minix/timers.h>
#include <stdlib.h>
#include <limits.h>
#include <termcap.h>
#include <termios.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <assert.h>
#include <sys/ttycom.h>
#include <sys/ioctl.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <minix/com.h>
#include <minix/config.h>
#include <minix/type.h>
#include <minix/endpoint.h>
#include <minix/const.h>
#include <minix/u64.h>
#include <minix/paths.h>
#include <minix/procfs.h>
#define TIMECYCLEKEY 't'
#define ORDERKEY 'o'
#define ORDER_CPU 0
#define ORDER_MEMORY 1
#define ORDER_HIGHEST ORDER_MEMORY
int order = ORDER_CPU;
u32_t system_hz;
/* name of cpu cycle types, in the order they appear in /psinfo. */
const char *cputimenames[] = { "user", "ipc", "kernelcall" };
#define CPUTIMENAMES ((int) (sizeof(cputimenames)/sizeof(cputimenames[0])))
#define CPUTIME(m, i) (m & (1L << (i)))
unsigned int nr_procs, nr_tasks;
int nr_total;
#define SLOT_NR(e) (_ENDPOINT_P(e) + nr_tasks)
#define TC_BUFFER 1024 /* Size of termcap(3) buffer */
#define TC_STRINGS 200 /* Enough room for cm,cl,so,se */
const char *Tclr_all;
int blockedverbose = 0;
#define USED 0x1
#define IS_TASK 0x2
#define IS_SYSTEM 0x4
#define BLOCKED 0x8
struct proc {
int p_flags;
endpoint_t p_endpoint;
pid_t p_pid;
u64_t p_cpucycles[CPUTIMENAMES];
int p_priority;
endpoint_t p_blocked;
time_t p_user_time;
vir_bytes p_memory;
uid_t p_effuid;
int p_nice;
char p_name[PROC_NAME_LEN+1];
};
struct proc *proc = NULL, *prev_proc = NULL;
static void parse_file(pid_t pid)
{
char path[PATH_MAX], name[256], type, state;
int version, endpt, effuid;
unsigned long cycles_hi, cycles_lo;
FILE *fp;
struct proc *p;
int slot;
int i;
sprintf(path, "%d/psinfo", pid);
if ((fp = fopen(path, "r")) == NULL)
return;
if (fscanf(fp, "%d", &version) != 1) {
fclose(fp);
return;
}
if (version != PSINFO_VERSION) {
fputs("procfs version mismatch!\n", stderr);
exit(1);
}
if (fscanf(fp, " %c %d", &type, &endpt) != 2) {
fclose(fp);
return;
}
slot = SLOT_NR(endpt);
if(slot < 0 || slot >= nr_total) {
fprintf(stderr, "top: unreasonable endpoint number %d\n", endpt);
fclose(fp);
return;
}
p = &proc[slot];
if (type == TYPE_TASK)
p->p_flags |= IS_TASK;
else if (type == TYPE_SYSTEM)
p->p_flags |= IS_SYSTEM;
p->p_endpoint = endpt;
p->p_pid = pid;
if (fscanf(fp, " %255s %c %d %d %llu %*u %lu %lu",
name, &state, &p->p_blocked, &p->p_priority,
&p->p_user_time, &cycles_hi, &cycles_lo) != 7) {
fclose(fp);
return;
}
strncpy(p->p_name, name, sizeof(p->p_name)-1);
p->p_name[sizeof(p->p_name)-1] = 0;
if (state != STATE_RUN)
p->p_flags |= BLOCKED;
p->p_cpucycles[0] = make64(cycles_lo, cycles_hi);
p->p_memory = 0L;
if (!(p->p_flags & IS_TASK)) {
int j;
if ((j=fscanf(fp, " %lu %*u %*u %*c %*d %*u %u %*u %d %*c %*d %*u",
&p->p_memory, &effuid, &p->p_nice)) != 3) {
fclose(fp);
return;
}
p->p_effuid = effuid;
} else p->p_effuid = 0;
for(i = 1; i < CPUTIMENAMES; i++) {
if(fscanf(fp, " %lu %lu",
&cycles_hi, &cycles_lo) == 2) {
p->p_cpucycles[i] = make64(cycles_lo, cycles_hi);
} else {
p->p_cpucycles[i] = 0;
}
}
if ((p->p_flags & IS_TASK)) {
if(fscanf(fp, " %lu", &p->p_memory) != 1) {
p->p_memory = 0;
}
}
p->p_flags |= USED;
fclose(fp);
}
static void parse_dir(void)
{
DIR *p_dir;
struct dirent *p_ent;
pid_t pid;
char *end;
if ((p_dir = opendir(".")) == NULL) {
perror("opendir on " _PATH_PROC);
exit(1);
}
for (p_ent = readdir(p_dir); p_ent != NULL; p_ent = readdir(p_dir)) {
pid = strtol(p_ent->d_name, &end, 10);
if (!end[0] && pid != 0)
parse_file(pid);
}
closedir(p_dir);
}
static void get_procs(void)
{
struct proc *p;
int i;
p = prev_proc;
prev_proc = proc;
proc = p;
if (proc == NULL) {
proc = malloc(nr_total * sizeof(proc[0]));
if (proc == NULL) {
fprintf(stderr, "Out of memory!\n");
exit(1);
}
}
for (i = 0; i < nr_total; i++)
proc[i].p_flags = 0;
parse_dir();
}
static int print_memory(void)
{
FILE *fp;
unsigned int pagesize;
unsigned long total, freemem, largest, cached;
if ((fp = fopen("meminfo", "r")) == NULL)
return 0;
if (fscanf(fp, "%u %lu %lu %lu %lu", &pagesize, &total, &freemem,
&largest, &cached) != 5) {
fclose(fp);
return 0;
}
fclose(fp);
printf("main memory: %ldK total, %ldK free, %ldK contig free, "
"%ldK cached\n",
(pagesize * total)/1024, (pagesize * freemem)/1024,
(pagesize * largest)/1024, (pagesize * cached)/1024);
return 1;
}
static int print_load(double *loads, int nloads)
{
int i;
printf("load averages: ");
for(i = 0; i < nloads; i++)
printf("%s %.2f", (i > 0) ? "," : "", loads[i]);
printf("\n");
return 1;
}
static int print_proc_summary(struct proc *pproc)
{
int p, alive, running, sleeping;
alive = running = sleeping = 0;
for(p = 0; p < nr_total; p++) {
if (pproc[p].p_endpoint == IDLE)
continue;
if(!(pproc[p].p_flags & USED))
continue;
alive++;
if(pproc[p].p_flags & BLOCKED)
sleeping++;
else
running++;
}
printf("%d processes: %d running, %d sleeping\n",
alive, running, sleeping);
return 1;
}
struct tp {
struct proc *p;
u64_t ticks;
};
static int cmp_procs(const void *v1, const void *v2)
{
const struct tp *p1 = (const struct tp *) v1,
*p2 = (const struct tp *) v2;
int p1blocked, p2blocked;
if(order == ORDER_MEMORY) {
if(p1->p->p_memory < p2->p->p_memory) return 1;
if(p1->p->p_memory > p2->p->p_memory) return -1;
return 0;
}
p1blocked = !!(p1->p->p_flags & BLOCKED);
p2blocked = !!(p2->p->p_flags & BLOCKED);
/* Primarily order by used number of cpu cycles.
*
* Exception: if in blockedverbose mode, a blocked
* process is always printed after an unblocked
* process, and used cpu cycles don't matter.
*
* In both cases, process slot number is a tie breaker.
*/
if(blockedverbose && (p1blocked || p2blocked)) {
if(!p1blocked && p2blocked)
return -1;
if(!p2blocked && p1blocked)
return 1;
} else if(p1->ticks != p2->ticks) {
if(p1->ticks > p2->ticks) return -1;
assert(p1->ticks < p2->ticks);
return 1;
}
/* Process slot number is a tie breaker. */
return (int) (p1->p - p2->p);
}
static struct tp *lookup(endpoint_t who, struct tp *tptab, int np)
{
int t;
for(t = 0; t < np; t++)
if(who == tptab[t].p->p_endpoint)
return &tptab[t];
fprintf(stderr, "lookup: tp %d (0x%x) not found.\n", who, who);
abort();
return NULL;
}
double ktotal = 0;
static void print_proc(struct tp *tp, u64_t total_ticks)
{
int euid = 0;
static struct passwd *who = NULL;
static int last_who = -1;
const char *name = "";
int ticks;
struct proc *pr = tp->p;
printf("%5d ", pr->p_pid);
euid = pr->p_effuid;
name = pr->p_name;
if(last_who != euid || !who) {
who = getpwuid(euid);
last_who = euid;
}
if(who && who->pw_name) printf("%-8s ", who->pw_name);
else if(!(pr->p_flags & IS_TASK)) printf("%8d ", pr->p_effuid);
else printf(" ");
printf(" %2d ", pr->p_priority);
if(!(pr->p_flags & IS_TASK)) {
printf(" %3d ", pr->p_nice);
} else printf(" ");
printf("%6ldK", (pr->p_memory + 512) / 1024);
printf("%6s", (pr->p_flags & BLOCKED) ? "" : "RUN");
ticks = pr->p_user_time;
printf(" %3u:%02u ", (ticks/system_hz/60), (ticks/system_hz)%60);
printf("%6.2f%% %s", 100.0 * tp->ticks / total_ticks, name);
}
static char *cputimemodename(int cputimemode)
{
static char name[100];
int i;
name[0] = '\0';
for(i = 0; i < CPUTIMENAMES; i++) {
if(CPUTIME(cputimemode, i)) {
assert(strlen(name) +
strlen(cputimenames[i]) < sizeof(name));
strcat(name, cputimenames[i]);
strcat(name, " ");
}
}
return name;
}
static u64_t cputicks(struct proc *p1, struct proc *p2, int timemode)
{
int i;
u64_t t = 0;
for(i = 0; i < CPUTIMENAMES; i++) {
if(!CPUTIME(timemode, i))
continue;
if(p1->p_endpoint == p2->p_endpoint) {
t = t + p2->p_cpucycles[i] - p1->p_cpucycles[i];
} else {
t = t + p2->p_cpucycles[i];
}
}
return t;
}
static const char *ordername(int orderno)
{
switch(orderno) {
case ORDER_CPU: return "cpu";
case ORDER_MEMORY: return "memory";
}
return "invalid order";
}
static void print_procs(int maxlines,
struct proc *proc1, struct proc *proc2, int cputimemode)
{
int p, nprocs;
u64_t idleticks = 0;
u64_t kernelticks = 0;
u64_t systemticks = 0;
u64_t userticks = 0;
u64_t total_ticks = 0;
int blockedseen = 0;
static struct tp *tick_procs = NULL;
if (tick_procs == NULL) {
tick_procs = malloc(nr_total * sizeof(tick_procs[0]));
if (tick_procs == NULL) {
fprintf(stderr, "Out of memory!\n");
exit(1);
}
}
for(p = nprocs = 0; p < nr_total; p++) {
u64_t uticks;
if(!(proc2[p].p_flags & USED))
continue;
tick_procs[nprocs].p = proc2 + p;
tick_procs[nprocs].ticks = cputicks(&proc1[p], &proc2[p], cputimemode);
uticks = cputicks(&proc1[p], &proc2[p], 1);
total_ticks = total_ticks + uticks;
if(p-NR_TASKS == IDLE) {
idleticks = uticks;
continue;
}
if(p-NR_TASKS == KERNEL) {
kernelticks = uticks;
}
if(!(proc2[p].p_flags & IS_TASK)) {
if(proc2[p].p_flags & IS_SYSTEM)
systemticks = systemticks + tick_procs[nprocs].ticks;
else
userticks = userticks + tick_procs[nprocs].ticks;
}
nprocs++;
}
if (total_ticks == 0)
return;
qsort(tick_procs, nprocs, sizeof(tick_procs[0]), cmp_procs);
printf("CPU states: %6.2f%% user, ", 100.0 * userticks / total_ticks);
printf("%6.2f%% system, ", 100.0 * systemticks / total_ticks);
printf("%6.2f%% kernel, ", 100.0 * kernelticks/ total_ticks);
printf("%6.2f%% idle", 100.0 * idleticks / total_ticks);
#define NEWLINE do { printf("\n"); if(--maxlines <= 0) { return; } } while(0)
NEWLINE;
printf("CPU time displayed ('%c' to cycle): %s; ",
TIMECYCLEKEY, cputimemodename(cputimemode));
printf(" sort order ('%c' to cycle): %s", ORDERKEY, ordername(order));
NEWLINE;
NEWLINE;
printf(" PID USERNAME PRI NICE SIZE STATE TIME CPU COMMAND");
NEWLINE;
for(p = 0; p < nprocs; p++) {
struct proc *pr;
int level = 0;
pr = tick_procs[p].p;
if((pr->p_flags & IS_TASK) && pr->p_pid != KERNEL) {
/* skip old kernel tasks as they don't run anymore */
continue;
}
/* If we're in blocked verbose mode, indicate start of
* blocked processes.
*/
if(blockedverbose && (pr->p_flags & BLOCKED) && !blockedseen) {
NEWLINE;
printf("Blocked processes:");
NEWLINE;
blockedseen = 1;
}
print_proc(&tick_procs[p], total_ticks);
NEWLINE;
if(!blockedverbose)
continue;
/* Traverse dependency chain if blocked. */
while(pr->p_flags & BLOCKED) {
endpoint_t dep = NONE;
struct tp *tpdep;
level += 5;
if((dep = pr->p_blocked) == NONE) {
printf("not blocked on a process");
NEWLINE;
break;
}
if(dep == ANY)
break;
tpdep = lookup(dep, tick_procs, nprocs);
pr = tpdep->p;
printf("%*s> ", level, "");
print_proc(tpdep, total_ticks);
NEWLINE;
}
}
}
static void showtop(int cputimemode, int r)
{
#define NLOADS 3
double loads[NLOADS];
int nloads, lines = 0;
struct winsize winsize;
if(ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize) != 0) {
perror("TIOCGWINSZ");
fprintf(stderr, "TIOCGWINSZ failed\n");
exit(1);
}
get_procs();
if (prev_proc == NULL)
get_procs();
if((nloads = getloadavg(loads, NLOADS)) != NLOADS) {
fprintf(stderr, "getloadavg() failed - %d loads\n", nloads);
exit(1);
}
printf("%s", Tclr_all);
lines += print_load(loads, NLOADS);
lines += print_proc_summary(proc);
lines += print_memory();
if(winsize.ws_row > 0) r = winsize.ws_row;
print_procs(r - lines - 2, prev_proc, proc, cputimemode);
fflush(NULL);
}
static void init(int *rows)
{
char *term;
static char buffer[TC_BUFFER], strings[TC_STRINGS];
char *s = strings, *v;
*rows = 0;
if(!(term = getenv("TERM"))) {
fprintf(stderr, "No TERM set\n");
exit(1);
}
if ( tgetent( buffer, term ) != 1 ) {
fprintf(stderr, "tgetent failed for term %s\n", term);
exit(1);
}
initscr();
cbreak();
if ( (Tclr_all = tgetstr( "cl", &s )) == NULL )
Tclr_all = "\f";
if((v = tgetstr ("li", &s)) != NULL)
sscanf(v, "%d", rows);
if(*rows < 1) *rows = 24;
}
static void sigwinch(int sig) { }
static void getkinfo(void)
{
FILE *fp;
if ((fp = fopen("kinfo", "r")) == NULL) {
fprintf(stderr, "opening " _PATH_PROC "kinfo failed\n");
exit(1);
}
if (fscanf(fp, "%u %u", &nr_procs, &nr_tasks) != 2) {
fprintf(stderr, "reading from " _PATH_PROC "kinfo failed\n");
exit(1);
}
fclose(fp);
nr_total = (int) (nr_procs + nr_tasks);
}
int main(int argc, char *argv[])
{
int r, optc, s = 0;
int cputimemode = 1; /* bitmap. */
if (chdir(_PATH_PROC) != 0) {
perror("chdir to " _PATH_PROC);
return 1;
}
system_hz = (u32_t) sysconf(_SC_CLK_TCK);
getkinfo();
init(&r);
while((optc=getopt(argc, argv, "s:B")) != EOF) {
switch(optc) {
case 's':
s = atoi(optarg);
break;
case 'B':
blockedverbose = 1;
break;
default:
fprintf(stderr,
"Usage: %s [-s<secdelay>] [-B]\n",
argv[0]);
return 1;
}
}
if(s < 1)
s = 2;
/* Catch window size changes so display is updated properly
* right away.
*/
signal(SIGWINCH, sigwinch);
while(1) {
fd_set fds;
int ns;
struct timeval tv;
showtop(cputimemode, r);
tv.tv_sec = s;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
if((ns=select(STDIN_FILENO+1, &fds, NULL, NULL, &tv)) < 0
&& errno != EINTR) {
perror("select");
sleep(1);
}
if(ns > 0 && FD_ISSET(STDIN_FILENO, &fds)) {
char inc;
if(read(STDIN_FILENO, &inc, 1) == 1) {
switch(inc) {
case 'q':
putchar('\r');
return 0;
break;
case 'o':
order++;
if(order > ORDER_HIGHEST)
order = 0;
break;
case TIMECYCLEKEY:
cputimemode++;
if(cputimemode >= (1L << CPUTIMENAMES))
cputimemode = 1;
break;
}
}
}
}
return 0;
}

View File

@@ -1,10 +0,0 @@
#
# Makefile for the toproto tool.
#
PROG=toproto
MAN=
CPPFLAGS+= -I${NETBSDSRCDIR}/sbin/mknod
.PATH: ${NETBSDSRCDIR}/sbin/mknod
.include <bsd.prog.mk>

View File

@@ -1,477 +0,0 @@
#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#else
#include <sys/cdefs.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "pack_dev.h"
#define MAX_ENTRIES 100000
#define MAX_LINE_SIZE 0xfff
/*
* Tool to convert the netbsd METALOG into a proto file usable by mkfs.mfs
*
* todo:
* Possibly use netbsd usr.sbin/makefs to create mfs file systems.
*/
enum entry_type
{ ENTRY_DIR, ENTRY_FILE, ENTRY_LINK, ENTRY_BLOCK, ENTRY_CHAR };
struct entry
{
char *path;
char *filename; /* point to last component in the path */
enum entry_type type; /* entry type */
int mode; /* unix mode e.g. 0755 */
char *uid;
char *gid;
char *time; /* time 1365836670.000000000 */
char *size;
char *link;
/* Can't use devmajor_t/devminor_t on linux systems :( */
int32_t dev_major;
int32_t dev_minor;
/* just internal variables used to create a tree */
int depth;
struct entry *parent;
};
static struct entry entries[MAX_ENTRIES];
static int entry_total_count;
static int
convert_to_entry(char *line, struct entry *entry)
{
/* convert a input line from sanitized input into an entry */
char *saveptr;
char *key, *value;
saveptr = NULL;
/* we need to have a terminated string */
assert(strnlen(line, MAX_LINE_SIZE - 1) != MAX_LINE_SIZE - 1);
/* skip comment lines */
if (*line == '#')
return 1;
line = strtok_r(line, " ", &saveptr);
/* skip empty lines */
if (!line) {
return 1;
}
memset(entry, 0, sizeof(struct entry));
/* the first entry is the path name */
entry->path = strndup(line, MAX_LINE_SIZE);
/* the next entries are key,value pairs */
while ((line = strtok_r(NULL, " ", &saveptr)) != NULL) {
key = value = NULL;
char *p;
if (strstr(line, "=") == NULL) {
fprintf(stderr, "expected key/value pair in %s\n",
line);
free(entry->path);
return 1;
}
p = NULL;
key = strtok_r(line, "=", &p);
value = strtok_r(NULL, "=", &p);
if (value) {
if (strncmp(key, "type", 5) == 0) {
if (strncmp(value, "dir", 4) == 0) {
entry->type = ENTRY_DIR;
} else if (strncmp(value, "file", 5) == 0) {
entry->type = ENTRY_FILE;
} else if (strncmp(value, "link", 5) == 0) {
entry->type = ENTRY_LINK;
} else if (strncmp(value, "block", 6) == 0) {
entry->type = ENTRY_BLOCK;
} else if (strncmp(value, "char", 5) == 0) {
entry->type = ENTRY_CHAR;
} else {
fprintf(stderr,
"\tunknown type %s -> '%s'\n", key,
value);
}
} else if (strncmp(key, "mode", 5) == 0) {
sscanf(value,"%o",&entry->mode);
} else if (strncmp(key, "uid", 4) == 0) {
entry->uid = strndup(value, MAX_LINE_SIZE);
} else if (strncmp(key, "gid", 4) == 0) {
entry->gid = strndup(value, MAX_LINE_SIZE);
} else if (strncmp(key, "time", 5) == 0) {
entry->time = strndup(value, MAX_LINE_SIZE);
} else if (strncmp(key, "size", 5) == 0) {
entry->size = strndup(value, MAX_LINE_SIZE);
} else if (strncmp(key, "link", 5) == 0) {
entry->link = strndup(value, MAX_LINE_SIZE);
} else if (strncmp(key, "device", 7) == 0) {
long int dev_id;
dev_id = strtoul(value, NULL, 16);
entry->dev_major = major_netbsd(dev_id);
entry->dev_minor = minor_netbsd(dev_id);
} else {
fprintf(stderr,
"\tunknown attribute %s -> %s\n", key,
value);
}
}
}
return 0;
}
static int
iterate_over_input(int fh_in, void (*callback) (char *line))
{
char buf[MAX_LINE_SIZE];
int r_size, err;
int line_size;
int buf_end;
int line_nr;
char *p;
memset(buf, 0, MAX_LINE_SIZE);
r_size = 0;
buf_end = 0;
line_nr = 0;
while (1 == 1) {
/* fill buffer taking into account there can already be
* content at the start */
r_size = read(fh_in, &buf[buf_end], MAX_LINE_SIZE - buf_end);
if (r_size == -1) {
err = errno;
fprintf(stderr, "failed reading input:%s\n",
strerror(err));
return 1;
}
/* checking for read size of 0 is not enough as the buffer
* still can contain content */
buf_end = buf_end + r_size;
/* is there data we need to process ? */
if (buf_end == 0) {
return 0; /* normal exit is here */
}
/* find end of line or eof. start a the start of the buffer */
p = buf;
while (p < buf + buf_end) {
if (*p == '\n' || *p == '\0') {
/* replace either by a null terminator */
line_nr++;
*p = '\0';
break;
}
p++;
}
/* If we are at the end of the buffer we did not find a
* terminator */
if (p == buf + buf_end) {
fprintf(stderr,
"Line(%d) does not fit the buffer %d\n", line_nr,
MAX_LINE_SIZE);
return 1;
}
line_size = p - buf; /* size of the line we currently are
* reading */
/* here we have a valid line */
callback(buf);
/* copy the remaining data over to the start */
memmove(buf, p + 1, MAX_LINE_SIZE - line_size);
buf_end -= (line_size + 1);
}
return 0;
}
static void
parse_line_cb(char *line)
{
if (convert_to_entry(line, &entries[entry_total_count]) == 0) {
entry_total_count++;
assert(entry_total_count < MAX_ENTRIES);
} else {
memset(&entries[entry_total_count], 0, sizeof(struct entry));
}
}
static int
create_entries(int handle)
{
int c;
char *p;
struct entry *entry;
char tmppath[MAX_LINE_SIZE];
int i;
if (iterate_over_input(handle, parse_line_cb)) {
return 1;
}
/* calculate depth for each entry */
for (c = 0; c < entry_total_count; c++) {
p = entries[c].path;
while (*p != 0) {
if (*p == '/') {
entries[c].depth++;
}
p++;
}
}
/* find parent entry and set the filename */
for (c = 0; c < entry_total_count; c++) {
entry = &entries[c];
if (entry->depth > 0) {
/* calculate path */
/* find last "/" element and "null" it */
strncpy(tmppath, entry->path, MAX_LINE_SIZE - 1);
i = strlen(tmppath);
while (i > 0) {
if (tmppath[i] == '/') {
entry->filename = &entry->path[i + 1];
tmppath[i] = '\0';
break;
}
i--;
}
if (i == 0) {
fprintf
(stderr,
"error while searching for parent path of %s\n",
entry->path);
return 1;
}
/* now compare with the other entries */
for (i = 0; i < entry_total_count; i++) {
if (strncmp(entries[i].path, tmppath,
MAX_LINE_SIZE) == 0) {
/* found entry */
entry->parent = &entries[i];
break;
}
}
if (entry->parent == NULL) {
fprintf(stderr,
"Failed to find parent directory of %s\n",
entry->path);
return 1;
}
assert(entry->parent->type == ENTRY_DIR);
} else {
/* same in this case */
entry->filename = entry->path;
}
}
return 0;
}
static char * parse_mode(int mode){
/* Convert a 4 digit octal number int a proto entry as described in
the mkfs.mfs man page e.g. [suid-char][guid-char]0777 mode */
static char m[6];
memset(m,0,6);
char suid,guid;
suid = (mode & 04000)?'u':'-';
guid = (mode & 02000)?'g':'-';
snprintf(m,6,"%c%c%3o",suid,guid,mode & 0777);
return m;
}
static char *parse_filename(char *path)
{
/* Skipping the first . in the path */
return &path[1];
}
static int
dump_entry(FILE * out, int mindex, const char *base_dir)
{
int space;
int i;
struct entry *entry = &entries[mindex];
/* Ensure uid & gid are set to something meaningful. */
if (entry->uid == NULL) {
entry->uid = __UNCONST("0");
}
if (entry->gid == NULL) {
entry->gid = __UNCONST("0");
}
/* Indent the line */
for (space = 0; space < entries[mindex].depth; space++) {
fprintf(out, " ");
}
if (entry->type == ENTRY_DIR) {
if (entries[mindex].depth > 0) {
fprintf(out, "%s ", entry->filename);
}
fprintf(out, "d%s", parse_mode(entry->mode));
fprintf(out, " %s", entry->uid);
fprintf(out, " %s", entry->gid);
fprintf(out, "\n");
for (i = 0; i < entry_total_count; i++) {
if (entries[i].parent == entry) {
dump_entry(out, i, base_dir);
}
}
for (space = 0; space < entries[mindex].depth; space++) {
fprintf(out, " ");
}
fprintf(out, "$\n");
} else if (entry->type == ENTRY_FILE) {
fprintf(out, "%s -%s %s %s %s%s\n", entry->filename,
parse_mode(entry->mode), entry->uid, entry->gid,
base_dir, parse_filename(entry->path));
} else if (entry->type == ENTRY_LINK) {
fprintf(out, "%s s%s %s %s %s\n", entry->filename,
parse_mode(entry->mode), entry->uid, entry->gid,
entry->link);
} else if (entry->type == ENTRY_CHAR) {
fprintf(out, "%s c%s %s %s %d %d\n", entry->filename,
parse_mode(entry->mode), entry->uid, entry->gid,
entry->dev_major, entry->dev_minor);
} else if (entry->type == ENTRY_BLOCK) {
fprintf(out, "%s b%s %s %s %d %d\n", entry->filename,
parse_mode(entry->mode), entry->uid, entry->gid,
entry->dev_major, entry->dev_minor);
} else {
/* Unknown line type. */
fprintf(out, "#");
fprintf(out, "%i %s\n", entry->type, entry->path);
exit(1);
return 1;
}
return 0;
}
static int
dump_proto(FILE * out, const char *base_dir)
{
int i;
fprintf(out, "boot\n0 0");
for (i = 0; i < entry_total_count; i++) {
if (entries[i].depth == 0) {
fprintf(out, "\n");
dump_entry(out, i, base_dir);
}
}
return 0;
}
static void
print_usage(void)
{
printf("Usage: toproto [OPTION]...\n");
printf
("Convert a netbsd METALOG file into a proto file for mkfs.mfs.\n");
printf("\n");
printf(" -i input METALOG\n");
printf(" -b base_path\n");
printf(" -o output proto\n");
printf(" -h show this this help and exit\n");
}
int
main(int argc, char **argv)
{
int ch, fh_in;
FILE *out;
const char *base_path;
char *input_file, *output_file;
input_file = NULL;
output_file = NULL;
base_path = ".";
fh_in = STDIN_FILENO;
out = stdout;
while ((ch = getopt(argc, argv, "i:b:o:h")) != -1) {
switch (ch) {
case 'i':
input_file = optarg;
break;
case 'b':
base_path = optarg;
break;
case 'o':
output_file = optarg;
break;
case 'h':
print_usage();
exit(0);
break;
default:
print_usage();
exit(1);
}
}
argc -= optind;
argv += optind;
if (input_file) {
fh_in = open(input_file, O_RDONLY);
if (fh_in == -1) {
fprintf(stderr, "Failed to open input file (%s):%s\n",
input_file, strerror(errno));
exit(1);
}
}
if (output_file) {
out = fopen(output_file, "w+");
if (!out) {
fprintf(stderr, "Failed to open input file (%s):%s\n",
input_file, strerror(errno));
exit(1);
}
}
if (create_entries(fh_in)) {
fprintf(stderr, "Failed to create entries\n");
exit(1);
}
if (input_file)
close(fh_in);
if (dump_proto(out, base_path)) {
fprintf(stderr, "Failed to create entries\n");
exit(1);
}
if (output_file)
fclose(out);
return 0;
}