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