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>
22 #include "gtxtextcb.h" /*Module interface */
23 #include <stdio.h> /*Standard I/O stuff */
35 static int gator_textcb_debug; /*Is debugging output turned on? */
37 /*------------------------------------------------------------------------
41 * Initialize the text circular buffer package.
44 * a_debug : Should debugging output be turned on?
48 * Error value otherwise.
51 * MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
54 * Just remembers if debugging output should be generated.
55 *------------------------------------------------------------------------*/
58 gator_textcb_Init(a_debug)
61 { /*gator_textcb_Init */
63 static int initd; /*Have we been called already? */
64 static char rn[] = "gator_textcb_Init"; /*Routine name */
68 "[%s] Initialization routine called multiple times!!\n", rn);
73 gator_textcb_debug = a_debug;
76 } /*gator_textcb_Init */
78 /*------------------------------------------------------------------------
82 * Create a new circular buffer.
85 * int a_maxEntriesStored : How many entries should it have?
86 * int a_maxCharsPerEntry : Max chars in each entry.
89 * Ptr to the fully-initialized circular buffer hdr if successful,
90 * Null pointer otherwise.
93 * Makes sure the lock structure is properly initialized.
96 * As advertised; space is allocated for the circ buff.
97 *------------------------------------------------------------------------*/
99 struct gator_textcb_hdr *
100 gator_textcb_Create(a_maxEntriesStored, a_maxCharsPerEntry)
101 int a_maxEntriesStored;
102 int a_maxCharsPerEntry;
104 { /*gator_textcb_Create */
106 static char rn[] = "gator_textcb_Create"; /*Routine name */
107 char *newBuff; /*Ptr to new text buffer */
108 struct gator_textcb_entry *newEntries; /*Ptr to new text entries */
109 struct gator_textcb_hdr *newHdr; /*Ptr to new text hdr */
110 int bytesToAllocate; /*Num bytes to allocate */
111 int numBuffBytes; /*Num bytes in text buffer */
112 int curr_ent_num; /*Current entry number */
113 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
114 char *curr_buff; /*Ptr to current buff pos */
115 int curr_inv; /*Current inversion idx */
116 char *blankLine; /*Ptr to blank line */
117 int i; /*Loop variable */
120 * Start off by allocating the text buffer itself. Don't forget we
121 * need to allocate one more character per line, to make sure we can
122 * always null-terminate them. We also need to allocate the blank
125 numBuffBytes = bytesToAllocate =
126 a_maxEntriesStored * (a_maxCharsPerEntry + 1);
127 if (gator_textcb_debug)
128 fprintf(stderr, "[%s] Allocating %d bytes for the text buffer\n", rn,
130 newBuff = (char *)malloc(bytesToAllocate);
131 if (newBuff == NULL) {
133 "[%s] Can't allocate %d bytes for text buffer; errno is %d\n",
134 rn, bytesToAllocate, errno);
135 return ((struct gator_textcb_hdr *)0);
136 } else if (gator_textcb_debug)
137 fprintf(stderr, "[%s] Text buffer allocated at 0x%x\n", rn, newBuff);
138 blankLine = (char *)malloc(a_maxCharsPerEntry + 1);
139 if (blankLine == NULL) {
141 "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n",
142 rn, a_maxCharsPerEntry + 1, errno);
144 return ((struct gator_textcb_hdr *)0);
148 * Next, allocate the entry array.
150 bytesToAllocate = a_maxEntriesStored * sizeof(struct gator_textcb_entry);
151 if (gator_textcb_debug)
153 "[%s] Allocating %d bytes for the %d text entry array items\n",
154 rn, bytesToAllocate, a_maxEntriesStored);
155 newEntries = (struct gator_textcb_entry *)malloc(bytesToAllocate);
156 if (newEntries == (struct gator_textcb_entry *)0) {
158 "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n",
159 rn, bytesToAllocate, a_maxEntriesStored, errno);
162 return ((struct gator_textcb_hdr *)0);
163 } else if (gator_textcb_debug)
164 fprintf(stderr, "[%s] Text buffer entry array allocated at 0x%x\n",
168 * Finish off by allocating the text circular buffer header.
170 bytesToAllocate = sizeof(struct gator_textcb_hdr);
171 if (gator_textcb_debug)
173 "[%s] Allocating %d bytes for the text circular buffer header\n",
174 rn, bytesToAllocate);
175 newHdr = (struct gator_textcb_hdr *)malloc(bytesToAllocate);
176 if (newHdr == (struct gator_textcb_hdr *)0) {
178 "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n",
179 rn, bytesToAllocate, errno);
183 return ((struct gator_textcb_hdr *)0);
184 } else if (gator_textcb_debug)
186 "[%s] Text circular buffer header allocated at 0x%x\n", rn,
190 * Now, just initialize all the pieces and plug them in.
192 if (gator_textcb_debug)
193 fprintf(stderr, "[%s] Zeroing %d bytes in text buffer at 0x%x\n", rn,
194 numBuffBytes, newBuff);
195 memset(newBuff, 0, numBuffBytes);
197 if (gator_textcb_debug)
198 fprintf(stderr, "[%s] Initializing blank line buffer at 0x%x\n", rn,
200 for (i = 0; i < a_maxCharsPerEntry; i++)
201 *(blankLine + i) = ' ';
202 *(blankLine + a_maxCharsPerEntry) = '\0';
205 * Initialize each buffer entry.
207 for (curr_ent_num = 0, curr_buff = newBuff, curr_ent = newEntries;
208 curr_ent_num < a_maxEntriesStored;
209 curr_ent++, curr_ent_num++, curr_buff += (a_maxCharsPerEntry + 1)) {
210 if (gator_textcb_debug)
212 "[%s] Initializing buffer entry %d; its text buffer address is 0x%x\n",
213 rn, curr_ent_num, curr_buff);
215 curr_ent->highlight = 0;
216 curr_ent->numInversions = 0;
217 curr_ent->charsUsed = 0;
218 curr_ent->textp = curr_buff;
219 memcpy(curr_ent->textp, blankLine, a_maxCharsPerEntry + 1);
220 for (curr_inv = 0; curr_inv < GATOR_TEXTCB_MAXINVERSIONS; curr_inv++)
221 curr_ent->inversion[curr_inv] = 0;
223 } /*Init each buffer entry */
225 if (gator_textcb_debug)
226 fprintf(stderr, "[%s] Filling in circ buff header at 0x%x\n", rn,
228 Lock_Init(&(newHdr->cbLock));
229 newHdr->maxEntriesStored = a_maxEntriesStored;
230 newHdr->maxCharsPerEntry = a_maxCharsPerEntry;
232 newHdr->currEntIdx = 0;
233 newHdr->oldestEnt = 0;
234 newHdr->oldestEntIdx = 0;
235 newHdr->entry = newEntries;
236 newHdr->blankLine = blankLine;
239 * Finally, return the location of the new header.
243 } /*gator_textcb_Create */
245 /*------------------------------------------------------------------------
249 * Move down to the next circular buffer entry.
252 * struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
255 * Ptr to the newest current entry.
258 * Nothing interesting.
262 *------------------------------------------------------------------------*/
264 static struct gator_textcb_entry *
266 struct gator_textcb_hdr *a_cbhdr;
270 static char rn[] = "bumpEntry"; /*Routine name */
271 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
272 int inv; /*Inversion number */
275 * Bump the total number of writes, and don't forget to advance
276 * the oldest entry, if appropriate.
278 if (gator_textcb_debug)
280 "[%s]: Bumping entry for circular buffer at 0x%x; current values: currEnt=%d (idx %d), oldestEnt=%d (idx %d), maxEntriesStored=%d\n",
281 rn, a_cbhdr, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
282 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx,
283 a_cbhdr->maxEntriesStored);
286 if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
287 a_cbhdr->currEntIdx = 0;
288 curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
290 if (gator_textcb_debug)
291 fprintf(stderr, "[%s] Zeroing entry %d (idx %d) at 0x%x\n", rn,
292 a_cbhdr->currEnt, a_cbhdr->currEntIdx, curr_ent);
294 curr_ent->ID = a_cbhdr->currEnt;
295 curr_ent->highlight = 0;
296 curr_ent->numInversions = 0;
297 curr_ent->charsUsed = 0;
299 * Copy over a blank line into the one we're initializing. We
300 * copy over the trailing null, too.
302 memcpy(curr_ent->textp, a_cbhdr->blankLine,
303 a_cbhdr->maxCharsPerEntry + 1);
304 for (inv = 0; inv < GATOR_TEXTCB_MAXINVERSIONS; inv++)
305 curr_ent->inversion[inv] = 0;
308 * If we've already stated circulating in the buffer, remember to
309 * bump the oldest entry info too.
311 if (a_cbhdr->currEnt >= a_cbhdr->maxEntriesStored) {
312 if (gator_textcb_debug)
314 "[%s]: Advancing oldest entry number & index; was entry %d, index %d, now ",
315 rn, a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx);
316 a_cbhdr->oldestEnt++;
317 if (++(a_cbhdr->oldestEntIdx) >= a_cbhdr->maxEntriesStored)
318 a_cbhdr->oldestEntIdx = 0;
319 if (gator_textcb_debug)
320 fprintf(stderr, "entry %d, index %d\n", a_cbhdr->oldestEnt,
321 a_cbhdr->oldestEntIdx);
324 /*Bump oldest entry info */
326 * Finally, return the address of the newest current entry.
332 /*------------------------------------------------------------------------
336 * Write the given string to the text circular buffer. Line
337 * breaks are caused either by overflowing the current text
338 * line or via explicit '\n's.
341 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
342 * char *a_textToWrite : Ptr to text to insert.
343 * int a_numChars : Number of chars to write.
344 * int a_highlight : Use highlighting?
345 * int a_skip; : Force a skip to the next line?
348 * Zero if successful,
349 * Error value otherwise.
352 * Circular buffer is consistent upon entry, namely the first and
353 * last entry pointers are legal.
357 *------------------------------------------------------------------------*/
360 gator_textcb_Write(a_cbhdr, a_textToWrite, a_numChars, a_highlight, a_skip)
361 struct gator_textcb_hdr *a_cbhdr;
367 { /*gator_textcb_Write */
369 static char rn[] = "gator_textcb_Write"; /*Routine name */
370 struct gator_textcb_entry *curr_ent; /*Ptr to current text entry */
371 int curr_ent_idx; /*Index of current entry */
372 int max_chars; /*Max chars per entry */
373 int chars_to_copy; /*Num chars to copy in */
374 int effective_highlight; /*Tmp highlight value */
375 char *dest; /*Destination of char copy */
378 * Make sure we haven't been passed a bogus buffer, and lock it
381 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
383 "[%s]: Null pointer passed in for circ buff header!! Aborting write operation.\n",
387 ObtainWriteLock(&(a_cbhdr->cbLock));
389 curr_ent_idx = a_cbhdr->currEntIdx;
390 curr_ent = (a_cbhdr->entry) + curr_ent_idx;
391 max_chars = a_cbhdr->maxCharsPerEntry;
392 effective_highlight = curr_ent->highlight;
393 if (curr_ent->numInversions % 2)
394 effective_highlight = (effective_highlight ? 0 : 1);
395 if (gator_textcb_debug)
397 "[%s]: Current entry: %d (at index %d, keeping %d max), effective highlight: %d, located at 0x%x\n",
398 rn, a_cbhdr->currEnt, curr_ent_idx, a_cbhdr->maxEntriesStored,
399 effective_highlight, curr_ent);
401 while (a_numChars > 0) {
403 * There are still characters to stuff into our circular buffer.
405 if (gator_textcb_debug)
407 "[%s]: Top of write loop: %d char(s) left to write.\n",
410 if (curr_ent->charsUsed >= max_chars) {
412 * Bump the entry in the given circular buffer.
414 if (gator_textcb_debug)
416 "[%s]: Entry %d at index %d full, advancing to next one.\n",
417 rn, a_cbhdr->currEnt, curr_ent_idx);
418 curr_ent = bumpEntry(a_cbhdr);
419 curr_ent_idx = a_cbhdr->currEntIdx;
420 if (gator_textcb_debug)
422 "[%s] New CB entry info: currEnt=%d (idx %d), oldestEnt=%d (idx %d), curr entry ptr is 0x%x\n",
423 rn, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
424 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx, curr_ent);
427 /*Bump current entry */
429 * At this point, the current entry has room for at least one more
430 * character, and we have at least one more character to write.
431 * Insert as much from the user text as possible.
433 chars_to_copy = max_chars - curr_ent->charsUsed;
434 if (a_numChars < chars_to_copy)
435 chars_to_copy = a_numChars;
436 dest = curr_ent->textp + curr_ent->charsUsed;
437 if (gator_textcb_debug)
439 "[%s]: Copying %d char(s) into current entry at 0x%x (entry buffer starts at 0x%x)\n",
440 rn, chars_to_copy, dest, curr_ent->textp);
443 * Handle highlighting and inversions.
445 if (curr_ent->charsUsed == 0) {
447 * No chars yet, so this sets the highlight field.
449 effective_highlight = curr_ent->highlight = a_highlight;
450 } else if (effective_highlight != a_highlight) {
452 * We need a new inversion, if there's room.
454 if (gator_textcb_debug)
456 "[%s]: Highlight mismatch, recording inversion at char loc %d\n",
457 rn, curr_ent->charsUsed);
458 if (curr_ent->numInversions < GATOR_TEXTCB_MAXINVERSIONS) {
459 effective_highlight = a_highlight;
460 curr_ent->inversion[curr_ent->numInversions] =
462 curr_ent->numInversions++;
463 } else if (gator_textcb_debug)
464 fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
467 /*Handle inversion */
469 * Move the string chunk into its place in the buffer, bump the
470 * number of chars used in the current entry.
472 strncpy(dest, a_textToWrite, chars_to_copy);
473 curr_ent->charsUsed += chars_to_copy;
474 a_textToWrite += chars_to_copy;
475 a_numChars -= chars_to_copy;
477 } /*while (a_numChars > 0) */
480 * All characters have been copied in. Handle the case where we've
481 * been asked to skip to the next entry, even if there's still room
482 * in the current one.
485 if (gator_textcb_debug)
486 fprintf(stderr, "[%s] Handling request to skip to next entry\n",
488 if (curr_ent->charsUsed > 0)
489 curr_ent = bumpEntry(a_cbhdr);
490 else if (gator_textcb_debug)
492 "[%s] Not skipping, we're already on a fresh entry\n",
496 /*Skip to the next entry */
498 * We can now unlock the CB and return successfully.
500 ReleaseWriteLock(&(a_cbhdr->cbLock));
503 } /*gator_textcb_Write */
505 /*------------------------------------------------------------------------
506 * gator_textcb_BlankLine
509 * Write out some number of blank lines to the given circular
513 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
514 * int *a_numBlanks : Num. blank lines to write.
517 * Zero if successful,
518 * Error value otherwise.
521 * Circular buffer is consistent upon entry, namely the first and
522 * last entry pointers are legal.
526 *------------------------------------------------------------------------*/
529 gator_textcb_BlankLine(a_cbhdr, a_numBlanks)
530 struct gator_textcb_hdr *a_cbhdr;
533 { /*gator_textcb_BlankLine */
535 static char rn[] = "gator_textcb_BlankLine"; /*Routine name */
537 if (gator_textcb_debug)
538 fprintf(stderr, "[%s] Putting out %d blank lines to the CB at 0x%x\n",
539 rn, a_numBlanks, a_cbhdr);
541 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
542 if (gator_textcb_debug)
543 fprintf(stderr, "[%s] Null pointer passed for CB hdr!!\n", rn);
547 while (a_numBlanks > 0) {
549 * The bumpEntry routine returns a struct gator_textcb_entry
550 * pointer, but we don't need it here, so we don't assign it.
557 * Return happily and successfully.
561 } /*gator_textcb_Write */
563 /*------------------------------------------------------------------------
564 * gator_textcb_Delete
567 * Delete the storage used by the given circular buffer, including
571 * struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
572 * circ buffer to reap.
575 * Zero if successful,
576 * Error value otherwise.
579 * We write-lock the buffer before deleting it, which is slightly
580 * silly, since it will stop existing after we're done. At least
581 * we'll make sure nobody is actively writing to it while it's
586 *------------------------------------------------------------------------*/
589 gator_textcb_Delete(a_cbhdr)
590 struct gator_textcb_hdr *a_cbhdr;
592 { /*gator_textcb_Delete */
594 static char rn[] = "gator_textcb_Delete"; /*Routine name */
596 if (gator_textcb_debug)
597 fprintf(stderr, "[%s]: Deleting text circular buffer at 0x%x\n", rn,
599 ObtainWriteLock(&(a_cbhdr->cbLock));
602 * The beginning of the text buffer itself is pointed to by the
605 if (gator_textcb_debug)
607 "[%s]: Freeing text buffer proper at 0x%x (%d bytes)\n", rn,
608 a_cbhdr->entry[0].textp,
609 (a_cbhdr->maxEntriesStored * a_cbhdr->maxCharsPerEntry));
610 free(a_cbhdr->entry[0].textp);
611 a_cbhdr->entry[0].textp = NULL;
613 if (gator_textcb_debug)
614 fprintf(stderr, "[%s]: Freeing text entry array at 0x%x (%d bytes)\n",
616 (a_cbhdr->maxEntriesStored *
617 sizeof(struct gator_textcb_entry)));
618 free(a_cbhdr->entry);
619 a_cbhdr->entry = (struct gator_textcb_entry *)0;
620 free(a_cbhdr->blankLine);
621 a_cbhdr->blankLine = NULL;
624 * Release the write lock on it, then free the header itself.
626 ReleaseWriteLock(&(a_cbhdr->cbLock));
627 if (gator_textcb_debug)
628 fprintf(stderr, "[%s] Freeing cicular buffer header at 0x%x\n", rn,
633 } /*gator_textcb_Delete */