(root)/
util-linux-2.39/
lib/
mbsedit.c
       1  /*
       2   * Very simple multibyte buffer editor. Allows to maintaine the current
       3   * position in the string, add and remove chars on the current position.
       4   *
       5   * This file may be distributed under the terms of the
       6   * GNU Lesser General Public License.
       7   *
       8   * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
       9   */
      10  #include <stdlib.h>
      11  #include <string.h>
      12  #include <errno.h>
      13  #include <stdio.h>
      14  
      15  #include "mbsalign.h"
      16  #include "mbsedit.h"
      17  
      18  struct mbs_editor *mbs_new_edit(char *buf, size_t bufsz, size_t ncells)
      19  {
      20  	struct mbs_editor *edit = calloc(1, sizeof(*edit));
      21  
      22  	if (edit) {
      23  		edit->buf = buf;
      24  		edit->max_bytes = bufsz;
      25  		edit->max_cells = ncells;
      26  		edit->cur_cells = mbs_safe_width(buf);
      27  		edit->cur_bytes = strlen(buf);
      28  	}
      29  	return edit;
      30  }
      31  
      32  char *mbs_free_edit(struct mbs_editor *edit)
      33  {
      34  	char *ret = edit ? edit->buf : NULL;
      35  
      36  	free(edit);
      37  	return ret;
      38  }
      39  
      40  static size_t mbs_next(const char *str, size_t *ncells)
      41  {
      42  #ifdef HAVE_WIDECHAR
      43  	wchar_t wc;
      44  	size_t n = 0;
      45  
      46  	if (!str || !*str)
      47  		return 0;
      48  
      49  	n = mbrtowc(&wc, str, MB_CUR_MAX, NULL);
      50  	*ncells = wcwidth(wc);
      51  	return n;
      52  #else
      53  	if (!str || !*str)
      54  		return 0;
      55  	*ncells = 1;
      56  	return 1;
      57  #endif
      58  }
      59  
      60  static size_t mbs_prev(const char *start, const char *end, size_t *ncells)
      61  {
      62  #ifdef HAVE_WIDECHAR
      63  	wchar_t wc = 0;
      64  	const char *p, *prev;
      65  	size_t n = 0;
      66  
      67  	if (!start || !end || start == end || !*start)
      68  		return 0;
      69  
      70  	prev = p = start;
      71  	while (p < end) {
      72  		n = mbrtowc(&wc, p, MB_CUR_MAX, NULL);
      73  		prev = p;
      74  
      75  		if (n == (size_t) -1 || n == (size_t) -2)
      76  			p++;
      77  		else
      78  			p += n;
      79  	}
      80  
      81  	if (prev == end)
      82  		return 0;
      83  	*ncells = wcwidth(wc);
      84  	return n;
      85  #else
      86  	if (!start || !end || start == end || !*start)
      87  		return 0;
      88  	*ncells = 1;
      89  	return 1;
      90  #endif
      91  }
      92  
      93  int mbs_edit_goto(struct mbs_editor *edit, int where)
      94  {
      95  	switch (where) {
      96  	case MBS_EDIT_LEFT:
      97  		if (edit->cursor == 0)
      98  			return 1;
      99  		else {
     100  			size_t n, cells;
     101  			n = mbs_prev(edit->buf, edit->buf + edit->cursor, &cells);
     102  			if (n) {
     103  				edit->cursor -= n;
     104  				edit->cursor_cells -= cells;
     105  			}
     106  		}
     107  		break;
     108  	case MBS_EDIT_RIGHT:
     109  		if (edit->cursor_cells >= edit->cur_cells)
     110  			return 1;
     111  		else {
     112  			size_t n, cells;
     113  			n = mbs_next(edit->buf + edit->cursor, &cells);
     114  			if (n) {
     115  				edit->cursor += n;
     116  				edit->cursor_cells += cells;
     117  			}
     118  		}
     119  		break;
     120  	case MBS_EDIT_HOME:
     121  		edit->cursor = 0;
     122  		edit->cursor_cells = 0;
     123  		break;
     124  	case MBS_EDIT_END:
     125  		edit->cursor = edit->cur_bytes;
     126  		edit->cursor_cells = edit->cur_cells;
     127  		break;
     128  	default:
     129  		return -EINVAL;
     130  	}
     131  
     132  	return 0;
     133  }
     134  
     135  /* Remove next MB from @str, returns number of removed bytes */
     136  static size_t remove_next(char *str, size_t *ncells)
     137  {
     138  	/* all in bytes! */
     139  	size_t bytes, move_bytes, n;
     140  
     141  	n          = mbs_next(str, ncells);
     142  	bytes      = strlen(str);
     143  	move_bytes = bytes - n;
     144  
     145  	memmove(str, str + n, move_bytes);
     146  	str[bytes - n] = '\0';
     147  	return n;
     148  }
     149  
     150  static size_t mbs_insert(char *str, wint_t c, size_t *ncells)
     151  {
     152  	/* all in bytes! */
     153  	size_t n = 1, bytes;
     154  	char *in;
     155  
     156  #ifdef HAVE_WIDECHAR
     157  	wchar_t wc = (wchar_t) c;
     158  	char in_buf[MB_CUR_MAX];
     159  
     160  	n = wctomb(in_buf, wc);
     161  	if (n == (size_t) -1)
     162  		return n;
     163  	*ncells = wcwidth(wc);
     164  	in = in_buf;
     165  #else
     166  	*ncells = 1;
     167  	in = (char *) &c;
     168  #endif
     169  	bytes       = strlen(str);
     170  
     171  	memmove(str + n, str, bytes);
     172  	memcpy(str, in, n);
     173  	str[bytes + n] = '\0';
     174  	return n;
     175  }
     176  
     177  static int mbs_edit_remove(struct mbs_editor *edit)
     178  {
     179  	size_t n, ncells;
     180  
     181  	if (edit->cur_cells == 0 || edit->cursor >= edit->cur_bytes)
     182  		return 1;
     183  
     184  	n = remove_next(edit->buf + edit->cursor, &ncells);
     185  	if (n == (size_t)-1)
     186  		return 1;
     187  
     188  	edit->cur_bytes -= n;
     189  	edit->cur_cells = mbs_safe_width(edit->buf);
     190  	return 0;
     191  }
     192  
     193  int mbs_edit_delete(struct mbs_editor *edit)
     194  {
     195  	if (edit->cursor >= edit->cur_bytes
     196  	    && mbs_edit_goto(edit, MBS_EDIT_LEFT) == 1)
     197  		return 1;
     198  
     199  	return mbs_edit_remove(edit);
     200  }
     201  
     202  int mbs_edit_backspace(struct mbs_editor *edit)
     203  {
     204  	if (mbs_edit_goto(edit, MBS_EDIT_LEFT) == 0)
     205  		return mbs_edit_remove(edit);
     206  	return 1;
     207  }
     208  
     209  int mbs_edit_insert(struct mbs_editor *edit, wint_t c)
     210  {
     211  	size_t n, ncells;
     212  
     213  	if (edit->cur_bytes + MB_CUR_MAX > edit->max_bytes)
     214  		return 1;
     215  
     216  	n = mbs_insert(edit->buf + edit->cursor, c, &ncells);
     217  	if (n == (size_t)-1)
     218  		return 1;
     219  
     220  	edit->cursor += n;
     221  	edit->cursor_cells += ncells;
     222  	edit->cur_bytes += n;
     223  	edit->cur_cells = mbs_safe_width(edit->buf);
     224  	return 0;
     225  }