]>
Dogcows Code - chaz/yoink/blob - build/dialog.c
2 /*] Copyright (c) 2011, Charles McGarvey [*******************************
3 **] All rights reserved.
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
10 **************************************************************************/
12 #define LUA_DIALOG_NAME "dialog"
13 #define LUA_DIALOG_VERSION "1.0"
33 static void printarray(const char* argv
[])
35 printf("%s", argv
[0]);
36 int i
; for (i
= 1; argv
[i
]; ++i
) printf(" %s", argv
[i
]);
42 * Fork and execute a command with arguments and optionally get a file
43 * descriptor connected to one of the child's own file descriptors. The
44 * process id of the child is returned, or -1 on error. If fd is not NULL,
45 * a pipe will be created and connected to *fd. If *fd is 0, then *fd will
46 * be set to a write file descriptor connected to the child's standard
47 * input. If *fd is not 0, then *fd will be set to a read file descriptor
48 * set connected to the specified file descriptor of the child. In either
49 * case, the caller has the responsibility to close fd when it is no longer
52 static pid_t
myexec(const char* command
, char* const argv
[], int* fd
)
60 if (pipe(p
) != 0) return -1;
61 parentFd
= (*fd
== 0);
63 if (!(child
= fork()))
68 if (dup2(p
[!parentFd
], *fd
) == -1) _exit(127);
77 close(p
[0]); close(p
[1]);
90 * Wait on a child process. Returns the exit status of the process if the
91 * child terminates normally, or -1 on error or abnormal child termination.
93 static int mywait(pid_t pid
)
96 if (waitpid(pid
, &status
, 0) == -1 || !WIFEXITED(status
)) return -1;
97 return WEXITSTATUS(status
);
102 * Read from a file descriptor until EOF and push contents to the top of
103 * the Lua stack. Closes the file descriptor afterward.
105 static void pushstream(lua_State
* L
, int fd
)
108 luaL_buffinit(L
, &B
);
112 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))))
114 if (bytes
== -1) break;
115 luaL_addlstring(&B
, buffer
, bytes
);
123 * Write some arbitrary bytes to a file descriptor, appending a newline.
125 static void writelstring(int fd
, const char* str
, size_t len
)
128 while ((bytes
= write(fd
, str
, len
)))
130 if (bytes
== -1) break;
134 bytes
= write(fd
, "\n", 1);
138 * Write a NULL-terminate string to a file descriptor. Uses writelstring.
140 static void writestring(int fd
, const char* str
)
142 writelstring(fd
, str
, strlen(str
));
146 * Write a Lua value, that is able to be converted to a string, to a file
149 static void tostream(lua_State
* L
, int index
, int fd
)
152 const char* buffer
= lua_tolstring(L
, index
, &len
);
153 writelstring(fd
, buffer
, len
);
158 * Add one or more strings to the end of a NULL-terminated array of
159 * strings. The parameter list itself should also be terminated with a
160 * NULL. The terminating NULL of the array of strings will be moved back
161 * as needed. It is the responsibility of the caller to assert there is
162 * enough room in the array so that an overflow doesn't occur. Strings are
163 * not duplicated as they are added to the array.
165 static void addstrings(const char* argv
[], ...)
168 int i
; for (i
= 0; argv
[i
]; ++i
);
170 const char* arg
= va_arg(ap
, const char*);
174 arg
= va_arg(ap
, const char*);
181 * Search an array of strings for a particular string, returning the index
182 * where the first equivalent string is found or -1 if it wasn't found.
184 static int searchstrings(const char* argv
[], const char* str
)
186 int i
; for (i
= 0; argv
[i
]; ++i
) if (strcmp(argv
[i
], str
) == 0) return i
;
192 * Add the command and backtitle to the argument list from the Lua state.
194 static void addcommand(lua_State
* L
, const char* argv
[])
196 lua_getglobal(L
, LUA_DIALOG_NAME
);
197 lua_getfield(L
, -1, "command");
198 lua_getfield(L
, -2, "title");
199 addstrings(argv
, lua_tostring(L
, -2), NULL
);
200 if (lua_isstring(L
, -1))
202 addstrings(argv
, "--backtitle", lua_tostring(L
, -1), NULL
);
208 * Add the height and width to the argument list from the Lua state.
210 static int addsize(lua_State
* L
, const char* argv
[], int extra
, int n
)
213 lua_getglobal(L
, LUA_DIALOG_NAME
);
214 if (lua_isnumber(L
, m
)) lua_pushvalue(L
, m
++);
215 else lua_getfield(L
, -1, "height");
216 if (lua_isnumber(L
, m
)) lua_pushvalue(L
, m
++);
217 else lua_getfield(L
, -2, "width");
218 addstrings(argv
, lua_tostring(L
, -2), lua_tostring(L
, -1), NULL
);
219 if (extra
) addstrings(argv
, lua_tostring(L
, -2), NULL
);
225 * Add the dialog title to the argument list from the Lua state. The title
226 * should be at index 1 on the stack, and an error is thrown if it is not
229 static void addtitle(lua_State
* L
, const char* argv
[])
231 addstrings(argv
, "--title", luaL_checkstring(L
, 1), NULL
);
235 * Add the caption to the argument list from the Lua state. The caption
236 * should be at index 2 on the stack, and an error is thrown if it is not
239 static void addcaption(lua_State
* L
, const char* argv
[])
241 addstrings(argv
, luaL_optstring(L
, 2, ""), NULL
);
245 * Get the string found at a certain index on the Lua stack, returned in
246 * the text argument. Returns whether the text was set.
248 static int getstring(lua_State
* L
, const char* argv
[], int n
, const char** text
)
250 if (lua_type(L
, n
) != LUA_TSTRING
) return 0;
251 *text
= lua_tostring(L
, n
);
256 * Add extra arguments to the argument list from the Lua state. The extra
257 * arguments exist in a table where the key is a flag and its value (if it
258 * is a string) is added as a flag option.
260 static int addextra(lua_State
* L
, const char* argv
[], int n
)
262 if (!lua_istable(L
, n
)) return 0;
264 while (lua_next(L
, n
))
266 addstrings(argv
, lua_tostring(L
, -2), NULL
);
267 if (lua_isstring(L
, -1)) addstrings(argv
, lua_tostring(L
, -1), NULL
);
274 * Add the default item to the argument list from the Lua stack for a menu
275 * command. Returns whether or not the arguments were found and added. If
276 * the value on the stack is nil, no arguments are added to the list, but
277 * the return value is still 1.
279 static int addselected(lua_State
* L
, const char* argv
[], int n
)
281 if (lua_isnil(L
, n
)) return 1;
282 if (!lua_isstring(L
, n
)) return 0;
283 addstrings(argv
, "--default-item", lua_tostring(L
, n
), NULL
);
288 * Add menu items to the argument list from the Lua stack. They should
289 * exist in a table at index 3 of a menu command. Each item should be a
290 * table itself with two or three strings, depending on whether --item-help
291 * exists in the argument list.
293 static void addmenuitems(lua_State
* L
, const char* argv
[])
296 if (searchstrings(argv
, "--item-help") != -1) fields
= 3;
298 if (!lua_istable(L
, 3)) luaL_argerror(L
, 3, "menu items");
299 int i
; for (i
= 1;; ++i
)
301 lua_pushinteger(L
, i
);
303 if (lua_isnil(L
, -1))
308 else if (lua_istable(L
, -1))
310 int subtable
= lua_gettop(L
);
312 int j
; for (j
= 0; j
< fields
; ++j
)
314 if (!lua_next(L
, subtable
)) luaL_argerror(L
, 3, "not enough fields");
315 addstrings(argv
, lua_tostring(L
, -1), NULL
);
322 if (fields
== 2) addstrings(argv
, "", "", NULL
);
323 else addstrings(argv
, "", "", "", NULL
);
330 * Fill out the argument list from the Lua state according to the standard
331 * 3 or 4 argument dialog command format.
333 static void addargs(lua_State
* L
, const char* command
, int nargs
, const char* argv
[])
335 assert((nargs
== 3 || nargs
== 4) && "nargs should be 3 or 4");
339 const char* text
= NULL
;
341 if (nargs
== 4) n
+= getstring(L
, argv
, n
, &text
);
342 n
+= addextra(L
, argv
, n
);
343 addstrings(argv
, command
, NULL
);
345 addsize(L
, argv
, 0, n
);
346 if (text
) addstrings(argv
, text
, NULL
);
351 * Close a gauge dialog if one is running. If a gauge is in progress, the
352 * status code will be pushed onto the stack and 1 is returned; otherwise,
355 static void closegauge(lua_State
* L
)
357 lua_getfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_pid");
358 lua_getfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_fd");
359 if (!lua_isnumber(L
, -2) || !lua_isnumber(L
, -1))
364 pid_t pid
= lua_tointeger(L
, -2);
365 int fd
= lua_tointeger(L
, -1);
369 lua_setfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_pid");
371 lua_setfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_fd");
374 int code
= mywait(pid
);
375 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
379 * Updates the progress of a gauge dialog. Takes a number argument in the
380 * range of 0-100 or 0.0-1.0 as the progress. Optionally takes a string as
381 * the second argument which causes the caption to change. The last call
382 * to this function should be with no arguments to end the gauge dialog.
383 * Returns nothing except the last call which returns the status code of
384 * the dialog which should always be OK.
386 static int updategauge(lua_State
* L
)
388 if (!lua_isnumber(L
, 1))
394 lua_getfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_fd");
395 if (!lua_isnumber(L
, -1)) return 0;
396 int fd
= lua_tointeger(L
, -1);
398 lua_Number percent
= lua_tonumber(L
, 1);
399 if (0.0 <= percent
&& percent
<= 1.0) percent
*= 100.0;
400 lua_pushinteger(L
, (lua_Integer
)percent
);
402 if (lua_isstring(L
, 2))
404 writestring(fd
, "XXX");
407 writestring(fd
, "XXX");
417 * Display a gauge dialog. Required arguments are dialog title and
418 * caption. Optional arguments include a table of extra flags. Returns a
419 * new function which can be used to update the progress.
421 static int dialog_gauge(lua_State
* L
)
423 const char* argv
[40] = {NULL
};
425 addargs(L
, "--gauge", 3, argv
);
428 pid_t pid
= myexec(argv
[0], (char* const*)argv
, &fd
);
429 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
430 lua_pushinteger(L
, pid
);
431 lua_setfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_pid");
432 lua_pushinteger(L
, fd
);
433 lua_setfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_fd");
434 lua_pushcfunction(L
, updategauge
);
438 * Display a menu dialog. Required arguments are dialog title, caption,
439 * and table of menu items. Optional arguments are selected item and table
442 static int dialog_menu(lua_State
* L
)
444 const char* argv
[1024] = {NULL
};
450 n
+= addselected(L
, argv
, n
);
451 n
+= addextra(L
, argv
, n
);
452 addstrings(argv
, "--menu", NULL
);
454 addsize(L
, argv
, 1, n
);
455 addmenuitems(L
, argv
);
460 pid_t pid
= myexec(argv
[0], (char* const*)argv
, &fd
);
461 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
462 int code
= mywait(pid
);
463 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
464 lua_pushinteger(L
, code
);
470 * Display a message dialog. Required arguments are dialog title and
471 * caption. Optional arguments include a table of extra flags.
473 static int dialog_msgbox(lua_State
* L
)
475 const char* argv
[40] = {NULL
};
477 addargs(L
, "--msgbox", 3, argv
);
479 pid_t pid
= myexec(argv
[0], (char* const*)argv
, NULL
);
480 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
481 int code
= mywait(pid
);
482 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
483 lua_pushinteger(L
, code
);
488 * Display an input dialog. Required arguments are dialog title and
489 * caption. Optional arguments are the default text and a table of extra
492 static int dialog_inputbox(lua_State
* L
)
494 const char* argv
[40] = {NULL
};
496 addargs(L
, "--inputbox", 4, argv
);
499 pid_t pid
= myexec(argv
[0], (char* const*)argv
, &fd
);
500 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
501 int code
= mywait(pid
);
502 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
503 lua_pushinteger(L
, code
);
509 * Display a yes/no dialog. Required arguments are dialog title and
510 * caption. Optional arguments include a table of extra flags.
512 static int dialog_yesno(lua_State
* L
)
514 const char* argv
[40] = {NULL
};
516 addargs(L
, "--yesno", 3, argv
);
518 pid_t pid
= myexec(argv
[0], (char* const*)argv
, NULL
);
519 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
520 int code
= mywait(pid
);
521 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
522 lua_pushinteger(L
, code
);
528 * Register the module functions and find a dialog command in the path.
530 LUALIB_API
int luaopen_dialog(lua_State
* L
)
532 const struct luaL_Reg dialog_funcs
[] = {
533 {"gauge", dialog_gauge
},
534 {"inputbox", dialog_inputbox
},
535 {"menu", dialog_menu
},
536 {"msgbox", dialog_msgbox
},
537 {"yesno", dialog_yesno
},
540 luaL_register(L
, LUA_DIALOG_NAME
, dialog_funcs
);
542 const char* names
[] = {getenv("DIALOG"), "dialog", "cdialog"};
543 int i
; for (i
= 0; i
< 3; ++i
)
547 char* path
= strdup(getenv("PATH"));
548 char* token
; char** paths
= &path
;
549 while ((token
= strsep(paths
, ":")))
552 luaL_buffinit(L
, &B
);
553 luaL_addstring(&B
, token
);
554 luaL_addstring(&B
, "/");
555 luaL_addstring(&B
, names
[i
]);
557 if (access(lua_tostring(L
, -1), X_OK
) == 0)
559 lua_setfield(L
, -2, "command");
567 luaL_error(L
, "cannot find dialog executable in the path; set DIALOG");
571 lua_pushinteger(L
, 0);
572 lua_setfield(L
, -2, "height");
573 lua_pushinteger(L
, 0);
574 lua_setfield(L
, -2, "width");
This page took 0.062956 seconds and 4 git commands to generate.