2 * Copyright 2000, International Business Machines Corporation and others.
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
11 * Implementation of the gator circular buffer package for its scrollable
14 *------------------------------------------------------------------------*/
16 #include <afsconfig.h>
17 #include <afs/param.h>
20 #include "gtxtextcb.h" /*Module interface */
21 #include <stdio.h> /*Standard I/O stuff */
26 static int gator_textcb_debug
; /*Is debugging output turned on? */
28 /*------------------------------------------------------------------------
32 * Initialize the text circular buffer package.
35 * a_debug : Should debugging output be turned on?
39 * Error value otherwise.
42 * MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
45 * Just remembers if debugging output should be generated.
46 *------------------------------------------------------------------------*/
49 gator_textcb_Init(int a_debug
)
50 { /*gator_textcb_Init */
52 static int initd
; /*Have we been called already? */
53 static char rn
[] = "gator_textcb_Init"; /*Routine name */
57 "[%s] Initialization routine called multiple times!!\n", rn
);
62 gator_textcb_debug
= a_debug
;
65 } /*gator_textcb_Init */
67 /*------------------------------------------------------------------------
71 * Create a new circular buffer.
74 * int a_maxEntriesStored : How many entries should it have?
75 * int a_maxCharsPerEntry : Max chars in each entry.
78 * Ptr to the fully-initialized circular buffer hdr if successful,
79 * Null pointer otherwise.
82 * Makes sure the lock structure is properly initialized.
85 * As advertised; space is allocated for the circ buff.
86 *------------------------------------------------------------------------*/
88 struct gator_textcb_hdr
*
89 gator_textcb_Create(int a_maxEntriesStored
, int a_maxCharsPerEntry
)
90 { /*gator_textcb_Create */
92 static char rn
[] = "gator_textcb_Create"; /*Routine name */
93 char *newBuff
; /*Ptr to new text buffer */
94 struct gator_textcb_entry
*newEntries
; /*Ptr to new text entries */
95 struct gator_textcb_hdr
*newHdr
; /*Ptr to new text hdr */
96 int bytesToAllocate
; /*Num bytes to allocate */
97 int numBuffBytes
; /*Num bytes in text buffer */
98 int curr_ent_num
; /*Current entry number */
99 struct gator_textcb_entry
*curr_ent
; /*Ptr to current entry */
100 char *curr_buff
; /*Ptr to current buff pos */
101 int curr_inv
; /*Current inversion idx */
102 char *blankLine
; /*Ptr to blank line */
103 int i
; /*Loop variable */
106 * Start off by allocating the text buffer itself. Don't forget we
107 * need to allocate one more character per line, to make sure we can
108 * always null-terminate them. We also need to allocate the blank
111 numBuffBytes
= bytesToAllocate
=
112 a_maxEntriesStored
* (a_maxCharsPerEntry
+ 1);
113 if (gator_textcb_debug
)
114 fprintf(stderr
, "[%s] Allocating %d bytes for the text buffer\n", rn
,
116 newBuff
= (char *)malloc(bytesToAllocate
);
117 if (newBuff
== NULL
) {
119 "[%s] Can't allocate %d bytes for text buffer; errno is %d\n",
120 rn
, bytesToAllocate
, errno
);
121 return ((struct gator_textcb_hdr
*)0);
122 } else if (gator_textcb_debug
)
123 fprintf(stderr
, "[%s] Text buffer allocated at %p\n", rn
, newBuff
);
124 blankLine
= (char *)malloc(a_maxCharsPerEntry
+ 1);
125 if (blankLine
== NULL
) {
127 "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n",
128 rn
, a_maxCharsPerEntry
+ 1, errno
);
130 return ((struct gator_textcb_hdr
*)0);
134 * Next, allocate the entry array.
136 bytesToAllocate
= a_maxEntriesStored
* sizeof(struct gator_textcb_entry
);
137 if (gator_textcb_debug
)
139 "[%s] Allocating %d bytes for the %d text entry array items\n",
140 rn
, bytesToAllocate
, a_maxEntriesStored
);
141 newEntries
= (struct gator_textcb_entry
*)malloc(bytesToAllocate
);
142 if (newEntries
== (struct gator_textcb_entry
*)0) {
144 "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n",
145 rn
, bytesToAllocate
, a_maxEntriesStored
, errno
);
148 return ((struct gator_textcb_hdr
*)0);
149 } else if (gator_textcb_debug
)
150 fprintf(stderr
, "[%s] Text buffer entry array allocated at %p\n",
154 * Finish off by allocating the text circular buffer header.
156 bytesToAllocate
= sizeof(struct gator_textcb_hdr
);
157 if (gator_textcb_debug
)
159 "[%s] Allocating %d bytes for the text circular buffer header\n",
160 rn
, bytesToAllocate
);
161 newHdr
= (struct gator_textcb_hdr
*)malloc(bytesToAllocate
);
162 if (newHdr
== (struct gator_textcb_hdr
*)0) {
164 "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n",
165 rn
, bytesToAllocate
, errno
);
169 return ((struct gator_textcb_hdr
*)0);
170 } else if (gator_textcb_debug
)
172 "[%s] Text circular buffer header allocated at %p\n", rn
,
176 * Now, just initialize all the pieces and plug them in.
178 if (gator_textcb_debug
)
179 fprintf(stderr
, "[%s] Zeroing %d bytes in text buffer at %p\n", rn
,
180 numBuffBytes
, newBuff
);
181 memset(newBuff
, 0, numBuffBytes
);
183 if (gator_textcb_debug
)
184 fprintf(stderr
, "[%s] Initializing blank line buffer at %p\n", rn
,
186 for (i
= 0; i
< a_maxCharsPerEntry
; i
++)
187 *(blankLine
+ i
) = ' ';
188 *(blankLine
+ a_maxCharsPerEntry
) = '\0';
191 * Initialize each buffer entry.
193 for (curr_ent_num
= 0, curr_buff
= newBuff
, curr_ent
= newEntries
;
194 curr_ent_num
< a_maxEntriesStored
;
195 curr_ent
++, curr_ent_num
++, curr_buff
+= (a_maxCharsPerEntry
+ 1)) {
196 if (gator_textcb_debug
)
198 "[%s] Initializing buffer entry %d; its text buffer address is %p\n",
199 rn
, curr_ent_num
, curr_buff
);
201 curr_ent
->highlight
= 0;
202 curr_ent
->numInversions
= 0;
203 curr_ent
->charsUsed
= 0;
204 curr_ent
->textp
= curr_buff
;
205 memcpy(curr_ent
->textp
, blankLine
, a_maxCharsPerEntry
+ 1);
206 for (curr_inv
= 0; curr_inv
< GATOR_TEXTCB_MAXINVERSIONS
; curr_inv
++)
207 curr_ent
->inversion
[curr_inv
] = 0;
209 } /*Init each buffer entry */
211 if (gator_textcb_debug
)
212 fprintf(stderr
, "[%s] Filling in circ buff header at %p\n", rn
,
214 Lock_Init(&(newHdr
->cbLock
));
215 newHdr
->maxEntriesStored
= a_maxEntriesStored
;
216 newHdr
->maxCharsPerEntry
= a_maxCharsPerEntry
;
218 newHdr
->currEntIdx
= 0;
219 newHdr
->oldestEnt
= 0;
220 newHdr
->oldestEntIdx
= 0;
221 newHdr
->entry
= newEntries
;
222 newHdr
->blankLine
= blankLine
;
225 * Finally, return the location of the new header.
229 } /*gator_textcb_Create */
231 /*------------------------------------------------------------------------
235 * Move down to the next circular buffer entry.
238 * struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
241 * Ptr to the newest current entry.
244 * Nothing interesting.
248 *------------------------------------------------------------------------*/
250 static struct gator_textcb_entry
*
251 bumpEntry(struct gator_textcb_hdr
*a_cbhdr
)
255 static char rn
[] = "bumpEntry"; /*Routine name */
256 struct gator_textcb_entry
*curr_ent
; /*Ptr to current entry */
257 int inv
; /*Inversion number */
260 * Bump the total number of writes, and don't forget to advance
261 * the oldest entry, if appropriate.
263 if (gator_textcb_debug
)
265 "[%s]: Bumping entry for circular buffer at %p; current values: currEnt=%d (idx %d), oldestEnt=%d (idx %d), maxEntriesStored=%d\n",
266 rn
, a_cbhdr
, a_cbhdr
->currEnt
, a_cbhdr
->currEntIdx
,
267 a_cbhdr
->oldestEnt
, a_cbhdr
->oldestEntIdx
,
268 a_cbhdr
->maxEntriesStored
);
271 if (++(a_cbhdr
->currEntIdx
) >= a_cbhdr
->maxEntriesStored
)
272 a_cbhdr
->currEntIdx
= 0;
273 curr_ent
= a_cbhdr
->entry
+ a_cbhdr
->currEntIdx
;
275 if (gator_textcb_debug
)
276 fprintf(stderr
, "[%s] Zeroing entry %d (idx %d) at %p\n", rn
,
277 a_cbhdr
->currEnt
, a_cbhdr
->currEntIdx
, curr_ent
);
279 curr_ent
->ID
= a_cbhdr
->currEnt
;
280 curr_ent
->highlight
= 0;
281 curr_ent
->numInversions
= 0;
282 curr_ent
->charsUsed
= 0;
284 * Copy over a blank line into the one we're initializing. We
285 * copy over the trailing null, too.
287 memcpy(curr_ent
->textp
, a_cbhdr
->blankLine
,
288 a_cbhdr
->maxCharsPerEntry
+ 1);
289 for (inv
= 0; inv
< GATOR_TEXTCB_MAXINVERSIONS
; inv
++)
290 curr_ent
->inversion
[inv
] = 0;
293 * If we've already stated circulating in the buffer, remember to
294 * bump the oldest entry info too.
296 if (a_cbhdr
->currEnt
>= a_cbhdr
->maxEntriesStored
) {
297 if (gator_textcb_debug
)
299 "[%s]: Advancing oldest entry number & index; was entry %d, index %d, now ",
300 rn
, a_cbhdr
->oldestEnt
, a_cbhdr
->oldestEntIdx
);
301 a_cbhdr
->oldestEnt
++;
302 if (++(a_cbhdr
->oldestEntIdx
) >= a_cbhdr
->maxEntriesStored
)
303 a_cbhdr
->oldestEntIdx
= 0;
304 if (gator_textcb_debug
)
305 fprintf(stderr
, "entry %d, index %d\n", a_cbhdr
->oldestEnt
,
306 a_cbhdr
->oldestEntIdx
);
309 /*Bump oldest entry info */
311 * Finally, return the address of the newest current entry.
317 /*------------------------------------------------------------------------
321 * Write the given string to the text circular buffer. Line
322 * breaks are caused either by overflowing the current text
323 * line or via explicit '\n's.
326 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
327 * char *a_textToWrite : Ptr to text to insert.
328 * int a_numChars : Number of chars to write.
329 * int a_highlight : Use highlighting?
330 * int a_skip; : Force a skip to the next line?
333 * Zero if successful,
334 * Error value otherwise.
337 * Circular buffer is consistent upon entry, namely the first and
338 * last entry pointers are legal.
342 *------------------------------------------------------------------------*/
345 gator_textcb_Write(struct gator_textcb_hdr
*a_cbhdr
, char *a_textToWrite
,
346 int a_numChars
, int a_highlight
, int a_skip
)
347 { /*gator_textcb_Write */
349 static char rn
[] = "gator_textcb_Write"; /*Routine name */
350 struct gator_textcb_entry
*curr_ent
; /*Ptr to current text entry */
351 int curr_ent_idx
; /*Index of current entry */
352 int max_chars
; /*Max chars per entry */
353 int chars_to_copy
; /*Num chars to copy in */
354 int effective_highlight
; /*Tmp highlight value */
355 char *dest
; /*Destination of char copy */
358 * Make sure we haven't been passed a bogus buffer, and lock it
361 if (a_cbhdr
== (struct gator_textcb_hdr
*)0) {
363 "[%s]: Null pointer passed in for circ buff header!! Aborting write operation.\n",
367 ObtainWriteLock(&(a_cbhdr
->cbLock
));
369 curr_ent_idx
= a_cbhdr
->currEntIdx
;
370 curr_ent
= (a_cbhdr
->entry
) + curr_ent_idx
;
371 max_chars
= a_cbhdr
->maxCharsPerEntry
;
372 effective_highlight
= curr_ent
->highlight
;
373 if (curr_ent
->numInversions
% 2)
374 effective_highlight
= (effective_highlight
? 0 : 1);
375 if (gator_textcb_debug
)
377 "[%s]: Current entry: %d (at index %d, keeping %d max), effective highlight: %d, located at %p\n",
378 rn
, a_cbhdr
->currEnt
, curr_ent_idx
, a_cbhdr
->maxEntriesStored
,
379 effective_highlight
, curr_ent
);
381 while (a_numChars
> 0) {
383 * There are still characters to stuff into our circular buffer.
385 if (gator_textcb_debug
)
387 "[%s]: Top of write loop: %d char(s) left to write.\n",
390 if (curr_ent
->charsUsed
>= max_chars
) {
392 * Bump the entry in the given circular buffer.
394 if (gator_textcb_debug
)
396 "[%s]: Entry %d at index %d full, advancing to next one.\n",
397 rn
, a_cbhdr
->currEnt
, curr_ent_idx
);
398 curr_ent
= bumpEntry(a_cbhdr
);
399 curr_ent_idx
= a_cbhdr
->currEntIdx
;
400 if (gator_textcb_debug
)
402 "[%s] New CB entry info: currEnt=%d (idx %d), oldestEnt=%d (idx %d), curr entry ptr is %p\n",
403 rn
, a_cbhdr
->currEnt
, a_cbhdr
->currEntIdx
,
404 a_cbhdr
->oldestEnt
, a_cbhdr
->oldestEntIdx
, curr_ent
);
407 /*Bump current entry */
409 * At this point, the current entry has room for at least one more
410 * character, and we have at least one more character to write.
411 * Insert as much from the user text as possible.
413 chars_to_copy
= max_chars
- curr_ent
->charsUsed
;
414 if (a_numChars
< chars_to_copy
)
415 chars_to_copy
= a_numChars
;
416 dest
= curr_ent
->textp
+ curr_ent
->charsUsed
;
417 if (gator_textcb_debug
)
419 "[%s]: Copying %d char(s) into current entry at %p (entry buffer starts at %p)\n",
420 rn
, chars_to_copy
, dest
, curr_ent
->textp
);
423 * Handle highlighting and inversions.
425 if (curr_ent
->charsUsed
== 0) {
427 * No chars yet, so this sets the highlight field.
429 effective_highlight
= curr_ent
->highlight
= a_highlight
;
430 } else if (effective_highlight
!= a_highlight
) {
432 * We need a new inversion, if there's room.
434 if (gator_textcb_debug
)
436 "[%s]: Highlight mismatch, recording inversion at char loc %d\n",
437 rn
, curr_ent
->charsUsed
);
438 if (curr_ent
->numInversions
< GATOR_TEXTCB_MAXINVERSIONS
) {
439 effective_highlight
= a_highlight
;
440 curr_ent
->inversion
[curr_ent
->numInversions
] =
442 curr_ent
->numInversions
++;
443 } else if (gator_textcb_debug
)
444 fprintf(stderr
, "[%s]: Out of inversions!!\n", rn
);
447 /*Handle inversion */
449 * Move the string chunk into its place in the buffer, bump the
450 * number of chars used in the current entry.
452 strncpy(dest
, a_textToWrite
, chars_to_copy
);
453 curr_ent
->charsUsed
+= chars_to_copy
;
454 a_textToWrite
+= chars_to_copy
;
455 a_numChars
-= chars_to_copy
;
457 } /*while (a_numChars > 0) */
460 * All characters have been copied in. Handle the case where we've
461 * been asked to skip to the next entry, even if there's still room
462 * in the current one.
465 if (gator_textcb_debug
)
466 fprintf(stderr
, "[%s] Handling request to skip to next entry\n",
468 if (curr_ent
->charsUsed
> 0)
469 curr_ent
= bumpEntry(a_cbhdr
);
470 else if (gator_textcb_debug
)
472 "[%s] Not skipping, we're already on a fresh entry\n",
476 /*Skip to the next entry */
478 * We can now unlock the CB and return successfully.
480 ReleaseWriteLock(&(a_cbhdr
->cbLock
));
483 } /*gator_textcb_Write */
485 /*------------------------------------------------------------------------
486 * gator_textcb_BlankLine
489 * Write out some number of blank lines to the given circular
493 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
494 * int *a_numBlanks : Num. blank lines to write.
497 * Zero if successful,
498 * Error value otherwise.
501 * Circular buffer is consistent upon entry, namely the first and
502 * last entry pointers are legal.
506 *------------------------------------------------------------------------*/
509 gator_textcb_BlankLine(struct gator_textcb_hdr
*a_cbhdr
,
511 { /*gator_textcb_BlankLine */
513 static char rn
[] = "gator_textcb_BlankLine"; /*Routine name */
515 if (gator_textcb_debug
)
516 fprintf(stderr
, "[%s] Putting out %d blank lines to the CB at %p\n",
517 rn
, a_numBlanks
, a_cbhdr
);
519 if (a_cbhdr
== (struct gator_textcb_hdr
*)0) {
520 if (gator_textcb_debug
)
521 fprintf(stderr
, "[%s] Null pointer passed for CB hdr!!\n", rn
);
525 while (a_numBlanks
> 0) {
527 * The bumpEntry routine returns a struct gator_textcb_entry
528 * pointer, but we don't need it here, so we don't assign it.
535 * Return happily and successfully.
539 } /*gator_textcb_Write */
541 /*------------------------------------------------------------------------
542 * gator_textcb_Delete
545 * Delete the storage used by the given circular buffer, including
549 * struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
550 * circ buffer to reap.
553 * Zero if successful,
554 * Error value otherwise.
557 * We write-lock the buffer before deleting it, which is slightly
558 * silly, since it will stop existing after we're done. At least
559 * we'll make sure nobody is actively writing to it while it's
564 *------------------------------------------------------------------------*/
567 gator_textcb_Delete(struct gator_textcb_hdr
*a_cbhdr
)
568 { /*gator_textcb_Delete */
570 static char rn
[] = "gator_textcb_Delete"; /*Routine name */
572 if (gator_textcb_debug
)
573 fprintf(stderr
, "[%s]: Deleting text circular buffer at %p\n", rn
,
575 ObtainWriteLock(&(a_cbhdr
->cbLock
));
578 * The beginning of the text buffer itself is pointed to by the
581 if (gator_textcb_debug
)
583 "[%s]: Freeing text buffer proper at %p (%d bytes)\n", rn
,
584 a_cbhdr
->entry
[0].textp
,
585 (a_cbhdr
->maxEntriesStored
* a_cbhdr
->maxCharsPerEntry
));
586 free(a_cbhdr
->entry
[0].textp
);
587 a_cbhdr
->entry
[0].textp
= NULL
;
589 if (gator_textcb_debug
)
590 fprintf(stderr
, "[%s]: Freeing text entry array at %p (%" AFS_SIZET_FMT
" bytes)\n",
592 (a_cbhdr
->maxEntriesStored
*
593 sizeof(struct gator_textcb_entry
)));
594 free(a_cbhdr
->entry
);
595 a_cbhdr
->entry
= (struct gator_textcb_entry
*)0;
596 free(a_cbhdr
->blankLine
);
597 a_cbhdr
->blankLine
= NULL
;
600 * Release the write lock on it, then free the header itself.
602 ReleaseWriteLock(&(a_cbhdr
->cbLock
));
603 if (gator_textcb_debug
)
604 fprintf(stderr
, "[%s] Freeing cicular buffer header at %p\n", rn
,
609 } /*gator_textcb_Delete */