1 /* $OpenBSD: region.c,v 1.27 2008/09/15 16:11:35 kjell Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Region based commands. 7 * The routines in this file deal with the region, that magic space between 8 * "." and mark. Some functions are commands. Some functions are just for 9 * internal use. 10 */ 11 12 #include "def.h" 13 14 static int getregion(struct region *); 15 static int setsize(struct region *, RSIZE); 16 17 /* 18 * Kill the region. Ask "getregion" to figure out the bounds of the region. 19 * Move "." to the start, and kill the characters. Mark is cleared afterwards. 20 */ 21 /* ARGSUSED */ 22 int 23 killregion(int f, int n) 24 { 25 int s; 26 struct region region; 27 28 if ((s = getregion(®ion)) != TRUE) 29 return (s); 30 /* This is a kill-type command, so do magic kill buffer stuff. */ 31 if ((lastflag & CFKILL) == 0) 32 kdelete(); 33 thisflag |= CFKILL; 34 curwp->w_dotp = region.r_linep; 35 curwp->w_doto = region.r_offset; 36 s = ldelete(region.r_size, KFORW); 37 clearmark(FFARG, 0); 38 39 return (s); 40 } 41 42 /* 43 * Copy all of the characters in the region to the kill buffer, 44 * clearing the mark afterwards. 45 * This is a bit like a kill region followed by a yank. 46 */ 47 /* ARGSUSED */ 48 int 49 copyregion(int f, int n) 50 { 51 struct line *linep; 52 struct region region; 53 int loffs; 54 int s; 55 56 if ((s = getregion(®ion)) != TRUE) 57 return (s); 58 59 /* kill type command */ 60 if ((lastflag & CFKILL) == 0) 61 kdelete(); 62 thisflag |= CFKILL; 63 64 /* current line */ 65 linep = region.r_linep; 66 67 /* current offset */ 68 loffs = region.r_offset; 69 70 while (region.r_size--) { 71 if (loffs == llength(linep)) { /* End of line. */ 72 if ((s = kinsert('\n', KFORW)) != TRUE) 73 return (s); 74 linep = lforw(linep); 75 loffs = 0; 76 } else { /* Middle of line. */ 77 if ((s = kinsert(lgetc(linep, loffs), KFORW)) != TRUE) 78 return (s); 79 ++loffs; 80 } 81 } 82 clearmark(FFARG, 0); 83 84 return (TRUE); 85 } 86 87 /* 88 * Lower case region. Zap all of the upper case characters in the region to 89 * lower case. Use the region code to set the limits. Scan the buffer, doing 90 * the changes. Call "lchange" to ensure that redisplay is done in all 91 * buffers. 92 */ 93 /* ARGSUSED */ 94 int 95 lowerregion(int f, int n) 96 { 97 struct line *linep; 98 struct region region; 99 int loffs, c, s; 100 101 if ((s = checkdirty(curbp)) != TRUE) 102 return (s); 103 if (curbp->b_flag & BFREADONLY) { 104 ewprintf("Buffer is read-only"); 105 return (FALSE); 106 } 107 108 if ((s = getregion(®ion)) != TRUE) 109 return (s); 110 111 undo_add_change(region.r_linep, region.r_offset, region.r_size); 112 113 lchange(WFFULL); 114 linep = region.r_linep; 115 loffs = region.r_offset; 116 while (region.r_size--) { 117 if (loffs == llength(linep)) { 118 linep = lforw(linep); 119 loffs = 0; 120 } else { 121 c = lgetc(linep, loffs); 122 if (ISUPPER(c) != FALSE) 123 lputc(linep, loffs, TOLOWER(c)); 124 ++loffs; 125 } 126 } 127 return (TRUE); 128 } 129 130 /* 131 * Upper case region. Zap all of the lower case characters in the region to 132 * upper case. Use the region code to set the limits. Scan the buffer, 133 * doing the changes. Call "lchange" to ensure that redisplay is done in all 134 * buffers. 135 */ 136 /* ARGSUSED */ 137 int 138 upperregion(int f, int n) 139 { 140 struct line *linep; 141 struct region region; 142 int loffs, c, s; 143 144 if ((s = checkdirty(curbp)) != TRUE) 145 return (s); 146 if (curbp->b_flag & BFREADONLY) { 147 ewprintf("Buffer is read-only"); 148 return (FALSE); 149 } 150 if ((s = getregion(®ion)) != TRUE) 151 return (s); 152 153 undo_add_change(region.r_linep, region.r_offset, region.r_size); 154 155 lchange(WFFULL); 156 linep = region.r_linep; 157 loffs = region.r_offset; 158 while (region.r_size--) { 159 if (loffs == llength(linep)) { 160 linep = lforw(linep); 161 loffs = 0; 162 } else { 163 c = lgetc(linep, loffs); 164 if (ISLOWER(c) != FALSE) 165 lputc(linep, loffs, TOUPPER(c)); 166 ++loffs; 167 } 168 } 169 return (TRUE); 170 } 171 172 /* 173 * This routine figures out the bound of the region in the current window, 174 * and stores the results into the fields of the REGION structure. Dot and 175 * mark are usually close together, but I don't know the order, so I scan 176 * outward from dot, in both directions, looking for mark. The size is kept 177 * in a long. At the end, after the size is figured out, it is assigned to 178 * the size field of the region structure. If this assignment loses any bits, 179 * then we print an error. This is "type independent" overflow checking. All 180 * of the callers of this routine should be ready to get an ABORT status, 181 * because I might add a "if regions is big, ask before clobbering" flag. 182 */ 183 static int 184 getregion(struct region *rp) 185 { 186 struct line *flp, *blp; 187 long fsize, bsize; 188 189 if (curwp->w_markp == NULL) { 190 ewprintf("No mark set in this window"); 191 return (FALSE); 192 } 193 194 /* "r_size" always ok */ 195 if (curwp->w_dotp == curwp->w_markp) { 196 rp->r_linep = curwp->w_dotp; 197 if (curwp->w_doto < curwp->w_marko) { 198 rp->r_offset = curwp->w_doto; 199 rp->r_size = (RSIZE)(curwp->w_marko - curwp->w_doto); 200 } else { 201 rp->r_offset = curwp->w_marko; 202 rp->r_size = (RSIZE)(curwp->w_doto - curwp->w_marko); 203 } 204 return (TRUE); 205 } 206 /* get region size */ 207 flp = blp = curwp->w_dotp; 208 bsize = curwp->w_doto; 209 fsize = llength(flp) - curwp->w_doto + 1; 210 while (lforw(flp) != curbp->b_headp || lback(blp) != curbp->b_headp) { 211 if (lforw(flp) != curbp->b_headp) { 212 flp = lforw(flp); 213 if (flp == curwp->w_markp) { 214 rp->r_linep = curwp->w_dotp; 215 rp->r_offset = curwp->w_doto; 216 return (setsize(rp, 217 (RSIZE)(fsize + curwp->w_marko))); 218 } 219 fsize += llength(flp) + 1; 220 } 221 if (lback(blp) != curbp->b_headp) { 222 blp = lback(blp); 223 bsize += llength(blp) + 1; 224 if (blp == curwp->w_markp) { 225 rp->r_linep = blp; 226 rp->r_offset = curwp->w_marko; 227 return (setsize(rp, 228 (RSIZE)(bsize - curwp->w_marko))); 229 } 230 } 231 } 232 ewprintf("Bug: lost mark"); 233 return (FALSE); 234 } 235 236 /* 237 * Set size, and check for overflow. 238 */ 239 static int 240 setsize(struct region *rp, RSIZE size) 241 { 242 rp->r_size = size; 243 if (rp->r_size != size) { 244 ewprintf("Region is too large"); 245 return (FALSE); 246 } 247 return (TRUE); 248 } 249 250 #define PREFIXLENGTH 40 251 static char prefix_string[PREFIXLENGTH] = {'>', '\0'}; 252 253 /* 254 * Prefix the region with whatever is in prefix_string. Leaves dot at the 255 * beginning of the line after the end of the region. If an argument is 256 * given, prompts for the line prefix string. 257 */ 258 /* ARGSUSED */ 259 int 260 prefixregion(int f, int n) 261 { 262 struct line *first, *last; 263 struct region region; 264 char *prefix = prefix_string; 265 int nline; 266 int s; 267 268 if ((s = checkdirty(curbp)) != TRUE) 269 return (s); 270 if (curbp->b_flag & BFREADONLY) { 271 ewprintf("Buffer is read-only"); 272 return (FALSE); 273 } 274 if ((f == TRUE) && ((s = setprefix(FFRAND, 1)) != TRUE)) 275 return (s); 276 277 /* get # of lines to affect */ 278 if ((s = getregion(®ion)) != TRUE) 279 return (s); 280 first = region.r_linep; 281 last = (first == curwp->w_dotp) ? curwp->w_markp : curwp->w_dotp; 282 for (nline = 1; first != last; nline++) 283 first = lforw(first); 284 285 /* move to beginning of region */ 286 curwp->w_dotp = region.r_linep; 287 curwp->w_doto = region.r_offset; 288 289 /* for each line, go to beginning and insert the prefix string */ 290 while (nline--) { 291 (void)gotobol(FFRAND, 1); 292 for (prefix = prefix_string; *prefix; prefix++) 293 (void)linsert(1, *prefix); 294 (void)forwline(FFRAND, 1); 295 } 296 (void)gotobol(FFRAND, 1); 297 return (TRUE); 298 } 299 300 /* 301 * Set line prefix string. Used by prefixregion. 302 */ 303 /* ARGSUSED */ 304 int 305 setprefix(int f, int n) 306 { 307 char buf[PREFIXLENGTH], *rep; 308 int retval; 309 310 if (prefix_string[0] == '\0') 311 rep = eread("Prefix string: ", buf, sizeof(buf), 312 EFNEW | EFCR); 313 else 314 rep = eread("Prefix string (default %s): ", buf, sizeof(buf), 315 EFNUL | EFNEW | EFCR, prefix_string); 316 if (rep == NULL) 317 return (ABORT); 318 if (rep[0] != '\0') { 319 (void)strlcpy(prefix_string, rep, sizeof(prefix_string)); 320 retval = TRUE; 321 } else if (rep[0] == '\0' && prefix_string[0] != '\0') { 322 /* CR -- use old one */ 323 retval = TRUE; 324 } else 325 retval = FALSE; 326 return (retval); 327 } 328 329 int 330 region_get_data(struct region *reg, char *buf, int len) 331 { 332 int i, off; 333 struct line *lp; 334 335 off = reg->r_offset; 336 lp = reg->r_linep; 337 for (i = 0; i < len; i++) { 338 if (off == llength(lp)) { 339 lp = lforw(lp); 340 if (lp == curbp->b_headp) 341 break; 342 off = 0; 343 buf[i] = '\n'; 344 } else { 345 buf[i] = lgetc(lp, off); 346 off++; 347 } 348 } 349 buf[i] = '\0'; 350 return (i); 351 } 352 353 void 354 region_put_data(const char *buf, int len) 355 { 356 int i; 357 358 for (i = 0; buf[i] != '\0' && i < len; i++) { 359 if (buf[i] == '\n') 360 lnewline(); 361 else 362 linsert(1, buf[i]); 363 } 364 } 365