int config_parse_path(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { char **s = data; char *n; assert(filename); assert(lvalue); assert(rvalue); assert(data); if (!utf8_is_valid(rvalue)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path is not UTF-8 clean, ignoring assignment: %s", rvalue); return 0; } if (!path_is_absolute(rvalue)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue); return 0; } n = strdup(rvalue); if (!n) return log_oom(); path_kill_slashes(n); free(*s); *s = n; return 0; }
bool ConvertString(CString &str, const CString &encodings) { CStringArray lst; split(encodings, ",", lst); for(unsigned i = 0; i < lst.size(); ++i) { if( lst[i] == "utf-8" ) { /* Is the string already valid utf-8? */ if( utf8_is_valid(str) ) return true; continue; } if( lst[i] == "english" ) { if(AttemptEnglishConversion(str)) return true; continue; } if( lst[i] == "japanese" ) { if(AttemptJapaneseConversion(str)) return true; continue; } if( lst[i] == "korean" ) { if(AttemptKoreanConversion(str)) return true; continue; } RageException::Throw( "Unexpected conversion string \"%s\" (string \"%s\")", lst[i].c_str(), str.c_str() ); } return false; }
int config_parse_string(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { char **s = data; char *n; assert(filename); assert(lvalue); assert(rvalue); assert(data); n = strdup(rvalue); if (!n) return log_oom(); if (!utf8_is_valid(n)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "String is not UTF-8 clean, ignoring assignment: %s", rvalue); free(n); return 0; } free(*s); if (*n) *s = n; else { free(n); *s = NULL; } return 0; }
bool env_value_is_valid(const char *e) { if (!e) return false; if (!utf8_is_valid(e)) return false; /* bash allows tabs in environment variables, and so should * we */ if (string_has_cc(e, "\t")) return false; /* POSIX says the overall size of the environment block cannot * be > ARG_MAX, an individual assignment hence cannot be * either. Discounting the shortest possible variable name of * length 1, the equal sign and trailing NUL this hence leaves * ARG_MAX-3 as longest possible variable value. */ if (strlen(e) > ARG_MAX - 3) return false; return true; }
int utf8_luacall_utf8_isvalid(lua_State *l) { size_t bufsize; if (lua_type(l, 1) != LUA_TSTRING) { lua_pushstring(l, "invalid argument #1: not a string"); return lua_error(l); } const char *p = luaL_checklstring(l, 1, &bufsize); if (!p) { lua_pushstring(l, "failed to obtain string buffer"); return lua_error(l); } if (bufsize == 0) { lua_pushboolean(l, 1); return 1; } if (!utf8_is_valid((unsigned char*) p, bufsize)) { lua_pushboolean(l, 0); return 1; } lua_pushboolean(l, 1); return 1; }
int config_parse_string( const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) { char **s = data, *n; assert(filename); assert(lvalue); assert(rvalue); assert(data); if (!utf8_is_valid(rvalue)) { log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); return 0; } if (isempty(rvalue)) n = NULL; else { n = strdup(rvalue); if (!n) return log_oom(); } free(*s); *s = n; return 0; }
TEST(Utf8, Validity) { char *error; /* check 8 bits */ LONGS_EQUAL(0, utf8_has_8bits (NULL)); LONGS_EQUAL(0, utf8_has_8bits ("")); LONGS_EQUAL(0, utf8_has_8bits ("abc")); LONGS_EQUAL(1, utf8_has_8bits ("no\xc3\xabl")); /* check validity */ LONGS_EQUAL(1, utf8_is_valid (NULL, NULL)); LONGS_EQUAL(1, utf8_is_valid (NULL, &error)); LONGS_EQUAL(1, utf8_is_valid ("", NULL)); LONGS_EQUAL(1, utf8_is_valid ("", &error)); LONGS_EQUAL(1, utf8_is_valid ("abc", &error)); POINTERS_EQUAL(NULL, error); LONGS_EQUAL(1, utf8_is_valid (noel_valid, &error)); POINTERS_EQUAL(NULL, error); LONGS_EQUAL(0, utf8_is_valid (noel_invalid, &error)); POINTERS_EQUAL(noel_invalid + 2, error); /* 2 bytes: code point must be in range U+0080-07FF */ LONGS_EQUAL(0, utf8_is_valid ("\xc0\x80", NULL)); /* U+0 */ LONGS_EQUAL(0, utf8_is_valid ("\xc1\xbf", NULL)); /* U+7F */ LONGS_EQUAL(1, utf8_is_valid ("\xc2\x80", NULL)); /* U+80 */ LONGS_EQUAL(1, utf8_is_valid ("\xdf\xbf", NULL)); /* U+7FF */ /* 3 bytes: code point must be in range: U+0800-FFFF */ LONGS_EQUAL(0, utf8_is_valid ("\xe0\x80\x80", NULL)); /* U+0 */ LONGS_EQUAL(0, utf8_is_valid ("\xe0\x9f\xbf", NULL)); /* U+7FF */ LONGS_EQUAL(0, utf8_is_valid ("\xed\xa0\x80", NULL)); /* U+D800 */ LONGS_EQUAL(0, utf8_is_valid ("\xed\xbf\xbf", NULL)); /* U+DFFF */ LONGS_EQUAL(1, utf8_is_valid ("\xe0\xa0\x80", NULL)); /* U+800 */ LONGS_EQUAL(1, utf8_is_valid ("\xed\x9f\xbf", NULL)); /* U+D7FF */ LONGS_EQUAL(1, utf8_is_valid ("\xe7\x80\x80", NULL)); /* U+E000 */ LONGS_EQUAL(1, utf8_is_valid ("\xef\xbf\xbf", NULL)); /* U+FFFF */ /* 4 bytes: code point must be in range: U+10000-1FFFFF */ LONGS_EQUAL(0, utf8_is_valid ("\xf0\x80\x80\x80", NULL)); /* U+0 */ LONGS_EQUAL(0, utf8_is_valid ("\xf0\x8f\xbf\xbf", NULL)); /* U+FFFF */ LONGS_EQUAL(1, utf8_is_valid ("\xf0\x90\x80\x80", NULL)); /* U+10000 */ LONGS_EQUAL(1, utf8_is_valid ("\xf7\xbf\xbf\xbf", NULL)); /* U+1FFFFF */ }
/* Parse a single logical line */ static int parse_line( const char* unit, const char *filename, unsigned line, const char *sections, ConfigItemLookup lookup, const void *table, ConfigParseFlags flags, char **section, unsigned *section_line, bool *section_ignored, char *l, void *userdata) { char *e, *include; assert(filename); assert(line > 0); assert(lookup); assert(l); l = strstrip(l); if (!*l) return 0; if (strchr(COMMENTS "\n", *l)) return 0; include = first_word(l, ".include"); if (include) { _cleanup_free_ char *fn = NULL; /* .includes are a bad idea, we only support them here * for historical reasons. They create cyclic include * problems and make it difficult to detect * configuration file changes with an easy * stat(). Better approaches, such as .d/ drop-in * snippets exist. * * Support for them should be eventually removed. */ if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) { log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring."); return 0; } log_syntax(unit, LOG_WARNING, filename, line, 0, ".include directives are deprecated, and support for them will be removed in a future version of systemd. " "Please use drop-in files instead."); fn = file_in_same_dir(filename, strstrip(include)); if (!fn) return -ENOMEM; return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata); } if (!utf8_is_valid(l)) return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l); if (*l == '[') { size_t k; char *n; k = strlen(l); assert(k > 0); if (l[k-1] != ']') { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l); return -EBADMSG; } n = strndup(l+1, k-2); if (!n) return -ENOMEM; if (sections && !nulstr_contains(sections, n)) { if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-")) log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); free(n); *section = mfree(*section); *section_line = 0; *section_ignored = true; } else { free_and_replace(*section, n); *section_line = line; *section_ignored = false; } return 0; } if (sections && !*section) { if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored) log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring."); return 0; } e = strchr(l, '='); if (!e) { log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='."); return -EINVAL; } *e = 0; e++; return next_assignment(unit, filename, line, lookup, table, *section, *section_line, strstrip(l), strstrip(e), flags, userdata); }
int take_etc_passwd_lock(const char *root) { struct flock flock = { .l_type = F_WRLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = 0, }; const char *path; int fd, r; /* This is roughly the same as lckpwdf(), but not as awful. We * don't want to use alarm() and signals, hence we implement * our own trivial version of this. * * Note that shadow-utils also takes per-database locks in * addition to lckpwdf(). However, we don't given that they * are redundant as they invoke lckpwdf() first and keep * it during everything they do. The per-database locks are * awfully racy, and thus we just won't do them. */ if (root) path = prefix_roota(root, "/etc/.pwd.lock"); else path = "/etc/.pwd.lock"; fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600); if (fd < 0) return -errno; r = fcntl(fd, F_SETLKW, &flock); if (r < 0) { safe_close(fd); return -errno; } return fd; } bool valid_user_group_name(const char *u) { const char *i; long sz; /* Checks if the specified name is a valid user/group name. */ if (isempty(u)) return false; if (!(u[0] >= 'a' && u[0] <= 'z') && !(u[0] >= 'A' && u[0] <= 'Z') && u[0] != '_') return false; for (i = u+1; *i; i++) { if (!(*i >= 'a' && *i <= 'z') && !(*i >= 'A' && *i <= 'Z') && !(*i >= '0' && *i <= '9') && *i != '_' && *i != '-') return false; } sz = sysconf(_SC_LOGIN_NAME_MAX); assert_se(sz > 0); if ((size_t) (i-u) > (size_t) sz) return false; if ((size_t) (i-u) > UT_NAMESIZE - 1) return false; return true; } bool valid_user_group_name_or_id(const char *u) { /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right * range, and not the invalid user ids. */ if (isempty(u)) return false; if (valid_user_group_name(u)) return true; return parse_uid(u, NULL) >= 0; } bool valid_gecos(const char *d) { if (!d) return false; if (!utf8_is_valid(d)) return false; if (string_has_cc(d, NULL)) return false; /* Colons are used as field separators, and hence not OK */ if (strchr(d, ':')) return false; return true; } bool valid_home(const char *p) { if (isempty(p)) return false; if (!utf8_is_valid(p)) return false; if (string_has_cc(p, NULL)) return false; if (!path_is_absolute(p)) return false; if (!path_is_safe(p)) return false; /* Colons are used as field separators, and hence not OK */ if (strchr(p, ':')) return false; return true; } int maybe_setgroups(size_t size, const gid_t *list) { int r; /* Check if setgroups is allowed before we try to drop all the auxiliary groups */ if (size == 0) { /* Dropping all aux groups? */ _cleanup_free_ char *setgroups_content = NULL; bool can_setgroups; r = read_one_line_file("/proc/self/setgroups", &setgroups_content); if (r == -ENOENT) /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */ can_setgroups = true; else if (r < 0) return r; else can_setgroups = streq(setgroups_content, "allow"); if (!can_setgroups) { log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'"); return 0; } } if (setgroups(size, list) < 0) return -errno; return 0; }
static int add_locales_from_archive(Set *locales) { /* Stolen from glibc... */ struct locarhead { uint32_t magic; /* Serial number. */ uint32_t serial; /* Name hash table. */ uint32_t namehash_offset; uint32_t namehash_used; uint32_t namehash_size; /* String table. */ uint32_t string_offset; uint32_t string_used; uint32_t string_size; /* Table with locale records. */ uint32_t locrectab_offset; uint32_t locrectab_used; uint32_t locrectab_size; /* MD5 sum hash table. */ uint32_t sumhash_offset; uint32_t sumhash_used; uint32_t sumhash_size; }; struct namehashent { /* Hash value of the name. */ uint32_t hashval; /* Offset of the name in the string table. */ uint32_t name_offset; /* Offset of the locale record. */ uint32_t locrec_offset; }; const struct locarhead *h; const struct namehashent *e; const void *p = MAP_FAILED; _cleanup_close_ int fd = -1; size_t sz = 0; struct stat st; size_t i; int r; fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) return errno == ENOENT ? 0 : -errno; if (fstat(fd, &st) < 0) return -errno; if (!S_ISREG(st.st_mode)) return -EBADMSG; if (st.st_size < (off_t) sizeof(struct locarhead)) return -EBADMSG; p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (p == MAP_FAILED) return -errno; h = (const struct locarhead *) p; if (h->magic != 0xde020109 || h->namehash_offset + h->namehash_size > st.st_size || h->string_offset + h->string_size > st.st_size || h->locrectab_offset + h->locrectab_size > st.st_size || h->sumhash_offset + h->sumhash_size > st.st_size) { r = -EBADMSG; goto finish; } e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset); for (i = 0; i < h->namehash_size; i++) { char *z; if (e[i].locrec_offset == 0) continue; if (!utf8_is_valid((char*) p + e[i].name_offset)) continue; z = strdup((char*) p + e[i].name_offset); if (!z) { r = -ENOMEM; goto finish; } r = set_consume(locales, z); if (r < 0) goto finish; } r = 0; finish: if (p != MAP_FAILED) munmap((void*) p, sz); return r; }
static void test_utf8_is_valid(void) { assert_se(utf8_is_valid("ascii is valid unicode")); assert_se(utf8_is_valid("\342\204\242")); assert_se(!utf8_is_valid("\341\204")); }
const char * gui_mouse_event_code2key (const char *code) { int i, x, y, code_utf8, length; double diff_x, diff_y, distance, angle, pi4; static char key[128]; const char *ptr_code; key[0] = '\0'; /* * mouse code must have at least: * one code (for event) + X + Y == 3 bytes or 3 UTF-8 chars */ code_utf8 = utf8_is_valid (code, NULL); length = (code_utf8) ? utf8_strlen (code) : (int)strlen (code); if (length < 3) return NULL; /* get coordinates and button */ if (code_utf8) { /* get coordinates using UTF-8 chars in code */ x = utf8_char_int (code + 1) - 33; ptr_code = utf8_next_char (code + 1); if (!ptr_code) return NULL; y = utf8_char_int (ptr_code) - 33; } else { /* get coordinates using ISO chars in code */ x = ((unsigned char)code[1]) - 33; y = ((unsigned char)code[2]) - 33; } if (x < 0) x = 0; if (y < 0) y = 0; /* ignore code if it's motion/end code received as first event */ if ((gui_mouse_event_index == 0) && (MOUSE_CODE_MOTION(code[0]) || MOUSE_CODE_END(code[0]))) { return NULL; } /* set data in "gui_mouse_event_xxx" */ gui_mouse_event_x[gui_mouse_event_index] = x; gui_mouse_event_y[gui_mouse_event_index] = y; if (gui_mouse_event_index == 0) gui_mouse_event_button = code[0]; if (gui_mouse_event_index == 0) gui_mouse_event_index = 1; /* * browse wheel codes, if one code is found, return event name immediately */ for (i = 0; gui_mouse_wheel_codes[i][0]; i++) { if (code[0] == gui_mouse_wheel_codes[i][0][0]) { strcat (key, gui_mouse_wheel_codes[i][1]); gui_mouse_event_x[1] = gui_mouse_event_x[0]; gui_mouse_event_y[1] = gui_mouse_event_y[0]; return key; } } /* add name of button event */ for (i = 0; gui_mouse_button_codes[i][0]; i++) { if (gui_mouse_event_button == gui_mouse_button_codes[i][0][0]) { strcat (key, gui_mouse_button_codes[i][1]); break; } } /* nothing found, reset now or mouse will be stuck */ if (!key[0]) { gui_mouse_event_reset (); return NULL; } if (!MOUSE_CODE_END(code[0])) { strcat (key, "-event-"); if (MOUSE_CODE_MOTION(code[0])) { strcat (key, "drag"); } else { gui_mouse_event_x[1] = gui_mouse_event_x[0]; gui_mouse_event_y[1] = gui_mouse_event_y[0]; strcat (key, "down"); } return key; } /* * Mouse gesture: if (x,y) on release is different from (x,y) on click, * compute distance and angle between 2 points. * * Distance: sqrt((x2-x1)²+(y2-y1)²) * Angle : atan2(x1-x1, y2-y1) * * Angle: * * 3.14 pi * /\ * -2.35 || 2.35 3/4 * pi * || * -1.57 /----++----\ 1.57 1/2 * pi * \----++----/ * || * -0.78 || 0.78 1/4 * pi * \/ * 0.00 0 * * Possible returned gestures are: * * key name | dist. | angle * ---------------------------+-------+-------------------------- * buttonX-gesture-up | 3..19 | -2.35..-3.14 + 2.35..3.14 * buttonX-gesture-up-long | >= 20 | * buttonX-gesture-down | 3..19 | -0.78..0.78 * buttonX-gesture-down-long | >= 20 | * buttonX-gesture-left | 3..39 | -0.78..-2.35 * buttonX-gesture-left-long | >= 40 | * buttonX-gesture-right | 3..39 | 0.78..2.35 * buttonX-gesture-right-long | >= 40 | */ if (key[0] && ((gui_mouse_event_x[0] != gui_mouse_event_x[1]) || (gui_mouse_event_y[0] != gui_mouse_event_y[1]))) { diff_x = gui_mouse_event_x[1] - gui_mouse_event_x[0]; diff_y = gui_mouse_event_y[1] - gui_mouse_event_y[0]; distance = sqrt ((diff_x * diff_x) + (diff_y * diff_y)); if (distance >= 3) { angle = atan2 ((double)(gui_mouse_event_x[1] - gui_mouse_event_x[0]), (double)(gui_mouse_event_y[1] - gui_mouse_event_y[0])); pi4 = 3.14159265358979 / 4; if ((angle <= pi4 * (-3)) || (angle >= pi4 * 3)) { strcat (key, "-gesture-up"); if (distance >= 20) strcat (key, "-long"); } else if ((angle >= pi4 * (-1)) && (angle <= pi4)) { strcat (key, "-gesture-down"); if (distance >= 20) strcat (key, "-long"); } else if ((angle >= pi4 * (-3)) && (angle <= pi4 * (-1))) { strcat (key, "-gesture-left"); if (distance >= 40) strcat (key, "-long"); } else if ((angle >= pi4) && (angle <= pi4 * 3)) { strcat (key, "-gesture-right"); if (distance >= 40) strcat (key, "-long"); } } } return key; }
int take_etc_passwd_lock(const char *root) { struct flock flock = { .l_type = F_WRLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = 0, }; const char *path; int fd, r; /* This is roughly the same as lckpwdf(), but not as awful. We * don't want to use alarm() and signals, hence we implement * our own trivial version of this. * * Note that shadow-utils also takes per-database locks in * addition to lckpwdf(). However, we don't given that they * are redundant as they invoke lckpwdf() first and keep * it during everything they do. The per-database locks are * awfully racy, and thus we just won't do them. */ if (root) path = prefix_roota(root, "/etc/.pwd.lock"); else path = "/etc/.pwd.lock"; fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600); if (fd < 0) return -errno; r = fcntl(fd, F_SETLKW, &flock); if (r < 0) { safe_close(fd); return -errno; } return fd; } bool valid_user_group_name(const char *u) { const char *i; long sz; /* Checks if the specified name is a valid user/group name. */ if (isempty(u)) return false; if (!(u[0] >= 'a' && u[0] <= 'z') && !(u[0] >= 'A' && u[0] <= 'Z') && u[0] != '_') return false; for (i = u+1; *i; i++) { if (!(*i >= 'a' && *i <= 'z') && !(*i >= 'A' && *i <= 'Z') && !(*i >= '0' && *i <= '9') && *i != '_' && *i != '-') return false; } sz = sysconf(_SC_LOGIN_NAME_MAX); assert_se(sz > 0); if ((size_t) (i-u) > (size_t) sz) return false; if ((size_t) (i-u) > UT_NAMESIZE - 1) return false; return true; } bool valid_user_group_name_or_id(const char *u) { /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right * range, and not the invalid user ids. */ if (isempty(u)) return false; if (valid_user_group_name(u)) return true; return parse_uid(u, NULL) >= 0; } bool valid_gecos(const char *d) { if (!d) return false; if (!utf8_is_valid(d)) return false; if (string_has_cc(d, NULL)) return false; /* Colons are used as field separators, and hence not OK */ if (strchr(d, ':')) return false; return true; } bool valid_home(const char *p) { if (isempty(p)) return false; if (!utf8_is_valid(p)) return false; if (string_has_cc(p, NULL)) return false; if (!path_is_absolute(p)) return false; if (!path_is_safe(p)) return false; /* Colons are used as field separators, and hence not OK */ if (strchr(p, ':')) return false; return true; }
void gui_key_flush (int paste) { int i, key, last_key_used, insert_ok, undo_done; static char key_str[64] = { '\0' }; static int length_key_str = 0; char key_temp[2], *key_utf, *input_old, *ptr_char, *next_char, *ptr_error; char utf_partial_char[16]; struct t_gui_buffer *old_buffer; /* if paste pending or bracketed paste detected, just return */ if (gui_key_paste_pending || gui_key_paste_bracketed) return; /* if buffer is empty, just return */ if (gui_key_buffer_size == 0) return; /* * if there's no paste pending, then we use buffer and do actions * according to keys */ gui_key_last_activity_time = time (NULL); last_key_used = -1; undo_done = 0; old_buffer = NULL; for (i = 0; i < gui_key_buffer_size; i++) { key = gui_key_buffer[i]; insert_ok = 1; utf_partial_char[0] = '\0'; if (gui_mouse_event_pending || (key < 32) || (key == 127)) { if (gui_mouse_event_pending) { insert_ok = 0; key_str[0] = (char)key; key_str[1] = '\0'; length_key_str = 1; } else if (key < 32) { insert_ok = 0; key_str[0] = '\x01'; key_str[1] = (char)key + '@'; key_str[2] = '\0'; length_key_str = 2; } else if (key == 127) { key_str[0] = '\x01'; key_str[1] = '?'; key_str[2] = '\0'; length_key_str = 2; } } else { if (local_utf8) { key_str[length_key_str] = (char)key; key_str[length_key_str + 1] = '\0'; length_key_str++; /* * replace invalid chars by "?", but NOT last char of * string, if it is incomplete UTF-8 char (another char * will be added to the string on next iteration) */ ptr_char = key_str; while (ptr_char && ptr_char[0]) { (void) utf8_is_valid (ptr_char, -1, &ptr_error); if (!ptr_error) break; next_char = (char *)utf8_next_char (ptr_error); if (next_char && next_char[0]) { ptr_char = ptr_error; while (ptr_char < next_char) { ptr_char[0] = '?'; ptr_char++; } } else { strcpy (utf_partial_char, ptr_char); ptr_char[0] = '\0'; break; } ptr_char = next_char; } } else { /* convert input to UTF-8 */ key_temp[0] = (char)key; key_temp[1] = '\0'; key_utf = string_iconv_to_internal (NULL, key_temp); strcat (key_str, key_utf); } } if (key_str[0]) { (void) hook_signal_send ("key_pressed", WEECHAT_HOOK_SIGNAL_STRING, key_str); if (gui_current_window->buffer->text_search != GUI_TEXT_SEARCH_DISABLED) input_old = (gui_current_window->buffer->input_buffer) ? strdup (gui_current_window->buffer->input_buffer) : strdup (""); else input_old = NULL; old_buffer = gui_current_window->buffer; if ((gui_key_pressed (key_str) != 0) && (insert_ok) && (!gui_cursor_mode)) { if (!paste || !undo_done) gui_buffer_undo_snap (gui_current_window->buffer); gui_input_insert_string (gui_current_window->buffer, key_str, -1); gui_input_text_changed_modifier_and_signal (gui_current_window->buffer, (!paste || !undo_done) ? 1 : 0, 1); /* stop completion */ undo_done = 1; } /* incremental text search in buffer */ if ((old_buffer == gui_current_window->buffer) && (gui_current_window->buffer->text_search != GUI_TEXT_SEARCH_DISABLED) && ((input_old == NULL) || (gui_current_window->buffer->input_buffer == NULL) || (strcmp (input_old, gui_current_window->buffer->input_buffer) != 0))) { /* * if following conditions are all true, then do not search * again (search will not find any result and can take some time * on a buffer with many lines): * - old search was not successful * - searching a string (not a regex) * - current input is longer than old input * - beginning of current input is exactly equal to old input. */ if (!gui_current_window->buffer->text_search_found && !gui_current_window->buffer->text_search_regex && (input_old != NULL) && (input_old[0]) && (gui_current_window->buffer->input_buffer != NULL) && (gui_current_window->buffer->input_buffer[0]) && (strlen (gui_current_window->buffer->input_buffer) > strlen (input_old)) && (strncmp (gui_current_window->buffer->input_buffer, input_old, strlen (input_old)) == 0)) { /* * do not search text in buffer, just alert about text not * found */ if (CONFIG_BOOLEAN(config_look_search_text_not_found_alert)) { fprintf (stderr, "\a"); fflush (stderr); } } else { gui_window_search_restart (gui_current_window); } } if (input_old) free (input_old); } /* prepare incomplete UTF-8 char for next iteration */ if (utf_partial_char[0]) strcpy (key_str, utf_partial_char); else key_str[0] = '\0'; length_key_str = strlen (key_str); /* set last key used in buffer if combo buffer is empty */ if (gui_key_grab || gui_mouse_event_pending || !gui_key_combo_buffer[0]) last_key_used = i; } if (last_key_used == gui_key_buffer_size - 1) gui_key_buffer_reset (); else if (last_key_used >= 0) gui_key_buffer_remove (0, last_key_used + 1); if (!gui_key_grab && !gui_mouse_event_pending) gui_key_combo_buffer[0] = '\0'; }
int utf8_luacall_utf8_sub(lua_State *l) { /// Reimplements lua's sub, but with unicode char indexes (the regular /// sub is not unicode aware and supports byte indexes only). /// Will throw error for any strings that aren't valid utf-8. luaL_checkstack(l, 4, ""); if (lua_type(l, 1) != LUA_TSTRING) { lua_pushstring(l, "invalid argument #1: not a string"); return lua_error(l); } if (lua_type(l, 2) != LUA_TNUMBER) { lua_pushstring(l, "invalid argument #2: not a number"); return lua_error(l); } if (lua_type(l, 3) != LUA_TNUMBER && lua_type(l, 3) != LUA_TNIL && lua_type(l, 3) != LUA_TNONE) { lua_pushstring(l, "invalid argument #3: not a number"); return lua_error(l); } size_t bufsize; const unsigned char *buf = (unsigned char*)luaL_checklstring(l, 1, &bufsize); if (!utf8_is_valid(buf, bufsize)) { lua_pushstring(l, "not a valid utf-8 string"); return lua_error(l); } // obtain start, end: int start = (int)lua_tonumber(l, 2); int end = -1; if (lua_type(l, 3) == LUA_TNUMBER) { end = (int)lua_tonumber(l, 3); } size_t length = utf8_length(buf, bufsize); if (start < 0) { start = (length + 1) + start; } if (end < 0) { end = (length + 1) + end; } // enforce sane limits: if (start < 1) {start = 1;} if (end > (int)length) {end = length;} if (start > (int)length || end < 1 || end < start) { // empty string. lua_pushstring(l, ""); return 1; } // obtain the result: UChar32 c; size_t startbytepos, endbytepos; int currentpos = 1; int i = 0; while (i < (int)bufsize) { if (currentpos == start) { startbytepos = i; } if (currentpos == end) { endbytepos = i + 1; break; } U8_NEXT(buf, i, bufsize, c); currentpos = currentpos + 1; } assert(endbytepos > startbytepos); char *resultbuf = malloc(1 + endbytepos - startbytepos); if (!resultbuf) { lua_pushstring(l, "allocation of end result failed"); return lua_error(l); } free_later(resultbuf); memcpy(resultbuf, buf + startbytepos, endbytepos - startbytepos); resultbuf[endbytepos - startbytepos] = 0; lua_pushstring(l, resultbuf); dont_free_later(resultbuf); free(resultbuf); return 1; }