#define _XOPEN_SOURCE 700 #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define MICO_HEADER "\211MiC\r\n\032\n" static int lflag; static int64_t fflag, tflag; zip_t *zip; struct bitreader { zip_file_t *input; uint64_t bitbuf; // value of bits in write buffer int bitcount; // number of bits in write buffer int eof; }; static uint64_t getbits1_msb(struct bitreader *input, int count) { assert(count >= 1 && count <= 57); // Keep reading extra bytes until we have enough bits buffered // Big endian; our current bits are at the top of the word, // new bits get added at the bottom. while (input->bitcount < count) { unsigned char buf; int ret = zip_fread(input->input, &buf, sizeof buf); if (ret < 0) { printf("err: %s", zip_strerror(zip)); } if (ret == 0) { input->eof = 1; return 0; } input->bitbuf |= (uint64_t)buf << (56 - input->bitcount); input->bitcount += 8; } // We now have enough bits present; the most significant // "count" bits in "bitbuf" are our result. uint64_t result = input->bitbuf >> (64 - count); // Now remove these bits from the buffer input->bitbuf <<= count; input->bitcount -= count; return result; } static int32_t getsigned(struct bitreader *input, int count) { uint64_t bits = getbits1_msb(input, count); uint64_t sign = 1 << (count - 1); if (bits & sign) return -(bits ^ sign) - 1; else return bits; } static int64_t get1(struct bitreader *input) { if (getbits1_msb(input, 1) == 0) return 0; if (getbits1_msb(input, 1) == 0) return getsigned(input, 7); if (getbits1_msb(input, 1) == 0) return getsigned(input, 9); if (getbits1_msb(input, 1) == 0) return getsigned(input, 12); int64_t v = getsigned(input, 32); v <<= 32; v |= getbits1_msb(input, 32); return v; } char * mystrccpy(char *dst, char *src, int c) { while ((*dst = *src)) { if (*dst == c) { *dst = 0; src++; return src; } dst++; src++; } return 0; } int64_t parsetimestamp(char *t) { int64_t ts; char *end; errno = 0; ts = strtoll(t, &end, 10); if (errno != 0 || *end) { fprintf(stderr, "can't parse unix milli timestamp: %s\n", t); exit(-1); } return ts; } void parsetimerange(char *t) { struct tm tm_from = { .tm_mday = 1 }; struct tm tm_to; /* XXX switch to iso format, treat number as raw timestamp */ switch(strlen(t)) { case 4: if (!strptime(t, "%Y", &tm_from)) goto err; tm_to = tm_from; tm_to.tm_year++; break; case 7: if (!strptime(t, "%Y-%m", &tm_from)) goto err; tm_to = tm_from; tm_to.tm_mon++; break; case 10: if (!strptime(t, "%Y-%m-%d", &tm_from)) goto err; tm_to = tm_from; tm_to.tm_mday++; break; case 13: if (!strptime(t, "%Y-%m-%dT%H", &tm_from)) goto err; tm_to = tm_from; tm_to.tm_hour++; break; case 16: if (!strptime(t, "%Y-%m-%dT%H:%M", &tm_from)) goto err; tm_to = tm_from; tm_to.tm_min++; break; case 19: if (!strptime(t, "%Y-%m-%dT%H:%M:%S", &tm_from)) goto err; tm_to = tm_from; tm_to.tm_sec++; break; default: err: fprintf(stderr, "can't recognize timestamp: %s\n", t); exit(-1); } fflag = timegm(&tm_from) * 1000; tflag = (timegm(&tm_to) * 1000) - 1; } #define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) int main(int argc, char *argv[]) { int c; while ((c = getopt(argc, argv, "f:lt:r:")) != -1) { switch (c) { case 'l': lflag = 1; break; case 'f': fflag = parsetimestamp(optarg); break; case 't': tflag = parsetimestamp(optarg); break; case 'r': parsetimerange(optarg); break; case '?': usage: fprintf(stderr, "usage: %s [-l] [-f TIME] [-t TIME] input.zip [filter...]\n", argv[0]); exit(-1); } } if (argc - optind < 1) goto usage; setenv("TZ", "", 1); tzset(); zip = zip_open(argv[optind], ZIP_RDONLY, 0); int entries = zip_get_num_entries(zip, 0); for (int i = 0; i < entries; i += 2) { struct bitreader ts = { 0 }; struct bitreader vs = { 0 }; /* XXX verify assumptions on zip file order */ ts.input = zip_fopen_index(zip, i, 0); vs.input = zip_fopen_index(zip, i+1, 0); char *name = strdup(zip_get_name(zip, i, ZIP_FL_ENC_RAW)); char *s = strchr(name, '/'); *s = '{'; s = strrchr(name, '/'); *s++ = '}'; *s = 0; int keep = 1; for (int j = optind + 1; j < argc; j++) { if (strchr(argv[j], '=')) { // filter on label char label[255]; char *s = strchr(name, '{'); if (!s) { keep = 0; break; } s++; int any = 0; do { s = mystrccpy(label, s, ','); if (fnmatch(argv[j], label, 0) == 0) { any = 1; break; } } while(s); if (!any) keep = 0; } else { // filter on name char series[255]; mystrccpy(series, name, '{'); if (fnmatch(argv[j], series, 0) == FNM_NOMATCH) { keep = 0; break; } } } if (!keep) continue; if (lflag) { printf("%s\n", name); continue; } char header[8]; uint64_t len, lenv; int ret = zip_fread(ts.input, &header, sizeof header); if (ret < 0) { printf("err: %s", zip_strerror(zip)); } if (memcmp(header, MICO_HEADER, 8) != 0) { printf("wrong header\n"); continue; } ret = zip_fread(ts.input, &len, sizeof len); if (ret < 0) { printf("err: %s", zip_strerror(zip)); } len = ntohll(len); ret = zip_fread(vs.input, &header, sizeof header); if (ret < 0) { printf("err: %s", zip_strerror(zip)); } if (memcmp(header, MICO_HEADER, 8) != 0) { printf("wrong header\n"); continue; } ret = zip_fread(vs.input, &lenv, sizeof lenv); if (ret < 0) { printf("err: %s", zip_strerror(zip)); } lenv = ntohll(lenv); if (len != lenv) { fprintf(stderr, "time and value length don't agree: %ld != %ld\n", len, lenv); exit(-1); } int64_t t = 0, td = 0; double v = 0.0; for (uint64_t j = 0; j < len; j++) { int64_t tdd = get1(&ts); int64_t vd = get1(&vs); td += tdd; t += td; v += vd; if (fflag && t < fflag) continue; if (tflag && t > tflag) break; printf("%s %f %ld\n", name, v/10000.0, t); } } }