afsconfig-and-rcsid-all-around-20010705
[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 <afs/param.h>
17 #include <afsconfig.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 static int gator_textcb_debug;          /*Is debugging output turned on?*/
26
27 /*------------------------------------------------------------------------
28  * gator_textcb_Init
29  *
30  * Description:
31  *      Initialize the text circular buffer package.
32  *
33  * Arguments:
34  *      a_debug : Should debugging output be turned on?
35  *
36  * Returns:
37  *      Zero if successful,
38  *      Error value otherwise.
39  *
40  * Environment:
41  *      MUST BE THE FIRST ROUTINE CALLED FROM THIS PACKAGE.
42  *
43  * Side Effects:
44  *      Just remembers if debugging output should be generated.
45  *------------------------------------------------------------------------*/
46
47 int gator_textcb_Init(a_debug)
48     int a_debug;
49
50 { /*gator_textcb_Init*/
51
52     static int initd;                           /*Have we been called already?*/
53     static char rn[] = "gator_textcb_Init";     /*Routine name*/
54
55     if (initd) {
56       fprintf(stderr, "[%s] Initialization routine called multiple times!!\n", rn);
57       return(0);
58     }
59     else
60       initd = 1;
61
62     gator_textcb_debug = a_debug;
63     return(0);
64
65 } /*gator_textcb_Init*/
66
67 /*------------------------------------------------------------------------
68  * gator_textcb_Create
69  *
70  * Description:
71  *      Create a new circular buffer.
72  *
73  * Arguments:
74  *      int a_maxEntriesStored : How many entries should it have?
75  *      int a_maxCharsPerEntry : Max chars in each entry.
76  *
77  * Returns:
78  *      Ptr to the fully-initialized circular buffer hdr if successful,
79  *      Null pointer otherwise.
80  *
81  * Environment:
82  *      Makes sure the lock structure is properly initialized.
83  *
84  * Side Effects:
85  *      As advertised; space is allocated for the circ buff.
86  *------------------------------------------------------------------------*/
87
88 struct gator_textcb_hdr *gator_textcb_Create(a_maxEntriesStored, a_maxCharsPerEntry)
89     int a_maxEntriesStored;
90     int a_maxCharsPerEntry;
91
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 = 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);
121     }
122     else
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);
128       free(newBuff);
129       return((struct gator_textcb_hdr *)0);
130     }
131
132     /*
133       * Next, allocate the entry array.
134       */
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);
141       free(newBuff);
142       free(blankLine);
143       return((struct gator_textcb_hdr *)0);
144     }
145     else
146       if (gator_textcb_debug)
147         fprintf(stderr, "[%s] Text buffer entry array allocated at 0x%x\n", rn, newEntries);
148
149     /*
150       * Finish off by allocating the text circular buffer header.
151       */
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);
158       free(newBuff);
159       free(blankLine);
160       free(newEntries);
161       return((struct gator_textcb_hdr *)0);
162     }
163     else
164       if (gator_textcb_debug)
165         fprintf(stderr, "[%s] Text circular buffer header allocated at 0x%x\n", rn, newHdr);
166
167     /*
168       * Now, just initialize all the pieces and plug them in.
169       */
170     if (gator_textcb_debug)
171       fprintf(stderr, "[%s] Zeroing %d bytes in text buffer at 0x%x\n", rn, numBuffBytes, newBuff);
172     bzero(newBuff, numBuffBytes);
173
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';
179
180     /*
181       * Initialize each buffer entry.
182       */
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);
188       curr_ent->ID            = 0;
189       curr_ent->highlight     = 0;
190       curr_ent->numInversions = 0;
191       curr_ent->charsUsed     = 0;
192       curr_ent->textp         = curr_buff;
193       bcopy(blankLine, curr_ent->textp, a_maxCharsPerEntry+1);
194       for (curr_inv = 0; curr_inv < GATOR_TEXTCB_MAXINVERSIONS; curr_inv++)
195            curr_ent->inversion[curr_inv] = 0;
196
197     } /*Init each buffer entry*/
198
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;
204     newHdr->currEnt           = 0;
205     newHdr->currEntIdx        = 0;
206     newHdr->oldestEnt         = 0;
207     newHdr->oldestEntIdx      = 0;
208     newHdr->entry             = newEntries;
209     newHdr->blankLine         = blankLine;
210
211     /*
212       * Finally, return the location of the new header.
213       */
214     return(newHdr);
215
216 } /*gator_textcb_Create*/
217
218 /*------------------------------------------------------------------------
219  * bumpCB
220  *
221  * Description:
222  *      Move down to the next circular buffer entry.
223  *
224  * Arguments:
225  *      struct gator_textcb_hdr *a_cbhdr : Circ buff header to bump.
226  *
227  * Returns:
228  *      Ptr to the newest current entry.
229  *
230  * Environment:
231  *      Nothing interesting.
232  *
233  * Side Effects:
234  *      As advertised.
235  *------------------------------------------------------------------------*/
236
237 static struct gator_textcb_entry *bumpEntry(a_cbhdr)
238     struct gator_textcb_hdr *a_cbhdr;
239
240 { /*bumpEntry*/
241
242     static char rn[] = "bumpEntry";             /*Routine name*/
243     struct gator_textcb_entry *curr_ent;        /*Ptr to current entry*/
244     int inv;                                    /*Inversion number*/
245
246     /*
247      * Bump the total number of writes, and don't forget to advance
248      * the oldest entry, if appropriate.
249      */
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);
252
253     a_cbhdr->currEnt++;
254     if (++(a_cbhdr->currEntIdx) >= a_cbhdr->maxEntriesStored)
255       a_cbhdr->currEntIdx = 0;
256     curr_ent = a_cbhdr->entry + a_cbhdr->currEntIdx;
257
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);
260     
261     curr_ent->ID            = a_cbhdr->currEnt;
262     curr_ent->highlight     = 0;
263     curr_ent->numInversions = 0;
264     curr_ent->charsUsed     = 0;
265     /*
266       * Copy over a blank line into the one we're initializing.  We
267       * copy over the trailing null, too.
268       */
269     bcopy(a_cbhdr->blankLine, curr_ent->textp, a_cbhdr->maxCharsPerEntry+1);
270     for (inv=0; inv < GATOR_TEXTCB_MAXINVERSIONS; inv++)
271       curr_ent->inversion[inv] = 0;
272
273     /*
274       * If we've already stated circulating in the buffer, remember to
275       * bump the oldest entry info too.
276       */
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*/
286
287     /*
288       * Finally, return the address of the newest current entry.
289       */
290     return(curr_ent);
291
292 } /*bumpEntry*/
293
294 /*------------------------------------------------------------------------
295  * gator_textcb_Write
296  *
297  * Description:
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.
301  *
302  * Arguments:
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?
308  *
309  * Returns:
310  *      Zero if successful,
311  *      Error value otherwise.
312  *
313  * Environment:
314  *      Circular buffer is consistent upon entry, namely the first and
315  *      last entry pointers are legal.
316  *
317  * Side Effects:
318  *      As advertised.
319  *------------------------------------------------------------------------*/
320
321 int gator_textcb_Write(a_cbhdr, a_textToWrite, a_numChars, a_highlight, a_skip)
322     struct gator_textcb_hdr *a_cbhdr;
323     char *a_textToWrite;
324     int a_numChars;
325     int a_highlight;
326     int a_skip;
327
328 { /*gator_textcb_Write*/
329
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*/
337
338     /*
339       * Make sure we haven't been passed a bogus buffer, and lock it
340       * before we start.
341       */
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);
344       return(-1);
345     }
346     ObtainWriteLock(&(a_cbhdr->cbLock));
347
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);
356
357     while (a_numChars > 0) {
358       /*
359         * There are still characters to stuff into our circular buffer.
360         */
361       if (gator_textcb_debug)
362         fprintf(stderr, "[%s]: Top of write loop: %d char(s) left to write.\n", rn, a_numChars);
363
364       if (curr_ent->charsUsed >= max_chars) {
365         /*
366           * Bump the entry in the given circular buffer.
367           */
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*/
375
376       /*
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.
380        */
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);
387
388       /*
389         * Handle highlighting and inversions.
390         */
391       if (curr_ent->charsUsed == 0) {
392         /*
393           * No chars yet, so this sets the highlight field.
394           */
395         effective_highlight = curr_ent->highlight = a_highlight;
396       }
397       else if (effective_highlight != a_highlight) {
398         /*
399           * We need a new inversion, if there's room.
400           */
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++;
407         }
408         else
409           if (gator_textcb_debug)
410             fprintf(stderr, "[%s]: Out of inversions!!\n", rn);
411       } /*Handle inversion*/
412
413       /*
414         * Move the string chunk into its place in the buffer, bump the
415         * number of chars used in the current entry.
416         */
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;
421
422     } /*while (a_numChars > 0)*/
423
424     /*
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.
428       */
429     if (a_skip) {
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);
434       else
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*/
438
439     /*
440       * We can now unlock the CB and return successfully.
441       */
442     ReleaseWriteLock(&(a_cbhdr->cbLock));
443     return(0);
444
445 } /*gator_textcb_Write*/
446
447 /*------------------------------------------------------------------------
448  * gator_textcb_BlankLine
449  *
450  * Description:
451  *      Write out some number of blank lines to the given circular
452  *      buffer.
453  *
454  * Arguments:
455  *      struct gator_textcb_hdr *a_cbhdr : Ptr to circ buff hdr.
456  *      int *a_numBlanks                 : Num. blank lines to write.
457  *
458  * Returns:
459  *      Zero if successful,
460  *      Error value otherwise.
461  *
462  * Environment:
463  *      Circular buffer is consistent upon entry, namely the first and
464  *      last entry pointers are legal.
465  *
466  * Side Effects:
467  *      As advertised.
468  *------------------------------------------------------------------------*/
469
470 int gator_textcb_BlankLine(a_cbhdr, a_numBlanks)
471     struct gator_textcb_hdr *a_cbhdr;
472     int a_numBlanks;
473
474 { /*gator_textcb_BlankLine*/
475
476     static char rn[] = "gator_textcb_BlankLine"; /*Routine name*/
477
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);
481
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);
485       return(-1);
486     }
487
488     while (a_numBlanks > 0) {
489       /*
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.
492         */
493       bumpEntry(a_cbhdr);
494       a_numBlanks--;
495     }
496
497     /*
498       * Return happily and successfully.
499       */
500     return(0);
501
502 } /*gator_textcb_Write*/
503
504 /*------------------------------------------------------------------------
505  * gator_textcb_Delete
506  *
507  * Description:
508  *      Delete the storage used by the given circular buffer, including
509  *      the header itself.
510  *
511  * Arguments:
512  *      struct gator_textcb_hdr *a_cbhdr : Ptr to the header of the
513  *                                              circ buffer to reap.
514  *
515  * Returns:
516  *      Zero if successful,
517  *      Error value otherwise.
518  *
519  * Environment:
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
523  *      being deleted.
524  *
525  * Side Effects:
526  *      As advertised.
527  *------------------------------------------------------------------------*/
528
529 int gator_textcb_Delete(a_cbhdr)
530     struct gator_textcb_hdr *a_cbhdr;
531
532 { /*gator_textcb_Delete*/
533
534     static char rn[] = "gator_textcb_Delete";   /*Routine name*/
535
536     if (gator_textcb_debug)
537       fprintf(stderr, "[%s]: Deleting text circular buffer at 0x%x\n",
538               rn, a_cbhdr);
539     ObtainWriteLock(&(a_cbhdr->cbLock));
540
541     /*
542       * The beginning of the text buffer itself is pointed to by the
543       * first text entry.
544       */
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;
551
552     if (gator_textcb_debug)
553       fprintf(stderr, "[%s]: Freeing text entry array at 0x%x (%d bytes)\n",
554               rn, a_cbhdr->entry,
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;
560
561     /*
562       * Release the write lock on it, then free the header itself.
563       */
564     ReleaseWriteLock(&(a_cbhdr->cbLock));
565     if (gator_textcb_debug)
566       fprintf(stderr, "[%s] Freeing cicular buffer header at 0x%x\n",
567               rn, a_cbhdr);
568     free(a_cbhdr);
569     return(0);
570
571 } /*gator_textcb_Delete*/