gtx-prototypes-20090316
[openafs.git] / src / gtx / textcb.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
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  */
9
10 /*
11  * Implementation of the gator circular buffer package for its scrollable
12  * text object.
13  *
14  *------------------------------------------------------------------------*/
15
16 #include <afsconfig.h>
17 #include <afs/param.h>
18
19 RCSID
20     ("$Header$");
21
22 #include "gtxtextcb.h"          /*Module interface */
23 #include <stdio.h>              /*Standard I/O stuff */
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27
28 static int gator_textcb_debug;  /*Is debugging output turned on? */
29
30 /*------------------------------------------------------------------------
31  * gator_textcb_Init
32  *
33  * Description:
34  *      Initialize the text circular buffer package.
35  *
36  * Arguments:
37  *      a_debug : Should debugging output be turned on?
38  *
39  * Returns:
40  *      Zero if successful,
41  *      Error value otherwise.
42  *
43  * Environment:
44  *      MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
45  *
46  * Side Effects:
47  *      Just remembers if debugging output should be generated.
48  *------------------------------------------------------------------------*/
49
50 int
51 gator_textcb_Init(int a_debug)
52 {                               /*gator_textcb_Init */
53
54     static int initd;           /*Have we been called already? */
55     static char rn[] = "gator_textcb_Init";     /*Routine name */
56
57     if (initd) {
58         fprintf(stderr,
59                 "[%s] Initialization routine called multiple times!!\n", rn);
60         return (0);
61     } else
62         initd = 1;
63
64     gator_textcb_debug = a_debug;
65     return (0);
66
67 }                               /*gator_textcb_Init */
68
69 /*------------------------------------------------------------------------
70  * gator_textcb_Create
71  *
72  * Description:
73  *      Create a new circular buffer.
74  *
75  * Arguments:
76  *      int a_maxEntriesStored : How many entries should it have?
77  *      int a_maxCharsPerEntry : Max chars in each entry.
78  *
79  * Returns:
80  *      Ptr to the fully-initialized circular buffer hdr if successful,
81  *      Null pointer otherwise.
82  *
83  * Environment:
84  *      Makes sure the lock structure is properly initialized.
85  *
86  * Side Effects:
87  *      As advertised; space is allocated for the circ buff.
88  *------------------------------------------------------------------------*/
89
90 struct gator_textcb_hdr *
91 gator_textcb_Create(int a_maxEntriesStored, int a_maxCharsPerEntry)
92 {                               /*gator_textcb_Create */
93
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 */
106
107     /*
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
111      * line buffer.
112      */
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,
117                 bytesToAllocate);
118     newBuff = (char *)malloc(bytesToAllocate);
119     if (newBuff == NULL) {
120         fprintf(stderr,
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) {
128         fprintf(stderr,
129                 "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n",
130                 rn, a_maxCharsPerEntry + 1, errno);
131         free(newBuff);
132         return ((struct gator_textcb_hdr *)0);
133     }
134
135     /*
136      * Next, allocate the entry array.
137      */
138     bytesToAllocate = a_maxEntriesStored * sizeof(struct gator_textcb_entry);
139     if (gator_textcb_debug)
140         fprintf(stderr,
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) {
145         fprintf(stderr,
146                 "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n",
147                 rn, bytesToAllocate, a_maxEntriesStored, errno);
148         free(newBuff);
149         free(blankLine);
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",
153                 rn, newEntries);
154
155     /*
156      * Finish off by allocating the text circular buffer header.
157      */
158     bytesToAllocate = sizeof(struct gator_textcb_hdr);
159     if (gator_textcb_debug)
160         fprintf(stderr,
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) {
165         fprintf(stderr,
166                 "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n",
167                 rn, bytesToAllocate, errno);
168         free(newBuff);
169         free(blankLine);
170         free(newEntries);
171         return ((struct gator_textcb_hdr *)0);
172     } else if (gator_textcb_debug)
173         fprintf(stderr,
174                 "[%s] Text circular buffer header allocated at %p\n", rn,
175                 newHdr);
176
177     /*
178      * Now, just initialize all the pieces and plug them in.
179      */
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);
184
185     if (gator_textcb_debug)
186         fprintf(stderr, "[%s] Initializing blank line buffer at %p\n", rn,
187                 blankLine);
188     for (i = 0; i < a_maxCharsPerEntry; i++)
189         *(blankLine + i) = ' ';
190     *(blankLine + a_maxCharsPerEntry) = '\0';
191
192     /*
193      * Initialize each buffer entry.
194      */
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)
199             fprintf(stderr,
200                     "[%s] Initializing buffer entry %d; its text buffer address is %p\n",
201                     rn, curr_ent_num, curr_buff);
202         curr_ent->ID = 0;
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;
210
211     }                           /*Init each buffer entry */
212
213     if (gator_textcb_debug)
214         fprintf(stderr, "[%s] Filling in circ buff header at %p\n", rn,
215                 newHdr);
216     Lock_Init(&(newHdr->cbLock));
217     newHdr->maxEntriesStored = a_maxEntriesStored;
218     newHdr->maxCharsPerEntry = a_maxCharsPerEntry;
219     newHdr->currEnt = 0;
220     newHdr->currEntIdx = 0;
221     newHdr->oldestEnt = 0;
222     newHdr->oldestEntIdx = 0;
223     newHdr->entry = newEntries;
224     newHdr->blankLine = blankLine;
225
226     /*
227      * Finally, return the location of the new header.
228      */
229     return (newHdr);
230
231 }                               /*gator_textcb_Create */
232
233 /*------------------------------------------------------------------------
234  * bumpCB
235  *
236  * Description:
237  *      Move down to the next circular buffer entry.
238  *
239  * Arguments:
240  *      struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
241  *
242  * Returns:
243  *      Ptr to the newest current entry.
244  *
245  * Environment:
246  *      Nothing interesting.
247  *
248  * Side Effects:
249  *      As advertised.
250  *------------------------------------------------------------------------*/
251
252 static struct gator_textcb_entry *
253 bumpEntry(struct gator_textcb_hdr *a_cbhdr)
254
255 {                               /*bumpEntry */
256
257     static char rn[] = "bumpEntry";     /*Routine name */
258     struct gator_textcb_entry *curr_ent;        /*Ptr to current entry */
259     int inv;                    /*Inversion number */
260
261     /*
262      * Bump the total number of writes, and don't forget to advance
263      * the oldest entry, if appropriate.
264      */
265     if (gator_textcb_debug)
266         fprintf(stderr,
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);
271
272     a_cbhdr->currEnt++;
273     if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
274         a_cbhdr->currEntIdx = 0;
275     curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
276
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);
280
281     curr_ent->ID = a_cbhdr->currEnt;
282     curr_ent->highlight = 0;
283     curr_ent->numInversions = 0;
284     curr_ent->charsUsed = 0;
285     /*
286      * Copy over a blank line into the one we're initializing.  We
287      * copy over the trailing null, too.
288      */
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;
293
294     /*
295      * If we've already stated circulating in the buffer, remember to
296      * bump the oldest entry info too.
297      */
298     if (a_cbhdr->currEnt >= a_cbhdr->maxEntriesStored) {
299         if (gator_textcb_debug)
300             fprintf(stderr,
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);
309     }
310
311     /*Bump oldest entry info */
312     /*
313      * Finally, return the address of the newest current entry.
314      */
315     return (curr_ent);
316
317 }                               /*bumpEntry */
318
319 /*------------------------------------------------------------------------
320  * gator_textcb_Write
321  *
322  * Description:
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.
326  *
327  * Arguments:
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?
333  *
334  * Returns:
335  *      Zero if successful,
336  *      Error value otherwise.
337  *
338  * Environment:
339  *      Circular buffer is consistent upon entry, namely the first and
340  *      last entry pointers are legal.
341  *
342  * Side Effects:
343  *      As advertised.
344  *------------------------------------------------------------------------*/
345
346 int
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 */
350
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 */
358
359     /*
360      * Make sure we haven't been passed a bogus buffer, and lock it
361      * before we start.
362      */
363     if (a_cbhdr == (struct gator_textcb_hdr *)0) {
364         fprintf(stderr,
365                 "[%s]: Null pointer passed in for circ buff header!!  Aborting write operation.\n",
366                 rn);
367         return (-1);
368     }
369     ObtainWriteLock(&(a_cbhdr->cbLock));
370
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)
378         fprintf(stderr,
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);
382
383     while (a_numChars > 0) {
384         /*
385          * There are still characters to stuff into our circular buffer.
386          */
387         if (gator_textcb_debug)
388             fprintf(stderr,
389                     "[%s]: Top of write loop: %d char(s) left to write.\n",
390                     rn, a_numChars);
391
392         if (curr_ent->charsUsed >= max_chars) {
393             /*
394              * Bump the entry in the given circular buffer.
395              */
396             if (gator_textcb_debug)
397                 fprintf(stderr,
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)
403                 fprintf(stderr,
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);
407         }
408
409         /*Bump current entry */
410         /*
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.
414          */
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)
420             fprintf(stderr,
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);
423
424         /*
425          * Handle highlighting and inversions.
426          */
427         if (curr_ent->charsUsed == 0) {
428             /*
429              * No chars yet, so this sets the highlight field.
430              */
431             effective_highlight = curr_ent->highlight = a_highlight;
432         } else if (effective_highlight != a_highlight) {
433             /*
434              * We need a new inversion, if there's room.
435              */
436             if (gator_textcb_debug)
437                 fprintf(stderr,
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] =
443                     curr_ent->charsUsed;
444                 curr_ent->numInversions++;
445             } else if (gator_textcb_debug)
446                 fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
447         }
448
449         /*Handle inversion */
450         /*
451          * Move the string chunk into its place in the buffer, bump the
452          * number of chars used in the current entry.
453          */
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;
458
459     }                           /*while (a_numChars > 0) */
460
461     /*
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.
465      */
466     if (a_skip) {
467         if (gator_textcb_debug)
468             fprintf(stderr, "[%s] Handling request to skip to next entry\n",
469                     rn);
470         if (curr_ent->charsUsed > 0)
471             curr_ent = bumpEntry(a_cbhdr);
472         else if (gator_textcb_debug)
473             fprintf(stderr,
474                     "[%s] Not skipping, we're already on a fresh entry\n",
475                     rn);
476     }
477
478     /*Skip to the next entry */
479     /*
480      * We can now unlock the CB and return successfully.
481      */
482     ReleaseWriteLock(&(a_cbhdr->cbLock));
483     return (0);
484
485 }                               /*gator_textcb_Write */
486
487 /*------------------------------------------------------------------------
488  * gator_textcb_BlankLine
489  *
490  * Description:
491  *      Write out some number of blank lines to the given circular
492  *      buffer.
493  *
494  * Arguments:
495  *      struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
496  *      int *a_numBlanks                 : Num. blank lines to write.
497  *
498  * Returns:
499  *      Zero if successful,
500  *      Error value otherwise.
501  *
502  * Environment:
503  *      Circular buffer is consistent upon entry, namely the first and
504  *      last entry pointers are legal.
505  *
506  * Side Effects:
507  *      As advertised.
508  *------------------------------------------------------------------------*/
509
510 int
511 gator_textcb_BlankLine(struct gator_textcb_hdr *a_cbhdr, 
512                        int a_numBlanks)
513 {                               /*gator_textcb_BlankLine */
514
515     static char rn[] = "gator_textcb_BlankLine";        /*Routine name */
516
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);
520
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);
524         return (-1);
525     }
526
527     while (a_numBlanks > 0) {
528         /*
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.
531          */
532         bumpEntry(a_cbhdr);
533         a_numBlanks--;
534     }
535
536     /*
537      * Return happily and successfully.
538      */
539     return (0);
540
541 }                               /*gator_textcb_Write */
542
543 /*------------------------------------------------------------------------
544  * gator_textcb_Delete
545  *
546  * Description:
547  *      Delete the storage used by the given circular buffer, including
548  *      the header itself.
549  *
550  * Arguments:
551  *      struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
552  *                                              circ buffer to reap.
553  *
554  * Returns:
555  *      Zero if successful,
556  *      Error value otherwise.
557  *
558  * Environment:
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
562  *      being deleted.
563  *
564  * Side Effects:
565  *      As advertised.
566  *------------------------------------------------------------------------*/
567
568 int
569 gator_textcb_Delete(struct gator_textcb_hdr *a_cbhdr)
570 {                               /*gator_textcb_Delete */
571
572     static char rn[] = "gator_textcb_Delete";   /*Routine name */
573
574     if (gator_textcb_debug)
575         fprintf(stderr, "[%s]: Deleting text circular buffer at %p\n", rn,
576                 a_cbhdr);
577     ObtainWriteLock(&(a_cbhdr->cbLock));
578
579     /*
580      * The beginning of the text buffer itself is pointed to by the
581      * first text entry.
582      */
583     if (gator_textcb_debug)
584         fprintf(stderr,
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;
590
591     if (gator_textcb_debug)
592         fprintf(stderr, "[%s]: Freeing text entry array at %p (%lu bytes)\n",
593                 rn, a_cbhdr->entry,
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;
600
601     /*
602      * Release the write lock on it, then free the header itself.
603      */
604     ReleaseWriteLock(&(a_cbhdr->cbLock));
605     if (gator_textcb_debug)
606         fprintf(stderr, "[%s] Freeing cicular buffer header at %p\n", rn,
607                 a_cbhdr);
608     free(a_cbhdr);
609     return (0);
610
611 }                               /*gator_textcb_Delete */