To reference one element of a C-string use []
[openafs.git] / src / gtx / textobject.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  * Description:
12  *      Implementation of the gator text object.
13  *------------------------------------------------------------------------*/
14 #define IGNORE_STDS_H
15 #include <afsconfig.h>
16 #include <afs/param.h>
17
18
19 #include "gtxtextobj.h"         /*Interface for this module */
20 #include "gtxwindows.h"         /*Gator window interface */
21 #include "gtxcurseswin.h"       /*Gator curses window interface */
22 #include "gtxdumbwin.h"         /*Gator dumb terminal window interface */
23 #include "gtxX11win.h"          /*Gator X11 window interface */
24 #include <stdio.h>              /*Standard I/O stuff */
25 #include <errno.h>
26 #include <string.h>
27 #include <stdlib.h>
28
29 /*Externally-advertised array of text onode operations*/
30 struct onodeops gator_text_ops = {
31     gator_text_destroy,
32     gator_text_display,
33     gator_text_release
34 };
35
36 static char mn[] = "gator_textobject";  /*Module name */
37
38 #define GATOR_TEXTCB_DO_BOX     0
39
40 /*------------------------------------------------------------------------
41  * gator_text_create
42  *
43  * Description:
44  *      Create a gator text object.
45  *
46  * Arguments:
47  *      struct onode *text_onp : Ptr to the text onode to fill out.
48  *      struct onode_createparams *params : Generic ptr to creation
49  *          parameters.
50  *
51  * Returns:
52  *      Zero if successful,
53  *      Error value otherwise.
54  *
55  * Environment:
56  *      The base onode fields have already been set.  Text onodes are
57  *      empty upon creation.
58  *
59  * Side Effects:
60  *      Upon successful creation, the onode's o_window field is
61  *      replaced with a new window created for the text object,
62  *      with the parent window is remembered within the new window
63  *      structure.  On failure, it remains unchanged.
64  *------------------------------------------------------------------------*/
65
66 int
67 gator_text_create(struct onode *text_onp, struct onode_createparams *params)
68 {                               /*gator_text_create */
69
70     static char rn[] = "gator_text_create";     /*Routine name */
71     struct gator_textobj_params *text_params;   /*My specific creation params */
72     struct gator_textobj *text_data;    /*Ptr to private data */
73     struct gator_textcb_hdr *newCB;     /*Ptr to CB hdr */
74
75     text_params = (struct gator_textobj_params *)params;
76     if (objects_debug) {
77         fprintf(stderr,
78                 "[%s:%s] Private data passed to text object at %p:\n", mn,
79                 rn, text_onp);
80         fprintf(stderr, "\tmaxEntries: %d, maxCharsPerEntry: %d\n",
81                 text_params->maxEntries, text_params->maxCharsPerEntry);
82     }
83
84     /*
85      * Allocate the private data area.
86      */
87     if (objects_debug)
88         fprintf(stderr,
89                 "[%s:%s] Allocating %lu bytes for text object private data region\n",
90                 mn, rn, sizeof(struct gator_textobj));
91     text_data = (struct gator_textobj *)malloc(sizeof(struct gator_textobj));
92     if (text_data == (struct gator_textobj *)0) {
93         fprintf(stderr,
94                 "[%s:%s] Can't allocate %lu bytes for text object private data region, errno is %d\n",
95                 mn, rn, sizeof(struct gator_textobj), errno);
96         return (errno);
97     }
98
99     /*
100      * Create the text circular buffer for this new object.
101      */
102     if (objects_debug)
103         fprintf(stderr, "[%s:%s] Creating circular buffer, %dx%d chars\n", mn,
104                 rn, text_params->maxEntries, text_params->maxCharsPerEntry);
105     newCB =
106         gator_textcb_Create(text_params->maxEntries,
107                             text_params->maxCharsPerEntry);
108     if (newCB == (struct gator_textcb_hdr *)0) {
109         fprintf(stderr, "[%s:%s] Can't create text object circular buffer\n",
110                 mn, rn);
111         free(text_data);
112         return (-1);
113     }
114
115     /*
116      * Now that we have the private structures allocated, set them up.
117      */
118     text_data->llrock = (int *)0;
119     text_data->numLines = text_onp->o_height;
120     text_data->cbHdr = newCB;
121     text_data->firstEntShown = 0;
122     text_data->lastEntShown = 0;
123     if (objects_debug)
124         fprintf(stderr, "[%s:%s] Number of lines in window: %d: ", mn, rn,
125                 text_data->numLines);
126
127     /*
128      * Attach the text-specific private
129      * data to the generic onode and return the happy news.
130      */
131     text_onp->o_data = (int *)text_data;
132     return (0);
133
134 }                               /*gator_text_create */
135
136 /*------------------------------------------------------------------------
137  * gator_text_destroy
138  *
139  * Description:
140  *      Destroy a gator text object.
141  *
142  * Arguments:
143  *      struct onode *onp : Ptr to the text onode to delete.
144  *
145  * Returns:
146  *      0: Success
147  *      Error value otherwise.
148  *
149  * Environment:
150  *      Nothing interesting.
151  *
152  * Side Effects:
153  *      As advertised.
154  *------------------------------------------------------------------------*/
155
156 int
157 gator_text_destroy(struct onode *onp)
158 {                               /*gator_text_destroy */
159
160     /*
161      * For now, this is a no-op.
162      */
163     return (0);
164
165 }                               /*gator_text_destroy */
166
167 /*------------------------------------------------------------------------
168  * gator_text_display
169  *
170  * Description:
171  *      Display/redraw a gator text object.
172  *
173  * Arguments:
174  *      struct onode *onp: Ptr to the text onode to display.
175  *
176  * Returns:
177  *      0: Success
178  *      Error value otherwise.
179  *
180  * Environment:
181  *      Nothing interesting.
182  *
183  * Side Effects:
184  *      As advertised.
185  *------------------------------------------------------------------------*/
186
187 int
188 gator_text_display(struct onode *onp)
189 {                               /*gator_text_display */
190
191     static char rn[] = "gator_text_display";    /*Routine name */
192     struct gator_textobj *text_data;    /*Ptr to text obj data */
193     struct gator_textcb_hdr *cbHdr;     /*Ptr to CB header */
194     struct gwin_strparams strparams;    /*String-drawing params */
195     int currLine;               /*Current line being updated */
196     int currLinesUsed;          /*Num screen lines used */
197     int currIdx;                /*Current line index */
198     int currEnt;                /*Current entry being drawn */
199     struct gator_textcb_entry *curr_ent;        /*Ptr to current entry */
200
201     if (objects_debug)
202         fprintf(stderr, "[%s:%s] Displaying text object at %p\n", mn, rn,
203                 onp);
204     text_data = (struct gator_textobj *)(onp->o_data);
205     cbHdr = text_data->cbHdr;
206     if (objects_debug)
207         fprintf(stderr,
208                 "[%s:%s] Displaying text object at %p, object-specific data at %p\n",
209                 mn, rn, onp, text_data);
210
211     /*
212      * Update each line in the screen buffer with its proper contents.
213      */
214     currEnt = text_data->firstEntShown;
215     currLinesUsed = text_data->lastEntShown - currEnt + 1;
216     currIdx =
217         (cbHdr->oldestEntIdx +
218          (currEnt - cbHdr->oldestEnt)) % cbHdr->maxEntriesStored;
219     curr_ent = cbHdr->entry + currIdx;
220
221     if (objects_debug)
222         fprintf(stderr,
223                 "[%s:%s] Drawing %d populated lines, starting with entry %d (index %d) at %p",
224                 mn, rn, currLinesUsed, currEnt, currIdx, curr_ent);
225
226     strparams.x = onp->o_x;
227     strparams.y = onp->o_y;
228     for (currLine = 0; currLine < text_data->numLines; currLine++) {
229         /*
230          * Draw the current entry.
231          */
232         if (currLinesUsed > 0) {
233             /*
234              * Drawing a populated line.  We need to iterate if there are
235              * inversions (I don't feel like doing this now).
236              */
237             strparams.s = curr_ent->textp;
238             strparams.highlight = curr_ent->highlight;
239             WOP_DRAWSTRING(onp->o_window, &strparams);
240
241             currLinesUsed--;
242             currEnt++;
243             currIdx++;
244             if (currIdx >= cbHdr->maxEntriesStored) {
245                 currIdx = 0;
246                 curr_ent = cbHdr->entry;
247             } else
248                 curr_ent++;
249         } else {
250             /*
251              * Draw a blank line.
252              */
253             strparams.s = cbHdr->blankLine;
254             strparams.highlight = 0;
255             WOP_DRAWSTRING(onp->o_window, &strparams);
256         }
257
258         /*
259          * Adjust the X and Y locations.
260          */
261         strparams.x = 0;
262         strparams.y++;
263
264     }                           /*Update loop */
265
266     /*
267      * Box the window before we leave.
268      */
269 #if GATOR_TEXTCB_DO_BOX
270     if (objects_debug)
271         fprintf(stderr, "[%s:%s] Boxing window structure at 0x%x\n", mn, rn,
272                 onp->o_window);
273     WOP_BOX(onp->o_window);
274 #endif /* GATOR_TEXTCB_DO_BOX */
275
276     /*
277      * For now, this is all we do.
278      */
279     return (0);
280
281 }                               /*gator_text_display */
282
283 /*------------------------------------------------------------------------
284  * gator_text_release
285  *
286  * Description:
287  *      Drop the refcount on a gator text object.
288  *
289  * Arguments:
290  *      struct onode *onp : Ptr to the onode whose refcount is
291  *                                   to be dropped.
292  *
293  * Returns:
294  *      0: Success
295  *      Error value otherwise.
296  *
297  * Environment:
298  *      Nothing interesting.
299  *
300  * Side Effects:
301  *      As advertised.
302  *------------------------------------------------------------------------*/
303
304 int
305 gator_text_release(struct onode *onp)
306 {                               /*gator_text_release */
307
308     /*
309      * For now, this is a no-op.
310      */
311     return (0);
312
313 }                               /*gator_text_release */
314
315 /*------------------------------------------------------------------------
316  * gator_text_Scroll
317  *
318  * Description:
319  *      Scroll a text object some number of lines.
320  *
321  * Arguments:
322  *      struct onode *onp : Ptr to the text onode to be scrolled.
323  *      int nlines        : Number of lines to scroll.
324  *      int direction     : Scroll up or down?
325  *
326  * Returns:
327  *      0: Success,
328  *      Error value otherwise.
329  *
330  * Environment:
331  *      Invariant: the text object's firstEntShown and lastEntShown
332  *              are always between oldestEnt and currEnt (inclusive).
333  *
334  * Side Effects:
335  *      As advertised.
336  *------------------------------------------------------------------------*/
337
338 int
339 gator_text_Scroll(struct onode *onp, int nlines, int direction)
340 {                               /*gator_text_Scroll */
341
342     static char rn[] = "gator_text_Scroll";     /*Routine name */
343     struct gator_textobj *text_data;    /*Ptr to text obj data */
344
345     /*
346      * We move the markers for first & last entries displayed, depending
347      * on what's available to us in the circular buffer.  We never leave
348      * the window empty.
349      */
350     if (objects_debug)
351         fprintf(stderr, "[%s:%s] Scrolling text object %d lines %s\n",
352                 mn, rn, nlines,
353                 (direction == GATOR_TEXT_SCROLL_UP) ? "UP" : "DOWN");
354
355     text_data = (struct gator_textobj *)(onp->o_data);
356     if (direction == GATOR_TEXT_SCROLL_DOWN) {
357         /*
358          * Move the object's text ``down'' by sliding the window up.
359          */
360         text_data->firstEntShown -= nlines;
361         if (text_data->firstEntShown < text_data->cbHdr->oldestEnt)
362             text_data->firstEntShown = text_data->cbHdr->oldestEnt;
363
364         text_data->lastEntShown -= nlines;
365         if (text_data->lastEntShown < text_data->cbHdr->oldestEnt)
366             text_data->lastEntShown = text_data->cbHdr->oldestEnt;
367
368     } /*Moving down */
369     else {
370         /*
371          * Move the object's text ``up'' by sliding the window down.
372          */
373         text_data->firstEntShown += nlines;
374         if (text_data->firstEntShown > text_data->cbHdr->currEnt)
375             text_data->firstEntShown = text_data->cbHdr->currEnt;
376
377         text_data->lastEntShown += nlines;
378         if (text_data->lastEntShown > text_data->cbHdr->currEnt)
379             text_data->lastEntShown = text_data->cbHdr->currEnt;
380
381     }                           /*Moving up */
382
383     /*
384      * Return the happy news.
385      */
386     return (0);
387
388 }                               /*gator_text_Scroll */
389
390 /*------------------------------------------------------------------------
391  * gator_text_Write
392  *
393  * Description:
394  *      Write the given string to the end of the gator text object.
395  *
396  * Arguments:
397  *      struct onode *onp : Ptr to the onode whose to which we're
398  *                              writing.
399  *      char *strToWrite  : String to write.
400  *      int numChars      : Number of chars to write.
401  *      int highlight     : Use highlighting?
402  *      int skip          : Force a skip to the next line?
403  *
404  * Returns:
405  *      0: Success
406  *      Error value otherwise.
407  *
408  * Environment:
409  *      Nothing interesting.
410  *
411  * Side Effects:
412  *      As advertised.
413  *------------------------------------------------------------------------*/
414
415 int
416 gator_text_Write(struct onode *onp, char *strToWrite, int numChars, 
417                  int highlight, int skip)
418 {                               /*gator_text_Write */
419
420     static char rn[] = "gator_text_Write";      /*Routine name */
421     register int code;          /*Return value on routines */
422     struct gator_textobj *text_data;    /*Ptr to text obj data */
423     struct gator_textcb_hdr *cbHdr;     /*Ptr to text CB header */
424     int i;                      /*Loop variable */
425     int oldCurrEnt;             /*CB's old currEnt value */
426     int redisplay;              /*Redisplay after write? */
427     int shownDiff;              /*Diff between 1st & last lines */
428     int writeDiff;              /*Num lines really written */
429     int bumpAmount;             /*Amount to bump count */
430
431     /*
432      * 
433      */
434     if (objects_debug) {
435         fprintf(stderr,
436                 "[%s:%s] Writing %d chars to text object at %p (highlight=%d, skip=%d: '",
437                 mn, rn, numChars, onp, highlight, skip);
438         for (i = 0; i < numChars; i++)
439             fprintf(stderr, "%c", strToWrite[i]);
440         fprintf(stderr, "\n");
441     }
442
443     if (numChars == 0)
444         numChars = strlen(strToWrite);  /* simplify caller */
445     text_data = (struct gator_textobj *)(onp->o_data);
446     cbHdr = text_data->cbHdr;
447     if (cbHdr == (struct gator_textcb_hdr *)0) {
448         fprintf(stderr, "[%s:%s] Text object missing its circular buffer!\n",
449                 mn, rn);
450         return (-1);
451     }
452     /*
453      * If the current CB entry is being displayed, we track the write
454      * visually and redisplay.
455      */
456     if ((cbHdr->currEnt <= text_data->lastEntShown)
457         && (cbHdr->currEnt >= text_data->firstEntShown)) {
458         if (objects_debug)
459             fprintf(stderr,
460                     "[%s:%s] Current entry is on screen.  Tracking this write\n",
461                     mn, rn);
462         oldCurrEnt = cbHdr->currEnt;
463         redisplay = 1;
464     } else {
465         if (objects_debug)
466             fprintf(stderr,
467                     "[%s:%s] Current entry NOT on screen, not tracking write\n",
468                     mn, rn);
469         redisplay = 0;
470     }
471
472
473     if (redisplay) {
474         /*
475          * We're tracking the write.  Compute the number of screen lines
476          * actually written and adjust our own numbers, then call the
477          * display function.
478          */
479         shownDiff = text_data->lastEntShown - text_data->firstEntShown;
480         writeDiff = cbHdr->currEnt - oldCurrEnt;
481         if (objects_debug)
482             fprintf(stderr,
483                     "[%s:%s] Preparing to redisplay.  Difference in shown lines=%d, difference in written lines=%d\n",
484                     mn, rn, shownDiff, writeDiff);
485         if (shownDiff < (text_data->numLines - 1)) {
486             /*
487              * We weren't showing a full screen of stuff.  Bump the last
488              * line shown by the minimum of the number of free lines still
489              * on the screen and the number of new lines actually written.
490              */
491             bumpAmount = (text_data->numLines - 1) - shownDiff;
492             if (writeDiff < bumpAmount)
493                 bumpAmount = writeDiff;
494             text_data->lastEntShown += bumpAmount;
495             writeDiff -= bumpAmount;
496             if (objects_debug)
497                 fprintf(stderr,
498                         "[%s:%s] Empty lines appeared on screen, bumping bottom line shown by %d; new writeDiff is %d\n",
499                         mn, rn, bumpAmount, writeDiff);
500         }
501
502         /*
503          * If we have any more lines that were written not taken care
504          * of by the above, we just bump the counters.
505          */
506         if (writeDiff > 0) {
507             if (objects_debug)
508                 fprintf(stderr,
509                         "[%s:%s] Still more lines need to be tracked.  Moving first & last shown down by %d\n",
510                         mn, rn, writeDiff);
511             text_data->firstEntShown += writeDiff;
512             text_data->lastEntShown += writeDiff;
513         }
514     }
515
516     /*Redisplay needed */
517     /*
518      * Simply call the circular buffer write op.
519      */
520     code = gator_textcb_Write(cbHdr, strToWrite, numChars, highlight, skip);
521     if (code) {
522         fprintf(stderr,
523                 "[%s:%s] Can't write to text object's circular buffer, errror code is %d\n",
524                 mn, rn, code);
525         return (code);
526     }
527
528     /*
529      * Everything went well.  Return the happy news.
530      */
531     return (0);
532
533 }                               /*gator_text_Write */
534
535 /*------------------------------------------------------------------------
536  * gator_text_BlankLine
537  *
538  * Description:
539  *      Write a given number of blank lines to the given text object.
540  *
541  * Arguments:
542  *      struct onode *onp : Ptr to the onode to which we're writing.
543  *      int numBlanks     : Number of blank lines to write.
544  *
545  * Returns:
546  *      0: Success,
547  *      Error value otherwise.
548  *
549  * Environment:
550  *      Nothing interesting.
551  *
552  * Side Effects:
553  *      As advertised.
554  *------------------------------------------------------------------------*/
555
556 int
557 gator_text_BlankLine(struct onode *onp, int numBlanks)
558 {                               /*gator_text_BlankLine */
559
560     static char rn[] = "gator_text_BlankLine";  /*Routine name */
561     register int code;          /*Return value on routines */
562     struct gator_textobj *text_data;    /*Ptr to text obj data */
563
564     /*
565      * We just call the circular buffer routine directly.
566      */
567     if (objects_debug)
568         fprintf(stderr,
569                 "[%s:%s] Writing %d blank lines to text object at %p\n", mn,
570                 rn, numBlanks, onp);
571
572     text_data = (struct gator_textobj *)(onp->o_data);
573     code = gator_textcb_BlankLine(text_data->cbHdr, numBlanks);
574     if (code) {
575         fprintf(stderr,
576                 "[%s:%s] Can't write %d blank lines to text object at %p\n",
577                 mn, rn, numBlanks, onp);
578         return (code);
579     }
580
581     /*
582      * Blank lines written successfully.  Adjust what lines are currently
583      * shown.  Iff we were tracking the end of the buffer, we have to
584      * follow the blank lines.
585      */
586     if (text_data->lastEntShown == text_data->cbHdr->currEnt - numBlanks) {
587         text_data->firstEntShown += numBlanks;
588         text_data->lastEntShown += numBlanks;
589     }
590
591     /*
592      * Return the happy news.
593      */
594     return (0);
595
596 }                               /*gator_text_BlankLine */