2014-02-25 08:28:24 +01:00
|
|
|
/* toxic_strings.c
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
|
|
*
|
|
|
|
* This file is part of Toxic.
|
|
|
|
*
|
|
|
|
* Toxic is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Toxic is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
2013-12-10 09:03:45 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2014-06-21 23:55:01 +02:00
|
|
|
#include <wchar.h>
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-06-12 00:06:55 +02:00
|
|
|
#include "toxic.h"
|
|
|
|
#include "windows.h"
|
2013-12-10 09:03:45 +01:00
|
|
|
#include "misc_tools.h"
|
2013-12-11 06:10:09 +01:00
|
|
|
#include "toxic_strings.h"
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-07-14 20:32:38 +02:00
|
|
|
/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */
|
|
|
|
int add_char_to_buf(ChatContext *ctx, wint_t ch)
|
2013-12-10 09:03:45 +01:00
|
|
|
{
|
2014-07-14 20:32:38 +02:00
|
|
|
if (ctx->len >= MAX_STR_SIZE - 1)
|
|
|
|
return -1;
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-06-21 23:55:01 +02:00
|
|
|
wmemmove(&ctx->line[ctx->pos + 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
2014-06-06 21:27:06 +02:00
|
|
|
ctx->line[ctx->pos++] = ch;
|
2014-06-21 23:55:01 +02:00
|
|
|
ctx->line[++ctx->len] = L'\0';
|
2014-07-14 20:32:38 +02:00
|
|
|
|
|
|
|
return 0;
|
2013-12-10 09:03:45 +01:00
|
|
|
}
|
|
|
|
|
2014-07-14 20:32:38 +02:00
|
|
|
/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */
|
|
|
|
int del_char_buf_bck(ChatContext *ctx)
|
2013-12-10 09:03:45 +01:00
|
|
|
{
|
2014-07-14 20:32:38 +02:00
|
|
|
if (ctx->pos <= 0)
|
|
|
|
return -1;
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-06-21 23:55:01 +02:00
|
|
|
wmemmove(&ctx->line[ctx->pos - 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
2014-06-06 21:27:06 +02:00
|
|
|
--ctx->pos;
|
2014-06-21 23:55:01 +02:00
|
|
|
ctx->line[--ctx->len] = L'\0';
|
2014-07-14 20:32:38 +02:00
|
|
|
|
|
|
|
return 0;
|
2013-12-10 09:03:45 +01:00
|
|
|
}
|
|
|
|
|
2014-07-14 20:32:38 +02:00
|
|
|
/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */
|
|
|
|
int del_char_buf_frnt(ChatContext *ctx)
|
2013-12-10 09:03:45 +01:00
|
|
|
{
|
2014-06-22 03:41:38 +02:00
|
|
|
if (ctx->pos >= ctx->len)
|
2014-07-14 20:32:38 +02:00
|
|
|
return -1;
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-06-21 23:55:01 +02:00
|
|
|
wmemmove(&ctx->line[ctx->pos], &ctx->line[ctx->pos + 1], ctx->len - ctx->pos - 1);
|
|
|
|
ctx->line[--ctx->len] = L'\0';
|
2014-07-14 20:32:38 +02:00
|
|
|
|
|
|
|
return 0;
|
2013-12-10 09:03:45 +01:00
|
|
|
}
|
|
|
|
|
2014-07-14 20:32:38 +02:00
|
|
|
/* Deletes the line from beginning to pos and puts discarded portion in yank buffer.
|
|
|
|
Return 0 on success, -1 if noting to discard. */
|
|
|
|
int discard_buf(ChatContext *ctx)
|
2013-12-10 09:03:45 +01:00
|
|
|
{
|
2014-06-06 21:27:06 +02:00
|
|
|
if (ctx->pos <= 0)
|
2014-07-14 20:32:38 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
ctx->yank_len = ctx->pos;
|
|
|
|
wmemcpy(ctx->yank, ctx->line, ctx->yank_len);
|
|
|
|
ctx->yank[ctx->yank_len] = L'\0';
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-06-21 23:55:01 +02:00
|
|
|
wmemmove(ctx->line, &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
|
|
|
ctx->len -= ctx->pos;
|
2014-06-06 21:27:06 +02:00
|
|
|
ctx->pos = 0;
|
2014-06-21 05:08:13 +02:00
|
|
|
ctx->start = 0;
|
2014-06-21 23:55:01 +02:00
|
|
|
ctx->line[ctx->len] = L'\0';
|
2014-07-14 20:32:38 +02:00
|
|
|
|
|
|
|
return 0;
|
2013-12-10 09:03:45 +01:00
|
|
|
}
|
|
|
|
|
2014-07-14 20:32:38 +02:00
|
|
|
/* Deletes the line from pos to len and puts killed portion in yank buffer.
|
|
|
|
Return 0 on success, -1 if nothing to kill. */
|
|
|
|
int kill_buf(ChatContext *ctx)
|
2013-12-10 09:03:45 +01:00
|
|
|
{
|
2014-07-14 20:32:38 +02:00
|
|
|
if (ctx->len <= ctx->pos)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ctx->yank_len = ctx->len - ctx->pos;
|
|
|
|
wmemcpy(ctx->yank, &ctx->line[ctx->pos], ctx->yank_len);
|
|
|
|
ctx->yank[ctx->yank_len] = L'\0';
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
ctx->line[ctx->pos] = L'\0';
|
|
|
|
ctx->len = ctx->pos;
|
2014-07-14 20:32:38 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Inserts string in ctx->yank into line at pos.
|
|
|
|
Return 0 on success, -1 if yank buffer is empty or too long */
|
|
|
|
int yank_buf(ChatContext *ctx)
|
|
|
|
{
|
|
|
|
if (!ctx->yank[0])
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (ctx->yank_len + ctx->len >= MAX_STR_SIZE - 1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
wmemmove(&ctx->line[ctx->pos + ctx->yank_len], &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
|
|
|
wmemcpy(&ctx->line[ctx->pos], ctx->yank, ctx->yank_len);
|
|
|
|
|
|
|
|
ctx->pos += ctx->yank_len;
|
|
|
|
ctx->len += ctx->yank_len;
|
|
|
|
ctx->line[ctx->len] = L'\0';
|
|
|
|
return 0;
|
2013-12-10 09:03:45 +01:00
|
|
|
}
|
|
|
|
|
2014-06-21 05:08:13 +02:00
|
|
|
/* nulls line and sets pos, len and start to 0 */
|
2014-06-06 21:27:06 +02:00
|
|
|
void reset_buf(ChatContext *ctx)
|
2013-12-10 09:03:45 +01:00
|
|
|
{
|
2014-06-06 21:27:06 +02:00
|
|
|
ctx->line[0] = L'\0';
|
|
|
|
ctx->pos = 0;
|
|
|
|
ctx->len = 0;
|
2014-06-21 05:08:13 +02:00
|
|
|
ctx->start = 0;
|
2013-12-10 09:03:45 +01:00
|
|
|
}
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
/* Removes trailing spaces from line. */
|
|
|
|
void rm_trailing_spaces_buf(ChatContext *ctx)
|
2014-06-04 02:35:00 +02:00
|
|
|
{
|
2014-06-06 21:27:06 +02:00
|
|
|
if (ctx->len <= 0)
|
2014-06-04 02:35:00 +02:00
|
|
|
return;
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
if (ctx->line[ctx->len - 1] != ' ')
|
2014-06-04 02:35:00 +02:00
|
|
|
return;
|
|
|
|
|
2014-06-04 20:41:36 +02:00
|
|
|
int i;
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
for (i = ctx->len - 1; i >= 0; --i) {
|
|
|
|
if (ctx->line[i] != ' ')
|
2014-06-04 20:41:36 +02:00
|
|
|
break;
|
|
|
|
}
|
2014-06-04 02:35:00 +02:00
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
ctx->len = i + 1;
|
2014-06-07 20:36:37 +02:00
|
|
|
ctx->pos = MIN(ctx->pos, ctx->len);
|
2014-06-06 21:27:06 +02:00
|
|
|
ctx->line[ctx->len] = L'\0';
|
2014-06-04 02:35:00 +02:00
|
|
|
}
|
|
|
|
|
2013-12-14 06:36:06 +01:00
|
|
|
#define HIST_PURGE MAX_LINE_HIST / 4
|
|
|
|
|
|
|
|
/* shifts hist items back and makes room for HIST_PURGE new entries */
|
2014-06-06 21:27:06 +02:00
|
|
|
static void shift_hist_back(ChatContext *ctx)
|
2013-12-14 06:36:06 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int n = MAX_LINE_HIST - HIST_PURGE;
|
|
|
|
|
|
|
|
for (i = 0; i < n; ++i)
|
2014-06-06 21:27:06 +02:00
|
|
|
wmemcpy(ctx->ln_history[i], ctx->ln_history[i + HIST_PURGE], MAX_STR_SIZE);
|
2013-12-14 06:36:06 +01:00
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
ctx->hst_tot = n;
|
2013-12-14 06:36:06 +01:00
|
|
|
}
|
|
|
|
|
2013-12-11 09:29:31 +01:00
|
|
|
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */
|
2014-06-06 21:27:06 +02:00
|
|
|
void add_line_to_hist(ChatContext *ctx)
|
2013-12-11 06:10:09 +01:00
|
|
|
{
|
2014-06-06 21:27:06 +02:00
|
|
|
if (ctx->len > MAX_STR_SIZE)
|
2013-12-11 06:10:09 +01:00
|
|
|
return;
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
if (ctx->hst_tot >= MAX_LINE_HIST)
|
|
|
|
shift_hist_back(ctx);
|
2013-12-11 06:10:09 +01:00
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
++ctx->hst_tot;
|
|
|
|
ctx->hst_pos = ctx->hst_tot;
|
2013-12-14 06:36:06 +01:00
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
wmemcpy(ctx->ln_history[ctx->hst_tot - 1], ctx->line, ctx->len + 1);
|
2013-12-11 06:10:09 +01:00
|
|
|
}
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
/* copies history item at hst_pos to line. Sets pos and len to the len of the history item.
|
2014-02-23 20:22:45 +01:00
|
|
|
hst_pos is decremented or incremented depending on key_dir.
|
2014-04-19 23:58:13 +02:00
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
resets line if at end of history */
|
|
|
|
void fetch_hist_item(ChatContext *ctx, int key_dir)
|
2013-12-11 06:10:09 +01:00
|
|
|
{
|
2014-06-26 08:33:09 +02:00
|
|
|
if (key_dir == KEY_UP) {
|
2014-06-06 21:27:06 +02:00
|
|
|
if (--ctx->hst_pos < 0) {
|
|
|
|
ctx->hst_pos = 0;
|
2013-12-11 10:49:21 +01:00
|
|
|
beep();
|
|
|
|
}
|
2013-12-11 06:10:09 +01:00
|
|
|
} else {
|
2014-06-06 21:27:06 +02:00
|
|
|
if (++ctx->hst_pos >= ctx->hst_tot) {
|
|
|
|
ctx->hst_pos = ctx->hst_tot;
|
|
|
|
reset_buf(ctx);
|
2013-12-11 10:49:21 +01:00
|
|
|
return;
|
|
|
|
}
|
2013-12-11 06:10:09 +01:00
|
|
|
}
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
const wchar_t *hst_line = ctx->ln_history[ctx->hst_pos];
|
2013-12-11 06:10:09 +01:00
|
|
|
size_t h_len = wcslen(hst_line);
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
wmemcpy(ctx->line, hst_line, h_len + 1);
|
|
|
|
ctx->pos = h_len;
|
|
|
|
ctx->len = h_len;
|
2013-12-11 06:10:09 +01:00
|
|
|
}
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
/* looks for the first instance in list that begins with the last entered word in line according to pos,
|
|
|
|
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
2013-12-10 09:03:45 +01:00
|
|
|
with "Hello john".
|
|
|
|
|
|
|
|
list is a pointer to the list of strings being compared, n_items is the number of items
|
2014-04-19 23:58:13 +02:00
|
|
|
in the list, and size is the size of each item in the list.
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
Returns the difference between the old len and new len of line on success, -1 if error */
|
|
|
|
int complete_line(ChatContext *ctx, const void *list, int n_items, int size)
|
2013-12-10 09:03:45 +01:00
|
|
|
{
|
2014-06-06 21:27:06 +02:00
|
|
|
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE)
|
2013-12-10 09:03:45 +01:00
|
|
|
return -1;
|
|
|
|
|
2014-07-07 04:15:35 +02:00
|
|
|
const char *L = (char *) list;
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-07-07 04:15:35 +02:00
|
|
|
char ubuf[MAX_STR_SIZE];
|
2014-04-19 23:58:13 +02:00
|
|
|
|
2013-12-10 09:03:45 +01:00
|
|
|
/* work with multibyte string copy of buf for simplicity */
|
2014-07-07 04:15:35 +02:00
|
|
|
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
|
2013-12-10 09:03:45 +01:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* isolate substring from space behind pos to pos */
|
2014-07-07 04:15:35 +02:00
|
|
|
char tmp[MAX_STR_SIZE];
|
2013-12-10 09:03:45 +01:00
|
|
|
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
2014-06-06 21:27:06 +02:00
|
|
|
tmp[ctx->pos] = '\0';
|
2014-07-07 04:15:35 +02:00
|
|
|
char *sub = strrchr(tmp, ' ');
|
2014-06-04 02:35:00 +02:00
|
|
|
int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */
|
2013-12-10 09:03:45 +01:00
|
|
|
|
|
|
|
if (!sub++) {
|
|
|
|
sub = tmp;
|
2014-06-04 02:35:00 +02:00
|
|
|
|
|
|
|
if (sub[0] != '/') /* make sure it's not a command */
|
|
|
|
n_endchrs = 2;
|
2013-12-10 09:03:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (string_is_empty(sub))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
int s_len = strlen(sub);
|
2014-07-07 04:15:35 +02:00
|
|
|
const char *match;
|
2013-12-10 09:03:45 +01:00
|
|
|
bool is_match = false;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* look for a match in list */
|
|
|
|
for (i = 0; i < n_items; ++i) {
|
2014-04-19 23:58:13 +02:00
|
|
|
match = &L[i * size];
|
|
|
|
|
2014-07-07 04:15:35 +02:00
|
|
|
if ((is_match = strncasecmp(match, sub, s_len) == 0))
|
2013-12-10 09:03:45 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_match)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* put match in correct spot in buf and append endchars (space or ": ") */
|
2014-07-07 04:15:35 +02:00
|
|
|
const char *endchrs = n_endchrs == 1 ? " " : ": ";
|
2013-12-10 09:03:45 +01:00
|
|
|
int m_len = strlen(match);
|
2014-06-06 21:27:06 +02:00
|
|
|
int strt = ctx->pos - s_len;
|
2013-12-10 09:03:45 +01:00
|
|
|
int diff = m_len - s_len + n_endchrs;
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
if (ctx->len + diff > MAX_STR_SIZE)
|
2013-12-10 09:03:45 +01:00
|
|
|
return -1;
|
|
|
|
|
2014-07-07 04:15:35 +02:00
|
|
|
char tmpend[MAX_STR_SIZE];
|
2014-06-06 21:27:06 +02:00
|
|
|
strcpy(tmpend, &ubuf[ctx->pos]);
|
2013-12-10 09:03:45 +01:00
|
|
|
strcpy(&ubuf[strt], match);
|
2014-04-19 23:58:13 +02:00
|
|
|
strcpy(&ubuf[strt + m_len], endchrs);
|
|
|
|
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
|
2013-12-10 09:03:45 +01:00
|
|
|
|
|
|
|
/* convert to widechar and copy back to original buf */
|
|
|
|
wchar_t newbuf[MAX_STR_SIZE];
|
|
|
|
|
2014-01-23 05:29:28 +01:00
|
|
|
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
|
2013-12-10 09:03:45 +01:00
|
|
|
return -1;
|
|
|
|
|
2014-06-06 21:27:06 +02:00
|
|
|
wcscpy(ctx->line, newbuf);
|
2013-12-10 09:03:45 +01:00
|
|
|
|
2014-06-26 08:33:09 +02:00
|
|
|
ctx->len += diff;
|
|
|
|
ctx->pos += diff;
|
2013-12-10 09:03:45 +01:00
|
|
|
|
|
|
|
return diff;
|
|
|
|
}
|