1b8ba871bSPeter Wemm /*- 2b8ba871bSPeter Wemm * Copyright (c) 1992, 1993, 1994 3b8ba871bSPeter Wemm * The Regents of the University of California. All rights reserved. 4b8ba871bSPeter Wemm * Copyright (c) 1992, 1993, 1994, 1995, 1996 5b8ba871bSPeter Wemm * Keith Bostic. All rights reserved. 6b8ba871bSPeter Wemm * 7b8ba871bSPeter Wemm * See the LICENSE file for redistribution information. 8b8ba871bSPeter Wemm */ 9b8ba871bSPeter Wemm 10b8ba871bSPeter Wemm #include "config.h" 11b8ba871bSPeter Wemm 12b8ba871bSPeter Wemm #ifndef lint 13b8ba871bSPeter Wemm static const char sccsid[] = "@(#)ex_txt.c 10.17 (Berkeley) 10/10/96"; 14b8ba871bSPeter Wemm #endif /* not lint */ 15b8ba871bSPeter Wemm 16b8ba871bSPeter Wemm #include <sys/types.h> 17b8ba871bSPeter Wemm #include <sys/queue.h> 18b8ba871bSPeter Wemm 19b8ba871bSPeter Wemm #include <bitstring.h> 20b8ba871bSPeter Wemm #include <ctype.h> 21b8ba871bSPeter Wemm #include <limits.h> 22b8ba871bSPeter Wemm #include <stdio.h> 23b8ba871bSPeter Wemm #include <stdlib.h> 24b8ba871bSPeter Wemm #include <string.h> 25b8ba871bSPeter Wemm 26b8ba871bSPeter Wemm #include "../common/common.h" 27b8ba871bSPeter Wemm 28b8ba871bSPeter Wemm /* 29b8ba871bSPeter Wemm * !!! 30b8ba871bSPeter Wemm * The backslash characters was special when it preceded a newline as part of 31b8ba871bSPeter Wemm * a substitution replacement pattern. For example, the input ":a\<cr>" would 32b8ba871bSPeter Wemm * failed immediately with an error, as the <cr> wasn't part of a substitution 33b8ba871bSPeter Wemm * replacement pattern. This implies a frightening integration of the editor 34b8ba871bSPeter Wemm * and the parser and/or the RE engine. There's no way I'm going to reproduce 35b8ba871bSPeter Wemm * those semantics. 36b8ba871bSPeter Wemm * 37b8ba871bSPeter Wemm * So, if backslashes are special, this code inserts the backslash and the next 38b8ba871bSPeter Wemm * character into the string, without regard for the character or the command 39b8ba871bSPeter Wemm * being entered. Since "\<cr>" was illegal historically (except for the one 40b8ba871bSPeter Wemm * special case), and the command will fail eventually, no historical scripts 41b8ba871bSPeter Wemm * should break (presuming they didn't depend on the failure mode itself or the 42b8ba871bSPeter Wemm * characters remaining when failure occurred. 43b8ba871bSPeter Wemm */ 44b8ba871bSPeter Wemm 45b8ba871bSPeter Wemm static int txt_dent __P((SCR *, TEXT *)); 46b8ba871bSPeter Wemm static void txt_prompt __P((SCR *, TEXT *, ARG_CHAR_T, u_int32_t)); 47b8ba871bSPeter Wemm 48b8ba871bSPeter Wemm /* 49b8ba871bSPeter Wemm * ex_txt -- 50b8ba871bSPeter Wemm * Get lines from the terminal for ex. 51b8ba871bSPeter Wemm * 52b8ba871bSPeter Wemm * PUBLIC: int ex_txt __P((SCR *, TEXTH *, ARG_CHAR_T, u_int32_t)); 53b8ba871bSPeter Wemm */ 54b8ba871bSPeter Wemm int 55b8ba871bSPeter Wemm ex_txt(sp, tiqh, prompt, flags) 56b8ba871bSPeter Wemm SCR *sp; 57b8ba871bSPeter Wemm TEXTH *tiqh; 58b8ba871bSPeter Wemm ARG_CHAR_T prompt; 59b8ba871bSPeter Wemm u_int32_t flags; 60b8ba871bSPeter Wemm { 61b8ba871bSPeter Wemm EVENT ev; 62b8ba871bSPeter Wemm GS *gp; 63b8ba871bSPeter Wemm TEXT ait, *ntp, *tp; 64b8ba871bSPeter Wemm carat_t carat_st; 65b8ba871bSPeter Wemm size_t cnt; 66b8ba871bSPeter Wemm int rval; 67b8ba871bSPeter Wemm 68b8ba871bSPeter Wemm rval = 0; 69b8ba871bSPeter Wemm 70b8ba871bSPeter Wemm /* 71b8ba871bSPeter Wemm * Get a TEXT structure with some initial buffer space, reusing the 72b8ba871bSPeter Wemm * last one if it's big enough. (All TEXT bookkeeping fields default 73b8ba871bSPeter Wemm * to 0 -- text_init() handles this.) 74b8ba871bSPeter Wemm */ 75b8ba871bSPeter Wemm if (tiqh->cqh_first != (void *)tiqh) { 76b8ba871bSPeter Wemm tp = tiqh->cqh_first; 77b8ba871bSPeter Wemm if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) { 78b8ba871bSPeter Wemm text_lfree(tiqh); 79b8ba871bSPeter Wemm goto newtp; 80b8ba871bSPeter Wemm } 81b8ba871bSPeter Wemm tp->len = 0; 82b8ba871bSPeter Wemm } else { 83b8ba871bSPeter Wemm newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL) 84b8ba871bSPeter Wemm goto err; 85b8ba871bSPeter Wemm CIRCLEQ_INSERT_HEAD(tiqh, tp, q); 86b8ba871bSPeter Wemm } 87b8ba871bSPeter Wemm 88b8ba871bSPeter Wemm /* Set the starting line number. */ 89b8ba871bSPeter Wemm tp->lno = sp->lno + 1; 90b8ba871bSPeter Wemm 91b8ba871bSPeter Wemm /* 92b8ba871bSPeter Wemm * If it's a terminal, set up autoindent, put out the prompt, and 93b8ba871bSPeter Wemm * set it up so we know we were suspended. Otherwise, turn off 94b8ba871bSPeter Wemm * the autoindent flag, as that requires less special casing below. 95b8ba871bSPeter Wemm * 96b8ba871bSPeter Wemm * XXX 97b8ba871bSPeter Wemm * Historic practice is that ^Z suspended command mode (but, because 98b8ba871bSPeter Wemm * it ran in cooked mode, it was unaffected by the autowrite option.) 99b8ba871bSPeter Wemm * On restart, any "current" input was discarded, whether in insert 100b8ba871bSPeter Wemm * mode or not, and ex was in command mode. This code matches historic 101b8ba871bSPeter Wemm * practice, but not 'cause it's easier. 102b8ba871bSPeter Wemm */ 103b8ba871bSPeter Wemm gp = sp->gp; 104b8ba871bSPeter Wemm if (F_ISSET(gp, G_SCRIPTED)) 105b8ba871bSPeter Wemm LF_CLR(TXT_AUTOINDENT); 106b8ba871bSPeter Wemm else { 107b8ba871bSPeter Wemm if (LF_ISSET(TXT_AUTOINDENT)) { 108b8ba871bSPeter Wemm LF_SET(TXT_EOFCHAR); 109b8ba871bSPeter Wemm if (v_txt_auto(sp, sp->lno, NULL, 0, tp)) 110b8ba871bSPeter Wemm goto err; 111b8ba871bSPeter Wemm } 112b8ba871bSPeter Wemm txt_prompt(sp, tp, prompt, flags); 113b8ba871bSPeter Wemm } 114b8ba871bSPeter Wemm 115b8ba871bSPeter Wemm for (carat_st = C_NOTSET;;) { 116b8ba871bSPeter Wemm if (v_event_get(sp, &ev, 0, 0)) 117b8ba871bSPeter Wemm goto err; 118b8ba871bSPeter Wemm 119b8ba871bSPeter Wemm /* Deal with all non-character events. */ 120b8ba871bSPeter Wemm switch (ev.e_event) { 121b8ba871bSPeter Wemm case E_CHARACTER: 122b8ba871bSPeter Wemm break; 123b8ba871bSPeter Wemm case E_ERR: 124b8ba871bSPeter Wemm goto err; 125b8ba871bSPeter Wemm case E_REPAINT: 126b8ba871bSPeter Wemm case E_WRESIZE: 127b8ba871bSPeter Wemm continue; 128b8ba871bSPeter Wemm case E_EOF: 129b8ba871bSPeter Wemm rval = 1; 130b8ba871bSPeter Wemm /* FALLTHROUGH */ 131b8ba871bSPeter Wemm case E_INTERRUPT: 132b8ba871bSPeter Wemm /* 133b8ba871bSPeter Wemm * Handle EOF/SIGINT events by discarding partially 134b8ba871bSPeter Wemm * entered text and returning. EOF returns failure, 135b8ba871bSPeter Wemm * E_INTERRUPT returns success. 136b8ba871bSPeter Wemm */ 137b8ba871bSPeter Wemm goto notlast; 138b8ba871bSPeter Wemm default: 139b8ba871bSPeter Wemm v_event_err(sp, &ev); 140b8ba871bSPeter Wemm goto notlast; 141b8ba871bSPeter Wemm } 142b8ba871bSPeter Wemm 143b8ba871bSPeter Wemm /* 144b8ba871bSPeter Wemm * Deal with character events. 145b8ba871bSPeter Wemm * 146b8ba871bSPeter Wemm * Check to see if the character fits into the input buffer. 147b8ba871bSPeter Wemm * (Use tp->len, ignore overwrite and non-printable chars.) 148b8ba871bSPeter Wemm */ 149b8ba871bSPeter Wemm BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1); 150b8ba871bSPeter Wemm 151b8ba871bSPeter Wemm switch (ev.e_value) { 152b8ba871bSPeter Wemm case K_CR: 153b8ba871bSPeter Wemm /* 154b8ba871bSPeter Wemm * !!! 155b8ba871bSPeter Wemm * Historically, <carriage-return>'s in the command 156b8ba871bSPeter Wemm * weren't special, so the ex parser would return an 157b8ba871bSPeter Wemm * unknown command error message. However, if they 158b8ba871bSPeter Wemm * terminated the command if they were in a map. I'm 159b8ba871bSPeter Wemm * pretty sure this still isn't right, but it handles 160b8ba871bSPeter Wemm * what I've seen so far. 161b8ba871bSPeter Wemm */ 162b8ba871bSPeter Wemm if (!F_ISSET(&ev.e_ch, CH_MAPPED)) 163b8ba871bSPeter Wemm goto ins_ch; 164b8ba871bSPeter Wemm /* FALLTHROUGH */ 165b8ba871bSPeter Wemm case K_NL: 166b8ba871bSPeter Wemm /* 167b8ba871bSPeter Wemm * '\' can escape <carriage-return>/<newline>. We 168b8ba871bSPeter Wemm * don't discard the backslash because we need it 169b8ba871bSPeter Wemm * to get the <newline> through the ex parser. 170b8ba871bSPeter Wemm */ 171b8ba871bSPeter Wemm if (LF_ISSET(TXT_BACKSLASH) && 172b8ba871bSPeter Wemm tp->len != 0 && tp->lb[tp->len - 1] == '\\') 173b8ba871bSPeter Wemm goto ins_ch; 174b8ba871bSPeter Wemm 175b8ba871bSPeter Wemm /* 176b8ba871bSPeter Wemm * CR returns from the ex command line. 177b8ba871bSPeter Wemm * 178b8ba871bSPeter Wemm * XXX 179b8ba871bSPeter Wemm * Terminate with a nul, needed by filter. 180b8ba871bSPeter Wemm */ 181b8ba871bSPeter Wemm if (LF_ISSET(TXT_CR)) { 182b8ba871bSPeter Wemm tp->lb[tp->len] = '\0'; 183b8ba871bSPeter Wemm goto done; 184b8ba871bSPeter Wemm } 185b8ba871bSPeter Wemm 186b8ba871bSPeter Wemm /* 187b8ba871bSPeter Wemm * '.' may terminate text input mode; free the current 188b8ba871bSPeter Wemm * TEXT. 189b8ba871bSPeter Wemm */ 190b8ba871bSPeter Wemm if (LF_ISSET(TXT_DOTTERM) && tp->len == tp->ai + 1 && 191b8ba871bSPeter Wemm tp->lb[tp->len - 1] == '.') { 192b8ba871bSPeter Wemm notlast: CIRCLEQ_REMOVE(tiqh, tp, q); 193b8ba871bSPeter Wemm text_free(tp); 194b8ba871bSPeter Wemm goto done; 195b8ba871bSPeter Wemm } 196b8ba871bSPeter Wemm 197b8ba871bSPeter Wemm /* Set up bookkeeping for the new line. */ 198b8ba871bSPeter Wemm if ((ntp = text_init(sp, NULL, 0, 32)) == NULL) 199b8ba871bSPeter Wemm goto err; 200b8ba871bSPeter Wemm ntp->lno = tp->lno + 1; 201b8ba871bSPeter Wemm 202b8ba871bSPeter Wemm /* 203b8ba871bSPeter Wemm * Reset the autoindent line value. 0^D keeps the ai 204b8ba871bSPeter Wemm * line from changing, ^D changes the level, even if 205b8ba871bSPeter Wemm * there were no characters in the old line. Note, if 206b8ba871bSPeter Wemm * using the current tp structure, use the cursor as 207b8ba871bSPeter Wemm * the length, the autoindent characters may have been 208b8ba871bSPeter Wemm * erased. 209b8ba871bSPeter Wemm */ 210b8ba871bSPeter Wemm if (LF_ISSET(TXT_AUTOINDENT)) { 211b8ba871bSPeter Wemm if (carat_st == C_NOCHANGE) { 212b8ba871bSPeter Wemm if (v_txt_auto(sp, 213b8ba871bSPeter Wemm OOBLNO, &ait, ait.ai, ntp)) 214b8ba871bSPeter Wemm goto err; 215b8ba871bSPeter Wemm free(ait.lb); 216b8ba871bSPeter Wemm } else 217b8ba871bSPeter Wemm if (v_txt_auto(sp, 218b8ba871bSPeter Wemm OOBLNO, tp, tp->len, ntp)) 219b8ba871bSPeter Wemm goto err; 220b8ba871bSPeter Wemm carat_st = C_NOTSET; 221b8ba871bSPeter Wemm } 222b8ba871bSPeter Wemm txt_prompt(sp, ntp, prompt, flags); 223b8ba871bSPeter Wemm 224b8ba871bSPeter Wemm /* 225b8ba871bSPeter Wemm * Swap old and new TEXT's, and insert the new TEXT 226b8ba871bSPeter Wemm * into the queue. 227b8ba871bSPeter Wemm */ 228b8ba871bSPeter Wemm tp = ntp; 229b8ba871bSPeter Wemm CIRCLEQ_INSERT_TAIL(tiqh, tp, q); 230b8ba871bSPeter Wemm break; 231b8ba871bSPeter Wemm case K_CARAT: /* Delete autoindent chars. */ 232b8ba871bSPeter Wemm if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) 233b8ba871bSPeter Wemm carat_st = C_CARATSET; 234b8ba871bSPeter Wemm goto ins_ch; 235b8ba871bSPeter Wemm case K_ZERO: /* Delete autoindent chars. */ 236b8ba871bSPeter Wemm if (tp->len <= tp->ai && LF_ISSET(TXT_AUTOINDENT)) 237b8ba871bSPeter Wemm carat_st = C_ZEROSET; 238b8ba871bSPeter Wemm goto ins_ch; 239b8ba871bSPeter Wemm case K_CNTRLD: /* Delete autoindent char. */ 240b8ba871bSPeter Wemm /* 241b8ba871bSPeter Wemm * !!! 242b8ba871bSPeter Wemm * Historically, the ^D command took (but then ignored) 243b8ba871bSPeter Wemm * a count. For simplicity, we don't return it unless 244b8ba871bSPeter Wemm * it's the first character entered. The check for len 245b8ba871bSPeter Wemm * equal to 0 is okay, TXT_AUTOINDENT won't be set. 246b8ba871bSPeter Wemm */ 247b8ba871bSPeter Wemm if (LF_ISSET(TXT_CNTRLD)) { 248b8ba871bSPeter Wemm for (cnt = 0; cnt < tp->len; ++cnt) 249b8ba871bSPeter Wemm if (!isblank(tp->lb[cnt])) 250b8ba871bSPeter Wemm break; 251b8ba871bSPeter Wemm if (cnt == tp->len) { 252b8ba871bSPeter Wemm tp->len = 1; 253b8ba871bSPeter Wemm tp->lb[0] = ev.e_c; 254b8ba871bSPeter Wemm tp->lb[1] = '\0'; 255b8ba871bSPeter Wemm 256b8ba871bSPeter Wemm /* 257b8ba871bSPeter Wemm * Put out a line separator, in case 258b8ba871bSPeter Wemm * the command fails. 259b8ba871bSPeter Wemm */ 260b8ba871bSPeter Wemm (void)putchar('\n'); 261b8ba871bSPeter Wemm goto done; 262b8ba871bSPeter Wemm } 263b8ba871bSPeter Wemm } 264b8ba871bSPeter Wemm 265b8ba871bSPeter Wemm /* 266b8ba871bSPeter Wemm * POSIX 1003.1b-1993, paragraph 7.1.1.9, states that 267b8ba871bSPeter Wemm * the EOF characters are discarded if there are other 268b8ba871bSPeter Wemm * characters to process in the line, i.e. if the EOF 269b8ba871bSPeter Wemm * is not the first character in the line. For this 270b8ba871bSPeter Wemm * reason, historic ex discarded the EOF characters, 271b8ba871bSPeter Wemm * even if occurring in the middle of the input line. 272b8ba871bSPeter Wemm * We match that historic practice. 273b8ba871bSPeter Wemm * 274b8ba871bSPeter Wemm * !!! 275b8ba871bSPeter Wemm * The test for discarding in the middle of the line is 276b8ba871bSPeter Wemm * done in the switch, because the CARAT forms are N+1, 277b8ba871bSPeter Wemm * not N. 278b8ba871bSPeter Wemm * 279b8ba871bSPeter Wemm * !!! 280b8ba871bSPeter Wemm * There's considerable magic to make the terminal code 281b8ba871bSPeter Wemm * return the EOF character at all. See that code for 282b8ba871bSPeter Wemm * details. 283b8ba871bSPeter Wemm */ 284b8ba871bSPeter Wemm if (!LF_ISSET(TXT_AUTOINDENT) || tp->len == 0) 285b8ba871bSPeter Wemm continue; 286b8ba871bSPeter Wemm switch (carat_st) { 287b8ba871bSPeter Wemm case C_CARATSET: /* ^^D */ 288b8ba871bSPeter Wemm if (tp->len > tp->ai + 1) 289b8ba871bSPeter Wemm continue; 290b8ba871bSPeter Wemm 291b8ba871bSPeter Wemm /* Save the ai string for later. */ 292b8ba871bSPeter Wemm ait.lb = NULL; 293b8ba871bSPeter Wemm ait.lb_len = 0; 294b8ba871bSPeter Wemm BINC_GOTO(sp, ait.lb, ait.lb_len, tp->ai); 295b8ba871bSPeter Wemm memcpy(ait.lb, tp->lb, tp->ai); 296b8ba871bSPeter Wemm ait.ai = ait.len = tp->ai; 297b8ba871bSPeter Wemm 298b8ba871bSPeter Wemm carat_st = C_NOCHANGE; 299b8ba871bSPeter Wemm goto leftmargin; 300b8ba871bSPeter Wemm case C_ZEROSET: /* 0^D */ 301b8ba871bSPeter Wemm if (tp->len > tp->ai + 1) 302b8ba871bSPeter Wemm continue; 303b8ba871bSPeter Wemm 304b8ba871bSPeter Wemm carat_st = C_NOTSET; 305b8ba871bSPeter Wemm leftmargin: (void)gp->scr_ex_adjust(sp, EX_TERM_CE); 306b8ba871bSPeter Wemm tp->ai = tp->len = 0; 307b8ba871bSPeter Wemm break; 308b8ba871bSPeter Wemm case C_NOTSET: /* ^D */ 309b8ba871bSPeter Wemm if (tp->len > tp->ai) 310b8ba871bSPeter Wemm continue; 311b8ba871bSPeter Wemm 312b8ba871bSPeter Wemm if (txt_dent(sp, tp)) 313b8ba871bSPeter Wemm goto err; 314b8ba871bSPeter Wemm break; 315b8ba871bSPeter Wemm default: 316b8ba871bSPeter Wemm abort(); 317b8ba871bSPeter Wemm } 318b8ba871bSPeter Wemm 319b8ba871bSPeter Wemm /* Clear and redisplay the line. */ 320b8ba871bSPeter Wemm (void)gp->scr_ex_adjust(sp, EX_TERM_CE); 321b8ba871bSPeter Wemm txt_prompt(sp, tp, prompt, flags); 322b8ba871bSPeter Wemm break; 323b8ba871bSPeter Wemm default: 324b8ba871bSPeter Wemm /* 325b8ba871bSPeter Wemm * See the TXT_BEAUTIFY comment in vi/v_txt_ev.c. 326b8ba871bSPeter Wemm * 327b8ba871bSPeter Wemm * Silently eliminate any iscntrl() character that was 328b8ba871bSPeter Wemm * not already handled specially, except for <tab> and 329b8ba871bSPeter Wemm * <ff>. 330b8ba871bSPeter Wemm */ 331b8ba871bSPeter Wemm ins_ch: if (LF_ISSET(TXT_BEAUTIFY) && iscntrl(ev.e_c) && 332b8ba871bSPeter Wemm ev.e_value != K_FORMFEED && ev.e_value != K_TAB) 333b8ba871bSPeter Wemm break; 334b8ba871bSPeter Wemm 335b8ba871bSPeter Wemm tp->lb[tp->len++] = ev.e_c; 336b8ba871bSPeter Wemm break; 337b8ba871bSPeter Wemm } 338b8ba871bSPeter Wemm } 339b8ba871bSPeter Wemm /* NOTREACHED */ 340b8ba871bSPeter Wemm 341b8ba871bSPeter Wemm done: return (rval); 342b8ba871bSPeter Wemm 343b8ba871bSPeter Wemm err: 344b8ba871bSPeter Wemm alloc_err: 345b8ba871bSPeter Wemm return (1); 346b8ba871bSPeter Wemm } 347b8ba871bSPeter Wemm 348b8ba871bSPeter Wemm /* 349b8ba871bSPeter Wemm * txt_prompt -- 350b8ba871bSPeter Wemm * Display the ex prompt, line number, ai characters. Characters had 351b8ba871bSPeter Wemm * better be printable by the terminal driver, but that's its problem, 352b8ba871bSPeter Wemm * not ours. 353b8ba871bSPeter Wemm */ 354b8ba871bSPeter Wemm static void 355b8ba871bSPeter Wemm txt_prompt(sp, tp, prompt, flags) 356b8ba871bSPeter Wemm SCR *sp; 357b8ba871bSPeter Wemm TEXT *tp; 358b8ba871bSPeter Wemm ARG_CHAR_T prompt; 359b8ba871bSPeter Wemm u_int32_t flags; 360b8ba871bSPeter Wemm { 361b8ba871bSPeter Wemm /* Display the prompt. */ 362b8ba871bSPeter Wemm if (LF_ISSET(TXT_PROMPT)) 363b8ba871bSPeter Wemm (void)printf("%c", prompt); 364b8ba871bSPeter Wemm 365b8ba871bSPeter Wemm /* Display the line number. */ 366b8ba871bSPeter Wemm if (LF_ISSET(TXT_NUMBER) && O_ISSET(sp, O_NUMBER)) 367b8ba871bSPeter Wemm (void)printf("%6lu ", (u_long)tp->lno); 368b8ba871bSPeter Wemm 369b8ba871bSPeter Wemm /* Print out autoindent string. */ 370b8ba871bSPeter Wemm if (LF_ISSET(TXT_AUTOINDENT)) 371b8ba871bSPeter Wemm (void)printf("%.*s", (int)tp->ai, tp->lb); 372b8ba871bSPeter Wemm (void)fflush(stdout); 373b8ba871bSPeter Wemm } 374b8ba871bSPeter Wemm 375b8ba871bSPeter Wemm /* 376b8ba871bSPeter Wemm * txt_dent -- 377b8ba871bSPeter Wemm * Handle ^D outdents. 378b8ba871bSPeter Wemm * 379b8ba871bSPeter Wemm * Ex version of vi/v_ntext.c:txt_dent(). See that code for the (usual) 380b8ba871bSPeter Wemm * ranting and raving. This is a fair bit simpler as ^T isn't special. 381b8ba871bSPeter Wemm */ 382b8ba871bSPeter Wemm static int 383b8ba871bSPeter Wemm txt_dent(sp, tp) 384b8ba871bSPeter Wemm SCR *sp; 385b8ba871bSPeter Wemm TEXT *tp; 386b8ba871bSPeter Wemm { 387b8ba871bSPeter Wemm u_long sw, ts; 388b8ba871bSPeter Wemm size_t cno, off, scno, spaces, tabs; 389b8ba871bSPeter Wemm 390b8ba871bSPeter Wemm ts = O_VAL(sp, O_TABSTOP); 391b8ba871bSPeter Wemm sw = O_VAL(sp, O_SHIFTWIDTH); 392b8ba871bSPeter Wemm 393b8ba871bSPeter Wemm /* Get the current screen column. */ 394b8ba871bSPeter Wemm for (off = scno = 0; off < tp->len; ++off) 395b8ba871bSPeter Wemm if (tp->lb[off] == '\t') 396b8ba871bSPeter Wemm scno += COL_OFF(scno, ts); 397b8ba871bSPeter Wemm else 398b8ba871bSPeter Wemm ++scno; 399b8ba871bSPeter Wemm 400b8ba871bSPeter Wemm /* Get the previous shiftwidth column. */ 401b8ba871bSPeter Wemm cno = scno; 402b8ba871bSPeter Wemm scno -= --scno % sw; 403b8ba871bSPeter Wemm 404b8ba871bSPeter Wemm /* 405b8ba871bSPeter Wemm * Since we don't know what comes before the character(s) being 406b8ba871bSPeter Wemm * deleted, we have to resolve the autoindent characters . The 407b8ba871bSPeter Wemm * example is a <tab>, which doesn't take up a full shiftwidth 408b8ba871bSPeter Wemm * number of columns because it's preceded by <space>s. This is 409b8ba871bSPeter Wemm * easy to get if the user sets shiftwidth to a value less than 410b8ba871bSPeter Wemm * tabstop, and then uses ^T to indent, and ^D to outdent. 411b8ba871bSPeter Wemm * 412b8ba871bSPeter Wemm * Count up spaces/tabs needed to get to the target. 413b8ba871bSPeter Wemm */ 414b8ba871bSPeter Wemm for (cno = 0, tabs = 0; cno + COL_OFF(cno, ts) <= scno; ++tabs) 415b8ba871bSPeter Wemm cno += COL_OFF(cno, ts); 416b8ba871bSPeter Wemm spaces = scno - cno; 417b8ba871bSPeter Wemm 418b8ba871bSPeter Wemm /* Make sure there's enough room. */ 419b8ba871bSPeter Wemm BINC_RET(sp, tp->lb, tp->lb_len, tabs + spaces + 1); 420b8ba871bSPeter Wemm 421b8ba871bSPeter Wemm /* Adjust the final ai character count. */ 422b8ba871bSPeter Wemm tp->ai = tabs + spaces; 423b8ba871bSPeter Wemm 424b8ba871bSPeter Wemm /* Enter the replacement characters. */ 425b8ba871bSPeter Wemm for (tp->len = 0; tabs > 0; --tabs) 426b8ba871bSPeter Wemm tp->lb[tp->len++] = '\t'; 427b8ba871bSPeter Wemm for (; spaces > 0; --spaces) 428b8ba871bSPeter Wemm tp->lb[tp->len++] = ' '; 429b8ba871bSPeter Wemm return (0); 430b8ba871bSPeter Wemm } 431