]> git.scottworley.com Git - uniqt/blame - uniqt.c
-f to specify time format
[uniqt] / uniqt.c
CommitLineData
ccaad020 1#define _POSIX_C_SOURCE 200809L
63eb64b5
SW
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <time.h>
7
8void die(const char *message) {
9 fputs(message, stderr);
10 fputc('\n', stderr);
11 exit(1);
12}
13
14void die_err(const char *message) {
15 perror(message);
16 exit(1);
17}
18
19char *encode_time(time_t t) {
20 struct tm tm;
21 localtime_r(&t, &tm);
22 const size_t size = 20;
23 char *out = (char *)malloc(size);
24 if (strftime(out, size, "%Y %m %d %H %M %S", &tm) != size - 1)
25 die("Couldn't format time");
26 return out;
27}
28
48964c1e
SW
29typedef struct {
30 char *time_format;
31} conf_t;
32
63eb64b5
SW
33typedef struct {
34 time_t start, end;
35} time_range_t;
36
37const time_t NULL_TIME = (time_t)-1;
38
39static time_range_t make_time_range(time_t t) { return (time_range_t){t, t}; }
40
41static void extend_time_range(time_range_t *r, time_t t) { r->end = t; }
42
43static char *read_line() {
44 char *line = NULL;
45 int scanf_ret = scanf("%m[^\n]", &line);
46 if (scanf_ret == EOF && ferror(stdin))
47 die_err("Error reading");
48 int newline = getchar();
49 if (newline != EOF && newline != (int)'\n')
50 die("Expected newline");
ccaad020
SW
51 if (scanf_ret == 0 && newline == (int)'\n')
52 return strdup("\n");
63eb64b5
SW
53 return line;
54}
55
48964c1e
SW
56static void format_time(conf_t *conf, char *buf, size_t size, time_t *t) {
57 struct tm *tm = localtime(t);
58 if (tm == NULL)
59 die_err("Couldn't unpack time");
60 if (strftime(buf, size, conf->time_format, tm) == 0)
61 die_err("Couldn't format time");
62}
63
64static void write_line(conf_t *conf, time_range_t *range, char *line) {
63eb64b5
SW
65 if (line == NULL)
66 return;
48964c1e
SW
67 if (conf->time_format) {
68 const size_t MAX_TIMESTAMP_LENGTH = 1024;
69 char a[MAX_TIMESTAMP_LENGTH];
70 char b[MAX_TIMESTAMP_LENGTH];
71 format_time(conf, a, MAX_TIMESTAMP_LENGTH, &range->start);
72 format_time(conf, b, MAX_TIMESTAMP_LENGTH, &range->end);
73 if (printf("%s %s %s\n", a, b, line) < 0)
74 die("Couldn't write");
75 } else {
76 if (printf("%ld %ld %s\n", range->start, range->end, line) < 0)
77 die("Couldn't write");
78 }
be6fd520
SW
79 if (fflush(stdout) == EOF)
80 die_err("Couldn't flush");
63eb64b5
SW
81}
82
83static int same(char *a, char *b) { return a && b && strcmp(a, b) == 0; }
84
48964c1e 85static void uniqt(conf_t *conf) {
63eb64b5
SW
86 char *current_line = NULL;
87 time_range_t current_time_range = make_time_range(NULL_TIME);
88 for (;;) {
89 char *new_line = read_line();
90 if (new_line == NULL)
91 break;
92 time_t now = time(NULL);
93 if (same(current_line, new_line)) {
94 free(new_line);
95 extend_time_range(&current_time_range, now);
96 } else {
48964c1e 97 write_line(conf, &current_time_range, current_line);
63eb64b5
SW
98 free(current_line);
99 current_line = new_line;
100 current_time_range = make_time_range(now);
101 }
102 }
48964c1e 103 write_line(conf, &current_time_range, current_line);
63eb64b5
SW
104 free(current_line);
105}
106
48964c1e
SW
107void usage() { die("usage: uniqt [-f time_format]"); }
108
109conf_t parse_command_line(int argc, char *argv[]) {
110 conf_t conf;
111 conf.time_format = NULL;
112
113 for (int i = 1; i < argc; i++) {
114 if (strcmp(argv[i], "-f") == 0) {
115 if (i + 1 >= argc)
116 die("-f requires an argument");
117 i++;
118 conf.time_format = argv[i];
119 } else
120 usage();
121 }
122
123 return conf;
124}
125
126int main(int argc, char *argv[]) {
127 conf_t conf = parse_command_line(argc, argv);
128 uniqt(&conf);
63eb64b5
SW
129 return 0;
130}