Última actividad 1719877040

This program will print the content of a file and then waits for a change of the file and prints the added content.

adridoesthings's Avatar adridoesthings revisó este gist 1719877040. Ir a la revisión

1 file changed, 239 insertions

follow.c(archivo creado)

@@ -0,0 +1,239 @@
1 + /*
2 + * This program will print the content of a file and then waits for a change of the file and prints the added content.
3 + * Author: AdriDoesThings <adri@adridoesthings.com>
4 + */
5 +
6 + #include <stdint.h>
7 + #include <stdio.h>
8 + #include <stdlib.h>
9 + #include <string.h>
10 + #include <unistd.h>
11 + #include <fcntl.h>
12 + #include <sys/inotify.h>
13 + #include <signal.h>
14 +
15 + #define BUFFER_SIZE 255
16 +
17 + int fd;
18 + int ifd;
19 + int wfd;
20 +
21 + typedef struct {
22 + char* file;
23 + int skip_n_line; // -1 if all lines should be skipped
24 + } args_t;
25 +
26 + void print_usage(FILE* file, char* exec) {
27 + fprintf(file, "Usage: %s [OPTIONS] FILE\nPrint the output of a file and print the new content when new content is added to the file.\n\nOptions:\n\t-s, --skip-lines\tSkip the first n lines. Set this value to 'a' to skip all lines while first printing.\n\t-h, --help\tPrint this help.\n", exec);
28 + }
29 +
30 + /*
31 + * Parse arguments (argc long string list in argv) to args
32 + * Returns:
33 + * - 1: success but the program should exit with EXIT_SUCCESS right now
34 + * - 0: success, args contains the arguments
35 + * - -1: error, program should exit with EXIT_FAILURE now
36 + */
37 + int parse_args(args_t *args, int argc, char **argv) {
38 + args->file = NULL;
39 + args->skip_n_line = 0;
40 +
41 + for (size_t i=1; i < argc; i++) { // start at i=1, first argument is executable
42 + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
43 + print_usage(stdout, argv[0]);
44 + return 1;
45 + } else {
46 + // -s <N>, --skip-lines <N>, -s=<N> and --skip-lines=<N> syntax is allowed
47 + // if N is 'a' then args->skip_n_lines should be -1
48 + int s_cmp = strncmp(argv[i], "-s", 2);
49 + int sl_cmp = strncmp(argv[i], "--skip-lines", 12);
50 + if (s_cmp == 0 && strlen(argv[i]) > 3) { // -s=<N> syntax
51 + if (argv[i][2] != '=') {
52 + print_usage(stderr, argv[0]);
53 + return -1;
54 + }
55 + if (argv[i][3] == 'a') {
56 + args->skip_n_line = -1;
57 + continue;
58 + }
59 + args->skip_n_line = atoi(&argv[i][3]);
60 + if (args->skip_n_line == 0) {
61 + print_usage(stderr, argv[0]);
62 + return -1;
63 + }
64 + } else if (sl_cmp == 0 && strlen(argv[i]) > 13) { // --skip-lines=<N> syntax
65 + if (argv[i][12] != '=') {
66 + print_usage(stderr, argv[0]);
67 + return -1;
68 + }
69 + if (argv[i][13] == 'a') {
70 + args->skip_n_line = -1;
71 + continue;
72 + }
73 + args->skip_n_line = atoi(&argv[i][13]);
74 + if (args->skip_n_line == 0) {
75 + print_usage(stderr, argv[0]);
76 + return -1;
77 + }
78 + } else if (s_cmp == 0 || sl_cmp == 0) { // not '=' syntax
79 + i++; // go to next argument
80 + if (i == argc) { // there isn't a next argument
81 + print_usage(stderr, argv[0]);
82 + return -1;
83 + }
84 + if (argv[i][0] == 'a') {
85 + args->skip_n_line = -1;
86 + continue;
87 + }
88 + args->skip_n_line = atoi(argv[i]);
89 + if (args->skip_n_line == 0) {
90 + print_usage(stderr, argv[0]);
91 + return -1;
92 + }
93 + } else {
94 + args->file = argv[i];
95 + }
96 + }
97 + }
98 +
99 + // file is a required argument
100 + if (args->file == NULL) {
101 + print_usage(stderr, argv[0]);
102 + return -1;
103 + }
104 +
105 + return 0;
106 + }
107 +
108 + /*
109 + * print the content of the file of fd, but skip the first skip_n_line lines
110 + */
111 + void print_file_content(int skip_n_line) {
112 + while (1) { // read in 255 byte chuns
113 + u_int8_t buffer[sizeof(char) * BUFFER_SIZE];
114 + ssize_t n = read(fd, buffer, sizeof(char) * BUFFER_SIZE);
115 +
116 + if (n == 0) { // there is nothing to read anymore
117 + break;
118 + }
119 +
120 + if (n < 0) {
121 + perror("Error while reading file");
122 + close(fd);
123 + close(ifd);
124 + exit(EXIT_FAILURE);
125 + }
126 +
127 + // pointer should be mutable
128 + uint8_t *write_buffer = buffer;
129 +
130 + // there are lines to skip and data to read (n > 0)
131 + while (skip_n_line > 0 && n > 0) {
132 + size_t i;
133 + for (i=0; i < n; i++) {
134 + if (((char*) write_buffer)[i] == '\n') {
135 + break;
136 + }
137 + }
138 + // i contains the index of the next '\n' or n if there is no newline
139 +
140 + // there is no newline, but lines should be skipped (skip_n_line > 0) so the next chunk should be read
141 + if (i == n) {
142 + break;
143 + }
144 +
145 + // skipped i characters and 1 for the newline
146 + n -= i + 1;
147 + write_buffer = &write_buffer[i + 1];
148 +
149 + // skipped one line
150 + skip_n_line--;
151 +
152 + }
153 +
154 + // lines should be skipped or no data available anymore, go to read of the next chunk
155 + if (skip_n_line > 0 || n == 0) {
156 + continue;
157 + }
158 +
159 +
160 + if (write(STDOUT_FILENO, write_buffer, n) <= 0) {
161 + perror("Error while writing to stdout");
162 + close(fd);
163 + close(ifd);
164 + exit(EXIT_FAILURE);
165 + }
166 + }
167 + }
168 +
169 +
170 + void sighandler(int arg) {
171 + close(fd);
172 + close(wfd);
173 + close(ifd);
174 + printf("\n");
175 + exit(EXIT_SUCCESS);
176 + }
177 +
178 + int main(int argc, char **argv) {
179 + args_t args;
180 + int arg_r = parse_args(&args, argc, argv);
181 + if (arg_r == 1) {
182 + return EXIT_SUCCESS;
183 + } else if (arg_r != 0) {
184 + return EXIT_FAILURE;
185 + }
186 +
187 + fd = open(args.file, O_RDONLY);
188 + if (fd < 0) {
189 + perror("Error while opening file");
190 + return EXIT_FAILURE;
191 + }
192 +
193 + int ifd = inotify_init();
194 + if (ifd < 0) {
195 + perror("Error while creating inotify");
196 + close(fd);
197 + return EXIT_FAILURE;
198 + }
199 +
200 + wfd = inotify_add_watch(ifd, args.file, IN_MODIFY);
201 + if (wfd < 0) {
202 + perror("Error while adding inotify watcher");
203 + close(fd);
204 + close(ifd);
205 + return EXIT_FAILURE;
206 + }
207 +
208 + if (signal(SIGINT, &sighandler) == SIG_ERR) {
209 + perror("Error while registering signal");
210 + close(fd);
211 + close(wfd);
212 + close(ifd);
213 + return EXIT_FAILURE;
214 + }
215 +
216 + // if skip_n_lines < 0 (-1), the 'a' flag is set to all lines should be skipped
217 + if (args.skip_n_line >= 0) {
218 + print_file_content(args.skip_n_line);
219 + }
220 +
221 +
222 + for(;;) {
223 + struct inotify_event event;
224 + ssize_t n = read(ifd, &event, sizeof(struct inotify_event));
225 + if (n != sizeof(struct inotify_event)) {
226 + fprintf(stderr, "Error while reading inotify event\n");
227 + close(fd);
228 + close(wfd);
229 + close(ifd);
230 + return EXIT_FAILURE;
231 + }
232 +
233 + // 0 because no lines should be skipped
234 + print_file_content(0);
235 + }
236 +
237 + // end of function is unreachable
238 +
239 + }
Siguiente Anterior