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 */
28 static int gator_textcb_debug; /*Is debugging output turned on? */
30 /*------------------------------------------------------------------------
34 * Initialize the text circular buffer package.
37 * a_debug : Should debugging output be turned on?
41 * Error value otherwise.
44 * MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
47 * Just remembers if debugging output should be generated.
48 *------------------------------------------------------------------------*/
51 gator_textcb_Init(a_debug)
54 { /*gator_textcb_Init */
56 static int initd; /*Have we been called already? */
57 static char rn[] = "gator_textcb_Init"; /*Routine name */
61 "[%s] Initialization routine called multiple times!!\n", rn);
66 gator_textcb_debug = a_debug;
69 } /*gator_textcb_Init */
71 /*------------------------------------------------------------------------
75 * Create a new circular buffer.
78 * int a_maxEntriesStored : How many entries should it have?
79 * int a_maxCharsPerEntry : Max chars in each entry.
82 * Ptr to the fully-initialized circular buffer hdr if successful,
83 * Null pointer otherwise.
86 * Makes sure the lock structure is properly initialized.
89 * As advertised; space is allocated for the circ buff.
90 *------------------------------------------------------------------------*/
92 struct gator_textcb_hdr *
93 gator_textcb_Create(a_maxEntriesStored, a_maxCharsPerEntry)
94 int a_maxEntriesStored;
95 int a_maxCharsPerEntry;
97 { /*gator_textcb_Create */
99 static char rn[] = "gator_textcb_Create"; /*Routine name */
100 char *newBuff; /*Ptr to new text buffer */
101 struct gator_textcb_entry *newEntries; /*Ptr to new text entries */
102 struct gator_textcb_hdr *newHdr; /*Ptr to new text hdr */
103 int bytesToAllocate; /*Num bytes to allocate */
104 int numBuffBytes; /*Num bytes in text buffer */
105 int curr_ent_num; /*Current entry number */
106 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
107 char *curr_buff; /*Ptr to current buff pos */
108 int curr_inv; /*Current inversion idx */
109 char *blankLine; /*Ptr to blank line */
110 int i; /*Loop variable */
113 * Start off by allocating the text buffer itself. Don't forget we
114 * need to allocate one more character per line, to make sure we can
115 * always null-terminate them. We also need to allocate the blank
118 numBuffBytes = bytesToAllocate =
119 a_maxEntriesStored * (a_maxCharsPerEntry + 1);
120 if (gator_textcb_debug)
121 fprintf(stderr, "[%s] Allocating %d bytes for the text buffer\n", rn,
123 newBuff = (char *)malloc(bytesToAllocate);
124 if (newBuff == NULL) {
126 "[%s] Can't allocate %d bytes for text buffer; errno is %d\n",
127 rn, bytesToAllocate, errno);
128 return ((struct gator_textcb_hdr *)0);
129 } else if (gator_textcb_debug)
130 fprintf(stderr, "[%s] Text buffer allocated at 0x%x\n", rn, newBuff);
131 blankLine = (char *)malloc(a_maxCharsPerEntry + 1);
132 if (blankLine == NULL) {
134 "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n",
135 rn, a_maxCharsPerEntry + 1, errno);
137 return ((struct gator_textcb_hdr *)0);
141 * Next, allocate the entry array.
143 bytesToAllocate = a_maxEntriesStored * sizeof(struct gator_textcb_entry);
144 if (gator_textcb_debug)
146 "[%s] Allocating %d bytes for the %d text entry array items\n",
147 rn, bytesToAllocate, a_maxEntriesStored);
148 newEntries = (struct gator_textcb_entry *)malloc(bytesToAllocate);
149 if (newEntries == (struct gator_textcb_entry *)0) {
151 "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n",
152 rn, bytesToAllocate, a_maxEntriesStored, errno);
155 return ((struct gator_textcb_hdr *)0);
156 } else if (gator_textcb_debug)
157 fprintf(stderr, "[%s] Text buffer entry array allocated at 0x%x\n",
161 * Finish off by allocating the text circular buffer header.
163 bytesToAllocate = sizeof(struct gator_textcb_hdr);
164 if (gator_textcb_debug)
166 "[%s] Allocating %d bytes for the text circular buffer header\n",
167 rn, bytesToAllocate);
168 newHdr = (struct gator_textcb_hdr *)malloc(bytesToAllocate);
169 if (newHdr == (struct gator_textcb_hdr *)0) {
171 "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n",
172 rn, bytesToAllocate, errno);
176 return ((struct gator_textcb_hdr *)0);
177 } else if (gator_textcb_debug)
179 "[%s] Text circular buffer header allocated at 0x%x\n", rn,
183 * Now, just initialize all the pieces and plug them in.
185 if (gator_textcb_debug)
186 fprintf(stderr, "[%s] Zeroing %d bytes in text buffer at 0x%x\n", rn,
187 numBuffBytes, newBuff);
188 memset(newBuff, 0, numBuffBytes);
190 if (gator_textcb_debug)
191 fprintf(stderr, "[%s] Initializing blank line buffer at 0x%x\n", rn,
193 for (i = 0; i < a_maxCharsPerEntry; i++)
194 *(blankLine + i) = ' ';
195 *(blankLine + a_maxCharsPerEntry) = '\0';
198 * Initialize each buffer entry.
200 for (curr_ent_num = 0, curr_buff = newBuff, curr_ent = newEntries;
201 curr_ent_num < a_maxEntriesStored;
202 curr_ent++, curr_ent_num++, curr_buff += (a_maxCharsPerEntry + 1)) {
203 if (gator_textcb_debug)
205 "[%s] Initializing buffer entry %d; its text buffer address is 0x%x\n",
206 rn, curr_ent_num, curr_buff);
208 curr_ent->highlight = 0;
209 curr_ent->numInversions = 0;
210 curr_ent->charsUsed = 0;
211 curr_ent->textp = curr_buff;
212 memcpy(curr_ent->textp, blankLine, a_maxCharsPerEntry + 1);
213 for (curr_inv = 0; curr_inv < GATOR_TEXTCB_MAXINVERSIONS; curr_inv++)
214 curr_ent->inversion[curr_inv] = 0;
216 } /*Init each buffer entry */
218 if (gator_textcb_debug)
219 fprintf(stderr, "[%s] Filling in circ buff header at 0x%x\n", rn,
221 Lock_Init(&(newHdr->cbLock));
222 newHdr->maxEntriesStored = a_maxEntriesStored;
223 newHdr->maxCharsPerEntry = a_maxCharsPerEntry;
225 newHdr->currEntIdx = 0;
226 newHdr->oldestEnt = 0;
227 newHdr->oldestEntIdx = 0;
228 newHdr->entry = newEntries;
229 newHdr->blankLine = blankLine;
232 * Finally, return the location of the new header.
236 } /*gator_textcb_Create */
238 /*------------------------------------------------------------------------
242 * Move down to the next circular buffer entry.
245 * struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
248 * Ptr to the newest current entry.
251 * Nothing interesting.
255 *------------------------------------------------------------------------*/
257 static struct gator_textcb_entry *
259 struct gator_textcb_hdr *a_cbhdr;
263 static char rn[] = "bumpEntry"; /*Routine name */
264 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
265 int inv; /*Inversion number */
268 * Bump the total number of writes, and don't forget to advance
269 * the oldest entry, if appropriate.
271 if (gator_textcb_debug)
273 "[%s]: Bumping entry for circular buffer at 0x%x; current values: currEnt=%d (idx %d), oldestEnt=%d (idx %d), maxEntriesStored=%d\n",
274 rn, a_cbhdr, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
275 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx,
276 a_cbhdr->maxEntriesStored);
279 if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
280 a_cbhdr->currEntIdx = 0;
281 curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
283 if (gator_textcb_debug)
284 fprintf(stderr, "[%s] Zeroing entry %d (idx %d) at 0x%x\n", rn,
285 a_cbhdr->currEnt, a_cbhdr->currEntIdx, curr_ent);
287 curr_ent->ID = a_cbhdr->currEnt;
288 curr_ent->highlight = 0;
289 curr_ent->numInversions = 0;
290 curr_ent->charsUsed = 0;
292 * Copy over a blank line into the one we're initializing. We
293 * copy over the trailing null, too.
295 memcpy(curr_ent->textp, a_cbhdr->blankLine,
296 a_cbhdr->maxCharsPerEntry + 1);
297 for (inv = 0; inv < GATOR_TEXTCB_MAXINVERSIONS; inv++)
298 curr_ent->inversion[inv] = 0;
301 * If we've already stated circulating in the buffer, remember to
302 * bump the oldest entry info too.
304 if (a_cbhdr->currEnt >= a_cbhdr->maxEntriesStored) {
305 if (gator_textcb_debug)
307 "[%s]: Advancing oldest entry number & index; was entry %d, index %d, now ",
308 rn, a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx);
309 a_cbhdr->oldestEnt++;
310 if (++(a_cbhdr->oldestEntIdx) >= a_cbhdr->maxEntriesStored)
311 a_cbhdr->oldestEntIdx = 0;
312 if (gator_textcb_debug)
313 fprintf(stderr, "entry %d, index %d\n", a_cbhdr->oldestEnt,
314 a_cbhdr->oldestEntIdx);
317 /*Bump oldest entry info */
319 * Finally, return the address of the newest current entry.
325 /*------------------------------------------------------------------------
329 * Write the given string to the text circular buffer. Line
330 * breaks are caused either by overflowing the current text
331 * line or via explicit '\n's.
334 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
335 * char *a_textToWrite : Ptr to text to insert.
336 * int a_numChars : Number of chars to write.
337 * int a_highlight : Use highlighting?
338 * int a_skip; : Force a skip to the next line?
341 * Zero if successful,
342 * Error value otherwise.
345 * Circular buffer is consistent upon entry, namely the first and
346 * last entry pointers are legal.
350 *------------------------------------------------------------------------*/
353 gator_textcb_Write(a_cbhdr, a_textToWrite, a_numChars, a_highlight, a_skip)
354 struct gator_textcb_hdr *a_cbhdr;
360 { /*gator_textcb_Write */
362 static char rn[] = "gator_textcb_Write"; /*Routine name */
363 struct gator_textcb_entry *curr_ent; /*Ptr to current text entry */
364 int curr_ent_idx; /*Index of current entry */
365 int max_chars; /*Max chars per entry */
366 int chars_to_copy; /*Num chars to copy in */
367 int effective_highlight; /*Tmp highlight value */
368 char *dest; /*Destination of char copy */
371 * Make sure we haven't been passed a bogus buffer, and lock it
374 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
376 "[%s]: Null pointer passed in for circ buff header!! Aborting write operation.\n",
380 ObtainWriteLock(&(a_cbhdr->cbLock));
382 curr_ent_idx = a_cbhdr->currEntIdx;
383 curr_ent = (a_cbhdr->entry) + curr_ent_idx;
384 max_chars = a_cbhdr->maxCharsPerEntry;
385 effective_highlight = curr_ent->highlight;
386 if (curr_ent->numInversions % 2)
387 effective_highlight = (effective_highlight ? 0 : 1);
388 if (gator_textcb_debug)
390 "[%s]: Current entry: %d (at index %d, keeping %d max), effective highlight: %d, located at 0x%x\n",
391 rn, a_cbhdr->currEnt, curr_ent_idx, a_cbhdr->maxEntriesStored,
392 effective_highlight, curr_ent);
394 while (a_numChars > 0) {
396 * There are still characters to stuff into our circular buffer.
398 if (gator_textcb_debug)
400 "[%s]: Top of write loop: %d char(s) left to write.\n",
403 if (curr_ent->charsUsed >= max_chars) {
405 * Bump the entry in the given circular buffer.
407 if (gator_textcb_debug)
409 "[%s]: Entry %d at index %d full, advancing to next one.\n",
410 rn, a_cbhdr->currEnt, curr_ent_idx);
411 curr_ent = bumpEntry(a_cbhdr);
412 curr_ent_idx = a_cbhdr->currEntIdx;
413 if (gator_textcb_debug)
415 "[%s] New CB entry info: currEnt=%d (idx %d), oldestEnt=%d (idx %d), curr entry ptr is 0x%x\n",
416 rn, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
417 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx, curr_ent);
420 /*Bump current entry */
422 * At this point, the current entry has room for at least one more
423 * character, and we have at least one more character to write.
424 * Insert as much from the user text as possible.
426 chars_to_copy = max_chars - curr_ent->charsUsed;
427 if (a_numChars < chars_to_copy)
428 chars_to_copy = a_numChars;
429 dest = curr_ent->textp + curr_ent->charsUsed;
430 if (gator_textcb_debug)
432 "[%s]: Copying %d char(s) into current entry at 0x%x (entry buffer starts at 0x%x)\n",
433 rn, chars_to_copy, dest, curr_ent->textp);
436 * Handle highlighting and inversions.
438 if (curr_ent->charsUsed == 0) {
440 * No chars yet, so this sets the highlight field.
442 effective_highlight = curr_ent->highlight = a_highlight;
443 } else if (effective_highlight != a_highlight) {
445 * We need a new inversion, if there's room.
447 if (gator_textcb_debug)
449 "[%s]: Highlight mismatch, recording inversion at char loc %d\n",
450 rn, curr_ent->charsUsed);
451 if (curr_ent->numInversions < GATOR_TEXTCB_MAXINVERSIONS) {
452 effective_highlight = a_highlight;
453 curr_ent->inversion[curr_ent->numInversions] =
455 curr_ent->numInversions++;
456 } else if (gator_textcb_debug)
457 fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
460 /*Handle inversion */
462 * Move the string chunk into its place in the buffer, bump the
463 * number of chars used in the current entry.
465 strncpy(dest, a_textToWrite, chars_to_copy);
466 curr_ent->charsUsed += chars_to_copy;
467 a_textToWrite += chars_to_copy;
468 a_numChars -= chars_to_copy;
470 } /*while (a_numChars > 0) */
473 * All characters have been copied in. Handle the case where we've
474 * been asked to skip to the next entry, even if there's still room
475 * in the current one.
478 if (gator_textcb_debug)
479 fprintf(stderr, "[%s] Handling request to skip to next entry\n",
481 if (curr_ent->charsUsed > 0)
482 curr_ent = bumpEntry(a_cbhdr);
483 else if (gator_textcb_debug)
485 "[%s] Not skipping, we're already on a fresh entry\n",
489 /*Skip to the next entry */
491 * We can now unlock the CB and return successfully.
493 ReleaseWriteLock(&(a_cbhdr->cbLock));
496 } /*gator_textcb_Write */
498 /*------------------------------------------------------------------------
499 * gator_textcb_BlankLine
502 * Write out some number of blank lines to the given circular
506 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
507 * int *a_numBlanks : Num. blank lines to write.
510 * Zero if successful,
511 * Error value otherwise.
514 * Circular buffer is consistent upon entry, namely the first and
515 * last entry pointers are legal.
519 *------------------------------------------------------------------------*/
522 gator_textcb_BlankLine(a_cbhdr, a_numBlanks)
523 struct gator_textcb_hdr *a_cbhdr;
526 { /*gator_textcb_BlankLine */
528 static char rn[] = "gator_textcb_BlankLine"; /*Routine name */
530 if (gator_textcb_debug)
531 fprintf(stderr, "[%s] Putting out %d blank lines to the CB at 0x%x\n",
532 rn, a_numBlanks, a_cbhdr);
534 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
535 if (gator_textcb_debug)
536 fprintf(stderr, "[%s] Null pointer passed for CB hdr!!\n", rn);
540 while (a_numBlanks > 0) {
542 * The bumpEntry routine returns a struct gator_textcb_entry
543 * pointer, but we don't need it here, so we don't assign it.
550 * Return happily and successfully.
554 } /*gator_textcb_Write */
556 /*------------------------------------------------------------------------
557 * gator_textcb_Delete
560 * Delete the storage used by the given circular buffer, including
564 * struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
565 * circ buffer to reap.
568 * Zero if successful,
569 * Error value otherwise.
572 * We write-lock the buffer before deleting it, which is slightly
573 * silly, since it will stop existing after we're done. At least
574 * we'll make sure nobody is actively writing to it while it's
579 *------------------------------------------------------------------------*/
582 gator_textcb_Delete(a_cbhdr)
583 struct gator_textcb_hdr *a_cbhdr;
585 { /*gator_textcb_Delete */
587 static char rn[] = "gator_textcb_Delete"; /*Routine name */
589 if (gator_textcb_debug)
590 fprintf(stderr, "[%s]: Deleting text circular buffer at 0x%x\n", rn,
592 ObtainWriteLock(&(a_cbhdr->cbLock));
595 * The beginning of the text buffer itself is pointed to by the
598 if (gator_textcb_debug)
600 "[%s]: Freeing text buffer proper at 0x%x (%d bytes)\n", rn,
601 a_cbhdr->entry[0].textp,
602 (a_cbhdr->maxEntriesStored * a_cbhdr->maxCharsPerEntry));
603 free(a_cbhdr->entry[0].textp);
604 a_cbhdr->entry[0].textp = NULL;
606 if (gator_textcb_debug)
607 fprintf(stderr, "[%s]: Freeing text entry array at 0x%x (%d bytes)\n",
609 (a_cbhdr->maxEntriesStored *
610 sizeof(struct gator_textcb_entry)));
611 free(a_cbhdr->entry);
612 a_cbhdr->entry = (struct gator_textcb_entry *)0;
613 free(a_cbhdr->blankLine);
614 a_cbhdr->blankLine = NULL;
617 * Release the write lock on it, then free the header itself.
619 ReleaseWriteLock(&(a_cbhdr->cbLock));
620 if (gator_textcb_debug)
621 fprintf(stderr, "[%s] Freeing cicular buffer header at 0x%x\n", rn,
626 } /*gator_textcb_Delete */