-
Notifications
You must be signed in to change notification settings - Fork 6
/
tcat.c
139 lines (118 loc) · 3.12 KB
/
tcat.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
enum {
version_major = 0,
version_minor = 2,
version_patch = 1
};
typedef struct tm *(*time_format)(const time_t *, struct tm *);
static const char program_name[] = "tcat";
static const char env_flag[] = "TCAT_FORMAT";
static const char default_format[] = "%FT%T%z";
static const char* format = default_format;
static void io_error(FILE* file) {
if (feof(file)) {
fputs("file closed", stderr);
} else if (ferror(file)) {
perror("file error");
} else {
fputs("unknown error", stderr);
}
exit(EXIT_FAILURE);
}
static void print_time(time_format time_format) {
enum { buffer_max = 32 };
char buffer[buffer_max];
time_t rawtime;
time(&rawtime);
struct tm timeinfo;
time_format(&rawtime, &timeinfo);
const size_t len = strftime(buffer, buffer_max, format, &timeinfo);
if(len != fwrite(buffer, 1, len, stdout)){
io_error(stdout);
}
if (fputc('\t', stdout) == EOF) {
io_error(stdout);
}
}
static void version(FILE* output) {
fprintf(output, "%s version %d.%d.%d\n",
program_name,
version_major,
version_minor,
version_patch);
}
static inline void usage(FILE* output) {
fprintf(output,
"\n"
"Available Options:\n"
"\t-v, --version\tprint %s version\n"
"\t-h, --help\tprint this help\n"
"\t-l, --local\tuse local time instead of UTC\n"
"\t-f, --format\tset time format string in strftime syntax (default: \"%s\")\n"
"\n"
"Help can be found at https://github.com/marcomorain/tcat\n",
program_name, default_format);
}
static int match(const char* x, const char* a, const char* b) {
return (!strcmp(x, a)) || (!strcmp(x, b));
}
int main(int argc, char** argv) {
int bad_usage = 0;
char *format_env = getenv(env_flag);
if (format_env) {
format = format_env;
}
time_format time_format = gmtime_r;
for (int i = 1; i < argc; i++) {
if (match(argv[i], "-v", "--version")) {
version(stdout);
return EXIT_SUCCESS;
}
if (match(argv[i], "-h", "--help")) {
usage(stdout);
return EXIT_SUCCESS;
}
if (match(argv[i], "-l", "--local")) {
time_format = localtime_r;
continue;
}
if (match(argv[i], "-f", "--format")) {
if (++i >= argc) {
fprintf(stderr, "Error: no format string given provided for %s option\n", argv[i-1]);
return EXIT_FAILURE;
}
format = argv[i];
continue;
}
// Show all bad options
bad_usage = 1;
fprintf(stderr, "Invalid option:\t%s\n", argv[i]);
}
// Show correct usage
if (bad_usage) {
usage(stderr);
return EXIT_FAILURE;
}
if (isatty(fileno(stdin))) {
fprintf(stderr, "Warning: input is from TTY\n");
}
int last = '\n';
for(int c = fgetc(stdin); c != EOF; c = fgetc(stdin)) {
if (last == '\n') {
print_time(time_format);
}
if (EOF == fputc(c, stdout)) {
io_error(stdout);
}
last = c;
}
if (ferror(stdin)) {
perror("output error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}