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*/
22 #include <stdio.h> /*Standard I/O stuff*/
25 static int gator_textcb_debug; /*Is debugging output turned on?*/
27 /*------------------------------------------------------------------------
31 * Initialize the text circular buffer package.
34 * a_debug : Should debugging output be turned on?
38 * Error value otherwise.
41 * MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
44 * Just remembers if debugging output should be generated.
45 *------------------------------------------------------------------------*/
47 int gator_textcb_Init(a_debug)
50 { /*gator_textcb_Init*/
52 static int initd; /*Have we been called already?*/
53 static char rn[] = "gator_textcb_Init"; /*Routine name*/
56 fprintf(stderr, "[%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 *gator_textcb_Create(a_maxEntriesStored, a_maxCharsPerEntry)
89 int a_maxEntriesStored;
90 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 = a_maxEntriesStored *
114 (a_maxCharsPerEntry + 1);
115 if (gator_textcb_debug)
116 fprintf(stderr, "[%s] Allocating %d bytes for the text buffer\n", rn, bytesToAllocate);
117 newBuff = (char *) malloc(bytesToAllocate);
118 if (newBuff == (char *)0) {
119 fprintf(stderr, "[%s] Can't allocate %d bytes for text buffer; errno is %d\n", rn, bytesToAllocate, errno);
120 return((struct gator_textcb_hdr *)0);
123 if (gator_textcb_debug)
124 fprintf(stderr, "[%s] Text buffer allocated at 0x%x\n", rn, newBuff);
125 blankLine = (char *) malloc(a_maxCharsPerEntry + 1);
126 if (blankLine == (char *)0) {
127 fprintf(stderr, "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n", rn, a_maxCharsPerEntry+1, errno);
129 return((struct gator_textcb_hdr *)0);
133 * Next, allocate the entry array.
135 bytesToAllocate = a_maxEntriesStored * sizeof(struct gator_textcb_entry);
136 if (gator_textcb_debug)
137 fprintf(stderr, "[%s] Allocating %d bytes for the %d text entry array items\n", rn, bytesToAllocate, a_maxEntriesStored);
138 newEntries = (struct gator_textcb_entry *) malloc(bytesToAllocate);
139 if (newEntries == (struct gator_textcb_entry *)0) {
140 fprintf(stderr, "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n", rn, bytesToAllocate, a_maxEntriesStored, errno);
143 return((struct gator_textcb_hdr *)0);
146 if (gator_textcb_debug)
147 fprintf(stderr, "[%s] Text buffer entry array allocated at 0x%x\n", rn, newEntries);
150 * Finish off by allocating the text circular buffer header.
152 bytesToAllocate = sizeof(struct gator_textcb_hdr);
153 if (gator_textcb_debug)
154 fprintf(stderr, "[%s] Allocating %d bytes for the text circular buffer header\n", rn, bytesToAllocate);
155 newHdr = (struct gator_textcb_hdr *) malloc(bytesToAllocate);
156 if (newHdr == (struct gator_textcb_hdr *)0) {
157 fprintf(stderr, "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n", rn, bytesToAllocate, errno);
161 return((struct gator_textcb_hdr *)0);
164 if (gator_textcb_debug)
165 fprintf(stderr, "[%s] Text circular buffer header allocated at 0x%x\n", rn, newHdr);
168 * Now, just initialize all the pieces and plug them in.
170 if (gator_textcb_debug)
171 fprintf(stderr, "[%s] Zeroing %d bytes in text buffer at 0x%x\n", rn, numBuffBytes, newBuff);
172 memset(newBuff, 0, numBuffBytes);
174 if (gator_textcb_debug)
175 fprintf(stderr, "[%s] Initializing blank line buffer at 0x%x\n", rn, blankLine);
176 for (i=0; i < a_maxCharsPerEntry; i++)
177 *(blankLine+i) = ' ';
178 *(blankLine+a_maxCharsPerEntry) = '\0';
181 * Initialize each buffer entry.
183 for (curr_ent_num = 0, curr_buff = newBuff, curr_ent = newEntries;
184 curr_ent_num < a_maxEntriesStored;
185 curr_ent++, curr_ent_num++, curr_buff += (a_maxCharsPerEntry+1)) {
186 if (gator_textcb_debug)
187 fprintf(stderr, "[%s] Initializing buffer entry %d; its text buffer address is 0x%x\n", rn, curr_ent_num, curr_buff);
189 curr_ent->highlight = 0;
190 curr_ent->numInversions = 0;
191 curr_ent->charsUsed = 0;
192 curr_ent->textp = curr_buff;
193 memcpy(curr_ent->textp, blankLine, a_maxCharsPerEntry+1);
194 for (curr_inv = 0; curr_inv < GATOR_TEXTCB_MAXINVERSIONS; curr_inv++)
195 curr_ent->inversion[curr_inv] = 0;
197 } /*Init each buffer entry*/
199 if (gator_textcb_debug)
200 fprintf(stderr, "[%s] Filling in circ buff header at 0x%x\n", rn, newHdr);
201 Lock_Init(&(newHdr->cbLock));
202 newHdr->maxEntriesStored = a_maxEntriesStored;
203 newHdr->maxCharsPerEntry = a_maxCharsPerEntry;
205 newHdr->currEntIdx = 0;
206 newHdr->oldestEnt = 0;
207 newHdr->oldestEntIdx = 0;
208 newHdr->entry = newEntries;
209 newHdr->blankLine = blankLine;
212 * Finally, return the location of the new header.
216 } /*gator_textcb_Create*/
218 /*------------------------------------------------------------------------
222 * Move down to the next circular buffer entry.
225 * struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
228 * Ptr to the newest current entry.
231 * Nothing interesting.
235 *------------------------------------------------------------------------*/
237 static struct gator_textcb_entry *bumpEntry(a_cbhdr)
238 struct gator_textcb_hdr *a_cbhdr;
242 static char rn[] = "bumpEntry"; /*Routine name*/
243 struct gator_textcb_entry *curr_ent; /*Ptr to current entry*/
244 int inv; /*Inversion number*/
247 * Bump the total number of writes, and don't forget to advance
248 * the oldest entry, if appropriate.
250 if (gator_textcb_debug)
251 fprintf(stderr, "[%s]: Bumping entry for circular buffer at 0x%x; current values: currEnt=%d (idx %d), oldestEnt=%d (idx %d), maxEntriesStored=%d\n", rn, a_cbhdr, a_cbhdr->currEnt, a_cbhdr->currEntIdx, a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx, a_cbhdr->maxEntriesStored);
254 if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
255 a_cbhdr->currEntIdx = 0;
256 curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
258 if (gator_textcb_debug)
259 fprintf(stderr, "[%s] Zeroing entry %d (idx %d) at 0x%x\n", rn, a_cbhdr->currEnt, a_cbhdr->currEntIdx, curr_ent);
261 curr_ent->ID = a_cbhdr->currEnt;
262 curr_ent->highlight = 0;
263 curr_ent->numInversions = 0;
264 curr_ent->charsUsed = 0;
266 * Copy over a blank line into the one we're initializing. We
267 * copy over the trailing null, too.
269 memcpy(curr_ent->textp, a_cbhdr->blankLine, a_cbhdr->maxCharsPerEntry+1);
270 for (inv=0; inv < GATOR_TEXTCB_MAXINVERSIONS; inv++)
271 curr_ent->inversion[inv] = 0;
274 * If we've already stated circulating in the buffer, remember to
275 * bump the oldest entry info too.
277 if (a_cbhdr->currEnt >= a_cbhdr->maxEntriesStored) {
278 if (gator_textcb_debug)
279 fprintf(stderr, "[%s]: Advancing oldest entry number & index; was entry %d, index %d, now ", rn, a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx);
280 a_cbhdr->oldestEnt++;
281 if (++(a_cbhdr->oldestEntIdx) >= a_cbhdr->maxEntriesStored)
282 a_cbhdr->oldestEntIdx = 0;
283 if (gator_textcb_debug)
284 fprintf(stderr, "entry %d, index %d\n", a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx);
285 } /*Bump oldest entry info*/
288 * Finally, return the address of the newest current entry.
294 /*------------------------------------------------------------------------
298 * Write the given string to the text circular buffer. Line
299 * breaks are caused either by overflowing the current text
300 * line or via explicit '\n's.
303 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
304 * char *a_textToWrite : Ptr to text to insert.
305 * int a_numChars : Number of chars to write.
306 * int a_highlight : Use highlighting?
307 * int a_skip; : Force a skip to the next line?
310 * Zero if successful,
311 * Error value otherwise.
314 * Circular buffer is consistent upon entry, namely the first and
315 * last entry pointers are legal.
319 *------------------------------------------------------------------------*/
321 int gator_textcb_Write(a_cbhdr, a_textToWrite, a_numChars, a_highlight, a_skip)
322 struct gator_textcb_hdr *a_cbhdr;
328 { /*gator_textcb_Write*/
330 static char rn[] = "gator_textcb_Write"; /*Routine name*/
331 struct gator_textcb_entry *curr_ent; /*Ptr to current text entry*/
332 int curr_ent_idx; /*Index of current entry*/
333 int max_chars; /*Max chars per entry*/
334 int chars_to_copy; /*Num chars to copy in*/
335 int effective_highlight; /*Tmp highlight value*/
336 char *dest; /*Destination of char copy*/
339 * Make sure we haven't been passed a bogus buffer, and lock it
342 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
343 fprintf(stderr, "[%s]: Null pointer passed in for circ buff header!! Aborting write operation.\n", rn);
346 ObtainWriteLock(&(a_cbhdr->cbLock));
348 curr_ent_idx = a_cbhdr->currEntIdx;
349 curr_ent = (a_cbhdr->entry) + curr_ent_idx;
350 max_chars = a_cbhdr->maxCharsPerEntry;
351 effective_highlight = curr_ent->highlight;
352 if (curr_ent->numInversions % 2)
353 effective_highlight = (effective_highlight ? 0 : 1);
354 if (gator_textcb_debug)
355 fprintf(stderr, "[%s]: Current entry: %d (at index %d, keeping %d max), effective highlight: %d, located at 0x%x\n", rn, a_cbhdr->currEnt, curr_ent_idx, a_cbhdr->maxEntriesStored, effective_highlight, curr_ent);
357 while (a_numChars > 0) {
359 * There are still characters to stuff into our circular buffer.
361 if (gator_textcb_debug)
362 fprintf(stderr, "[%s]: Top of write loop: %d char(s) left to write.\n", rn, a_numChars);
364 if (curr_ent->charsUsed >= max_chars) {
366 * Bump the entry in the given circular buffer.
368 if (gator_textcb_debug)
369 fprintf(stderr, "[%s]: Entry %d at index %d full, advancing to next one.\n", rn, a_cbhdr->currEnt, curr_ent_idx);
370 curr_ent = bumpEntry(a_cbhdr);
371 curr_ent_idx = a_cbhdr->currEntIdx;
372 if (gator_textcb_debug)
373 fprintf(stderr, "[%s] New CB entry info: currEnt=%d (idx %d), oldestEnt=%d (idx %d), curr entry ptr is 0x%x\n", rn, a_cbhdr->currEnt, a_cbhdr->currEntIdx, a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx, curr_ent);
374 } /*Bump current entry*/
377 * At this point, the current entry has room for at least one more
378 * character, and we have at least one more character to write.
379 * Insert as much from the user text as possible.
381 chars_to_copy = max_chars - curr_ent->charsUsed;
382 if (a_numChars < chars_to_copy)
383 chars_to_copy = a_numChars;
384 dest = curr_ent->textp + curr_ent->charsUsed;
385 if (gator_textcb_debug)
386 fprintf(stderr, "[%s]: Copying %d char(s) into current entry at 0x%x (entry buffer starts at 0x%x)\n", rn, chars_to_copy, dest, curr_ent->textp);
389 * Handle highlighting and inversions.
391 if (curr_ent->charsUsed == 0) {
393 * No chars yet, so this sets the highlight field.
395 effective_highlight = curr_ent->highlight = a_highlight;
397 else if (effective_highlight != a_highlight) {
399 * We need a new inversion, if there's room.
401 if (gator_textcb_debug)
402 fprintf(stderr, "[%s]: Highlight mismatch, recording inversion at char loc %d\n", rn, curr_ent->charsUsed);
403 if (curr_ent->numInversions < GATOR_TEXTCB_MAXINVERSIONS) {
404 effective_highlight = a_highlight;
405 curr_ent->inversion[curr_ent->numInversions] = curr_ent->charsUsed;
406 curr_ent->numInversions++;
409 if (gator_textcb_debug)
410 fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
411 } /*Handle inversion*/
414 * Move the string chunk into its place in the buffer, bump the
415 * number of chars used in the current entry.
417 strncpy(dest, a_textToWrite, chars_to_copy);
418 curr_ent->charsUsed += chars_to_copy;
419 a_textToWrite += chars_to_copy;
420 a_numChars -= chars_to_copy;
422 } /*while (a_numChars > 0)*/
425 * All characters have been copied in. Handle the case where we've
426 * been asked to skip to the next entry, even if there's still room
427 * in the current one.
430 if (gator_textcb_debug)
431 fprintf(stderr, "[%s] Handling request to skip to next entry\n", rn);
432 if (curr_ent->charsUsed > 0)
433 curr_ent = bumpEntry(a_cbhdr);
435 if (gator_textcb_debug)
436 fprintf(stderr, "[%s] Not skipping, we're already on a fresh entry\n", rn);
437 } /*Skip to the next entry*/
440 * We can now unlock the CB and return successfully.
442 ReleaseWriteLock(&(a_cbhdr->cbLock));
445 } /*gator_textcb_Write*/
447 /*------------------------------------------------------------------------
448 * gator_textcb_BlankLine
451 * Write out some number of blank lines to the given circular
455 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
456 * int *a_numBlanks : Num. blank lines to write.
459 * Zero if successful,
460 * Error value otherwise.
463 * Circular buffer is consistent upon entry, namely the first and
464 * last entry pointers are legal.
468 *------------------------------------------------------------------------*/
470 int gator_textcb_BlankLine(a_cbhdr, a_numBlanks)
471 struct gator_textcb_hdr *a_cbhdr;
474 { /*gator_textcb_BlankLine*/
476 static char rn[] = "gator_textcb_BlankLine"; /*Routine name*/
478 if (gator_textcb_debug)
479 fprintf(stderr, "[%s] Putting out %d blank lines to the CB at 0x%x\n",
480 rn, a_numBlanks, a_cbhdr);
482 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
483 if (gator_textcb_debug)
484 fprintf(stderr, "[%s] Null pointer passed for CB hdr!!\n", rn);
488 while (a_numBlanks > 0) {
490 * The bumpEntry routine returns a struct gator_textcb_entry
491 * pointer, but we don't need it here, so we don't assign it.
498 * Return happily and successfully.
502 } /*gator_textcb_Write*/
504 /*------------------------------------------------------------------------
505 * gator_textcb_Delete
508 * Delete the storage used by the given circular buffer, including
512 * struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
513 * circ buffer to reap.
516 * Zero if successful,
517 * Error value otherwise.
520 * We write-lock the buffer before deleting it, which is slightly
521 * silly, since it will stop existing after we're done. At least
522 * we'll make sure nobody is actively writing to it while it's
527 *------------------------------------------------------------------------*/
529 int gator_textcb_Delete(a_cbhdr)
530 struct gator_textcb_hdr *a_cbhdr;
532 { /*gator_textcb_Delete*/
534 static char rn[] = "gator_textcb_Delete"; /*Routine name*/
536 if (gator_textcb_debug)
537 fprintf(stderr, "[%s]: Deleting text circular buffer at 0x%x\n",
539 ObtainWriteLock(&(a_cbhdr->cbLock));
542 * The beginning of the text buffer itself is pointed to by the
545 if (gator_textcb_debug)
546 fprintf(stderr, "[%s]: Freeing text buffer proper at 0x%x (%d bytes)\n",
547 rn, a_cbhdr->entry[0].textp,
548 (a_cbhdr->maxEntriesStored * a_cbhdr->maxCharsPerEntry));
549 free(a_cbhdr->entry[0].textp);
550 a_cbhdr->entry[0].textp = (char *)0;
552 if (gator_textcb_debug)
553 fprintf(stderr, "[%s]: Freeing text entry array at 0x%x (%d bytes)\n",
555 (a_cbhdr->maxEntriesStored * sizeof(struct gator_textcb_entry)));
556 free(a_cbhdr->entry);
557 a_cbhdr->entry = (struct gator_textcb_entry *)0;
558 free(a_cbhdr->blankLine);
559 a_cbhdr->blankLine = (char *)0;
562 * Release the write lock on it, then free the header itself.
564 ReleaseWriteLock(&(a_cbhdr->cbLock));
565 if (gator_textcb_debug)
566 fprintf(stderr, "[%s] Freeing cicular buffer header at 0x%x\n",
571 } /*gator_textcb_Delete*/