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