3b095e687ad351239c008127397e7adee9f3156f
[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(a_debug)
52      int a_debug;
53
54 {                               /*gator_textcb_Init */
55
56     static int initd;           /*Have we been called already? */
57     static char rn[] = "gator_textcb_Init";     /*Routine name */
58
59     if (initd) {
60         fprintf(stderr,
61                 "[%s] Initialization routine called multiple times!!\n", rn);
62         return (0);
63     } else
64         initd = 1;
65
66     gator_textcb_debug = a_debug;
67     return (0);
68
69 }                               /*gator_textcb_Init */
70
71 /*------------------------------------------------------------------------
72  * gator_textcb_Create
73  *
74  * Description:
75  *      Create a new circular buffer.
76  *
77  * Arguments:
78  *      int a_maxEntriesStored : How many entries should it have?
79  *      int a_maxCharsPerEntry : Max chars in each entry.
80  *
81  * Returns:
82  *      Ptr to the fully-initialized circular buffer hdr if successful,
83  *      Null pointer otherwise.
84  *
85  * Environment:
86  *      Makes sure the lock structure is properly initialized.
87  *
88  * Side Effects:
89  *      As advertised; space is allocated for the circ buff.
90  *------------------------------------------------------------------------*/
91
92 struct gator_textcb_hdr *
93 gator_textcb_Create(a_maxEntriesStored, a_maxCharsPerEntry)
94      int a_maxEntriesStored;
95      int a_maxCharsPerEntry;
96
97 {                               /*gator_textcb_Create */
98
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 */
111
112     /*
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
116      * line buffer.
117      */
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,
122                 bytesToAllocate);
123     newBuff = (char *)malloc(bytesToAllocate);
124     if (newBuff == NULL) {
125         fprintf(stderr,
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) {
133         fprintf(stderr,
134                 "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n",
135                 rn, a_maxCharsPerEntry + 1, errno);
136         free(newBuff);
137         return ((struct gator_textcb_hdr *)0);
138     }
139
140     /*
141      * Next, allocate the entry array.
142      */
143     bytesToAllocate = a_maxEntriesStored * sizeof(struct gator_textcb_entry);
144     if (gator_textcb_debug)
145         fprintf(stderr,
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) {
150         fprintf(stderr,
151                 "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n",
152                 rn, bytesToAllocate, a_maxEntriesStored, errno);
153         free(newBuff);
154         free(blankLine);
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",
158                 rn, newEntries);
159
160     /*
161      * Finish off by allocating the text circular buffer header.
162      */
163     bytesToAllocate = sizeof(struct gator_textcb_hdr);
164     if (gator_textcb_debug)
165         fprintf(stderr,
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) {
170         fprintf(stderr,
171                 "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n",
172                 rn, bytesToAllocate, errno);
173         free(newBuff);
174         free(blankLine);
175         free(newEntries);
176         return ((struct gator_textcb_hdr *)0);
177     } else if (gator_textcb_debug)
178         fprintf(stderr,
179                 "[%s] Text circular buffer header allocated at 0x%x\n", rn,
180                 newHdr);
181
182     /*
183      * Now, just initialize all the pieces and plug them in.
184      */
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);
189
190     if (gator_textcb_debug)
191         fprintf(stderr, "[%s] Initializing blank line buffer at 0x%x\n", rn,
192                 blankLine);
193     for (i = 0; i < a_maxCharsPerEntry; i++)
194         *(blankLine + i) = ' ';
195     *(blankLine + a_maxCharsPerEntry) = '\0';
196
197     /*
198      * Initialize each buffer entry.
199      */
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)
204             fprintf(stderr,
205                     "[%s] Initializing buffer entry %d; its text buffer address is 0x%x\n",
206                     rn, curr_ent_num, curr_buff);
207         curr_ent->ID = 0;
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;
215
216     }                           /*Init each buffer entry */
217
218     if (gator_textcb_debug)
219         fprintf(stderr, "[%s] Filling in circ buff header at 0x%x\n", rn,
220                 newHdr);
221     Lock_Init(&(newHdr->cbLock));
222     newHdr->maxEntriesStored = a_maxEntriesStored;
223     newHdr->maxCharsPerEntry = a_maxCharsPerEntry;
224     newHdr->currEnt = 0;
225     newHdr->currEntIdx = 0;
226     newHdr->oldestEnt = 0;
227     newHdr->oldestEntIdx = 0;
228     newHdr->entry = newEntries;
229     newHdr->blankLine = blankLine;
230
231     /*
232      * Finally, return the location of the new header.
233      */
234     return (newHdr);
235
236 }                               /*gator_textcb_Create */
237
238 /*------------------------------------------------------------------------
239  * bumpCB
240  *
241  * Description:
242  *      Move down to the next circular buffer entry.
243  *
244  * Arguments:
245  *      struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
246  *
247  * Returns:
248  *      Ptr to the newest current entry.
249  *
250  * Environment:
251  *      Nothing interesting.
252  *
253  * Side Effects:
254  *      As advertised.
255  *------------------------------------------------------------------------*/
256
257 static struct gator_textcb_entry *
258 bumpEntry(a_cbhdr)
259      struct gator_textcb_hdr *a_cbhdr;
260
261 {                               /*bumpEntry */
262
263     static char rn[] = "bumpEntry";     /*Routine name */
264     struct gator_textcb_entry *curr_ent;        /*Ptr to current entry */
265     int inv;                    /*Inversion number */
266
267     /*
268      * Bump the total number of writes, and don't forget to advance
269      * the oldest entry, if appropriate.
270      */
271     if (gator_textcb_debug)
272         fprintf(stderr,
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);
277
278     a_cbhdr->currEnt++;
279     if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
280         a_cbhdr->currEntIdx = 0;
281     curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
282
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);
286
287     curr_ent->ID = a_cbhdr->currEnt;
288     curr_ent->highlight = 0;
289     curr_ent->numInversions = 0;
290     curr_ent->charsUsed = 0;
291     /*
292      * Copy over a blank line into the one we're initializing.  We
293      * copy over the trailing null, too.
294      */
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;
299
300     /*
301      * If we've already stated circulating in the buffer, remember to
302      * bump the oldest entry info too.
303      */
304     if (a_cbhdr->currEnt >= a_cbhdr->maxEntriesStored) {
305         if (gator_textcb_debug)
306             fprintf(stderr,
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);
315     }
316
317     /*Bump oldest entry info */
318     /*
319      * Finally, return the address of the newest current entry.
320      */
321     return (curr_ent);
322
323 }                               /*bumpEntry */
324
325 /*------------------------------------------------------------------------
326  * gator_textcb_Write
327  *
328  * Description:
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.
332  *
333  * Arguments:
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?
339  *
340  * Returns:
341  *      Zero if successful,
342  *      Error value otherwise.
343  *
344  * Environment:
345  *      Circular buffer is consistent upon entry, namely the first and
346  *      last entry pointers are legal.
347  *
348  * Side Effects:
349  *      As advertised.
350  *------------------------------------------------------------------------*/
351
352 int
353 gator_textcb_Write(a_cbhdr, a_textToWrite, a_numChars, a_highlight, a_skip)
354      struct gator_textcb_hdr *a_cbhdr;
355      char *a_textToWrite;
356      int a_numChars;
357      int a_highlight;
358      int a_skip;
359
360 {                               /*gator_textcb_Write */
361
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 */
369
370     /*
371      * Make sure we haven't been passed a bogus buffer, and lock it
372      * before we start.
373      */
374     if (a_cbhdr == (struct gator_textcb_hdr *)0) {
375         fprintf(stderr,
376                 "[%s]: Null pointer passed in for circ buff header!!  Aborting write operation.\n",
377                 rn);
378         return (-1);
379     }
380     ObtainWriteLock(&(a_cbhdr->cbLock));
381
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)
389         fprintf(stderr,
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);
393
394     while (a_numChars > 0) {
395         /*
396          * There are still characters to stuff into our circular buffer.
397          */
398         if (gator_textcb_debug)
399             fprintf(stderr,
400                     "[%s]: Top of write loop: %d char(s) left to write.\n",
401                     rn, a_numChars);
402
403         if (curr_ent->charsUsed >= max_chars) {
404             /*
405              * Bump the entry in the given circular buffer.
406              */
407             if (gator_textcb_debug)
408                 fprintf(stderr,
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)
414                 fprintf(stderr,
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);
418         }
419
420         /*Bump current entry */
421         /*
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.
425          */
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)
431             fprintf(stderr,
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);
434
435         /*
436          * Handle highlighting and inversions.
437          */
438         if (curr_ent->charsUsed == 0) {
439             /*
440              * No chars yet, so this sets the highlight field.
441              */
442             effective_highlight = curr_ent->highlight = a_highlight;
443         } else if (effective_highlight != a_highlight) {
444             /*
445              * We need a new inversion, if there's room.
446              */
447             if (gator_textcb_debug)
448                 fprintf(stderr,
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] =
454                     curr_ent->charsUsed;
455                 curr_ent->numInversions++;
456             } else if (gator_textcb_debug)
457                 fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
458         }
459
460         /*Handle inversion */
461         /*
462          * Move the string chunk into its place in the buffer, bump the
463          * number of chars used in the current entry.
464          */
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;
469
470     }                           /*while (a_numChars > 0) */
471
472     /*
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.
476      */
477     if (a_skip) {
478         if (gator_textcb_debug)
479             fprintf(stderr, "[%s] Handling request to skip to next entry\n",
480                     rn);
481         if (curr_ent->charsUsed > 0)
482             curr_ent = bumpEntry(a_cbhdr);
483         else if (gator_textcb_debug)
484             fprintf(stderr,
485                     "[%s] Not skipping, we're already on a fresh entry\n",
486                     rn);
487     }
488
489     /*Skip to the next entry */
490     /*
491      * We can now unlock the CB and return successfully.
492      */
493     ReleaseWriteLock(&(a_cbhdr->cbLock));
494     return (0);
495
496 }                               /*gator_textcb_Write */
497
498 /*------------------------------------------------------------------------
499  * gator_textcb_BlankLine
500  *
501  * Description:
502  *      Write out some number of blank lines to the given circular
503  *      buffer.
504  *
505  * Arguments:
506  *      struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
507  *      int *a_numBlanks                 : Num. blank lines to write.
508  *
509  * Returns:
510  *      Zero if successful,
511  *      Error value otherwise.
512  *
513  * Environment:
514  *      Circular buffer is consistent upon entry, namely the first and
515  *      last entry pointers are legal.
516  *
517  * Side Effects:
518  *      As advertised.
519  *------------------------------------------------------------------------*/
520
521 int
522 gator_textcb_BlankLine(a_cbhdr, a_numBlanks)
523      struct gator_textcb_hdr *a_cbhdr;
524      int a_numBlanks;
525
526 {                               /*gator_textcb_BlankLine */
527
528     static char rn[] = "gator_textcb_BlankLine";        /*Routine name */
529
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);
533
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);
537         return (-1);
538     }
539
540     while (a_numBlanks > 0) {
541         /*
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.
544          */
545         bumpEntry(a_cbhdr);
546         a_numBlanks--;
547     }
548
549     /*
550      * Return happily and successfully.
551      */
552     return (0);
553
554 }                               /*gator_textcb_Write */
555
556 /*------------------------------------------------------------------------
557  * gator_textcb_Delete
558  *
559  * Description:
560  *      Delete the storage used by the given circular buffer, including
561  *      the header itself.
562  *
563  * Arguments:
564  *      struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
565  *                                              circ buffer to reap.
566  *
567  * Returns:
568  *      Zero if successful,
569  *      Error value otherwise.
570  *
571  * Environment:
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
575  *      being deleted.
576  *
577  * Side Effects:
578  *      As advertised.
579  *------------------------------------------------------------------------*/
580
581 int
582 gator_textcb_Delete(a_cbhdr)
583      struct gator_textcb_hdr *a_cbhdr;
584
585 {                               /*gator_textcb_Delete */
586
587     static char rn[] = "gator_textcb_Delete";   /*Routine name */
588
589     if (gator_textcb_debug)
590         fprintf(stderr, "[%s]: Deleting text circular buffer at 0x%x\n", rn,
591                 a_cbhdr);
592     ObtainWriteLock(&(a_cbhdr->cbLock));
593
594     /*
595      * The beginning of the text buffer itself is pointed to by the
596      * first text entry.
597      */
598     if (gator_textcb_debug)
599         fprintf(stderr,
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;
605
606     if (gator_textcb_debug)
607         fprintf(stderr, "[%s]: Freeing text entry array at 0x%x (%d bytes)\n",
608                 rn, a_cbhdr->entry,
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;
615
616     /*
617      * Release the write lock on it, then free the header itself.
618      */
619     ReleaseWriteLock(&(a_cbhdr->cbLock));
620     if (gator_textcb_debug)
621         fprintf(stderr, "[%s] Freeing cicular buffer header at 0x%x\n", rn,
622                 a_cbhdr);
623     free(a_cbhdr);
624     return (0);
625
626 }                               /*gator_textcb_Delete */