- Use the included S-Lang again, since we include a better version now.
[midnight-commander.git] / src / background.c
blob46b1c161cad81a145dd45e4dd9e8b0f23ff0b68c
1 /* {{{ Copyright */
3 /* Background support.
4 Copyright (C) 1996 The Free Software Foundation
6 Written by: 1996 Miguel de Icaza
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 /* }}} */
24 #include <config.h>
25 #include <stdarg.h>
26 #include <sys/types.h>
27 #include <errno.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <stdio.h>
35 #include "global.h"
36 #include "tty.h"
37 #include "dlg.h"
38 #include "widget.h"
39 #include "wtools.h"
42 * We currenlty only support one way of comunicating the background
43 * and foreground process by using the socketpair system call
45 #ifdef WITH_BACKGROUND
46 # include <sys/socket.h>
47 #endif
48 #include "dialog.h"
49 #include "fileopctx.h"
50 #include "key.h" /* For add_select_channel(), delete_select_channel() */
51 #include "eregex.h"
52 #include "file.h"
53 #include "filegui.h"
55 /* If true, this is a background process */
56 int we_are_background = 0;
58 #ifdef WITH_BACKGROUND
59 /* If set background tasks wait to be attached */
60 int background_wait = 0;
62 #ifndef HAVE_SOCKETPAIR
63 int socketpair(int, int, int, int fd[2]);
64 #endif
66 /* File descriptor for talking to our parent */
67 static int parent_fd;
69 #define MAXCALLARGS 4 /* Number of arguments supported */
70 #define mymsg "Desde el hijo\n\r"
72 struct TaskList *task_list = NULL;
74 static int background_attention (int fd, void *closure);
76 static void
77 register_task_running (FileOpContext *ctx, pid_t pid, int fd, char *info)
79 TaskList *new;
81 new = g_new (TaskList, 1);
82 new->pid = pid;
83 new->info = info;
84 new->state = Task_Running;
85 new->next = task_list;
86 new->fd = fd;
87 task_list = new;
89 add_select_channel (fd, background_attention, ctx);
92 void
93 unregister_task_running (pid_t pid, int fd)
95 TaskList *p = task_list;
96 TaskList *prev = 0;
98 while (p){
99 if (p->pid == pid){
100 if (prev)
101 prev->next = p->next;
102 else
103 task_list = p->next;
104 g_free (p->info);
105 g_free (p);
106 break;
108 prev = p;
109 p = p->next;
111 delete_select_channel (fd);
115 * Try to make the Midnight Commander a background job
117 * Returns:
118 * 1 for parent
119 * 0 for child
120 * -1 on failure
123 do_background (FileOpContext *ctx, char *info)
125 int comm [2]; /* control connection stream */
126 pid_t pid;
128 if (socketpair (AF_UNIX, SOCK_STREAM, 0, comm) == -1)
129 return -1;
131 if ((pid = fork ()) == -1)
132 return -1;
134 if (pid == 0){
135 int nullfd;
137 parent_fd = comm [1];
138 we_are_background = 1;
140 /* Make stdin/stdout/stderr point somewhere */
141 close (0);
142 close (1);
143 close (2);
145 if ((nullfd = open ("/dev/null", O_RDONLY)) != -1){
146 while (dup2 (nullfd, 0) == -1 && errno == EINTR)
148 while (dup2 (nullfd, 1) == -1 && errno == EINTR)
150 while (dup2 (nullfd, 2) == -1 && errno == EINTR)
154 /* To make it obvious if it fails, there is a bug report on this */
155 write (2, mymsg, sizeof (mymsg));
156 write (1, mymsg, sizeof (mymsg));
158 /* Just for debugging the background back end */
159 if (background_wait){
160 volatile int i = 1;
162 while (i)
165 return 0;
166 } else {
167 close (comm [1]);
168 ctx->pid = pid;
169 register_task_running (ctx, pid, comm [0], info);
170 return 1;
174 static char *
175 background_title (char *str)
177 return g_strconcat (_("Background process:"), str, NULL);
180 /* {{{ Routines that do the real job */
181 static void
182 real_message_1s (enum OperationMode mode, int *flags, char *title, const char *str1)
184 if (mode == Background)
185 title = background_title (title);
187 message (*flags, title, "%s", str1);
189 if (mode == Background)
190 g_free (title);
193 static void
194 real_message_2s (enum OperationMode mode, int *flags, char *title,
195 const char *str1, const char *str2)
197 if (mode == Background)
198 title = background_title (title);
200 message (*flags, title, str1, str2);
202 if (mode == Background)
203 g_free (title);
206 static void
207 real_message_3s (enum OperationMode mode, int *flags, char *title,
208 const char *str1, const char *str2, const char *str3)
210 if (mode == Background)
211 title = background_title (title);
213 message (*flags, title, str1, str2, str3);
215 if (mode == Background)
216 g_free (title);
218 /* }}} */
220 /* {{{ Parent handlers */
222 /* Parent/child protocol
224 * the child (the background) process send the following:
225 * void *routine -- routine to be invoked in the parent
226 * int nargc -- number of arguments
227 * int type -- Return argument type.
229 * If the routine is zero, then it is a way to tell the parent
230 * that the process is dying.
232 * nargc arguments in the following format:
233 * int size of the coming block
234 * size bytes with the block
236 * Now, the parent loads all those and then invokes
237 * the routine with pointers to the information passed
238 * (we just support pointers).
240 * If the return type is integer:
242 * the parent then writes an int to the child with
243 * the return value from the routine and the values
244 * of any global variable that is modified in the parent
245 * currently: do_append and recursive_result.
247 * If the return type is a string:
249 * the parent writes the resulting string lenght
250 * if the result string was NULL or the empty string,
251 * then the lenght is zero.
252 * The parent then writes the string lenght and frees
253 * the result string.
256 * Receive requests from background process and invoke the
257 * specified routine
260 static int
261 background_attention (int fd, void *closure)
263 FileOpContext *ctx;
264 int have_ctx;
265 void *routine;
266 int argc, i, result, status;
267 char *data [MAXCALLARGS];
268 int bytes;
269 enum ReturnType type;
271 ctx = closure;
273 bytes = read (fd, &routine, sizeof (routine));
274 if (bytes < (sizeof (routine))){
275 char *background_process_error = _(" Background process error ");
277 if (errno == ECHILD)
278 message (1, background_process_error, _(" Child died unexpectedly "));
279 else
280 message (1, background_process_error, _(" Unknown error in child "));
281 unregister_task_running (ctx->pid, fd);
282 waitpid (ctx->pid, &status, 0);
283 return 0;
286 /* If the routine is zero, then the child is telling us that he is dying */
287 if ((long) routine == MSG_CHILD_EXITING){
288 unregister_task_running (ctx->pid, fd);
289 waitpid (ctx->pid, &status, 0);
290 return 0;
293 read (fd, &argc, sizeof (argc));
294 if (argc > MAXCALLARGS){
295 message (1, _(" Background protocol error "),
296 _(" Background process sent us a request for more arguments \n"
297 " than we can handle. \n"));
299 read (fd, &type, sizeof (type));
300 read (fd, &have_ctx, sizeof (have_ctx));
301 if (have_ctx)
302 read (fd, ctx, sizeof (FileOpContext));
304 for (i = 0; i < argc; i++){
305 int size;
307 read (fd, &size, sizeof (size));
308 data [i] = g_malloc (size+1);
309 read (fd, data [i], size);
311 data [i][size] = 0; /* NULL terminate the blocks (they could be strings) */
314 /* Handle the call */
315 if (type == Return_Integer){
316 if (!have_ctx)
317 switch (argc){
318 case 1:
319 result = (*(int (*)(int, char *))routine)(Background, data [0]);
320 break;
321 case 2:
322 result = (*(int (*)(int, char *, char *))routine)
323 (Background, data [0], data [1]);
324 break;
325 case 3:
326 result = (*(int (*)(int, char *, char *, char *))routine)
327 (Background, data [0], data [1], data [2]);
328 break;
329 case 4:
330 result = (*(int (*)(int, char *, char *, char *, char *))routine)
331 (Background, data [0], data [1], data [2], data [3]);
332 break;
334 else
335 switch (argc){
336 case 1:
337 result = (*(int (*)(FileOpContext *, int, char *))routine)
338 (ctx, Background, data [0]);
339 break;
340 case 2:
341 result = (*(int (*)(FileOpContext *, int, char *, char *))routine)
342 (ctx, Background, data [0], data [1]);
343 break;
344 case 3:
345 result = (*(int (*)(FileOpContext *, int, char *, char *, char *))routine)
346 (ctx, Background, data [0], data [1], data [2]);
347 break;
348 case 4:
349 result = (*(int (*)(FileOpContext *, int, char *, char *, char *, char *))routine)
350 (ctx, Background, data [0], data [1], data [2], data [3]);
351 break;
354 /* Send the result code and the value for shared variables */
355 write (fd, &result, sizeof (int));
356 if (have_ctx)
357 write (fd, ctx, sizeof (FileOpContext));
358 } else if (type == Return_String) {
359 int len;
360 char *resstr = NULL;
362 /* FIXME: string routines should also use the Foreground/Background
363 * parameter. Currently, this is not used here
365 switch (argc){
366 case 1:
367 resstr = (*(char * (*)(char *))routine)(data [0]);
368 break;
369 case 2:
370 resstr = (*(char * (*)(char *, char *))routine)
371 (data [0], data [1]);
372 break;
373 case 3:
374 resstr = (*(char * (*)(char *, char *, char *))routine)
375 (data [0], data [1], data [2]);
376 break;
377 case 4:
378 resstr = (*(char * (*)(char *, char *, char *, char *))routine)
379 (data [0], data [1], data [2], data [3]);
380 break;
381 default: g_assert_not_reached();
383 if (resstr){
384 len = strlen (resstr);
385 write (fd, &len, sizeof (len));
386 if (len){
387 write (fd, resstr, len);
388 g_free (resstr);
390 } else {
391 len = 0;
392 write (fd, &len, sizeof (len));
395 for (i = 0; i < argc; i++)
396 g_free (data [i]);
398 do_refresh ();
399 mc_refresh ();
400 doupdate ();
401 return 0;
405 /* }}} */
407 /* {{{ client RPC routines */
409 /* Sends the header for a call to a routine in the parent process. If the file
410 * operation context is not NULL, then it requests that the first parameter of
411 * the call be a file operation context.
413 static void
414 parent_call_header (void *routine, int argc, enum ReturnType type, FileOpContext *ctx)
416 int have_ctx;
418 have_ctx = (ctx != NULL);
420 write (parent_fd, &routine, sizeof (routine));
421 write (parent_fd, &argc, sizeof (int));
422 write (parent_fd, &type, sizeof (type));
423 write (parent_fd, &have_ctx, sizeof (have_ctx));
425 if (have_ctx)
426 write (parent_fd, ctx, sizeof (FileOpContext));
430 parent_call (void *routine, FileOpContext *ctx, int argc, ...)
432 va_list ap;
433 int i;
435 va_start (ap, argc);
436 parent_call_header (routine, argc, Return_Integer, ctx);
437 for (i = 0; i < argc; i++) {
438 int len;
439 void *value;
441 len = va_arg (ap, int);
442 value = va_arg (ap, void *);
443 write (parent_fd, &len, sizeof (int));
444 write (parent_fd, value, len);
446 read (parent_fd, &i, sizeof (int));
447 if (ctx)
448 read (parent_fd, ctx, sizeof (FileOpContext));
450 return i;
453 static char *
454 parent_call_string (void *routine, int argc, ...)
456 va_list ap;
457 char *str;
458 int i;
460 va_start (ap, argc);
461 parent_call_header (routine, argc, Return_String, NULL);
462 for (i = 0; i < argc; i++){
463 int len;
464 void *value;
466 len = va_arg (ap, int);
467 value = va_arg (ap, void *);
468 write (parent_fd, &len, sizeof (int));
469 write (parent_fd, value, len);
471 read (parent_fd, &i, sizeof (int));
472 if (!i)
473 return NULL;
474 str = g_malloc (i + 1);
475 read (parent_fd, str, i);
476 str [i] = 0;
477 return str;
480 void
481 tell_parent (int msg)
483 write (parent_fd, &msg, sizeof (int));
486 void
487 message_1s (int flags, char *title, const char *str1)
489 if (we_are_background)
490 parent_call ((void *)real_message_1s, NULL, 3, sizeof (flags), &flags,
491 strlen (title), title, strlen (str1), str1);
492 else
493 real_message_1s (Foreground, &flags, title, str1);
496 void
497 message_2s (int flags, char *title, const char *str1, const char *str2)
499 if (we_are_background)
500 parent_call ((void *)real_message_2s, NULL, 4, sizeof (flags), &flags,
501 strlen (title), title, strlen (str1), str1,
502 strlen (str2), str2);
503 else
504 real_message_2s (Foreground, &flags, title, str1, str2);
507 void
508 message_3s (int flags, char *title, const char *str1,
509 const char *str2, const char *str3)
511 if (we_are_background)
512 parent_call ((void *)real_message_3s, NULL, 3, sizeof (flags), &flags,
513 strlen (title), title, strlen (str1), str1,
514 strlen (str2), str2, strlen (str3), str3);
515 else
516 real_message_3s (Foreground, &flags, title, str1, str2, str3);
519 char *
520 input_dialog_help (char *header, char *text, char *help, char *def_text)
522 if (we_are_background)
523 return parent_call_string ((void *)real_input_dialog_help, 4,
524 strlen (header), header,
525 strlen (text), text,
526 strlen (help), help,
527 strlen (def_text), def_text);
528 else
529 return real_input_dialog_help (header, text, help, def_text);
532 #else /* Else => No background code support */
534 /* {{{ Stubs if background code is not supported */
535 void
536 message_1s (int flags, char *title, const char *str1)
538 message (flags, title, "%s", str1);
541 void
542 message_2s (int flags, char *title, const char *str1, const char *str2)
544 message (flags, title, str1, str2);
547 void
548 message_3s (int flags, char *title, const char *str1,
549 const char *str2, const char *str3)
551 message (flags, title, str1, str2, str3);
554 char *
555 input_dialog_help (char *header, char *text, char *help, char *def_text)
557 return real_input_dialog_help (header, text, help, def_text);
559 /* }}} */
561 #endif /* !WITH_BACKGROUND */
563 /* {{{ Functions shared between background and foreground */
565 void
566 message_1s1d (int flags, char *title, const char *str, int d)
568 char *p = g_strdup_printf (str, d);
569 message_1s (flags, title, p);
570 g_free (p);
573 /* }}} */