]>
Dogcows Code - chaz/yoink/blob - build/dialog.c
2 /*] Copyright (c) 2011, Charles McGarvey [**********************************
3 **] All rights reserved.
5 * Distributable under the terms and conditions of the 2-clause BSD license;
6 * see the file COPYING for a complete text of the license.
8 *****************************************************************************/
10 #define LUA_DIALOG_NAME "dialog"
11 #define LUA_DIALOG_VERSION "1.0"
31 static void printarray(const char* argv
[])
33 printf("%s", argv
[0]);
34 int i
; for (i
= 1; argv
[i
]; ++i
) printf(" %s", argv
[i
]);
40 * Fork and execute a command with arguments and optionally get a file
41 * descriptor connected to one of the child's own file descriptors. The
42 * process id of the child is returned, or -1 on error. If fd is not NULL, a
43 * pipe will be created and connected to *fd. If *fd is 0, then *fd will be
44 * set to a write file descriptor connected to the child's standard input. If
45 * *fd is not 0, then *fd will be set to a read file descriptor set connected
46 * to the specified file descriptor of the child. In either case, the caller
47 * has the responsibility to close fd when it is no longer needed.
49 static pid_t
myexec(const char* command
, char* const argv
[], int* fd
)
56 if (pipe(p
) != 0) return -1;
57 parentFd
= (*fd
== 0);
59 if (!(child
= fork())) {
62 if (dup2(p
[!parentFd
], *fd
) == -1) _exit(127);
69 close(p
[0]); close(p
[1]);
80 * Wait on a child process. Returns the exit status of the process if the
81 * child terminates normally, or -1 on error or abnormal child termination.
83 static int mywait(pid_t pid
)
86 if (waitpid(pid
, &status
, 0) == -1 || !WIFEXITED(status
)) return -1;
87 return WEXITSTATUS(status
);
92 * Read from a file descriptor until EOF and push contents to the top of the
93 * Lua stack. Closes the file descriptor afterward.
95 static void pushstream(lua_State
* L
, int fd
)
102 while ((bytes
= read(fd
, buffer
, sizeof(buffer
)))) {
103 if (bytes
== -1) break;
104 luaL_addlstring(&B
, buffer
, bytes
);
112 * Write some arbitrary bytes to a file descriptor, appending a newline.
114 static void writelstring(int fd
, const char* str
, size_t len
)
117 while ((bytes
= write(fd
, str
, len
))) {
118 if (bytes
== -1) break;
122 bytes
= write(fd
, "\n", 1);
126 * Write a NULL-terminate string to a file descriptor. Uses writelstring.
128 static void writestring(int fd
, const char* str
)
130 writelstring(fd
, str
, strlen(str
));
134 * Write a Lua value, that is able to be converted to a string, to a file
137 static void tostream(lua_State
* L
, int index
, int fd
)
140 const char* buffer
= lua_tolstring(L
, index
, &len
);
141 writelstring(fd
, buffer
, len
);
146 * Add one or more strings to the end of a NULL-terminated array of
147 * strings. The parameter list itself should also be terminated with a
148 * NULL. The terminating NULL of the array of strings will be moved back
149 * as needed. It is the responsibility of the caller to assert there is
150 * enough room in the array so that an overflow doesn't occur. Strings are
151 * not duplicated as they are added to the array.
153 static void addstrings(const char* argv
[], ...)
156 int i
; for (i
= 0; argv
[i
]; ++i
);
158 const char* arg
= va_arg(ap
, const char*);
161 arg
= va_arg(ap
, const char*);
168 * Search an array of strings for a particular string, returning the index
169 * where the first equivalent string is found or -1 if it wasn't found.
171 static int searchstrings(const char* argv
[], const char* str
)
173 int i
; for (i
= 0; argv
[i
]; ++i
) if (strcmp(argv
[i
], str
) == 0) return i
;
179 * Add the command and backtitle to the argument list from the Lua state.
181 static void addcommand(lua_State
* L
, const char* argv
[])
183 lua_getglobal(L
, LUA_DIALOG_NAME
);
184 lua_getfield(L
, -1, "command");
185 lua_getfield(L
, -2, "title");
186 addstrings(argv
, lua_tostring(L
, -2), NULL
);
187 if (lua_isstring(L
, -1)) {
188 addstrings(argv
, "--backtitle", lua_tostring(L
, -1), NULL
);
194 * Add the height and width to the argument list from the Lua state.
196 static int addsize(lua_State
* L
, const char* argv
[], int extra
, int n
)
199 lua_getglobal(L
, LUA_DIALOG_NAME
);
200 if (lua_isnumber(L
, m
)) lua_pushvalue(L
, m
++);
201 else lua_getfield(L
, -1, "height");
202 if (lua_isnumber(L
, m
)) lua_pushvalue(L
, m
++);
203 else lua_getfield(L
, -2, "width");
204 addstrings(argv
, lua_tostring(L
, -2), lua_tostring(L
, -1), NULL
);
205 if (extra
) addstrings(argv
, lua_tostring(L
, -2), NULL
);
211 * Add the dialog title to the argument list from the Lua state. The title
212 * should be at index 1 on the stack, and an error is thrown if it is not
215 static void addtitle(lua_State
* L
, const char* argv
[])
217 addstrings(argv
, "--title", luaL_checkstring(L
, 1), NULL
);
221 * Add the caption to the argument list from the Lua state. The caption
222 * should be at index 2 on the stack, and an error is thrown if it is not
225 static void addcaption(lua_State
* L
, const char* argv
[])
227 addstrings(argv
, luaL_optstring(L
, 2, ""), NULL
);
231 * Get the string found at a certain index on the Lua stack, returned in
232 * the text argument. Returns whether the text was set.
234 static int getstring(lua_State
* L
, const char* argv
[], int n
, const char** text
)
236 if (lua_type(L
, n
) != LUA_TSTRING
) return 0;
237 *text
= lua_tostring(L
, n
);
242 * Add extra arguments to the argument list from the Lua state. The extra
243 * arguments exist in a table where the key is a flag and its value (if it
244 * is a string) is added as a flag option.
246 static int addextra(lua_State
* L
, const char* argv
[], int n
)
248 if (!lua_istable(L
, n
)) return 0;
250 while (lua_next(L
, n
)) {
251 addstrings(argv
, lua_tostring(L
, -2), NULL
);
252 if (lua_isstring(L
, -1)) addstrings(argv
, lua_tostring(L
, -1), NULL
);
259 * Add the default item to the argument list from the Lua stack for a menu
260 * command. Returns whether or not the arguments were found and added. If
261 * the value on the stack is nil, no arguments are added to the list, but
262 * the return value is still 1.
264 static int addselected(lua_State
* L
, const char* argv
[], int n
)
266 if (lua_isnil(L
, n
)) return 1;
267 if (!lua_isstring(L
, n
)) return 0;
268 addstrings(argv
, "--default-item", lua_tostring(L
, n
), NULL
);
273 * Add menu items to the argument list from the Lua stack. They should
274 * exist in a table at index 3 of a menu command. Each item should be a
275 * table itself with two or three strings, depending on whether --item-help
276 * exists in the argument list.
278 static void addmenuitems(lua_State
* L
, const char* argv
[])
281 if (searchstrings(argv
, "--item-help") != -1) fields
= 3;
283 if (!lua_istable(L
, 3)) luaL_argerror(L
, 3, "menu items");
284 int i
; for (i
= 1;; ++i
) {
285 lua_pushinteger(L
, i
);
287 if (lua_isnil(L
, -1)) {
291 else if (lua_istable(L
, -1)) {
292 int subtable
= lua_gettop(L
);
294 int j
; for (j
= 0; j
< fields
; ++j
) {
295 if (!lua_next(L
, subtable
)) luaL_argerror(L
, 3, "not enough fields");
296 addstrings(argv
, lua_tostring(L
, -1), NULL
);
302 if (fields
== 2) addstrings(argv
, "", "", NULL
);
303 else addstrings(argv
, "", "", "", NULL
);
310 * Fill out the argument list from the Lua state according to the standard
311 * 3 or 4 argument dialog command format.
313 static void addargs(lua_State
* L
, const char* command
, int nargs
, const char* argv
[])
315 assert((nargs
== 3 || nargs
== 4) && "nargs should be 3 or 4");
319 const char* text
= NULL
;
321 if (nargs
== 4) n
+= getstring(L
, argv
, n
, &text
);
322 n
+= addextra(L
, argv
, n
);
323 addstrings(argv
, command
, NULL
);
325 addsize(L
, argv
, 0, n
);
326 if (text
) addstrings(argv
, text
, NULL
);
331 * Close a gauge dialog if one is running. If a gauge is in progress, the
332 * status code will be pushed onto the stack and 1 is returned; otherwise,
335 static void closegauge(lua_State
* L
)
337 lua_getfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_pid");
338 lua_getfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_fd");
339 if (!lua_isnumber(L
, -2) || !lua_isnumber(L
, -1)) {
343 pid_t pid
= lua_tointeger(L
, -2);
344 int fd
= lua_tointeger(L
, -1);
348 lua_setfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_pid");
350 lua_setfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_fd");
353 int code
= mywait(pid
);
354 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
358 * Updates the progress of a gauge dialog. Takes a number argument in the
359 * range of 0-100 or 0.0-1.0 as the progress. Optionally takes a string as
360 * the second argument which causes the caption to change. The last call
361 * to this function should be with no arguments to end the gauge dialog.
362 * Returns nothing except the last call which returns the status code of
363 * the dialog which should always be OK.
365 static int updategauge(lua_State
* L
)
367 if (!lua_isnumber(L
, 1)) {
372 lua_getfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_fd");
373 if (!lua_isnumber(L
, -1)) return 0;
374 int fd
= lua_tointeger(L
, -1);
376 lua_Number percent
= lua_tonumber(L
, 1);
377 if (0.0 <= percent
&& percent
<= 1.0) percent
*= 100.0;
378 lua_pushinteger(L
, (lua_Integer
)percent
);
380 if (lua_isstring(L
, 2)) {
381 writestring(fd
, "XXX");
384 writestring(fd
, "XXX");
393 * Display a gauge dialog. Required arguments are dialog title and
394 * caption. Optional arguments include a table of extra flags. Returns a
395 * new function which can be used to update the progress.
397 static int dialog_gauge(lua_State
* L
)
399 const char* argv
[40] = {NULL
};
401 addargs(L
, "--gauge", 3, argv
);
404 pid_t pid
= myexec(argv
[0], (char* const*)argv
, &fd
);
405 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
406 lua_pushinteger(L
, pid
);
407 lua_setfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_pid");
408 lua_pushinteger(L
, fd
);
409 lua_setfield(L
, LUA_REGISTRYINDEX
, "dialog_gauge_fd");
410 lua_pushcfunction(L
, updategauge
);
414 * Display a menu dialog. Required arguments are dialog title, caption,
415 * and table of menu items. Optional arguments are selected item and table
418 static int dialog_menu(lua_State
* L
)
420 const char* argv
[1024] = {NULL
};
426 n
+= addselected(L
, argv
, n
);
427 n
+= addextra(L
, argv
, n
);
428 addstrings(argv
, "--menu", NULL
);
430 addsize(L
, argv
, 1, n
);
431 addmenuitems(L
, argv
);
436 pid_t pid
= myexec(argv
[0], (char* const*)argv
, &fd
);
437 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
438 int code
= mywait(pid
);
439 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
440 lua_pushinteger(L
, code
);
446 * Display a message dialog. Required arguments are dialog title and
447 * caption. Optional arguments include a table of extra flags.
449 static int dialog_msgbox(lua_State
* L
)
451 const char* argv
[40] = {NULL
};
453 addargs(L
, "--msgbox", 3, argv
);
455 pid_t pid
= myexec(argv
[0], (char* const*)argv
, NULL
);
456 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
457 int code
= mywait(pid
);
458 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
459 lua_pushinteger(L
, code
);
464 * Display an input dialog. Required arguments are dialog title and
465 * caption. Optional arguments are the default text and a table of extra
468 static int dialog_inputbox(lua_State
* L
)
470 const char* argv
[40] = {NULL
};
472 addargs(L
, "--inputbox", 4, argv
);
475 pid_t pid
= myexec(argv
[0], (char* const*)argv
, &fd
);
476 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
477 int code
= mywait(pid
);
478 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
479 lua_pushinteger(L
, code
);
485 * Display a yes/no dialog. Required arguments are dialog title and
486 * caption. Optional arguments include a table of extra flags.
488 static int dialog_yesno(lua_State
* L
)
490 const char* argv
[40] = {NULL
};
492 addargs(L
, "--yesno", 3, argv
);
494 pid_t pid
= myexec(argv
[0], (char* const*)argv
, NULL
);
495 if (pid
== -1) luaL_error(L
, "dialog failed to execute");
496 int code
= mywait(pid
);
497 if (code
== -1) luaL_error(L
, "dialog killed abnormally");
498 lua_pushinteger(L
, code
);
504 * Register the module functions and find a dialog command in the path.
506 LUALIB_API
int luaopen_dialog(lua_State
* L
)
508 const struct luaL_Reg dialog_funcs
[] = {
509 {"gauge", dialog_gauge
},
510 {"inputbox", dialog_inputbox
},
511 {"menu", dialog_menu
},
512 {"msgbox", dialog_msgbox
},
513 {"yesno", dialog_yesno
},
516 luaL_register(L
, LUA_DIALOG_NAME
, dialog_funcs
);
518 const char* names
[] = {getenv("DIALOG"), "dialog", "cdialog"};
519 int i
; for (i
= 0; i
< 3; ++i
) {
521 char* path
= strdup(getenv("PATH"));
522 char* token
; char** paths
= &path
;
523 while ((token
= strsep(paths
, ":"))) {
525 luaL_buffinit(L
, &B
);
526 luaL_addstring(&B
, token
);
527 luaL_addstring(&B
, "/");
528 luaL_addstring(&B
, names
[i
]);
530 if (access(lua_tostring(L
, -1), X_OK
) == 0) {
531 lua_setfield(L
, -2, "command");
539 luaL_error(L
, "cannot find dialog executable in the path; set DIALOG");
543 lua_pushinteger(L
, 0);
544 lua_setfield(L
, -2, "height");
545 lua_pushinteger(L
, 0);
546 lua_setfield(L
, -2, "width");
This page took 0.057378 seconds and 4 git commands to generate.