SLuchar_Type *SLwchar_bskip_range (SLwchar_Lut_Type *r, SLuchar_Type *pmin, SLuchar_Type *p, int ignore_combining, int invert) { unsigned char *lut; SLuchar_Type *pmax; int utf8_mode; if ((r == NULL) || (p == NULL) || (pmin == NULL)) return NULL; lut = r->lut; pmax = p; invert = (invert != 0); utf8_mode = r->utf8_mode; while (p > pmin) { SLuchar_Type *p0; SLwchar_Type wch; SLstrlen_Type dn; p0 = p - 1; if ((*p0 < 0x80) || (utf8_mode == 0)) { if ((int)lut[*p0] == invert) return p; p = p0; continue; } p0 = SLutf8_bskip_char (pmin, p); if (NULL == SLutf8_decode (p0, pmax, &wch, &dn)) { if (invert) return p; p = p0; continue; } if ((ignore_combining) && (0 == SLwchar_wcwidth (wch))) { p = p0; continue; } if (invert == wch_in_lut (r, wch)) return p; p = p0; } return p; }
SLuchar_Type *SLwchar_skip_range (SLwchar_Lut_Type *r, SLuchar_Type *p, SLuchar_Type *pmax, int ignore_combining, int invert) { unsigned char *lut; int utf8_mode; if ((r == NULL) || (p == NULL) || (pmax == NULL)) return NULL; lut = r->lut; invert = (invert != 0); utf8_mode = r->utf8_mode; while (p < pmax) { SLwchar_Type wch; SLstrlen_Type dn; if ((*p < 0x80) || (utf8_mode == 0)) { if ((int)lut[*p] == invert) return p; p++; continue; } if (NULL == SLutf8_decode (p, pmax, &wch, &dn)) { if (invert == 0) return p; p++; continue; } if ((ignore_combining) && (0 == SLwchar_wcwidth (wch))) { p += dn; continue; } if (invert == wch_in_lut (r, wch)) return p; p += dn; } return p; }
/* These routines depend upon the value of the _pSLinterp_UTF8_Mode variable. * They also generate slang errors upon error. */ SLuchar_Type *_pSLinterp_decode_wchar (SLuchar_Type *u, SLuchar_Type *umax, SLwchar_Type *chp) { if (_pSLinterp_UTF8_Mode == 0) { if (u < umax) *chp = (SLwchar_Type) *u++; return u; } if (NULL == (u = SLutf8_decode (u, umax, chp, NULL))) _pSLang_verror (SL_INVALID_UTF8, "Invalid UTF-8 encoded string"); return u; }
static SLuchar_Type *compute_char_width (SLuchar_Type *b, SLuchar_Type *bmax, int utf8_mode, unsigned int *wp, SLwchar_Type *wchp, int *illegalp) { SLwchar_Type wch; if (illegalp != NULL) *illegalp = 0; if (b >= bmax) { *wp = 0; if (wchp != NULL) *wchp = 0; return b; } if (utf8_mode == 0) { *wp = Char_Widths[*b]; if (wchp != NULL) *wchp = *b; return b + 1; } if (NULL == SLutf8_decode (b, bmax, &wch, NULL)) { /* Illegal byte sequence */ *wp = 4; /* <XX> */ if (wchp != NULL) *wchp = *b; if (illegalp != NULL) *illegalp = 1; return b + 1; } /* skip combining */ if ((wch >= ' ') && (wch < 127)) *wp = 1; else if (wch > 127) *wp = SLwchar_wcwidth (wch); else *wp = 2; /* ^X */ if (wchp != NULL) *wchp = wch; /* skip combining chars too */ return SLutf8_skip_chars (b, bmax, 1, NULL, 1); }
int _pSLbstring_foreach (SLtype type, SLang_Foreach_Context_Type *c) { unsigned char ch; SLwchar_Type wch; unsigned char *s, *s1, *smax; (void) type; s = c->s; smax = c->smax; if (s == smax) return 0; if (c->using_chars == 0) { ch = (unsigned char) *s++; c->s = s; if (-1 == SLclass_push_char_obj (SLANG_UCHAR_TYPE, ch)) return -1; return 1; } s1 = SLutf8_decode (s, smax, &wch, NULL); if (s1 == NULL) { int iwch = (int) *s; c->s = s + 1; /* Invalid encoded char-- return it as a negative int */ if (-1 == SLang_push_int (-iwch)) return -1; return 1; } c->s = s1; if (-1 == SLang_push_wchar (wch)) return -1; return 1; }
int SLutf8_decode_bytes (SLuchar_Type *u, SLuchar_Type *umax, unsigned char *b, unsigned int *np) { unsigned char *bmax; bmax = b; while (u < umax) { SLwchar_Type w; if (0 == (*u & 0x80)) { *bmax++ = *u++; continue; } if (NULL == (u = SLutf8_decode (u, umax, &w, NULL))) return -1; /* FIXME: HANDLE ERROR */ if (w > 0xFF) { #if 0 sprintf (bmax, "<U+%04X>", w); bmax += strlen (bmax); continue; #endif /* FIXME: HANDLE ERROR */ w = w & 0xFF; } *bmax++ = w; } *np = bmax - b; *bmax = 0; return 0; }
void SLsmg_write_wrapped_string (SLuchar_Type *u, int r, int c, unsigned int dr, unsigned int dc, int fill) { int maxc = (int) dc; unsigned char *p, *pmax; int utf8_mode = UTF8_Mode; unsigned char display_8bit = (unsigned char) SLsmg_Display_Eight_Bit; if (utf8_mode) display_8bit = 0xA0; if ((dr == 0) || (dc == 0)) return; if (u == NULL) u = (unsigned char *)""; p = u; pmax = u + strlen ((char *)u); dc = 0; while (1) { SLwchar_Type wc; unsigned int nconsumed; unsigned char ch = *p; unsigned int ddc; if ((ch == 0) || (ch == '\n')) { int diff; diff = maxc - (int) dc; SLsmg_gotorc (r, c); SLsmg_write_chars (u, p); if (fill && (diff > 0)) { unsigned char *blank = (unsigned char *)" "; while (diff--) SLsmg_write_chars (blank, blank+1); } if ((ch == 0) || (dr == 1)) break; r++; dc = 0; dr--; p++; u = p; continue; } /* If the width of the characters buffered so far extend to or beyond * the width of the box, then write them out and goto the * next line. Note that dc > maxc if the displayable width of * the last character to be written is greater than the width * of the box. This will be the case if (maxc<ddc) -- see below */ if ((int) dc >= maxc) goto write_chars_and_reset; nconsumed = 1; if (ch < 0x80) { p++; if ((ch >= 0x20) && (ch != 0x7F)) { dc++; continue; } /* Otherwise display as ^X */ dc += 2; continue; } nconsumed = 1; if ((utf8_mode == 0) || (NULL == SLutf8_decode (p, pmax, &wc, &nconsumed))) { if ((utf8_mode == 0) && (display_8bit && (*p >= display_8bit))) { dc++; p += nconsumed; continue; } ddc = 4*nconsumed; /* <XX> */ } else if (wc < (SLwchar_Type)display_8bit) ddc = 4; /* displays as <XX> */ else ddc = SLwchar_wcwidth (wc); dc += ddc; if (((int)dc > maxc) && (maxc > (int)ddc)) { dc -= ddc; goto write_chars_and_reset; } p += nconsumed; continue; write_chars_and_reset: SLsmg_gotorc (r, c); SLsmg_write_chars (u, p); while ((int)dc < maxc) { SLsmg_write_char (' '); dc++; } if (dr == 1) break; r++; dc = 0; dr--; u = p; } }
void SLsmg_write_chars (unsigned char *u, unsigned char *umax) { SLsmg_Char_Type *p, *pmax; SLsmg_Color_Type color; int flags; int col, start_col, max_col; int newline_flag; int utf8_mode = UTF8_Mode; unsigned char display_8bit; int last_was_double_width = 0; int alt_char_set_flag; unsigned int i; #if SLSMG_HAS_EMBEDDED_ESCAPE SLsmg_Color_Type default_color; #endif if (Smg_Inited == 0) return; if (u >= umax) return; display_8bit = (unsigned char) SLsmg_Display_Eight_Bit; if (utf8_mode) display_8bit = 0xA0; color = This_Color; /* If we are using unicode characters for the line drawing characters, then * do not attempt to use the terminals alternate character set */ alt_char_set_flag = (color & SLSMG_ACS_MASK); if (Current_ACS_Mode == ACS_MODE_UNICODE) color = color & ~SLSMG_ACS_MASK; #if SLSMG_HAS_EMBEDDED_ESCAPE default_color = color; /* used for ESC[m */ #endif top: /* get here only on newline */ newline_flag = 0; start_col = Start_Col; if (point_visible (0) == 0) return; col = This_Col; max_col = start_col + Screen_Cols; p = SL_Screen[This_Row - Start_Row].neew; pmax = p + Screen_Cols; if (col >= start_col) { p += (col - start_col); if ((p < pmax) && (p->nchars == 0)) { /* It looks like we are about to overwrite the right side of a * double width character. Let's see... */ if (col > start_col) { p--; if (p->nchars != 0) { p->nchars = 1; p->wchars[0] = ' '; } p++; } } } flags = SL_Screen[This_Row - Start_Row].flags; i = 0; while (u < umax) { SLwchar_Type wc; unsigned int width, nconsumed; if (*u < (SLuchar_Type) 0x80) /* ASCII */ { unsigned char ch; ch = (unsigned char) *u++; if (alt_char_set_flag) { wc = ACS_Map[ch]; ADD_CHAR_OR_BREAK(wc); continue; } if ((ch >= (SLuchar_Type)0x20) && (ch < (SLuchar_Type)0x7F)) { ADD_CHAR_OR_BREAK(ch); continue; } if ((ch == '\t') && (SLsmg_Tab_Width > 0)) { do { if (col < start_col) col++; else { ADD_CHAR_OR_BREAK(' '); NEXT_CHAR_CELL; } } while (col % SLsmg_Tab_Width); continue; } if ((ch == '\n') && (SLsmg_Newline_Behavior != SLSMG_NEWLINE_PRINTABLE)) { newline_flag = 1; break; } if ((ch == 0x8) && SLsmg_Backspace_Moves) { if (col != 0) { if (i != 0) { NEXT_CHAR_CELL; col--; p--; } col--; p--; } continue; } #if SLSMG_HAS_EMBEDDED_ESCAPE if ((ch == 033) && Embedded_Escape_Mode) { SLsmg_Color_Type next_color; if (0 == parse_embedded_escape (u, umax, default_color, &u, &next_color)) { if (i != 0) NEXT_CHAR_CELL; color = next_color; continue; } } #endif ADD_CHAR_OR_BREAK('^'); if (ch == 127) ch = '?'; else ch = ch + '@'; ADD_CHAR_OR_BREAK (ch); continue; } nconsumed = 1; if ((utf8_mode == 0) || (NULL == SLutf8_decode (u, umax, &wc, &nconsumed))) { unsigned int ii, jj; unsigned char hexbuf[8]; if ((utf8_mode == 0) && display_8bit && (*u >= display_8bit)) { ADD_CHAR_OR_BREAK(*u); } else for (ii = 0; ii < nconsumed; ii++) { sprintf ((char *)hexbuf, "<%02X>", u[ii]); for (jj = 0; jj < 4; jj++) { ADD_CHAR_OR_BREAK (hexbuf[jj]); } } u += nconsumed; continue; } u += nconsumed; if (wc < (SLwchar_Type)display_8bit) { unsigned char hexbuf[8]; unsigned int jj; sprintf ((char *)hexbuf, "<%02X>", (unsigned char) wc); for (jj = 0; jj < 4; jj++) { ADD_CHAR_OR_BREAK (hexbuf[jj]); } continue; } width = SLwchar_wcwidth (wc); if (width == 0) { /* Combining character--- must follow non-zero width char */ if (i == 0) continue; if (i < SLSMG_MAX_CHARS_PER_CELL) { ADD_TO_CHAR_CELL (wc); } continue; } if (width == 2) { if (col + 2 <= start_col) { col += 2; continue; } if (col + 1 == start_col) { /* double width character is clipped at left part of screen. * So, display right edge as a space */ col++; /* left edge */ ADD_CHAR_OR_BREAK('<'); continue; } if (i != 0) /* finish active cell */ NEXT_CHAR_CELL; if (last_was_double_width) { /* and right half of the cell */ last_was_double_width = 0; NEXT_CHAR_CELL; } if (col + 2 > max_col) { /* right side of character gets clipped */ ADD_TO_CHAR_CELL('>'); col++; break; } ADD_CHAR_OR_BREAK(wc); last_was_double_width = 1; continue; } ADD_CHAR_OR_BREAK(wc); } if (i != 0) { NEXT_CHAR_CELL; } if (last_was_double_width) { if (col < max_col) NEXT_CHAR_CELL; last_was_double_width = 0; } else if ((col < max_col) && (p->nchars == 0)) { /* The left side of a double with character was overwritten */ p->nchars = 1; p->wchars[0] = ' '; } SL_Screen[This_Row - Start_Row].flags = flags; This_Col = col; /* Why would u be NULL here?? */ if (SLsmg_Newline_Behavior == SLSMG_NEWLINE_IGNORED) { #if SLSMG_HAS_EMBEDDED_ESCAPE if (Embedded_Escape_Mode && (u != NULL)) parse_embedded_set_color (u, umax, default_color); #endif return; } if (newline_flag == 0) { #if SLSMG_HAS_EMBEDDED_ESCAPE SLuchar_Type *usave = u; #endif if (u == NULL) return; while (u < umax) { if (*u == '\n') break; u++; } if (u >= umax) { #if SLSMG_HAS_EMBEDDED_ESCAPE if (Embedded_Escape_Mode) parse_embedded_set_color (usave, umax, default_color); #endif return; } u++; } This_Row++; This_Col = 0; if (This_Row == Start_Row + (int)Screen_Rows) { if (SLsmg_Newline_Behavior == SLSMG_NEWLINE_SCROLLS) scroll_up (); } goto top; }
/* If the string u were written at the current positition, this function * returns the number of bytes necessary to reach the specified width. */ unsigned int SLsmg_strbytes (SLuchar_Type *u, SLuchar_Type *umax, unsigned int width) { SLuchar_Type *ustart; unsigned char display_8bit; int utf8_mode = UTF8_Mode; int col, col_max; if (u == NULL) return 0; display_8bit = (unsigned char) SLsmg_Display_Eight_Bit; if (utf8_mode) display_8bit = 0xA0; col = This_Col; col_max = col + width; ustart = u; while (u < umax) { SLuchar_Type ch; SLwchar_Type wc; unsigned int nconsumed = 1; ch = *u; if (ch < 0x80) { if ((ch >= 0x20) && (ch != 0x7F)) col++; else if ((ch == '\t') && (SLsmg_Tab_Width > 0)) { if (col >= 0) col = (1 + col/SLsmg_Tab_Width) * SLsmg_Tab_Width; else col = ((col + 1)/SLsmg_Tab_Width) * SLsmg_Tab_Width; } else if ((ch == '\n') && (SLsmg_Newline_Behavior != SLSMG_NEWLINE_PRINTABLE)) break; else if ((ch == 0x8) && SLsmg_Backspace_Moves) col--; #if SLSMG_HAS_EMBEDDED_ESCAPE else if ((ch == 033) && Embedded_Escape_Mode) { SLsmg_Color_Type color; SLuchar_Type *u1 = u+1; if (-1 == parse_embedded_escape (u1, umax, 0, &u1, &color)) col += 2; nconsumed = (u1 - u); } #endif else col += 2; } else if ((utf8_mode == 0) || (NULL == SLutf8_decode (u, umax, &wc, &nconsumed))) { if ((utf8_mode == 0) && (display_8bit && (*u >= display_8bit))) col++; else col += 4*nconsumed; /* <XX> */ } else if (wc < (SLwchar_Type)display_8bit) col += 4; else col += SLwchar_wcwidth (wc); if (col >= col_max) break; u += nconsumed; } return (unsigned int) (u - ustart); }
unsigned int SLsmg_strwidth (SLuchar_Type *u, SLuchar_Type *umax) { unsigned char display_8bit; int utf8_mode = UTF8_Mode; int col; if (u == NULL) return 0; display_8bit = (unsigned char) SLsmg_Display_Eight_Bit; if (utf8_mode) display_8bit = 0xA0; col = This_Col; while (u < umax) { SLuchar_Type ch; unsigned int nconsumed; SLwchar_Type wc; ch = *u; if (ch < 0x80) { u++; if ((ch >= 0x20) && (ch != 0x7F)) { col++; continue; } if ((ch == '\t') && (SLsmg_Tab_Width > 0)) { if (col >= 0) col = (1 + col/SLsmg_Tab_Width) * SLsmg_Tab_Width; else col = ((col + 1)/SLsmg_Tab_Width) * SLsmg_Tab_Width; continue; } if ((ch == '\n') && (SLsmg_Newline_Behavior != SLSMG_NEWLINE_PRINTABLE)) break; if ((ch == 0x8) && SLsmg_Backspace_Moves) { col--; continue; } #if SLSMG_HAS_EMBEDDED_ESCAPE if ((ch == 033) && Embedded_Escape_Mode) { SLsmg_Color_Type color; if (0 == parse_embedded_escape (u, umax, 0, &u, &color)) continue; } #endif col += 2; continue; } nconsumed = 1; if ((utf8_mode == 0) || (NULL == SLutf8_decode (u, umax, &wc, &nconsumed))) { if ((utf8_mode == 0) && (display_8bit && (*u >= display_8bit))) col++; else col += 4*nconsumed; u += nconsumed; continue; } u += nconsumed; if (wc < (SLwchar_Type)display_8bit) { col += 4; continue; } col += SLwchar_wcwidth (wc); } if (col < This_Col) return 0; return (unsigned int) (col - This_Col); }
/* Note: if len is < 0, entire string will be used. */ int SLcurses_waddnstr (SLcurses_Window_Type *w, char *str, int len) { unsigned int nrows, ncols, crow, ccol; SLuchar_Type *u, *umax; int is_acs = 0; if ((w == NULL) || (str == NULL)) return -1; w->modified = 1; nrows = w->nrows; ncols = w->ncols; crow = w->_cury; ccol = w->_curx; if (w->scroll_max <= nrows) nrows = w->scroll_max; if (crow >= nrows) crow = 0; /* wrap back to top */ u = (SLuchar_Type *)str; umax = &u[len >= 0 ? (unsigned int)len : strlen(str)]; while (u < umax) { SLwchar_Type ch; unsigned int nconsumed; int width = 1; if (SLsmg_is_utf8_mode () && SLutf8_decode (u, umax, &ch, &nconsumed)) { u += nconsumed; if ((ch & A_CHARTEXT) != ch) { ch = (SLwchar_Type)0xFFFDL; /* Unicode replacement character */ width = 1; } else if (SLwchar_isprint (ch)) width = SLwchar_wcwidth (ch); else width = 0; /* FIXME: cope with <%02X> printstrings. */ } else { ch = (SLwchar_Type)*u++; if (ch < 0x20 || (ch >= 0x7f && ch < 0xa0)) width = 0; /* FIXME: use display_8bit */ } if (ch == '\t') width = 1; /* HACK forcing linewrap if ccol==ncols */ if (ch == 0) continue; /* Avoid adding a literal SLCURSES_NULLCHAR. */ /* FIXME; should this function be defined in terms of waddch? */ if (ch == '\n') { w->_cury = crow; w->_curx = ccol; SLcurses_wclrtoeol (w); do_newline (w); crow = w->_cury; ccol = w->_curx; continue; } if (ccol + width > ncols) { w->_curx = ccol; w->_cury = crow; SLcurses_wclrtoeol(w); /* no-op if width<=1 */ w->_curx = ccol = 0; w->_cury = ++crow; if (crow >= nrows) { do_newline (w); crow = w->_cury; ccol = w->_curx; } } if (ch == '\t') { /* assert (ccol < ncols); */ w->_curx = ccol; w->_cury = crow; do { SLcurses_placechar (w, (SLwchar_Type)' ', 1, w->color, is_acs); w->_curx = ++ccol; } while (ccol < ncols && ccol % SLsmg_Tab_Width != 0); continue; } SLcurses_placechar (w, ch, width, w->color, is_acs); w->_curx = (ccol += width); } w->_curx = ccol; w->_cury = crow; return 0; }
int SLutf8_compare (SLuchar_Type *a, SLuchar_Type *amax, SLuchar_Type *b, SLuchar_Type *bmax, unsigned int nchars, int cs) { while (nchars && (a < amax) && (b < bmax)) { SLwchar_Type cha, chb; unsigned int na, nb; int aok, bok; if (*a < 0x80) { cha = (SLwchar_Type) *a++; aok = 1; } else { aok = (NULL != SLutf8_decode (a, amax, &cha, &na)); a += na; } if (*b < 0x80) { chb = (SLwchar_Type) *b++; bok = 1; } else { bok = (NULL != SLutf8_decode (b, bmax, &chb, &nb)); b += nb; } nchars--; if (aok && bok) { if (cs == 0) { cha = SLwchar_toupper (cha); chb = SLwchar_toupper (chb); } } else if (aok) return 1; else if (bok) return -1; if (cha == chb) continue; if (cha > chb) return 1; return -1; } if (nchars == 0) return 0; if ((a >= amax) && (b >= bmax)) return 0; if (b >= bmax) return 1; return -1; }
static SLuchar_Type *xform_utf8 (SLuchar_Type *u, SLuchar_Type *umax, SLwchar_Type (*fun)(SLwchar_Type)) { SLuchar_Type *buf, *p; unsigned int malloced_len, len; if (umax < u) return NULL; len = 0; p = buf = NULL; malloced_len = 0; while (1) { SLwchar_Type w; SLuchar_Type *u1; unsigned int nconsumed; if (malloced_len <= len + SLUTF8_MAX_MBLEN) { SLuchar_Type *newbuf; malloced_len += 1 + (umax - u) + SLUTF8_MAX_MBLEN; newbuf = (SLuchar_Type *)SLrealloc ((char *)buf, malloced_len); if (newbuf == NULL) { SLfree ((char *)buf); return NULL; } buf = newbuf; p = buf + len; } if (u >= umax) { *p = 0; p = (SLuchar_Type *) SLang_create_nslstring ((char *)buf, len); SLfree ((char *)buf); return p; } if (NULL == (u1 = SLutf8_decode (u, umax, &w, &nconsumed))) { /* Invalid sequence */ memcpy ((char *) p, u, nconsumed); p += nconsumed; len += nconsumed; u1 = u + nconsumed; } else { SLuchar_Type *p1; p1 = SLutf8_encode ((*fun)(w), p, malloced_len); if (p1 == NULL) { SLfree ((char *)buf); _pSLang_verror (SL_INTERNAL_ERROR, "SLutf8_encode returned NULL"); return NULL; } len += p1 - p; p = p1; } u = u1; } }
SLuchar_Type *SLutf8_bskip_chars (SLuchar_Type *smin, SLuchar_Type *s, unsigned int num, unsigned int *dnum, int ignore_combining) { unsigned int n; SLuchar_Type *smax = s; n = 0; while ((n < num) && (s > smin)) { unsigned char ch; unsigned int dn; s--; ch = *s; if (ch < 0x80) { n++; smax = s; continue; } dn = 0; while ((s != smin) && (Len_Map[ch] == 0) && (dn < SLUTF8_MAX_MBLEN)) { s--; ch = *s; dn++; } if (ch <= 0xBF) { /* Invalid sequence */ n++; smax--; s = smax; continue; } if (ch > 0xBF) { SLwchar_Type w; SLuchar_Type *s1; if ((NULL == (s1 = SLutf8_decode (s, smax, &w, NULL))) || (s1 != smax)) { /* This means we backed up over an invalid sequence */ dn = (unsigned int) (smax - s); n++; smax--; s = smax; continue; } if ((ignore_combining == 0) || (0 != SLwchar_wcwidth (w))) n++; smax = s; } } if (dnum != NULL) *dnum = n; return s; }
SLuchar_Type *SLutf8_skip_chars (SLuchar_Type *s, SLuchar_Type *smax, unsigned int num, unsigned int *dnum, int ignore_combining) { unsigned int n; n = 0; while ((n < num) && (s < smax)) { unsigned int len = Len_Map[*s]; if (len <= 1) { n++; s++; continue; } if (s + len > smax) { s++; n++; continue; } if (is_invalid_or_overlong_utf8 (s, len)) { s++; n++; continue; } if (ignore_combining) { SLwchar_Type w = fast_utf8_decode (s, len); if (0 != SLwchar_wcwidth (w)) n++; s += len; continue; } n++; s += len; } if (ignore_combining) { while (s < smax) { SLwchar_Type w; unsigned int nconsumed; if (NULL == SLutf8_decode (s, smax, &w, &nconsumed)) break; if (0 != SLwchar_wcwidth (w)) break; s += nconsumed; } } if (dnum != NULL) *dnum = n; return s; }