Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / gtx / textcb.c
blob0f15d7292fd9eedd006cf424dfcc268eafaf9d8a
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
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
8 */
11 * Implementation of the gator circular buffer package for its scrollable
12 * text object.
14 *------------------------------------------------------------------------*/
16 #include <afsconfig.h>
17 #include <afs/param.h>
20 #include "gtxtextcb.h" /*Module interface */
21 #include <stdio.h> /*Standard I/O stuff */
22 #include <errno.h>
23 #include <string.h>
24 #include <stdlib.h>
26 static int gator_textcb_debug; /*Is debugging output turned on? */
28 /*------------------------------------------------------------------------
29 * gator_textcb_Init
31 * Description:
32 * Initialize the text circular buffer package.
34 * Arguments:
35 * a_debug : Should debugging output be turned on?
37 * Returns:
38 * Zero if successful,
39 * Error value otherwise.
41 * Environment:
42 * MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
44 * Side Effects:
45 * Just remembers if debugging output should be generated.
46 *------------------------------------------------------------------------*/
48 int
49 gator_textcb_Init(int a_debug)
50 { /*gator_textcb_Init */
52 static int initd; /*Have we been called already? */
53 static char rn[] = "gator_textcb_Init"; /*Routine name */
55 if (initd) {
56 fprintf(stderr,
57 "[%s] Initialization routine called multiple times!!\n", rn);
58 return (0);
59 } else
60 initd = 1;
62 gator_textcb_debug = a_debug;
63 return (0);
65 } /*gator_textcb_Init */
67 /*------------------------------------------------------------------------
68 * gator_textcb_Create
70 * Description:
71 * Create a new circular buffer.
73 * Arguments:
74 * int a_maxEntriesStored : How many entries should it have?
75 * int a_maxCharsPerEntry : Max chars in each entry.
77 * Returns:
78 * Ptr to the fully-initialized circular buffer hdr if successful,
79 * Null pointer otherwise.
81 * Environment:
82 * Makes sure the lock structure is properly initialized.
84 * Side Effects:
85 * As advertised; space is allocated for the circ buff.
86 *------------------------------------------------------------------------*/
88 struct gator_textcb_hdr *
89 gator_textcb_Create(int a_maxEntriesStored, int a_maxCharsPerEntry)
90 { /*gator_textcb_Create */
92 static char rn[] = "gator_textcb_Create"; /*Routine name */
93 char *newBuff; /*Ptr to new text buffer */
94 struct gator_textcb_entry *newEntries; /*Ptr to new text entries */
95 struct gator_textcb_hdr *newHdr; /*Ptr to new text hdr */
96 int bytesToAllocate; /*Num bytes to allocate */
97 int numBuffBytes; /*Num bytes in text buffer */
98 int curr_ent_num; /*Current entry number */
99 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
100 char *curr_buff; /*Ptr to current buff pos */
101 int curr_inv; /*Current inversion idx */
102 char *blankLine; /*Ptr to blank line */
103 int i; /*Loop variable */
106 * Start off by allocating the text buffer itself. Don't forget we
107 * need to allocate one more character per line, to make sure we can
108 * always null-terminate them. We also need to allocate the blank
109 * line buffer.
111 numBuffBytes = bytesToAllocate =
112 a_maxEntriesStored * (a_maxCharsPerEntry + 1);
113 if (gator_textcb_debug)
114 fprintf(stderr, "[%s] Allocating %d bytes for the text buffer\n", rn,
115 bytesToAllocate);
116 newBuff = (char *)malloc(bytesToAllocate);
117 if (newBuff == NULL) {
118 fprintf(stderr,
119 "[%s] Can't allocate %d bytes for text buffer; errno is %d\n",
120 rn, bytesToAllocate, errno);
121 return ((struct gator_textcb_hdr *)0);
122 } else if (gator_textcb_debug)
123 fprintf(stderr, "[%s] Text buffer allocated at %p\n", rn, newBuff);
124 blankLine = (char *)malloc(a_maxCharsPerEntry + 1);
125 if (blankLine == NULL) {
126 fprintf(stderr,
127 "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n",
128 rn, a_maxCharsPerEntry + 1, errno);
129 free(newBuff);
130 return ((struct gator_textcb_hdr *)0);
134 * Next, allocate the entry array.
136 bytesToAllocate = a_maxEntriesStored * sizeof(struct gator_textcb_entry);
137 if (gator_textcb_debug)
138 fprintf(stderr,
139 "[%s] Allocating %d bytes for the %d text entry array items\n",
140 rn, bytesToAllocate, a_maxEntriesStored);
141 newEntries = (struct gator_textcb_entry *)malloc(bytesToAllocate);
142 if (newEntries == (struct gator_textcb_entry *)0) {
143 fprintf(stderr,
144 "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n",
145 rn, bytesToAllocate, a_maxEntriesStored, errno);
146 free(newBuff);
147 free(blankLine);
148 return ((struct gator_textcb_hdr *)0);
149 } else if (gator_textcb_debug)
150 fprintf(stderr, "[%s] Text buffer entry array allocated at %p\n",
151 rn, newEntries);
154 * Finish off by allocating the text circular buffer header.
156 bytesToAllocate = sizeof(struct gator_textcb_hdr);
157 if (gator_textcb_debug)
158 fprintf(stderr,
159 "[%s] Allocating %d bytes for the text circular buffer header\n",
160 rn, bytesToAllocate);
161 newHdr = (struct gator_textcb_hdr *)malloc(bytesToAllocate);
162 if (newHdr == (struct gator_textcb_hdr *)0) {
163 fprintf(stderr,
164 "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n",
165 rn, bytesToAllocate, errno);
166 free(newBuff);
167 free(blankLine);
168 free(newEntries);
169 return ((struct gator_textcb_hdr *)0);
170 } else if (gator_textcb_debug)
171 fprintf(stderr,
172 "[%s] Text circular buffer header allocated at %p\n", rn,
173 newHdr);
176 * Now, just initialize all the pieces and plug them in.
178 if (gator_textcb_debug)
179 fprintf(stderr, "[%s] Zeroing %d bytes in text buffer at %p\n", rn,
180 numBuffBytes, newBuff);
181 memset(newBuff, 0, numBuffBytes);
183 if (gator_textcb_debug)
184 fprintf(stderr, "[%s] Initializing blank line buffer at %p\n", rn,
185 blankLine);
186 for (i = 0; i < a_maxCharsPerEntry; i++)
187 *(blankLine + i) = ' ';
188 *(blankLine + a_maxCharsPerEntry) = '\0';
191 * Initialize each buffer entry.
193 for (curr_ent_num = 0, curr_buff = newBuff, curr_ent = newEntries;
194 curr_ent_num < a_maxEntriesStored;
195 curr_ent++, curr_ent_num++, curr_buff += (a_maxCharsPerEntry + 1)) {
196 if (gator_textcb_debug)
197 fprintf(stderr,
198 "[%s] Initializing buffer entry %d; its text buffer address is %p\n",
199 rn, curr_ent_num, curr_buff);
200 curr_ent->ID = 0;
201 curr_ent->highlight = 0;
202 curr_ent->numInversions = 0;
203 curr_ent->charsUsed = 0;
204 curr_ent->textp = curr_buff;
205 memcpy(curr_ent->textp, blankLine, a_maxCharsPerEntry + 1);
206 for (curr_inv = 0; curr_inv < GATOR_TEXTCB_MAXINVERSIONS; curr_inv++)
207 curr_ent->inversion[curr_inv] = 0;
209 } /*Init each buffer entry */
211 if (gator_textcb_debug)
212 fprintf(stderr, "[%s] Filling in circ buff header at %p\n", rn,
213 newHdr);
214 Lock_Init(&(newHdr->cbLock));
215 newHdr->maxEntriesStored = a_maxEntriesStored;
216 newHdr->maxCharsPerEntry = a_maxCharsPerEntry;
217 newHdr->currEnt = 0;
218 newHdr->currEntIdx = 0;
219 newHdr->oldestEnt = 0;
220 newHdr->oldestEntIdx = 0;
221 newHdr->entry = newEntries;
222 newHdr->blankLine = blankLine;
225 * Finally, return the location of the new header.
227 return (newHdr);
229 } /*gator_textcb_Create */
231 /*------------------------------------------------------------------------
232 * bumpCB
234 * Description:
235 * Move down to the next circular buffer entry.
237 * Arguments:
238 * struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
240 * Returns:
241 * Ptr to the newest current entry.
243 * Environment:
244 * Nothing interesting.
246 * Side Effects:
247 * As advertised.
248 *------------------------------------------------------------------------*/
250 static struct gator_textcb_entry *
251 bumpEntry(struct gator_textcb_hdr *a_cbhdr)
253 { /*bumpEntry */
255 static char rn[] = "bumpEntry"; /*Routine name */
256 struct gator_textcb_entry *curr_ent; /*Ptr to current entry */
257 int inv; /*Inversion number */
260 * Bump the total number of writes, and don't forget to advance
261 * the oldest entry, if appropriate.
263 if (gator_textcb_debug)
264 fprintf(stderr,
265 "[%s]: Bumping entry for circular buffer at %p; current values: currEnt=%d (idx %d), oldestEnt=%d (idx %d), maxEntriesStored=%d\n",
266 rn, a_cbhdr, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
267 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx,
268 a_cbhdr->maxEntriesStored);
270 a_cbhdr->currEnt++;
271 if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
272 a_cbhdr->currEntIdx = 0;
273 curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
275 if (gator_textcb_debug)
276 fprintf(stderr, "[%s] Zeroing entry %d (idx %d) at %p\n", rn,
277 a_cbhdr->currEnt, a_cbhdr->currEntIdx, curr_ent);
279 curr_ent->ID = a_cbhdr->currEnt;
280 curr_ent->highlight = 0;
281 curr_ent->numInversions = 0;
282 curr_ent->charsUsed = 0;
284 * Copy over a blank line into the one we're initializing. We
285 * copy over the trailing null, too.
287 memcpy(curr_ent->textp, a_cbhdr->blankLine,
288 a_cbhdr->maxCharsPerEntry + 1);
289 for (inv = 0; inv < GATOR_TEXTCB_MAXINVERSIONS; inv++)
290 curr_ent->inversion[inv] = 0;
293 * If we've already stated circulating in the buffer, remember to
294 * bump the oldest entry info too.
296 if (a_cbhdr->currEnt >= a_cbhdr->maxEntriesStored) {
297 if (gator_textcb_debug)
298 fprintf(stderr,
299 "[%s]: Advancing oldest entry number & index; was entry %d, index %d, now ",
300 rn, a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx);
301 a_cbhdr->oldestEnt++;
302 if (++(a_cbhdr->oldestEntIdx) >= a_cbhdr->maxEntriesStored)
303 a_cbhdr->oldestEntIdx = 0;
304 if (gator_textcb_debug)
305 fprintf(stderr, "entry %d, index %d\n", a_cbhdr->oldestEnt,
306 a_cbhdr->oldestEntIdx);
309 /*Bump oldest entry info */
311 * Finally, return the address of the newest current entry.
313 return (curr_ent);
315 } /*bumpEntry */
317 /*------------------------------------------------------------------------
318 * gator_textcb_Write
320 * Description:
321 * Write the given string to the text circular buffer. Line
322 * breaks are caused either by overflowing the current text
323 * line or via explicit '\n's.
325 * Arguments:
326 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
327 * char *a_textToWrite : Ptr to text to insert.
328 * int a_numChars : Number of chars to write.
329 * int a_highlight : Use highlighting?
330 * int a_skip; : Force a skip to the next line?
332 * Returns:
333 * Zero if successful,
334 * Error value otherwise.
336 * Environment:
337 * Circular buffer is consistent upon entry, namely the first and
338 * last entry pointers are legal.
340 * Side Effects:
341 * As advertised.
342 *------------------------------------------------------------------------*/
345 gator_textcb_Write(struct gator_textcb_hdr *a_cbhdr, char *a_textToWrite,
346 int a_numChars, int a_highlight, int a_skip)
347 { /*gator_textcb_Write */
349 static char rn[] = "gator_textcb_Write"; /*Routine name */
350 struct gator_textcb_entry *curr_ent; /*Ptr to current text entry */
351 int curr_ent_idx; /*Index of current entry */
352 int max_chars; /*Max chars per entry */
353 int chars_to_copy; /*Num chars to copy in */
354 int effective_highlight; /*Tmp highlight value */
355 char *dest; /*Destination of char copy */
358 * Make sure we haven't been passed a bogus buffer, and lock it
359 * before we start.
361 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
362 fprintf(stderr,
363 "[%s]: Null pointer passed in for circ buff header!! Aborting write operation.\n",
364 rn);
365 return (-1);
367 ObtainWriteLock(&(a_cbhdr->cbLock));
369 curr_ent_idx = a_cbhdr->currEntIdx;
370 curr_ent = (a_cbhdr->entry) + curr_ent_idx;
371 max_chars = a_cbhdr->maxCharsPerEntry;
372 effective_highlight = curr_ent->highlight;
373 if (curr_ent->numInversions % 2)
374 effective_highlight = (effective_highlight ? 0 : 1);
375 if (gator_textcb_debug)
376 fprintf(stderr,
377 "[%s]: Current entry: %d (at index %d, keeping %d max), effective highlight: %d, located at %p\n",
378 rn, a_cbhdr->currEnt, curr_ent_idx, a_cbhdr->maxEntriesStored,
379 effective_highlight, curr_ent);
381 while (a_numChars > 0) {
383 * There are still characters to stuff into our circular buffer.
385 if (gator_textcb_debug)
386 fprintf(stderr,
387 "[%s]: Top of write loop: %d char(s) left to write.\n",
388 rn, a_numChars);
390 if (curr_ent->charsUsed >= max_chars) {
392 * Bump the entry in the given circular buffer.
394 if (gator_textcb_debug)
395 fprintf(stderr,
396 "[%s]: Entry %d at index %d full, advancing to next one.\n",
397 rn, a_cbhdr->currEnt, curr_ent_idx);
398 curr_ent = bumpEntry(a_cbhdr);
399 curr_ent_idx = a_cbhdr->currEntIdx;
400 if (gator_textcb_debug)
401 fprintf(stderr,
402 "[%s] New CB entry info: currEnt=%d (idx %d), oldestEnt=%d (idx %d), curr entry ptr is %p\n",
403 rn, a_cbhdr->currEnt, a_cbhdr->currEntIdx,
404 a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx, curr_ent);
407 /*Bump current entry */
409 * At this point, the current entry has room for at least one more
410 * character, and we have at least one more character to write.
411 * Insert as much from the user text as possible.
413 chars_to_copy = max_chars - curr_ent->charsUsed;
414 if (a_numChars < chars_to_copy)
415 chars_to_copy = a_numChars;
416 dest = curr_ent->textp + curr_ent->charsUsed;
417 if (gator_textcb_debug)
418 fprintf(stderr,
419 "[%s]: Copying %d char(s) into current entry at %p (entry buffer starts at %p)\n",
420 rn, chars_to_copy, dest, curr_ent->textp);
423 * Handle highlighting and inversions.
425 if (curr_ent->charsUsed == 0) {
427 * No chars yet, so this sets the highlight field.
429 effective_highlight = curr_ent->highlight = a_highlight;
430 } else if (effective_highlight != a_highlight) {
432 * We need a new inversion, if there's room.
434 if (gator_textcb_debug)
435 fprintf(stderr,
436 "[%s]: Highlight mismatch, recording inversion at char loc %d\n",
437 rn, curr_ent->charsUsed);
438 if (curr_ent->numInversions < GATOR_TEXTCB_MAXINVERSIONS) {
439 effective_highlight = a_highlight;
440 curr_ent->inversion[curr_ent->numInversions] =
441 curr_ent->charsUsed;
442 curr_ent->numInversions++;
443 } else if (gator_textcb_debug)
444 fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
447 /*Handle inversion */
449 * Move the string chunk into its place in the buffer, bump the
450 * number of chars used in the current entry.
452 strncpy(dest, a_textToWrite, chars_to_copy);
453 curr_ent->charsUsed += chars_to_copy;
454 a_textToWrite += chars_to_copy;
455 a_numChars -= chars_to_copy;
457 } /*while (a_numChars > 0) */
460 * All characters have been copied in. Handle the case where we've
461 * been asked to skip to the next entry, even if there's still room
462 * in the current one.
464 if (a_skip) {
465 if (gator_textcb_debug)
466 fprintf(stderr, "[%s] Handling request to skip to next entry\n",
467 rn);
468 if (curr_ent->charsUsed > 0)
469 curr_ent = bumpEntry(a_cbhdr);
470 else if (gator_textcb_debug)
471 fprintf(stderr,
472 "[%s] Not skipping, we're already on a fresh entry\n",
473 rn);
476 /*Skip to the next entry */
478 * We can now unlock the CB and return successfully.
480 ReleaseWriteLock(&(a_cbhdr->cbLock));
481 return (0);
483 } /*gator_textcb_Write */
485 /*------------------------------------------------------------------------
486 * gator_textcb_BlankLine
488 * Description:
489 * Write out some number of blank lines to the given circular
490 * buffer.
492 * Arguments:
493 * struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
494 * int *a_numBlanks : Num. blank lines to write.
496 * Returns:
497 * Zero if successful,
498 * Error value otherwise.
500 * Environment:
501 * Circular buffer is consistent upon entry, namely the first and
502 * last entry pointers are legal.
504 * Side Effects:
505 * As advertised.
506 *------------------------------------------------------------------------*/
509 gator_textcb_BlankLine(struct gator_textcb_hdr *a_cbhdr,
510 int a_numBlanks)
511 { /*gator_textcb_BlankLine */
513 static char rn[] = "gator_textcb_BlankLine"; /*Routine name */
515 if (gator_textcb_debug)
516 fprintf(stderr, "[%s] Putting out %d blank lines to the CB at %p\n",
517 rn, a_numBlanks, a_cbhdr);
519 if (a_cbhdr == (struct gator_textcb_hdr *)0) {
520 if (gator_textcb_debug)
521 fprintf(stderr, "[%s] Null pointer passed for CB hdr!!\n", rn);
522 return (-1);
525 while (a_numBlanks > 0) {
527 * The bumpEntry routine returns a struct gator_textcb_entry
528 * pointer, but we don't need it here, so we don't assign it.
530 bumpEntry(a_cbhdr);
531 a_numBlanks--;
535 * Return happily and successfully.
537 return (0);
539 } /*gator_textcb_Write */
541 /*------------------------------------------------------------------------
542 * gator_textcb_Delete
544 * Description:
545 * Delete the storage used by the given circular buffer, including
546 * the header itself.
548 * Arguments:
549 * struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
550 * circ buffer to reap.
552 * Returns:
553 * Zero if successful,
554 * Error value otherwise.
556 * Environment:
557 * We write-lock the buffer before deleting it, which is slightly
558 * silly, since it will stop existing after we're done. At least
559 * we'll make sure nobody is actively writing to it while it's
560 * being deleted.
562 * Side Effects:
563 * As advertised.
564 *------------------------------------------------------------------------*/
567 gator_textcb_Delete(struct gator_textcb_hdr *a_cbhdr)
568 { /*gator_textcb_Delete */
570 static char rn[] = "gator_textcb_Delete"; /*Routine name */
572 if (gator_textcb_debug)
573 fprintf(stderr, "[%s]: Deleting text circular buffer at %p\n", rn,
574 a_cbhdr);
575 ObtainWriteLock(&(a_cbhdr->cbLock));
578 * The beginning of the text buffer itself is pointed to by the
579 * first text entry.
581 if (gator_textcb_debug)
582 fprintf(stderr,
583 "[%s]: Freeing text buffer proper at %p (%d bytes)\n", rn,
584 a_cbhdr->entry[0].textp,
585 (a_cbhdr->maxEntriesStored * a_cbhdr->maxCharsPerEntry));
586 free(a_cbhdr->entry[0].textp);
587 a_cbhdr->entry[0].textp = NULL;
589 if (gator_textcb_debug)
590 fprintf(stderr, "[%s]: Freeing text entry array at %p (%" AFS_SIZET_FMT " bytes)\n",
591 rn, a_cbhdr->entry,
592 (a_cbhdr->maxEntriesStored *
593 sizeof(struct gator_textcb_entry)));
594 free(a_cbhdr->entry);
595 a_cbhdr->entry = (struct gator_textcb_entry *)0;
596 free(a_cbhdr->blankLine);
597 a_cbhdr->blankLine = NULL;
600 * Release the write lock on it, then free the header itself.
602 ReleaseWriteLock(&(a_cbhdr->cbLock));
603 if (gator_textcb_debug)
604 fprintf(stderr, "[%s] Freeing cicular buffer header at %p\n", rn,
605 a_cbhdr);
606 free(a_cbhdr);
607 return (0);
609 } /*gator_textcb_Delete */