Skip to content

Commit f84466b

Browse files
committed
sh: Hack to fix display width when prompt contains wide characters
1 parent 15a586a commit f84466b

File tree

1 file changed

+77
-27
lines changed

1 file changed

+77
-27
lines changed

apps/sh.c

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
* @copyright
1212
* This file is part of ToaruOS and is released under the terms
1313
* of the NCSA / University of Illinois License - see LICENSE.md
14-
* Copyright (C) 2013-2018 K. Lange
14+
* Copyright (C) 2013-2022 K. Lange
1515
*/
1616
#define _XOPEN_SOURCE 500
17-
#define _POSIX_C_SOURCE 200112L
17+
#define _POSIX_C_SOURCE 200809L
1818
#include <stdio.h>
1919
#include <stdint.h>
2020
#include <string.h>
@@ -28,6 +28,7 @@
2828
#include <errno.h>
2929
#include <fcntl.h>
3030
#include <ctype.h>
31+
#include <wchar.h>
3132

3233
#include <sys/time.h>
3334
#include <sys/times.h>
@@ -174,6 +175,63 @@ void gethost() {
174175
memcpy(_hostname, buf.nodename, len+1);
175176
}
176177

178+
#define UTF8_ACCEPT 0
179+
#define UTF8_REJECT 1
180+
static inline uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
181+
static int state_table[32] = {
182+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xxxxxxx */
183+
1,1,1,1,1,1,1,1, /* 10xxxxxx */
184+
2,2,2,2, /* 110xxxxx */
185+
3,3, /* 1110xxxx */
186+
4, /* 11110xxx */
187+
1 /* 11111xxx */
188+
};
189+
190+
static int mask_bytes[32] = {
191+
0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
192+
0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
193+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
194+
0x1F,0x1F,0x1F,0x1F,
195+
0x0F,0x0F,
196+
0x07,
197+
0x00
198+
};
199+
200+
static int next[5] = {
201+
0,
202+
1,
203+
0,
204+
2,
205+
3
206+
};
207+
208+
if (*state == UTF8_ACCEPT) {
209+
*codep = byte & mask_bytes[byte >> 3];
210+
*state = state_table[byte >> 3];
211+
} else if (*state > 0) {
212+
*codep = (byte & 0x3F) | (*codep << 6);
213+
*state = next[*state];
214+
}
215+
return *state;
216+
}
217+
218+
int display_width_of_string(const char * str) {
219+
uint8_t * s = (uint8_t *)str;
220+
221+
int out = 0;
222+
uint32_t c, state = 0;
223+
while (*s) {
224+
if (!decode(&state, &c, *s)) {
225+
out += wcwidth(c);
226+
} else if (state == UTF8_REJECT) {
227+
state = 0;
228+
}
229+
s++;
230+
}
231+
232+
return out;
233+
}
234+
177235
void print_extended_ps(char * format, char * buffer, int * display_width) {
178236
/* Get the time */
179237
struct tm * timeinfo;
@@ -208,15 +266,16 @@ void print_extended_ps(char * format, char * buffer, int * display_width) {
208266

209267
size_t offset = 0;
210268
int is_visible = 1;
211-
*display_width = 0;
269+
char dispchars[1024] = {0};
270+
char * dispout = dispchars;
212271

213272
while (*format) {
214273
if (*format == '\\') {
215274
format++;
216275
switch (*format) {
217276
case '\\':
218277
buffer[offset++] = *format;
219-
(*display_width) += is_visible ? 1 : 0;
278+
if (is_visible) *dispout++ = *format;
220279
format++;
221280
break;
222281
case '[':
@@ -249,57 +308,57 @@ void print_extended_ps(char * format, char * buffer, int * display_width) {
249308
}
250309
}
251310
buffer[offset++] = i;
252-
(*display_width) += is_visible ? 1 : 0;
311+
if (is_visible) *dispout++ = i;
253312
}
254313
break;
255314
case 'e':
256315
buffer[offset++] = '\033';
257-
(*display_width) += is_visible ? 1 : 0;
316+
if (is_visible) *dispout++ = '\033';
258317
format++;
259318
break;
260319
case 'd':
261320
{
262321
int size = sprintf(buffer+offset, "%s", date_buffer);
263322
offset += size;
264-
(*display_width) += is_visible ? size : 0;
323+
if (is_visible) { dispout += sprintf(dispout, "%s", date_buffer); }
265324
}
266325
format++;
267326
break;
268327
case 't':
269328
{
270329
int size = sprintf(buffer+offset, "%s", time_buffer);
271330
offset += size;
272-
(*display_width) += is_visible ? size : 0;
331+
if (is_visible) { dispout += sprintf(dispout, "%s", time_buffer); }
273332
}
274333
format++;
275334
break;
276335
case 'h':
277336
{
278337
int size = sprintf(buffer+offset, "%s", _hostname);
279338
offset += size;
280-
(*display_width) += is_visible ? size : 0;
339+
if (is_visible) { dispout += sprintf(dispout, "%s", _hostname); }
281340
}
282341
format++;
283342
break;
284343
case 'u':
285344
{
286345
int size = sprintf(buffer+offset, "%s", username);
287346
offset += size;
288-
(*display_width) += is_visible ? size : 0;
347+
if (is_visible) { dispout += sprintf(dispout, "%s", username); }
289348
}
290349
format++;
291350
break;
292351
case 'w':
293352
{
294353
int size = sprintf(buffer+offset, "%s", _cwd);
295354
offset += size;
296-
(*display_width) += is_visible ? size : 0;
355+
if (is_visible) { dispout += sprintf(dispout, "%s", _cwd); }
297356
}
298357
format++;
299358
break;
300359
case '$':
301360
buffer[offset++] = (getuid() == 0 ? '#' : '$');
302-
(*display_width) += is_visible ? 1 : 0;
361+
if (is_visible) *dispout++ = (getuid() == 0 ? '#' : '$');
303362
format++;
304363
break;
305364
case 'U': /* prompt color string */
@@ -314,39 +373,29 @@ void print_extended_ps(char * format, char * buffer, int * display_width) {
314373
{
315374
int size = sprintf(buffer+offset, "%s", ret);
316375
offset += size;
317-
(*display_width) += is_visible ? size : 0;
376+
if (is_visible) { dispout += sprintf(dispout, "%s", ret); }
318377
}
319378
format++;
320379
break;
321380
default:
322381
{
323382
int size = sprintf(buffer+offset, "\\%c", *format);
324383
offset += size;
325-
(*display_width) += is_visible ? size : 0;
384+
if (is_visible) { dispout += sprintf(dispout, "\\%c", *format); }
326385
}
327386
format++;
328387
break;
329388
}
330389
} else {
331390
buffer[offset++] = *format;
332-
(*display_width) += is_visible ? 1 : 0;
391+
if (is_visible) *dispout++ = *format;
333392
format++;
334393
}
335394
}
336395

337-
buffer[offset] = '\0';
338-
}
339-
340-
#define FALLBACK_PS1 "\\u@\\h \\w\\$ "
396+
*display_width = display_width_of_string(dispchars);
341397

342-
/* Draw the user prompt */
343-
void draw_prompt(void) {
344-
char * ps1 = getenv("PS1");
345-
char buf[1024];
346-
int display_width;
347-
print_extended_ps(ps1 ? ps1 : FALLBACK_PS1, buf, &display_width);
348-
fprintf(stdout, "%s", buf);
349-
fflush(stdout);
398+
buffer[offset] = '\0';
350399
}
351400

352401
volatile int break_while = 0;
@@ -712,6 +761,7 @@ void add_environment(list_t * env) {
712761
}
713762
}
714763

764+
#define FALLBACK_PS1 "\\u@\\h \\w\\$ "
715765
int read_entry(char * buffer) {
716766
char lprompt[1024], rprompt[1024];
717767
int lwidth, rwidth;

0 commit comments

Comments
 (0)