1 /*
2 * CmdSubrs.c --
3 *
4 * The functions in this file are local to the commands module
5 * and not intended to be used by its clients.
6 *
7 * *********************************************************************
8 * * Copyright (C) 1985, 1990 Regents of the University of California. *
9 * * Permission to use, copy, modify, and distribute this *
10 * * software and its documentation for any purpose and without *
11 * * fee is hereby granted, provided that the above copyright *
12 * * notice appear in all copies. The University of California *
13 * * makes no representations about the suitability of this *
14 * * software for any purpose. It is provided "as is" without *
15 * * express or implied warranty. Export of this software outside *
16 * * of the United States of America may require an export license. *
17 * *********************************************************************
18 */
19
20 #ifndef lint
21 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/commands/CmdSubrs.c,v 1.2 2010/06/24 12:37:15 tim Exp $";
22 #endif /* not lint */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <math.h> /* For round() function */
29
30 #include "utils/magic.h"
31 #include "utils/geometry.h"
32 #include "utils/utils.h"
33 #include "database/database.h"
34 #include "tiles/tile.h"
35 #include "utils/hash.h"
36 #include "utils/malloc.h"
37 #include "windows/windows.h"
38 #include "dbwind/dbwind.h"
39 #include "utils/main.h"
40 #include "commands/commands.h"
41 #include "textio/textio.h"
42 #include "cif/cif.h"
43 #include "drc/drc.h"
44 #include "textio/txcommands.h"
45 #include "utils/undo.h"
46 #include "utils/macros.h"
47 #include "sim/sim.h"
48 #include "select/select.h"
49 #ifdef SCHEME_INTERPRETER
50 #include "lisp/lisp.h"
51 #endif
52
53 /* Forward declarations */
54
55 extern char *cmdCheckNewName();
56 extern int cmdSaveWindSet();
57 extern void CmdSetWindCaption();
58
59 TileTypeBitMask CmdYMLabel;
60 TileTypeBitMask CmdYMCell;
61 TileTypeBitMask CmdYMAllButSpace;
62
63 /*
64 * ----------------------------------------------------------------------------
65 *
66 * cmdScaleCoord --
67 *
68 * Replaces the use of atoi() in command parsing of coordinates. Allows
69 * coordinates to be declared in internal units, lambda, user units, or
70 * microns, and translates to internal units accordingly. It also allows
71 * coordinates to be specified in floating-point.
72 * A suffix of "i" indicates internal units, a suffix of "l" indicates
73 * lambda, a suffix of "g" indicates the user grid, and a suffix in metric
74 * notation ("nm", "um", "mm", "cm") indicates natural units. Other valid
75 * units are "cu" or "centimicrons" for centimicrons, or "microns" for um.
76 * Units without any suffix are assumed to be in lambda if "snap"
77 * (DBWSnapToGrid) is set to lambda, grid units if "snap" is set to the
78 * user grid, and internal units otherwise.
79 *
80 * MagWindow argument w is used only with grid-based snapping, to find
81 * the value of the grid for the given window. In this case, because the
82 * grid specifies an offset, "is_relative" specifies whether the given
83 * coordinate is a relative measurement (ignore offset) or an absolute
84 * position. Because the grid can be different in X and Y, "is_x"
85 * specifies whether the given distance is in the X (TRUE) or Y (FALSE)
86 * direction.
87 *
88 * This is the "general-purpose" routine, taking a value "scale" so
89 * that returned units can be a fraction of internal units, e.g., to
90 * represent wire centerlines (scale = 2) or highlights (any scale).
91 *
92 * Results:
93 * Integer representing the given coordinate in internal units,
94 * multiplied up by "scale".
95 *
96 * Side effects:
97 * None
98 *
99 * ----------------------------------------------------------------------------
100 */
101
102 int
cmdScaleCoord(w,arg,is_relative,is_x,scale)103 cmdScaleCoord(w, arg, is_relative, is_x, scale)
104 MagWindow *w;
105 char *arg;
106 bool is_relative, is_x;
107 int scale;
108 {
109 char *endptr;
110 double dval = 0;
111 int mscale = 1;
112 DBWclientRec *crec;
113
114 if (*arg == '{') arg++;
115 while (isspace(*arg)) arg++;
116
117 dval = strtod(arg, &endptr);
118 dval *= (double)scale;
119
120 if (endptr == arg)
121 {
122 /* strtod() error condition */
123 TxError("Coordinate value cannot be parsed: assuming 0\n");
124 return 0;
125 }
126
127 else if ((*endptr == 'l')
128 || ((*endptr == '\0') && (DBWSnapToGrid == DBW_SNAP_LAMBDA)))
129 {
130 /* lambda or default units */
131 dval *= (double)DBLambda[1];
132 dval /= (double)DBLambda[0];
133 return round(dval);
134 }
135 else if ((*endptr == 'i')
136 || ((*endptr == '\0') && (DBWSnapToGrid == DBW_SNAP_INTERNAL)))
137 {
138 /* internal units */
139 return round(dval);
140 }
141 else if ((*endptr == 'g')
142 || ((*endptr == '\0') && (DBWSnapToGrid == DBW_SNAP_USER)))
143 {
144 /* grid units */
145 if (w == (MagWindow *)NULL)
146 {
147 windCheckOnlyWindow(&w, DBWclientID);
148 if (w == (MagWindow *)NULL)
149 return round(dval); /* Default, if window is unknown */
150 }
151 crec = (DBWclientRec *) w->w_clientData;
152 if (is_x)
153 {
154 dval *= (double)(crec->dbw_gridRect.r_xtop
155 - crec->dbw_gridRect.r_xbot);
156 if (!is_relative)
157 dval += (double)crec->dbw_gridRect.r_xbot;
158 }
159 else
160 {
161 dval *= (double)(crec->dbw_gridRect.r_ytop
162 - crec->dbw_gridRect.r_ybot);
163 if (!is_relative)
164 dval += (double)crec->dbw_gridRect.r_ybot;
165 }
166 return round(dval);
167 }
168 else
169 {
170 /* natural units referred to the current cifoutput style */
171 if (*(endptr + 1) == 'm')
172 {
173 switch (*endptr)
174 {
175 case 'n':
176 mscale = 1;
177 break;
178 case 'u':
179 mscale = 1000;
180 break;
181 case 'm':
182 mscale = 1000000;
183 break;
184 case 'c':
185 mscale = 10000000;
186 break;
187 default:
188 TxError("Unknown metric prefix \"%cm\"; assuming internal units\n",
189 *endptr);
190 return round(dval);
191 }
192 }
193 else if (!strncmp(endptr, "micron", 6))
194 mscale = 1000;
195 else if (!strncmp(endptr, "centimicron", 11) || !strcmp(endptr, "cu"))
196 mscale = 10;
197 else if (!isspace(*endptr))
198 {
199 TxError("Unknown coordinate type \"%s\"; assuming internal units\n",
200 endptr);
201 return round(dval);
202 }
203 }
204 dval /= CIFGetOutputScale(mscale);
205 return round(dval);
206 }
207
208 /*
209 * ----------------------------------------------------------------------------
210 *
211 * cmdParseCoord ---
212 *
213 * This is the "normal" usage, calling cmdScaleCoord at a scale of 1.
214 * This routine should be used in all circumstances where the result
215 * is expected in internal units.
216 *
217 * Results:
218 * Integer representing the given coordinate in internal units
219 *
220 * Side Effects:
221 * None.
222 *
223 * ----------------------------------------------------------------------------
224 */
225
226 int
cmdParseCoord(w,arg,is_relative,is_x)227 cmdParseCoord(w, arg, is_relative, is_x)
228 MagWindow *w;
229 char *arg;
230 bool is_relative, is_x;
231 {
232 return cmdScaleCoord(w, arg, is_relative, is_x, 1);
233 }
234
235 /*
236 * ----------------------------------------------------------------------------
237 *
238 * CmdInit --
239 *
240 * Initialization for the commands module.
241 * All we do now is set up the TileTypeBitMasks CmdYMLabel, CmdYMCell
242 * and CmdYMAllButSpace.
243 *
244 * Results:
245 * None.
246 *
247 * Side effects:
248 * See above.
249 *
250 * ----------------------------------------------------------------------------
251 */
252
253 void
CmdInit()254 CmdInit()
255 {
256 TTMaskZero(&CmdYMLabel);
257 TTMaskSetType(&CmdYMLabel, L_LABEL);
258
259 TTMaskZero(&CmdYMCell);
260 TTMaskSetType(&CmdYMCell, L_CELL);
261
262 CmdYMAllButSpace = DBAllButSpaceBits;
263 TTMaskClearType(&CmdYMAllButSpace, L_CELL);
264 }
265
266 /*
267 * ----------------------------------------------------------------------------
268 *
269 * cmdFlushCell --
270 *
271 * Throw away all changes made within Magic to the specified cell,
272 * and re-read it from disk. If no cell is specified, the default
273 * is the current edit cell.
274 *
275 * Results:
276 * None.
277 *
278 * Side effects:
279 * THIS IS NOT UNDO-ABLE!
280 * Modifies the specified CellDef, but marks it as being unmodified.
281 * All parents of the CellDef are re-DRC'ed over both the old and
282 * new areas of the cell.
283 *
284 * ----------------------------------------------------------------------------
285 */
286
287 void
cmdFlushCell(def,force_deref)288 cmdFlushCell(def, force_deref)
289 CellDef *def;
290 bool force_deref;
291 {
292 CellUse *parentUse;
293 bool dereference;
294
295 if (def == NULL) return;
296
297 /* Disallow flushing a cell that contains the edit cell as a child */
298 if (EditCellUse && (EditCellUse->cu_parent == def))
299 {
300 TxError("Cannot flush cell whose subcell is being edited.\n");
301 TxError("%s not flushed\n", def->cd_name);
302 return;
303 }
304
305 UndoFlush();
306
307 if (force_deref)
308 {
309 /* Force dereferencing */
310 def->cd_flags |= CDDEREFERENCE;
311 freeMagic(def->cd_file);
312 def->cd_file = NULL;
313 }
314
315 DBWAreaChanged(def, &def->cd_bbox, DBW_ALLWINDOWS,
316 (TileTypeBitMask *) NULL);
317 for (parentUse = def->cd_parents; parentUse != NULL;
318 parentUse = parentUse->cu_nextuse)
319 {
320 if (parentUse->cu_parent == NULL) continue;
321 DRCCheckThis(parentUse->cu_parent, TT_CHECKSUBCELL,
322 &parentUse->cu_bbox);
323 }
324 DBCellClearDef(def);
325 DBCellClearAvail(def);
326 dereference = (def->cd_flags & CDDEREFERENCE) ? TRUE : FALSE;
327 (void) DBCellRead(def, (char *) NULL, TRUE, dereference, NULL);
328 DBCellSetAvail(def);
329 DBReComputeBbox(def);
330 DBCellSetModified(def, FALSE);
331 DBWAreaChanged(def, &def->cd_bbox, DBW_ALLWINDOWS,
332 &DBAllButSpaceBits);
333 for (parentUse = def->cd_parents; parentUse != NULL;
334 parentUse = parentUse->cu_nextuse)
335 {
336 if (parentUse->cu_parent == NULL) continue;
337 DRCCheckThis(parentUse->cu_parent, TT_CHECKSUBCELL,
338 &parentUse->cu_bbox);
339 }
340 }
341
342 /*
343 * ----------------------------------------------------------------------------
344 *
345 * CmdParseLayers --
346 *
347 * Convert a string specifying a collection of layers into a TileTypeBitMask
348 * representing the layers specified.
349 *
350 * A special layer, '$', refers to all tile types underneath the point
351 * tool, except for the DRC "CHECKxxx" types.
352 *
353 * The layer '*' refers to all tile types except for "check-this" and
354 * the label and cell pseudo-types.
355 *
356 * Results:
357 * TRUE on success, FALSE if any layers are unrecognized.
358 *
359 * Side effects:
360 * Prints an error message if any layers are unrecognized.
361 * Sets bits in 'mask' according to layers in layer specification.
362 * Leaves 'mask' set to 0 if any layers are unrecognized.
363 *
364 * Eventually, this routine should return a "minimal" TileTypeBitMask,
365 * ie, one with the minimum number of bits set consistent with the
366 * string supplied it.
367 *
368 * ----------------------------------------------------------------------------
369 */
370
371 bool
CmdParseLayers(s,mask)372 CmdParseLayers(s, mask)
373 char *s;
374 TileTypeBitMask *mask;
375 {
376 TileTypeBitMask newmask, tempmask;
377 char *dp, c;
378 char name[50];
379 TileType type;
380 Rect rootRect;
381 MagWindow *window;
382 DBWclientRec *crec;
383 bool adding = TRUE;
384 int which, i;
385 #define LN_CELL 0
386 #define LN_LABELS 1
387 #define LN_ALL 2
388 #define LN_DOLLAR 3
389 #define LN_ERRORS 4
390 #define LN_CONNECT 5
391 static struct
392 {
393 char *layer_name;
394 int layer_value;
395 }
396 special[] =
397 {
398 "$", LN_DOLLAR,
399 "*", LN_ALL,
400 "errors", LN_ERRORS,
401 "labels", LN_LABELS,
402 "subcell", LN_CELL,
403 "connect", LN_CONNECT,
404 0,
405 };
406
407
408 TTMaskZero(mask);
409 while (c = *s++)
410 {
411 switch (c)
412 {
413 case '-':
414 adding = FALSE;
415 continue;
416 case '+':
417 adding = TRUE;
418 continue;
419 case ',':
420 case ' ':
421 continue;
422 }
423
424 dp = name; *dp++ = c;
425 while (*s && *s != ',' && *s != '+' && *s != '-' && *s != ' ')
426 *dp++ = *s++;
427 *dp = '\0';
428 if (name[0] == '\0')
429 continue;
430
431 TTMaskZero(&newmask);
432
433 type = DBTechNameTypes(name, &newmask);
434 if (type == -2)
435 {
436 which = LookupStruct(name, (LookupTable *) special, sizeof special[0]);
437 if (which >= 0)
438 {
439 switch (special[which].layer_value)
440 {
441 case LN_LABELS:
442 TTMaskSetType(&newmask, L_LABEL);
443 break;
444 case LN_CELL:
445 TTMaskSetType(&newmask, L_CELL);
446 break;
447 /*
448 * All layers currently beneath the point tool.
449 * Currently, neither labels nor cells are ever included
450 * in this.
451 */
452 case LN_DOLLAR:
453 window = CmdGetRootPoint((Point *) NULL, &rootRect);
454 if ((window == (MagWindow *) NULL)
455 || (window->w_client != DBWclientID))
456 return (FALSE);
457 crec = (DBWclientRec *) window->w_clientData;
458 DBSeeTypesAll(((CellUse *)window->w_surfaceID),
459 &rootRect, crec->dbw_bitmask, &newmask);
460 TTMaskAndMask(&newmask, &crec->dbw_visibleLayers);
461 tempmask = DBAllButSpaceAndDRCBits;
462 TTMaskSetType(&tempmask, TT_SPACE);
463 TTMaskAndMask(&newmask, &tempmask);
464 break;
465 /*
466 * Everything but labels and subcells
467 */
468 case LN_ALL:
469 newmask = DBAllButSpaceAndDRCBits;
470 TTMaskClearType(&newmask, L_LABEL);
471 TTMaskClearType(&newmask, L_CELL);
472 break;
473 /*
474 * All DRC error layers.
475 */
476 case LN_ERRORS:
477 TTMaskSetType(&newmask, TT_ERROR_P);
478 TTMaskSetType(&newmask, TT_ERROR_S);
479 TTMaskSetType(&newmask, TT_ERROR_PS);
480 break;
481 /*
482 * Add in all layers connected to layers already parsed
483 */
484 case LN_CONNECT:
485 for (type = TT_TECHDEPBASE; type < DBNumTypes; type++)
486 if (TTMaskHasType(mask, type))
487 {
488 TileType ttype;
489 for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
490 if (DBConnectsTo(type, ttype))
491 TTMaskSetType(&newmask, ttype);
492 }
493 break;
494 }
495 }
496 else
497 {
498 TxError("Unrecognized layer: %s\n", name);
499 printTypes:
500 DBTechPrintTypes(&DBAllButSpaceAndDRCBits, FALSE);
501 for (i = 0; ; i++)
502 {
503 if (special[i].layer_name == NULL) break;
504 TxError(" %s\n", special[i].layer_name);
505 }
506 return (FALSE);
507 }
508 }
509 else if (type == -1)
510 {
511 TxError("Ambiguous layer: %s\n", name);
512 goto printTypes;
513 }
514
515 if (adding)
516 {
517 TTMaskSetMask(mask, &newmask);
518 }
519 else
520 {
521 TTMaskClearMask(mask, &newmask);
522 }
523 }
524
525 return (TRUE);
526 }
527
528 /*
529 * ----------------------------------------------------------------------------
530 *
531 * cmdMaskToType --
532 *
533 * Convert a TileTypeBitMask into a TileType.
534 *
535 * Results:
536 * Returns -1 if more than one type bit is set in the TileTypeBitMask;
537 * otherwise, returns the TileType of the bit set.
538 *
539 * Side effects:
540 * None.
541 *
542 * ----------------------------------------------------------------------------
543 */
544
545 TileType
cmdMaskToType(mask)546 cmdMaskToType(mask)
547 TileTypeBitMask *mask;
548 {
549 TileType type, t;
550
551 type = -1;
552 for (t = TT_SELECTBASE; t < DBNumTypes; t++)
553 {
554 if (TTMaskHasType(mask, t))
555 {
556 if (type >= 0)
557 return (-1);
558 type = t;
559 }
560 }
561
562 if (type < 0)
563 return (TT_SPACE);
564 return (type);
565 }
566
567 /*
568 * ----------------------------------------------------------------------------
569 *
570 * cmdSaveCell --
571 *
572 * Save a given cell out to disk.
573 * If a filename is given, the cell is written out to that file;
574 * otherwise, the cell is written out to the file stored with the
575 * cellDef, or to a newly created file of the same name as the
576 * cellDef. If there is no name associated with the cell, the
577 * save is disallowed.
578 *
579 * The name of the cell is set to the filename, if it is specified.
580 *
581 * Results:
582 * None.
583 *
584 * Side effects:
585 * Writes the cell out to a disk file.
586 * Clears the modified bit in the cd_flags.
587 *
588 * ----------------------------------------------------------------------------
589 */
590
591 void
cmdSaveCell(cellDef,newName,noninteractive,tryRename)592 cmdSaveCell(cellDef, newName, noninteractive, tryRename)
593 CellDef *cellDef; /* Pointer to def of cell to be saved */
594 char *newName; /* Pointer to name of file in which cell is to be
595 * saved. May be NULL, in which case the name from
596 * the CellDef is taken.
597 */
598 bool noninteractive;/* If true, try hard but don't ask the user
599 * questions.
600 */
601 bool tryRename; /* We should rename the cell to the name of the
602 * place where it was saved.
603 */
604 {
605 char *fileName = newName;
606
607 /* Eliminate the phony labels added for use by rsim */
608 #ifndef NO_SIM_MODULE
609 SimEraseLabels();
610 #endif
611
612 /*
613 * Whenever the "unnamed" cell is saved, the name of the
614 * cell changes to the name of the file in which it was
615 * saved.
616 */
617
618 if (strcmp(cellDef->cd_name, UNNAMED) == 0)
619 {
620 if (newName == NULL)
621 TxPrintf("Must specify name for cell %s.\n", UNNAMED);
622 fileName = cmdCheckNewName(cellDef, newName, TRUE, noninteractive);
623 if (fileName == NULL) return;
624 }
625 else if (newName != NULL)
626 {
627 fileName = cmdCheckNewName(cellDef, newName, TRUE, noninteractive);
628 if (fileName == NULL) return;
629 }
630 else
631 {
632 if (cellDef->cd_file == NULL)
633 {
634 fileName = cmdCheckNewName(cellDef, cellDef->cd_name,
635 TRUE, noninteractive);
636 if (fileName == NULL) return;
637 }
638 }
639
640 DBUpdateStamps();
641 if (!DBCellWrite(cellDef, fileName))
642 {
643 TxError("Could not write file. Cell not written.\n");
644 goto cleanup;
645 }
646
647 if (!tryRename || (fileName == NULL) || (strcmp(cellDef->cd_name, fileName) == 0))
648 goto cleanup;
649
650 /* Rename the cell */
651 if (!DBCellRenameDef(cellDef, fileName))
652 {
653 /* This should never happen */
654 TxError("Magic error: there is already a cell named \"%s\"\n",
655 fileName);
656 goto cleanup;
657 }
658
659 if (EditCellUse && (cellDef == EditCellUse->cu_def))
660 {
661 /*
662 * The cell is the edit cell.
663 * All windows with compatible roots should show
664 * a caption of "root EDITING edit"
665 */
666 CmdSetWindCaption(EditCellUse, EditRootDef);
667 }
668 else
669 {
670 /*
671 * The cell is not the edit cell.
672 * We want to find all windows for which this is
673 * the root cell and update their captions.
674 */
675 (void) WindSearch(DBWclientID, (ClientData) NULL, (Rect *) NULL,
676 cmdSaveWindSet, (ClientData) cellDef);
677 }
678
679 cleanup:
680 if ((fileName != newName) && (fileName != cellDef->cd_name))
681 freeMagic(fileName);
682 return;
683 }
684
685 /*
686 * ----------------------------------------------------------------------------
687 *
688 * cmdCheckNewName --
689 *
690 * Get the name of the file in which the argument CellDef is to
691 * be saved, if a name was not already provided.
692 * If the name of the file is different from the name of the cell,
693 * check to make sure that the file doesn't already exist.
694 * If the CellDef is to be renamed after saving it, check to make
695 * sure that no cell already exists by the new name.
696 *
697 * Results:
698 * Returns a pointer to a string holding the filename in which
699 * the cell is to be saved, or NULL if the save should be aborted.
700 *
701 * Side effects:
702 * May prompt the user for a new file name if one is required.
703 * If the filename returned was one typed by the user in response to
704 * this prompt, it overwrites the previous such filename returned,
705 * as it is stored in a static array.
706 *
707 * ----------------------------------------------------------------------------
708 */
709
710 char *
cmdCheckNewName(def,newName,tryRename,noninteractive)711 cmdCheckNewName(def, newName, tryRename, noninteractive)
712 CellDef *def;
713 char *newName;
714 bool tryRename;
715 bool noninteractive;
716 {
717 static char *yesno[] = { "no", "yes", 0 };
718 char *filename;
719 char *prompt;
720 char *returnname;
721 int code;
722 FILE *f;
723
724 returnname = newName;
725
726 again:
727 if (returnname == NULL)
728 {
729 if (noninteractive) {
730 TxError("Can't write file named '%s'\n", def->cd_name);
731 return NULL;
732 };
733 TxPrintf("File for cell %s: [hit return to abort save] ", def->cd_name);
734 returnname = (char *)mallocMagic(1024 * sizeof(char));
735 if (TxGetLine(returnname, sizeof returnname) == NULL || returnname[0] == '\0')
736 {
737 TxPrintf("Cell not saved.\n");
738 freeMagic(returnname);
739 return ((char *) NULL);
740 }
741 if (CmdIllegalChars(returnname, "[],", "Cell name"))
742 {
743 freeMagic(returnname);
744 goto again;
745 }
746 }
747
748 /* Remove any ".mag" file extension from the name */
749 if (strlen(returnname) > 4)
750 if (!strcmp(returnname + strlen(returnname) - 4, ".mag"))
751 *(returnname + strlen(returnname) - 4) = '\0';
752
753 if (strcmp(returnname, def->cd_name) != 0)
754 {
755 if (f = PaOpen(returnname, "r", DBSuffix, ".", (char *) NULL, &filename))
756 {
757 (void) fclose(f);
758 if (noninteractive) {
759 TxError("Overwriting file '%s' with cell '%s'\n", filename,
760 def->cd_name);
761 }
762 else {
763 prompt = TxPrintString("File %s already exists.\n"
764 " Overwrite it with %s? ", filename, def->cd_name);
765 code = TxDialog(prompt, yesno, 0);
766
767 if (code == 0)
768 {
769 /* No -- don't overwrite */
770 if (returnname != newName) freeMagic(returnname);
771 returnname = NULL;
772 goto again;
773 }
774 }
775 }
776
777 if (tryRename && DBCellLookDef(returnname) != NULL)
778 {
779 TxError("Can't rename cell '%s' to '%s' because that cell already exists.\n",
780 def->cd_name, returnname);
781 if (returnname != newName) freeMagic(returnname);
782 if (noninteractive) return NULL;
783 returnname = NULL;
784 goto again;
785 }
786 }
787 return (returnname);
788 }
789
790 /*
791 * ----------------------------------------------------------------------------
792 *
793 * nameEllipsis ---
794 *
795 * Truncate a string an append an ellipsis ("...") to the end if the string
796 * will overflow a fixed array length.
797 *
798 * ----------------------------------------------------------------------------
799 */
800
801 static char *
nameEllipsis(name,maxlen,prefix)802 nameEllipsis(name, maxlen, prefix)
803 char *name;
804 int maxlen;
805 char **prefix;
806 {
807 int l = strlen(name);
808
809 if (l < maxlen)
810 {
811 *prefix = "";
812 return name;
813 }
814 else
815 {
816 *prefix = "...";
817 return &name[l - maxlen + 3];
818 }
819 }
820
821 /*
822 * ----------------------------------------------------------------------------
823 *
824 * cmdSaveWindSet --
825 *
826 * Filter function for cmdSaveCell() above.
827 * Called by WindSearch() with each window.
828 *
829 * The idea is to change only those captions in windows whose root
830 * uses are instances of the def 'def'. Sets the caption of each such
831 * window to:
832 *
833 * def [NOT BEING EDITED]
834 *
835 * Results:
836 * Always 0 to keep the search going.
837 *
838 * Side effects:
839 * Modifies captions and clientData for the window
840 *
841 * ----------------------------------------------------------------------------
842 */
843
844 int
cmdSaveWindSet(window,def)845 cmdSaveWindSet(window, def)
846 MagWindow *window;
847 CellDef *def;
848 {
849 char caption[200];
850 CellDef *rootDef;
851 char *name, *name_pfx;
852
853 rootDef = ((CellUse *) window->w_surfaceID)->cu_def;
854 if (rootDef != def)
855 return 0;
856
857 name = nameEllipsis(def->cd_name, 175, &name_pfx);
858 (void) snprintf(caption, sizeof(caption), "%s%s [NOT BEING EDITED]", name_pfx, name);
859 (void) StrDup(&window->w_iconname, def->cd_name);
860 WindCaption(window, caption);
861 return 0;
862 }
863
864 /*
865 * ----------------------------------------------------------------------------
866 *
867 * CmdSetWindCaption --
868 *
869 * Update the captions of all windows to reflect a new EditCell.
870 * The caption of each window having the same root cell def as the
871 * one in which the Edit Cell was selected is set to show that the
872 * window is subediting the new edit cell. The captions in all
873 * other windows show that these windows are not subediting the
874 * edit cell.
875 *
876 * Results:
877 * None.
878 *
879 * Side effects:
880 * Modifies captions and clientData for each window.
881 *
882 * ----------------------------------------------------------------------------
883 */
884
885 /*
886 * The following are used to pass information down to the filter
887 * function applied by WindSearch(), since it is not intended that
888 * CmdSetWindCaption() be re-entrant.
889 */
890
891 static CellDef *newEditDef; /* Pointer to new edit cell def */
892 static CellDef *newRootDef; /* Pointer to root def of window in which
893 * new edit cell was selected. This is
894 * used to determine whether the edit cell
895 * is being edited in a window or not.
896 */
897
898 void
CmdSetWindCaption(newEditUse,rootDef)899 CmdSetWindCaption(newEditUse, rootDef)
900 CellUse *newEditUse; /* Pointer to new edit cell use */
901 CellDef *rootDef; /* Root cell def of the window in which the
902 * edit cell was selected.
903 */
904 {
905 int cmdWindSet();
906
907 newEditDef = (newEditUse) ? newEditUse->cu_def : NULL;
908 newRootDef = rootDef;
909 (void) WindSearch(DBWclientID, (ClientData) NULL, (Rect *) NULL,
910 cmdWindSet, (ClientData) 0);
911 }
912
913 /*
914 * ----------------------------------------------------------------------------
915 *
916 * cmdWindSet --
917 *
918 * Filter function for CmdSetWindCaption() above.
919 * Called by WindSearch() with each window.
920 *
921 * If the window is compatible with the new edit cell, sets the caption
922 * for that window to be
923 *
924 * RootDef EDITING EditDef
925 *
926 * Otherwise, sets the caption to be
927 *
928 * RootDef [NOT BEING EDITED]
929 *
930 * Results:
931 * Always 0 to keep the function going.
932 *
933 * Side effects:
934 * Modifies captions and clientData for the window
935 *
936 * ----------------------------------------------------------------------------
937 */
938
939 int
cmdWindSet(window)940 cmdWindSet(window)
941 MagWindow *window;
942 {
943 char caption[200];
944 CellDef *wDef;
945 char *name[2], *name_pfx[2];
946
947 wDef = ((CellUse *) window->w_surfaceID)->cu_def;
948
949
950
951 if (wDef != newRootDef) {
952 name[0] = nameEllipsis(wDef->cd_name, 175, &name_pfx[0]);
953 (void) snprintf(caption, sizeof(caption), "%s%s [NOT BEING EDITED]",
954 name_pfx[0], name[0]);
955 } else {
956 name[0] = nameEllipsis(wDef->cd_name, 90, &name_pfx[0]);
957 name[1] = nameEllipsis(newEditDef->cd_name, 90, &name_pfx[1]);
958 (void) snprintf(caption, sizeof(caption), "%s%s EDITING %s%s",
959 name_pfx[0], name[0], name_pfx[1], name[1]);
960
961 #ifdef SCHEME_INTERPRETER
962 /* Add a binding to scheme variable "edit-cell" */
963 LispSetEdit (newEditDef->cd_name);
964 #endif
965 }
966
967 (void) StrDup(&window->w_iconname, wDef->cd_name);
968 WindCaption(window, caption);
969 return 0;
970 }
971
972
973 /*
974 * ----------------------------------------------------------------------------
975 *
976 * CmdGetRootPoint --
977 *
978 * Get the window containing the point tool, and return (in root cell
979 * coordinates for that window) the coordinates of the point, and of
980 * a minimum-grid-size rectangle enclosing the point.
981 *
982 * Results:
983 * Pointer to window containing the point tool, or NULL if the
984 * point tool is not present.
985 *
986 * Side effects:
987 * Sets *point to be the coordinates of the point tool in root
988 * coordinates, and *rect to be the minimum-grid-size enclosing
989 * rectangle.
990 *
991 * Prints an error message if the point is not found.
992 *
993 * ----------------------------------------------------------------------------
994 */
995
996 MagWindow *
CmdGetRootPoint(point,rect)997 CmdGetRootPoint(point, rect)
998 Point *point;
999 Rect *rect;
1000 {
1001 MagWindow *window;
1002
1003 window = ToolGetPoint(point, rect);
1004 if (window == (MagWindow *) NULL)
1005 TxError("Crosshair not in a valid window for this command\n");
1006
1007 return (window);
1008 }
1009
1010 /*
1011 * ----------------------------------------------------------------------------
1012 *
1013 * CmdGetEditPoint --
1014 *
1015 * Get the window containing the point tool, and return (in edit cell
1016 * coordinates for that window) the coordinates of the point, and of
1017 * a minimum-grid-size rectangle enclosing the point.
1018 *
1019 * Results:
1020 * Pointer to window containing the point tool, or NULL if the
1021 * point tool is not present.
1022 *
1023 * Side effects:
1024 * Sets *point to be the coordinates of the point tool in edit
1025 * coordinates, and *rect to be the minimum-grid-size enclosing
1026 * rectangle.
1027 *
1028 * ----------------------------------------------------------------------------
1029 */
1030
1031 MagWindow *
CmdGetEditPoint(point,rect)1032 CmdGetEditPoint(point, rect)
1033 Point *point;
1034 Rect *rect;
1035 {
1036 MagWindow *window;
1037 Rect rootRect;
1038 Point rootPoint;
1039
1040 window = CmdGetRootPoint(&rootPoint, &rootRect);
1041 if (window != (MagWindow *) NULL)
1042 {
1043 GeoTransRect(&RootToEditTransform, &rootRect, rect);
1044 GeoTransPoint(&RootToEditTransform, &rootPoint, point);
1045 }
1046
1047 return (window);
1048 }
1049
1050 /*
1051 * ----------------------------------------------------------------------------
1052 *
1053 * CmdWarnWrite --
1054 *
1055 * Check to see if there are modified and unwritten cells and ask the
1056 * user whether he wants to stay in magic or lose all these cells.
1057 *
1058 * Results:
1059 * TRUE if the user wishes to continue anyway without writing the
1060 * modified cells out to disk, FALSE if not.
1061 *
1062 * Side effects:
1063 * None.
1064 *
1065 * ----------------------------------------------------------------------------
1066 */
1067
1068 bool
CmdWarnWrite()1069 CmdWarnWrite()
1070 {
1071 int count, code;
1072 int cmdWarnWriteFunc();
1073 static char *yesno[] = { "no", "yes", 0 };
1074 char *prompt;
1075
1076 count = 0;
1077 (void) DBCellSrDefs(CDMODIFIED|CDBOXESCHANGED|CDSTAMPSCHANGED,
1078 cmdWarnWriteFunc, (ClientData) &count);
1079 if (count == 0)
1080 return TRUE;
1081
1082 prompt = TxPrintString("%d Magic cell%s been modified.\n Do you"
1083 " want to exit magic and lose %s? ", count,
1084 count == 1 ? " has" : "s have",
1085 count == 1 ? "it" : "them");
1086 code = TxDialog(prompt, yesno, 0);
1087 return (code) ? TRUE : FALSE;
1088 }
1089
1090 int
cmdWarnWriteFunc(cellDef,pcount)1091 cmdWarnWriteFunc(cellDef, pcount)
1092 CellDef *cellDef;
1093 int *pcount;
1094 {
1095 if ((cellDef->cd_flags & CDINTERNAL) == 0)
1096 (*pcount)++;
1097 return 0;
1098 }
1099
1100 /*
1101 * ----------------------------------------------------------------------------
1102 * cmdExpandOneLevel --
1103 *
1104 * Expand (unexpand) a cell, and unexpand all of its children. This is
1105 * called by commands such as getcell, expand current cell, and load.
1106 * Don't bother to unexpand children if we are unexpanding this cell.
1107 *
1108 * Results:
1109 * None.
1110 *
1111 * Side effects:
1112 * None.
1113 * ----------------------------------------------------------------------------
1114 */
1115
1116 void
cmdExpandOneLevel(cu,bitmask,expand)1117 cmdExpandOneLevel(cu, bitmask, expand)
1118 CellUse *cu;
1119 int bitmask;
1120 bool expand;
1121 {
1122 extern int cmdExpand1func();
1123
1124 /* first, expand this cell use */
1125 DBExpand(cu, bitmask, expand);
1126
1127 /* now, unexpand its direct children (ONE LEVEL ONLY) */
1128 if (expand)
1129 (void) DBCellEnum(cu->cu_def, cmdExpand1func, (ClientData) bitmask);
1130 }
1131
1132 int
cmdExpand1func(cu,bitmask)1133 cmdExpand1func(cu, bitmask)
1134 CellUse *cu;
1135 ClientData bitmask;
1136 {
1137 DBExpand(cu, (int) bitmask, FALSE);
1138 return 0;
1139 }
1140
1141 /*
1142 * ----------------------------------------------------------------------------
1143 *
1144 * CmdGetSelectedCell --
1145 *
1146 * This procedure returns a pointer to the selected cell.
1147 *
1148 * Results:
1149 * The return value is a pointer to the selected cell. If more
1150 * than one cell is selected, the upper-leftmost cell is returned.
1151 * If no cell is selected, NULL is returned.
1152 *
1153 * Side effects:
1154 * If pTrans isn't NULL, the area it points to is modified to hold
1155 * the transform from coords of the selected cell to root coords.
1156 *
1157 * ----------------------------------------------------------------------------
1158 */
1159
1160 Transform *cmdSelTrans; /* Shared between CmdGetSelectedCell and
1161 * cmdGetCellFunc.
1162 */
1163
1164 CellUse *
CmdGetSelectedCell(pTrans)1165 CmdGetSelectedCell(pTrans)
1166 Transform *pTrans; /* If non-NULL, transform from selected
1167 * cell to root coords is stored here.
1168 */
1169 {
1170 CellUse *result = NULL;
1171 int cmdGetSelFunc(); /* Forward declaration. */
1172
1173 cmdSelTrans = pTrans;
1174 (void) SelEnumCells(FALSE, (bool *) NULL, (SearchContext *) NULL,
1175 cmdGetSelFunc, (ClientData) &result);
1176 return result;
1177 }
1178
1179 /* ARGSUSED */
1180 int
cmdGetSelFunc(selUse,realUse,transform,pResult)1181 cmdGetSelFunc(selUse, realUse, transform, pResult)
1182 CellUse *selUse; /* Not used. */
1183 CellUse *realUse; /* The first selected use. */
1184 Transform *transform; /* Transform from coords of realUse to root. */
1185 CellUse **pResult; /* Store realUse here. */
1186 {
1187 *pResult = realUse;
1188 if (cmdSelTrans != NULL)
1189 *cmdSelTrans = *transform;
1190 return 1; /* Skip any other selected cells. */
1191 }
1192
1193 /*
1194 * ----------------------------------------------------------------------------
1195 *
1196 * CmdIllegalChars --
1197 *
1198 * Checks a string for any of a number of illegal characters.
1199 * If any is found, it's printed in an error message.
1200 *
1201 * Results:
1202 * TRUE is returned if any of the characters in "illegal" is
1203 * also in "string", or if "string" contains any control or
1204 * non-ASCII characters. Otherwise, FALSE is returned.
1205 *
1206 * Side effects:
1207 * None.
1208 *
1209 * ----------------------------------------------------------------------------
1210 */
1211
1212 bool
CmdIllegalChars(string,illegal,msg)1213 CmdIllegalChars(string, illegal, msg)
1214 char *string; /* String to check for illegal chars. */
1215 char *illegal; /* String containing illegal chars. */
1216 char *msg; /* String identifying what string is
1217 * supposed to represent, for ease in
1218 * printing error messages.
1219 */
1220 {
1221 char *p, *bad;
1222
1223 for (p = string; *p != 0; p++)
1224 {
1225 if (!isascii(*p)) goto error;
1226 if (iscntrl(*p)) goto error;
1227 for (bad = illegal; *bad != 0; bad++)
1228 {
1229 if (*bad == *p) goto error;
1230 }
1231 continue;
1232
1233 error:
1234 if (!isascii(*p) || iscntrl(*p))
1235 {
1236 TxError("%s contains illegal control character 0x%x\n",
1237 msg, *p);
1238 }
1239 else TxError("%s contains illegal character \"%c\"\n",
1240 msg, *p);
1241 return TRUE;
1242 }
1243 return FALSE;
1244 }
1245
1246