1 /*
2  * Motif
3  *
4  * Copyright (c) 1987-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 /*
25  * HISTORY
26  */
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 
32 #ifdef REV_INFO
33 #ifndef lint
34 static char rcsid[] = "$TOG: XmStringFunc.c /main/9 1999/10/14 11:20:50 mgreess $"
35 #endif
36 #endif
37 
38 #include <Xm/XmosP.h>
39 
40 #include "XmI.h"
41 #include "XmStringI.h"
42 #include "XmRenderTI.h"
43 #include "XmTabListI.h"
44 
45 /********    Static Function Declarations    ********/
46 
47 static void new_line(_XmString string) ;
48 
49 /********    End Static Function Declarations    ********/
50 
51 
52 XmStringComponentType
XmStringPeekNextTriple(XmStringContext context)53 XmStringPeekNextTriple(XmStringContext context)
54 {
55   unsigned int len;
56   XtPointer    val;
57 
58   return XmeStringGetComponent((_XmStringContext) context, False, False, &len, &val);
59 }
60 
61 Boolean
XmStringHasSubstring(XmString string,XmString substring)62 XmStringHasSubstring(
63         XmString string,
64         XmString substring )
65 {
66   _XmStringContextRec stack_context;
67   char               *text;
68   char               *subtext;
69   short               char_count;
70   short               subchar_count;
71   Boolean             found;
72   int                 i, j, max;
73   _XmStringEntry      line, *entry, seg;
74   XmStringComponentType type;
75   unsigned int        len;
76   XtPointer	      val;
77 
78   _XmProcessLock();
79   if ((string == NULL) || (substring == NULL) || (XmStringEmpty(substring))) {
80     _XmProcessUnlock();
81     return (FALSE);
82   }
83 
84   /*
85    * The substring must be a one line/one segment string.
86    */
87 
88   if (_XmStrEntryCountGet(substring) != 1) {
89     _XmProcessUnlock();
90     return (FALSE);
91   }
92 
93   if (((entry = _XmStrEntryGet(substring)) != NULL) &&
94       _XmEntrySegmentCountGet(entry[0]) > 1) {
95     _XmProcessUnlock();
96     return (FALSE);
97   }
98 
99   /*
100    * Get the text out of the substring.
101    */
102 
103   if (_XmStrOptimized(substring))
104     {
105       subchar_count = (short)_XmStrByteCount(substring);
106       subtext = (char *)_XmStrText(substring);
107     }
108   else if (_XmStrMultiple(substring))
109     {
110       line = entry[0];
111 
112       if (_XmEntryMultiple(line))
113 	{
114 	  seg = (_XmStringEntry)_XmEntrySegmentGet(line)[0];
115 
116 	  subchar_count = (short)_XmEntryByteCountGet(seg);
117 	  subtext = (char*) _XmEntryTextGet(seg);
118 	}
119       else
120 	{
121 	  subchar_count = (short)_XmEntryByteCountGet(line);
122 	  subtext = (char*) _XmEntryTextGet(line);
123 	}
124     }
125   else { 		  /* Oops, some weird string! */
126     _XmProcessUnlock();
127     return (FALSE);
128   }
129 
130 
131   if ((subchar_count == 0) || (subtext == NULL)) {
132     _XmProcessUnlock();
133     return (FALSE);
134   }
135 
136   /** Find a text component that matches. **/
137   if (string) {
138     _XmStringContextReInit(&stack_context, string);
139     while ((type = XmeStringGetComponent(&stack_context, TRUE, FALSE,
140 					 &len, &val)) !=
141 	   XmSTRING_COMPONENT_END)
142       {
143 	switch(type)
144 	  {
145 	  case XmSTRING_COMPONENT_TEXT:
146 	  case XmSTRING_COMPONENT_LOCALE_TEXT:
147 	  case XmSTRING_COMPONENT_WIDECHAR_TEXT:
148 
149 	    char_count = len;
150 	    text = (char *)val;
151 
152 	    if (char_count >= subchar_count) {
153 	      max = char_count - subchar_count;
154 	      for (i = 0; i <= max; i++) {
155 		found = TRUE;
156 
157 		for (j = 0; j < subchar_count; j++) {
158 		  if (text[i+j] != subtext[j])  {
159 		    found = FALSE;
160 		    break;
161 		  }
162 		}
163 		if (found) {
164 		  _XmStringContextFree(&stack_context);
165     		  _XmProcessUnlock();
166 		  return(TRUE);
167 		}
168 	      }
169 	    }
170 	    break;
171 	  default:
172 	    break;
173 	  }
174       }
175     _XmStringContextFree(&stack_context);
176   }
177   _XmProcessUnlock();
178   return (FALSE);
179 }
180 
181 XmStringTable
XmStringTableParseStringArray(XtPointer * strings,Cardinal count,XmStringTag tag,XmTextType type,XmParseTable parse,Cardinal parse_count,XtPointer call_data)182 XmStringTableParseStringArray(XtPointer   *strings,
183 			      Cardinal     count,
184 			      XmStringTag  tag,
185 			      XmTextType   type,
186 			      XmParseTable parse,
187 			      Cardinal     parse_count,
188 			      XtPointer    call_data)
189 {
190   int	i;
191   XmStringTable strs;
192 
193   _XmProcessLock();
194   if ((strings == NULL) || (count == 0)) {
195     _XmProcessUnlock();
196     return(NULL);
197   }
198 
199   strs = (XmStringTable)XtMalloc(count * sizeof(XmString));
200 
201   for (i = 0; i < count; i++)
202     {
203       strs[i] = XmStringParseText(strings[i], NULL, tag, type,
204 				  parse, parse_count, call_data);
205     }
206 
207   _XmProcessUnlock();
208   return(strs);
209 }
210 
211 XtPointer *
XmStringTableUnparse(XmStringTable table,Cardinal count,XmStringTag tag,XmTextType tag_type,XmTextType output_type,XmParseTable parse,Cardinal parse_count,XmParseModel parse_model)212 XmStringTableUnparse(XmStringTable     table,
213 		     Cardinal	       count,
214 		     XmStringTag       tag,
215 		     XmTextType        tag_type,
216 		     XmTextType        output_type,
217 		     XmParseTable      parse,
218 		     Cardinal          parse_count,
219 		     XmParseModel      parse_model)
220 {
221   XtPointer	*strs;
222   int		i;
223 
224   _XmProcessLock();
225   if ((table == NULL) || (count == 0)) {
226     _XmProcessUnlock();
227     return(NULL);
228   }
229 
230   strs = (XtPointer *)XtMalloc(count * sizeof(XtPointer));
231 
232   for (i = 0; i < count; i++)
233     strs[i] = XmStringUnparse(table[i], tag, tag_type, output_type,
234 			      parse, parse_count, parse_model);
235   _XmProcessUnlock();
236   return(strs);
237 }
238 
239 XmString
XmStringTableToXmString(XmStringTable table,Cardinal count,XmString break_comp)240 XmStringTableToXmString(XmStringTable  table,
241 			Cardinal       count,
242 			XmString       break_comp)
243 {
244   /* Note: this is a very expensive way to do this.  Fix for Beta */
245   int		i;
246   XmString	str = NULL, tmp1, tmp2;
247 
248   _XmProcessLock();
249   tmp1 = NULL;
250 
251   for (i = 0; i < count; i++)
252     {
253       tmp2 = XmStringConcatAndFree(tmp1, XmStringCopy(table[i]));
254       str = XmStringConcatAndFree(tmp2, XmStringCopy(break_comp));
255 
256       tmp1 = str;
257     }
258 
259   _XmProcessUnlock();
260   return(str);
261 }
262 
263 XmString
XmStringPutRendition(XmString string,XmStringTag rendition)264 XmStringPutRendition(XmString string,
265 		     XmStringTag rendition)
266 {
267   /* Quick and dirty.  Fix for beta! */
268   XmString	str, tmp1, tmp2;
269 
270   tmp1 = XmStringComponentCreate(XmSTRING_COMPONENT_RENDITION_BEGIN,
271 				 strlen(rendition), (XtPointer)rendition);
272   tmp2 = XmStringConcatAndFree(tmp1, XmStringCopy(string));
273 
274   tmp1 = XmStringComponentCreate(XmSTRING_COMPONENT_RENDITION_END,
275 				 strlen(rendition), (XtPointer)rendition);
276   str = XmStringConcatAndFree(tmp2, tmp1);
277 
278   return(str);
279 }
280 
281 void
XmParseMappingGetValues(XmParseMapping mapping,ArgList arg_list,Cardinal arg_count)282 XmParseMappingGetValues(XmParseMapping mapping,
283 			ArgList        arg_list,
284 			Cardinal       arg_count)
285 {
286   register Cardinal i;
287   register String arg_name;
288 
289   _XmProcessLock();
290   /* Do a little error checking. */
291   if (mapping == NULL) {
292     _XmProcessUnlock();
293     return;
294   }
295 
296   /* Modify the specified values. */
297   for (i = 0; i < arg_count; i++)
298     {
299       arg_name = arg_list[i].name;
300 
301       if ((arg_name == XmNpattern) ||
302 	  (strcmp(arg_name, XmNpattern) == 0))
303 	*((XtPointer*)arg_list[i].value) = mapping->pattern;
304       else if ((arg_name == XmNpatternType) ||
305 	       (strcmp(arg_name, XmNpatternType) == 0))
306 	*((XmTextType*)arg_list[i].value) = mapping->pattern_type;
307       else if ((arg_name == XmNsubstitute) ||
308 	       (strcmp(arg_name, XmNsubstitute) == 0))
309 	*((XmString*)arg_list[i].value) = XmStringCopy(mapping->substitute);
310       else if ((arg_name == XmNinvokeParseProc) ||
311 	       (strcmp(arg_name, XmNinvokeParseProc) == 0))
312 	*((XmParseProc*)arg_list[i].value) = mapping->parse_proc;
313       else if ((arg_name == XmNclientData) ||
314 	       (strcmp(arg_name, XmNclientData) == 0))
315 	*((XtPointer*)arg_list[i].value) = mapping->client_data;
316       else if ((arg_name == XmNincludeStatus) ||
317 	       (strcmp(arg_name, XmNincludeStatus) == 0))
318 	*((XmIncludeStatus*)arg_list[i].value) = mapping->include_status;
319     }
320   _XmProcessUnlock();
321 }
322 
323 void
XmParseMappingFree(XmParseMapping mapping)324 XmParseMappingFree(XmParseMapping mapping)
325 {
326   _XmProcessLock();
327   if (mapping != NULL)
328     {
329       /* Free copied data. */
330       XmStringFree(mapping->substitute);
331 
332       /* Free the record. */
333       XtFree((char*) mapping);
334     }
335   _XmProcessUnlock();
336 }
337 
338 void
XmParseTableFree(XmParseTable parse_table,Cardinal parse_count)339 XmParseTableFree(XmParseTable parse_table,
340 		 Cardinal     parse_count)
341 {
342   /* Free each entry in the table. */
343   Cardinal i;
344 
345   _XmProcessLock();
346   for (i = 0; i < parse_count; i++)
347     XmParseMappingFree(parse_table[i]);
348 
349   /* Free the table itself. */
350   XtFree((char*) parse_table);
351   _XmProcessUnlock();
352 }
353 
354 /*
355  * XmeGetNextCharacter: An XmParseProc to consume the triggering
356  *	character and insert the following character.
357  */
358 /*ARGSUSED*/
359 XmIncludeStatus
XmeGetNextCharacter(XtPointer * in_out,XtPointer text_end,XmTextType type,XmStringTag tag,XmParseMapping entry,int pattern_length,XmString * str_include,XtPointer call_data)360 XmeGetNextCharacter(XtPointer     *in_out,
361 		    XtPointer      text_end,
362 		    XmTextType     type,
363 		    XmStringTag    tag,
364 		    XmParseMapping entry, /* unused */
365 		    int		   pattern_length,
366 		    XmString      *str_include,
367 		    XtPointer      call_data) /* unused */
368 {
369   char* ptr = (char*) *in_out;
370   int len = 0;
371   XmStringComponentType comp_type;
372   assert(in_out != NULL);
373 
374   _XmProcessLock();
375   /* Initialize the out parameters */
376   *str_include = NULL;
377 
378   /* Consume the triggering characters. */
379   ptr += pattern_length;
380 
381   /* Select the component type. */
382   switch (type)
383     {
384     case XmCHARSET_TEXT:
385       if ((tag != NULL) && (strcmp(XmFONTLIST_DEFAULT_TAG, tag) == 0))
386 	comp_type = XmSTRING_COMPONENT_LOCALE_TEXT;
387       else
388 	comp_type = XmSTRING_COMPONENT_TEXT;
389       if ((text_end == NULL) || (ptr < (char*) text_end))
390 #ifndef NO_MULTIBYTE
391 	len = mblen(ptr, MB_CUR_MAX);
392 #else
393         len = *ptr ? 1 : 0;
394 #endif
395       break;
396 
397     case XmMULTIBYTE_TEXT:
398       /* In Motif 2.0 dynamic switching of locales isn't supported. */
399       comp_type = XmSTRING_COMPONENT_LOCALE_TEXT;
400       if ((text_end == NULL) || (ptr < (char*) text_end))
401 #ifndef NO_MULTIBYTE
402 	len = mblen(ptr, MB_CUR_MAX);
403 #else
404         len = *ptr ? 1 : 0;
405 #endif
406       break;
407 
408     case XmWIDECHAR_TEXT:
409       comp_type = XmSTRING_COMPONENT_WIDECHAR_TEXT;
410       if ((text_end == NULL) || (ptr < (char*) text_end))
411 	len = sizeof(wchar_t);
412       break;
413 
414     default:
415       comp_type = XmSTRING_COMPONENT_UNKNOWN;
416       break;
417     }
418 
419   /* Quit if mblen() failed or if type was unrecognized. */
420   if ((len <= 0) || (comp_type == XmSTRING_COMPONENT_UNKNOWN))
421     {
422       *in_out = (XtPointer) ptr;
423       _XmProcessUnlock();
424       return XmINSERT;
425     }
426 
427   /* Create a component containing the next character. */
428   *str_include = XmStringComponentCreate(comp_type, len, ptr);
429   ptr += len;
430   *in_out = (XtPointer) ptr;
431 
432   _XmProcessUnlock();
433   return XmINSERT;
434 }
435 
436 static void
new_line(_XmString string)437 new_line(
438         _XmString string )
439 {
440     int lc = _XmStrEntryCount(string);
441     _XmStringEntry line;
442 
443     _XmStrImplicitLine(string) = TRUE;
444 
445     _XmStrEntry(string) = (_XmStringEntry *)
446       XtRealloc((char *) _XmStrEntry(string),
447 		sizeof(_XmStringEntry) * (lc + 1));
448 
449     _XmEntryCreate(line, XmSTRING_ENTRY_ARRAY);
450     _XmStrEntry(string)[lc] = line;
451 
452     _XmEntrySegmentCount(line) = 0;
453     _XmEntrySegment(line) = NULL;
454 
455     _XmStrEntryCount(string)++;
456 }
457 
458 static XmString
MakeStrFromSeg(XmStringContext start)459 MakeStrFromSeg(XmStringContext start)
460 {
461   _XmStringEntry	*line;
462   _XmStringEntry	*segs, seg;
463   _XmString		str;
464 
465   if (_XmStrContOpt(start)) {
466     _XmStrContError(start) = TRUE;
467     return(XmStringCopy(_XmStrContString(start)));
468   } else {
469     /* get segment */
470     line = _XmStrEntry(_XmStrContString(start));
471 
472     /* Create XmString structure */
473     _XmStrCreate(str, XmSTRING_MULTIPLE_ENTRY, 0);
474 
475     if (_XmEntryMultiple(line[_XmStrContCurrLine(start)])) {
476       segs = (_XmStringEntry*)_XmEntrySegment(line[_XmStrContCurrLine(start)]);
477 
478       new_line(str);
479 
480       if (_XmStrContCurrSeg(start) < _XmEntrySegmentCount(line)) {
481 	seg = segs[_XmStrContCurrSeg(start)];
482 
483 	_XmStringSegmentNew(str, 0, seg, True);
484 
485 	_XmStrContCurrSeg(start)++;
486 
487 	_XmStrContDir(start)     = _XmEntryDirectionGet(seg);
488 	_XmStrContTag(start)     = _XmEntryTag(seg);
489 	_XmStrContTagType(start) = (XmTextType) _XmEntryTextTypeGet(seg);
490       } else {
491 	new_line(str);
492 	_XmStrContCurrSeg(start) = 0;
493 	_XmStrContCurrLine(start)++;
494       }
495     } else {
496       seg = line[_XmStrContCurrLine(start)];
497       _XmStringSegmentNew(str, 0, seg, True);
498 
499       _XmStrContCurrSeg(start) = 0;
500       _XmStrContCurrLine(start)++;
501 
502       _XmStrContDir(start)     = _XmEntryDirectionGet(seg);
503       _XmStrContTag(start)     = _XmEntryTag(seg);
504       _XmStrContTagType(start) = (XmTextType) _XmEntryTextTypeGet(seg);
505     }
506     _XmStrContState(start)   = PUSH_STATE;
507   }
508   return(str);
509 }
510 
511 static Boolean
LastSeg(XmStringContext start)512 LastSeg(XmStringContext start)
513 {
514   _XmStringEntry	*line;
515 
516   if (_XmStrContOpt(start))
517     {
518       return(TRUE);
519     } else {
520       line = _XmStrEntry(_XmStrContString(start));
521 
522       if (_XmEntryMultiple(line[_XmStrContCurrLine(start)]))
523 	return(_XmStrContCurrSeg(start) == _XmEntrySegmentCount(line));
524       else return(TRUE);
525     }
526 }
527 
528 static Boolean
ContextsMatch(XmStringContext a,XmStringContext b)529 ContextsMatch(XmStringContext a,
530 	      XmStringContext b)
531 {
532   if ((_XmStrContCurrLine(a) == _XmStrContCurrLine(b)) &&
533       (_XmStrContCurrSeg(a) == _XmStrContCurrSeg(b)) &&
534       (_XmStrContState(a) == _XmStrContState(b)))
535 
536     if (((_XmStrContState(a) == BEGIN_REND_STATE) ||
537 	(_XmStrContState(a) == END_REND_STATE)))
538 
539       if (_XmStrContRendIndex(a) == _XmStrContRendIndex(b))
540 	return(TRUE);
541       else return(FALSE);
542 
543     else return(TRUE);
544 
545   else return(FALSE);
546 }
547 
548 static XmString
MakeStr(XmStringContext start,XmStringContext end)549 MakeStr(XmStringContext start,
550 	XmStringContext end)
551 {
552   /* This is quick and dirty, need to be smarter about it before Beta. */
553   XmStringComponentType	type;
554   unsigned int		len;
555   XtPointer		val;
556   XmString		str;
557 
558 
559   /* Next component over start until at segment break */
560   str = NULL;
561 
562   while (_XmStrContState(start) != PUSH_STATE)
563     {
564       type = XmeStringGetComponent(start, TRUE, FALSE, &len, &val);
565 
566       if (ContextsMatch(start, end)) return(str);
567 
568       str = XmStringConcatAndFree(str,
569 				  XmStringComponentCreate(type, len, val));
570     }
571 
572   /* Next segment over start incrementing until one segment before context */
573   while ((_XmStrContCurrLine(start) < (_XmStrContCurrLine(end) - 1)) ||
574 	 ((_XmStrContCurrLine(start) == _XmStrContCurrLine(end)) &&
575 	  (_XmStrContCurrSeg(start) < _XmStrContCurrSeg(end))) ||
576 	 !LastSeg(start))
577     {
578       str = XmStringConcatAndFree(str, MakeStrFromSeg(start));
579     }
580 
581   /* Next component over start until it matches context */
582   type = XmeStringGetComponent(start, TRUE, FALSE, &len, &val);
583   while (!ContextsMatch(start, end))
584     {
585       str = XmStringConcatAndFree(str, XmStringComponentCreate(type, len, val));
586       type = XmeStringGetComponent(start, TRUE, FALSE, &len, &val);
587   }
588 
589   return(str);
590 }
591 
592 Cardinal
XmStringToXmStringTable(XmString string,XmString break_component,XmStringTable * table)593 XmStringToXmStringTable(XmString string,
594 			XmString break_component,
595 			XmStringTable *table)
596 {
597   /* Note: this is a very expensive way to do this.  Fix for Beta */
598   _XmStringContextRec	stack_context, stack_start;
599   XmStringComponentType	type, b_type;
600   unsigned int		len, b_len;
601   XtPointer		val, b_val;
602   int			i, count;
603 
604   _XmProcessLock();
605   /* Get triple for first component of break_component */
606   if (break_component)
607     {
608       _XmStringContextReInit(&stack_context, break_component);
609       b_type = XmeStringGetComponent(&stack_context, TRUE, FALSE,
610 				     &b_len, &b_val);
611       _XmStringContextFree(&stack_context);
612     }
613   else
614     /* Nothing to match against.  Return complete string. */
615     {
616       if (table != NULL)
617 	{
618 	  *table = (XmStringTable)XtMalloc(sizeof(XmString));
619 	  *table[0] = XmStringCopy(string);
620 	}
621       _XmProcessUnlock();
622       return(1);
623     }
624 
625   /* Get context */
626   if (!string)
627     {
628       if (table != NULL) *table = NULL;
629       _XmProcessUnlock();
630       return(0);
631     }
632   _XmStringContextReInit(&stack_context, string);
633 
634   /* Count number of entries for table */
635   count = 0;
636   while ((type = XmeStringGetComponent(&stack_context, TRUE, FALSE,
637 				       &len, &val)) !=
638 	 XmSTRING_COMPONENT_END)
639     {
640       if ((type == b_type) && (len == b_len) &&
641 	  (memcmp(val, b_val, len) == 0))
642 	count++;
643     }
644 
645   /* Allocate table and insert new strings */
646   if (table != NULL)
647     {
648       *table = (XmStringTable)XtMalloc(count * sizeof(XmString));
649 
650       _XmStringContextReInit(&stack_context, string);
651       _XmStringContextReInit(&stack_start, string);
652 
653       i = 0;
654 
655       while ((type = XmeStringGetComponent(&stack_context, TRUE, FALSE,
656 					   &len, &val)) !=
657 	     XmSTRING_COMPONENT_END)
658 	{
659 	  if ((type == b_type) && (len == b_len) &&
660 	      (memcmp(val, b_val, len) == 0))
661 	    {
662 	      /* make XmString from start to end */
663 	      (*table)[i] = MakeStr(&stack_start, &stack_context);
664 	      i++;
665 	    }
666 	}
667 
668       _XmStringContextFree(&stack_start);
669     }
670   _XmStringContextFree(&stack_context);
671 
672   _XmProcessUnlock();
673   return(count);
674 }
675 
676 
677 
678 XmTabList
XmStringTableProposeTablist(XmStringTable strings,Cardinal num_strings,Widget widget,float pad_value,XmOffsetModel offset_model)679 XmStringTableProposeTablist(XmStringTable strings,
680 			    Cardinal num_strings,
681 			    Widget widget,
682 			    float pad_value,
683 			    XmOffsetModel offset_model)
684 {
685   int			i, j;
686   _XmStringContextRec	stack_ctx;
687   XmTabList		tl;
688   XmTab			tab, prev, start;
689   float			width, val;
690   unsigned char		units;
691   Arg			args[1];
692   int			n;
693   _XmRenditionRec	scratch;
694   XmRendition		rend;
695   _XmRendition		tmp;
696   XmRenderTable		rt;
697   NextTabResult		ret_val;
698 
699   _XmProcessLock();
700   if ((strings == NULL) || (num_strings == 0)) {
701     _XmProcessUnlock();
702     return ((XmTabList)NULL);
703   }
704 
705   bzero((char*) &scratch, sizeof(_XmRenditionRec));
706   tmp = &scratch;
707   rend = &tmp;
708 
709   _XmRendDisplay(rend) = XtDisplayOfObject(widget);
710 
711   n = 0;
712   XtSetArg(args[n], XmNrenderTable, &rt); n++;
713   XtGetValues(widget, args, n);
714 
715   /* Work around weird bug with XtGetValues. */
716   n = 0;
717   XtSetArg(args[n], XmNunitType, &units); n++;
718   XtGetValues(widget, args, n);
719 
720   if (rt == NULL) rt = XmeGetDefaultRenderTable(widget, XmTEXT_FONTLIST);
721 
722   tab = XmTabCreate(0.0, units, offset_model, XmALIGNMENT_BEGINNING, ".");
723 
724   tl = XmTabListInsertTabs(NULL, &tab, 1, 0);
725 
726   XmTabFree(tab);
727 
728   for (i = 0; i < num_strings; i++)
729     {
730       if (!strings[i])
731 	{
732 	  /* Clean up */
733 	  XmTabListFree(tl);
734 	  _XmProcessUnlock();
735 	  return((XmTabList)NULL);
736 	}
737       _XmStringContextReInit(&stack_ctx, strings[i]);
738 
739       tab = _XmTabLStart(tl);
740       val = 0.0;
741 
742       /* Scan str for tabs, update tl if necessary. */
743       j = 0;
744 
745       while ((ret_val = _XmStringGetNextTabWidth(&stack_ctx, widget, units,
746 						 rt, &width, &rend)) !=
747 	     XmTAB_EOS)
748 	{
749 	  if (ret_val == XmTAB_NEWLINE)
750 	    {
751 	      tab = _XmTabLStart(tl);
752 	      j = 0;
753 	      continue;
754 	    }
755 
756 	  val = width + pad_value;
757 
758 	  if (j >= _XmTabLCount(tl))
759 	    /* Need to add a tab */
760 	    {
761 	      tab = XmTabCreate(0.0, units, offset_model,
762 				XmALIGNMENT_BEGINNING, ".");
763 	      start = _XmTabLStart(tl);
764 	      prev = _XmTabPrev(start);
765 
766 	      _XmTabNext(prev) = tab;
767 	      _XmTabPrev(tab) = prev;
768 	      _XmTabNext(tab) = start;
769 	      _XmTabPrev(start) = tab;
770 	      _XmTabLCount(tl)++;
771 	    }
772 	  else if (j > 0)
773 	    {
774 	      tab = _XmTabNext(tab);
775 	    }
776 
777 	  if (val > _XmTabValue(tab)) XmTabSetValue(tab, val);
778 	  else val = _XmTabValue(tab);
779 	  j++;
780 	}
781 
782       _XmStringContextFree(&stack_ctx);
783     }
784 
785   if (offset_model == XmABSOLUTE)
786     {
787       start = _XmTabLStart(tl);
788       val = _XmTabValue(start);
789 
790       for (tab = _XmTabNext(start); tab != start; tab = _XmTabNext(tab))
791 	{
792 	  val += _XmTabValue(tab);
793 	  XmTabSetValue(tab, val);
794 	}
795     }
796 
797   _XmProcessUnlock();
798   return(tl);
799 }
800 
801 /*
802  * Helper function for XmTabList.c
803  * This routine performs successive reads on an XmStringContext
804  * and returns the width (in units of XmNunitType of widget) of
805  * the text segments between the previous and next tab or end of line.
806  * It uses the XmNrenderTable from widget to calculate the width.  It
807  * returns XmTAB_EOS if the end of the string has been reached, XmTAB_NEWLINE
808  * if the end of line is reached and XmTAB_NEXT if a tab is encountered.
809  */
810 NextTabResult
_XmStringGetNextTabWidth(XmStringContext ctx,Widget widget,unsigned char units,XmRenderTable rt,float * width,XmRendition * rend)811 _XmStringGetNextTabWidth(XmStringContext ctx,
812 			 Widget widget,
813 			 unsigned char units,
814 			 XmRenderTable rt,
815 			 float *width,
816 			 XmRendition *rend)
817 {
818   float 	divisor;
819   int		toType;			  /*  passed to XmConvertUnits */
820   Dimension	w_sum, w_cur;
821 
822   if (_XmStrContError(ctx))
823     {
824       *width = 0.0;
825       return(XmTAB_EOS);
826     }
827 
828   w_sum = 0;
829   *width = 0.0;
830 
831   /* Big units need to be converted to small ones. */
832   toType = _XmConvertFactor(units, &divisor);
833 
834   /* Calculate the width to the next tab. */
835   if (_XmStrContOpt(ctx))
836     {
837       _XmStrContError(ctx) = True;
838       return(XmTAB_EOS);
839     }
840   else
841     {
842       _XmString 		str = _XmStrContString(ctx);
843       _XmStringEntry 		line;
844       int			line_count;
845       _XmStringEntry 		seg;
846       int			seg_count;
847       _XmStringArraySegRec	array_seg;
848 
849       line_count = _XmStrLineCountGet(str);
850 
851       /* Keep checking lines and segments until we run out or hit a tab. */
852       if (_XmStrContCurrLine(ctx) < line_count)
853 	{
854 	  if (_XmStrImplicitLine(str))
855 	    {
856 	      line = _XmStrEntry(str)[_XmStrContCurrLine(ctx)];
857 	    }
858 	  else
859 	    {
860 	      _XmEntryType(&array_seg) = XmSTRING_ENTRY_ARRAY;
861 	      _XmEntrySegmentCount(&array_seg) = _XmStrEntryCount(str);
862 	      _XmEntrySegment(&array_seg) = (_XmStringNREntry *)_XmStrEntry(str);
863 	      line = (_XmStringEntry)&array_seg;
864 	    }
865 
866 	  if (_XmEntryMultiple(line))
867 	    seg_count = _XmEntrySegmentCount(line);
868 	  else
869 	    seg_count = 1;
870 
871 	  if (seg_count == 0) {
872 	    /* Empty line. */
873 	    _XmStrContCurrLine(ctx)++;
874 	    *width = 0.0;
875 	    return(XmTAB_NEWLINE);
876 	  }
877 
878 	  while (_XmStrContCurrSeg(ctx) < seg_count)
879 	    {
880 	      if (_XmEntryMultiple(line))
881 		seg = (_XmStringEntry)_XmEntrySegment(line)[_XmStrContCurrSeg(ctx)];
882 	      else
883 		seg = line;
884 
885 	      w_cur = 0;
886 
887 	      if (_XmStrContTabCount(ctx) < _XmEntryTabsGet(seg)) {
888 		_XmStrContTabCount(ctx)++;
889 		*width = (XmConvertUnits(widget, XmHORIZONTAL,
890 					 XmPIXELS, w_sum, toType) / divisor);
891 		return(XmTAB_NEXT);
892 	      }
893 
894 	      (void)_XmStringSegmentExtents(seg, rt, rend, NULL,
895 					    &w_cur, NULL, NULL, NULL);
896 	      w_sum += w_cur;
897 
898 	      _XmStrContCurrSeg(ctx)++;
899 	      _XmStrContTabCount(ctx) = 0;
900 	    }
901 
902 	  _XmStrContCurrLine(ctx)++;
903 	  _XmStrContCurrSeg(ctx) = 0;
904 	  _XmStrContTabCount(ctx) = 0;
905 
906 	  return(XmTAB_NEWLINE);
907 	}
908 
909       _XmStrContError(ctx) = True;
910       return(XmTAB_EOS);
911     }
912 }
913 
914