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>
21 #include "gtxtextcb.h" /*Module interface */
23 static int gator_textcb_debug
; /*Is debugging output turned on? */
25 /*------------------------------------------------------------------------
29 * Initialize the text circular buffer package.
32 * a_debug : Should debugging output be turned on?
36 * Error value otherwise.
39 * MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
42 * Just remembers if debugging output should be generated.
43 *------------------------------------------------------------------------*/
46 gator_textcb_Init(int a_debug
)
47 { /*gator_textcb_Init */
49 static int initd
; /*Have we been called already? */
50 static char rn
[] = "gator_textcb_Init"; /*Routine name */
54 "[%s] Initialization routine called multiple times!!\n", rn
);
59 gator_textcb_debug
= a_debug
;
62 } /*gator_textcb_Init */
64 /*------------------------------------------------------------------------
68 * Create a new circular buffer.
71 * int a_maxEntriesStored : How many entries should it have?
72 * int a_maxCharsPerEntry : Max chars in each entry.
75 * Ptr to the fully-initialized circular buffer hdr if successful,
76 * Null pointer otherwise.
79 * Makes sure the lock structure is properly initialized.
82 * As advertised; space is allocated for the circ buff.
83 *------------------------------------------------------------------------*/
85 struct gator_textcb_hdr
*
86 gator_textcb_Create(int a_maxEntriesStored
, int a_maxCharsPerEntry
)
87 { /*gator_textcb_Create */
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 */
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
108 a_maxEntriesStored
* (a_maxCharsPerEntry
+ 1);
109 if (gator_textcb_debug
)
110 fprintf(stderr
, "[%s] Allocating %d bytes for the text buffer\n", rn
,
112 newBuff
= calloc(1, bytesToAllocate
);
113 if (newBuff
== NULL
) {
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
) {
123 "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n",
124 rn
, a_maxCharsPerEntry
+ 1, errno
);
126 return ((struct gator_textcb_hdr
*)0);
130 * Next, allocate the entry array.
132 bytesToAllocate
= a_maxEntriesStored
* sizeof(struct gator_textcb_entry
);
133 if (gator_textcb_debug
)
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) {
140 "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n",
141 rn
, bytesToAllocate
, a_maxEntriesStored
, errno
);
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",
150 * Finish off by allocating the text circular buffer header.
152 bytesToAllocate
= sizeof(struct gator_textcb_hdr
);
153 if (gator_textcb_debug
)
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) {
160 "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n",
161 rn
, bytesToAllocate
, errno
);
165 return ((struct gator_textcb_hdr
*)0);
166 } else if (gator_textcb_debug
)
168 "[%s] Text circular buffer header allocated at %p\n", rn
,
172 * Now, just initialize all the pieces and plug them in.
174 if (gator_textcb_debug
)
175 fprintf(stderr
, "[%s] Initializing blank line buffer at %p\n", rn
,
177 for (i
= 0; i
< a_maxCharsPerEntry
; i
++)
178 *(blankLine
+ i
) = ' ';
179 *(blankLine
+ a_maxCharsPerEntry
) = '\0';
182 * Initialize each buffer entry.
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
)
189 "[%s] Initializing buffer entry %d; its text buffer address is %p\n",
190 rn
, curr_ent_num
, curr_buff
);
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;
200 } /*Init each buffer entry */
202 if (gator_textcb_debug
)
203 fprintf(stderr
, "[%s] Filling in circ buff header at %p\n", rn
,
205 Lock_Init(&(newHdr
->cbLock
));
206 newHdr
->maxEntriesStored
= a_maxEntriesStored
;
207 newHdr
->maxCharsPerEntry
= a_maxCharsPerEntry
;
209 newHdr
->currEntIdx
= 0;
210 newHdr
->oldestEnt
= 0;
211 newHdr
->oldestEntIdx
= 0;
212 newHdr
->entry
= newEntries
;
213 newHdr
->blankLine
= blankLine
;
216 * Finally, return the location of the new header.
220 } /*gator_textcb_Create */
222 /*------------------------------------------------------------------------
226 * Move down to the next circular buffer entry.
229 * struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
232 * Ptr to the newest current entry.
235 * Nothing interesting.
239 *------------------------------------------------------------------------*/
241 static struct gator_textcb_entry
*
242 bumpEntry(struct gator_textcb_hdr
*a_cbhdr
)
246 static char rn
[] = "bumpEntry"; /*Routine name */
247 struct gator_textcb_entry
*curr_ent
; /*Ptr to current entry */
248 int inv
; /*Inversion number */
251 * Bump the total number of writes, and don't forget to advance
252 * the oldest entry, if appropriate.
254 if (gator_textcb_debug
)
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
);
262 if (++(a_cbhdr
->currEntIdx
) >= a_cbhdr
->maxEntriesStored
)
263 a_cbhdr
->currEntIdx
= 0;
264 curr_ent
= a_cbhdr
->entry
+ a_cbhdr
->currEntIdx
;
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
);
270 curr_ent
->ID
= a_cbhdr
->currEnt
;
271 curr_ent
->highlight
= 0;
272 curr_ent
->numInversions
= 0;
273 curr_ent
->charsUsed
= 0;
275 * Copy over a blank line into the one we're initializing. We
276 * copy over the trailing null, too.
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;
284 * If we've already stated circulating in the buffer, remember to
285 * bump the oldest entry info too.
287 if (a_cbhdr
->currEnt
>= a_cbhdr
->maxEntriesStored
) {
288 if (gator_textcb_debug
)
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
);
300 /*Bump oldest entry info */
302 * Finally, return the address of the newest current entry.
308 /*------------------------------------------------------------------------
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.
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?
324 * Zero if successful,
325 * Error value otherwise.
328 * Circular buffer is consistent upon entry, namely the first and
329 * last entry pointers are legal.
333 *------------------------------------------------------------------------*/
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 */
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 */
349 * Make sure we haven't been passed a bogus buffer, and lock it
352 if (a_cbhdr
== (struct gator_textcb_hdr
*)0) {
354 "[%s]: Null pointer passed in for circ buff header!! Aborting write operation.\n",
358 ObtainWriteLock(&(a_cbhdr
->cbLock
));
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
)
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
);
372 while (a_numChars
> 0) {
374 * There are still characters to stuff into our circular buffer.
376 if (gator_textcb_debug
)
378 "[%s]: Top of write loop: %d char(s) left to write.\n",
381 if (curr_ent
->charsUsed
>= max_chars
) {
383 * Bump the entry in the given circular buffer.
385 if (gator_textcb_debug
)
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
)
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
);
398 /*Bump current entry */
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.
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
)
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
);
414 * Handle highlighting and inversions.
416 if (curr_ent
->charsUsed
== 0) {
418 * No chars yet, so this sets the highlight field.
420 effective_highlight
= curr_ent
->highlight
= a_highlight
;
421 } else if (effective_highlight
!= a_highlight
) {
423 * We need a new inversion, if there's room.
425 if (gator_textcb_debug
)
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
] =
433 curr_ent
->numInversions
++;
434 } else if (gator_textcb_debug
)
435 fprintf(stderr
, "[%s]: Out of inversions!!\n", rn
);
438 /*Handle inversion */
440 * Move the string chunk into its place in the buffer, bump the
441 * number of chars used in the current entry.
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
;
448 } /*while (a_numChars > 0) */
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.
456 if (gator_textcb_debug
)
457 fprintf(stderr
, "[%s] Handling request to skip to next entry\n",
459 if (curr_ent
->charsUsed
> 0)
460 curr_ent
= bumpEntry(a_cbhdr
);
461 else if (gator_textcb_debug
)
463 "[%s] Not skipping, we're already on a fresh entry\n",
467 /*Skip to the next entry */
469 * We can now unlock the CB and return successfully.
471 ReleaseWriteLock(&(a_cbhdr
->cbLock
));
474 } /*gator_textcb_Write */
476 /*------------------------------------------------------------------------
477 * gator_textcb_BlankLine
480 * Write out some number of blank lines to the given circular
484 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
485 * int *a_numBlanks : Num. blank lines to write.
488 * Zero if successful,
489 * Error value otherwise.
492 * Circular buffer is consistent upon entry, namely the first and
493 * last entry pointers are legal.
497 *------------------------------------------------------------------------*/
500 gator_textcb_BlankLine(struct gator_textcb_hdr
*a_cbhdr
,
502 { /*gator_textcb_BlankLine */
504 static char rn
[] = "gator_textcb_BlankLine"; /*Routine name */
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
);
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
);
516 while (a_numBlanks
> 0) {
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.
526 * Return happily and successfully.
530 } /*gator_textcb_Write */
532 /*------------------------------------------------------------------------
533 * gator_textcb_Delete
536 * Delete the storage used by the given circular buffer, including
540 * struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
541 * circ buffer to reap.
544 * Zero if successful,
545 * Error value otherwise.
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
555 *------------------------------------------------------------------------*/
558 gator_textcb_Delete(struct gator_textcb_hdr
*a_cbhdr
)
559 { /*gator_textcb_Delete */
561 static char rn
[] = "gator_textcb_Delete"; /*Routine name */
563 if (gator_textcb_debug
)
564 fprintf(stderr
, "[%s]: Deleting text circular buffer at %p\n", rn
,
566 ObtainWriteLock(&(a_cbhdr
->cbLock
));
569 * The beginning of the text buffer itself is pointed to by the
572 if (gator_textcb_debug
)
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
;
580 if (gator_textcb_debug
)
581 fprintf(stderr
, "[%s]: Freeing text entry array at %p (%" AFS_SIZET_FMT
" bytes)\n",
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
;
591 * Release the write lock on it, then free the header itself.
593 ReleaseWriteLock(&(a_cbhdr
->cbLock
));
594 if (gator_textcb_debug
)
595 fprintf(stderr
, "[%s] Freeing cicular buffer header at %p\n", rn
,
600 } /*gator_textcb_Delete */