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