This patch adds support for doing Report(netlength,net_name)
[geda-pcb/pcjc2.git] / src / report.c
blobcb9afc6207a2abab6da4237cce97644d46c39793
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,1997,1998,1999 Thomas Nau
7 * This module, report.c, was written and is Copyright (C) 1997 harry eaton
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 * Contact addresses for paper mail and Email:
24 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
25 * Thomas.Nau@rz.uni-ulm.de
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
34 #include <math.h>
36 #include "report.h"
37 #include "crosshair.h"
38 #include "data.h"
39 #include "drill.h"
40 #include "error.h"
41 #include "search.h"
42 #include "misc.h"
43 #include "mymem.h"
44 #include "rats.h"
45 #include "rtree.h"
46 #include "strflags.h"
47 #include "macro.h"
48 #include "undo.h"
49 #include "find.h"
50 #include "draw.h"
51 #include "pcb-printf.h"
52 #ifdef HAVE_REGEX_H
53 #include <regex.h>
54 #endif
56 #ifdef HAVE_REGCOMP
57 #undef HAVE_RE_COMP
58 #endif
60 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
61 #define USE_RE
62 #endif
64 #ifdef HAVE_LIBDMALLOC
65 #include <dmalloc.h>
66 #endif
68 #define USER_UNITMASK (Settings.grid_unit->allow)
70 static int
71 ReportDrills (int argc, char **argv, Coord x, Coord y)
73 DrillInfoTypePtr AllDrills;
74 Cardinal n;
75 char *stringlist, *thestring;
76 int total_drills = 0;
78 AllDrills = GetDrillInfo (PCB->Data);
79 RoundDrillInfo (AllDrills, 100);
81 for (n = 0; n < AllDrills->DrillN; n++)
83 total_drills += AllDrills->Drill[n].PinCount;
84 total_drills += AllDrills->Drill[n].ViaCount;
85 total_drills += AllDrills->Drill[n].UnplatedCount;
88 stringlist = (char *)malloc (512L + AllDrills->DrillN * 64L);
90 /* Use tabs for formatting since can't count on a fixed font anymore.
91 | And even that probably isn't going to work in all cases.
93 sprintf (stringlist,
94 "There are %d different drill sizes used in this layout, %d holes total\n\n"
95 "Drill Diam. (%s)\t# of Pins\t# of Vias\t# of Elements\t# Unplated\n",
96 AllDrills->DrillN, total_drills, Settings.grid_unit->suffix);
97 thestring = stringlist;
98 while (*thestring != '\0')
99 thestring++;
100 for (n = 0; n < AllDrills->DrillN; n++)
102 pcb_sprintf (thestring,
103 "%10m*\t\t%d\t\t%d\t\t%d\t\t%d\n",
104 Settings.grid_unit->suffix,
105 AllDrills->Drill[n].DrillSize,
106 AllDrills->Drill[n].PinCount, AllDrills->Drill[n].ViaCount,
107 AllDrills->Drill[n].ElementN,
108 AllDrills->Drill[n].UnplatedCount);
109 while (*thestring != '\0')
110 thestring++;
112 FreeDrillInfo (AllDrills);
113 /* create dialog box */
114 gui->report_dialog ("Drill Report", stringlist);
116 free (stringlist);
117 return 0;
121 static const char reportdialog_syntax[] = "ReportDialog()";
123 static const char reportdialog_help[] =
124 "Report on the object under the crosshair";
126 /* %start-doc actions ReportDialog
128 This is a shortcut for @code{Report(Object)}.
130 %end-doc */
132 static int
133 ReportDialog (int argc, char **argv, Coord x, Coord y)
135 void *ptr1, *ptr2, *ptr3;
136 int type;
137 char report[2048];
139 type = SearchScreen (x, y, REPORT_TYPES, &ptr1, &ptr2, &ptr3);
140 if (type == NO_TYPE)
141 type =
142 SearchScreen (x, y, REPORT_TYPES | LOCKED_TYPE, &ptr1, &ptr2, &ptr3);
144 switch (type)
146 case VIA_TYPE:
148 PinTypePtr via;
149 #ifndef NDEBUG
150 if (gui->shift_is_pressed ())
152 __r_dump_tree (PCB->Data->via_tree->root, 0);
153 return 0;
155 #endif
156 via = (PinTypePtr) ptr2;
157 if (TEST_FLAG (HOLEFLAG, via))
158 pcb_sprintf (&report[0], "%m+VIA ID# %ld; Flags:%s\n"
159 "(X,Y) = %$mD.\n"
160 "It is a pure hole of diameter %$mS.\n"
161 "Name = \"%s\"."
162 "%s", USER_UNITMASK, via->ID, flags_to_string (via->Flags, VIA_TYPE),
163 via->X, via->Y, via->DrillingHole, EMPTY (via->Name),
164 TEST_FLAG (LOCKFLAG, via) ? "It is LOCKED.\n" : "");
165 else
166 pcb_sprintf (&report[0], "%m+VIA ID# %ld; Flags:%s\n"
167 "(X,Y) = %$mD.\n"
168 "Copper width = %$mS. Drill width = %$mS.\n"
169 "Clearance width in polygons = %$mS.\n"
170 "Annulus = %$mS.\n"
171 "Solder mask hole = %$mS (gap = %$mS).\n"
172 "Name = \"%s\"."
173 "%s", USER_UNITMASK, via->ID, flags_to_string (via->Flags, VIA_TYPE),
174 via->X, via->Y,
175 via->Thickness,
176 via->DrillingHole,
177 via->Clearance / 2,
178 (via->Thickness - via->DrillingHole) / 2,
179 via->Mask,
180 (via->Mask - via->Thickness) / 2,
181 EMPTY (via->Name), TEST_FLAG (LOCKFLAG, via) ?
182 "It is LOCKED.\n" : "");
183 break;
185 case PIN_TYPE:
187 PinTypePtr Pin;
188 ElementTypePtr element;
189 #ifndef NDEBUG
190 if (gui->shift_is_pressed ())
192 __r_dump_tree (PCB->Data->pin_tree->root, 0);
193 return 0;
195 #endif
196 Pin = (PinTypePtr) ptr2;
197 element = (ElementTypePtr) ptr1;
199 PIN_LOOP (element);
201 if (pin == Pin)
202 break;
204 END_LOOP;
205 if (TEST_FLAG (HOLEFLAG, Pin))
206 pcb_sprintf (&report[0], "%m+PIN ID# %ld; Flags:%s\n"
207 "(X,Y) = %$mD.\n"
208 "It is a mounting hole. Drill width = %$mS.\n"
209 "It is owned by element %$mS.\n"
210 "%s", USER_UNITMASK, Pin->ID, flags_to_string (Pin->Flags, PIN_TYPE),
211 Pin->X, Pin->Y, Pin->DrillingHole,
212 EMPTY (element->Name[1].TextString),
213 TEST_FLAG (LOCKFLAG, Pin) ? "It is LOCKED.\n" : "");
214 else
215 pcb_sprintf (&report[0],
216 "%m+PIN ID# %ld; Flags:%s\n" "(X,Y) = %$mD.\n"
217 "Copper width = %$mS. Drill width = %$mS.\n"
218 "Clearance width to Polygon = %$mS.\n"
219 "Annulus = %$mS.\n"
220 "Solder mask hole = %$mS (gap = %$mS).\n"
221 "Name = \"%s\".\n"
222 "It is owned by element %s\n as pin number %s.\n"
223 "%s", USER_UNITMASK,
224 Pin->ID, flags_to_string (Pin->Flags, PIN_TYPE),
225 Pin->X, Pin->Y, Pin->Thickness,
226 Pin->DrillingHole,
227 Pin->Clearance / 2,
228 (Pin->Thickness - Pin->DrillingHole) / 2,
229 Pin->Mask,
230 (Pin->Mask - Pin->Thickness) / 2,
231 EMPTY (Pin->Name),
232 EMPTY (element->Name[1].TextString), EMPTY (Pin->Number),
233 TEST_FLAG (LOCKFLAG, Pin) ? "It is LOCKED.\n" : "");
234 break;
236 case LINE_TYPE:
238 LineTypePtr line;
239 #ifndef NDEBUG
240 if (gui->shift_is_pressed ())
242 LayerTypePtr layer = (LayerTypePtr) ptr1;
243 __r_dump_tree (layer->line_tree->root, 0);
244 return 0;
246 #endif
247 line = (LineTypePtr) ptr2;
248 pcb_sprintf (&report[0], "%m+LINE ID# %ld; Flags:%s\n"
249 "FirstPoint(X,Y) = %$mD, ID = %ld.\n"
250 "SecondPoint(X,Y) = %$mD, ID = %ld.\n"
251 "Width = %$mS.\nClearance width in polygons = %$mS.\n"
252 "It is on layer %d\n"
253 "and has name \"%s\".\n"
254 "%s", USER_UNITMASK,
255 line->ID, flags_to_string (line->Flags, LINE_TYPE),
256 line->Point1.X, line->Point1.Y, line->Point1.ID,
257 line->Point2.X, line->Point2.Y, line->Point2.ID,
258 line->Thickness, line->Clearance / 2,
259 GetLayerNumber (PCB->Data, (LayerTypePtr) ptr1),
260 UNKNOWN (line->Number),
261 TEST_FLAG (LOCKFLAG, line) ? "It is LOCKED.\n" : "");
262 break;
264 case RATLINE_TYPE:
266 RatTypePtr line;
267 #ifndef NDEBUG
268 if (gui->shift_is_pressed ())
270 __r_dump_tree (PCB->Data->rat_tree->root, 0);
271 return 0;
273 #endif
274 line = (RatTypePtr) ptr2;
275 pcb_sprintf (&report[0], "%m+RAT-LINE ID# %ld; Flags:%s\n"
276 "FirstPoint(X,Y) = %$mD; ID = %ld; "
277 "connects to layer group %d.\n"
278 "SecondPoint(X,Y) = %$mD; ID = %ld; "
279 "connects to layer group %d.\n",
280 USER_UNITMASK, line->ID, flags_to_string (line->Flags, LINE_TYPE),
281 line->Point1.X, line->Point1.Y,
282 line->Point1.ID, line->group1,
283 line->Point2.X, line->Point2.Y,
284 line->Point2.ID, line->group2);
285 break;
287 case ARC_TYPE:
289 ArcTypePtr Arc;
290 BoxTypePtr box;
291 #ifndef NDEBUG
292 if (gui->shift_is_pressed ())
294 LayerTypePtr layer = (LayerTypePtr) ptr1;
295 __r_dump_tree (layer->arc_tree->root, 0);
296 return 0;
298 #endif
299 Arc = (ArcTypePtr) ptr2;
300 box = GetArcEnds (Arc);
302 pcb_sprintf (&report[0], "%m+ARC ID# %ld; Flags:%s\n"
303 "CenterPoint(X,Y) = %$mD.\n"
304 "Radius = %$mS, Thickness = %$mS.\n"
305 "Clearance width in polygons = %$mS.\n"
306 "StartAngle = %ma degrees, DeltaAngle = %ma degrees.\n"
307 "Bounding Box is %$mD, %$mD.\n"
308 "That makes the end points at %$mD and %$mD.\n"
309 "It is on layer %d.\n"
310 "%s", USER_UNITMASK, Arc->ID, flags_to_string (Arc->Flags, ARC_TYPE),
311 Arc->X, Arc->Y,
312 Arc->Width, Arc->Thickness,
313 Arc->Clearance / 2, Arc->StartAngle, Arc->Delta,
314 Arc->BoundingBox.X1, Arc->BoundingBox.Y1,
315 Arc->BoundingBox.X2, Arc->BoundingBox.Y2,
316 box->X1, box->Y1,
317 box->X2, box->Y2,
318 GetLayerNumber (PCB->Data, (LayerTypePtr) ptr1),
319 TEST_FLAG (LOCKFLAG, Arc) ? "It is LOCKED.\n" : "");
320 break;
322 case POLYGON_TYPE:
324 PolygonTypePtr Polygon;
325 #ifndef NDEBUG
326 if (gui->shift_is_pressed ())
328 LayerTypePtr layer = (LayerTypePtr) ptr1;
329 __r_dump_tree (layer->polygon_tree->root, 0);
330 return 0;
332 #endif
333 Polygon = (PolygonTypePtr) ptr2;
335 pcb_sprintf (&report[0], "%m+POLYGON ID# %ld; Flags:%s\n"
336 "Its bounding box is %$mD %$mD.\n"
337 "It has %d points and could store %d more\n"
338 " without using more memory.\n"
339 "It has %d holes and resides on layer %d.\n"
340 "%s", USER_UNITMASK, Polygon->ID,
341 flags_to_string (Polygon->Flags, POLYGON_TYPE),
342 Polygon->BoundingBox.X1, Polygon->BoundingBox.Y1,
343 Polygon->BoundingBox.X2, Polygon->BoundingBox.Y2,
344 Polygon->PointN, Polygon->PointMax - Polygon->PointN,
345 Polygon->HoleIndexN,
346 GetLayerNumber (PCB->Data, (LayerTypePtr) ptr1),
347 TEST_FLAG (LOCKFLAG, Polygon) ? "It is LOCKED.\n" : "");
348 break;
350 case PAD_TYPE:
352 Coord len;
353 PadTypePtr Pad;
354 ElementTypePtr element;
355 #ifndef NDEBUG
356 if (gui->shift_is_pressed ())
358 __r_dump_tree (PCB->Data->pad_tree->root, 0);
359 return 0;
361 #endif
362 Pad = (PadTypePtr) ptr2;
363 element = (ElementTypePtr) ptr1;
365 PAD_LOOP (element);
368 if (pad == Pad)
369 break;
372 END_LOOP;
373 len = Distance (Pad->Point1.X, Pad->Point1.Y, Pad->Point2.X, Pad->Point2.Y);
374 pcb_sprintf (&report[0], "%m+PAD ID# %ld; Flags:%s\n"
375 "FirstPoint(X,Y) = %$mD; ID = %ld.\n"
376 "SecondPoint(X,Y) = %$mD; ID = %ld.\n"
377 "Width = %$mS. Length = %$mS.\n"
378 "Clearance width in polygons = %$mS.\n"
379 "Solder mask = %$mS x %$mS (gap = %$mS).\n"
380 "Name = \"%s\".\n"
381 "It is owned by SMD element %s\n"
382 " as pin number %s and is on the %s\n"
383 "side of the board.\n"
384 "%s", USER_UNITMASK, Pad->ID,
385 flags_to_string (Pad->Flags, PAD_TYPE),
386 Pad->Point1.X, Pad->Point1.Y, Pad->Point1.ID,
387 Pad->Point2.X, Pad->Point2.Y, Pad->Point2.ID,
388 Pad->Thickness, len + Pad->Thickness,
389 Pad->Clearance / 2,
390 Pad->Mask, len + Pad->Mask,
391 (Pad->Mask - Pad->Thickness) / 2,
392 EMPTY (Pad->Name),
393 EMPTY (element->Name[1].TextString),
394 EMPTY (Pad->Number),
395 TEST_FLAG (ONSOLDERFLAG,
396 Pad) ? "solder (bottom)" : "component",
397 TEST_FLAG (LOCKFLAG, Pad) ? "It is LOCKED.\n" : "");
398 break;
400 case ELEMENT_TYPE:
402 ElementTypePtr element;
403 #ifndef NDEBUG
404 if (gui->shift_is_pressed ())
406 __r_dump_tree (PCB->Data->element_tree->root, 0);
407 return 0;
409 #endif
410 element = (ElementTypePtr) ptr2;
411 pcb_sprintf (&report[0], "%m+ELEMENT ID# %ld; Flags:%s\n"
412 "BoundingBox %$mD %$mD.\n"
413 "Descriptive Name \"%s\".\n"
414 "Name on board \"%s\".\n"
415 "Part number name \"%s\".\n"
416 "It is %$mS tall and is located at (X,Y) = %$mD %s.\n"
417 "Mark located at point (X,Y) = %$mD.\n"
418 "It is on the %s side of the board.\n"
419 "%s", USER_UNITMASK,
420 element->ID, flags_to_string (element->Flags, ELEMENT_TYPE),
421 element->BoundingBox.X1, element->BoundingBox.Y1,
422 element->BoundingBox.X2, element->BoundingBox.Y2,
423 EMPTY (element->Name[0].TextString),
424 EMPTY (element->Name[1].TextString),
425 EMPTY (element->Name[2].TextString),
426 (Coord) (0.45 * element->Name[1].Scale * 100),
427 element->Name[1].X, element->Name[1].Y,
428 TEST_FLAG (HIDENAMEFLAG, element) ? ",\n but it's hidden" : "",
429 element->MarkX, element->MarkY,
430 TEST_FLAG (ONSOLDERFLAG, element) ? "solder (bottom)" : "component",
431 TEST_FLAG (LOCKFLAG, element) ? "It is LOCKED.\n" : "");
432 break;
434 case TEXT_TYPE:
435 #ifndef NDEBUG
436 if (gui->shift_is_pressed ())
438 LayerTypePtr layer = (LayerTypePtr) ptr1;
439 __r_dump_tree (layer->text_tree->root, 0);
440 return 0;
442 #endif
443 case ELEMENTNAME_TYPE:
445 char laynum[32];
446 TextTypePtr text;
447 #ifndef NDEBUG
448 if (gui->shift_is_pressed ())
450 __r_dump_tree (PCB->Data->name_tree[NAME_INDEX (PCB)]->root, 0);
451 return 0;
453 #endif
454 text = (TextTypePtr) ptr2;
456 if (type == TEXT_TYPE)
457 sprintf (laynum, "It is on layer %d.",
458 GetLayerNumber (PCB->Data, (LayerTypePtr) ptr1));
459 pcb_sprintf (&report[0], "%m+TEXT ID# %ld; Flags:%s\n"
460 "Located at (X,Y) = %$mD.\n"
461 "Characters are %$mS tall.\n"
462 "Value is \"%s\".\n"
463 "Direction is %d.\n"
464 "The bounding box is %$mD %$mD.\n"
465 "%s\n"
466 "%s", USER_UNITMASK, text->ID, flags_to_string (text->Flags, TEXT_TYPE),
467 text->X, text->Y, (Coord) (0.45 * MIL_TO_COORD (text->Scale)),
468 text->TextString, text->Direction,
469 text->BoundingBox.X1, text->BoundingBox.Y1,
470 text->BoundingBox.X2, text->BoundingBox.Y2,
471 (type == TEXT_TYPE) ? laynum : "It is an element name.",
472 TEST_FLAG (LOCKFLAG, text) ? "It is LOCKED.\n" : "");
473 break;
475 case LINEPOINT_TYPE:
476 case POLYGONPOINT_TYPE:
478 PointTypePtr point = (PointTypePtr) ptr2;
479 pcb_sprintf (&report[0], "%m+POINT ID# %ld.\n"
480 "Located at (X,Y) = %$mD.\n"
481 "It belongs to a %s on layer %d.\n", USER_UNITMASK, point->ID,
482 point->X, point->Y,
483 (type == LINEPOINT_TYPE) ? "line" : "polygon",
484 GetLayerNumber (PCB->Data, (LayerTypePtr) ptr1));
485 break;
487 case NO_TYPE:
488 report[0] = '\0';
489 break;
491 default:
492 sprintf (&report[0], "Unknown\n");
493 break;
496 if (report[0] == '\0')
498 Message (_("Nothing found to report on\n"));
499 return 1;
501 /* create dialog box */
502 gui->report_dialog ("Report", &report[0]);
504 return 0;
507 static int
508 ReportFoundPins (int argc, char **argv, Coord x, Coord y)
510 static DynamicStringType list;
511 char temp[64];
512 int col = 0;
514 DSClearString (&list);
515 DSAddString (&list, "The following pins/pads are FOUND:\n");
516 ELEMENT_LOOP (PCB->Data);
518 PIN_LOOP (element);
520 if (TEST_FLAG (FOUNDFLAG, pin))
522 sprintf (temp, "%s-%s,%c",
523 NAMEONPCB_NAME (element),
524 pin->Number,
525 ((col++ % (COLUMNS + 1)) == COLUMNS) ? '\n' : ' ');
526 DSAddString (&list, temp);
529 END_LOOP;
530 PAD_LOOP (element);
532 if (TEST_FLAG (FOUNDFLAG, pad))
534 sprintf (temp, "%s-%s,%c",
535 NAMEONPCB_NAME (element), pad->Number,
536 ((col++ % (COLUMNS + 1)) == COLUMNS) ? '\n' : ' ');
537 DSAddString (&list, temp);
540 END_LOOP;
542 END_LOOP;
544 gui->report_dialog ("Report", list.Data);
545 return 0;
548 static double
549 XYtoNetLength (Coord x, Coord y, int *found)
551 double length;
553 length = 0;
554 *found = 0;
555 LookupConnection (x, y, true, PCB->Grid, FOUNDFLAG);
557 ALLLINE_LOOP (PCB->Data);
559 if (TEST_FLAG (FOUNDFLAG, line))
561 double l;
562 int dx, dy;
563 dx = line->Point1.X - line->Point2.X;
564 dy = line->Point1.Y - line->Point2.Y;
565 l = sqrt ((double)dx*dx + (double)dy*dy);
566 length += l;
567 *found = 1;
570 ENDALL_LOOP;
572 ALLARC_LOOP (PCB->Data);
574 if (TEST_FLAG (FOUNDFLAG, arc))
576 double l;
577 /* FIXME: we assume width==height here */
578 l = M_PI * 2*arc->Width * abs(arc->Delta)/360.0;
579 length += l;
580 *found = 1;
583 ENDALL_LOOP;
585 return length;
588 static int
589 ReportAllNetLengths (int argc, char **argv, Coord x, Coord y)
591 int ni;
592 int found;
594 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
596 char *netname = PCB->NetlistLib.Menu[ni].Name + 2;
597 char *ename = PCB->NetlistLib.Menu[ni].Entry[0].ListEntry;
598 char *pname;
599 bool got_one = 0;
601 ename = strdup (ename);
602 pname = strchr (ename, '-');
603 if (! pname)
605 free (ename);
606 continue;
608 *pname++ = 0;
610 ELEMENT_LOOP (PCB->Data);
612 char *es = element->Name[NAMEONPCB_INDEX].TextString;
613 if (es && strcmp (es, ename) == 0)
615 PIN_LOOP (element);
617 if (strcmp (pin->Number, pname) == 0)
619 x = pin->X;
620 y = pin->Y;
621 got_one = 1;
622 break;
625 END_LOOP;
626 PAD_LOOP (element);
628 if (strcmp (pad->Number, pname) == 0)
630 x = (pad->Point1.X + pad->Point2.X) / 2;
631 y = (pad->Point1.Y + pad->Point2.Y) / 2;
632 got_one = 1;
633 break;
636 END_LOOP;
639 END_LOOP;
641 if (got_one)
643 char buf[50];
644 const char *units_name = argv[0];
645 Coord length;
647 if (argc < 1)
648 units_name = Settings.grid_unit->suffix;
650 if (ResetConnections (true))
651 Draw ();
652 /* NB: XYtoNetLength calls LookupConnection, which performs an undo
653 * serial number update, so we don't need to add one here.
655 length = XYtoNetLength (x, y, &found);
657 pcb_sprintf(buf, "%$m*", units_name, length);
658 gui->log("Net %s length %s\n", netname, buf);
661 return 0;
664 static int
665 ReportNetLength (int argc, char **argv, Coord x, Coord y)
667 Coord length = 0;
668 char *netname = 0;
669 int found = 0;
671 if (ResetConnections (true))
672 Draw ();
673 /* NB: XYtoNetLength calls LookupConnection, which performs an undo
674 * serial number update, so we don't need to add one here.
676 gui->get_coords ("Click on a connection", &x, &y);
678 length = XYtoNetLength (x, y, &found);
680 if (!found)
682 gui->log ("No net under cursor.\n");
683 return 1;
686 ELEMENT_LOOP (PCB->Data);
688 PIN_LOOP (element);
690 if (TEST_FLAG (FOUNDFLAG, pin))
692 int ni, nei;
693 char *ename = element->Name[NAMEONPCB_INDEX].TextString;
694 char *pname = pin->Number;
695 char *n;
697 if (ename && pname)
699 n = Concat (ename, "-", pname, NULL);
700 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
701 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
703 if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
705 netname = PCB->NetlistLib.Menu[ni].Name + 2;
706 goto got_net_name; /* four for loops deep */
712 END_LOOP;
713 PAD_LOOP (element);
715 if (TEST_FLAG (FOUNDFLAG, pad))
717 int ni, nei;
718 char *ename = element->Name[NAMEONPCB_INDEX].TextString;
719 char *pname = pad->Number;
720 char *n;
722 if (ename && pname)
724 n = Concat (ename, "-", pname, NULL);
725 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
726 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
728 if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
730 netname = PCB->NetlistLib.Menu[ni].Name + 2;
731 goto got_net_name; /* four for loops deep */
737 END_LOOP;
739 END_LOOP;
740 got_net_name:
743 char buf[50];
744 pcb_sprintf(buf, "%$m*", Settings.grid_unit->suffix, length);
745 if (netname)
746 gui->log ("Net \"%s\" length: %s\n", netname, buf);
747 else
748 gui->log ("Net length: %s\n", buf);
750 return 0;
753 static int
754 ReportNetLengthByName (char *tofind, int x, int y)
756 int result;
757 char *netname = 0;
758 Coord length = 0;
759 int found = 0;
760 int i;
761 LibraryMenuType *net;
762 ConnectionType conn;
763 int net_found = 0;
764 #if defined(USE_RE)
765 int use_re = 0;
766 #endif
767 #if defined(HAVE_REGCOMP)
768 regex_t elt_pattern;
769 regmatch_t match;
770 #endif
771 #if defined(HAVE_RE_COMP)
772 char *elt_pattern;
773 #endif
775 if (!PCB)
776 return 1;
778 if (!tofind)
779 return 1;
781 SaveUndoSerialNumber ();
782 ResetFoundPinsViasAndPads (true);
783 RestoreUndoSerialNumber ();
784 ResetFoundLinesAndPolygons (true);
785 RestoreUndoSerialNumber ();
787 #if defined(USE_RE)
788 use_re = 1;
789 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
791 net = PCB->NetlistLib.Menu + i;
792 if (strcasecmp (tofind, net->Name + 2) == 0)
793 use_re = 0;
795 if (use_re)
797 #if defined(HAVE_REGCOMP)
798 result =
799 regcomp (&elt_pattern, tofind,
800 REG_EXTENDED | REG_ICASE | REG_NOSUB);
801 if (result)
803 char errorstring[128];
805 regerror (result, &elt_pattern, errorstring, 128);
806 Message (_("regexp error: %s\n"), errorstring);
807 regfree (&elt_pattern);
808 return (1);
810 #endif
811 #if defined(HAVE_RE_COMP)
812 if ((elt_pattern = re_comp (tofind)) != NULL)
814 Message (_("re_comp error: %s\n"), elt_pattern);
815 return (false);
817 #endif
819 #endif
821 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
823 net = PCB->NetlistLib.Menu + i;
825 #if defined(USE_RE)
826 if (use_re)
828 #if defined(HAVE_REGCOMP)
829 if (regexec (&elt_pattern, net->Name + 2, 1, &match, 0) != 0)
830 continue;
831 #endif
832 #if defined(HAVE_RE_COMP)
833 if (re_exec (net->Name + 2) != 1)
834 continue;
835 #endif
837 else
838 #endif
839 if (strcasecmp (net->Name + 2, tofind))
840 continue;
842 if (SeekPad (net->Entry, &conn, false))
844 switch (conn.type)
846 case PIN_TYPE:
847 x = ((PinType *) (conn.ptr2))->X;
848 y = ((PinType *) (conn.ptr2))->Y;
849 net_found=1;
850 break;
851 case PAD_TYPE:
852 x = ((PadType *) (conn.ptr2))->Point1.X;
853 y = ((PadType *) (conn.ptr2))->Point1.Y;
854 net_found=1;
855 break;
857 if (net_found)
858 break;
862 if (!net_found)
864 gui->log ("No net named %s\n", tofind);
865 return 1;
867 #ifdef HAVE_REGCOMP
868 if (use_re)
869 regfree (&elt_pattern);
870 #endif
872 length = XYtoNetLength (x, y, &found);
873 netname = net->Name + 2;
875 if (!found && net_found)
877 gui->log ("Net found, but no lines or arcs were flagged.\n");
878 return 1;
880 else if (!found)
882 gui->log ("Net not found.\n");
883 return 1;
887 char buf[50];
888 pcb_sprintf(buf, "%$m*", Settings.grid_unit->suffix, length);
889 if (netname)
890 gui->log ("Net \"%s\" length: %s\n", netname, buf);
891 else
892 gui->log ("Net length: %s\n", buf);
894 return 0;
897 /* ---------------------------------------------------------------------------
898 * reports on an object
899 * syntax:
902 static const char report_syntax[] = "Report(Object|DrillReport|FoundPins|NetLength|AllNetLengths|{,name})";
904 static const char report_help[] = "Produce various report.";
906 /* %start-doc actions Report
908 @table @code
910 @item Object
911 The object under the crosshair will be reported, describing various
912 aspects of the object.
914 @item DrillReport
915 A report summarizing the number of drill sizes used, and how many of
916 each, will be produced.
918 @item FoundPins
919 A report listing all pins and pads which are marked as ``found'' will
920 be produced.
922 @item NetLength
923 The name and length of the net under the crosshair will be reported to
924 the message log.
926 @item AllNetLengths
927 The name and length of the net under the crosshair will be reported to
928 the message log. An optional parameter specifies mm, mil, pcb, or in
929 units
931 @end table
933 %end-doc */
935 static int
936 Report (int argc, char **argv, Coord x, Coord y)
938 if ((argc < 1) || (argc > 2))
939 AUSAGE (report);
940 else if (strcasecmp (argv[0], "Object") == 0)
942 gui->get_coords ("Click on an object", &x, &y);
943 return ReportDialog (argc - 1, argv + 1, x, y);
945 else if (strcasecmp (argv[0], "DrillReport") == 0)
946 return ReportDrills (argc - 1, argv + 1, x, y);
947 else if (strcasecmp (argv[0], "FoundPins") == 0)
948 return ReportFoundPins (argc - 1, argv + 1, x, y);
949 else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 1))
950 return ReportNetLength (argc - 1, argv + 1, x, y);
951 else if (strcasecmp (argv[0], "AllNetLengths") == 0)
952 return ReportAllNetLengths (argc - 1, argv + 1, x, y);
953 else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 2))
954 return ReportNetLengthByName (argv[1], x, y);
955 else if (argc == 2)
956 AUSAGE (report);
957 else
958 AFAIL (report);
959 return 1;
962 HID_Action report_action_list[] = {
963 {"ReportObject", "Click on an object", ReportDialog,
964 reportdialog_help, reportdialog_syntax}
966 {"Report", 0, Report,
967 report_help, report_syntax}
970 REGISTER_ACTIONS (report_action_list)