-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdsort.c
347 lines (316 loc) · 11.1 KB
/
dsort.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/**
* @file dsort.c
*
* @author Ulrike Schaefer 1327450
*
*
* @brief dsort executes command1 and command2 and prints the duplicated output of those commands in a sorted order
*
* @details dsort execute command1 and command2, save the ouput of th commands in an array,
this array gets sorted and only duplicated lines will be printed.
*
* @date 22.11.2016
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/wait.h>
/* === Constants === */
/**
* LINE_SIZE
* @brief max length for the strings in string_list - 1023 real characters and a \0
*/
#define LINE_SIZE (1024)
/* === Macros === */
/**
* DEBUG
* @brief print debug messages if the debug flag is set
*/
#ifdef ENDEBUG
#define DEBUG(...) do { fprintf(stderr, __VA_ARGS__); } while(0)
#else
#define DEBUG(...)
#endif
/* === Global Variables === */
/**
* progname
* @brief name of the program
*/
static const char *progname = "dsort";
/**
* command_output (=struct string_list)
* string-List that contains the Output of command1 and command2 (= input)
*/
static struct string_list command_output;
/* === Type Definitions === */
/**
* struct string_list
* @brief struct that represents a list of strings
*/
struct string_list {
/** currently contained strings in the list */
char** content;
/** amount of strings stored in the list */
int amnt_strings;
/** current amount of allocated memory for strings - string-list is full if amnt_strings == max_amnt_strings */
int max_amnt_strings;
};
/* === Prototypes === */
/**
* free_resources
* @brief free allocated resources
*/
static void free_resources(void);
/**
* bail_out
* @brief terminate program on program error
* @param exitcode exit code
* @param fmt format string
*/
static void bail_out(int exitcode, const char *fmt, ...);
/**
* read_from_child
* @brief run command in child and pipe stdout of the child to command_output of the parent
*/
static void read_from_child(char* command);
/**
* write_to_child
* @brief run command in child and command_output of the parent to stdin of the child
*/
static void write_to_child(char* command);
/**
* wait_for_child
* @brief partent-process waits for child-process to terminate before parent terminates
*/
static void wait_for_child(pid_t child_pid);
/**
* cmpstringp
* @brief method to compare two strings - for qsort
* @param p1 one string
* @param p2 other string
*/
static int cmpstringp(const void *p1, const void *p2);
/* === Implementations === */
static void free_resources(void)
{
DEBUG("free_resources");
/* clean up resources */
for (int i = 0; i < command_output.amnt_strings; ++i) {
free(command_output.content[i]);
}
free(&command_output.content[0]);
command_output.content = NULL;
command_output.amnt_strings = 0;
command_output.max_amnt_strings = 0;
}
static void bail_out(int exitcode, const char *fmt, ...)
{
va_list ap;
(void) fprintf(stderr, "%s: ", progname);
if (fmt != NULL) {
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
} if (errno != 0) {
(void) fprintf(stderr, ": %s", strerror(errno));
}
(void) fprintf(stderr, "\n");
free_resources();
exit(exitcode);
}
static void read_from_child(char* command) {
DEBUG("read_from_child %s\n",command);
/* create argument list for executing the command */
char *cmd[] = { "bash", "-c", command, (char *) 0};
/* create the pipe */
int command_pipe[2];
if(pipe(command_pipe) == -1) {
bail_out(errno, "create pipe failed\n");
}
/* flush stdout before fork so that buffer is empty */
if(fflush(stdout) != 0) {
bail_out(errno, "flush of stdout failed\n");
}
/* do fork */
pid_t pid;
switch (pid = fork()) {
case -1:
DEBUG("read_from_child - ERROR\n");
bail_out(errno,"fork failed\n");
break;
case 0:
/* child - execute command and rewire stdout to write end of pipe */
DEBUG("read_from_child - CHILD\n");
/* close unused pipe end - read */
if(close(command_pipe[0]) != 0) {
bail_out(errno,"close pipe read end of child failed\n");
}
/* rewire stdout to the write pipe end */
if(dup2(command_pipe[1], fileno(stdout)) < 0) {
bail_out(errno,"rewire stdout to write pipe end failed\n");
}
/* close write pipe after redirect */
if(close(command_pipe[1]) != 0) {
bail_out(errno,"close pipe write end of child failed after rewire\n");
}
/* execute command */
(void)execv("/bin/bash", cmd);
bail_out(errno,"executing %s failed\n",command);
break;
default:
/* parent - read whatever the child writes and write it to command_output */
DEBUG("read_from_child - PARENT\n");
/* close unused pipe end - write */
if(close(command_pipe[1]) != 0) {
bail_out(errno,"close pipe read end of parent failed\n");
}
/* create buffer for next line */
char next_line[LINE_SIZE];
/* create stream for reading the output from the child */
FILE* output_stream;
if ((output_stream = fdopen(command_pipe[0], "r")) == NULL) {
bail_out(errno,"creating of write-stream failed\n");
}
/* get next line of output and save in command output */
while(fgets(next_line, sizeof(next_line), output_stream) != NULL) {
if(command_output.content == NULL) {
command_output.max_amnt_strings = 5;
command_output.content = malloc(command_output.max_amnt_strings * sizeof(char*));
} else if(command_output.max_amnt_strings == command_output.amnt_strings) {
command_output.max_amnt_strings += 5;
command_output.content = realloc(command_output.content, command_output.max_amnt_strings * sizeof(char*));
}
command_output.content[command_output.amnt_strings] = strdup(next_line);
++command_output.amnt_strings;
} if(fclose(output_stream) != 0) {
bail_out(errno,"close output-read-stream failed\n");
}
wait_for_child(pid);
}
}
static void write_to_child(char* command) {
DEBUG("write_to_child %s\n",command);
/* create argument list for executing the command */
char *cmd[] = { "bash", "-c", command, (char *) 0};
/* create the pipe */
int command_pipe[2];
if(pipe(command_pipe) == -1) {
bail_out(errno, "create pipe failed\n");
}
/* flush stdout before fork so that buffer is empty */
if(fflush(stdout) != 0) {
bail_out(errno, "flush of stdout failed\n");
}
/* do fork */
pid_t pid = fork();
switch (pid) {
case -1:
DEBUG("write_to_child - ERROR\n");
bail_out(errno,"fork failed\n");
break;
case 0:
/* child - execute command and get input for the command from read pipe rewired to stdin */
DEBUG("write_to_child - CHILD\n");
/* close unused pipe end - write */
if(close(command_pipe[1]) != 0) {
bail_out(errno,"close pipe write end of child failed\n");
}
/* rewire stdin to the read pipe end */
if(dup2(command_pipe[0], fileno(stdin)) < 0) {
bail_out(errno,"rewire stdin to read pipe end failed\n");
}
/* close read pipe after redirect */
if(close(command_pipe[0]) != 0) {
bail_out(errno,"close pipe read end of child failed after rewire\n");
}
/* execute command */
(void)execv("/bin/bash", cmd);
bail_out(errno,"executing %s failed\n",command);
break;
default:
/* parent - write whatever is in command_output into the write end of the pipe */
DEBUG("write_to_child - PARENT\n");
/* close unused pipe end - read */
if(close(command_pipe[0]) != 0) {
bail_out(errno,"close pipe read end of parent failed\n");
}
/* create stream for writing command_output to the child */
FILE *input_stream;
if ((input_stream = fdopen(command_pipe[1], "w")) == NULL) {
bail_out(errno,"creating write-stream failed\n");
}
/* write item for item from command_output into input stream */
for (int i = 0; i < command_output.amnt_strings; ++i) {
if(fputs(command_output.content[i], input_stream) == EOF) {
bail_out(EXIT_FAILURE,"writing to child failed\n");
}
}
if(fclose(input_stream) != 0) {
bail_out(errno,"close input-write-stream failed\n");
}
wait_for_child(pid);
}
}
static void wait_for_child(pid_t child_pid) {
DEBUG("wait_for_child\n");
int status;
pid_t pid;
while((pid = wait(&status)) != child_pid) {
if(pid != -1) {
continue; /* other child */
} if(errno == EINTR) {
continue; /* interrupted */
}
bail_out(errno,"wait for child failed\n");
} if(WEXITSTATUS(status) == EXIT_SUCCESS) {
DEBUG("child exit successfully\n");
} else {
DEBUG("error in child occured\n");
}
}
static int cmpstringp(const void *p1, const void *p2) {
/* The actual arguments to this function are "pointers to
pointers to char", but strcmp(3) arguments are "pointers
to char", hence the following cast plus dereference */
return strcmp(*(char * const *)p1, *(char * const *)p2);
}
/**
* main
* @brief starting point of program
* @param argc number of program arguments
* @param argv program arguments
*/
int main(int argc, char **argv) {
/* check if correct intput was passed on */
if(argc > 0) {
progname = argv[0];
} if (argc != 3) {
bail_out(EXIT_FAILURE, "Usage: %s <command1> <command2>\n", progname);
}
/* exectue commands and read output from clients into command_output */
command_output.content = NULL;
command_output.amnt_strings = 0;
command_output.max_amnt_strings = 0;
read_from_child(argv[1]);
for (int i = 0; i < command_output.amnt_strings; ++i) {
DEBUG("%d: %s\n", i, command_output.content[i]);
}
read_from_child(argv[2]);
for (int i = 0; i < command_output.amnt_strings; ++i) {
DEBUG("%d: %s\n", i, command_output.content[i]);
}
/* sort command_output */
qsort(command_output.content, command_output.amnt_strings, sizeof(char*), cmpstringp);
for (int i = 0; i < command_output.amnt_strings; ++i) {
DEBUG("%d: %s\n", i, command_output.content[i]);
}
/* execute uniq -d and print */
write_to_child("uniq -d");
/* free resources and exit program without error */
free_resources();
return EXIT_SUCCESS;
}