diff --git a/cmd_behave.c b/cmd_behave.c index 4412f8d..70d7a1f 100644 --- a/cmd_behave.c +++ b/cmd_behave.c @@ -13,9 +13,6 @@ struct events { { NULL, 0 }, }; -/* So we can invoke xdotool from within this command */ -extern int context_execute(context_t *context); - int cmd_behave(context_t *context) { int ret = 0; char *cmd = *context->argv; diff --git a/cmd_behave_screen_edge.c b/cmd_behave_screen_edge.c index 3dbfe89..5af5016 100644 --- a/cmd_behave_screen_edge.c +++ b/cmd_behave_screen_edge.c @@ -195,6 +195,10 @@ int cmd_behave_screen_edge(context_t *context) { /* Only create a context copy if we need one */ if (need_new_context) { tmpcontext = calloc(1, sizeof(context_t)); + if (tmpcontext == NULL) { + fprintf(stderr, "%s: error: failed to allocate memory\n", cmd); + exit(EXIT_FAILURE); + } memcpy(tmpcontext, context, sizeof(context_t)); } @@ -304,9 +308,7 @@ int cmd_behave_screen_edge(context_t *context) { } /* if quiesce */ } /* if trigger == True */ - if (tmpcontext != NULL) { - free(tmpcontext); - } + free(tmpcontext); if (ret != XDO_SUCCESS) { printf("Command failed.\n"); diff --git a/cmd_exec.c b/cmd_exec.c index e68f569..6f308b9 100644 --- a/cmd_exec.c +++ b/cmd_exec.c @@ -81,6 +81,10 @@ int cmd_exec(context_t *context) { } command = calloc(context->argc + 1, sizeof(char *)); + if (command == NULL) { + fprintf(stderr, "%s: error: failed to allocate memory\n", cmd); + return EXIT_FAILURE; + } for (i=0; i < context->argc; i++) { if (arity > 0 && i == arity) { @@ -94,6 +98,15 @@ int cmd_exec(context_t *context) { } command[i] = strdup(context->argv[i]); + if (command[i] == NULL) { + fprintf(stderr, "exec: error: failed to allocate memory\n"); + for (;i;i--) { + free(command[i]); + } + free(terminator); + free(command); + return EXIT_FAILURE; + } command_count = i + 1; /* i starts at 0 */ xdotool_debug(context, "Exec arg[%d]: %s", i, command[i]); } @@ -116,9 +129,7 @@ int cmd_exec(context_t *context) { } consume_args(context, command_count); - if (terminator != NULL) { - free(terminator); - } + free(terminator); for (i=0; i < command_count; i++) { free(command[i]); diff --git a/cmd_mousedown.c b/cmd_mousedown.c index 17f992e..88cd087 100644 --- a/cmd_mousedown.c +++ b/cmd_mousedown.c @@ -73,6 +73,7 @@ int cmd_mousedown(context_t *context) { }); /* window_each(...) */ consume_args(context, 1); + free(window_arg); return ret; } diff --git a/cmd_mousemove.c b/cmd_mousemove.c index 2ffe784..4c0c952 100644 --- a/cmd_mousemove.c +++ b/cmd_mousemove.c @@ -131,6 +131,8 @@ int cmd_mousemove(context_t *context) { } }); /* window_each(...) */ + free(window_arg); + return ret; } diff --git a/cmd_mouseup.c b/cmd_mouseup.c index 33f9150..fcb37a0 100644 --- a/cmd_mouseup.c +++ b/cmd_mouseup.c @@ -72,9 +72,7 @@ int cmd_mouseup(context_t *context) { } }); /* window_each(...) */ - if (window_arg != NULL) { - free(window_arg); - } + free(window_arg); consume_args(context, 1); return ret; } diff --git a/cmd_search.c b/cmd_search.c index 3e5cdb1..fc1a97e 100644 --- a/cmd_search.c +++ b/cmd_search.c @@ -190,9 +190,7 @@ int cmd_search(context_t *context) { } do { - if (list != NULL) { - free(list); - } + free(list); xdo_search_windows(context->xdo, &search, &list, &nwindows); @@ -214,9 +212,7 @@ int cmd_search(context_t *context) { } while (op_sync && nwindows == 0); /* Free old list as it's malloc'd by xdo_search_windows */ - if (context->windows != NULL) { - free(context->windows); - } + free(context->windows); context->windows = list; context->nwindows = nwindows; diff --git a/cmd_type.c b/cmd_type.c index 0fa8213..04d76eb 100644 --- a/cmd_type.c +++ b/cmd_type.c @@ -86,10 +86,18 @@ int cmd_type(context_t *context) { break; case opt_terminator: terminator = strdup(optarg); + if (terminator == NULL) { + fprintf(stderr, "type: error: failed to allocate memory\n"); + return EXIT_FAILURE; + } break; case opt_file: - file = strdup(optarg); - break; + file = strdup(optarg); + if (file == NULL) { + fprintf(stderr, "type: error: failed to allocate memory\n"); + return EXIT_FAILURE; + } + break; default: fprintf(stderr, usage, cmd); return EXIT_FAILURE; @@ -101,7 +109,7 @@ int cmd_type(context_t *context) { if (context->argc == 0 && file == NULL) { fprintf(stderr, "You specified the wrong number of args.\n"); fprintf(stderr, usage, cmd); - return 1; + return EXIT_FAILURE; } if (arity > 0 && terminator != NULL) { @@ -117,6 +125,10 @@ int cmd_type(context_t *context) { if (file != NULL) { data = calloc(1 + context->argc, sizeof(char *)); + if (data == NULL) { + fprintf(stderr, "Failure allocating for '%s': %s\n", file, strerror(errno)); + return EXIT_FAILURE; + } /* determine whether reading from a file or from stdin */ if (!strcmp(file, "-")) { @@ -157,6 +169,10 @@ int cmd_type(context_t *context) { } else { data = calloc(context->argc, sizeof(char *)); + if (data == NULL) { + fprintf(stderr, "type: error: failed to allocate memory\n"); + return EXIT_FAILURE; + } } /* Apply any --arity or --terminator */ @@ -174,6 +190,17 @@ int cmd_type(context_t *context) { } data[data_count] = strdup(context->argv[i]); + if (data[data_count] == NULL) { + fprintf(stderr, "type: error: failed to allocate memory\n"); + ret = 0; /* EXIT_FAILURE */ + for (i = 0; i < data_count; i++) + free(data[i]); + free(data); + free(file); + free(terminator); + return EXIT_FAILURE; + } + xdotool_debug(context, "Exec arg[%d]: %s", i, data[data_count]); data_count++; args_count++; @@ -202,9 +229,14 @@ int cmd_type(context_t *context) { } }); /* window_each(...) */ + consume_args(context, args_count); + + for (i = 0; i < data_count; i++) + free(data[i]); free(data); + free(file); + free(terminator); - consume_args(context, args_count); return ret > 0; } diff --git a/xdo.c b/xdo.c index e6756f3..9c69d96 100644 --- a/xdo.c +++ b/xdo.c @@ -107,13 +107,20 @@ xdo_t* xdo_new(const char *display_name) { return xdo_new_with_opened_display(xdpy, display_name, 1); } +/* Output error on stderr, without formatting. + * Might be called in low-memory conditions so no printf */ +static void early_error(const char *msg) { + const size_t len = strlen(msg); + write(STDERR_FILENO, msg, len); +} + xdo_t* xdo_new_with_opened_display(Display *xdpy, const char *display, int close_display_when_freed) { xdo_t *xdo = NULL; if (xdpy == NULL) { /* Can't use _xdo_eprintf yet ... */ - fprintf(stderr, "xdo_new: xdisplay I was given is a null pointer\n"); + early_error("xdo_new: xdisplay I was given is a null pointer\n"); return NULL; } @@ -126,10 +133,11 @@ xdo_t* xdo_new_with_opened_display(Display *xdpy, const char *display, //return NULL; //} - - /* XXX: Check for NULL here */ - xdo = malloc(sizeof(xdo_t)); - memset(xdo, 0, sizeof(xdo_t)); + xdo = calloc(1, sizeof(xdo_t)); + if (xdo == NULL) { + early_error("xdo_new: out of memory allocating the main context\n"); + return NULL; + } xdo->xdpy = xdpy; xdo->close_display_when_freed = close_display_when_freed; @@ -160,10 +168,8 @@ void xdo_free(xdo_t *xdo) { if (xdo == NULL) return; - if (xdo->display_name) - free(xdo->display_name); - if (xdo->charcodes) - free(xdo->charcodes); + free(xdo->display_name); + free(xdo->charcodes); if (xdo->xdpy && xdo->close_display_when_freed) XCloseDisplay(xdo->xdpy); @@ -359,6 +365,10 @@ int xdo_set_window_class (const xdo_t *xdo, Window wid, const char *name, const char *_class) { int ret = 0; XClassHint *hint = XAllocClassHint(); + if (hint == NULL) { + fprintf(stderr, "error: failed to allocate ClassHint\n"); + return 1; + } XGetClassHint(xdo->xdpy, wid, hint); if (name != NULL) hint->res_name = (char*)name; @@ -376,6 +386,10 @@ int xdo_set_window_urgency (const xdo_t *xdo, Window wid, int urgency) { XWMHints *hint = XGetWMHints(xdo->xdpy, wid); if (hint == NULL) hint = XAllocWMHints(); + if (hint == NULL) { + fprintf(stderr, "error: failed to allocate WMHints\n"); + return 1; + } if (urgency) hint->flags = hint->flags | XUrgencyHint; @@ -1038,9 +1052,7 @@ int _xdo_send_keysequence_window_do(const xdo_t *xdo, Window window, const char } ret = xdo_send_keysequence_window_list_do(xdo, window, keys, nkeys, pressed, modifier, delay); - if (keys != NULL) { - free(keys); - } + free(keys); return ret; } @@ -1061,7 +1073,7 @@ int xdo_send_keysequence_window_list_do(const xdo_t *xdo, Window window, charcod /* Find a keycode that is unused for scratchspace */ for (i = xdo->keycode_low; i <= xdo->keycode_high; i++) { - int j = 0; + int j; int key_is_empty = 1; for (j = 0; j < keysyms_per_keycode; j++) { /*char *symname;*/ @@ -1203,6 +1215,7 @@ int xdo_find_window_client(const xdo_t *xdo, Window window, Window *window_ret, Window dummy, parent, *children = NULL; unsigned int nchildren; Atom atom_wmstate = XInternAtom(xdo->xdpy, "WM_STATE", False); + unsigned char *data; int done = False; while (!done) { @@ -1212,7 +1225,8 @@ int xdo_find_window_client(const xdo_t *xdo, Window window, Window *window_ret, long items; _xdo_debug(xdo, "get_window_property on %lu", window); - xdo_get_window_property_by_atom(xdo, window, atom_wmstate, &items, NULL, NULL); + data = xdo_get_window_property_by_atom(xdo, window, atom_wmstate, &items, NULL, NULL); + XFree(data); if (items == 0) { /* This window doesn't have WM_STATE property, keep searching. */ @@ -1292,16 +1306,11 @@ static void _xdo_charcodemap_from_char(const xdo_t *xdo, charcodemap_t *key) { } static void _xdo_charcodemap_from_keysym(const xdo_t *xdo, charcodemap_t *key, KeySym keysym) { - int i = 0; - int len = xdo->charcodes_len; + int i; - key->code = 0; key->symbol = keysym; - key->group = 0; - key->modmask = 0; - key->needs_binding = 1; - for (i = 0; i < len; i++) { + for (i = 0; i < xdo->charcodes_len; i++) { if (xdo->charcodes[i].symbol == keysym) { key->code = xdo->charcodes[i].code; key->group = xdo->charcodes[i].group; @@ -1310,6 +1319,10 @@ static void _xdo_charcodemap_from_keysym(const xdo_t *xdo, charcodemap_t *key, K return; } } + key->code = 0; + key->group = 0; + key->modmask = 0; + key->needs_binding = 1; } static int _xdo_has_xtest(const xdo_t *xdo) { @@ -1336,6 +1349,10 @@ static void _xdo_populate_charcode_map(xdo_t *xdo) { * xdo->keysyms_per_keycode; xdo->charcodes = calloc(keycodes_length, sizeof(charcodemap_t)); + if (xdo->charcodes == NULL) { + fprintf(stderr, "error: failed to allocate charcodes\n"); + exit(EXIT_FAILURE); + } XkbDescPtr desc = XkbGetMap(xdo->xdpy, XkbAllClientInfoMask, XkbUseCoreKbd); for (keycode = xdo->keycode_low; keycode <= xdo->keycode_high; keycode++) { @@ -1365,7 +1382,7 @@ static void _xdo_populate_charcode_map(xdo_t *xdo) { } } xdo->charcodes_len = idx; - XkbFreeClientMap(desc, 0, 1); + XkbFreeKeyboard(desc, 0, 1); XFreeModifiermap(modmap); } @@ -1392,6 +1409,12 @@ int _xdo_send_keysequence_window_to_keycode_list(const xdo_t *xdo, const char *k *nkeys = 0; *keys = calloc(keys_size, sizeof(charcodemap_t)); keyseq_copy = strptr = strdup(keyseq); + if (*keys == NULL || keyseq_copy == NULL) { + fprintf(stderr, "error: failed to allocate keys\n"); + free(*keys); + free(keyseq_copy); + return False; + } while ((tok = strtok_r(strptr, "+", &tokctx)) != NULL) { KeySym sym; KeyCode key; @@ -1430,7 +1453,12 @@ int _xdo_send_keysequence_window_to_keycode_list(const xdo_t *xdo, const char *k (*nkeys)++; if (*nkeys == keys_size) { keys_size *= 2; - *keys = realloc(*keys, keys_size * sizeof(KeyCode)); + *keys = realloc(*keys, keys_size * sizeof(charcodemap_t)); + if (*keys == NULL) { + fprintf(stderr, "error: failed to allocate keys\n"); + free(keyseq_copy); + return False; + } } } @@ -1466,6 +1494,18 @@ unsigned char *xdo_get_window_property_by_atom(const xdo_t *xdo, Window window, unsigned char *prop; int status; + if (nitems != NULL) { + *nitems = 0; + } + + if (type != NULL) { + *type = 0; + } + + if (size != NULL) { + *size = 0; + } + status = XGetWindowProperty(xdo->xdpy, window, atom, 0, (~0L), False, AnyPropertyType, &actual_type, &actual_format, &_nitems, &bytes_after, @@ -1644,6 +1684,11 @@ int xdo_get_active_modifiers(const xdo_t *xdo, charcodemap_t **keys, XModifierKeymap *modifiers = XGetModifierMapping(xdo->xdpy); *nkeys = 0; *keys = malloc(keys_size * sizeof(charcodemap_t)); + if (*keys == NULL) { + fprintf(stderr, "error: failed to allocate keys\n"); + XFreeModifiermap(modifiers); + return False; + } XQueryKeymap(xdo->xdpy, keymap); @@ -1666,10 +1711,15 @@ int xdo_get_active_modifiers(const xdo_t *xdo, charcodemap_t **keys, if (*nkeys == keys_size) { keys_size *= 2; *keys = realloc(keys, keys_size * sizeof(charcodemap_t)); + if (*keys == NULL) { + fprintf(stderr, "error: failed to allocate keys\n"); + XFreeModifiermap(modifiers); + return False; + } } } } - } + } XFreeModifiermap(modifiers); @@ -1936,12 +1986,16 @@ int xdo_get_window_name(const xdo_t *xdo, Window window, return 0; } -int xdo_get_window_classname(const xdo_t *xdo, Window window, unsigned char **name_ret) { +int xdo_get_window_classname(const xdo_t *xdo, Window window, unsigned char **class_ret) { XClassHint classhint; - XGetClassHint(xdo->xdpy, window, &classhint); - XFree(classhint.res_name); - *name_ret = (unsigned char*) classhint.res_class; - return 0; + Status ret = XGetClassHint(xdo->xdpy, window, &classhint); + + if (ret) { + XFree(classhint.res_name); + *class_ret = (unsigned char*) classhint.res_class; + } else + *class_ret = NULL; + return _is_success("XGetClassHint[WM_CLASS]", ret == 0, xdo); } int xdo_window_state(xdo_t *xdo, Window window, unsigned long action, const char *property) { @@ -2058,4 +2112,4 @@ static int appears_to_be_wayland(Display *xdpy) { XFreeDeviceList(devices); return 0; -} \ No newline at end of file +} diff --git a/xdo.h b/xdo.h index 7a716ff..3ede944 100644 --- a/xdo.h +++ b/xdo.h @@ -618,6 +618,13 @@ int xdo_minimize_window(const xdo_t *xdo, Window wid); #define _NET_WM_STATE_ADD 1 /* add/set property */ #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ +/** + * Get window classname + * @param window the window + * @param class_ret Pointer to the window classname WM_CLASS + */ +int xdo_get_window_classname(const xdo_t *xdo, Window window, unsigned char **class_ret); + /** * Change window state * @param action the _NET_WM_STATE action diff --git a/xdo_cmd.h b/xdo_cmd.h index 8c3313f..83e5c1c 100644 --- a/xdo_cmd.h +++ b/xdo_cmd.h @@ -32,10 +32,11 @@ extern void window_list(context_t *context, const char *window_arg, extern void window_save(context_t *context, Window window); extern int is_command(char *cmd); -extern int window_is_valid(context_t *context, const char *window_arg); extern int window_get_arg(context_t *context, int min_arg, int window_arg_pos, const char **window_arg); +extern int context_execute(context_t *); + extern void xdotool_debug(context_t *context, const char *format, ...); extern void xdotool_output(context_t *context, const char *format, ...); diff --git a/xdotool.c b/xdotool.c index 62e0229..af73b82 100644 --- a/xdotool.c +++ b/xdotool.c @@ -5,7 +5,7 @@ * getwindowfocus contributed by Lee Pumphret * keyup/down contributed by Lee Pumphret * - * vim:expandtab shiftwidth=2 softtabstop=2 + * vi: expandtab shiftwidth=2 softtabstop=2 */ #define _GNU_SOURCE 1 @@ -27,21 +27,10 @@ #include "xdo.h" #include "xdotool.h" +#include "xdo_cmd.h" static int script_main(int argc, char **argv); static int args_main(int argc, char **argv); -int context_execute(context_t *context); -void consume_args(context_t *context, int argc); -void window_save(context_t *context, Window window); -void window_list(context_t *context, const char *window_arg, - Window **windowlist_ret, int *nwindows_ret, - const int add_to_list); -int window_get_arg(context_t *context, int min_arg, int window_arg_pos, - const char **window_arg); -int window_is_valid(context_t *context, const char *window_arg); -int is_command(char* cmd); -void xdotool_debug(context_t *context, const char *format, ...); -void xdotool_output(context_t *context, const char *format, ...); void consume_args(context_t *context, int argc) { if (argc > context->argc) { @@ -58,16 +47,14 @@ void consume_args(context_t *context, int argc) { } /* void consume_args(context_t *, int) */ void window_save(context_t *context, Window window) { - if (context->windows != NULL) { - free(context->windows); - } + free(context->windows); context->windows = calloc(1, sizeof(Window)); context->nwindows = 1; context->windows[0] = window; } /* void window_save(context_t *, Window) */ -int window_is_valid(context_t *context, const char *window_arg) { +static int window_is_valid(context_t *context, const char *window_arg) { if (window_arg == NULL) { return True; } @@ -323,13 +310,15 @@ int script_main(int argc, char **argv) { * args_main(). */ + int result = XDO_SUCCESS; FILE *input = NULL; - const char *path = argv[1]; char buffer[4096]; - - char **script_argv = (char **) calloc(1, sizeof(char *)); int script_argc = 0; int script_argc_max = 0; + size_t token_len, total_len = 0; + char *token = NULL; + const char *path = argv[1]; + char **script_argv = NULL; /* determine whether reading from a file or from stdin */ if (!strcmp(path, "-")) { @@ -352,34 +341,29 @@ int script_main(int argc, char **argv) { if (context.xdo == NULL) { fprintf(stderr, "Failed creating new xdo instance\n"); - return 1; + fclose(input); + return EXIT_FAILURE; } context.xdo->debug = context.debug; - + /* read input... */ - int pos; - char *token; - int result = XDO_SUCCESS; - while (fgets(buffer, 4096, input) != NULL) { + while (fgets(buffer, sizeof(buffer), input) != NULL) { char *line = buffer; - token = NULL; - - /* Ignore leading whitespace */ - line += strspn(line, " \t"); + const size_t end = strcspn(line, "\n"); - /* blanklines or line comment are ignored, too */ - if (line[0] == '\n' || line[0] == '#') { - continue; - } - - /* replace newline with null */ - if (line[strlen(line)-1] == '\n') - line[strlen(line)-1] = '\0'; + /* replace newline with nul */ + line[end] = '\0'; /* tokenize line into script_argv... */ - while (strlen(line)) { - token = NULL; + while (*line != '\0') { + /* skip leading whitespace */ + line += strspn(line, " \t"); + /* ignore comment lines */ + if (line[0] == '#') { + line += strlen(line); + continue; + } /* modify line to contain the current token. Tokens are * separated by whitespace, or quoted with single/double quotes. @@ -402,10 +386,11 @@ int script_main(int argc, char **argv) { */ if (line[0] == '$') { /* ignore dollar sign */ - line++; - + line++; + if (isdigit(line[0])) { - /* get the position of this parameter in argv */ + int pos; + /* get the position of this parameter in argv */ pos = atoi(line) + 1; /* $1 is actually index 2 in the argv array */ /* bail if no argument was given for this parameter */ @@ -413,6 +398,13 @@ int script_main(int argc, char **argv) { fprintf (stderr, "%s: error: `%s' needs at least %d %s; only %d given\n", argv[0], argv[1], pos - 1, pos == 2 ? "argument" : "arguments", argc - 2); + fclose(input); + xdo_free(context.xdo); + free(context.windows); + for (int i=0; i script_argc_max){ - script_argv = realloc(script_argv, (script_argc + 1) * sizeof(char *)); - script_argc_max++; - } + /* append token */ + /* allocate memory for the new token and final NULL if needed */ + if (script_argc + 1 > script_argc_max) { + script_argv = realloc(script_argv, (script_argc + 2) * sizeof(char *)); if (script_argv == NULL) { - fprintf(stderr, "%s: error: failed to allocate memory while parsing `%s'.\n", + fprintf(stderr, "%s: error: failed to allocate memory while parsing `%s'.\n", argv[0], argv[1]); + fclose(input); + xdo_free(context.xdo); + free(context.windows); exit(EXIT_FAILURE); } - script_argv[script_argc] = (char *) calloc(strlen(token) + 1, sizeof(char)); + script_argc_max = script_argc + 2; + script_argv[script_argc+0] = NULL; + script_argv[script_argc+1] = NULL; /* argv terminating NULL */ + } - //printf("arg %d: %s\n", script_argc, token); - strcpy(script_argv[script_argc], token); - script_argc++; + script_argv[script_argc] = realloc(script_argv[script_argc], + (total_len + token_len + 1) * sizeof(char) + ); + if (script_argv[script_argc] == NULL) { + fprintf(stderr, "%s: error: failed to allocate memory while parsing `%s'.\n", + argv[0], argv[1]); + fclose(input); + xdo_free(context.xdo); + free(context.windows); + for (int i=0; i 0){ + if (end < sizeof(buffer) - 1 && script_argc > 0){ context.argc = script_argc; context.argv = script_argv; result = context_execute(&context); @@ -478,26 +506,20 @@ int script_main(int argc, char **argv) { /* * Free the allocated memory for tokens. */ - for(int j = 0; j < script_argc; j++){ - if(*(script_argv + j) != NULL){ - free(*(script_argv + j)); - } + for(int i = 0; i <= script_argc; i++){ + free(script_argv[i]); + *(script_argv + i) = NULL; } - script_argc = 0; - *script_argv = NULL; } } fclose(input); - xdo_free(context.xdo); - if (context.windows != NULL) { - free(context.windows); - } + free(context.windows); - for(int i=0; i 100% 50% + xdotool windowsize 100% 50% Percentages are valid with --usehints and still mean pixel-width relative to the screen size. @@ -598,7 +598,7 @@ necessary to the requested size. =back Example: To set a terminal to be 80x24 characters, you would use: - xdotool windowsize --usehints I 80 24 + xdotool windowsize --usehints 80 24 =item B I<[options]> I<[window]> I I @@ -619,7 +619,7 @@ Percentages are valid for width and height. They are relative to the geometry of the screen the window is on. For example, to make a window the full width of the screen, but half height: - xdotool windowmove I 100% 50% + xdotool windowmove 100% 50% =over