1 /*-----------------------------------------------------------------------*/
2 /* text.c --- text processing routines for xcircuit		 	 */
3 /* Copyright (c) 2002  Tim Edwards, Johns Hopkins University        	 */
4 /*-----------------------------------------------------------------------*/
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <ctype.h>   /* for isprint() and isdigit() */
9 
10 #ifndef _MSC_VER
11 #include <X11/Intrinsic.h>
12 #include <X11/StringDefs.h>
13 #endif
14 
15 /*------------------------------------------------------------------------*/
16 /* Local includes                                                         */
17 /*------------------------------------------------------------------------*/
18 
19 #ifdef TCL_WRAPPER
20 #include <tk.h>
21 #endif
22 
23 #include "colordefs.h"
24 #include "xcircuit.h"
25 
26 /*----------------------------------------------------------------------*/
27 /* Function prototype declarations                                      */
28 /*----------------------------------------------------------------------*/
29 #include "prototypes.h"
30 
31 /*------------------------------------------------------------------------*/
32 /* External Variable definitions                                          */
33 /*------------------------------------------------------------------------*/
34 
35 extern Display *dpy;
36 extern XCWindowData *areawin;
37 extern Globaldata xobjs;
38 extern short fontcount;
39 extern fontinfo *fonts;
40 extern colorindex *colorlist;
41 extern char _STR[150];
42 
43 #ifdef HAVE_CAIRO
44 extern const char *utf8encodings[][256];
45 #endif
46 /* Global value of distance between characters in the font catalog */
47 short del = 64;
48 
49 #ifndef TCL_WRAPPER
50 
51 /*----------------------------------------------------------------------*/
52 /* Evaluation of expression types in strings (non-Tcl version---these	*/
53 /* are PostScript expressions).						*/
54 /*									*/
55 /* For now, expressions are just copied as-is, without evaluation.	*/
56 /* An allocated string is returned, and it is the responsibility of the	*/
57 /* calling routine to free it.						*/
58 /*----------------------------------------------------------------------*/
59 
evaluate_expr(objectptr thisobj,oparamptr ops,objinstptr pinst)60 char *evaluate_expr(objectptr thisobj, oparamptr ops, objinstptr pinst)
61 {
62    UNUSED(thisobj);
63    UNUSED(pinst);
64 
65    if (ops->type != XC_EXPR) return NULL;
66    return strdup(ops->parameter.expr);
67 }
68 
69 #endif
70 
71 /*----------------------------------------------------------------------*/
72 /* Determine if a label contains a parameter.				*/
73 /*----------------------------------------------------------------------*/
74 
hasparameter(labelptr curlabel)75 Boolean hasparameter(labelptr curlabel)
76 {
77    stringpart *chrptr;
78 
79    for (chrptr = curlabel->string; chrptr != NULL; chrptr = chrptr->nextpart)
80       if (chrptr->type == PARAM_START)
81          return True;
82 
83    return False;
84 }
85 
86 /*----------------------------------------------------------------------*/
87 /* Join selected labels together.					*/
88 /*----------------------------------------------------------------------*/
89 
joinlabels()90 void joinlabels()
91 {
92   /* genericptr *genobj; (jdk) */
93    short *jl;
94    stringpart *endpart;
95    /* int tpos; (jdk) */
96    labelptr dest, source;
97 
98    if (areawin->selects < 2) {
99       Wprintf("Not enough labels selected for joining");
100       return;
101    }
102 
103    SetForeground(dpy, areawin->gc, BACKGROUND);
104 
105    for (jl = areawin->selectlist; jl < areawin->selectlist +
106 		areawin->selects; jl++) {
107       if (SELECTTYPE(jl) == LABEL) {
108 	 dest = SELTOLABEL(jl);
109 	 UDrawString(dest, DOFORALL, areawin->topinstance);
110 	 for (endpart = dest->string; endpart->nextpart != NULL; endpart =
111 			endpart->nextpart);
112 	 break;
113       }
114    }
115 
116    for (++jl; jl < areawin->selectlist + areawin->selects; jl++) {
117       if (SELECTTYPE(jl) == LABEL) {
118 	 source = SELTOLABEL(jl);
119 	 UDrawString(source, DOFORALL, areawin->topinstance);
120 	 endpart->nextpart = source->string;
121 	 for (; endpart->nextpart != NULL; endpart = endpart->nextpart);
122 	 free(source);
123 	 removep(jl, 0);
124          reviseselect(areawin->selectlist, areawin->selects, jl);
125       }
126    }
127 
128    SetForeground(dpy, areawin->gc, dest->color);
129    UDrawString(dest, dest->color, areawin->topinstance);
130 
131    incr_changes(topobject);
132    clearselects();
133 }
134 
135 /*----------------------------------------------------------------------*/
136 /* Insert a new segment into a string					*/
137 /*----------------------------------------------------------------------*/
138 
makesegment(stringpart ** strhead,stringpart * before)139 stringpart *makesegment(stringpart **strhead, stringpart *before)
140 {
141    stringpart *newptr, *lastptr, *nextptr;
142 
143    newptr = (stringpart *)malloc(sizeof(stringpart));
144    newptr->data.string = NULL;
145 
146    if (before == *strhead) {	/* insert at beginning */
147       newptr->nextpart = *strhead;
148       *strhead = newptr;
149    }
150    else {				/* otherwise */
151       for(lastptr = *strhead; lastptr != NULL;) {
152 	 nextptr = nextstringpart(lastptr, areawin->topinstance);
153 	 if (nextptr == before) {
154 	    if (lastptr->type == PARAM_START) {
155 	       oparamptr obs = NULL;
156 	       char *key = lastptr->data.string;
157 	       obs = find_param(areawin->topinstance, key);
158 	       if (obs == NULL) {
159 		  Wprintf("Error:  Bad parameter \"%s\"!", key);
160 	       } else {
161 	          obs->parameter.string = newptr;	/* ?? */
162 	       }
163 	    }
164 	    else {
165 	       lastptr->nextpart = newptr;
166 	    }
167 	    newptr->nextpart = nextptr;
168 	    break;
169          }
170 	 else if (lastptr->nextpart == before && lastptr->type == PARAM_START) {
171 	    lastptr->nextpart = newptr;
172 	    newptr->nextpart = before;
173 	    break;
174 	 }
175 	 lastptr = nextptr;
176       }
177    }
178    return newptr;
179 }
180 
181 /*----------------------------------------------------------------------*/
182 /* Split a string across text segments					*/
183 /*----------------------------------------------------------------------*/
184 
splitstring(int tpos,stringpart ** strtop,objinstptr localinst)185 stringpart *splitstring(int tpos, stringpart **strtop, objinstptr localinst)
186 {
187    int locpos, slen;
188    stringpart *newpart, *ipart;
189 
190    ipart = findstringpart(tpos, &locpos, *strtop, localinst);
191    if (locpos > 0) {        /* split the string */
192       newpart = makesegment(strtop, ipart);
193       newpart->type = TEXT_STRING;
194       newpart->data.string = ipart->data.string;
195       slen = strlen(newpart->data.string) - locpos;
196       ipart->data.string = (u_char *)malloc(slen + 1);
197       strncpy(ipart->data.string, newpart->data.string + locpos, slen  + 1);
198       *(newpart->data.string + locpos) = '\0';
199    }
200    else newpart = ipart;
201 
202    return newpart;
203 }
204 
205 /*----------------------------------------------------------------------*/
206 /* Get the next string part, linking to a parameter if necessary	*/
207 /*----------------------------------------------------------------------*/
208 
nextstringpartrecompute(stringpart * strptr,objinstptr thisinst)209 stringpart *nextstringpartrecompute(stringpart *strptr, objinstptr thisinst)
210 {
211    stringpart *nextptr = strptr->nextpart;
212 
213    if (strptr->type == PARAM_START)
214       nextptr = linkstring(thisinst, strptr, TRUE);
215    else if (strptr->type == PARAM_END) {
216       strptr->nextpart = NULL;
217 
218       /* Parameters that have a non-NULL entry in data have	*/
219       /* been promoted from an expression or numerical value	*/
220       /* to a string.  The memory allocated for this string	*/
221       /* should be free'd.					*/
222 
223       if (strptr->data.string != (u_char *)NULL) {
224 	 fprintf(stderr, "Non-NULL data in PARAM_END segment\n");
225 	 free(strptr->data.string);
226 	 strptr->data.string = (u_char *)NULL;
227       }
228    }
229    return nextptr;
230 }
231 
232 /*----------------------------------------------------------------------*/
233 /* Same as the above routine, but don't recompute expression parameters	*/
234 /* when encountered.  Use the previously generated result.		*/
235 /*----------------------------------------------------------------------*/
236 
nextstringpart(stringpart * strptr,objinstptr thisinst)237 stringpart *nextstringpart(stringpart *strptr, objinstptr thisinst)
238 {
239    stringpart *nextptr = strptr->nextpart;
240 
241    if (strptr->type == PARAM_START)
242       nextptr = linkstring(thisinst, strptr, FALSE);
243    else if (strptr->type == PARAM_END) {
244       strptr->nextpart = NULL;
245       if (strptr->data.string != (u_char *)NULL) {
246 	 fprintf(stderr, "Non-NULL data in PARAM_END segment\n");
247 	 free(strptr->data.string);
248 	 strptr->data.string = (u_char *)NULL;
249       }
250    }
251    return nextptr;
252 }
253 
254 /*----------------------------------------------------------------------*/
255 /* Remove a string part from the string					*/
256 /*----------------------------------------------------------------------*/
257 
deletestring0(stringpart * dstr,stringpart ** strtop,objinstptr thisinst,Boolean domerge)258 stringpart *deletestring0(stringpart *dstr, stringpart **strtop, objinstptr thisinst,
259 	Boolean domerge)
260 {
261    stringpart *strptr = NULL, *nextptr;
262    char *key;
263    oparamptr ops;
264 
265    if (dstr == *strtop)
266       *strtop = dstr->nextpart;
267    else {
268       strptr = *strtop;
269       while (strptr != NULL) {
270 	 nextptr = nextstringpart(strptr, thisinst);
271 	 if (nextptr == dstr) break;
272 	 strptr = nextptr;
273       }
274       if (strptr == NULL)
275 	 return NULL;
276 
277       /* If this is the begining of a parameter, then we have to figure */
278       /* out if it's an instance or a default, and change the pointer	*/
279       /* to the parameter in the parameter list, accordingly.		*/
280 
281       else if ((strptr->type == PARAM_START) && (thisinst != NULL)) {
282 	 key = strptr->data.string;
283 	 ops = find_param(thisinst, key);
284 	 if (ops == NULL) {
285 	    Fprintf(stderr, "Error in deletestring:  Bad parameter %s found\n", key);
286 	 }
287 	 else {
288 	    switch(ops->type) {
289 	       case XC_STRING:
290 		  ops->parameter.string = dstr->nextpart;
291 		  break;
292 	       case XC_EXPR:
293                   /* Deleting an expression result can only result	*/
294 		  /* in bad things happening.				*/
295 		  return NULL;
296 	       default:
297 		  /* What to be done here? */
298 		  break;
299 	    }
300 	 }
301       }
302       /* If this is the end of a parameter, we have to link the		*/
303       /* PARAM_START, not the PARAM_END, which has already been nulled.	*/
304       else if (strptr->type == PARAM_END) {
305 	 for (strptr = *strtop; strptr != NULL; strptr = strptr->nextpart) {
306 	    if (strptr->nextpart == dstr) {
307 	       strptr->nextpart = dstr->nextpart;
308 	       break;
309 	    }
310          }
311       }
312       else
313 	 strptr->nextpart = dstr->nextpart;
314    }
315    if (dstr->type == TEXT_STRING)
316       free(dstr->data.string);
317    free(dstr);
318 
319    /* attempt to merge, if legal, and requested */
320    if (strptr && domerge)
321       mergestring(strptr);
322 
323    return strptr;
324 }
325 
326 /*----------------------------------------------------------------------*/
327 /* deletestring() is a wrapper for deletestring0() 			*/
328 /*----------------------------------------------------------------------*/
329 
deletestring(stringpart * dstr,stringpart ** strtop,objinstptr thisinst)330 stringpart *deletestring(stringpart *dstr, stringpart **strtop, objinstptr thisinst)
331 {
332    return deletestring0(dstr, strtop, thisinst, TRUE);
333 }
334 
335 /*----------------------------------------------------------------------*/
336 /* Merge string parts at boundary, if parts can be legally merged	*/
337 /* If the indicated string part is text and the part following the	*/
338 /* indicated string part is also text, merge the two.  The indicated	*/
339 /* string part is returned, and the following part is freed.		*/
340 /*									*/
341 /* (Fixes thanks to Petter Larsson 11/17/03)				*/
342 /*----------------------------------------------------------------------*/
343 
mergestring(stringpart * firststr)344 stringpart *mergestring(stringpart *firststr)
345 {
346    stringpart *nextstr = NULL;
347 
348    if (firststr) nextstr = firststr->nextpart;
349    if (nextstr != NULL) {
350       if (firststr->type == TEXT_STRING && nextstr->type == TEXT_STRING) {
351          firststr->nextpart = nextstr->nextpart;
352          firststr->data.string = (char *)realloc(firststr->data.string,
353 		1 + strlen(firststr->data.string) + strlen(nextstr->data.string));
354          strcat(firststr->data.string, nextstr->data.string);
355          free(nextstr->data.string);
356          free(nextstr);
357       }
358    }
359    return firststr;
360 }
361 
362 /*----------------------------------------------------------------------*/
363 /* Link a parameter to a string						*/
364 /* If compute_exprs is TRUE, then we should recompute any expression	*/
365 /* parameters encountered.  If FALSE, then we assume that all		*/
366 /* expressions have been computed previously, and may use the recorded	*/
367 /* instance value.							*/
368 /*									*/
369 /* 11/20/06---changed to allow two different static strings to save	*/
370 /* promoted results.  This is necessary because we may be comparing	*/
371 /* two promoted results in, e.g., stringcomprelaxed(), and we don't	*/
372 /* want to overwrite the first result with the second.			*/
373 /*----------------------------------------------------------------------*/
374 
linkstring(objinstptr localinst,stringpart * strstart,Boolean compute_exprs)375 stringpart *linkstring(objinstptr localinst, stringpart *strstart,
376 	Boolean compute_exprs)
377 {
378    char *key;
379    stringpart *tmpptr, *nextptr = NULL;
380    static stringpart *promote[2] = {NULL, NULL};
381    static unsigned char pidx = 0;
382    oparamptr ops;
383 
384    if (strstart->type != PARAM_START) return NULL;
385 
386    key = strstart->data.string;
387 
388    /* In case of no calling instance, always get the default from the	*/
389    /* current page object.						*/
390 
391    if (localinst == NULL) {
392       ops = match_param(topobject, key);
393       if (ops == NULL)
394 	 return NULL;
395    }
396    else {
397       ops = find_param(localinst, key);
398       if (ops == NULL) {
399 	 /* We get here in cases where the object definition is being read,	*/
400 	 /* and there is no instance of the object to link to.  In that	*/
401 	 /* case, we ignore parameters and move on to the next part.		*/
402 	 return strstart->nextpart;
403       }
404    }
405 
406    if (ops->type != XC_STRING) {
407 
408       if (promote[pidx] == NULL) {
409          /* Generate static string for promoting numerical parameters */
410          tmpptr = makesegment(&promote[pidx], NULL);
411          tmpptr->type = TEXT_STRING;
412          tmpptr = makesegment(&promote[pidx], NULL);
413          tmpptr->type = PARAM_END;
414       }
415       else {
416 	 if (promote[pidx]->data.string != NULL) {
417 	    free(promote[pidx]->data.string);
418 	    promote[pidx]->data.string = NULL;
419 	 }
420       }
421 
422       /* Promote numerical type to string */
423       if (ops->type == XC_INT) {
424 	 promote[pidx]->data.string = (char *)malloc(13);
425 	 sprintf(promote[pidx]->data.string, "%12d", ops->parameter.ivalue);
426          nextptr = promote[pidx++];
427       }
428       else if (ops->type == XC_FLOAT) {
429 	 promote[pidx]->data.string = (char *)malloc(13);
430 	 sprintf(promote[pidx]->data.string, "%g", (double)(ops->parameter.fvalue));
431          nextptr = promote[pidx++];
432       }
433       else {	/* ops->type == XC_EXPR */
434 	 oparamptr ips;
435 	 if (!compute_exprs && (ips = match_instance_param(localinst, key))
436 		!= NULL && (ips->type == XC_STRING)) {
437 	    nextptr = ips->parameter.string;
438 	    promote[pidx]->data.string = NULL;
439 	 }
440 	 else {
441 	    promote[pidx]->data.string = evaluate_expr(((localinst == NULL) ?
442 			topobject : localinst->thisobject), ops, localinst);
443 	    if (promote[pidx]->data.string != NULL)
444                nextptr = promote[pidx++];
445 	    else
446 	       nextptr = NULL;
447 	 }
448       }
449       pidx &= 0x1;	/* pidx toggles between 0 and 1 */
450    }
451    else
452       nextptr = ops->parameter.string;
453 
454    /* If the parameter exists, link the end of the parameter back to	*/
455    /* the calling string.						*/
456 
457    if (nextptr != NULL) {
458       tmpptr = nextptr;
459       while (tmpptr->type != PARAM_END)
460 	 if ((tmpptr = tmpptr->nextpart) == NULL)
461 	    return NULL;
462       tmpptr->nextpart = strstart->nextpart;
463       return nextptr;
464    }
465    return NULL;
466 }
467 
468 /*----------------------------------------------------------------------*/
469 /* Find the last font used prior to the indicated text position		*/
470 /*----------------------------------------------------------------------*/
471 
findcurfont(int tpos,stringpart * strtop,objinstptr thisinst)472 int findcurfont(int tpos, stringpart *strtop, objinstptr thisinst)
473 {
474    stringpart *curpos;
475    int cfont = -1;
476    stringpart *strptr;
477 
478    curpos = findstringpart(tpos, NULL, strtop, thisinst);
479    for (strptr = strtop; (strptr != NULL) && (strptr != curpos);
480 		strptr = nextstringpart(strptr, thisinst))
481       if (strptr->type == FONT_NAME)
482          cfont = strptr->data.font;
483 
484    return cfont;
485 }
486 
487 /*----------------------------------------------------------------------*/
488 /* Return a local position and stringpart for the first occurrence of	*/
489 /* "substring" in the the indicated xcircuit string.  If non-NULL,	*/
490 /* "locpos" is set to the position of the substring in the stringpart,	*/
491 /* or -1 if the text was not found.  Text cannot cross stringpart	*/
492 /* boundaries (although this should be allowed).			*/
493 /*----------------------------------------------------------------------*/
494 
findtextinstring(char * search,int * locpos,stringpart * strtop,objinstptr localinst)495 stringpart *findtextinstring(char *search, int *locpos, stringpart *strtop,
496 	objinstptr localinst)
497 {
498    stringpart *strptr = strtop;
499    char *strstart;
500 
501    for (strptr = strtop; strptr != NULL; strptr = nextstringpart(strptr, localinst)) {
502       if ((strptr->type == TEXT_STRING) && strptr->data.string) {
503 	 strstart = strstr(strptr->data.string, search);
504 	 if (strstart != NULL) {
505 	    if (locpos != NULL)
506 	       *locpos = (int)(strstart - (char *)strptr->data.string);
507 	    return strptr;
508 	 }
509       }
510    }
511    if (locpos != NULL) *locpos = -1;
512    return NULL;
513 }
514 
515 /*----------------------------------------------------------------------*/
516 /* Return a local position and stringpart for "tpos" positions into	*/
517 /* the indicated string.  Position and stringpart are for the character */
518 /* or command immediately preceding "tpos"				*/
519 /*----------------------------------------------------------------------*/
520 
findstringpart(int tpos,int * locpos,stringpart * strtop,objinstptr localinst)521 stringpart *findstringpart(int tpos, int *locpos, stringpart *strtop,
522 	objinstptr localinst)
523 {
524    stringpart *strptr = strtop;
525    int testpos = 0, tmplen;
526 
527    for (strptr = strtop; strptr != NULL; strptr = nextstringpart(strptr, localinst)) {
528       if ((strptr->type == TEXT_STRING) && strptr->data.string) {
529 	 tmplen = strlen(strptr->data.string);
530 	 if (testpos + tmplen > tpos) {
531 	    if (locpos != NULL) *locpos = (tpos - testpos);
532 	    return strptr;
533 	 }
534 	 else testpos += tmplen - 1;
535       }
536       if (locpos != NULL) *locpos = -1;
537       if (testpos >= tpos) return strptr;
538 
539       testpos++;
540    }
541    return NULL;
542 }
543 
544 /*----------------------------------------------------------------------*/
545 /* The following must be in an order matching the "Text string part	*/
546 /* types" defined in xcircuit.h. 					*/
547 /*----------------------------------------------------------------------*/
548 
549 static char *nonprint[] = {
550 	"Text", "Subscript", "Superscript", "Normalscript",
551 	"Underline", "Overline", "Noline",
552 	"Tab_Stop", "Tab_Forward", "Tab_Backward",
553 	"Halfspace", "Quarterspace", "<Return>",
554 	"Font", "Scale", "Color", "Margin_Stop", "Kern",
555         "Parameter", ">", "Net_Name", "Error", NULL}; /* (jdk) */
556 
557 /* Handling of certain text escapes (subscript, superscript, underline,	*/
558 /* and overline) in TeX (added by Fabian Inostroza)			*/
559 
560 static char *nonprinttex[] = {
561 	"", "_{", "^{", "}",
562 	"\\underline{", "\\overline{", "}",
563 	"Tab_Stop", "Tab_Forward", "Tab_Backward",
564 	"Halfspace", "Quarterspace", "<Return>",
565 	"Font", "Scale", "Color", "Margin_Stop", "Kern",
566         "Parameter", ">", "Net_Name", "Error", NULL};
567 
568 /*----------------------------------------------------------------------*/
569 /* charprint():  							*/
570 /* Write a printable version of the character or command at the 	*/
571 /* indicated string part and position.					*/
572 /*----------------------------------------------------------------------*/
573 
charprint(char * sout,stringpart * strptr,int locpos)574 void charprint(char *sout, stringpart *strptr, int locpos)
575 {
576    char sc;
577 
578    switch (strptr->type) {
579       case TEXT_STRING:
580 	 if (strptr->data.string) {
581             if (locpos > (int) strlen(strptr->data.string)) {
582 	       strcpy(sout, "<ERROR>");
583 	    }
584             else sc = *(strptr->data.string + locpos);
585             if (isprint(sc))
586 	       sprintf(sout, "%c", sc);
587             else
588 	       sprintf(sout, "/%03o", (u_char)sc);
589 	 }
590 	 else
591 	    *sout = '\0';
592 	 break;
593       case FONT_NAME:
594 	 sprintf(sout, "Font=%s", (strptr->data.font >= fontcount) ?
595 		"(unknown)" : fonts[strptr->data.font].psname);
596 	 break;
597       case FONT_SCALE:
598 	 sprintf(sout, "Scale=%3.2f", strptr->data.scale);
599 	 break;
600       case KERN:
601 	 sprintf(sout, "Kern=(%d,%d)", strptr->data.kern[0], strptr->data.kern[1]);
602 	 break;
603       case PARAM_START:
604 	 sprintf(sout, "Parameter(%s)<", strptr->data.string);
605 	 break;
606       default:
607          strcpy(sout, nonprint[strptr->type]);
608 	 break;
609    }
610 }
611 
612 /*----------------------------------------------------------------------*/
613 /* Version of the above, for printing LaTeX strings			*/
614 /* added by Fabian Inostroza  7/14/2013					*/
615 /*----------------------------------------------------------------------*/
616 
charprinttex(char * sout,stringpart * strptr,int locpos)617 void charprinttex(char *sout, stringpart *strptr, int locpos)
618 {
619    char sc;
620 
621    switch (strptr->type) {
622       case TEXT_STRING:
623 	 if (strptr->data.string) {
624             if (locpos > (int) strlen(strptr->data.string)) {
625 	       strcpy(sout, "<ERROR>");
626 	    }
627             else sc = *(strptr->data.string + locpos);
628             if (isprint(sc))
629 	       sprintf(sout, "%c", sc);
630             else
631 	       sprintf(sout, "/%03o", (u_char)sc);
632 	 }
633 	 else
634 	    *sout = '\0';
635 	 break;
636 
637       default:
638 	 *sout = '\0';
639 	 break;
640    }
641 }
642 
643 /*----------------------------------------------------------------------*/
644 /* Print a string (allocates memory for the string; must be freed by	*/
645 /* the calling routine).						*/
646 /*----------------------------------------------------------------------*/
647 
xcstringtostring(stringpart * strtop,objinstptr localinst,Boolean textonly)648 char *xcstringtostring(stringpart *strtop, objinstptr localinst, Boolean textonly)
649 {
650    stringpart *strptr;
651    int pos = 0, locpos;
652    char *sout;
653 
654    sout = (char *)malloc(1);
655    sout[0] = '\0';
656 
657    while ((strptr = findstringpart(pos++, &locpos, strtop, localinst)) != NULL) {
658       if (!textonly || strptr->type == TEXT_STRING) {
659          charprint(_STR, strptr, locpos);
660          sout = (char *)realloc(sout, strlen(sout) + strlen(_STR) + 1);
661          strcat(sout, _STR);
662       }
663       /* Overbar on schematic names is translated to logical-NOT ("!") */
664       else if (textonly && strptr->type == OVERLINE) {
665          sout = (char *)realloc(sout, strlen(sout) + 2);
666          strcat(sout, "!");
667       }
668    }
669    return sout;
670 }
671 
672 /*----------------------------------------------------------------------*/
673 /* Version of the above, for printing LaTeX strings			*/
674 /* added by Fabian Inostroza  7/14/2013					*/
675 /*----------------------------------------------------------------------*/
676 
textprinttex(stringpart * strtop,objinstptr localinst)677 char *textprinttex(stringpart *strtop, objinstptr localinst)
678 {
679    stringpart *strptr;
680    int pos = 0, locpos;
681    char *sout;
682 
683    sout = (char *)malloc(1);
684    sout[0] = '\0';
685 
686    while ((strptr = findstringpart(pos++, &locpos, strtop, localinst)) != NULL) {
687       charprinttex(_STR, strptr, locpos);
688       sout = (char *)realloc(sout, strlen(sout) + strlen(_STR) + 1);
689       strcat(sout, _STR);
690    }
691    return sout;
692 }
693 
694 /*----------------------------------------------------------------------*/
695 /* Wrappers for xcstringtostring():					*/
696 /*  stringprint() includes information on text controls appropriate	*/
697 /*  for printing in the message window (charreport())			*/
698 /*----------------------------------------------------------------------*/
699 
stringprint(stringpart * strtop,objinstptr localinst)700 char *stringprint(stringpart *strtop, objinstptr localinst)
701 {
702    return xcstringtostring(strtop, localinst, False);
703 }
704 
705 /*----------------------------------------------------------------------*/
706 /*  textprint() excludes text controls, resulting in a string 		*/
707 /*  appropriate for netlist information, or for promoting a string	*/
708 /*  parameter to a numeric type.					*/
709 /*----------------------------------------------------------------------*/
710 
textprint(stringpart * strtop,objinstptr localinst)711 char *textprint(stringpart *strtop, objinstptr localinst)
712 {
713    return xcstringtostring(strtop, localinst, True);
714 }
715 
716 /*----------------------------------------------------------------------*/
717 /* textprintsubnet() is like textprint(), except that strings in bus	*/
718 /* notation are reduced to just the single subnet.			*/
719 /*----------------------------------------------------------------------*/
720 
textprintsubnet(stringpart * strtop,objinstptr localinst,int subnet)721 char *textprintsubnet(stringpart *strtop, objinstptr localinst, int subnet)
722 {
723    char *newstr, *busptr, *endptr, *substr;
724 
725    newstr = xcstringtostring(strtop, localinst, True);
726    if (subnet >= 0) {
727       busptr = strchr(newstr, areawin->buschar);
728       if (busptr != NULL) {
729 	 endptr = find_delimiter(busptr);
730 	 if (endptr != NULL) {
731 	    if (busptr == newstr)
732 	       sprintf(newstr, "%d", subnet);
733 	    else {
734 	       substr = strdup(newstr);
735 	       busptr++;
736 	       sprintf(substr + (int)(busptr - newstr), "%d%s", subnet, endptr);
737 	       free(newstr);
738 	       return substr;
739 	    }
740 	 }
741       }
742       else {
743 	 /* Promote a non-bus label to a bus label */
744 	 substr = malloc(10 + strlen(newstr));
745 	 strcpy(substr, newstr);
746 	 endptr = substr;
747 	 while ((*endptr) != '\0') endptr++;
748 	 sprintf(endptr, "%c%d%c", areawin->buschar, subnet,
749 		standard_delimiter_end(areawin->buschar));
750 	 free(newstr);
751 	 return substr;
752       }
753    }
754    return newstr;
755 }
756 
757 /*----------------------------------------------------------------------*/
758 /* Another variant:  Print a subnet list according to the entries in	*/
759 /* a netlist (Genericlist *) pointer.  This includes the condition in	*/
760 /* which the list is a single wire, not a bus.  If "pinstring" is non-	*/
761 /* NULL, it will be used as the name of the subnet.  Otherwise, 	*/
762 /* "prefix" will be used, and will be appended with the net ID of the	*/
763 /* first net in the sublist to make the identifier unique.		*/
764 /*----------------------------------------------------------------------*/
765 
textprintnet(char * prefix,char * pinstring,Genericlist * sublist)766 char *textprintnet(char *prefix, char *pinstring, Genericlist *sublist)
767 {
768    char *newstr, *sptr;
769    buslist *sbus;
770    int i;
771    UNUSED(pinstring);
772 
773    if (sublist->subnets == 0) {
774       newstr = (char *)malloc(strlen(prefix) + 10);
775       sprintf(newstr, "%s%d", prefix, sublist->net.id);
776    }
777    else {	/* just make a comma-separated list */
778       newstr = (char *)malloc(strlen(prefix) + 20 + 3 * sublist->subnets);
779       sbus = sublist->net.list;
780       sprintf(newstr, "%s%d%c", prefix, sbus->netid, areawin->buschar);
781       for (i = 0; i < sublist->subnets; i++) {
782 	 sbus = sublist->net.list + i;
783 	 sptr = newstr + strlen(newstr);
784 	 if (i != 0)
785 	    strcat(sptr++, ",");
786 	 sprintf(sptr, "%d", sbus->subnetid);
787       }
788       sptr = newstr + strlen(newstr);
789       sprintf(sptr, "%c", standard_delimiter_end(areawin->buschar));
790    }
791    return newstr;
792 }
793 
794 /*----------------------------------------------------------------------*/
795 /* Test equivalence of the text string parts of a label with the	*/
796 /* indicated char * string.						*/
797 /*									*/
798 /* Return 0 if the strings match.  Return 1 or the result of strcmp()	*/
799 /* on the first non-matching substring text segment if the strings	*/
800 /* don't match.								*/
801 /*									*/
802 /* If "exact" is True, requires an exact match, otherwise requires	*/
803 /* that the label only match text to the length of text.		*/
804 /*----------------------------------------------------------------------*/
805 
textcompx(stringpart * string,char * text,Boolean exact,objinstptr localinst)806 int textcompx(stringpart *string, char *text, Boolean exact, objinstptr localinst)
807 {
808    stringpart *strptr;
809    char *tptr = text;
810    char *sptr;
811    int rval;
812    size_t llen = strlen(text), slen;
813    Boolean has_text = FALSE;
814 
815    for (strptr = string; strptr != NULL; strptr = nextstringpart(strptr, localinst)) {
816       if (strptr->type == TEXT_STRING) {
817 	 has_text = TRUE;
818 	 sptr = strptr->data.string;
819 	 slen = min(strlen(sptr), llen);
820 	 llen -= slen;
821 	 if (!exact && (rval = strncmp(sptr, tptr, slen)))
822 	    return rval;
823 	 else if (exact && (rval = strcmp(sptr, tptr)))
824 	    return rval;
825 	 else if (!exact && (llen == 0))
826 	    return 0;
827 	 else
828 	    tptr += slen;
829       }
830    }
831 
832    /* Check condition that no text was encountered in the xcircuit string */
833    return ((llen > 0) && !has_text) ? 1 : 0;
834 }
835 
836 /*----------------------------------------------------------------------*/
837 /* Wrappers for textcompx(), equivalent to strcmp() and strncmp().	*/
838 /*----------------------------------------------------------------------*/
839 
textcomp(stringpart * string,char * text,objinstptr localinst)840 int textcomp(stringpart *string, char *text, objinstptr localinst)
841 {
842    return textcompx(string, text, True, localinst);
843 }
844 
845 /*----------------------------------------------------------------------*/
846 
textncomp(stringpart * string,char * text,objinstptr localinst)847 int textncomp(stringpart *string, char *text, objinstptr localinst)
848 {
849    return textcompx(string, text, False, localinst);
850 }
851 
852 /*----------------------------------------------------------------------*/
853 /* Test equivalence of two label strings				*/
854 /*----------------------------------------------------------------------*/
855 
stringcomp(stringpart * string1,stringpart * string2)856 int stringcomp(stringpart *string1, stringpart *string2)
857 {
858    stringpart *strptr1, *strptr2;
859 
860    for (strptr1 = string1, strptr2 = string2; strptr1 != NULL && strptr2 != NULL;
861 		strptr1 = strptr1->nextpart, strptr2 = strptr2->nextpart) {
862       if (strptr1->type != strptr2->type)
863 	 return 1;
864       else {
865          switch (strptr1->type) {
866 	    case TEXT_STRING:
867 	       if (strptr1->data.string && strptr2->data.string) {
868 	          if (strcmp(strptr1->data.string, strptr2->data.string))
869 		     return 1;
870 	       }
871 	       else if (strptr1->data.string || strptr2->data.string)
872 		  return 1;
873 	       break;
874 	    case FONT_SCALE:
875 	       if (strptr1->data.scale != strptr2->data.scale) return 1;
876 	       break;
877 	    case FONT_COLOR:
878 	       if (strptr1->data.color != strptr2->data.color) return 1;
879 	       break;
880 	    case FONT_NAME:
881 	       if (strptr1->data.font != strptr2->data.font) return 1;
882 	       break;
883 	    case KERN:
884 	       if (strptr1->data.kern[0] != strptr2->data.kern[0] ||
885 		   strptr1->data.kern[1] != strptr2->data.kern[1]) return 1;
886 	       break;
887 	 }
888       }
889    }
890 
891    /* One string continues after the other ends. . . */
892    if (strptr1 != NULL || strptr2 != NULL) return 1;
893    return 0;
894 }
895 
896 /*----------------------------------------------------------------------*/
897 /* Test if the specified font is in the "Symbol" font family.		*/
898 /*----------------------------------------------------------------------*/
899 
issymbolfont(int fontnumber)900 Boolean issymbolfont(int fontnumber)
901 {
902    if (!strcmp(fonts[fontnumber].family, "Symbol")) return True;
903    return False;
904 }
905 
906 /*----------------------------------------------------------------------*/
907 /* Test if the specified font is in the "Helvetica" font family.	*/
908 /* SVG uses this to make sure it scales down the font by 7/8 to match	*/
909 /* our internal vectors, and to use "oblique" for the style instead of	*/
910 /* "italic".								*/
911 /*----------------------------------------------------------------------*/
912 
issansfont(int fontnumber)913 Boolean issansfont(int fontnumber)
914 {
915    if (!strcmp(fonts[fontnumber].family, "Helvetica")) return True;
916    return False;
917 }
918 
919 /*----------------------------------------------------------------------*/
920 /* Test if the specified font is ISO-Latin1 encoding			*/
921 /*----------------------------------------------------------------------*/
922 
isisolatin1(int fontnumber)923 Boolean isisolatin1(int fontnumber)
924 {
925    if ((fonts[fontnumber].flags & 0xf80) == 0x100) return True;
926    return False;
927 }
928 
929 /*----------------------------------------------------------------------*/
930 /* For a label representing a single bus subnet, return the index of	*/
931 /* the subnet.								*/
932 /*----------------------------------------------------------------------*/
933 
sub_bus_idx(labelptr thislab,objinstptr thisinst)934 int sub_bus_idx(labelptr thislab, objinstptr thisinst)
935 {
936    stringpart *strptr;
937    char *busptr;
938    int busidx;
939 
940    for (strptr = thislab->string; strptr != NULL; strptr =
941 		nextstringpart(strptr, thisinst)) {
942       if (strptr->type == TEXT_STRING) {
943 	 if ((busptr = strchr(strptr->data.string, areawin->buschar)) != NULL) {
944 	    if (sscanf(++busptr, "%d", &busidx) == 1)
945 	       return busidx;
946 	 }
947 	 if (sscanf(strptr->data.string, "%d", &busidx) == 1)
948 	    return busidx;
949       }
950    }
951    return -1;
952 }
953 
954 /*----------------------------------------------------------------------*/
955 /* The following routine is like sub_bus_idx but returns TRUE or FALSE	*/
956 /* depending on whether the label was determined to be in bus notation	*/
957 /* or not.  Note that sub_bux_idx may be run on sub-bus names (those	*/
958 /* that are internally generated from labels in bus notation), but	*/
959 /* pin_is_bus should not, because pin numbers in bus notation get the	*/
960 /* bus delimiters stripped from them.					*/
961 /*----------------------------------------------------------------------*/
962 
pin_is_bus(labelptr thislab,objinstptr thisinst)963 Boolean pin_is_bus(labelptr thislab, objinstptr thisinst)
964 {
965    stringpart *strptr;
966    char *busptr;
967    /* int busidx; (jdk) */
968    Boolean found_delimiter = FALSE;
969 
970    for (strptr = thislab->string; strptr != NULL; strptr =
971 		nextstringpart(strptr, thisinst)) {
972       if (strptr->type == TEXT_STRING) {
973 	 if ((busptr = strchr(strptr->data.string, areawin->buschar)) != NULL) {
974 	    if (isdigit(*(++busptr)))
975 	       return TRUE;
976 	    else
977 	       found_delimiter = TRUE;
978 	 }
979 	 else if (found_delimiter == TRUE) {
980 	    return (isdigit(*(strptr->data.string))) ? TRUE : FALSE;
981 	 }
982       }
983    }
984    return FALSE;
985 }
986 
987 /*----------------------------------------------------------------------*/
988 /* When encountering a label with bus notation, create a list of	*/
989 /* subnets belonging to the bus.  Return the list as a pointer to a	*/
990 /* Genericlist structure.  This structure is statically allocated and	*/
991 /* is expected to have its contents copied into the target netlist	*/
992 /* element.								*/
993 /*									*/
994 /* Unlike the above routine, this routine prints the original string	*/
995 /* into a char* array using textprint() so that escape sequences are	*/
996 /* removed and will not affect the result.				*/
997 /*									*/
998 /* To speed things up, the calling routine should have already called	*/
999 /* pin_is_bus() to determine if the label does indeed represent a bus.	*/
1000 /* break_up_bus() is much slower in determining this.  If break_up_bus	*/
1001 /* is passed a string that cannot be identified as a bus, then it	*/
1002 /* returns a NULL pointer.						*/
1003 /*									*/
1004 /* If netlist points to a structure with no subnets, then its net ID	*/
1005 /* is the starting point for the nets returned by break_up_bus.  	*/
1006 /* Otherwise, netlist is assumed to be a valid netlist for a bus that	*/
1007 /* matches "blab", and we will use net IDs from this list.		*/
1008 /*----------------------------------------------------------------------*/
1009 
break_up_bus(labelptr blab,objinstptr thisinst,Genericlist * netlist)1010 Genericlist *break_up_bus(labelptr blab, objinstptr thisinst, Genericlist *netlist)
1011 {
1012    static Genericlist *subnets = NULL;
1013    char *tptr;
1014    buslist *sbus, *jbus;
1015    int istart, iend, i, j, netstart, matched;
1016    char *buspos, *busend, *busptr;
1017 
1018    if (pin_is_bus(blab, thisinst) == FALSE) return NULL;
1019    if (subnets == NULL) {
1020       /* This happens on the first pass only */
1021       subnets = (Genericlist *)malloc(sizeof(Genericlist));
1022       subnets->net.list = (buslist *)malloc(sizeof(buslist));
1023    }
1024    subnets->subnets = 0;
1025 
1026    tptr = textprint(blab->string, thisinst);
1027    buspos = strchr(tptr, areawin->buschar);
1028 
1029    /* The notation "(...)" with NO TEXT preceding the opening bus	*/
1030    /* delimiter, is assumed to represent a numerical pin range.  So	*/
1031    /* instead of generating labels, e.g., "(1)", "(2)", etc., we	*/
1032    /* generate	labels "1", "2", etc.					*/
1033 
1034    if (buspos == NULL) {
1035       Fprintf(stderr, "Error:  Bus specification has no start delimiter!\n");
1036       goto doneBus;
1037    }
1038 
1039    netstart = (netlist->subnets == 0) ? netlist->net.id : 0;
1040 
1041    busend = find_delimiter(buspos);
1042 
1043    if (busend == NULL) {
1044       Fprintf(stderr, "Error:  Bus specification has no end delimiter!\n");
1045       goto doneBus;
1046    }
1047 
1048    /* Find the range of each bus */
1049 
1050    matched = 0;
1051    istart = -1;
1052    for (busptr = buspos + 1; busptr < busend; busptr++) {
1053       if (sscanf(busptr, "%d", &iend) == 0) break;
1054       while ((*busptr != ':') && (*busptr != '-') && (*busptr != ',')
1055 		&& (*busptr != *busend))
1056 	 busptr++;
1057       if ((*busptr == ':') || (*busptr == '-')) 	/* numerical range */
1058 	 istart = iend;
1059       else {
1060 	 if (istart < 0) istart = iend;
1061 	 i = istart;
1062 	 while (1) {
1063 
1064 	    /* Create a new list entry for this subnet number */
1065 
1066 	    subnets->subnets++;
1067 	    subnets->net.list = (buslist *)realloc(subnets->net.list,
1068 			subnets->subnets * sizeof(buslist));
1069 
1070 	    sbus = subnets->net.list + subnets->subnets - 1;
1071 	    sbus->subnetid = i;
1072 	    if (netstart > 0) {
1073 	       sbus->netid = netstart++;
1074 	       matched++;
1075 	    }
1076 	    else {
1077 	       /* Net ID is the net ID for the matching subnet of netlist */
1078 	       for (j = 0; j < netlist->subnets; j++) {
1079 		  jbus = netlist->net.list + j;
1080 		  if (jbus->subnetid == i) {
1081 		     matched++;
1082 		     sbus->netid = jbus->netid;
1083 		     break;
1084 		  }
1085 	       }
1086 	       /* Insert a net ID of zero if it can't be found */
1087 	       if (j == netlist->subnets) {
1088 		  sbus->netid = 0;
1089 	       }
1090 	    }
1091 
1092 	    if (i == iend) break;
1093 	    else if (istart > iend) i--;
1094 	    else i++;
1095 	 }
1096 	 istart = -1;
1097       }
1098    }
1099 
1100 doneBus:
1101    free(tptr);
1102    return (matched == 0) ? NULL : subnets;
1103 }
1104 
1105 /*----------------------------------------------------------------------*/
1106 /* Test equivalence of two label strings (relaxed constraints)		*/
1107 /*    Like stringcomp(), but ignores "superficial" differences such as  */
1108 /*    color and font (unless one of the fonts is Symbol), scale,	*/
1109 /*    underlining, tabbing, and kerning.				*/
1110 /*									*/
1111 /* Return 0 if matching.  Return 1 if not matching.			*/
1112 /*									*/
1113 /* For bus notation, ignore everything inside the bus delimiters.	*/
1114 /* The calling routine must determine if the labels being compared	*/
1115 /* have matching subnet ranges.  Note that as written, this does not	*/
1116 /* check any text occurring after the opening bus delimiter!  Thus,	*/
1117 /* "mynet(1:2)" and "mynet(3:4)" and "mynet(1)_alt" are all considered	*/
1118 /* exact matches in bus notation.					*/
1119 /*----------------------------------------------------------------------*/
1120 
stringcomprelaxed(stringpart * string1,stringpart * string2,objinstptr thisinst)1121 int stringcomprelaxed(stringpart *string1, stringpart *string2,
1122 			objinstptr thisinst)
1123 {
1124    stringpart *strptr1 = string1, *strptr2 = string2;
1125    Boolean font1 = False, font2 = False, inbus_match = TRUE;
1126    int in_bus = 0;
1127    char *buspos;
1128    int bpos;
1129 
1130    if (strptr1->type == FONT_NAME)
1131       font1 = issymbolfont(strptr1->data.font);
1132    if (strptr2->type == FONT_NAME)
1133       font2 = issymbolfont(strptr2->data.font);
1134 
1135    while ((strptr1 != NULL) || (strptr2 != NULL)) {
1136       while (strptr1 != NULL && strptr1->type != TEXT_STRING &&
1137 		strptr1->type != OVERLINE) {
1138 	 if (strptr1->type == FONT_NAME)
1139 	    font1 = issymbolfont(strptr1->data.font);
1140 	 strptr1 = nextstringpart(strptr1, thisinst);
1141       }
1142       while (strptr2 != NULL && strptr2->type != TEXT_STRING &&
1143 		strptr2->type != OVERLINE) {
1144 	 if (strptr2->type == FONT_NAME)
1145 	    font2 = issymbolfont(strptr2->data.font);
1146 	 strptr2 = nextstringpart(strptr2, thisinst);
1147       }
1148       if (strptr1 == NULL || strptr2 == NULL) break;
1149       if (font1 != font2) return 1;
1150       if (strptr1->type != strptr2->type) return 1;
1151       else {
1152          switch (strptr1->type) {
1153 	    case TEXT_STRING:
1154 	       if (in_bus == 1) {
1155 		  char matchchar = areawin->buschar;
1156 		  switch (areawin->buschar) {
1157 		     case '(': matchchar = ')'; break;
1158 		     case '[': matchchar = ']'; break;
1159 		     case '{': matchchar = '}'; break;
1160 		     case '<': matchchar = '>'; break;
1161 		  }
1162 		  buspos = strchr(strptr1->data.string, matchchar);
1163 		  if (buspos != NULL) {
1164 		     bpos = (int)(buspos - (char *)(strptr1->data.string));
1165 		     if ((int) strlen(strptr2->data.string) > bpos) {
1166 		        if (!strcmp(strptr1->data.string + bpos,
1167 			  	strptr2->data.string + bpos)) {
1168 		           in_bus = 2;
1169 		           break;
1170 			}
1171 		     }
1172 		     return 1;
1173 		  }
1174 		  if (inbus_match == TRUE)
1175 		     if (strcmp(strptr1->data.string, strptr2->data.string))
1176 			 inbus_match = FALSE;
1177 	       }
1178 	       else if (!strcmp(strptr1->data.string, strptr2->data.string))
1179 		  break;
1180 
1181 	       /* To be a matching bus, the strings match everywhere	*/
1182 	       /* except between the bus notation delimiters (default	*/
1183 	       /* "()".							*/
1184 
1185 	       buspos = strchr(strptr1->data.string, areawin->buschar);
1186 	       if (buspos != NULL) {
1187 
1188 		  bpos = (int)(buspos - (char *)(strptr1->data.string)) + 1;
1189 		  if (!strncmp(strptr1->data.string, strptr2->data.string, bpos)) {
1190 		     in_bus = 1;
1191 		     break;
1192 		  }
1193 	       }
1194 	       return 1;  /* strings did not match, exactly or as bus notation */
1195 	       break;
1196 	    case OVERLINE:
1197 	       if (strptr1->type != strptr2->type) return 1;
1198 	       break;
1199          }
1200 	 strptr1 = nextstringpart(strptr1, thisinst);
1201 	 strptr2 = nextstringpart(strptr2, thisinst);
1202       }
1203    }
1204 
1205    /* One string continues after the other ends. . . */
1206    if (strptr1 != NULL || strptr2 != NULL) return 1;
1207 
1208    /* Treat no closing bus delimiter as a non-bus string */
1209    else if ((in_bus == 1) && (inbus_match == FALSE)) return 1;
1210    return 0;
1211 }
1212 
1213 /*----------------------------------------------------------------------*/
1214 /* Find the number of parts in a string	(excluding parameter contents)	*/
1215 /*----------------------------------------------------------------------*/
1216 
stringparts(stringpart * string)1217 int stringparts(stringpart *string)
1218 {
1219    stringpart *strptr;
1220    int ptotal = 0;
1221 
1222    for (strptr = string; strptr != NULL; strptr = strptr->nextpart)
1223       ptotal++;
1224 
1225    return ptotal;
1226 }
1227 
1228 /*----------------------------------------------------------------------*/
1229 /* Compute the total character length of a string 			*/
1230 /*    If "doparam" is True, include parameter contents.			*/
1231 /*----------------------------------------------------------------------*/
1232 
stringlength(stringpart * string,Boolean doparam,objinstptr thisinst)1233 int stringlength(stringpart *string, Boolean doparam, objinstptr thisinst)
1234 {
1235    stringpart *strptr;
1236    int ctotal = 0;
1237 
1238    for (strptr = string; strptr != NULL; strptr = (doparam) ?
1239 		nextstringpart(strptr, thisinst) : strptr->nextpart) {
1240       if (strptr->type == TEXT_STRING) {
1241 	 if (strptr->data.string)
1242 	    ctotal += strlen(strptr->data.string);
1243       }
1244       else
1245 	 ctotal++;
1246    }
1247 
1248    return ctotal;
1249 }
1250 
1251 /*----------------------------------------------------------------------*/
1252 /* Copy the contents of a string (excluding parameter contents)		*/
1253 /*----------------------------------------------------------------------*/
1254 
stringcopy(stringpart * string)1255 stringpart *stringcopy(stringpart *string)
1256 {
1257    stringpart *strptr, *newpart, *newtop = NULL, *topptr;
1258 
1259    for (strptr = string; strptr != NULL; strptr = strptr->nextpart) {
1260 
1261       /* Don't use makesegment(), which looks at parameter contents */
1262       newpart = (stringpart *)malloc(sizeof(stringpart));
1263       newpart->nextpart = NULL;
1264       if (newtop == NULL)
1265 	 newtop = newpart;
1266       else
1267 	 topptr->nextpart = newpart;
1268       topptr = newpart;
1269 
1270       newpart->type = strptr->type;
1271       if ((strptr->type == TEXT_STRING) || (strptr->type == PARAM_START)) {
1272 	 newpart->data.string = (char *)malloc(1 + strlen(strptr->data.string));
1273 	 strcpy(newpart->data.string, strptr->data.string);
1274       }
1275       else
1276          newpart->data = strptr->data;
1277    }
1278    return newtop;
1279 }
1280 
1281 /*----------------------------------------------------------------------*/
1282 /* Copy the contents of a string, embedding parameter contents		*/
1283 /*----------------------------------------------------------------------*/
1284 
stringcopyall(stringpart * string,objinstptr thisinst)1285 stringpart *stringcopyall(stringpart *string, objinstptr thisinst)
1286 {
1287    stringpart *strptr, *newpart, *newtop, *topend;
1288 
1289    for (strptr = string; strptr != NULL;
1290 		strptr = nextstringpart(strptr, thisinst)) {
1291       newpart = (stringpart *)malloc(sizeof(stringpart));
1292       newpart->type = strptr->type;
1293       newpart->nextpart = NULL;
1294       if (strptr == string) newtop = newpart;
1295       else topend->nextpart = newpart;
1296       topend = newpart;
1297       if ((strptr->type == TEXT_STRING || strptr->type == PARAM_START)
1298 		&& strptr->data.string) {
1299 	 newpart->data.string = (char *)malloc(1 + strlen(strptr->data.string));
1300 	 strcpy(newpart->data.string, strptr->data.string);
1301       }
1302       else
1303          newpart->data = strptr->data;
1304    }
1305    return newtop;
1306 }
1307 
1308 /*----------------------------------------------------------------------*/
1309 /* Copy the contents of a saved string with embedded parameter contents	*/
1310 /* back to the string and the instance parameters.			*/
1311 /*----------------------------------------------------------------------*/
1312 
stringcopyback(stringpart * string,objinstptr thisinst)1313 stringpart *stringcopyback(stringpart *string, objinstptr thisinst)
1314 {
1315    stringpart *strptr, *newpart, *curend = NULL;
1316    stringpart *rettop, *curtop, *savend = NULL;
1317    char *key = NULL;
1318    oparamptr pparam;
1319    Boolean need_free;
1320 
1321    for (strptr = string; strptr != NULL; strptr = strptr->nextpart) {
1322 
1323       newpart = (stringpart *)malloc(sizeof(stringpart));
1324       newpart->type = strptr->type;
1325       newpart->nextpart = NULL;
1326       newpart->data.string = NULL;
1327 
1328       if (strptr == string) rettop = newpart;	/* initial segment */
1329       else curend->nextpart = newpart;  	/* append segment to label */
1330 
1331       if (curend) {
1332          if (curend->type == PARAM_START) {
1333 	    key = curend->data.string;
1334 	    curtop = newpart;
1335 	    savend = curend;
1336 	    need_free = False;
1337          }
1338          else if (curend->type == PARAM_END) {
1339 	    curend->nextpart = NULL;
1340 	    savend->nextpart = newpart;
1341 	    if (need_free) freelabel(curtop);
1342 	    need_free = False;
1343          }
1344       }
1345       curend = newpart;
1346 
1347       if (strptr->type == TEXT_STRING || strptr->type == PARAM_START) {
1348 	 if (strptr->data.string) {
1349 	    newpart->data.string = (char *)malloc(1 + strlen(strptr->data.string));
1350 	    strcpy(newpart->data.string, strptr->data.string);
1351 	 }
1352 	 else
1353 	    newpart->data.string = NULL;
1354       }
1355       else if (strptr->type == PARAM_END) {
1356 	 if (key != NULL) {
1357 	    pparam = find_param(thisinst, key);
1358 	    if (pparam == NULL) {
1359 	       Fprintf(stderr, "Error:  Bad parameter %s encountered!\n", key);
1360 	    }
1361 	    else if (pparam->type == XC_STRING) {
1362 	       freelabel(pparam->parameter.string);
1363 	       pparam->parameter.string = curtop;
1364 	       key = NULL;
1365 	    }
1366 	    else {
1367 	       float fval;
1368 	       int ival;
1369 	       char *tmpstr = textprint(curtop, thisinst);
1370 
1371 	       /* Promote string types into the type of the parameter */
1372 	       switch (pparam->type) {
1373 		  case XC_INT:
1374 		     if (sscanf(tmpstr, "%d", &ival) == 1)
1375 		        pparam->parameter.ivalue = ival;
1376 		     free(tmpstr);
1377 		     break;
1378 		  case XC_FLOAT:
1379 		     if (sscanf(tmpstr, "%g", &fval) == 1)
1380 		        pparam->parameter.fvalue = fval;
1381 		     break;
1382 		  case XC_EXPR:
1383 		     /* Expression results are derived and cannot be	*/
1384 		     /* changed except by changing the expression	*/
1385 		     /* itself.						*/
1386 		     break;
1387 	       }
1388 	       free(tmpstr);
1389 	       need_free = True;
1390 	       key = NULL;
1391 	    }
1392 	 }
1393          else {
1394 	    Fprintf(stderr, "Error:  Bad parameter in stringcopyback()\n");
1395 	 }
1396       }
1397       else
1398          newpart->data = strptr->data;
1399    }
1400 
1401    /* tie up loose ends, if necessary */
1402    if ((curend != NULL) && (curend->type == PARAM_END)) {
1403       savend->nextpart = NULL;
1404       if (need_free) freelabel(curtop);
1405    }
1406 
1407    return rettop;
1408 }
1409 
1410 /*---------------------------------------------------------------------*/
1411 /* draw a single character at 0, 0 using current transformation matrix */
1412 /*---------------------------------------------------------------------*/
1413 
1414 #ifndef HAVE_CAIRO
UDrawChar(u_char code,short styles,short ffont,int groupheight,int passcolor,float passwidth)1415 static float UDrawChar(u_char code, short styles, short ffont, int groupheight,
1416 	int passcolor, float passwidth)
1417 {
1418    objectptr drawchar;
1419    XPoint alphapts[2];
1420    float localwidth;
1421    objinst charinst;
1422 
1423    if ((ffont >= fontcount) || (fonts[ffont].encoding == NULL))
1424       return 0;
1425 
1426    alphapts[0].x = 0;
1427    alphapts[0].y = 0;
1428    charinst.type = OBJINST;
1429    charinst.color = DEFAULTCOLOR;
1430    charinst.rotation = 0.0;
1431    charinst.scale = fonts[ffont].scale;
1432    charinst.position = alphapts[0];
1433    charinst.params = NULL;
1434 
1435    /* get proper font and character */
1436 
1437    drawchar = fonts[ffont].encoding[(u_char)code];
1438    charinst.thisobject = drawchar;
1439 
1440    localwidth = (drawchar->bbox.lowerleft.x + drawchar->bbox.width)
1441 	 * fonts[ffont].scale;
1442 
1443    if ((fonts[ffont].flags & 0x22) == 0x22) { /* font is derived and italic */
1444       USlantCTM(DCTM, 0.25);  		/* premultiply by slanting function */
1445    }
1446 
1447    if (!(styles & 64)) {
1448 
1449       UDrawObject(&charinst, SINGLE, passcolor, passwidth, NULL);
1450 
1451       /* under- and overlines */
1452       if (styles & 8)
1453          alphapts[0].y = alphapts[1].y = -6;
1454       else if (styles & 16)
1455          alphapts[0].y = alphapts[1].y = groupheight + 4;
1456       if (styles & 24) {
1457          alphapts[0].x = 0; alphapts[1].x = localwidth;
1458          UDrawSimpleLine(&alphapts[0], &alphapts[1]);
1459       }
1460    }
1461    return localwidth;
1462 }
1463 #endif /* !HAVE_CAIRO */
1464 
1465 /*---------------------------------------------------------------------*/
1466 /* Draw a string at position offset, starting at the start index and   */
1467 /* ending before the end index. The offset is updated on return.       */
1468 /*---------------------------------------------------------------------*/
1469 
1470 #ifndef HAVE_CAIRO
UDrawCharString(u_char * text,int start,int end,XfPoint * offset,short styles,short ffont,int groupheight,int passcolor,float tmpscale)1471 void UDrawCharString(u_char *text, int start, int end, XfPoint *offset,
1472       short styles, short ffont, int groupheight, int passcolor, float tmpscale)
1473 {
1474    int idx;
1475 
1476    float tmpthick = ((fonts[ffont].flags & 0x21) == 0x21) ?  4.0 : 2.0;
1477    for (idx = start; idx < end; idx++)
1478    {
1479       XPoint p;
1480       p.x = offset->x;
1481       p.y = offset->y;
1482       UPushCTM();
1483       UPreMultCTM(DCTM, p, tmpscale, 0);
1484       offset->x += UDrawChar(text[idx], styles, ffont, groupheight, passcolor,
1485 	    tmpthick) * tmpscale;
1486       UPopCTM();
1487    }
1488 }
1489 #endif /* !HAVE_CAIRO */
1490 
1491 /*----------------------------------------------------------------------*/
1492 /* Draw an entire string, including parameter substitutions		*/
1493 /* (Normally called as UDrawString(); see below)			*/
1494 /*----------------------------------------------------------------------*/
1495 
UDrawString0(labelptr drawlabel,int passcolor,objinstptr localinst,Boolean drawX)1496 void UDrawString0(labelptr drawlabel, int passcolor, objinstptr localinst,
1497 	Boolean drawX)
1498 {
1499    stringpart *strptr;
1500    u_char *textptr;
1501    short  fstyle, ffont, tmpanchor;
1502    float  baseline;
1503    int    pos, group = 0;
1504    int    defaultcolor, curcolor, scolor;
1505    float  oldx;
1506    short  oldfont, oldstyle;
1507    float  tmpscale = 1.0, natscale = 1.0;
1508    XfPoint newpoint;
1509    XPoint  bboxin[2], bboxout[2];
1510    u_char xm, ym;
1511    TextExtents tmpext;
1512    TextLinesInfo tlinfo;
1513    short *tabstops = NULL;
1514    short tabno, numtabs = 0;
1515    short linenum = 0;
1516 #ifdef HAVE_CAIRO
1517    cairo_matrix_t fm = {40., 0., 0., -40., 0., 0.}; /* TODO: Why 40? */
1518 
1519    if (!areawin->redraw_ongoing) {
1520       areawin->redraw_needed = True;
1521       return;
1522    }
1523 #endif /* HAVE_CAIRO */
1524 
1525    if (fontcount == 0) return;
1526 
1527    /* Don't draw temporary labels from schematic capture system */
1528    if (drawlabel->string->type != FONT_NAME) return;
1529 
1530    if (passcolor == DOSUBSTRING)
1531       defaultcolor = curcolor = drawlabel->color;
1532    else
1533       defaultcolor = curcolor = passcolor;
1534 
1535    if (defaultcolor != DOFORALL) {
1536       if (drawlabel->color != DEFAULTCOLOR)
1537 	 curcolor = drawlabel->color;
1538       else
1539 	 curcolor = defaultcolor;
1540       XTopSetForeground(curcolor);
1541    }
1542 
1543    /* calculate the transformation matrix for this object */
1544    /* in natural units of the alphabet vectors		  */
1545    /* (conversion to window units)			  */
1546 
1547    UPushCTM();
1548    UPreMultCTM(DCTM, drawlabel->position, drawlabel->scale, drawlabel->rotation);
1549 
1550    /* check for flip invariance; recompute CTM and anchoring if necessary */
1551 
1552    tmpanchor = flipadjust(drawlabel->anchor);
1553 
1554    tlinfo.dostop = 0;
1555    tlinfo.tbreak = NULL;
1556    tlinfo.padding = NULL;
1557 
1558    /* "natural" (unscaled) length */
1559    tmpext = ULength(drawlabel, localinst, &tlinfo);
1560 
1561    newpoint.x = (tmpanchor & NOTLEFT ?
1562        (tmpanchor & RIGHT ? -tmpext.maxwidth : -tmpext.maxwidth >> 1) : 0);
1563    newpoint.y = (tmpanchor & NOTBOTTOM ?
1564        (tmpanchor & TOP ? -tmpext.ascent : -(tmpext.ascent + tmpext.base) >> 1)
1565 		: -tmpext.base);
1566 
1567    /* Pinlabels have an additional offset spacing to pad */
1568    /* them from the circuit point to which they attach.  */
1569 
1570    if (drawlabel->pin) {
1571       XPoint p;
1572       p.x = newpoint.x;
1573       p.y = newpoint.y;
1574       pinadjust(tmpanchor, &p.x, &p.y, 1);
1575       newpoint.x = p.x;
1576       newpoint.y = p.y;
1577    }
1578 
1579    oldx = newpoint.x;
1580    baseline = newpoint.y;
1581 
1582    /* do quick calculation on bounding box; don't draw if off-screen */
1583 
1584    bboxin[0].x = newpoint.x;
1585    bboxin[0].y = newpoint.y + tmpext.descent;
1586    bboxin[1].x = newpoint.x + tmpext.maxwidth;
1587    bboxin[1].y = newpoint.y + tmpext.ascent;
1588    UTransformbyCTM(DCTM, bboxin, bboxout, 2);
1589    xm = (bboxout[0].x < bboxout[1].x) ? 0 : 1;
1590    ym = (bboxout[0].y < bboxout[1].y) ? 0 : 1;
1591 
1592    if (bboxout[xm].x < areawin->width && bboxout[ym].y < areawin->height &&
1593        bboxout[1 - xm].x > 0 && bboxout[1 - ym].y > 0) {
1594 
1595        pos = 0;
1596 
1597        /* At the beginning of the line, add offsets for right or center	*/
1598        /* justification, if required.					*/
1599 
1600        if (tlinfo.padding != NULL) {
1601 	  if (tmpanchor & JUSTIFYRIGHT)
1602 	     newpoint.x += tlinfo.padding[linenum];
1603 	  else if (tmpanchor & TEXTCENTERED)
1604 	     newpoint.x += 0.5 * tlinfo.padding[linenum];
1605 	  linenum++;
1606        }
1607 
1608        for (strptr = drawlabel->string; strptr != NULL;
1609 		strptr = nextstringpart(strptr, localinst)) {
1610 
1611 	  /* All segments other than text cancel any	*/
1612 	  /* existing overline/underline in effect.	*/
1613 
1614 	  if (strptr->type != TEXT_STRING)
1615 	     fstyle &= 0xfc7;
1616 
1617           /* deal with each text segment type */
1618 
1619 	  switch(strptr->type) {
1620 	     case FONT_NAME:
1621 		if (strptr->data.font < fontcount) {
1622 		   ffont = strptr->data.font;
1623 		   fstyle = 0;		   /* style reset by font change */
1624 	           if (baseline == newpoint.y) {  /* set top-level font and style */
1625 	              oldfont = ffont;
1626 	              oldstyle = fstyle;
1627 	           }
1628 		}
1629 #ifdef HAVE_CAIRO
1630 		cairo_set_font_face(areawin->cr, fonts[ffont].font_face);
1631 		cairo_set_font_matrix(areawin->cr, &fm);
1632 #endif /* HAVE_CAIRO */
1633 		break;
1634 
1635 	     case FONT_SCALE:
1636 		tmpscale = natscale * strptr->data.scale;
1637 	        /* if (baseline == newpoint.y) */ /* reset top-level scale */
1638 		/*    natscale = tmpscale; */
1639 		break;
1640 
1641 	     case KERN:
1642 	        newpoint.x += strptr->data.kern[0];
1643 	        newpoint.y += strptr->data.kern[1];
1644 		break;
1645 
1646 	     case FONT_COLOR:
1647 		if (defaultcolor != DOFORALL) {
1648 		   if (strptr->data.color != DEFAULTCOLOR) {
1649 		      curcolor = strptr->data.color;
1650 #ifdef HAVE_CAIRO
1651 		      XTopSetForeground(curcolor);
1652 #endif
1653 		   }
1654 		   else {
1655 		      if (curcolor != DEFAULTCOLOR) {
1656 			 XTopSetForeground(defaultcolor);
1657 		      }
1658 		      curcolor = DEFAULTCOLOR;
1659 		   }
1660 		}
1661 		break;
1662 
1663 	     case TABBACKWARD:	/* find first tab value with x < xtotal */
1664 	        for (tabno = numtabs - 1; tabno >= 0; tabno--) {
1665 	           if (tabstops[tabno] < newpoint.x) {
1666 		      newpoint.x = tabstops[tabno];
1667 		      break;
1668 	           }
1669 	        }
1670 	        break;
1671 
1672 	     case TABFORWARD:	/* find first tab value with x > xtotal */
1673 	        for (tabno = 0; tabno < numtabs; tabno++) {
1674 	           if (tabstops[tabno] > newpoint.x) {
1675 		      newpoint.x = tabstops[tabno];
1676 		      break;
1677 	           }
1678 	        }
1679 	        break;
1680 
1681 	     case TABSTOP:
1682 	        numtabs++;
1683 	        if (tabstops == NULL) tabstops = (short *)malloc(sizeof(short));
1684 	        else tabstops = (short *)realloc(tabstops, numtabs * sizeof(short));
1685 	        tabstops[numtabs - 1] = newpoint.x;
1686 		break;
1687 
1688 	     case RETURN:
1689 		tmpscale = natscale = 1.0;
1690 		baseline -= BASELINE;
1691 	        newpoint.y = baseline;
1692 		newpoint.x = oldx;
1693 
1694 	        if (tlinfo.padding != NULL) {
1695 	 	  if (tmpanchor & JUSTIFYRIGHT)
1696 	 	     newpoint.x += tlinfo.padding[linenum];
1697 	 	  else if (tmpanchor & TEXTCENTERED)
1698 	 	     newpoint.x += 0.5 * tlinfo.padding[linenum];
1699 		  linenum++;
1700 	        }
1701 		break;
1702 
1703 	     case SUBSCRIPT:
1704 	        natscale *= SUBSCALE;
1705 		tmpscale = natscale;
1706 	        newpoint.y -= (short)((TEXTHEIGHT >> 1) * natscale);
1707 		break;
1708 
1709 	     case SUPERSCRIPT:
1710 	        natscale *= SUBSCALE;
1711 		tmpscale = natscale;
1712 	        newpoint.y += (short)(TEXTHEIGHT * natscale);
1713 		break;
1714 
1715 	     case NORMALSCRIPT:
1716 	        tmpscale = natscale = 1.0;
1717 	        ffont = oldfont;	/* revert to top-level font and style */
1718 	        fstyle = oldstyle;
1719 	        newpoint.y = baseline;
1720 #ifdef HAVE_CAIRO
1721 		cairo_set_font_face(areawin->cr, fonts[oldfont].font_face);
1722 		cairo_set_font_matrix(areawin->cr, &fm);
1723 #endif /* HAVE_CAIRO */
1724 		break;
1725 
1726 	     case UNDERLINE:
1727 	        fstyle |= 8;
1728 		break;
1729 
1730 	     case OVERLINE:
1731 		if (strptr->nextpart != NULL && strptr->nextpart->type == TEXT_STRING) {
1732 		   int tmpheight;
1733 
1734 		   group = 0;
1735 		   for (textptr = strptr->nextpart->data.string;
1736 				textptr && *textptr != '\0'; textptr++) {
1737 #ifdef HAVE_CAIRO
1738 		      tmpheight = fonts[ffont].glyph_top[*textptr];
1739 #else /* HAVE_CAIRO */
1740 		      objectptr charptr;
1741 		      charptr = fonts[ffont].encoding[*(u_char *)textptr];
1742 		      tmpheight = (int)((float)charptr->bbox.height
1743 				* fonts[ffont].scale);
1744 #endif /* HAVE_CAIRO */
1745 		      if (group < tmpheight) group = tmpheight;
1746 		   }
1747 	           fstyle |= 16;
1748 		}
1749 		break;
1750 
1751 	     case NOLINE:
1752 		break;
1753 
1754 #ifdef HAVE_CAIRO
1755              case HALFSPACE:
1756                 newpoint.x += fonts[ffont].glyph_advance[' '] * tmpscale / 2.;
1757                 break;
1758 
1759              case QTRSPACE:
1760                 newpoint.x += fonts[ffont].glyph_advance[' '] * tmpscale / 4.;
1761                 break;
1762 #else /* HAVE_CAIRO */
1763 	     case HALFSPACE: case QTRSPACE: {
1764 		XPoint p;
1765 		short addx;
1766 	       	p.x = newpoint.x;
1767 		p.y = newpoint.y;
1768 		UPushCTM();
1769 		UPreMultCTM(DCTM, p, tmpscale, 0);
1770 		addx = UDrawChar((u_char)32, fstyle, ffont, group,
1771 			curcolor, 2.);
1772 		newpoint.x += addx >> ((strptr->type == HALFSPACE) ? 1 : 2);
1773 		UPopCTM();
1774 		} break;
1775 #endif /* HAVE_CAIRO */
1776 
1777 	     case TEXT_STRING:
1778 		textptr = strptr->data.string;
1779 
1780 		/* Don't write technology names in catalog mode if this	*/
1781 		/* option is enabled	(but *not* CATTEXT_MODE!)	*/
1782 
1783 		if (((eventmode == CATALOG_MODE) && !xobjs.showtech)
1784 			|| ((eventmode == CATTEXT_MODE)
1785 			&& (drawlabel != TOLABEL(EDITPART)))) {
1786 		   char *nsptr = strstr(textptr, "::");
1787 		   if (nsptr != NULL) {
1788 		      textptr = nsptr + 2;
1789 		      pos += (pointertype)nsptr - (pointertype)strptr->data.string + 2;
1790 		   }
1791 		}
1792 
1793 	        scolor = curcolor;
1794 	        if (passcolor == DOSUBSTRING) {
1795 		   int len = strlen(textptr);
1796 		   int begin_sel = min(max(0, areawin->textend - pos), len);
1797 		   int end_sel = min(max(0, areawin->textpos - pos), len);
1798 		   if (begin_sel > 0) {
1799 		      UDrawCharString(textptr, 0, begin_sel, &newpoint, fstyle,
1800 			    ffont, group, scolor, tmpscale);
1801 		   }
1802 		   if (begin_sel < end_sel) {
1803 		      scolor = SELECTCOLOR;
1804 #ifdef HAVE_CAIRO
1805       		      XTopSetForeground(scolor);
1806 #endif /* HAVE_CAIRO */
1807 		      UDrawCharString(textptr, begin_sel, end_sel, &newpoint,
1808 			    fstyle, ffont, group, scolor, tmpscale);
1809 		   }
1810 		   if (end_sel < len) {
1811 		      scolor = curcolor;
1812       		      XTopSetForeground(curcolor);
1813 		      UDrawCharString(textptr, end_sel, len, &newpoint, fstyle,
1814 			    ffont, group, scolor, tmpscale);
1815 		   }
1816 		}
1817 		else {
1818 	           UDrawCharString(textptr, 0, strlen(textptr),
1819 		         &newpoint, fstyle, ffont, group, scolor, tmpscale);
1820 		}
1821 	        pos += strlen(textptr) - 1;
1822 		break;
1823 	  }
1824 	  pos++;
1825        }
1826    }
1827 
1828    /* enddraw: (jdk) */
1829 
1830    if (tabstops != NULL) free(tabstops);
1831    if (tlinfo.padding != NULL) free(tlinfo.padding);
1832 
1833    /* Pop the string transformation matrix */
1834 
1835    UPopCTM();
1836 
1837    if (drawX && drawlabel->pin) UDrawXDown(drawlabel);
1838 
1839    if ((defaultcolor != DOFORALL) && (passcolor != curcolor) &&
1840 		(passcolor != DOSUBSTRING)) {
1841       XTopSetForeground(passcolor);
1842    }
1843 }
1844 
1845 /*----------------------------------------------------------------------*/
1846 /* Draw String, with an "x" mark at the origin				*/
1847 /*----------------------------------------------------------------------*/
1848 
UDrawString(labelptr drawlabel,int passcolor,objinstptr localinst)1849 void UDrawString(labelptr drawlabel, int passcolor, objinstptr localinst)
1850 {
1851    UDrawString0(drawlabel, passcolor, localinst, TRUE);
1852 }
1853 
1854 /*----------------------------------------------------------------------*/
1855 /* Draw String, without the "x" mark					*/
1856 /*----------------------------------------------------------------------*/
1857 
UDrawStringNoX(labelptr drawlabel,int passcolor,objinstptr localinst)1858 void UDrawStringNoX(labelptr drawlabel, int passcolor, objinstptr localinst)
1859 {
1860    UDrawString0(drawlabel, passcolor, localinst, FALSE);
1861 }
1862 
1863 /*----------------------------------------------------------------------*/
1864 /* Compute the actual length of a string or portion thereof.		*/
1865 /*									*/
1866 /* The third argument is a pointer to a structure TextLinesInfo, having	*/
1867 /* fields "dostop", "tbreak", and "padding".  The third argument may	*/
1868 /* be NULL, in which case dostop defaults to 0 and tbreak and 		*/
1869 /* padding default to NULL.						*/
1870 /*									*/
1871 /* If "dostop" is non-zero, ULength computes the length to a specific	*/
1872 /* location in the string.						*/
1873 /* If "padding" is non-NULL, it is a pointer to an array to be		*/
1874 /* allocated and filled in with the individual padding lengths.  If	*/
1875 /* the array is already allocated, then it is assumed to contain valid	*/
1876 /* linewidth information from a previous call to ULength().		*/
1877 /* If "tbreak" is non-NULL, it is a pointer to an XPoint, and the	*/
1878 /* return TextExtents record "width" will be filled in with the index	*/
1879 /* position in the string that is closest to that point.		*/
1880 /* When using either "dostop" or "tbreak", the line number stopped on	*/
1881 /* is returned in TextLinesInfo field "line".  When using "tbreak", the	*/
1882 /* position index is returned in "dostop".				*/
1883 /*----------------------------------------------------------------------*/
1884 
ULength(labelptr drawlabel,objinstptr localinst,TextLinesInfo * tlinfo)1885 TextExtents ULength(labelptr drawlabel, objinstptr localinst,
1886 	TextLinesInfo *tlinfo)
1887 {
1888    short dostop;
1889    XPoint tbreak;
1890 
1891    float oldscale, strscale, natscale, locscale = 1.0, xtotal = 0.5;
1892    float lasttotal = xtotal;
1893    stringpart *strptr;
1894    u_char *textptr;
1895    objectptr *somebet = NULL;
1896    short locpos = 0, lastpos = 0;
1897    float ykern = 0.0;
1898    TextExtents retext;
1899    short *tabstops = NULL;
1900    short tabno, numtabs = 0;
1901    short linenum = 0;
1902    Boolean dobreak = FALSE;
1903 #ifdef HAVE_CAIRO
1904    fontinfo *font = NULL;
1905 #endif /* HAVE_CAIRO */
1906 
1907    dostop = (tlinfo == NULL) ? 0 : tlinfo->dostop;
1908 
1909    retext.ascent = retext.descent = retext.base = 0;
1910    retext.width = retext.maxwidth = 0;
1911 
1912    if (fontcount == 0) return retext;
1913 
1914    /* Don't draw temporary labels from schematic capture system */
1915    else if (drawlabel->string->type != FONT_NAME) return retext;
1916 
1917    /* Copy tbreak point locally */
1918    if ((tlinfo != NULL) && (tlinfo->tbreak != NULL)) tbreak = *tlinfo->tbreak;
1919 
1920    /* Adjust tbreak x value for justification */
1921    if ((tlinfo != NULL) && (tlinfo->padding != NULL)) {
1922       if (drawlabel->anchor & JUSTIFYRIGHT)
1923          tbreak.x -= tlinfo->padding[linenum];
1924       else if (drawlabel->anchor & TEXTCENTERED)
1925          tbreak.x -= 0.5 * tlinfo->padding[linenum];
1926    }
1927 
1928    natscale = 1.0;
1929    oldscale = strscale = natscale;
1930 
1931    for (strptr = drawlabel->string; strptr != NULL;
1932 		strptr = nextstringpart(strptr, localinst)) {
1933       switch (strptr->type) {
1934 	 case SUPERSCRIPT:
1935 	    natscale *= SUBSCALE;
1936 	    strscale = natscale;
1937 	    ykern += TEXTHEIGHT * natscale;
1938 	    break;
1939 	 case SUBSCRIPT:
1940 	    natscale *= SUBSCALE;
1941 	    strscale = natscale;
1942 	    ykern -= TEXTHEIGHT * natscale / 2.0;
1943 	    break;
1944 	 case NORMALSCRIPT:
1945 	    natscale = strscale = oldscale;
1946 	    ykern = 0.0;
1947 	    break;
1948 	 case RETURN:
1949 	    if (tlinfo != NULL) {
1950 	       if (tlinfo->padding == NULL) {
1951 	          stringpart *nextret = strptr;
1952 	          int numlines = 2;
1953 	          while ((nextret = nextstringpart(nextret, localinst)) != NULL)
1954 		     if (nextret->type == RETURN)
1955 		        numlines++;
1956 	          tlinfo->padding = (float *)malloc(numlines * sizeof(float));
1957 	       }
1958 	       if (tlinfo->padding != NULL)
1959 		  tlinfo->padding[linenum++] = xtotal;
1960 	    }
1961 	    natscale = strscale = oldscale;
1962 	    ykern = 0.0;
1963 	    retext.base -= BASELINE;
1964 	    retext.maxwidth = max(retext.width, retext.maxwidth);
1965 	    retext.maxwidth = max(retext.maxwidth, xtotal);
1966 	    xtotal = 0.5;
1967 
1968 	    /* Re-copy tbreak point locally */
1969 	    if ((tlinfo != NULL) && (tlinfo->tbreak != NULL))
1970 	       tbreak = *tlinfo->tbreak;
1971 
1972 	    /* Adjust tbreak x value for justification on next line */
1973 	    if ((tlinfo != NULL) && (tlinfo->padding != NULL)) {
1974 	       if (drawlabel->anchor & JUSTIFYRIGHT)
1975 	          tbreak.x -= tlinfo->padding[linenum];
1976 	       else if (drawlabel->anchor & TEXTCENTERED)
1977 	          tbreak.x -= 0.5 * tlinfo->padding[linenum];
1978 	    }
1979 	    break;
1980 	 case HALFSPACE:
1981 	    if (somebet) {
1982 #ifdef HAVE_CAIRO
1983 	       xtotal += font->glyph_advance[' '] * locscale * strscale / 2.;
1984 #else /* HAVE_CAIRO */
1985 	       objectptr chptr = (*(somebet + 32));
1986 	       xtotal += (float)(chptr->bbox.width + chptr->bbox.lowerleft.x)
1987 			* locscale * natscale / 2.;
1988 #endif /* HAVE_CAIRO */
1989 	    }
1990 	    break;
1991 	 case QTRSPACE:
1992 	    if (somebet) {
1993 #ifdef HAVE_CAIRO
1994 	       xtotal += font->glyph_advance[' '] * locscale * strscale / 4.;
1995 #else /* HAVE_CAIRO */
1996 	       objectptr chptr = (*(somebet + 32));
1997 	       xtotal += (float)(chptr->bbox.width + chptr->bbox.lowerleft.x)
1998 			* locscale * natscale / 4.;
1999 #endif /* HAVE_CAIRO */
2000 	    }
2001 	    break;
2002 	 case TABBACKWARD:	/* find first tab value with x < xtotal */
2003 	    for (tabno = numtabs - 1; tabno >= 0; tabno--) {
2004 	       if (tabstops[tabno] < xtotal) {
2005 		  xtotal = tabstops[tabno];
2006 		  break;
2007 	       }
2008 	    }
2009 	    break;
2010 	 case TABFORWARD:	/* find first tab value with x > xtotal */
2011 	    for (tabno = 0; tabno < numtabs; tabno++) {
2012 	       if (tabstops[tabno] > xtotal) {
2013 		  xtotal = tabstops[tabno];
2014 		  break;
2015 	       }
2016 	    }
2017 	    break;
2018 	 case TABSTOP:
2019 	    numtabs++;
2020 	    if (tabstops == NULL) tabstops = (short *)malloc(sizeof(short));
2021 	    else tabstops = (short *)realloc(tabstops, numtabs * sizeof(short));
2022 	    tabstops[numtabs - 1] = xtotal;
2023 	    break;
2024 	 case FONT_SCALE:
2025 	    strscale = natscale * strptr->data.scale;
2026 	    /* if (ykern == 0.0) */
2027 	    /*   natscale = strscale; */
2028 	    break;
2029 	 case KERN:
2030 	    xtotal += strptr->data.kern[0];
2031 	    ykern += strptr->data.kern[1];
2032 	    break;
2033 	 case FONT_NAME:
2034 	    if (strptr->data.font < fontcount) {
2035 	       somebet = fonts[strptr->data.font].encoding;
2036 	       locscale = fonts[strptr->data.font].scale;
2037 	       if (ykern == 0.0)
2038 	          natscale = locscale;
2039 #ifdef HAVE_CAIRO
2040 	       font = fonts + strptr->data.font;
2041 #endif /* HAVE_CAIRO */
2042 	    }
2043 	    break;
2044 	 case TEXT_STRING:
2045 	    textptr = strptr->data.string;
2046 
2047 	    /* Don't write technology names in catalog mode if	*/
2048 	    /* the option is enabled, so ignore when measuring	*/
2049 
2050 	    if (((eventmode == CATALOG_MODE) && !xobjs.showtech)
2051 			|| ((eventmode == CATTEXT_MODE)
2052 			&& (drawlabel != TOLABEL(EDITPART)))) {
2053 		char *nsptr = strstr(textptr, "::");
2054 		if (nsptr != NULL) {
2055 		   textptr = nsptr + 2;
2056 		   locpos += (pointertype)nsptr - (pointertype)strptr->data.string + 2;
2057 		}
2058 	    }
2059 
2060 	    if (somebet == NULL) break;
2061 
2062 	    for (; textptr && *textptr != '\0'; textptr++) {
2063 	       float advance, top, bottom;
2064                if (dostop && (locpos >= dostop)) break;
2065 	       locpos++;
2066 
2067 #ifdef HAVE_CAIRO
2068 	       advance = font->glyph_advance[*textptr];
2069 	       top = font->glyph_top[*textptr];
2070 	       bottom = font->glyph_bottom[*textptr];
2071 #else /* HAVE_CAIRO */
2072 	       {
2073 	       	  objectptr chptr = *(somebet + *textptr);
2074 	          advance = chptr->bbox.width + chptr->bbox.lowerleft.x;
2075 	          top = chptr->bbox.height + chptr->bbox.lowerleft.y;
2076 	          bottom = chptr->bbox.lowerleft.y;
2077 	       }
2078 #endif /* HAVE_CAIRO */
2079 	       xtotal += advance * locscale * strscale;
2080 	       retext.ascent = max(retext.ascent, (short)(retext.base + ykern
2081 		     + top * locscale * strscale));
2082 	       retext.descent = min(retext.descent, (short)(retext.base + ykern
2083 		     + bottom * locscale * strscale));
2084 
2085 	       if ((tlinfo != NULL) && (tlinfo->tbreak != NULL)) {
2086 	          if ((xtotal > tbreak.x) && (retext.base <= tbreak.y)) {
2087 		     dobreak = TRUE;
2088 		     break;
2089 		  }
2090 	       }
2091                lasttotal = xtotal;
2092 	       lastpos = locpos;
2093 	    }
2094 	    break;
2095       }
2096 
2097       if (strptr->type != TEXT_STRING) locpos++;
2098       if (dostop && (locpos >= dostop)) break;
2099       if (dobreak) break;
2100    }
2101    if (tabstops != NULL) free(tabstops);
2102 
2103    /* Update width and maxwidth entries */
2104    retext.width = max(retext.width, xtotal);
2105    retext.maxwidth = max(retext.maxwidth, xtotal);
2106 
2107    /* If not doing "dostop", put the last position into the 	*/
2108    /* list of padding.	Since padding values are currently	*/
2109    /* linewidths, subtract them from maxwidth.			*/
2110 
2111    if (tlinfo != NULL) {
2112       int i;
2113       if ((tlinfo->dostop == 0) && (tlinfo->padding != NULL)) {
2114 	  tlinfo->padding[linenum] = xtotal;
2115 	  for (i = 0; i <= linenum; i++)
2116 	     tlinfo->padding[i] = retext.maxwidth - tlinfo->padding[i];
2117       }
2118       tlinfo->line = linenum;
2119    }
2120 
2121    /* Return character position in tlinfo->dostop (which should	*/
2122    /* be unused if using "tbreak" 				*/
2123 
2124    if ((tlinfo != NULL) && (tlinfo->tbreak != NULL)) {
2125       int slen = stringlength(drawlabel->string, True, localinst);
2126       if ((tbreak.x - lasttotal) < (xtotal - tbreak.x))
2127 	 locpos = lastpos + 1;
2128       if (locpos < 1) locpos = 1;
2129       else if (locpos > slen) locpos = slen;
2130       if (tlinfo != NULL) tlinfo->dostop = locpos;
2131    }
2132    return retext;
2133 }
2134 
2135 /*----------------------------------------------------------------------*/
2136 /* Remove all RETURN directives following a MARGINSTOP.  Automatically	*/
2137 /* generated line breaks are identified by an nonzero "flags" field.	*/
2138 /*----------------------------------------------------------------------*/
2139 
RemoveMarginNewlines(labelptr settext,objinstptr localinst)2140 void RemoveMarginNewlines(labelptr settext, objinstptr localinst)
2141 {
2142    stringpart *strptr;
2143    int strpos = 0;
2144 
2145    for (strptr = settext->string; strptr != NULL;
2146 		strptr = nextstringpart(strptr, localinst)) {
2147       switch (strptr->type) {
2148 	 case RETURN:
2149 	    if (strptr->data.flags != 0) {
2150 	       /* Remove (without merge) */
2151 	       strptr = deletestring0(strptr, &settext->string, localinst, FALSE);
2152 	       if (strpos <= areawin->textpos) areawin->textpos--;
2153 	    }
2154 	    strpos++;
2155 	    break;
2156 
2157 	 case TEXT_STRING:
2158 	    if (strptr->data.string)
2159 	       strpos += strlen(strptr->data.string);
2160 	    break;
2161 
2162 	 default:
2163 	    strpos++;
2164 	    break;
2165       }
2166    }
2167 }
2168 
2169 /*----------------------------------------------------------------------*/
2170 /* Analyze a text label and insert RETURN directives as necessary to	*/
2171 /* keep the text within the width limit set by MARGIN.  Break up text	*/
2172 /* at word boundaries where necessary.  This routine is run only when	*/
2173 /* (1) editing a text label or loading one from a file, and (2) the	*/
2174 /* text label both contains a MARGIN directive and exceeds the margin	*/
2175 /* width, as determined by CheckMarginStop().  				*/
2176 /*----------------------------------------------------------------------*/
2177 
InsertMarginNewlines(labelptr settext,objinstptr localinst)2178 void InsertMarginNewlines(labelptr settext, objinstptr localinst)
2179 {
2180    stringpart *strptr, *lastseg = NULL;
2181    int margin = 0;
2182    int strpos = 0, locpos, slen, savelen;
2183    TextExtents tmpext;
2184    TextLinesInfo tlinfo;
2185 
2186    /* 1) Find the position of the margin stop.  Track position	*/
2187    /* in string a la findstringpart(), as we need to pass this	*/
2188    /* to ULength()						*/
2189 
2190    for (strptr = settext->string; strptr != NULL;
2191 		strptr = nextstringpart(strptr, localinst)) {
2192       switch (strptr->type) {
2193 	 case MARGINSTOP:
2194 	    margin = strptr->data.width;
2195 	    strpos++;
2196 	    break;
2197 
2198 	 case TEXT_STRING:
2199 	    if (strptr->data.string)
2200 	       strpos += strlen(strptr->data.string);
2201 	    break;
2202 
2203 	 default:
2204 	    strpos++;
2205 	    break;
2206       }
2207       if (margin > 0) break;
2208    }
2209    if (margin == 0) return;	/* Should not happen. . . */
2210    lastseg = strptr;
2211 
2212    /* 2) Compute the drawn string length at each word break.  When a	*/
2213    /*    word overruns the margin, place a line break in front of it.	*/
2214 
2215    tlinfo.tbreak = NULL;
2216    tlinfo.padding = NULL;
2217 
2218    while (1) {
2219       strptr = findstringpart(strpos, &locpos, settext->string, localinst);
2220       if (strptr == NULL) break;
2221       else if (strptr->type == TEXT_STRING) {
2222 	 slen = strlen(strptr->data.string);
2223 	 /* Ignore trailing spaces */
2224 	 while ((slen > 0) && (*(strptr->data.string + slen - 1) == ' ')) slen--;
2225 
2226 	 tlinfo.dostop = strpos + slen;
2227 
2228          tmpext = ULength(settext, localinst, &tlinfo);
2229 	 if (tmpext.width > margin) {
2230 	    savelen = 0;
2231             while ((slen > 0) && (tmpext.width > margin)) {
2232 	       while ((slen > 0) && (*(strptr->data.string + slen - 1) != ' ')) slen--;
2233 	       while ((slen > 0) && (*(strptr->data.string + slen - 1) == ' ')) {
2234 		  slen--;
2235 		  savelen = slen;
2236 	       }
2237 	       tlinfo.dostop = strpos + slen - 1;
2238 	       tmpext = ULength(settext, localinst, &tlinfo);
2239 	    }
2240 	    /* Take the first space, in case we have a single word so long that	*/
2241 	    /* it exceeds the margin by itself.					*/
2242 	    if (savelen > slen) slen = savelen;
2243 	    if (slen > 0) {
2244 	       /* Split string at word separation before the margin. */
2245 
2246 	       while ((slen > 0) && (*(strptr->data.string + slen) == ' ')) slen++;
2247 	       strptr = splitstring(strpos + slen, &settext->string, localinst);
2248 	       strptr = nextstringpart(strptr, localinst);
2249 	    }
2250 
2251 	    /* Insert a carriage return, if the previous segment was not */
2252 	    /* already one. */
2253 	    if (slen > 0 || (lastseg->type != RETURN)) {
2254 	       strptr = makesegment(&settext->string, strptr);
2255 	       strptr->type = RETURN;
2256 	       strptr->data.flags = 1;	/* Mark as auto-generated line wrap */
2257 	       strpos += slen;
2258 	       if (areawin->textpos > strpos) areawin->textpos++;
2259 	    }
2260 	    else
2261 	       strpos += strlen(strptr->data.string);
2262 	 }
2263 	 else
2264 	    strpos += strlen(strptr->data.string);
2265       }
2266       else if (strptr->type == MARGINSTOP) {
2267 	 /* Allows multiple margin stops in the same text block */
2268 	 margin = strptr->data.width;
2269 	 strpos++;
2270       }
2271       else
2272          strpos++;
2273 
2274       lastseg = strptr;
2275    }
2276 }
2277 
2278 /*----------------------------------------------------------------------*/
2279 /* Check a string for presence of a MARGINSTOP directive.  If it has	*/
2280 /* one, check if ULength exceeds the margin.  If so, remove all Return	*/
2281 /* directives after the MARGINSTOP, and re-insert them such that the	*/
2282 /* text stays within the margin.					*/
2283 /*----------------------------------------------------------------------*/
2284 
CheckMarginStop(labelptr settext,objinstptr localinst,Boolean force)2285 void CheckMarginStop(labelptr settext, objinstptr localinst, Boolean force)
2286 {
2287    stringpart *strptr;
2288    int margin = 0;
2289    TextExtents tmpext;
2290    TextLinesInfo tlinfo;
2291 
2292    for (strptr = settext->string; strptr != NULL;
2293 		strptr = nextstringpart(strptr, localinst)) {
2294       switch (strptr->type) {
2295 	 case MARGINSTOP:
2296 	    margin = strptr->data.width;
2297 	    break;
2298       }
2299       if (margin > 0) break;
2300    }
2301    if (margin > 0) {
2302       tlinfo.dostop = 0;
2303       tlinfo.tbreak = NULL;
2304       tlinfo.padding = NULL;
2305       tmpext = ULength(settext, localinst, &tlinfo);
2306       if ((force == TRUE) || (tmpext.maxwidth > margin)) {
2307 	 RemoveMarginNewlines(settext, localinst);
2308 	 InsertMarginNewlines(settext, localinst);
2309       }
2310    }
2311    else {
2312       /* In case the Margin Stop directive just got deleted. . . */
2313       RemoveMarginNewlines(settext, localinst);
2314    }
2315 }
2316 
2317 /*----------------------------------------------------------------------*/
2318 /* low-level routines for drawing and erasing labels			*/
2319 /*----------------------------------------------------------------------*/
2320 
undrawtextsimple(labelptr settext)2321 void undrawtextsimple(labelptr settext)
2322 {
2323    XTopSetForeground(BACKGROUND);
2324    UDrawString(settext, DOFORALL, areawin->topinstance);
2325 }
2326 
2327 /*----------------------------------------------------------------------*/
2328 
redrawtextsimple(labelptr settext)2329 void redrawtextsimple(labelptr settext)
2330 {
2331    UDrawString(settext, settext->color, areawin->topinstance);
2332 }
2333 
2334 /*----------------------------------------------------------------------*/
2335 /* Redraw all labels in the current object which contain the same	*/
2336 /* parameter(s) as the indicated label.					*/
2337 /* (It's easier not to bother to check for the same parameter, as there	*/
2338 /* are typically so few parameters in an object that the extra compute	*/
2339 /* and draw time is negligible.)					*/
2340 /*									*/
2341 /* Function pointer (undrawtextsimple or redrawtextsimple) indicates	*/
2342 /* which function to call on this text label.				*/
2343 /*----------------------------------------------------------------------*/
2344 
drawtextandupdate(labelptr curlabel,void (* function)(labelptr))2345 void drawtextandupdate(labelptr curlabel, void (*function)(labelptr))
2346 {
2347    genericptr *pgen;
2348    labelptr slab;
2349 
2350    for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts;
2351 		pgen++) {
2352       if (IS_LABEL(*pgen)) {
2353 	 slab = TOLABEL(pgen);
2354 	 if (slab == curlabel) continue;
2355 	 if (hasparameter(slab))
2356 	    function(slab);
2357       }
2358    }
2359 }
2360 
2361 /*----------------------------------------------------------------------*/
2362 /* Wrapper functions for drawtextandupdate()				*/
2363 /*----------------------------------------------------------------------*/
2364 
undrawtext(labelptr curlabel)2365 void undrawtext(labelptr curlabel)
2366 {
2367    undrawtextsimple(curlabel);
2368 
2369    if (hasparameter(curlabel))
2370       drawtextandupdate(curlabel, undrawtextsimple);
2371 }
2372 
2373 /*----------------------------------------------------------------------*/
2374 
redrawtext(labelptr curlabel)2375 void redrawtext(labelptr curlabel)
2376 {
2377    redrawtextsimple(curlabel);
2378 
2379    if (hasparameter(curlabel))
2380       drawtextandupdate(curlabel, redrawtextsimple);
2381 }
2382 
2383 /*----------------------------------------------------------------------*/
2384 /* Draw the catalog of font characters 					*/
2385 /*----------------------------------------------------------------------*/
2386 
composefontlib(short cfont)2387 void composefontlib(short cfont)
2388 {
2389   /* genericptr *pgen; (jdk) */
2390    objectptr nullobj;
2391    objectptr directory = xobjs.libtop[FONTLIB]->thisobject;
2392    short visobjects, i;
2393    polyptr *drawbox;
2394    pointlist pointptr;
2395 
2396    reset(directory, NORMAL);
2397 
2398    /* Find the number of non-null characters.  Do this by assuming */
2399    /* that all fonts encode nullchar at position zero.		   */
2400 
2401    visobjects = 0;
2402    nullobj = fonts[cfont].encoding[0];
2403    for(i = 1; i < 256; i++)
2404       if (fonts[cfont].encoding[i] != nullobj) visobjects++;
2405 
2406    /* add the box and gridlines */
2407 
2408    visobjects += 34;
2409 
2410    /* generate the list of object instances */
2411 
2412    directory->plist = (genericptr *) realloc(directory->plist, visobjects
2413 		* sizeof(genericptr));
2414    directory->parts = 0;
2415 
2416    for (i = 0; i < 256; i++) {
2417       stringpart *sfont, *schar;
2418       labelptr label;
2419       u_char character[2] = {0, 0};
2420       character[0] = i;
2421 
2422       if (fonts[cfont].encoding[i] == nullobj)
2423 	 continue;
2424 
2425       sfont = (stringpart *) malloc(sizeof(stringpart));
2426       schar = (stringpart *) malloc(sizeof(stringpart));
2427       sfont->type = FONT_NAME;
2428       sfont->data.font = cfont;
2429       sfont->nextpart = schar;
2430       schar->type = TEXT_STRING;
2431       schar->data.string = strdup(character);
2432       schar->nextpart = NULL;
2433       label = new_label(xobjs.libtop[FONTLIB], sfont, NORMAL,
2434 		(i % 16) * del + del / 2, -(i / 16) * del - del / 2 - 32 /2,
2435 		(u_char)0);
2436       label->anchor = NOTLEFT;
2437       label->color = DEFAULTCOLOR;
2438    }
2439 
2440    /* separate characters with gridlines (17 vert., 17 horiz.) */
2441 
2442    for (i = 0; i < 34; i++) {
2443       NEW_POLY(drawbox, directory);
2444       polydefaults(*drawbox, 2, 0, 0);
2445 
2446       (*drawbox)->color = SNAPCOLOR;   /* default red */
2447       (*drawbox)->style = UNCLOSED;
2448       (*drawbox)->width = 1.0;
2449 
2450       if (i < 17) {
2451          pointptr = (*drawbox)->points;
2452          pointptr->x = i * del;
2453          pointptr->y = 0;
2454          pointptr = (*drawbox)->points + 1;
2455          pointptr->x = i * del;
2456          pointptr->y = -16 * del;
2457       }
2458       else {
2459          pointptr = (*drawbox)->points;
2460          pointptr->x = 0;
2461          pointptr->y = (17 - i) * del;
2462          pointptr = (*drawbox)->points + 1;
2463          pointptr->x = 16 * del;
2464          pointptr->y = (17 - i) * del;
2465       }
2466    }
2467 
2468    /* Set the bounding box for this display. 				*/
2469    /* This is just the bounds of the grid built above, so there's no	*/
2470    /* need to call any of the bounding box calculation routines.     	*/
2471 
2472    directory->bbox.lowerleft.x = 0;
2473    directory->bbox.lowerleft.y = pointptr->y;
2474    directory->bbox.width = pointptr->x;
2475    directory->bbox.height = pointptr->x;
2476 
2477    xobjs.libtop[FONTLIB]->bbox.lowerleft.x = 0;
2478    xobjs.libtop[FONTLIB]->bbox.lowerleft.y = pointptr->y;
2479    xobjs.libtop[FONTLIB]->bbox.width = pointptr->x;
2480    xobjs.libtop[FONTLIB]->bbox.height = pointptr->x;
2481 
2482    centerview(xobjs.libtop[FONTLIB]);
2483 }
2484 
2485 /*------------------------------------------------------*/
2486 /* ButtonPress handler during font catalog viewing mode */
2487 /*------------------------------------------------------*/
2488 
fontcat_op(int op,int x,int y)2489 void fontcat_op(int op, int x, int y)
2490 {
2491    short chx, chy;
2492    u_long rch = 0;
2493 
2494    if (op != XCF_Cancel) {
2495 
2496       window_to_user(x, y, &areawin->save);
2497 
2498       chy = -areawin->save.y / del;
2499       chx = areawin->save.x / del;
2500 
2501       chx = min(15, chx);
2502       chy = min(15, chy);
2503 
2504       rch = (u_long)(chy * 16 + chx);
2505    }
2506 
2507    catreturn();
2508 
2509    if (rch != 0)
2510       labeltext(rch, NULL);
2511 }
2512 
2513 /*-------------------------------------------------------------------------*/
2514