]> git.zerfleddert.de Git - micropolis/blob - src/tk/tktxidx.c
Import Micropolis from http://www.donhopkins.com/home/micropolis/
[micropolis] / src / tk / tktxidx.c
1 /*
2 * tkTextIndex.c --
3 *
4 * This module provides procedures that manipulate indices for
5 * text widgets.
6 *
7 * Copyright 1992 Regents of the University of California.
8 * Permission to use, copy, modify, and distribute this
9 * software and its documentation for any purpose and without
10 * fee is hereby granted, provided that the above copyright
11 * notice appear in all copies. The University of California
12 * makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without
14 * express or implied warranty.
15 */
16
17 #ifndef lint
18 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkTextIndex.c,v 1.2 92/07/16 16:32:26 ouster Exp $ SPRITE (Berkeley)";
19 #endif
20
21 #include "default.h"
22 #include "tkconfig.h"
23 #include "tk.h"
24 #include "tktext.h"
25
26 /*
27 * Forward declarations for procedures defined later in this file:
28 */
29
30 static void BackwardChars _ANSI_ARGS_((TkText *textPtr,
31 TkTextLine *linePtr, int *lineIndexPtr,
32 int *chPtr, int count));
33 static char * ForwBack _ANSI_ARGS_((TkText *textPtr,
34 char *string, int *lineIndexPtr, int *chPtr));
35 static void ForwardChars _ANSI_ARGS_((TkText *textPtr,
36 TkTextLine *linePtr, int *lineIndexPtr,
37 int *chPtr, int count));
38 static char * StartEnd _ANSI_ARGS_((TkText *textPtr,
39 char *string, int *lineIndexPtr, int *chPtr));
40 \f
41 /*
42 *----------------------------------------------------------------------
43 *
44 * TkTextGetIndex --
45 *
46 * Given a string, return the line and character indices that
47 * it describes.
48 *
49 * Results:
50 * The return value is a standard Tcl return result. If
51 * TCL_OK is returned, then everything went well and information
52 * is stored at *lineIndexPtr and *chPtr; otherwise TCL_ERROR
53 * is returned and an error message is left in interp->result.
54 *
55 * Side effects:
56 * None.
57 *
58 *----------------------------------------------------------------------
59 */
60
61 int
62 TkTextGetIndex(interp, textPtr, string, lineIndexPtr, chPtr)
63 Tcl_Interp *interp; /* Use this for error reporting. */
64 TkText *textPtr; /* Information about text widget. */
65 char *string; /* Textual description of position. */
66 int *lineIndexPtr; /* Store line number here. */
67 int *chPtr; /* Store character position here. */
68 {
69 register char *p;
70 char *end, *endOfBase;
71 TkTextLine *linePtr;
72 Tcl_HashEntry *hPtr;
73 TkAnnotation *markPtr;
74 TkTextTag *tagPtr;
75 TkTextSearch search;
76 int first;
77 char c;
78
79 /*
80 *------------------------------------------------
81 * Stage 1: parse the base index.
82 *------------------------------------------------
83 */
84
85 if (string[0] == '@') {
86 /*
87 * Find character at a given x,y location in the window.
88 */
89
90 int x, y;
91
92 p = string+1;
93 x = strtol(p, &end, 0);
94 if ((end == p) || (*end != ',')) {
95 goto error;
96 }
97 p = end+1;
98 y = strtol(p, &end, 0);
99 if (end == p) {
100 goto error;
101 }
102 *lineIndexPtr = TkBTreeLineIndex(TkTextCharAtLoc(textPtr, x,
103 y, chPtr));
104 endOfBase = end;
105 goto gotBase;
106 } else if (isdigit(string[0]) || (string[0] == '-')) {
107 /*
108 * Base is identified with line and character indices.
109 */
110
111 *lineIndexPtr = strtol(string, &end, 0) - 1;
112 if ((end == string) || (*end != '.')) {
113 goto error;
114 }
115 p = end+1;
116 if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
117 linePtr = TkBTreeFindLine(textPtr->tree, *lineIndexPtr);
118 if (linePtr == NULL) {
119 Tcl_AppendResult(interp, "bad text index \"", string,
120 "\": no such line in text", (char *) NULL);
121 return TCL_ERROR;
122 }
123 *chPtr = linePtr->numBytes - 1;
124 endOfBase = p+3;
125 goto gotBase;
126 } else {
127 *chPtr = strtol(p, &end, 0);
128 if (end == p) {
129 goto error;
130 }
131 endOfBase = end;
132 goto gotBase;
133 }
134 }
135
136 for (p = string; *p != 0; p++) {
137 if (isspace(*p) || (*p == '+') || (*p == '-')) {
138 break;
139 }
140 }
141 endOfBase = p;
142 if ((string[0] == 'e')
143 && (strncmp(string, "end", endOfBase-string) == 0)) {
144 /*
145 * Base position is end of text.
146 */
147
148 *lineIndexPtr = TkBTreeNumLines(textPtr->tree) - 1;
149 linePtr = TkBTreeFindLine(textPtr->tree, *lineIndexPtr);
150 *chPtr = linePtr->numBytes - 1;
151 goto gotBase;
152 } else {
153 /*
154 * See if the base position is the name of a mark.
155 */
156
157 c = *endOfBase;
158 *endOfBase = 0;
159 hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
160 *endOfBase = c;
161 if (hPtr != NULL) {
162 markPtr = (TkAnnotation *) Tcl_GetHashValue(hPtr);
163 *lineIndexPtr = TkBTreeLineIndex(markPtr->linePtr);
164 *chPtr = markPtr->ch;
165 goto gotBase;
166 }
167 }
168
169 /*
170 * Nothing has worked so far. See if the base has the form
171 * "tag.first" or "tag.last" where "tag" is the name of a valid
172 * tag.
173 */
174
175 p = strchr(string, '.');
176 if (p == NULL) {
177 goto error;
178 }
179 if ((p[1] == 'f') && (endOfBase == (p+6))
180 && (strncmp(p+1, "first", endOfBase - (p+1)) == 0)) {
181 first = 1;
182 } else if ((p[1] == 'l') && (endOfBase == (p+5))
183 && (strncmp(p+1, "last", endOfBase - (p+1)) == 0)) {
184 first = 0;
185 } else {
186 goto error;
187 }
188 *p = 0;
189 hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
190 *p = '.';
191 if (hPtr == NULL) {
192 goto error;
193 }
194 tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
195 TkBTreeStartSearch(textPtr->tree, 0, 0, TkBTreeNumLines(textPtr->tree),
196 0, tagPtr, &search);
197 if (!TkBTreeNextTag(&search)) {
198 Tcl_AppendResult(interp,
199 "text doesn't contain any characters tagged with \"",
200 Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"", (char *) NULL);
201 return TCL_ERROR;
202 }
203 if (first) {
204 *lineIndexPtr = search.line1;
205 *chPtr = search.ch1;
206 } else {
207 while (TkBTreeNextTag(&search)) {
208 *lineIndexPtr = search.line1;
209 *chPtr = search.ch1;
210 }
211 }
212
213 /*
214 *-------------------------------------------------------------------
215 * Stage 2: process zero or more modifiers. Each modifier is either
216 * a keyword like "wordend" or "linestart", or it has the form
217 * "op count units" where op is + or -, count is a number, and units
218 * is "chars" or "lines".
219 *-------------------------------------------------------------------
220 */
221
222 gotBase:
223 p = endOfBase;
224 while (1) {
225 while (isspace(*p)) {
226 p++;
227 }
228 if (*p == 0) {
229 return TCL_OK;
230 }
231
232 if ((*p == '+') || (*p == '-')) {
233 p = ForwBack(textPtr, p, lineIndexPtr, chPtr);
234 } else {
235 p = StartEnd(textPtr, p, lineIndexPtr, chPtr);
236 }
237 if (p == NULL) {
238 goto error;
239 }
240 }
241
242 error:
243 Tcl_AppendResult(interp, "bad text index \"", string, "\"",
244 (char *) NULL);
245 return TCL_ERROR;
246 }
247 \f
248 /*
249 *----------------------------------------------------------------------
250 *
251 * TkTextPrintIndex --
252 *
253 * Given a line number and a character index, this procedure
254 * generates a string description of the position, which is
255 * suitable for reading in again later.
256 *
257 * Results:
258 * The characters pointed to by string are modified.
259 *
260 * Side effects:
261 * None.
262 *
263 *----------------------------------------------------------------------
264 */
265
266 void
267 TkTextPrintIndex(line, ch, string)
268 int line; /* Line number. */
269 int ch; /* Character position within line. */
270 char *string; /* Place to store the position. Must have
271 * at least POS_CHARS characters. */
272 {
273 sprintf(string, "%d.%d", line+1, ch);
274 }
275 \f
276 /*
277 *----------------------------------------------------------------------
278 *
279 * TkTextRoundIndex --
280 *
281 * Given a line index and a character index, this procedure
282 * adjusts those positions if necessary to correspond to the
283 * nearest actual character within the text.
284 *
285 * Results:
286 * The return value is a pointer to the line structure for
287 * the line of the text's B-tree that contains the indicated
288 * character. In addition, *lineIndexPtr and *chPtr are
289 * modified if necessary to refer to an existing character
290 * in the file.
291 *
292 * Side effects:
293 * None.
294 *
295 *----------------------------------------------------------------------
296 */
297
298
299 TkTextLine *
300 TkTextRoundIndex(textPtr, lineIndexPtr, chPtr)
301 TkText *textPtr; /* Information about text widget. */
302 int *lineIndexPtr; /* Points to initial line index,
303 * which is overwritten with actual
304 * line index. */
305 int *chPtr; /* Points to initial character index,
306 * which is overwritten with actual
307 * character index. */
308 {
309 int line, ch, lastLine;
310 TkTextLine *linePtr;
311
312 line = *lineIndexPtr;
313 ch = *chPtr;
314 if (line < 0) {
315 line = 0;
316 ch = 0;
317 }
318 lastLine = TkBTreeNumLines(textPtr->tree) - 1;
319 if (line > lastLine) {
320 line = lastLine;
321 linePtr = TkBTreeFindLine(textPtr->tree, line);
322 ch = linePtr->numBytes - 1;
323 } else {
324 linePtr = TkBTreeFindLine(textPtr->tree, line);
325 if (ch < 0) {
326 ch = 0;
327 }
328 if (ch >= linePtr->numBytes) {
329 if (line == lastLine) {
330 ch = linePtr->numBytes - 1;
331 } else {
332 line++;
333 linePtr = TkBTreeNextLine(linePtr);
334 ch = 0;
335 }
336 }
337 }
338 *lineIndexPtr = line;
339 *chPtr = ch;
340 return linePtr;
341 }
342 \f
343 /*
344 *----------------------------------------------------------------------
345 *
346 * ForwBack --
347 *
348 * This procedure handles +/- modifiers for indices to adjust
349 * the index forwards or backwards.
350 *
351 * Results:
352 * If the modifier is successfully parsed then the return value
353 * is the address of the first character after the modifier, and
354 * *lineIndexPtr and *chPtr are updated to reflect the modifier.
355 * If there is a syntax error in the modifier then NULL is returned.
356 *
357 * Side effects:
358 * None.
359 *
360 *----------------------------------------------------------------------
361 */
362
363 static char *
364 ForwBack(textPtr, string, lineIndexPtr, chPtr)
365 TkText *textPtr; /* Information about widget that index
366 * refers to. */
367 char *string; /* String to parse for additional info
368 * about modifier (count and units).
369 * Points to "+" or "-" that starts
370 * modifier. */
371 int *lineIndexPtr; /* Points to current line index, which will
372 * be updated to reflect modifier. */
373 int *chPtr; /* Points to current character index, which
374 * will be updated to reflect modifier. */
375 {
376 register char *p;
377 char *end, *units;
378 int count, length, lastLine;
379 TkTextLine *linePtr;
380
381 /*
382 * Get the count (how many units forward or backward).
383 */
384
385 p = string+1;
386 while (isspace(*p)) {
387 p++;
388 }
389 count = strtoul(p, &end, 0);
390 if (end == p) {
391 return NULL;
392 }
393 p = end;
394 while (isspace(*p)) {
395 p++;
396 }
397
398 /*
399 * Find the end of this modifier (next space or + or - character),
400 * then parse the unit specifier and update the position
401 * accordingly.
402 */
403
404 units = p;
405 while ((*p != 0) && !isspace(*p) && (*p != '+') && (*p != '-')) {
406 p++;
407 }
408 length = p - units;
409 if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
410 linePtr = TkTextRoundIndex(textPtr, lineIndexPtr, chPtr);
411 if (*string == '+') {
412 ForwardChars(textPtr, linePtr, lineIndexPtr, chPtr, count);
413 } else {
414 BackwardChars(textPtr, linePtr, lineIndexPtr, chPtr, count);
415 }
416 } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
417 if (*string == '+') {
418 *lineIndexPtr += count;
419 lastLine = TkBTreeNumLines(textPtr->tree) - 1;
420 if (*lineIndexPtr > lastLine) {
421 *lineIndexPtr = lastLine;
422 }
423 } else {
424 *lineIndexPtr -= count;
425 if (*lineIndexPtr < 0) {
426 *lineIndexPtr = 0;
427 }
428 }
429 linePtr = TkBTreeFindLine(textPtr->tree, *lineIndexPtr);
430 if (*chPtr >= linePtr->numBytes) {
431 *chPtr = linePtr->numBytes - 1;
432 }
433 if (*chPtr < 0) {
434 *chPtr = 0;
435 }
436 } else {
437 return NULL;
438 }
439 return p;
440 }
441 \f
442 /*
443 *----------------------------------------------------------------------
444 *
445 * ForwardChars --
446 *
447 * Given a position in a text widget, this procedure computes
448 * a new position that is "count" characters ahead of the given
449 * position.
450 *
451 * Results:
452 * *LineIndexPtr and *chPtr are overwritten with new values
453 * corresponding to the new position.
454 *
455 * Side effects:
456 * None.
457 *
458 *----------------------------------------------------------------------
459 */
460
461 /* ARGSUSED */
462 static void
463 ForwardChars(textPtr, linePtr, lineIndexPtr, chPtr, count)
464 TkText *textPtr; /* Information about text widget. */
465 register TkTextLine *linePtr; /* Text line corresponding to
466 * *lineIndexPtr. */
467 int *lineIndexPtr; /* Points to initial line index,
468 * which is overwritten with final
469 * line index. */
470 int *chPtr; /* Points to initial character index,
471 * which is overwritten with final
472 * character index. */
473 int count; /* How many characters forward to
474 * move. Must not be negative. */
475 {
476 TkTextLine *nextPtr;
477 int bytesInLine;
478
479 while (count > 0) {
480 bytesInLine = linePtr->numBytes - *chPtr;
481 if (bytesInLine > count) {
482 *chPtr += count;
483 return;
484 }
485 nextPtr = TkBTreeNextLine(linePtr);
486 if (nextPtr == NULL) {
487 *chPtr = linePtr->numBytes - 1;
488 return;
489 }
490 *chPtr = 0;
491 *lineIndexPtr += 1;
492 linePtr = nextPtr;
493 count -= bytesInLine;
494 }
495 }
496 \f
497 /*
498 *----------------------------------------------------------------------
499 *
500 * BackwardChars --
501 *
502 * Given a position in a text widget, this procedure computes
503 * a new position that is "count" characters earlier than the given
504 * position.
505 *
506 * Results:
507 * *LineIndexPtr and *chPtr are overwritten with new values
508 * corresponding to the new position.
509 *
510 * Side effects:
511 * None.
512 *
513 *----------------------------------------------------------------------
514 */
515
516 static void
517 BackwardChars(textPtr, linePtr, lineIndexPtr, chPtr, count)
518 TkText *textPtr; /* Information about text widget. */
519 register TkTextLine *linePtr; /* Text line corresponding to
520 * *lineIndexPtr. */
521 int *lineIndexPtr; /* Points to initial line index,
522 * which is overwritten with final
523 * line index. */
524 int *chPtr; /* Points to initial character index,
525 * which is overwritten with final
526 * character index. */
527 int count; /* How many characters backward to
528 * move. Must not be negative. */
529 {
530 int bytesInLine;
531
532 while (count > 0) {
533 bytesInLine = *chPtr;
534 if (bytesInLine >= count) {
535 *chPtr -= count;
536 return;
537 }
538 if (*lineIndexPtr <= 0) {
539 *chPtr = 0;
540 return;
541 }
542 *lineIndexPtr -= 1;
543 linePtr = TkBTreeFindLine(textPtr->tree, *lineIndexPtr);
544 count -= bytesInLine;
545 *chPtr = linePtr->numBytes;
546 }
547 }
548 \f
549 /*
550 *----------------------------------------------------------------------
551 *
552 * StartEnd --
553 *
554 * This procedure handles modifiers like "wordstart" and "lineend"
555 * to adjust indices forwards or backwards.
556 *
557 * Results:
558 * If the modifier is successfully parsed then the return value
559 * is the address of the first character after the modifier, and
560 * *lineIndexPtr and *chPtr are updated to reflect the modifier.
561 * If there is a syntax error in the modifier then NULL is returned.
562 *
563 * Side effects:
564 * None.
565 *
566 *----------------------------------------------------------------------
567 */
568
569 static char *
570 StartEnd(textPtr, string, lineIndexPtr, chPtr)
571 TkText *textPtr; /* Information about widget that index
572 * refers to. */
573 char *string; /* String to parse for additional info
574 * about modifier (count and units).
575 * Points to first character of modifer
576 * word. */
577 int *lineIndexPtr; /* Points to current line index, which will
578 * be updated to reflect modifier. */
579 int *chPtr; /* Points to current character index, which
580 * will be updated to reflect modifier. */
581 {
582 char *p, c;
583 int length;
584 register TkTextLine *linePtr;
585
586 /*
587 * Find the end of the modifier word.
588 */
589
590 for (p = string; isalnum(*p); p++) {
591 /* Empty loop body. */
592 }
593 length = p-string;
594 linePtr = TkTextRoundIndex(textPtr, lineIndexPtr, chPtr);
595 if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
596 && (length >= 5)) {
597 *chPtr = linePtr->numBytes - 1;
598 } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
599 && (length >= 5)) {
600 *chPtr = 0;
601 } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
602 && (length >= 5)) {
603 c = linePtr->bytes[*chPtr];
604 if (!isalnum(c) && (c != '_')) {
605 if (*chPtr >= (linePtr->numBytes - 1)) {
606 /*
607 * End of line: go to start of next line unless this is the
608 * last line in the text.
609 */
610
611 if (TkBTreeNextLine(linePtr) != NULL) {
612 *lineIndexPtr += 1;
613 *chPtr = 0;
614 }
615 } else {
616 *chPtr += 1;
617 }
618 } else {
619 do {
620 *chPtr += 1;
621 c = linePtr->bytes[*chPtr];
622 } while (isalnum(c) || (c == '_'));
623 }
624 } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
625 && (length >= 5)) {
626 c = linePtr->bytes[*chPtr];
627 if (isalnum(c) || (c == '_')) {
628 while (*chPtr > 0) {
629 c = linePtr->bytes[(*chPtr) - 1];
630 if (!isalnum(c) && (c != '_')) {
631 break;
632 }
633 *chPtr -= 1;
634 }
635 }
636 } else {
637 return NULL;
638 }
639 return p;
640 }
Impressum, Datenschutz