pull-prototypes-to-head-20020821
[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("$Header$");
20
21 #include "gtxtextcb.h"          /*Module interface*/
22 #include <stdio.h>                      /*Standard I/O stuff*/
23 #include <errno.h>
24
25 #ifdef HAVE_STRING_H
26 #include <string.h>
27 #else
28 #ifdef HAVE_STRINGS_H
29 #include <strings.h>
30 #endif
31 #endif
32 #include <stdlib.h>
33
34 static int gator_textcb_debug;          /*Is debugging output turned on?*/
35
36 /*------------------------------------------------------------------------
37  * gator_textcb_Init
38  *
39  * Description:
40  *      Initialize the text circular buffer package.
41  *
42  * Arguments:
43  *      a_debug : Should debugging output be turned on?
44  *
45  * Returns:
46  *      Zero if successful,
47  *      Error value otherwise.
48  *
49  * Environment:
50  *      MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
51  *
52  * Side Effects:
53  *      Just remembers if debugging output should be generated.
54  *------------------------------------------------------------------------*/
55
56 int gator_textcb_Init(a_debug)
57     int a_debug;
58
59 { /*gator_textcb_Init*/
60
61     static int initd;                           /*Have we been called already?*/
62     static char rn[] = "gator_textcb_Init";     /*Routine name*/
63
64     if (initd) {
65       fprintf(stderr, "[%s] Initialization routine called multiple times!!\n", rn);
66       return(0);
67     }
68     else
69       initd = 1;
70
71     gator_textcb_debug = a_debug;
72     return(0);
73
74 } /*gator_textcb_Init*/
75
76 /*------------------------------------------------------------------------
77  * gator_textcb_Create
78  *
79  * Description:
80  *      Create a new circular buffer.
81  *
82  * Arguments:
83  *      int a_maxEntriesStored : How many entries should it have?
84  *      int a_maxCharsPerEntry : Max chars in each entry.
85  *
86  * Returns:
87  *      Ptr to the fully-initialized circular buffer hdr if successful,
88  *      Null pointer otherwise.
89  *
90  * Environment:
91  *      Makes sure the lock structure is properly initialized.
92  *
93  * Side Effects:
94  *      As advertised; space is allocated for the circ buff.
95  *------------------------------------------------------------------------*/
96
97 struct gator_textcb_hdr *gator_textcb_Create(a_maxEntriesStored, a_maxCharsPerEntry)
98     int a_maxEntriesStored;
99     int a_maxCharsPerEntry;
100
101 { /*gator_textcb_Create*/
102
103     static char rn[] = "gator_textcb_Create";   /*Routine name*/
104     char *newBuff;                              /*Ptr to new text buffer*/
105     struct gator_textcb_entry *newEntries;      /*Ptr to new text entries*/
106     struct gator_textcb_hdr *newHdr;            /*Ptr to new text hdr*/
107     int bytesToAllocate;                        /*Num bytes to allocate*/
108     int numBuffBytes;                           /*Num bytes in text buffer*/
109     int curr_ent_num;                           /*Current entry number*/
110     struct gator_textcb_entry *curr_ent;        /*Ptr to current entry*/
111     char *curr_buff;                            /*Ptr to current buff pos*/
112     int curr_inv;                               /*Current inversion idx*/
113     char *blankLine;                            /*Ptr to blank line*/
114     int i;                                      /*Loop variable*/
115
116     /*
117       * Start off by allocating the text buffer itself.  Don't forget we
118       * need to allocate one more character per line, to make sure we can
119       * always null-terminate them.  We also need to allocate the blank
120       * line buffer.
121       */
122     numBuffBytes = bytesToAllocate = a_maxEntriesStored *
123       (a_maxCharsPerEntry + 1);
124     if (gator_textcb_debug)
125       fprintf(stderr, "[%s] Allocating %d bytes for the text buffer\n", rn, bytesToAllocate);
126     newBuff = (char *) malloc(bytesToAllocate);
127     if (newBuff == NULL) {
128       fprintf(stderr, "[%s] Can't allocate %d bytes for text buffer; errno is %d\n", rn, bytesToAllocate, errno);
129       return((struct gator_textcb_hdr *)0);
130     }
131     else
132       if (gator_textcb_debug)
133         fprintf(stderr, "[%s] Text buffer allocated at 0x%x\n", rn, newBuff);
134     blankLine = (char *) malloc(a_maxCharsPerEntry + 1);
135     if (blankLine == NULL) {
136       fprintf(stderr, "[%s] Can't allocate %d bytes for blank line buffer; errno is %d\n", rn, a_maxCharsPerEntry+1, errno);
137       free(newBuff);
138       return((struct gator_textcb_hdr *)0);
139     }
140
141     /*
142       * Next, allocate the entry array.
143       */
144     bytesToAllocate = a_maxEntriesStored * sizeof(struct gator_textcb_entry);
145     if (gator_textcb_debug)
146       fprintf(stderr, "[%s] Allocating %d bytes for the %d text entry array items\n", rn, bytesToAllocate, a_maxEntriesStored);
147     newEntries = (struct gator_textcb_entry *) malloc(bytesToAllocate);
148     if (newEntries == (struct gator_textcb_entry *)0) {
149       fprintf(stderr, "[%s] Can't allocate %d bytes for the %d-member text entry array; errno is %d\n", rn, bytesToAllocate, a_maxEntriesStored, errno);
150       free(newBuff);
151       free(blankLine);
152       return((struct gator_textcb_hdr *)0);
153     }
154     else
155       if (gator_textcb_debug)
156         fprintf(stderr, "[%s] Text buffer entry array allocated at 0x%x\n", rn, newEntries);
157
158     /*
159       * Finish off by allocating the text circular buffer header.
160       */
161     bytesToAllocate = sizeof(struct gator_textcb_hdr);
162     if (gator_textcb_debug)
163       fprintf(stderr, "[%s] Allocating %d bytes for the text circular buffer header\n", rn, bytesToAllocate);
164     newHdr = (struct gator_textcb_hdr *) malloc(bytesToAllocate);
165     if (newHdr == (struct gator_textcb_hdr *)0) {
166       fprintf(stderr, "[%s] Can't allocate %d bytes for text circular buffer header; errno is %d\n", rn, bytesToAllocate, errno);
167       free(newBuff);
168       free(blankLine);
169       free(newEntries);
170       return((struct gator_textcb_hdr *)0);
171     }
172     else
173       if (gator_textcb_debug)
174         fprintf(stderr, "[%s] Text circular buffer header allocated at 0x%x\n", rn, newHdr);
175
176     /*
177       * Now, just initialize all the pieces and plug them in.
178       */
179     if (gator_textcb_debug)
180       fprintf(stderr, "[%s] Zeroing %d bytes in text buffer at 0x%x\n", rn, numBuffBytes, newBuff);
181     memset(newBuff, 0, numBuffBytes);
182
183     if (gator_textcb_debug)
184       fprintf(stderr, "[%s] Initializing blank line buffer at 0x%x\n", rn, blankLine);
185     for (i=0; i < a_maxCharsPerEntry; i++)
186       *(blankLine+i) = ' ';
187     *(blankLine+a_maxCharsPerEntry) = '\0';
188
189     /*
190       * Initialize each buffer entry.
191       */
192     for (curr_ent_num = 0, curr_buff = newBuff, curr_ent = newEntries;
193          curr_ent_num < a_maxEntriesStored;
194          curr_ent++, curr_ent_num++, curr_buff += (a_maxCharsPerEntry+1)) {
195       if (gator_textcb_debug)
196         fprintf(stderr, "[%s] Initializing buffer entry %d; its text buffer address is 0x%x\n", rn, curr_ent_num, curr_buff);
197       curr_ent->ID            = 0;
198       curr_ent->highlight     = 0;
199       curr_ent->numInversions = 0;
200       curr_ent->charsUsed     = 0;
201       curr_ent->textp         = curr_buff;
202       memcpy(curr_ent->textp, blankLine, a_maxCharsPerEntry+1);
203       for (curr_inv = 0; curr_inv < GATOR_TEXTCB_MAXINVERSIONS; curr_inv++)
204            curr_ent->inversion[curr_inv] = 0;
205
206     } /*Init each buffer entry*/
207
208     if (gator_textcb_debug)
209       fprintf(stderr, "[%s] Filling in circ buff header at 0x%x\n", rn, newHdr);
210     Lock_Init(&(newHdr->cbLock));
211     newHdr->maxEntriesStored  = a_maxEntriesStored;
212     newHdr->maxCharsPerEntry  = a_maxCharsPerEntry;
213     newHdr->currEnt           = 0;
214     newHdr->currEntIdx        = 0;
215     newHdr->oldestEnt         = 0;
216     newHdr->oldestEntIdx      = 0;
217     newHdr->entry             = newEntries;
218     newHdr->blankLine         = blankLine;
219
220     /*
221       * Finally, return the location of the new header.
222       */
223     return(newHdr);
224
225 } /*gator_textcb_Create*/
226
227 /*------------------------------------------------------------------------
228  * bumpCB
229  *
230  * Description:
231  *      Move down to the next circular buffer entry.
232  *
233  * Arguments:
234  *      struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
235  *
236  * Returns:
237  *      Ptr to the newest current entry.
238  *
239  * Environment:
240  *      Nothing interesting.
241  *
242  * Side Effects:
243  *      As advertised.
244  *------------------------------------------------------------------------*/
245
246 static struct gator_textcb_entry *bumpEntry(a_cbhdr)
247     struct gator_textcb_hdr *a_cbhdr;
248
249 { /*bumpEntry*/
250
251     static char rn[] = "bumpEntry";             /*Routine name*/
252     struct gator_textcb_entry *curr_ent;        /*Ptr to current entry*/
253     int inv;                                    /*Inversion number*/
254
255     /*
256      * Bump the total number of writes, and don't forget to advance
257      * the oldest entry, if appropriate.
258      */
259     if (gator_textcb_debug)
260           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);
261
262     a_cbhdr->currEnt++;
263     if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
264       a_cbhdr->currEntIdx = 0;
265     curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
266
267     if (gator_textcb_debug)
268       fprintf(stderr, "[%s] Zeroing entry %d (idx %d) at 0x%x\n", rn, a_cbhdr->currEnt, a_cbhdr->currEntIdx, curr_ent);
269     
270     curr_ent->ID            = a_cbhdr->currEnt;
271     curr_ent->highlight     = 0;
272     curr_ent->numInversions = 0;
273     curr_ent->charsUsed     = 0;
274     /*
275       * Copy over a blank line into the one we're initializing.  We
276       * copy over the trailing null, too.
277       */
278     memcpy(curr_ent->textp, a_cbhdr->blankLine, a_cbhdr->maxCharsPerEntry+1);
279     for (inv=0; inv < GATOR_TEXTCB_MAXINVERSIONS; inv++)
280       curr_ent->inversion[inv] = 0;
281
282     /*
283       * If we've already stated circulating in the buffer, remember to
284       * bump the oldest entry info too.
285       */
286     if (a_cbhdr->currEnt >= a_cbhdr->maxEntriesStored) {
287       if (gator_textcb_debug)
288         fprintf(stderr, "[%s]: Advancing oldest entry number & index; was entry %d, index %d, now ", rn, a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx);
289       a_cbhdr->oldestEnt++;
290       if (++(a_cbhdr->oldestEntIdx) >= a_cbhdr->maxEntriesStored)
291         a_cbhdr->oldestEntIdx = 0;
292       if (gator_textcb_debug)
293         fprintf(stderr, "entry %d, index %d\n", a_cbhdr->oldestEnt, a_cbhdr->oldestEntIdx);
294     } /*Bump oldest entry info*/
295
296     /*
297       * Finally, return the address of the newest current entry.
298       */
299     return(curr_ent);
300
301 } /*bumpEntry*/
302
303 /*------------------------------------------------------------------------
304  * gator_textcb_Write
305  *
306  * Description:
307  *      Write the given string to the text circular buffer.  Line
308  *      breaks are caused either by overflowing the current text
309  *      line or via explicit '\n's.
310  *
311  * Arguments:
312  *      struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
313  *      char *a_textToWrite              : Ptr to text to insert.
314  *      int a_numChars                   : Number of chars to write.
315  *      int a_highlight                  : Use highlighting?
316  *      int a_skip;                      : Force a skip to the next line?
317  *
318  * Returns:
319  *      Zero if successful,
320  *      Error value otherwise.
321  *
322  * Environment:
323  *      Circular buffer is consistent upon entry, namely the first and
324  *      last entry pointers are legal.
325  *
326  * Side Effects:
327  *      As advertised.
328  *------------------------------------------------------------------------*/
329
330 int gator_textcb_Write(a_cbhdr, a_textToWrite, a_numChars, a_highlight, a_skip)
331     struct gator_textcb_hdr *a_cbhdr;
332     char *a_textToWrite;
333     int a_numChars;
334     int a_highlight;
335     int a_skip;
336
337 { /*gator_textcb_Write*/
338
339     static char rn[] = "gator_textcb_Write";    /*Routine name*/
340     struct gator_textcb_entry *curr_ent;        /*Ptr to current text entry*/
341     int curr_ent_idx;                           /*Index of current entry*/
342     int max_chars;                              /*Max chars per entry*/
343     int chars_to_copy;                          /*Num chars to copy in*/
344     int effective_highlight;                    /*Tmp highlight value*/
345     char *dest;                                 /*Destination of char copy*/
346
347     /*
348       * Make sure we haven't been passed a bogus buffer, and lock it
349       * before we start.
350       */
351     if (a_cbhdr == (struct gator_textcb_hdr *)0) {
352       fprintf(stderr, "[%s]: Null pointer passed in for circ buff header!!  Aborting write operation.\n", rn);
353       return(-1);
354     }
355     ObtainWriteLock(&(a_cbhdr->cbLock));
356
357     curr_ent_idx        = a_cbhdr->currEntIdx;
358     curr_ent            = (a_cbhdr->entry) + curr_ent_idx;
359     max_chars           = a_cbhdr->maxCharsPerEntry;
360     effective_highlight = curr_ent->highlight;
361     if (curr_ent->numInversions % 2)
362       effective_highlight = (effective_highlight ? 0 : 1);
363     if (gator_textcb_debug)
364       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);
365
366     while (a_numChars > 0) {
367       /*
368         * There are still characters to stuff into our circular buffer.
369         */
370       if (gator_textcb_debug)
371         fprintf(stderr, "[%s]: Top of write loop: %d char(s) left to write.\n", rn, a_numChars);
372
373       if (curr_ent->charsUsed >= max_chars) {
374         /*
375           * Bump the entry in the given circular buffer.
376           */
377         if (gator_textcb_debug)
378           fprintf(stderr, "[%s]: Entry %d at index %d full, advancing to next one.\n", rn, a_cbhdr->currEnt, curr_ent_idx);     
379         curr_ent = bumpEntry(a_cbhdr);
380         curr_ent_idx = a_cbhdr->currEntIdx;
381         if (gator_textcb_debug)
382           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);
383       } /*Bump current entry*/
384
385       /*
386        * At this point, the current entry has room for at least one more
387        * character, and we have at least one more character to write.
388        * Insert as much from the user text as possible.
389        */
390       chars_to_copy = max_chars - curr_ent->charsUsed;
391       if (a_numChars < chars_to_copy)
392         chars_to_copy = a_numChars;
393       dest = curr_ent->textp + curr_ent->charsUsed;
394       if (gator_textcb_debug)
395         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);
396
397       /*
398         * Handle highlighting and inversions.
399         */
400       if (curr_ent->charsUsed == 0) {
401         /*
402           * No chars yet, so this sets the highlight field.
403           */
404         effective_highlight = curr_ent->highlight = a_highlight;
405       }
406       else if (effective_highlight != a_highlight) {
407         /*
408           * We need a new inversion, if there's room.
409           */
410         if (gator_textcb_debug)
411           fprintf(stderr, "[%s]: Highlight mismatch, recording inversion at char loc %d\n", rn, curr_ent->charsUsed);
412         if (curr_ent->numInversions < GATOR_TEXTCB_MAXINVERSIONS) {
413           effective_highlight = a_highlight;
414           curr_ent->inversion[curr_ent->numInversions] = curr_ent->charsUsed;
415           curr_ent->numInversions++;
416         }
417         else
418           if (gator_textcb_debug)
419             fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
420       } /*Handle inversion*/
421
422       /*
423         * Move the string chunk into its place in the buffer, bump the
424         * number of chars used in the current entry.
425         */
426       strncpy(dest, a_textToWrite, chars_to_copy);
427       curr_ent->charsUsed += chars_to_copy;
428       a_textToWrite       += chars_to_copy;
429       a_numChars          -= chars_to_copy;
430
431     } /*while (a_numChars > 0)*/
432
433     /*
434       * All characters have been copied in.  Handle the case where we've
435       * been asked to skip to the next entry, even if there's still room
436       * in the current one.
437       */
438     if (a_skip) {
439       if (gator_textcb_debug)
440         fprintf(stderr, "[%s] Handling request to skip to next entry\n", rn);
441       if (curr_ent->charsUsed > 0)
442         curr_ent = bumpEntry(a_cbhdr);
443       else
444         if (gator_textcb_debug)
445           fprintf(stderr, "[%s] Not skipping, we're already on a fresh entry\n", rn);
446     } /*Skip to the next entry*/
447
448     /*
449       * We can now unlock the CB and return successfully.
450       */
451     ReleaseWriteLock(&(a_cbhdr->cbLock));
452     return(0);
453
454 } /*gator_textcb_Write*/
455
456 /*------------------------------------------------------------------------
457  * gator_textcb_BlankLine
458  *
459  * Description:
460  *      Write out some number of blank lines to the given circular
461  *      buffer.
462  *
463  * Arguments:
464  *      struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
465  *      int *a_numBlanks                 : Num. blank lines to write.
466  *
467  * Returns:
468  *      Zero if successful,
469  *      Error value otherwise.
470  *
471  * Environment:
472  *      Circular buffer is consistent upon entry, namely the first and
473  *      last entry pointers are legal.
474  *
475  * Side Effects:
476  *      As advertised.
477  *------------------------------------------------------------------------*/
478
479 int gator_textcb_BlankLine(a_cbhdr, a_numBlanks)
480     struct gator_textcb_hdr *a_cbhdr;
481     int a_numBlanks;
482
483 { /*gator_textcb_BlankLine*/
484
485     static char rn[] = "gator_textcb_BlankLine"; /*Routine name*/
486
487     if (gator_textcb_debug)
488       fprintf(stderr, "[%s] Putting out %d blank lines to the CB at 0x%x\n",
489               rn, a_numBlanks, a_cbhdr);
490
491     if (a_cbhdr == (struct gator_textcb_hdr *)0) {
492       if (gator_textcb_debug)
493         fprintf(stderr, "[%s] Null pointer passed for CB hdr!!\n", rn);
494       return(-1);
495     }
496
497     while (a_numBlanks > 0) {
498       /*
499         * The bumpEntry routine returns a struct gator_textcb_entry
500         * pointer, but we don't need it here, so we don't assign it.
501         */
502       bumpEntry(a_cbhdr);
503       a_numBlanks--;
504     }
505
506     /*
507       * Return happily and successfully.
508       */
509     return(0);
510
511 } /*gator_textcb_Write*/
512
513 /*------------------------------------------------------------------------
514  * gator_textcb_Delete
515  *
516  * Description:
517  *      Delete the storage used by the given circular buffer, including
518  *      the header itself.
519  *
520  * Arguments:
521  *      struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
522  *                                              circ buffer to reap.
523  *
524  * Returns:
525  *      Zero if successful,
526  *      Error value otherwise.
527  *
528  * Environment:
529  *      We write-lock the buffer before deleting it, which is slightly
530  *      silly, since it will stop existing after we're done.  At least
531  *      we'll make sure nobody is actively writing to it while it's
532  *      being deleted.
533  *
534  * Side Effects:
535  *      As advertised.
536  *------------------------------------------------------------------------*/
537
538 int gator_textcb_Delete(a_cbhdr)
539     struct gator_textcb_hdr *a_cbhdr;
540
541 { /*gator_textcb_Delete*/
542
543     static char rn[] = "gator_textcb_Delete";   /*Routine name*/
544
545     if (gator_textcb_debug)
546       fprintf(stderr, "[%s]: Deleting text circular buffer at 0x%x\n",
547               rn, a_cbhdr);
548     ObtainWriteLock(&(a_cbhdr->cbLock));
549
550     /*
551       * The beginning of the text buffer itself is pointed to by the
552       * first text entry.
553       */
554     if (gator_textcb_debug)
555       fprintf(stderr, "[%s]: Freeing text buffer proper at 0x%x (%d bytes)\n",
556               rn, a_cbhdr->entry[0].textp,
557               (a_cbhdr->maxEntriesStored * a_cbhdr->maxCharsPerEntry));
558     free(a_cbhdr->entry[0].textp);
559     a_cbhdr->entry[0].textp = NULL;
560
561     if (gator_textcb_debug)
562       fprintf(stderr, "[%s]: Freeing text entry array at 0x%x (%d bytes)\n",
563               rn, a_cbhdr->entry,
564               (a_cbhdr->maxEntriesStored * sizeof(struct gator_textcb_entry)));
565     free(a_cbhdr->entry);
566     a_cbhdr->entry = (struct gator_textcb_entry *)0;
567     free(a_cbhdr->blankLine);
568     a_cbhdr->blankLine = NULL;
569
570     /*
571       * Release the write lock on it, then free the header itself.
572       */
573     ReleaseWriteLock(&(a_cbhdr->cbLock));
574     if (gator_textcb_debug)
575       fprintf(stderr, "[%s] Freeing cicular buffer header at 0x%x\n",
576               rn, a_cbhdr);
577     free(a_cbhdr);
578     return(0);
579
580 } /*gator_textcb_Delete*/