/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 2006-2011 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* sort uniq summary discipline
*/
static const char usage[] =
"[-1lp0s5P?\n@(#)$Id: glean (AT&T Research) 2007-04-20 $\n]"
"[+DESCRIPTION?The \bglean\b \bsort\b(1) discipline gleans minima "
"and maxima from a record stream. Each record is categorized by the main "
"sort key. If there is no main sort key then all records are in one "
"category. If the \bmin\b key sorts less than the current category "
"minimum or if the \bmax\b key sorts greater than the category maximum "
"then the category minimum or maximum is updated and the record is "
"written to the standard output. \bmax=\b\ak1\a,\bmax=\b\ak2\a sets one "
"maximum using the two sort keys \ak1\a and \ak2\a. "
"\bmax:=\b\ak1\a,\bmax:=\b\ak2\a sets two maxima, the first using sort "
"key \ak1\a and the second using sort key \ak2\a.]"
"[+?Note that \b,\b is the plugin option separator. Literal \b,\b must "
"either be quoted or escaped \aafter\a normal shell expansion.]"
"category.]"
"[c:count?Precede each output record with its category index, "
"category count, and total record count.]"
"[f:flush?Flush each line written to the standard output.]"
"[m:min?Mimima \bsort\b(1) key specification.]:[sort-key]"
"[M:max?Maxima \bsort\b(1) key specification.]:[sort-key]"
"[+EXAMPLES]"
"{"
"[+sort -t, -k1,1 -k2,2n -lglean,flush,min='\"3,3n\"',min='\"4,4\"' old.dat -?Sorts "
"on the \b,\b separated fields 1 (string) and 2 (numeric) and "
"lists the current minimal records per field 3 (numeric) and "
"field 4 (string), for each value of the catenation of fields 1 and 2.]"
"}"
"[+SEE ALSO?\bsort\b(1)]"
"\n\n--library=glean[,option[=value]...]\n\n"
;
#include <ast.h>
#include <debug.h>
#include <dt.h>
#include <error.h>
#include <recsort.h>
#include <vmalloc.h>
typedef struct Data_s
{
void* data;
} Data_t;
typedef struct Category_s
{
unsigned int index;
} Category_t;
struct Field_s
{
int mm;
int index;
};
typedef struct State_s
{
unsigned int index;
unsigned int fields;
int absolute;
int count;
} State_t;
static int
{
{
{
return -1;
}
}
if (data)
return 0;
}
static int
{
Category_t* x = (Category_t*)a;
Category_t* y = (Category_t*)b;
message((-4, "gleancmp a:%d:%-.*s: b:%d:%-.*s:", x->key.len, x->key.len, x->key.data, y->key.len, y->key.len, y->key.data));
return -1;
return 1;
}
static char*
{
size_t i;
char* b;
char* s;
for (i = 0; i < size; i++)
return b;
}
static int
{
register Rsobj_t* r;
register Category_t* p;
register Field_t* f;
Category_t x;
ssize_t k;
int n;
int m;
Data_t t;
switch (op)
{
case RS_POP:
{
sfprintf(sfstdout, "%u/%I*u/%I*u ", f->category->index, sizeof(f->count), f->count, sizeof(f->total), f->total);
}
return 0;
case RS_READ:
if (state->categories && !(p = (Category_t*)dtsearch(state->categories, &x)) || !state->categories && !(p = state->all))
{
{
return -1;
}
if (state->categories)
else
}
p->count++;
message((-2, "glean record p=%p %I*u/%I*u key='%-.*s' r:%d:%-.*s: '%-.*s'", p, sizeof(p->count), p->count, sizeof(state->total), state->total, r->keylen, r->key, x.key.len, x.key.len, x.key.data, r->datalen && r->data[r->datalen - 1] == '\n' ? r->datalen - 1 : r->datalen, r->data));
m = 0;
{
{
k = 4;
k *= r->datalen;
return -1;
if ((k = (*f->lim->disc->defkeyf)(NiL, r->data, r->datalen, state->key.data, state->key.size, f->lim->disc)) < 0)
return -1;
}
else
{
t.len = k;
}
else
{
message((-1, "glean [%d] %c b:%d:%s:", f->index, f->mm, p->lim[f->index].len, fmtdata(p->lim[f->index].data, p->lim[f->index].len)));
}
if (f->mm == 'm' ? (n < 0) : (n > 0))
{
{
}
return -1;
m = 1;
return -1;
else
{
f->category = p;
}
}
}
if (m)
{
sfprintf(sfstdout, "%u/%I*u/%I*u ", p->index, sizeof(p->count), p->count, sizeof(state->total), state->total);
}
return RS_DELETE;
}
return -1;
}
{
register Field_t* f;
int i;
if (options)
{
for (;;)
{
{
case 0:
break;
case 'a':
continue;
case 'c':
continue;
case 'f':
continue;
case 'm':
case 'M':
{
f->mm = i;
}
goto drop;
continue;
case '?':
goto drop;
case ':':
goto drop;
}
break;
}
goto drop;
}
drop:
rskeyclose(f->lim);
return 0;
}