Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / gtx / textcb.c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10 /*
11 * Implementation of the gator circular buffer package for its scrollable
12 * text object.
13 *
14 *------------------------------------------------------------------------*/
15
16 #include <afsconfig.h>
17 #include <afs/param.h>
18
19 #include <roken.h>
20
21 #include "gtxtextcb.h" /*Module interface */
22
23 static int gator_textcb_debug; /*Is debugging output turned on? */
24
25 /*------------------------------------------------------------------------
26 * gator_textcb_Init
27 *
28 * Description:
29 * Initialize the text circular buffer package.
30 *
31 * Arguments:
32 * a_debug : Should debugging output be turned on?
33 *
34 * Returns:
35 * Zero if successful,
36 * Error value otherwise.
37 *
38 * Environment:
39 * MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
40 *
41 * Side Effects:
42 * Just remembers if debugging output should be generated.
43 *------------------------------------------------------------------------*/
44
45 int
46 gator_textcb_Init(int a_debug)
47 { /*gator_textcb_Init */
48
49 static int initd; /*Have we been called already? */
50 static char rn[] = "gator_textcb_Init"; /*Routine name */
51
52 if (initd) {
53 fprintf(stderr,
54 "[%s] Initialization routine called multiple times!!\n", rn);
55 return (0);
56 } else
57 initd = 1;
58
59 gator_textcb_debug = a_debug;
60 return (0);
61
62 } /*gator_textcb_Init */
63
64 /*------------------------------------------------------------------------
65 * gator_textcb_Create
66 *
67 * Description:
68 * Create a new circular buffer.
69 *
70 * Arguments:
71 * int a_maxEntriesStored : How many entries should it have?
72 * int a_maxCharsPerEntry : Max chars in each entry.
73 *
74 * Returns:
75 * Ptr to the fully-initialized circular buffer hdr if successful,
76 * Null pointer otherwise.
77 *
78 * Environment:
79 * Makes sure the lock structure is properly initialized.
80 *
81 * Side Effects:
82 * As advertised; space is allocated for the circ buff.
83 *------------------------------------------------------------------------*/
84
85 struct gator_textcb_hdr *
86 gator_textcb_Create(int a_maxEntriesStored, int a_maxCharsPerEntry)
87 { /*gator_textcb_Create */
88
89 static char rn[] = "gator_textcb_Create"; /*Routine name */
90 char *newBuff; /*Ptr to new text buffer */
91 struct gator_textcb_entry *newEntries; /*Ptr to new text entries */
92 struct gator_textcb_hdr *newHdr; /*Ptr to new text hdr */
93 int bytesToAllocate; /*Num bytes to allocate */
94 int curr_ent_num; /*Current entry number */
95 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
96 char *curr_buff; /*Ptr to current buff pos */
97 int curr_inv; /*Current inversion idx */
98 char *blankLine; /*Ptr to blank line */
99 int i; /*Loop variable */
100
101 /*
102 * Start off by allocating the text buffer itself. Don't forget we
103 * need to allocate one more character per line, to make sure we can
104 * always null-terminate them. We also need to allocate the blank
105 * line buffer.
106 */
107 bytesToAllocate =
108 a_maxEntriesStored * (a_maxCharsPerEntry + 1);
109 if (gator_textcb_debug)
110 fprintf(stderr, "[%s] Allocating %d bytes for the text buffer\n", rn,
111 bytesToAllocate);
112 newBuff = calloc(1, bytesToAllocate);
113 if (newBuff == NULL) {
114 fprintf(stderr,
115 "[%s] Can't allocate %d bytes for text buffer; errno is %d\n",
116 rn, bytesToAllocate, errno);
117 return ((struct gator_textcb_hdr *)0);
118 } else if (gator_textcb_debug)
119 fprintf(stderr, "[%s] Text buffer allocated at %p\n", rn, newBuff);
120 blankLine = malloc(a_maxCharsPerEntry + 1);
121 if (blankLine == NULL) {
122 fprintf(stderr,
123 "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n",
124 rn, a_maxCharsPerEntry + 1, errno);
125 free(newBuff);
126 return ((struct gator_textcb_hdr *)0);
127 }
128
129 /*
130 * Next, allocate the entry array.
131 */
132 bytesToAllocate = a_maxEntriesStored * sizeof(struct gator_textcb_entry);
133 if (gator_textcb_debug)
134 fprintf(stderr,
135 "[%s] Allocating %d bytes for the %d text entry array items\n",
136 rn, bytesToAllocate, a_maxEntriesStored);
137 newEntries = malloc(bytesToAllocate);
138 if (newEntries == (struct gator_textcb_entry *)0) {
139 fprintf(stderr,
140 "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n",
141 rn, bytesToAllocate, a_maxEntriesStored, errno);
142 free(newBuff);
143 free(blankLine);
144 return ((struct gator_textcb_hdr *)0);
145 } else if (gator_textcb_debug)
146 fprintf(stderr, "[%s] Text buffer entry array allocated at %p\n",
147 rn, newEntries);
148
149 /*
150 * Finish off by allocating the text circular buffer header.
151 */
152 bytesToAllocate = sizeof(struct gator_textcb_hdr);
153 if (gator_textcb_debug)
154 fprintf(stderr,
155 "[%s] Allocating %d bytes for the text circular buffer header\n",
156 rn, bytesToAllocate);
157 newHdr = malloc(bytesToAllocate);
158 if (newHdr == (struct gator_textcb_hdr *)0) {
159 fprintf(stderr,
160 "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n",
161 rn, bytesToAllocate, errno);
162 free(newBuff);
163 free(blankLine);
164 free(newEntries);
165 return ((struct gator_textcb_hdr *)0);
166 } else if (gator_textcb_debug)
167 fprintf(stderr,
168 "[%s] Text circular buffer header allocated at %p\n", rn,
169 newHdr);
170
171 /*
172 * Now, just initialize all the pieces and plug them in.
173 */
174 if (gator_textcb_debug)
175 fprintf(stderr, "[%s] Initializing blank line buffer at %p\n", rn,
176 blankLine);
177 for (i = 0; i < a_maxCharsPerEntry; i++)
178 *(blankLine + i) = ' ';
179 *(blankLine + a_maxCharsPerEntry) = '\0';
180
181 /*
182 * Initialize each buffer entry.
183 */
184 for (curr_ent_num = 0, curr_buff = newBuff, curr_ent = newEntries;
185 curr_ent_num < a_maxEntriesStored;
186 curr_ent++, curr_ent_num++, curr_buff += (a_maxCharsPerEntry + 1)) {
187 if (gator_textcb_debug)
188 fprintf(stderr,
189 "[%s] Initializing buffer entry %d; its text buffer address is %p\n",
190 rn, curr_ent_num, curr_buff);
191 curr_ent->ID = 0;
192 curr_ent->highlight = 0;
193 curr_ent->numInversions = 0;
194 curr_ent->charsUsed = 0;
195 curr_ent->textp = curr_buff;
196 memcpy(curr_ent->textp, blankLine, a_maxCharsPerEntry + 1);
197 for (curr_inv = 0; curr_inv < GATOR_TEXTCB_MAXINVERSIONS; curr_inv++)
198 curr_ent->inversion[curr_inv] = 0;
199
200 } /*Init each buffer entry */
201
202 if (gator_textcb_debug)
203 fprintf(stderr, "[%s] Filling in circ buff header at %p\n", rn,
204 newHdr);
205 Lock_Init(&(newHdr->cbLock));
206 newHdr->maxEntriesStored = a_maxEntriesStored;
207 newHdr->maxCharsPerEntry = a_maxCharsPerEntry;
208 newHdr->currEnt = 0;
209 newHdr->currEntIdx = 0;
210 newHdr->oldestEnt = 0;
211 newHdr->oldestEntIdx = 0;
212 newHdr->entry = newEntries;
213 newHdr->blankLine = blankLine;
214
215 /*
216 * Finally, return the location of the new header.
217 */
218 return (newHdr);
219
220 } /*gator_textcb_Create */
221
222 /*------------------------------------------------------------------------
223 * bumpCB
224 *
225 * Description:
226 * Move down to the next circular buffer entry.
227 *
228 * Arguments:
229 * struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
230 *
231 * Returns:
232 * Ptr to the newest current entry.
233 *
234 * Environment:
235 * Nothing interesting.
236 *
237 * Side Effects:
238 * As advertised.
239 *------------------------------------------------------------------------*/
240
241 static struct gator_textcb_entry *
242 bumpEntry(struct gator_textcb_hdr *a_cbhdr)
243
244 { /*bumpEntry */
245
246 static char rn[] = "bumpEntry"; /*Routine name */
247 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
248 int inv; /*Inversion number */
249
250 /*
251 * Bump the total number of writes, and don't forget to advance
252 * the oldest entry, if appropriate.
253 */
254 if (gator_textcb_debug)
255 fprintf(stderr,
256 "[%s]: Bumping entry for circular buffer at %p; current values: currEnt=%d (idx %d), oldestEnt=%d (idx %d), maxEntriesStored=%d\n",
257 rn, a_cbhdr, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
258 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx,
259 a_cbhdr->maxEntriesStored);
260
261 a_cbhdr->currEnt++;
262 if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
263 a_cbhdr->currEntIdx = 0;
264 curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
265
266 if (gator_textcb_debug)
267 fprintf(stderr, "[%s] Zeroing entry %d (idx %d) at %p\n", rn,
268 a_cbhdr->currEnt, a_cbhdr->currEntIdx, curr_ent);
269
270 curr_ent->ID = a_cbhdr->currEnt;
271 curr_ent->highlight = 0;
272 curr_ent->numInversions = 0;
273 curr_ent->charsUsed = 0;
274 /*
275 * Copy over a blank line into the one we're initializing. We
276 * copy over the trailing null, too.
277 */
278 memcpy(curr_ent->textp, a_cbhdr->blankLine,
279 a_cbhdr->maxCharsPerEntry + 1);
280 for (inv = 0; inv < GATOR_TEXTCB_MAXINVERSIONS; inv++)
281 curr_ent->inversion[inv] = 0;
282
283 /*
284 * If we've already stated circulating in the buffer, remember to
285 * bump the oldest entry info too.
286 */
287 if (a_cbhdr->currEnt >= a_cbhdr->maxEntriesStored) {
288 if (gator_textcb_debug)
289 fprintf(stderr,
290 "[%s]: Advancing oldest entry number & index; was entry %d, index %d, now ",
291 rn, a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx);
292 a_cbhdr->oldestEnt++;
293 if (++(a_cbhdr->oldestEntIdx) >= a_cbhdr->maxEntriesStored)
294 a_cbhdr->oldestEntIdx = 0;
295 if (gator_textcb_debug)
296 fprintf(stderr, "entry %d, index %d\n", a_cbhdr->oldestEnt,
297 a_cbhdr->oldestEntIdx);
298 }
299
300 /*Bump oldest entry info */
301 /*
302 * Finally, return the address of the newest current entry.
303 */
304 return (curr_ent);
305
306 } /*bumpEntry */
307
308 /*------------------------------------------------------------------------
309 * gator_textcb_Write
310 *
311 * Description:
312 * Write the given string to the text circular buffer. Line
313 * breaks are caused either by overflowing the current text
314 * line or via explicit '\n's.
315 *
316 * Arguments:
317 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
318 * char *a_textToWrite : Ptr to text to insert.
319 * int a_numChars : Number of chars to write.
320 * int a_highlight : Use highlighting?
321 * int a_skip; : Force a skip to the next line?
322 *
323 * Returns:
324 * Zero if successful,
325 * Error value otherwise.
326 *
327 * Environment:
328 * Circular buffer is consistent upon entry, namely the first and
329 * last entry pointers are legal.
330 *
331 * Side Effects:
332 * As advertised.
333 *------------------------------------------------------------------------*/
334
335 int
336 gator_textcb_Write(struct gator_textcb_hdr *a_cbhdr, char *a_textToWrite,
337 int a_numChars, int a_highlight, int a_skip)
338 { /*gator_textcb_Write */
339
340 static char rn[] = "gator_textcb_Write"; /*Routine name */
341 struct gator_textcb_entry *curr_ent; /*Ptr to current text entry */
342 int curr_ent_idx; /*Index of current entry */
343 int max_chars; /*Max chars per entry */
344 int chars_to_copy; /*Num chars to copy in */
345 int effective_highlight; /*Tmp highlight value */
346 char *dest; /*Destination of char copy */
347
348 /*
349 * Make sure we haven't been passed a bogus buffer, and lock it
350 * before we start.
351 */
352 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
353 fprintf(stderr,
354 "[%s]: Null pointer passed in for circ buff header!! Aborting write operation.\n",
355 rn);
356 return (-1);
357 }
358 ObtainWriteLock(&(a_cbhdr->cbLock));
359
360 curr_ent_idx = a_cbhdr->currEntIdx;
361 curr_ent = (a_cbhdr->entry) + curr_ent_idx;
362 max_chars = a_cbhdr->maxCharsPerEntry;
363 effective_highlight = curr_ent->highlight;
364 if (curr_ent->numInversions % 2)
365 effective_highlight = (effective_highlight ? 0 : 1);
366 if (gator_textcb_debug)
367 fprintf(stderr,
368 "[%s]: Current entry: %d (at index %d, keeping %d max), effective highlight: %d, located at %p\n",
369 rn, a_cbhdr->currEnt, curr_ent_idx, a_cbhdr->maxEntriesStored,
370 effective_highlight, curr_ent);
371
372 while (a_numChars > 0) {
373 /*
374 * There are still characters to stuff into our circular buffer.
375 */
376 if (gator_textcb_debug)
377 fprintf(stderr,
378 "[%s]: Top of write loop: %d char(s) left to write.\n",
379 rn, a_numChars);
380
381 if (curr_ent->charsUsed >= max_chars) {
382 /*
383 * Bump the entry in the given circular buffer.
384 */
385 if (gator_textcb_debug)
386 fprintf(stderr,
387 "[%s]: Entry %d at index %d full, advancing to next one.\n",
388 rn, a_cbhdr->currEnt, curr_ent_idx);
389 curr_ent = bumpEntry(a_cbhdr);
390 curr_ent_idx = a_cbhdr->currEntIdx;
391 if (gator_textcb_debug)
392 fprintf(stderr,
393 "[%s] New CB entry info: currEnt=%d (idx %d), oldestEnt=%d (idx %d), curr entry ptr is %p\n",
394 rn, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
395 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx, curr_ent);
396 }
397
398 /*Bump current entry */
399 /*
400 * At this point, the current entry has room for at least one more
401 * character, and we have at least one more character to write.
402 * Insert as much from the user text as possible.
403 */
404 chars_to_copy = max_chars - curr_ent->charsUsed;
405 if (a_numChars < chars_to_copy)
406 chars_to_copy = a_numChars;
407 dest = curr_ent->textp + curr_ent->charsUsed;
408 if (gator_textcb_debug)
409 fprintf(stderr,
410 "[%s]: Copying %d char(s) into current entry at %p (entry buffer starts at %p)\n",
411 rn, chars_to_copy, dest, curr_ent->textp);
412
413 /*
414 * Handle highlighting and inversions.
415 */
416 if (curr_ent->charsUsed == 0) {
417 /*
418 * No chars yet, so this sets the highlight field.
419 */
420 effective_highlight = curr_ent->highlight = a_highlight;
421 } else if (effective_highlight != a_highlight) {
422 /*
423 * We need a new inversion, if there's room.
424 */
425 if (gator_textcb_debug)
426 fprintf(stderr,
427 "[%s]: Highlight mismatch, recording inversion at char loc %d\n",
428 rn, curr_ent->charsUsed);
429 if (curr_ent->numInversions < GATOR_TEXTCB_MAXINVERSIONS) {
430 effective_highlight = a_highlight;
431 curr_ent->inversion[curr_ent->numInversions] =
432 curr_ent->charsUsed;
433 curr_ent->numInversions++;
434 } else if (gator_textcb_debug)
435 fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
436 }
437
438 /*Handle inversion */
439 /*
440 * Move the string chunk into its place in the buffer, bump the
441 * number of chars used in the current entry.
442 */
443 strncpy(dest, a_textToWrite, chars_to_copy);
444 curr_ent->charsUsed += chars_to_copy;
445 a_textToWrite += chars_to_copy;
446 a_numChars -= chars_to_copy;
447
448 } /*while (a_numChars > 0) */
449
450 /*
451 * All characters have been copied in. Handle the case where we've
452 * been asked to skip to the next entry, even if there's still room
453 * in the current one.
454 */
455 if (a_skip) {
456 if (gator_textcb_debug)
457 fprintf(stderr, "[%s] Handling request to skip to next entry\n",
458 rn);
459 if (curr_ent->charsUsed > 0)
460 curr_ent = bumpEntry(a_cbhdr);
461 else if (gator_textcb_debug)
462 fprintf(stderr,
463 "[%s] Not skipping, we're already on a fresh entry\n",
464 rn);
465 }
466
467 /*Skip to the next entry */
468 /*
469 * We can now unlock the CB and return successfully.
470 */
471 ReleaseWriteLock(&(a_cbhdr->cbLock));
472 return (0);
473
474 } /*gator_textcb_Write */
475
476 /*------------------------------------------------------------------------
477 * gator_textcb_BlankLine
478 *
479 * Description:
480 * Write out some number of blank lines to the given circular
481 * buffer.
482 *
483 * Arguments:
484 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
485 * int *a_numBlanks : Num. blank lines to write.
486 *
487 * Returns:
488 * Zero if successful,
489 * Error value otherwise.
490 *
491 * Environment:
492 * Circular buffer is consistent upon entry, namely the first and
493 * last entry pointers are legal.
494 *
495 * Side Effects:
496 * As advertised.
497 *------------------------------------------------------------------------*/
498
499 int
500 gator_textcb_BlankLine(struct gator_textcb_hdr *a_cbhdr,
501 int a_numBlanks)
502 { /*gator_textcb_BlankLine */
503
504 static char rn[] = "gator_textcb_BlankLine"; /*Routine name */
505
506 if (gator_textcb_debug)
507 fprintf(stderr, "[%s] Putting out %d blank lines to the CB at %p\n",
508 rn, a_numBlanks, a_cbhdr);
509
510 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
511 if (gator_textcb_debug)
512 fprintf(stderr, "[%s] Null pointer passed for CB hdr!!\n", rn);
513 return (-1);
514 }
515
516 while (a_numBlanks > 0) {
517 /*
518 * The bumpEntry routine returns a struct gator_textcb_entry
519 * pointer, but we don't need it here, so we don't assign it.
520 */
521 bumpEntry(a_cbhdr);
522 a_numBlanks--;
523 }
524
525 /*
526 * Return happily and successfully.
527 */
528 return (0);
529
530 } /*gator_textcb_Write */
531
532 /*------------------------------------------------------------------------
533 * gator_textcb_Delete
534 *
535 * Description:
536 * Delete the storage used by the given circular buffer, including
537 * the header itself.
538 *
539 * Arguments:
540 * struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
541 * circ buffer to reap.
542 *
543 * Returns:
544 * Zero if successful,
545 * Error value otherwise.
546 *
547 * Environment:
548 * We write-lock the buffer before deleting it, which is slightly
549 * silly, since it will stop existing after we're done. At least
550 * we'll make sure nobody is actively writing to it while it's
551 * being deleted.
552 *
553 * Side Effects:
554 * As advertised.
555 *------------------------------------------------------------------------*/
556
557 int
558 gator_textcb_Delete(struct gator_textcb_hdr *a_cbhdr)
559 { /*gator_textcb_Delete */
560
561 static char rn[] = "gator_textcb_Delete"; /*Routine name */
562
563 if (gator_textcb_debug)
564 fprintf(stderr, "[%s]: Deleting text circular buffer at %p\n", rn,
565 a_cbhdr);
566 ObtainWriteLock(&(a_cbhdr->cbLock));
567
568 /*
569 * The beginning of the text buffer itself is pointed to by the
570 * first text entry.
571 */
572 if (gator_textcb_debug)
573 fprintf(stderr,
574 "[%s]: Freeing text buffer proper at %p (%d bytes)\n", rn,
575 a_cbhdr->entry[0].textp,
576 (a_cbhdr->maxEntriesStored * a_cbhdr->maxCharsPerEntry));
577 free(a_cbhdr->entry[0].textp);
578 a_cbhdr->entry[0].textp = NULL;
579
580 if (gator_textcb_debug)
581 fprintf(stderr, "[%s]: Freeing text entry array at %p (%" AFS_SIZET_FMT " bytes)\n",
582 rn, a_cbhdr->entry,
583 (a_cbhdr->maxEntriesStored *
584 sizeof(struct gator_textcb_entry)));
585 free(a_cbhdr->entry);
586 a_cbhdr->entry = (struct gator_textcb_entry *)0;
587 free(a_cbhdr->blankLine);
588 a_cbhdr->blankLine = NULL;
589
590 /*
591 * Release the write lock on it, then free the header itself.
592 */
593 ReleaseWriteLock(&(a_cbhdr->cbLock));
594 if (gator_textcb_debug)
595 fprintf(stderr, "[%s] Freeing cicular buffer header at %p\n", rn,
596 a_cbhdr);
597 free(a_cbhdr);
598 return (0);
599
600 } /*gator_textcb_Delete */