ch09/csvcount.c40 lines
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "usage: %s file.csv\n", argv[0]);
return 2;
}
FILE *f = fopen(argv[1], "r");
if (f == NULL) {
perror(argv[1]);
return 1;
}
char line[128];
size_t lineno = 0, good = 0;
while (fgets(line, sizeof line, f) != NULL) {
lineno++;
if (strchr(line, '\n') == NULL && !feof(f)) {
fprintf(stderr, "line %zu: too long\n", lineno);
fclose(f);
return 1;
}
unsigned id, flags; long long t; double v;
int n = sscanf(line, "%u,%lld,%lf,%u",
&id, &t, &v, &flags);
if (n != 4) {
fprintf(stderr, "line %zu: bad: %s", lineno, line);
continue;
}
good++;
}
int failed = ferror(f); /* end of file, or a failure? */
fclose(f);
if (failed) {
fprintf(stderr, "%s: read error\n", argv[1]);
return 1;
}
printf("%zu lines, %zu good records\n", lineno, good);
return 0;
}
ch09/day.csv5 lines
1001,60,21.75,3
1002,60,64.5,3
1003,60,1013.25,1
1004,60,3.5
1001,120,21.5,3
ch09/db_demo.c40 lines
#include <stdio.h>
#include "sensor_db.h"
#include "stn_log.h"
struct frost_ctx { long minutes; double coldest; };
static void count_frost(const struct reading *r, void *ctx) {
struct frost_ctx *fc = ctx; /* we know what we passed */
if (r->sensor_id % 1000 != 1) return; /* temperature */
if (r->value < 2.0) fc->minutes++;
if (r->value < fc->coldest) fc->coldest = r->value;
}
int main(int argc, char *argv[]) {
if (argc != 2) return 2;
FILE *f = fopen(argv[1], "rb");
if (f == NULL) { perror(argv[1]); return 1; }
struct stn_db *db = stn_db_create(argv[1], 16);
uint32_t n = 0;
enum stn_log_status st = db ? stn_log_read_header(f, &n)
: STN_LOG_IO_ERROR;
struct reading r;
for (uint32_t i = 0; st == STN_LOG_OK && i < n; i++) {
st = stn_log_read_record(f, &r);
if (st == STN_LOG_OK && stn_db_add(db, &r) != 0)
st = STN_LOG_IO_ERROR;
}
fclose(f);
if (st != STN_LOG_OK) {
fprintf(stderr, "%s: %s\n", argv[1], stn_log_status_name(st));
stn_db_destroy(db);
return 1;
}
struct frost_ctx fc = { 0, 1e9 };
stn_db_for_each(db, count_frost, &fc);
printf("%zu readings; frost minutes %ld; coldest %.2f C\n",
stn_db_count(db), fc.minutes, fc.coldest);
stn_db_destroy(db);
return 0;
}
ch09/endian.c21 lines
#include <stdint.h>
#include <stdio.h>
static void show(const char *label, const void *p, size_t n) {
const unsigned char *b = p;
printf("%-10s", label);
for (size_t i = 0; i < n; i++) {
printf(" %02x", (unsigned)b[i]);
}
putchar('\n');
}
int main(void) {
uint32_t count = 1440; /* 0x000005a0 */
int64_t t = -60;
double v = 21.75;
show("count", &count, sizeof count);
show("time_s", &t, sizeof t);
show("value", &v, sizeof v);
return 0;
}
ch09/escape.c18 lines
#include <stdio.h>
#include "sensor_db.h"
const struct reading *stn_db_items(const struct stn_db *db);
int main(void) {
struct stn_db *db = stn_db_create("escape", 16);
if (db == NULL) return 1;
struct reading r = { 1001, 60, 21.75, STN_FLAG_VALID };
for (int i = 0; i < 16; i++) {
if (stn_db_add(db, &r) != 0) return 1;
}
const struct reading *items = stn_db_items(db);
if (stn_db_add(db, &r) != 0) return 1; /* 17th: grows */
printf("first value %.2f\n", items[0].value);
stn_db_destroy(db);
return 0;
}
ch09/flags.c26 lines
#include <stdint.h>
#include <stdio.h>
#include "station.h"
static void print_bits(const char *label, uint8_t x) {
printf("%-14s 0x%02x ", label, (unsigned)x);
for (int bit = 7; bit >= 0; bit--) {
putchar((x >> bit) & 1u ? '1' : '0');
}
putchar('\n');
}
int main(void) {
uint8_t flags = 0;
flags |= STN_FLAG_VALID | STN_FLAG_CALIBRATED; /* set */
print_bits("set V,C", flags);
flags |= STN_FLAG_LOW_BATTERY; /* set */
print_bits("set LOW_BAT", flags);
flags &= (uint8_t)~STN_FLAG_CALIBRATED; /* clear */
print_bits("clear CAL", flags);
flags ^= STN_FLAG_VALID; /* toggle */
print_bits("toggle VALID", flags);
int low = (flags & STN_FLAG_LOW_BATTERY) != 0; /* test */
printf("low battery? %d\n", low);
return 0;
}
ch09/fp.c24 lines
#include <stdio.h>
static double c_to_f(double c) { return c * 9.0 / 5.0 + 32.0; }
static double c_to_k(double c) { return c + 273.15; }
/* apply calls whatever conversion it is handed: a callback. */
static void apply(double (*conv)(double),
const double *xs, size_t n) {
for (size_t i = 0; i < n; i++) {
printf(" %.2f", conv(xs[i]));
}
putchar('\n');
}
int main(void) {
double (*fp)(double) = c_to_f; /* no & needed */
printf("fp(21.75) = %.2f\n", fp(21.75));
printf("fp == c_to_f? %d\n", fp == c_to_f);
typedef double (*converter)(double);
converter table[2] = { c_to_f, c_to_k };
const double temps[3] = { -5.0, 0.0, 21.75 };
for (size_t i = 0; i < 2; i++) apply(table[i], temps, 3);
return 0;
}
ch09/mklog.c34 lines
#include <stdio.h>
#include <stdlib.h>
#include "stn_log.h"
/* mklog FILE MINUTES: one reading per minute from each of
the four sensors 1001..1004. */
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "usage: %s file minutes\n", argv[0]);
return 2;
}
long minutes = strtol(argv[2], NULL, 10);
if (minutes < 1 || minutes > 100000) {
fprintf(stderr, "minutes must be 1..100000\n");
return 2;
}
FILE *f = fopen(argv[1], "wb");
if (f == NULL) { perror(argv[1]); return 1; }
static const double base[4] = {3.0, 91.0, 1013.25, 3.5};
uint32_t n = (uint32_t)(4 * minutes);
int ok = stn_log_write_header(f, n) == STN_LOG_OK;
for (long m = 1; ok && m <= minutes; m++) {
for (uint32_t k = 0; ok && k < 4; k++) {
double v = base[k] - 0.25 * (double)(m % 8);
struct reading r = { 1001 + k, 60 * m, v,
STN_FLAG_VALID | STN_FLAG_CALIBRATED };
ok = stn_log_write_record(f, &r) == STN_LOG_OK;
}
}
if (!ok) { perror("write"); fclose(f); return 1; }
if (fclose(f) != 0) { perror("close"); return 1; }
printf("wrote %u records to %s\n", (unsigned)n, argv[1]);
return 0;
}
ch09/pack.c32 lines
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "station.h"
/* Pack four booleans into the station's flag byte. */
uint8_t stn_pack_flags(bool valid, bool calibrated,
bool low_battery, bool out_of_range) {
uint8_t f = 0;
if (valid) f |= STN_FLAG_VALID;
if (calibrated) f |= STN_FLAG_CALIBRATED;
if (low_battery) f |= STN_FLAG_LOW_BATTERY;
if (out_of_range) f |= STN_FLAG_OUT_OF_RANGE;
return f;
}
/* Raw ADC word: bits 15..12 channel, bits 11..0 sample. */
uint16_t adc_pack(unsigned channel, unsigned sample) {
unsigned w = ((channel & 0xFu) << 12) | (sample & 0xFFFu);
return (uint16_t)w;
}
unsigned adc_channel(uint16_t w) { return (w >> 12) & 0xFu; }
unsigned adc_sample(uint16_t w) { return w & 0xFFFu; }
int main(void) {
printf("flags = 0x%02x\n",
(unsigned)stn_pack_flags(true, false, true, false));
uint16_t w = adc_pack(3, 2748);
printf("word = 0x%04x channel = %u sample = %u\n",
(unsigned)w, adc_channel(w), adc_sample(w));
return 0;
}
ch09/poke.c10 lines
#include <stdio.h>
#include "sensor_db.h"
int main(void) {
struct stn_db *db = stn_db_create("north-field", 8);
printf("%zu\n", db->count); /* reach behind the wall */
struct stn_db copy; /* make one on the stack */
stn_db_destroy(db);
return 0;
}
ch09/predict.c16 lines
#include <stdint.h>
#include <stdio.h>
#include "station.h"
int main(void) {
uint8_t f = 0x0B; /* VALID | CALIBRATED | OUT_OF_RANGE */
printf("a) %#04x\n", (unsigned)(f & STN_FLAG_CALIBRATED));
printf("b) %#04x\n", (unsigned)(f | STN_FLAG_LOW_BATTERY));
printf("c) %#04x\n", (unsigned)(f ^ 0x0Fu));
printf("d) %#04x\n", (unsigned)(f >> 1));
printf("e) %d\n", f << 5);
printf("f) %d\n", (uint8_t)(f << 5));
printf("g) %d\n", ~f);
printf("h) %d\n", (f & STN_FLAG_LOW_BATTERY) != 0);
return 0;
}
ch09/reader_bad.c42 lines
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "station.h"
static void show(const struct reading *r) {
printf("%u t=%lld v=%.2f\n", (unsigned)r->sensor_id,
(long long)r->time_s, r->value);
}
static void (*const handlers[4])(const struct reading *) = {
show, show, show, show };
static uint32_t u32(const unsigned char *b) {
return (uint32_t)b[0] | (uint32_t)b[1] << 8 |
(uint32_t)b[2] << 16 | (uint32_t)b[3] << 24;
}
static uint64_t u64(const unsigned char *b) {
return u32(b) | (uint64_t)u32(b + 4) << 32;
}
int main(int argc, char *argv[]) {
if (argc < 2) return 1;
FILE *f = fopen(argv[1], "rb");
unsigned char hdr[8];
fread(hdr, 1, 8, f);
uint32_t count = u32(hdr + 4);
unsigned char *buf = malloc((size_t)count * 21);
fread(buf, 21, count, f);
for (uint32_t i = 0; i < count; i++) {
const unsigned char *b = buf + (size_t)i * 21;
uint64_t vb = u64(b + 12);
double v;
memcpy(&v, &vb, sizeof v);
struct reading r = { u32(b), (int64_t)u64(b + 4),
v, b[20] };
handlers[r.sensor_id % 1000 - 1](&r);
}
free(buf);
fclose(f);
return 0;
}
ch09/sensor_db.c87 lines
/* sensor_db.c (Chapter 9): Chapter 7's file with growth by doubling
* and stn_db_for_each. Still the only file that knows the fields. */
#include <stdlib.h>
#include <string.h>
#include "sensor_db.h"
struct stn_db {
char *name; /* owned: heap copy of the name */
struct reading *rows; /* owned: capacity slots */
size_t count; /* slots in use */
size_t capacity;
};
struct stn_db *stn_db_create(const char *name, size_t capacity) {
if (capacity == 0) {
return NULL;
}
struct stn_db *db = malloc(sizeof *db);
if (db == NULL) {
return NULL;
}
db->name = malloc(strlen(name) + 1);
db->rows = calloc(capacity, sizeof *db->rows);
if (db->name == NULL || db->rows == NULL) {
free(db->name); /* free(NULL) is allowed and does nothing */
free(db->rows);
free(db);
return NULL;
}
strcpy(db->name, name);
db->count = 0;
db->capacity = capacity;
return db;
}
void stn_db_destroy(struct stn_db *db) {
if (db != NULL) {
free(db->rows);
free(db->name);
free(db);
}
}
int stn_db_add(struct stn_db *db, const struct reading *r) {
if (db->count == db->capacity) { /* full: double */
size_t cap = 2 * db->capacity;
struct reading *p = realloc(db->rows, cap * sizeof *p);
if (p == NULL) return -1; /* db->rows untouched */
db->rows = p;
db->capacity = cap;
}
db->rows[db->count++] = *r; /* struct assignment */
return 0;
}
size_t stn_db_count(const struct stn_db *db) {
return db->count;
}
const struct reading *stn_db_get(const struct stn_db *db, size_t i) {
return i < db->count ? &db->rows[i] : NULL;
}
const char *stn_db_name(const struct stn_db *db) {
return db->name;
}
double stn_db_mean(const struct stn_db *db, uint32_t sensor_id,
size_t *n_out) {
double sum = 0.0;
size_t n = 0;
for (size_t i = 0; i < db->count; i++) {
const struct reading *r = &db->rows[i];
if (r->sensor_id == sensor_id && (r->flags & STN_FLAG_VALID)) {
sum += r->value;
n++;
}
}
*n_out = n;
return n > 0 ? sum / (double)n : 0.0;
}
void stn_db_for_each(const struct stn_db *db,
stn_db_visit visit, void *ctx) {
for (size_t i = 0; i < db->count; i++) {
visit(&db->rows[i], ctx);
}
}
ch09/sensor_db.h51 lines
/* sensor_db.h (Chapter 9): Chapter 7's interface, now growable.
* Same opaque type and functions; one new operation. */
#ifndef SENSOR_DB_H
#define SENSOR_DB_H
#include <stddef.h>
#include "station.h"
struct stn_db; /* declared here; defined in sensor_db.c */
/* OWNERSHIP RULES (the type system cannot say these; we must)
* 1. stn_db_create returns a new db, or NULL if out of
* memory or capacity is 0. The caller owns it and calls
* stn_db_destroy exactly once. capacity is only a
* starting size: the db grows.
* 2. The db stores its own copy of name. The caller still
* owns the string it passed and may change or free it
* afterward.
* 3. stn_db_add copies *r into the db and keeps no pointer
* to r.
* 4. stn_db_get and stn_db_name return BORROWED pointers
* into the db. Never free them. They dangle after
* stn_db_destroy. A pointer from stn_db_get also dangles
* after the next stn_db_add, because growth may move the
* rows.
* 5. stn_db_for_each passes visit a BORROWED pointer, valid
* only during that call.
*/
struct stn_db *stn_db_create(const char *name, size_t capacity);
void stn_db_destroy(struct stn_db *db); /* NULL: no-op */
/* 0 on success, -1 if out of memory (db unchanged). */
int stn_db_add(struct stn_db *db, const struct reading *r);
size_t stn_db_count(const struct stn_db *db);
const struct reading *stn_db_get(const struct stn_db *db,
size_t i);
/* NULL if i >= count */
const char *stn_db_name(const struct stn_db *db);
/* Mean of VALID readings from sensor_id; stores how many
* in *n_out. Returns 0.0 when *n_out is 0. */
double stn_db_mean(const struct stn_db *db, uint32_t sensor_id,
size_t *n_out);
/* Calls visit(r, ctx) once per reading, in insertion order. */
typedef void (*stn_db_visit)(const struct reading *r,
void *ctx);
void stn_db_for_each(const struct stn_db *db,
stn_db_visit visit, void *ctx);
#endif /* SENSOR_DB_H */
ch09/sensor_db_leaky.c92 lines
/* sensor_db.c (Chapter 9): Chapter 7's file with growth by doubling
* and stn_db_for_each. Still the only file that knows the fields. */
#include <stdlib.h>
#include <string.h>
#include "sensor_db.h"
struct stn_db {
char *name; /* owned: heap copy of the name */
struct reading *rows; /* owned: capacity slots */
size_t count; /* slots in use */
size_t capacity;
};
struct stn_db *stn_db_create(const char *name, size_t capacity) {
if (capacity == 0) {
return NULL;
}
struct stn_db *db = malloc(sizeof *db);
if (db == NULL) {
return NULL;
}
db->name = malloc(strlen(name) + 1);
db->rows = calloc(capacity, sizeof *db->rows);
if (db->name == NULL || db->rows == NULL) {
free(db->name); /* free(NULL) is allowed and does nothing */
free(db->rows);
free(db);
return NULL;
}
strcpy(db->name, name);
db->count = 0;
db->capacity = capacity;
return db;
}
void stn_db_destroy(struct stn_db *db) {
if (db != NULL) {
free(db->rows);
free(db->name);
free(db);
}
}
int stn_db_add(struct stn_db *db, const struct reading *r) {
if (db->count == db->capacity) { /* full: double */
size_t cap = 2 * db->capacity;
struct reading *p = realloc(db->rows, cap * sizeof *p);
if (p == NULL) return -1; /* db->rows untouched */
db->rows = p;
db->capacity = cap;
}
db->rows[db->count++] = *r; /* struct assignment */
return 0;
}
size_t stn_db_count(const struct stn_db *db) {
return db->count;
}
const struct reading *stn_db_get(const struct stn_db *db, size_t i) {
return i < db->count ? &db->rows[i] : NULL;
}
const char *stn_db_name(const struct stn_db *db) {
return db->name;
}
double stn_db_mean(const struct stn_db *db, uint32_t sensor_id,
size_t *n_out) {
double sum = 0.0;
size_t n = 0;
for (size_t i = 0; i < db->count; i++) {
const struct reading *r = &db->rows[i];
if (r->sensor_id == sensor_id && (r->flags & STN_FLAG_VALID)) {
sum += r->value;
n++;
}
}
*n_out = n;
return n > 0 ? sum / (double)n : 0.0;
}
void stn_db_for_each(const struct stn_db *db,
stn_db_visit visit, void *ctx) {
for (size_t i = 0; i < db->count; i++) {
visit(&db->rows[i], ctx);
}
}
/* Agent-added accessor (illustrative): hands out the array. */
const struct reading *stn_db_items(const struct stn_db *db) {
return db->rows;
}
ch09/shift2.c7 lines
#include <stdio.h>
static unsigned bit(unsigned n) { return 1u << n; }
int main(void) {
volatile unsigned k = 32;
printf("%u %u\n", bit(32), bit(k));
return 0;
}
ch09/shiftbug.c22 lines
#include <inttypes.h>
#include <stdio.h>
/* Agent-written decoder (illustrative). */
static uint64_t get_u64le_agent(const unsigned char *b) {
uint64_t x = 0;
for (int i = 0; i < 8; i++) {
x |= b[i] << (8 * i);
}
return x;
}
int main(void) {
/* 86400 s (one day) and 5000000000 s (about 158 years) */
const unsigned char day[8] = {0x80, 0x51, 0x01, 0,
0, 0, 0, 0};
const unsigned char big[8] = {0x00, 0xf2, 0x05, 0x2a,
0x01, 0, 0, 0};
printf("day: %" PRIu64 "\n", get_u64le_agent(day));
printf("big: %" PRIu64 "\n", get_u64le_agent(big));
return 0;
}
ch09/shortread.c24 lines
#include <stdint.h>
#include <stdio.h>
int main(void) {
FILE *f = fopen("ten.bin", "wb");
if (f == NULL) { perror("ten.bin"); return 1; }
const unsigned char bytes[10] = {1, 2, 3, 4, 5,
6, 7, 8, 9, 10};
if (fwrite(bytes, 1, sizeof bytes, f) != sizeof bytes) {
perror("write"); fclose(f); return 1;
}
if (fclose(f) != 0) { perror("close"); return 1; }
f = fopen("ten.bin", "rb"); /* holds bytes 1..10 */
if (f == NULL) { perror("ten.bin"); return 1; }
uint32_t words[4] = {0};
size_t got = fread(words, sizeof words[0], 4, f);
printf("asked for 4 items, got %zu\n", got);
printf("feof=%d ferror=%d\n", feof(f) != 0, ferror(f) != 0);
printf("words[2]=0x%08x words[3]=0x%08x\n",
(unsigned)words[2], (unsigned)words[3]);
fclose(f);
return 0;
}
ch09/sort_time.c40 lines
#include <stdio.h>
#include <stdlib.h>
#include "station.h"
/* qsort comparator: negative, zero or positive, like strcmp. */
static int by_time(const void *pa, const void *pb) {
const struct reading *a = pa;
const struct reading *b = pb;
return (a->time_s > b->time_s) - (a->time_s < b->time_s);
}
static int by_id_then_time(const void *pa, const void *pb) {
const struct reading *a = pa;
const struct reading *b = pb;
if (a->sensor_id != b->sensor_id) {
return (a->sensor_id > b->sensor_id) ? 1 : -1;
}
return by_time(pa, pb);
}
static void print_all(const struct reading *r, size_t n) {
for (size_t i = 0; i < n; i++) {
printf(" %u %lld %.2f\n", (unsigned)r[i].sensor_id,
(long long)r[i].time_s, r[i].value);
}
}
int main(void) {
struct reading rs[4] = {
{1002, 120, 64.0, 3}, {1001, 60, 21.75, 3},
{1001, 120, 21.5, 3}, {1002, 60, 64.5, 3},
};
qsort(rs, 4, sizeof rs[0], by_time);
puts("by time:");
print_all(rs, 4);
qsort(rs, 4, sizeof rs[0], by_id_then_time);
puts("by id, then time:");
print_all(rs, 4);
return 0;
}
ch09/station.h29 lines
/* station.h: types shared by every station module.
* Types and constants only: no variables, no functions. */
#ifndef STATION_H
#define STATION_H
#include <stdint.h>
enum sensor_kind { SENSOR_TEMP, SENSOR_HUMIDITY,
SENSOR_PRESSURE, SENSOR_WIND };
#define STN_FLAG_VALID 0x01
#define STN_FLAG_CALIBRATED 0x02
#define STN_FLAG_LOW_BATTERY 0x04
#define STN_FLAG_OUT_OF_RANGE 0x08
/* One sample from one sensor.
* OWNERSHIP: a plain value with no pointers inside. Copy it
* with =; there is nothing to free.
* LAYOUT: chosen by the compiler and the target, padding
* included. Never fwrite the struct itself; write the
* fields one at a time. */
struct reading {
uint32_t sensor_id; /* e.g. 1001 */
int64_t time_s; /* seconds since station start */
double value; /* in the sensor's unit */
uint8_t flags; /* STN_FLAG_* bits */
};
#endif /* STATION_H */
ch09/station_read.c94 lines
#include <stdio.h>
#include "stn_log.h"
/* station.h has no count of sensor kinds, so name one here. */
#define KIND_COUNT (SENSOR_WIND + 1)
struct day_summary {
long frost_minutes; /* temperature below 2.0 C */
long humid_minutes; /* humidity above 90 % */
long pressure_n; /* pressure readings seen */
double min_pressure; /* hPa, valid if pressure_n > 0 */
double max_wind; /* m/s */
};
typedef void (*reading_handler)(const struct reading *r,
struct day_summary *s);
static void on_temp(const struct reading *r,
struct day_summary *s) {
if (r->value < 2.0) s->frost_minutes++;
}
static void on_humidity(const struct reading *r,
struct day_summary *s) {
if (r->value > 90.0) s->humid_minutes++;
}
static void on_pressure(const struct reading *r,
struct day_summary *s) {
if (s->pressure_n++ == 0 || r->value < s->min_pressure)
s->min_pressure = r->value;
}
static void on_wind(const struct reading *r,
struct day_summary *s) {
if (r->value > s->max_wind) s->max_wind = r->value;
}
static const reading_handler handlers[KIND_COUNT] = {
[SENSOR_TEMP] = on_temp,
[SENSOR_HUMIDITY] = on_humidity,
[SENSOR_PRESSURE] = on_pressure,
[SENSOR_WIND] = on_wind,
};
/* 1001 -> SENSOR_TEMP ... 1004 -> SENSOR_WIND; -1 if unknown */
static int kind_of(uint32_t sensor_id) {
uint32_t k = sensor_id % 1000;
if (k < 1 || k > KIND_COUNT) return -1;
return (int)(k - 1);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "usage: %s log\n", argv[0]);
return 2;
}
FILE *f = fopen(argv[1], "rb");
if (f == NULL) { perror(argv[1]); return 1; }
uint32_t count = 0;
enum stn_log_status st = stn_log_read_header(f, &count);
if (st != STN_LOG_OK) {
fprintf(stderr, "%s: header: %s\n", argv[1],
stn_log_status_name(st));
fclose(f);
return 1;
}
struct day_summary sum = {0, 0, 0, 0.0, 0.0};
uint32_t n = 0, skipped = 0;
struct reading r;
while (n < count) {
st = stn_log_read_record(f, &r);
if (st != STN_LOG_OK) break;
n++;
int k = kind_of(r.sensor_id);
if (k < 0 || !(r.flags & STN_FLAG_VALID)) {
skipped++;
continue;
}
handlers[k](&r, &sum);
}
fclose(f);
printf("records: %u of %u (skipped %u)\n",
(unsigned)n, (unsigned)count, (unsigned)skipped);
printf("frost min: %ld humid min: %ld\n",
sum.frost_minutes, sum.humid_minutes);
if (sum.pressure_n > 0)
printf("min pressure: %.2f hPa ", sum.min_pressure);
printf("max wind: %.2f m/s\n", sum.max_wind);
if (n < count) {
fprintf(stderr, "%s: stopped after record %u of %u: %s\n",
argv[1], (unsigned)n, (unsigned)count,
stn_log_status_name(st));
return 1;
}
return 0;
}
ch09/stn_log.c94 lines
#include "stn_log.h"
#include <string.h>
static void put_u32le(unsigned char *b, uint32_t x) {
for (int i = 0; i < 4; i++) {
b[i] = (unsigned char)(x >> (8 * i)); /* low byte 1st */
}
}
static void put_u64le(unsigned char *b, uint64_t x) {
for (int i = 0; i < 8; i++) {
b[i] = (unsigned char)(x >> (8 * i));
}
}
static uint32_t get_u32le(const unsigned char *b) {
uint32_t x = 0;
for (int i = 0; i < 4; i++) {
x |= (uint32_t)b[i] << (8 * i); /* widen, then shift */
}
return x;
}
static uint64_t get_u64le(const unsigned char *b) {
uint64_t x = 0;
for (int i = 0; i < 8; i++) {
x |= (uint64_t)b[i] << (8 * i);
}
return x;
}
enum stn_log_status stn_log_write_header(FILE *f, uint32_t n) {
unsigned char b[STN_LOG_HEADER_SIZE];
memcpy(b, "STN1", 4);
put_u32le(b + 4, n);
if (fwrite(b, 1, sizeof b, f) != sizeof b) {
return STN_LOG_IO_ERROR;
}
return STN_LOG_OK;
}
enum stn_log_status stn_log_write_record(FILE *f,
const struct reading *r) {
unsigned char b[STN_LOG_RECORD_SIZE];
uint64_t tbits, vbits;
memcpy(&tbits, &r->time_s, sizeof tbits);
memcpy(&vbits, &r->value, sizeof vbits);
put_u32le(b + 0, r->sensor_id);
put_u64le(b + 4, tbits);
put_u64le(b + 12, vbits);
b[20] = r->flags;
if (fwrite(b, 1, sizeof b, f) != sizeof b) {
return STN_LOG_IO_ERROR;
}
return STN_LOG_OK;
}
enum stn_log_status stn_log_read_header(FILE *f, uint32_t *n) {
unsigned char b[STN_LOG_HEADER_SIZE];
size_t got = fread(b, 1, sizeof b, f);
if (got != sizeof b) {
return ferror(f) ? STN_LOG_IO_ERROR : STN_LOG_TRUNCATED;
}
if (memcmp(b, "STN1", 4) != 0) {
return STN_LOG_BAD_MAGIC;
}
*n = get_u32le(b + 4);
return STN_LOG_OK;
}
enum stn_log_status stn_log_read_record(FILE *f,
struct reading *out) {
unsigned char b[STN_LOG_RECORD_SIZE];
size_t got = fread(b, 1, sizeof b, f);
if (got != sizeof b) {
if (ferror(f)) return STN_LOG_IO_ERROR;
return got == 0 ? STN_LOG_END : STN_LOG_TRUNCATED;
}
uint64_t tbits = get_u64le(b + 4);
uint64_t vbits = get_u64le(b + 12);
out->sensor_id = get_u32le(b + 0);
memcpy(&out->time_s, &tbits, sizeof tbits);
memcpy(&out->value, &vbits, sizeof vbits);
out->flags = b[20];
return STN_LOG_OK;
}
const char *stn_log_status_name(enum stn_log_status s) {
switch (s) {
case STN_LOG_OK: return "ok";
case STN_LOG_END: return "end of file";
case STN_LOG_TRUNCATED: return "truncated";
case STN_LOG_BAD_MAGIC: return "bad magic";
case STN_LOG_IO_ERROR: return "I/O error";
}
return "unknown status";
}
ch09/stn_log.h28 lines
#ifndef STN_LOG_H
#define STN_LOG_H
#include <stdint.h>
#include <stdio.h>
#include "station.h"
#define STN_LOG_HEADER_SIZE 8 /* "STN1" + uint32 count */
#define STN_LOG_RECORD_SIZE 21 /* 4 + 8 + 8 + 1 bytes */
enum stn_log_status {
STN_LOG_OK, /* one complete item read or written */
STN_LOG_END, /* no bytes left: clean end of file */
STN_LOG_TRUNCATED, /* some bytes, but fewer than needed */
STN_LOG_BAD_MAGIC, /* not a station log */
STN_LOG_IO_ERROR /* the stream reported an error */
};
/* Each returns STN_LOG_OK on success. The caller owns f,
opened in binary mode, and closes it. */
enum stn_log_status stn_log_write_header(FILE *f, uint32_t n);
enum stn_log_status stn_log_write_record(FILE *f,
const struct reading *r);
enum stn_log_status stn_log_read_header(FILE *f, uint32_t *n);
enum stn_log_status stn_log_read_record(FILE *f,
struct reading *out);
const char *stn_log_status_name(enum stn_log_status s);
#endif
ch09/tocsv.c12 lines
#include <stdio.h>
#include "stn_log.h"
int main(int argc, char *argv[]) {
if (argc != 2) return 2;
FILE *f = fopen(argv[1], "rb");
if (!f) { perror(argv[1]); return 1; }
uint32_t n; struct reading r;
if (stn_log_read_header(f, &n) != STN_LOG_OK) return 1;
for (uint32_t i = 0; i < n && stn_log_read_record(f, &r) == STN_LOG_OK; i++)
printf("%u,%lld,%.2f,%u\n", (unsigned)r.sensor_id, (long long)r.time_s, r.value, (unsigned)r.flags);
fclose(f); return 0;
}