/* * wcswidth.c * Copyright (C) 2020 Kovid Goyal * * Distributed under terms of the GPL3 license. */ #include "wcwidth-std.h" #include "wcswidth.h" #include "unicode-data.h" void initialize_wcs_state(WCSState *state) { zero_at_ptr(state); } static inline bool is_flag_pair(char_type a, char_type b) { return is_flag_codepoint(a) && is_flag_codepoint(b); } int wcswidth_step(WCSState *state, const char_type ch) { int ans = 0; switch (state->parser_state) { case IN_CSI: { state->prev_width = 0; if (0x40 <= ch && ch <= 0x7e) state->parser_state = NORMAL; } break; case IN_ST_TERMINATED: { state->prev_width = 0; if (ch == 0x9c || (ch == '\\' && state->prev_ch == 0x1b)) state->parser_state = NORMAL; } break; case FLAG_PAIR_STARTED: { state->parser_state = NORMAL; if (is_flag_pair(state->prev_ch, ch)) break; } /* fallthrough */ case NORMAL: { switch(ch) { case 0x1b: { state->prev_width = 0; state->parser_state = IN_ESC; } break; case 0xfe0f: { if (is_emoji_presentation_base(state->prev_ch) && state->prev_width == 1) { ans += 1; state->prev_width = 2; } else state->prev_width = 0; } break; case 0xfe0e: { if (is_emoji_presentation_base(state->prev_ch) && state->prev_width == 2) { ans -= 1; state->prev_width = 1; } else state->prev_width = 0; } break; default: { if (is_flag_codepoint(ch)) state->parser_state = FLAG_PAIR_STARTED; int w = wcwidth_std(ch); switch(w) { case -1: case 0: state->prev_width = 0; break; case 2: state->prev_width = 2; break; default: state->prev_width = 1; break; } ans += state->prev_width; } break; } break; // switch(ch) } break; // case NORMAL case IN_ESC: switch (ch) { case '[': state->parser_state = IN_CSI; break; case 'P': case ']': case 'X': case '^': case '_': state->parser_state = IN_ST_TERMINATED; break; case 'D': case 'E': case 'H': case 'M': case 'N': case 'O': case 'Z': case '6': case '7': case '8': case '9': case '=': case '>': case 'F': case 'c': case 'l': case 'm': case 'n': case 'o': case '|': case '}': case '~': break; default: state->prev_ch = 0x1b; state->prev_width = 0; state->parser_state = NORMAL; return wcswidth_step(state, ch); } break; } state->prev_ch = ch; return ans; } PyObject * wcswidth_std(PyObject UNUSED *self, PyObject *str) { if (PyUnicode_READY(str) != 0) return NULL; int kind = PyUnicode_KIND(str); void *data = PyUnicode_DATA(str); Py_ssize_t len = PyUnicode_GET_LENGTH(str), i; WCSState state; initialize_wcs_state(&state); size_t ans = 0; for (i = 0; i < len; i++) { char_type ch = PyUnicode_READ(kind, data, i); ans += wcswidth_step(&state, ch); } return PyLong_FromSize_t(ans); }