solidc
Robust collection of general-purpose cross-platform C libraries and data structures designed for rapid and safe development in C
Loading...
Searching...
No Matches
pipeline.c
1#include "../include/pipeline.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7#ifdef _WIN32
8#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
9#define close(fd) _close(fd)
10#define dup2(oldfd, newfd) _dup2(oldfd, newfd)
11#endif
12
20 CommandNode* node = (CommandNode*)malloc(sizeof(CommandNode));
21 if (!node) {
22 perror("malloc");
23 exit(EXIT_FAILURE);
24 }
25 node->args = args;
26 node->next = NULL;
27 return node;
28}
29
30#ifdef _WIN32
38void execute_pipeline(CommandNode* head, int output_fd) {
39 int pipefd[2] = {0};
40 int prev_pipe_read_end = -1;
41 CommandNode* current = head;
42 STARTUPINFO si = {0};
43 PROCESS_INFORMATION pi = {0};
44 HANDLE* proc_handles = NULL;
45 DWORD proc_count = 0;
46 DWORD command_count = 0;
47
48 // Count commands first to allocate handles array
49 CommandNode* count_node = head;
50 while (count_node != NULL) {
51 command_count++;
52 count_node = count_node->next;
53 }
54
55 proc_handles = (HANDLE*)calloc(command_count, sizeof(HANDLE));
56 if (!proc_handles) {
57 perror("calloc");
58 exit(EXIT_FAILURE);
59 }
60
61 while (current != NULL) {
62 // Create pipe if there's a next command
63 if (current->next != NULL) {
64 if (pipe(pipefd) < 0) {
65 perror("pipe");
66 exit(EXIT_FAILURE);
67 }
68 }
69
70 // Prepare the command string
71 char command[1024] = "";
72 int i = 0;
73 while (current->args[i] != NULL) {
74 // Add quotes if the argument contains spaces
75 if (strchr(current->args[i], ' ') != NULL) {
76 strcat(command, "\"");
77 strcat(command, current->args[i]);
78 strcat(command, "\" ");
79 } else {
80 strcat(command, current->args[i]);
81 strcat(command, " ");
82 }
83 i++;
84 }
85
86 // Initialize STARTUPINFO
87 ZeroMemory(&si, sizeof(si));
88 si.cb = sizeof(si);
89 si.dwFlags = STARTF_USESTDHANDLES;
90
91 // Set standard handles
92 si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
93 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
94 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
95
96 // Redirect input from previous pipe if any
97 if (prev_pipe_read_end != -1) {
98 si.hStdInput = (HANDLE)_get_osfhandle(prev_pipe_read_end);
99 }
100
101 // Redirect output to next pipe or specified file descriptor
102 if (current->next != NULL) {
103 si.hStdOutput = (HANDLE)_get_osfhandle(pipefd[1]);
104 } else if (output_fd != -1) {
105 si.hStdOutput = (HANDLE)_get_osfhandle(output_fd);
106 }
107
108 // Create the process
109 ZeroMemory(&pi, sizeof(pi));
110 if (!CreateProcess(NULL, // No module name (use command line)
111 command, // Command line
112 NULL, // Process handle not inheritable
113 NULL, // Thread handle not inheritable
114 TRUE, // Set handle inheritance to TRUE
115 0, // No creation flags
116 NULL, // Use parent's environment block
117 NULL, // Use parent's starting directory
118 &si, // Pointer to STARTUPINFO structure
119 &pi // Pointer to PROCESS_INFORMATION structure
120 )) {
121 fprintf(stderr, "CreateProcess failed: %lu\n", GetLastError());
122 exit(EXIT_FAILURE);
123 }
124
125 // Store process handle
126 proc_handles[proc_count++] = pi.hProcess;
127
128 // Close thread handle (not needed)
129 CloseHandle(pi.hThread);
130
131 // Close write end of pipe
132 if (current->next != NULL) {
133 close(pipefd[1]);
134 }
135
136 // Close read end of previous pipe
137 if (prev_pipe_read_end != -1) {
138 close(prev_pipe_read_end);
139 }
140
141 // Save read end for next command
142 prev_pipe_read_end = pipefd[0];
143
144 current = current->next;
145 }
146
147 // Wait for all processes to finish
148 WaitForMultipleObjects(proc_count, proc_handles, TRUE, INFINITE);
149
150 // Close process handles
151 for (size_t i = 0; i < proc_count; i++) {
152 CloseHandle(proc_handles[i]);
153 }
154
155 free(proc_handles);
156}
157#else
165void execute_pipeline(CommandNode* head, int output_fd) {
166 int pipefd[2];
167 int prev_pipe_read_end = -1;
168 CommandNode* current = head;
169
170 while (current != NULL) {
171 // Create a pipe if there's a next command
172 if (current->next != NULL) {
173 if (pipe(pipefd) < 0) {
174 perror("pipe");
175 exit(EXIT_FAILURE);
176 }
177 }
178
179 // Fork a child process
180 pid_t pid = fork();
181 if (pid < 0) {
182 perror("fork");
183 exit(EXIT_FAILURE);
184 }
185
186 if (pid == 0) { // Child process
187 // Redirect input from the previous pipe (if any)
188 if (prev_pipe_read_end != -1) {
189 dup2(prev_pipe_read_end, STDIN_FILENO);
190 close(prev_pipe_read_end);
191 }
192
193 // Redirect output to the next pipe or the specified file descriptor
194 if (current->next != NULL) {
195 dup2(pipefd[1], STDOUT_FILENO);
196 close(pipefd[1]);
197 } else if (output_fd != -1) {
198 // Redirect output of the last command to the specified file descriptor
199 dup2(output_fd, STDOUT_FILENO);
200 close(output_fd);
201 }
202
203 // Execute the command
204 execvp(current->args[0], current->args);
205 perror("execvp");
206 exit(EXIT_FAILURE);
207 } else { // Parent process
208 // Close the write end of the current pipe (if any)
209 if (current->next != NULL) {
210 close(pipefd[1]);
211 }
212
213 // Close the read end of the previous pipe (if any)
214 if (prev_pipe_read_end != -1) {
215 close(prev_pipe_read_end);
216 }
217
218 // Save the read end of the current pipe for the next command
219 prev_pipe_read_end = pipefd[0];
220 }
221
222 current = current->next;
223 }
224
225 // Wait for all child processes to finish
226 while (wait(NULL) > 0);
227}
228#endif
229
236 CommandNode* current = head;
237 while (current != NULL) {
238 CommandNode* next = current->next;
239 free(current);
240 current = next;
241 }
242}
243
250void build_pipeline(CommandNode** commands) {
251 if (!commands) return; // Handle NULL input
252
253 int i = 0;
254 while (commands[i]) {
255 if (commands[i + 1] != NULL) {
256 commands[i]->next = commands[i + 1];
257 } else {
258 commands[i]->next = NULL;
259 }
260 i++;
261 }
262}
263
264#if 0
265int main() {
266 // Create commands
267 char* args1[] = {"ls", "-l", NULL};
268 char* args2[] = {"grep", "-E", ".c$", NULL};
269 char* args3[] = {"wc", "-l", NULL};
270
271 CommandNode* cmd1 = create_command_node(args1);
272 CommandNode* cmd2 = create_command_node(args2);
273 CommandNode* cmd3 = create_command_node(args3);
274
275 // Create a NULL-terminated array of commands
276 CommandNode* commands[] = {cmd1, cmd2, cmd3, NULL};
277
278 // Build the pipeline
279 build_pipeline(commands);
280
281#ifdef _WIN32
282 // On Windows, we need to use _open instead of open
283 int output_fd = _open("output.txt", _O_WRONLY | _O_CREAT | _O_TRUNC, _S_IWRITE);
284#else
285 // Open a file to capture the output of the last command
286 int output_fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
287#endif
288
289 if (output_fd < 0) {
290 perror("open");
291 return EXIT_FAILURE;
292 }
293
294 // Execute the pipeline and capture the output of the last command
295 execute_pipeline(cmd1, output_fd);
296
297 // Close the output file descriptor
298 close(output_fd);
299
300 // Free the pipeline
301 free_pipeline(cmd1);
302
303 return 0;
304}
305#endif
void build_pipeline(CommandNode **commands)
Build the pipeline using a NULL-terminated array of CommandNode pointers.
Definition pipeline.c:250
void free_pipeline(CommandNode *head)
Free the linked list of commands.
Definition pipeline.c:235
CommandNode * create_command_node(char **args)
Create a new CommandNode.
Definition pipeline.c:19
void execute_pipeline(CommandNode *head, int output_fd)
Execute a pipeline of commands.
Definition pipeline.c:165
Structure representing a command in a pipeline.
Definition pipeline.h:32