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(int a_debug)
52 { /*gator_textcb_Init */
54 static int initd; /*Have we been called already? */
55 static char rn[] = "gator_textcb_Init"; /*Routine name */
59 "[%s] Initialization routine called multiple times!!\n", rn);
64 gator_textcb_debug = a_debug;
67 } /*gator_textcb_Init */
69 /*------------------------------------------------------------------------
73 * Create a new circular buffer.
76 * int a_maxEntriesStored : How many entries should it have?
77 * int a_maxCharsPerEntry : Max chars in each entry.
80 * Ptr to the fully-initialized circular buffer hdr if successful,
81 * Null pointer otherwise.
84 * Makes sure the lock structure is properly initialized.
87 * As advertised; space is allocated for the circ buff.
88 *------------------------------------------------------------------------*/
90 struct gator_textcb_hdr *
91 gator_textcb_Create(int a_maxEntriesStored, int a_maxCharsPerEntry)
92 { /*gator_textcb_Create */
94 static char rn[] = "gator_textcb_Create"; /*Routine name */
95 char *newBuff; /*Ptr to new text buffer */
96 struct gator_textcb_entry *newEntries; /*Ptr to new text entries */
97 struct gator_textcb_hdr *newHdr; /*Ptr to new text hdr */
98 int bytesToAllocate; /*Num bytes to allocate */
99 int numBuffBytes; /*Num bytes in text buffer */
100 int curr_ent_num; /*Current entry number */
101 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
102 char *curr_buff; /*Ptr to current buff pos */
103 int curr_inv; /*Current inversion idx */
104 char *blankLine; /*Ptr to blank line */
105 int i; /*Loop variable */
108 * Start off by allocating the text buffer itself. Don't forget we
109 * need to allocate one more character per line, to make sure we can
110 * always null-terminate them. We also need to allocate the blank
113 numBuffBytes = bytesToAllocate =
114 a_maxEntriesStored * (a_maxCharsPerEntry + 1);
115 if (gator_textcb_debug)
116 fprintf(stderr, "[%s] Allocating %d bytes for the text buffer\n", rn,
118 newBuff = (char *)malloc(bytesToAllocate);
119 if (newBuff == NULL) {
121 "[%s] Can't allocate %d bytes for text buffer; errno is %d\n",
122 rn, bytesToAllocate, errno);
123 return ((struct gator_textcb_hdr *)0);
124 } else if (gator_textcb_debug)
125 fprintf(stderr, "[%s] Text buffer allocated at %p\n", rn, newBuff);
126 blankLine = (char *)malloc(a_maxCharsPerEntry + 1);
127 if (blankLine == NULL) {
129 "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n",
130 rn, a_maxCharsPerEntry + 1, errno);
132 return ((struct gator_textcb_hdr *)0);
136 * Next, allocate the entry array.
138 bytesToAllocate = a_maxEntriesStored * sizeof(struct gator_textcb_entry);
139 if (gator_textcb_debug)
141 "[%s] Allocating %d bytes for the %d text entry array items\n",
142 rn, bytesToAllocate, a_maxEntriesStored);
143 newEntries = (struct gator_textcb_entry *)malloc(bytesToAllocate);
144 if (newEntries == (struct gator_textcb_entry *)0) {
146 "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n",
147 rn, bytesToAllocate, a_maxEntriesStored, errno);
150 return ((struct gator_textcb_hdr *)0);
151 } else if (gator_textcb_debug)
152 fprintf(stderr, "[%s] Text buffer entry array allocated at %p\n",
156 * Finish off by allocating the text circular buffer header.
158 bytesToAllocate = sizeof(struct gator_textcb_hdr);
159 if (gator_textcb_debug)
161 "[%s] Allocating %d bytes for the text circular buffer header\n",
162 rn, bytesToAllocate);
163 newHdr = (struct gator_textcb_hdr *)malloc(bytesToAllocate);
164 if (newHdr == (struct gator_textcb_hdr *)0) {
166 "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n",
167 rn, bytesToAllocate, errno);
171 return ((struct gator_textcb_hdr *)0);
172 } else if (gator_textcb_debug)
174 "[%s] Text circular buffer header allocated at %p\n", rn,
178 * Now, just initialize all the pieces and plug them in.
180 if (gator_textcb_debug)
181 fprintf(stderr, "[%s] Zeroing %d bytes in text buffer at %p\n", rn,
182 numBuffBytes, newBuff);
183 memset(newBuff, 0, numBuffBytes);
185 if (gator_textcb_debug)
186 fprintf(stderr, "[%s] Initializing blank line buffer at %p\n", rn,
188 for (i = 0; i < a_maxCharsPerEntry; i++)
189 *(blankLine + i) = ' ';
190 *(blankLine + a_maxCharsPerEntry) = '\0';
193 * Initialize each buffer entry.
195 for (curr_ent_num = 0, curr_buff = newBuff, curr_ent = newEntries;
196 curr_ent_num < a_maxEntriesStored;
197 curr_ent++, curr_ent_num++, curr_buff += (a_maxCharsPerEntry + 1)) {
198 if (gator_textcb_debug)
200 "[%s] Initializing buffer entry %d; its text buffer address is %p\n",
201 rn, curr_ent_num, curr_buff);
203 curr_ent->highlight = 0;
204 curr_ent->numInversions = 0;
205 curr_ent->charsUsed = 0;
206 curr_ent->textp = curr_buff;
207 memcpy(curr_ent->textp, blankLine, a_maxCharsPerEntry + 1);
208 for (curr_inv = 0; curr_inv < GATOR_TEXTCB_MAXINVERSIONS; curr_inv++)
209 curr_ent->inversion[curr_inv] = 0;
211 } /*Init each buffer entry */
213 if (gator_textcb_debug)
214 fprintf(stderr, "[%s] Filling in circ buff header at %p\n", rn,
216 Lock_Init(&(newHdr->cbLock));
217 newHdr->maxEntriesStored = a_maxEntriesStored;
218 newHdr->maxCharsPerEntry = a_maxCharsPerEntry;
220 newHdr->currEntIdx = 0;
221 newHdr->oldestEnt = 0;
222 newHdr->oldestEntIdx = 0;
223 newHdr->entry = newEntries;
224 newHdr->blankLine = blankLine;
227 * Finally, return the location of the new header.
231 } /*gator_textcb_Create */
233 /*------------------------------------------------------------------------
237 * Move down to the next circular buffer entry.
240 * struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
243 * Ptr to the newest current entry.
246 * Nothing interesting.
250 *------------------------------------------------------------------------*/
252 static struct gator_textcb_entry *
253 bumpEntry(struct gator_textcb_hdr *a_cbhdr)
257 static char rn[] = "bumpEntry"; /*Routine name */
258 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
259 int inv; /*Inversion number */
262 * Bump the total number of writes, and don't forget to advance
263 * the oldest entry, if appropriate.
265 if (gator_textcb_debug)
267 "[%s]: Bumping entry for circular buffer at %p; current values: currEnt=%d (idx %d), oldestEnt=%d (idx %d), maxEntriesStored=%d\n",
268 rn, a_cbhdr, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
269 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx,
270 a_cbhdr->maxEntriesStored);
273 if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
274 a_cbhdr->currEntIdx = 0;
275 curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
277 if (gator_textcb_debug)
278 fprintf(stderr, "[%s] Zeroing entry %d (idx %d) at %p\n", rn,
279 a_cbhdr->currEnt, a_cbhdr->currEntIdx, curr_ent);
281 curr_ent->ID = a_cbhdr->currEnt;
282 curr_ent->highlight = 0;
283 curr_ent->numInversions = 0;
284 curr_ent->charsUsed = 0;
286 * Copy over a blank line into the one we're initializing. We
287 * copy over the trailing null, too.
289 memcpy(curr_ent->textp, a_cbhdr->blankLine,
290 a_cbhdr->maxCharsPerEntry + 1);
291 for (inv = 0; inv < GATOR_TEXTCB_MAXINVERSIONS; inv++)
292 curr_ent->inversion[inv] = 0;
295 * If we've already stated circulating in the buffer, remember to
296 * bump the oldest entry info too.
298 if (a_cbhdr->currEnt >= a_cbhdr->maxEntriesStored) {
299 if (gator_textcb_debug)
301 "[%s]: Advancing oldest entry number & index; was entry %d, index %d, now ",
302 rn, a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx);
303 a_cbhdr->oldestEnt++;
304 if (++(a_cbhdr->oldestEntIdx) >= a_cbhdr->maxEntriesStored)
305 a_cbhdr->oldestEntIdx = 0;
306 if (gator_textcb_debug)
307 fprintf(stderr, "entry %d, index %d\n", a_cbhdr->oldestEnt,
308 a_cbhdr->oldestEntIdx);
311 /*Bump oldest entry info */
313 * Finally, return the address of the newest current entry.
319 /*------------------------------------------------------------------------
323 * Write the given string to the text circular buffer. Line
324 * breaks are caused either by overflowing the current text
325 * line or via explicit '\n's.
328 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
329 * char *a_textToWrite : Ptr to text to insert.
330 * int a_numChars : Number of chars to write.
331 * int a_highlight : Use highlighting?
332 * int a_skip; : Force a skip to the next line?
335 * Zero if successful,
336 * Error value otherwise.
339 * Circular buffer is consistent upon entry, namely the first and
340 * last entry pointers are legal.
344 *------------------------------------------------------------------------*/
347 gator_textcb_Write(struct gator_textcb_hdr *a_cbhdr, char *a_textToWrite,
348 int a_numChars, int a_highlight, int a_skip)
349 { /*gator_textcb_Write */
351 static char rn[] = "gator_textcb_Write"; /*Routine name */
352 struct gator_textcb_entry *curr_ent; /*Ptr to current text entry */
353 int curr_ent_idx; /*Index of current entry */
354 int max_chars; /*Max chars per entry */
355 int chars_to_copy; /*Num chars to copy in */
356 int effective_highlight; /*Tmp highlight value */
357 char *dest; /*Destination of char copy */
360 * Make sure we haven't been passed a bogus buffer, and lock it
363 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
365 "[%s]: Null pointer passed in for circ buff header!! Aborting write operation.\n",
369 ObtainWriteLock(&(a_cbhdr->cbLock));
371 curr_ent_idx = a_cbhdr->currEntIdx;
372 curr_ent = (a_cbhdr->entry) + curr_ent_idx;
373 max_chars = a_cbhdr->maxCharsPerEntry;
374 effective_highlight = curr_ent->highlight;
375 if (curr_ent->numInversions % 2)
376 effective_highlight = (effective_highlight ? 0 : 1);
377 if (gator_textcb_debug)
379 "[%s]: Current entry: %d (at index %d, keeping %d max), effective highlight: %d, located at %p\n",
380 rn, a_cbhdr->currEnt, curr_ent_idx, a_cbhdr->maxEntriesStored,
381 effective_highlight, curr_ent);
383 while (a_numChars > 0) {
385 * There are still characters to stuff into our circular buffer.
387 if (gator_textcb_debug)
389 "[%s]: Top of write loop: %d char(s) left to write.\n",
392 if (curr_ent->charsUsed >= max_chars) {
394 * Bump the entry in the given circular buffer.
396 if (gator_textcb_debug)
398 "[%s]: Entry %d at index %d full, advancing to next one.\n",
399 rn, a_cbhdr->currEnt, curr_ent_idx);
400 curr_ent = bumpEntry(a_cbhdr);
401 curr_ent_idx = a_cbhdr->currEntIdx;
402 if (gator_textcb_debug)
404 "[%s] New CB entry info: currEnt=%d (idx %d), oldestEnt=%d (idx %d), curr entry ptr is %p\n",
405 rn, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
406 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx, curr_ent);
409 /*Bump current entry */
411 * At this point, the current entry has room for at least one more
412 * character, and we have at least one more character to write.
413 * Insert as much from the user text as possible.
415 chars_to_copy = max_chars - curr_ent->charsUsed;
416 if (a_numChars < chars_to_copy)
417 chars_to_copy = a_numChars;
418 dest = curr_ent->textp + curr_ent->charsUsed;
419 if (gator_textcb_debug)
421 "[%s]: Copying %d char(s) into current entry at %p (entry buffer starts at %p)\n",
422 rn, chars_to_copy, dest, curr_ent->textp);
425 * Handle highlighting and inversions.
427 if (curr_ent->charsUsed == 0) {
429 * No chars yet, so this sets the highlight field.
431 effective_highlight = curr_ent->highlight = a_highlight;
432 } else if (effective_highlight != a_highlight) {
434 * We need a new inversion, if there's room.
436 if (gator_textcb_debug)
438 "[%s]: Highlight mismatch, recording inversion at char loc %d\n",
439 rn, curr_ent->charsUsed);
440 if (curr_ent->numInversions < GATOR_TEXTCB_MAXINVERSIONS) {
441 effective_highlight = a_highlight;
442 curr_ent->inversion[curr_ent->numInversions] =
444 curr_ent->numInversions++;
445 } else if (gator_textcb_debug)
446 fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
449 /*Handle inversion */
451 * Move the string chunk into its place in the buffer, bump the
452 * number of chars used in the current entry.
454 strncpy(dest, a_textToWrite, chars_to_copy);
455 curr_ent->charsUsed += chars_to_copy;
456 a_textToWrite += chars_to_copy;
457 a_numChars -= chars_to_copy;
459 } /*while (a_numChars > 0) */
462 * All characters have been copied in. Handle the case where we've
463 * been asked to skip to the next entry, even if there's still room
464 * in the current one.
467 if (gator_textcb_debug)
468 fprintf(stderr, "[%s] Handling request to skip to next entry\n",
470 if (curr_ent->charsUsed > 0)
471 curr_ent = bumpEntry(a_cbhdr);
472 else if (gator_textcb_debug)
474 "[%s] Not skipping, we're already on a fresh entry\n",
478 /*Skip to the next entry */
480 * We can now unlock the CB and return successfully.
482 ReleaseWriteLock(&(a_cbhdr->cbLock));
485 } /*gator_textcb_Write */
487 /*------------------------------------------------------------------------
488 * gator_textcb_BlankLine
491 * Write out some number of blank lines to the given circular
495 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
496 * int *a_numBlanks : Num. blank lines to write.
499 * Zero if successful,
500 * Error value otherwise.
503 * Circular buffer is consistent upon entry, namely the first and
504 * last entry pointers are legal.
508 *------------------------------------------------------------------------*/
511 gator_textcb_BlankLine(struct gator_textcb_hdr *a_cbhdr,
513 { /*gator_textcb_BlankLine */
515 static char rn[] = "gator_textcb_BlankLine"; /*Routine name */
517 if (gator_textcb_debug)
518 fprintf(stderr, "[%s] Putting out %d blank lines to the CB at %p\n",
519 rn, a_numBlanks, a_cbhdr);
521 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
522 if (gator_textcb_debug)
523 fprintf(stderr, "[%s] Null pointer passed for CB hdr!!\n", rn);
527 while (a_numBlanks > 0) {
529 * The bumpEntry routine returns a struct gator_textcb_entry
530 * pointer, but we don't need it here, so we don't assign it.
537 * Return happily and successfully.
541 } /*gator_textcb_Write */
543 /*------------------------------------------------------------------------
544 * gator_textcb_Delete
547 * Delete the storage used by the given circular buffer, including
551 * struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
552 * circ buffer to reap.
555 * Zero if successful,
556 * Error value otherwise.
559 * We write-lock the buffer before deleting it, which is slightly
560 * silly, since it will stop existing after we're done. At least
561 * we'll make sure nobody is actively writing to it while it's
566 *------------------------------------------------------------------------*/
569 gator_textcb_Delete(struct gator_textcb_hdr *a_cbhdr)
570 { /*gator_textcb_Delete */
572 static char rn[] = "gator_textcb_Delete"; /*Routine name */
574 if (gator_textcb_debug)
575 fprintf(stderr, "[%s]: Deleting text circular buffer at %p\n", rn,
577 ObtainWriteLock(&(a_cbhdr->cbLock));
580 * The beginning of the text buffer itself is pointed to by the
583 if (gator_textcb_debug)
585 "[%s]: Freeing text buffer proper at %p (%d bytes)\n", rn,
586 a_cbhdr->entry[0].textp,
587 (a_cbhdr->maxEntriesStored * a_cbhdr->maxCharsPerEntry));
588 free(a_cbhdr->entry[0].textp);
589 a_cbhdr->entry[0].textp = NULL;
591 if (gator_textcb_debug)
592 fprintf(stderr, "[%s]: Freeing text entry array at %p (%lu bytes)\n",
594 (a_cbhdr->maxEntriesStored *
595 sizeof(struct gator_textcb_entry)));
596 free(a_cbhdr->entry);
597 a_cbhdr->entry = (struct gator_textcb_entry *)0;
598 free(a_cbhdr->blankLine);
599 a_cbhdr->blankLine = NULL;
602 * Release the write lock on it, then free the header itself.
604 ReleaseWriteLock(&(a_cbhdr->cbLock));
605 if (gator_textcb_debug)
606 fprintf(stderr, "[%s] Freeing cicular buffer header at %p\n", rn,
611 } /*gator_textcb_Delete */