Cleanup r_string when leaving make_route_string().
[geda-pcb/pcjc2.git] / src / report.c
blobf3c51d771d8e790311734413669807070cb76acd
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 DrillInfoType *AllDrills;
74 Cardinal n;
75 char *stringlist, *thestring;
76 int total_drills = 0;
77 size_t size_left;
79 AllDrills = GetDrillInfo (PCB->Data);
81 for (n = 0; n < AllDrills->DrillN; n++)
83 total_drills += AllDrills->Drill[n].PinCount;
84 total_drills += AllDrills->Drill[n].ViaCount;
87 size_left = 512L + AllDrills->DrillN * 64L;
88 stringlist = (char *)malloc (size_left);
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);
98 thestring = stringlist;
99 while (size_left > 0 && *thestring != '\0')
101 thestring++;
102 size_left--;
105 for (n = 0; n < AllDrills->DrillN; n++)
107 pcb_snprintf (thestring, size_left,
108 "%10m*\t\t%d\t\t%d\t\t%d\t\t%d\n",
109 Settings.grid_unit->suffix,
110 AllDrills->Drill[n].DrillSize,
111 AllDrills->Drill[n].PinCount, AllDrills->Drill[n].ViaCount,
112 AllDrills->Drill[n].ElementN,
113 AllDrills->Drill[n].UnplatedCount);
114 while (size_left > 0 && *thestring != '\0')
116 thestring++;
117 size_left--;
120 FreeDrillInfo (AllDrills);
121 /* create dialog box */
122 gui->report_dialog (_("Drill Report"), stringlist);
124 free (stringlist);
125 return 0;
129 static const char reportdialog_syntax[] = N_("ReportDialog()");
131 static const char reportdialog_help[] =
132 N_("Report on the object under the crosshair");
134 /* %start-doc actions ReportDialog
136 This is a shortcut for @code{Report(Object)}.
138 %end-doc */
140 static int
141 ReportDialog (int argc, char **argv, Coord x, Coord y)
143 void *ptr1, *ptr2, *ptr3;
144 int type;
145 char report[2048];
147 type = SearchScreen (x, y, REPORT_TYPES, &ptr1, &ptr2, &ptr3);
148 if (type == NO_TYPE)
149 type =
150 SearchScreen (x, y, REPORT_TYPES | LOCKED_TYPE, &ptr1, &ptr2, &ptr3);
152 switch (type)
154 case VIA_TYPE:
156 PinType *via;
157 #ifndef NDEBUG
158 if (gui->shift_is_pressed ())
160 __r_dump_tree (PCB->Data->via_tree->root, 0);
161 return 0;
163 #endif
164 via = (PinType *) ptr2;
165 if (TEST_FLAG (HOLEFLAG, via))
166 pcb_snprintf (report, sizeof (report), _("%m+VIA ID# %ld; Flags:%s\n"
167 "(X,Y) = %$mD.\n"
168 "It is a pure hole of diameter %$mS.\n"
169 "Name = \"%s\"."
170 "%s"), USER_UNITMASK, via->ID, flags_to_string (via->Flags, VIA_TYPE),
171 via->X, via->Y, via->DrillingHole, EMPTY (via->Name),
172 TEST_FLAG (LOCKFLAG, via) ? _("It is LOCKED.\n") : "");
173 else
174 pcb_snprintf (report, sizeof (report), _("%m+VIA ID# %ld; Flags:%s\n"
175 "(X,Y) = %$mD.\n"
176 "Copper width = %$mS. Drill width = %$mS.\n"
177 "Clearance width in polygons = %$mS.\n"
178 "Annulus = %$mS.\n"
179 "Solder mask hole = %$mS (gap = %$mS).\n"
180 "Name = \"%s\"."
181 "%s"), USER_UNITMASK, via->ID, flags_to_string (via->Flags, VIA_TYPE),
182 via->X, via->Y,
183 via->Thickness,
184 via->DrillingHole,
185 via->Clearance / 2,
186 (via->Thickness - via->DrillingHole) / 2,
187 via->Mask,
188 (via->Mask - via->Thickness) / 2,
189 EMPTY (via->Name), TEST_FLAG (LOCKFLAG, via) ?
190 _("It is LOCKED.\n") : "");
191 break;
193 case PIN_TYPE:
195 PinType *Pin;
196 ElementType *element;
197 #ifndef NDEBUG
198 if (gui->shift_is_pressed ())
200 __r_dump_tree (PCB->Data->pin_tree->root, 0);
201 return 0;
203 #endif
204 Pin = (PinType *) ptr2;
205 element = (ElementType *) ptr1;
207 PIN_LOOP (element);
209 if (pin == Pin)
210 break;
212 END_LOOP;
213 if (TEST_FLAG (HOLEFLAG, Pin))
214 pcb_snprintf (report, sizeof (report), _("%m+PIN ID# %ld; Flags:%s\n"
215 "(X,Y) = %$mD.\n"
216 "It is a mounting hole. Drill width = %$mS.\n"
217 "It is owned by element %$mS.\n"
218 "%s"), USER_UNITMASK, Pin->ID, flags_to_string (Pin->Flags, PIN_TYPE),
219 Pin->X, Pin->Y, Pin->DrillingHole,
220 EMPTY (element->Name[1].TextString),
221 TEST_FLAG (LOCKFLAG, Pin) ? _("It is LOCKED.\n") : "");
222 else
223 pcb_snprintf (report, sizeof (report),
224 _("%m+PIN ID# %ld; Flags:%s\n" "(X,Y) = %$mD.\n"
225 "Copper width = %$mS. Drill width = %$mS.\n"
226 "Clearance width to Polygon = %$mS.\n"
227 "Annulus = %$mS.\n"
228 "Solder mask hole = %$mS (gap = %$mS).\n"
229 "Name = \"%s\".\n"
230 "It is owned by element %s\n as pin number %s.\n"
231 "%s"), USER_UNITMASK,
232 Pin->ID, flags_to_string (Pin->Flags, PIN_TYPE),
233 Pin->X, Pin->Y, Pin->Thickness,
234 Pin->DrillingHole,
235 Pin->Clearance / 2,
236 (Pin->Thickness - Pin->DrillingHole) / 2,
237 Pin->Mask,
238 (Pin->Mask - Pin->Thickness) / 2,
239 EMPTY (Pin->Name),
240 EMPTY (element->Name[1].TextString), EMPTY (Pin->Number),
241 TEST_FLAG (LOCKFLAG, Pin) ? _("It is LOCKED.\n") : "");
242 break;
244 case LINE_TYPE:
246 LineType *line;
247 #ifndef NDEBUG
248 if (gui->shift_is_pressed ())
250 LayerType *layer = (LayerType *) ptr1;
251 __r_dump_tree (layer->line_tree->root, 0);
252 return 0;
254 #endif
255 line = (LineType *) ptr2;
256 pcb_snprintf (report, sizeof (report), _("%m+LINE ID# %ld; Flags:%s\n"
257 "FirstPoint(X,Y) = %$mD, ID = %ld.\n"
258 "SecondPoint(X,Y) = %$mD, ID = %ld.\n"
259 "Width = %$mS.\nClearance width in polygons = %$mS.\n"
260 "It is on layer %d\n"
261 "and has name \"%s\".\n"
262 "%s"), USER_UNITMASK,
263 line->ID, flags_to_string (line->Flags, LINE_TYPE),
264 line->Point1.X, line->Point1.Y, line->Point1.ID,
265 line->Point2.X, line->Point2.Y, line->Point2.ID,
266 line->Thickness, line->Clearance / 2,
267 GetLayerNumber (PCB->Data, (LayerType *) ptr1),
268 UNKNOWN (line->Number),
269 TEST_FLAG (LOCKFLAG, line) ? _("It is LOCKED.\n") : "");
270 break;
272 case RATLINE_TYPE:
274 RatType *line;
275 #ifndef NDEBUG
276 if (gui->shift_is_pressed ())
278 __r_dump_tree (PCB->Data->rat_tree->root, 0);
279 return 0;
281 #endif
282 line = (RatType *) ptr2;
283 pcb_snprintf (report, sizeof (report), _("%m+RAT-LINE ID# %ld; Flags:%s\n"
284 "FirstPoint(X,Y) = %$mD; ID = %ld; "
285 "connects to layer group %d.\n"
286 "SecondPoint(X,Y) = %$mD; ID = %ld; "
287 "connects to layer group %d.\n"),
288 USER_UNITMASK, line->ID, flags_to_string (line->Flags, LINE_TYPE),
289 line->Point1.X, line->Point1.Y,
290 line->Point1.ID, line->group1,
291 line->Point2.X, line->Point2.Y,
292 line->Point2.ID, line->group2);
293 break;
295 case ARC_TYPE:
297 ArcType *Arc;
298 BoxType *box;
299 #ifndef NDEBUG
300 if (gui->shift_is_pressed ())
302 LayerType *layer = (LayerType *) ptr1;
303 __r_dump_tree (layer->arc_tree->root, 0);
304 return 0;
306 #endif
307 Arc = (ArcType *) ptr2;
308 box = GetArcEnds (Arc);
310 pcb_snprintf (report, sizeof (report), _("%m+ARC ID# %ld; Flags:%s\n"
311 "CenterPoint(X,Y) = %$mD.\n"
312 "Radius = %$mS, Thickness = %$mS.\n"
313 "Clearance width in polygons = %$mS.\n"
314 "StartAngle = %ma degrees, DeltaAngle = %ma degrees.\n"
315 "Bounding Box is %$mD, %$mD.\n"
316 "That makes the end points at %$mD and %$mD.\n"
317 "It is on layer %d.\n"
318 "%s"), USER_UNITMASK, Arc->ID, flags_to_string (Arc->Flags, ARC_TYPE),
319 Arc->X, Arc->Y,
320 Arc->Width, Arc->Thickness,
321 Arc->Clearance / 2, Arc->StartAngle, Arc->Delta,
322 Arc->BoundingBox.X1, Arc->BoundingBox.Y1,
323 Arc->BoundingBox.X2, Arc->BoundingBox.Y2,
324 box->X1, box->Y1,
325 box->X2, box->Y2,
326 GetLayerNumber (PCB->Data, (LayerType *) ptr1),
327 TEST_FLAG (LOCKFLAG, Arc) ? _("It is LOCKED.\n") : "");
328 break;
330 case POLYGON_TYPE:
332 PolygonType *Polygon;
333 #ifndef NDEBUG
334 if (gui->shift_is_pressed ())
336 LayerType *layer = (LayerType *) ptr1;
337 __r_dump_tree (layer->polygon_tree->root, 0);
338 return 0;
340 #endif
341 Polygon = (PolygonType *) ptr2;
343 pcb_snprintf (report, sizeof (report), _("%m+POLYGON ID# %ld; Flags:%s\n"
344 "Its bounding box is %$mD %$mD.\n"
345 "It has %d points and could store %d more\n"
346 " without using more memory.\n"
347 "It has %d holes and resides on layer %d.\n"
348 "%s"), USER_UNITMASK, Polygon->ID,
349 flags_to_string (Polygon->Flags, POLYGON_TYPE),
350 Polygon->BoundingBox.X1, Polygon->BoundingBox.Y1,
351 Polygon->BoundingBox.X2, Polygon->BoundingBox.Y2,
352 Polygon->PointN, Polygon->PointMax - Polygon->PointN,
353 Polygon->HoleIndexN,
354 GetLayerNumber (PCB->Data, (LayerType *) ptr1),
355 TEST_FLAG (LOCKFLAG, Polygon) ? _("It is LOCKED.\n") : "");
356 break;
358 case PAD_TYPE:
360 Coord len;
361 PadType *Pad;
362 ElementType *element;
363 #ifndef NDEBUG
364 if (gui->shift_is_pressed ())
366 __r_dump_tree (PCB->Data->pad_tree->root, 0);
367 return 0;
369 #endif
370 Pad = (PadType *) ptr2;
371 element = (ElementType *) ptr1;
373 PAD_LOOP (element);
376 if (pad == Pad)
377 break;
380 END_LOOP;
381 len = Distance (Pad->Point1.X, Pad->Point1.Y, Pad->Point2.X, Pad->Point2.Y);
382 pcb_snprintf (report, sizeof (report), _("%m+PAD ID# %ld; Flags:%s\n"
383 "FirstPoint(X,Y) = %$mD; ID = %ld.\n"
384 "SecondPoint(X,Y) = %$mD; ID = %ld.\n"
385 "Width = %$mS. Length = %$mS.\n"
386 "Clearance width in polygons = %$mS.\n"
387 "Solder mask = %$mS x %$mS (gap = %$mS).\n"
388 "Name = \"%s\".\n"
389 "It is owned by SMD element %s\n"
390 " as pin number %s and is on the %s\n"
391 "side of the board.\n"
392 "%s"), USER_UNITMASK, Pad->ID,
393 flags_to_string (Pad->Flags, PAD_TYPE),
394 Pad->Point1.X, Pad->Point1.Y, Pad->Point1.ID,
395 Pad->Point2.X, Pad->Point2.Y, Pad->Point2.ID,
396 Pad->Thickness, len + Pad->Thickness,
397 Pad->Clearance / 2,
398 Pad->Mask, len + Pad->Mask,
399 (Pad->Mask - Pad->Thickness) / 2,
400 EMPTY (Pad->Name),
401 EMPTY (element->Name[1].TextString),
402 EMPTY (Pad->Number),
403 TEST_FLAG (ONSOLDERFLAG,
404 Pad) ? _("solder (bottom)") : _("component"),
405 TEST_FLAG (LOCKFLAG, Pad) ? _("It is LOCKED.\n") : "");
406 break;
408 case ELEMENT_TYPE:
410 ElementType *element;
411 #ifndef NDEBUG
412 if (gui->shift_is_pressed ())
414 __r_dump_tree (PCB->Data->element_tree->root, 0);
415 return 0;
417 #endif
418 element = (ElementType *) ptr2;
419 pcb_snprintf (report, sizeof (report), _("%m+ELEMENT ID# %ld; Flags:%s\n"
420 "BoundingBox %$mD %$mD.\n"
421 "Descriptive Name \"%s\".\n"
422 "Name on board \"%s\".\n"
423 "Part number name \"%s\".\n"
424 "It is %$mS tall and is located at (X,Y) = %$mD %s.\n"
425 "Mark located at point (X,Y) = %$mD.\n"
426 "It is on the %s side of the board.\n"
427 "%s"), USER_UNITMASK,
428 element->ID, flags_to_string (element->Flags, ELEMENT_TYPE),
429 element->BoundingBox.X1, element->BoundingBox.Y1,
430 element->BoundingBox.X2, element->BoundingBox.Y2,
431 EMPTY (element->Name[0].TextString),
432 EMPTY (element->Name[1].TextString),
433 EMPTY (element->Name[2].TextString),
434 SCALE_TEXT (FONT_CAPHEIGHT, element->Name[1].Scale),
435 element->Name[1].X, element->Name[1].Y,
436 TEST_FLAG (HIDENAMEFLAG, element) ? _(",\n but it's hidden") : "",
437 element->MarkX, element->MarkY,
438 TEST_FLAG (ONSOLDERFLAG, element) ? _("solder (bottom)") : _("component"),
439 TEST_FLAG (LOCKFLAG, element) ? _("It is LOCKED.\n") : "");
440 break;
442 case TEXT_TYPE:
443 #ifndef NDEBUG
444 if (gui->shift_is_pressed ())
446 LayerType *layer = (LayerType *) ptr1;
447 __r_dump_tree (layer->text_tree->root, 0);
448 return 0;
450 #endif
451 case ELEMENTNAME_TYPE:
453 char laynum[32];
454 TextType *text;
455 #ifndef NDEBUG
456 if (gui->shift_is_pressed ())
458 __r_dump_tree (PCB->Data->name_tree[NAME_INDEX (PCB)]->root, 0);
459 return 0;
461 #endif
462 text = (TextType *) ptr2;
464 if (type == TEXT_TYPE)
465 sprintf (laynum, _("It is on layer %d."),
466 GetLayerNumber (PCB->Data, (LayerType *) ptr1));
467 pcb_snprintf (report, sizeof (report), _("%m+TEXT ID# %ld; Flags:%s\n"
468 "Located at (X,Y) = %$mD.\n"
469 "Characters are %$mS tall.\n"
470 "Value is \"%s\".\n"
471 "Direction is %d.\n"
472 "The bounding box is %$mD %$mD.\n"
473 "%s\n"
474 "%s"), USER_UNITMASK, text->ID, flags_to_string (text->Flags, TEXT_TYPE),
475 text->X, text->Y, SCALE_TEXT (FONT_CAPHEIGHT, text->Scale),
476 text->TextString, text->Direction,
477 text->BoundingBox.X1, text->BoundingBox.Y1,
478 text->BoundingBox.X2, text->BoundingBox.Y2,
479 (type == TEXT_TYPE) ? laynum : _("It is an element name."),
480 TEST_FLAG (LOCKFLAG, text) ? _("It is LOCKED.\n") : "");
481 break;
483 case LINEPOINT_TYPE:
484 case POLYGONPOINT_TYPE:
486 PointType *point = (PointType *) ptr2;
487 pcb_snprintf (report, sizeof (report), _("%m+POINT ID# %ld.\n"
488 "Located at (X,Y) = %$mD.\n"
489 "It belongs to a %s on layer %d.\n"), USER_UNITMASK, point->ID,
490 point->X, point->Y,
491 (type == LINEPOINT_TYPE) ?
492 C_("report", "line") : C_("report", "polygon"),
493 GetLayerNumber (PCB->Data, (LayerType *) ptr1));
494 break;
496 case NO_TYPE:
497 report[0] = '\0';
498 break;
500 default:
501 sprintf (report, _("Unknown\n"));
502 break;
505 if (report[0] == '\0')
507 Message (_("Nothing found to report on\n"));
508 return 1;
510 /* create dialog box */
511 gui->report_dialog (_("Report"), report);
513 return 0;
516 static int
517 ReportFoundPins (int argc, char **argv, Coord x, Coord y)
519 static DynamicStringType list;
520 char temp[64];
521 int col = 0;
523 DSClearString (&list);
524 DSAddString (&list, _("The following pins/pads are FOUND:\n"));
525 ELEMENT_LOOP (PCB->Data);
527 PIN_LOOP (element);
529 if (TEST_FLAG (FOUNDFLAG, pin))
531 sprintf (temp, _("%s-%s,%c"),
532 NAMEONPCB_NAME (element),
533 pin->Number,
534 ((col++ % (COLUMNS + 1)) == COLUMNS) ? '\n' : ' ');
535 DSAddString (&list, temp);
538 END_LOOP;
539 PAD_LOOP (element);
541 if (TEST_FLAG (FOUNDFLAG, pad))
543 sprintf (temp, _("%s-%s,%c"),
544 NAMEONPCB_NAME (element), pad->Number,
545 ((col++ % (COLUMNS + 1)) == COLUMNS) ? '\n' : ' ');
546 DSAddString (&list, temp);
549 END_LOOP;
551 END_LOOP;
553 gui->report_dialog (_("Report"), list.Data);
554 return 0;
557 /* Assumes that we start with a blank connection state,
558 * e.g. ClearFlagOnAllObjects() has been run.
559 * Does not add its own changes to the undo system
561 static double
562 XYtoNetLength (Coord x, Coord y, int *found)
564 double length;
566 length = 0;
567 *found = 0;
569 /* NB: The third argument here, 'false' ensures LookupConnection
570 * does not add its changes to the undo system.
572 LookupConnection (x, y, false, PCB->Grid, FOUNDFLAG, true);
574 ALLLINE_LOOP (PCB->Data);
576 if (TEST_FLAG (FOUNDFLAG, line))
578 int dx, dy;
579 dx = line->Point1.X - line->Point2.X;
580 dy = line->Point1.Y - line->Point2.Y;
581 length += hypot (dx, dy);
582 *found = 1;
585 ENDALL_LOOP;
587 ALLARC_LOOP (PCB->Data);
589 if (TEST_FLAG (FOUNDFLAG, arc))
591 double l;
592 /* FIXME: we assume width==height here */
593 l = M_PI * 2*arc->Width * abs(arc->Delta)/360.0;
594 length += l;
595 *found = 1;
598 ENDALL_LOOP;
600 return length;
603 static int
604 ReportAllNetLengths (int argc, char **argv, Coord x, Coord y)
606 int ni;
607 int found;
609 /* Reset all connection flags and save an undo-state to get back
610 * to the state the board was in when we started this function.
612 * After this, we don't add any changes to the undo system, but
613 * ensure we get back to a point where we can Undo() our changes
614 * by resetting the connections with ClearFlagOnAllObjects() before
615 * calling Undo() at the end of the procedure.
617 ClearFlagOnAllObjects (true, FOUNDFLAG);
618 IncrementUndoSerialNumber ();
620 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
622 char *netname = PCB->NetlistLib.Menu[ni].Name + 2;
623 char *ename = PCB->NetlistLib.Menu[ni].Entry[0].ListEntry;
624 char *pname;
625 bool got_one = 0;
627 ename = strdup (ename);
628 pname = strchr (ename, '-');
629 if (! pname)
631 free (ename);
632 continue;
634 *pname++ = 0;
636 ELEMENT_LOOP (PCB->Data);
638 char *es = element->Name[NAMEONPCB_INDEX].TextString;
639 if (es && strcmp (es, ename) == 0)
641 PIN_LOOP (element);
643 if (strcmp (pin->Number, pname) == 0)
645 x = pin->X;
646 y = pin->Y;
647 got_one = 1;
648 break;
651 END_LOOP;
652 PAD_LOOP (element);
654 if (strcmp (pad->Number, pname) == 0)
656 x = (pad->Point1.X + pad->Point2.X) / 2;
657 y = (pad->Point1.Y + pad->Point2.Y) / 2;
658 got_one = 1;
659 break;
662 END_LOOP;
665 END_LOOP;
667 if (got_one)
669 char buf[50];
670 const char *units_name = argv[0];
671 Coord length;
673 if (argc < 1)
674 units_name = Settings.grid_unit->suffix;
676 length = XYtoNetLength (x, y, &found);
678 /* Reset connectors for the next lookup */
679 ClearFlagOnAllObjects (false, FOUNDFLAG);
681 pcb_snprintf(buf, sizeof (buf), _("%$m*"), units_name, length);
682 gui->log(_("Net %s length %s\n"), netname, buf);
686 ClearFlagOnAllObjects (false, FOUNDFLAG);
687 Undo (true);
688 return 0;
691 static int
692 ReportNetLength (int argc, char **argv, Coord x, Coord y)
694 Coord length = 0;
695 char *netname = 0;
696 int found = 0;
698 gui->get_coords (_("Click on a connection"), &x, &y);
700 /* Reset all connection flags and save an undo-state to get back
701 * to the state the board was in when we started this function.
703 * After this, we don't add any changes to the undo system, but
704 * ensure we get back to a point where we can Undo() our changes
705 * by resetting the connections with ClearFlagOnAllObjects() before
706 * calling Undo() at the end of the procedure.
708 ClearFlagOnAllObjects (true, FOUNDFLAG);
709 IncrementUndoSerialNumber ();
711 length = XYtoNetLength (x, y, &found);
713 if (!found)
715 ClearFlagOnAllObjects (false, FOUNDFLAG);
716 Undo (true);
717 gui->log (_("No net under cursor.\n"));
718 return 1;
721 ELEMENT_LOOP (PCB->Data);
723 PIN_LOOP (element);
725 if (TEST_FLAG (FOUNDFLAG, pin))
727 int ni, nei;
728 char *ename = element->Name[NAMEONPCB_INDEX].TextString;
729 char *pname = pin->Number;
730 char *n;
732 if (ename && pname)
734 n = Concat (ename, _("-"), pname, NULL);
735 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
736 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
738 if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
740 netname = PCB->NetlistLib.Menu[ni].Name + 2;
741 goto got_net_name; /* four for loops deep */
747 END_LOOP;
748 PAD_LOOP (element);
750 if (TEST_FLAG (FOUNDFLAG, pad))
752 int ni, nei;
753 char *ename = element->Name[NAMEONPCB_INDEX].TextString;
754 char *pname = pad->Number;
755 char *n;
757 if (ename && pname)
759 n = Concat (ename, _("-"), pname, NULL);
760 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
761 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
763 if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
765 netname = PCB->NetlistLib.Menu[ni].Name + 2;
766 goto got_net_name; /* four for loops deep */
772 END_LOOP;
774 END_LOOP;
776 got_net_name:
777 ClearFlagOnAllObjects (false, FOUNDFLAG);
778 Undo (true);
781 char buf[50];
782 pcb_snprintf(buf, sizeof (buf), _("%$m*"), Settings.grid_unit->suffix, length);
783 if (netname)
784 gui->log (_("Net \"%s\" length: %s\n"), netname, buf);
785 else
786 gui->log (_("Net length: %s\n"), buf);
789 return 0;
792 static int
793 ReportNetLengthByName (char *tofind, int x, int y)
795 int result;
796 char *netname = 0;
797 Coord length = 0;
798 int found = 0;
799 int i;
800 LibraryMenuType *net;
801 ConnectionType conn;
802 int net_found = 0;
803 #if defined(USE_RE)
804 int use_re = 0;
805 #endif
806 #if defined(HAVE_REGCOMP)
807 regex_t elt_pattern;
808 regmatch_t match;
809 #endif
810 #if defined(HAVE_RE_COMP)
811 char *elt_pattern;
812 #endif
814 if (!PCB)
815 return 1;
817 if (!tofind)
818 return 1;
820 #if defined(USE_RE)
821 use_re = 1;
822 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
824 net = PCB->NetlistLib.Menu + i;
825 if (strcasecmp (tofind, net->Name + 2) == 0)
826 use_re = 0;
828 if (use_re)
830 #if defined(HAVE_REGCOMP)
831 result =
832 regcomp (&elt_pattern, tofind,
833 REG_EXTENDED | REG_ICASE | REG_NOSUB);
834 if (result)
836 char errorstring[128];
838 regerror (result, &elt_pattern, errorstring, 128);
839 Message (_("regexp error: %s\n"), errorstring);
840 regfree (&elt_pattern);
841 return (1);
843 #endif
844 #if defined(HAVE_RE_COMP)
845 if ((elt_pattern = re_comp (tofind)) != NULL)
847 Message (_("re_comp error: %s\n"), elt_pattern);
848 return (1);
850 #endif
852 #endif
854 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
856 net = PCB->NetlistLib.Menu + i;
858 #if defined(USE_RE)
859 if (use_re)
861 #if defined(HAVE_REGCOMP)
862 if (regexec (&elt_pattern, net->Name + 2, 1, &match, 0) != 0)
863 continue;
864 #endif
865 #if defined(HAVE_RE_COMP)
866 if (re_exec (net->Name + 2) != 1)
867 continue;
868 #endif
870 else
871 #endif
872 if (strcasecmp (net->Name + 2, tofind))
873 continue;
875 if (SeekPad (net->Entry, &conn, false))
877 switch (conn.type)
879 case PIN_TYPE:
880 x = ((PinType *) (conn.ptr2))->X;
881 y = ((PinType *) (conn.ptr2))->Y;
882 net_found=1;
883 break;
884 case PAD_TYPE:
885 x = ((PadType *) (conn.ptr2))->Point1.X;
886 y = ((PadType *) (conn.ptr2))->Point1.Y;
887 net_found=1;
888 break;
890 if (net_found)
891 break;
895 if (!net_found)
897 gui->log (_("No net named %s\n"), tofind);
898 return 1;
901 #ifdef HAVE_REGCOMP
902 if (use_re)
903 regfree (&elt_pattern);
904 #endif
906 /* Reset all connection flags and save an undo-state to get back
907 * to the state the board was in when we started.
909 * After this, we don't add any changes to the undo system, but
910 * ensure we get back to a point where we can Undo() our changes
911 * by resetting the connections with ClearFlagOnAllObjects() before
912 * calling Undo() when we are finished.
914 ClearFlagOnAllObjects (true, FOUNDFLAG);
915 IncrementUndoSerialNumber ();
917 length = XYtoNetLength (x, y, &found);
918 netname = net->Name + 2;
920 ClearFlagOnAllObjects (false, FOUNDFLAG);
921 Undo (true);
923 if (!found)
925 if (net_found)
926 gui->log (_("Net found, but no lines or arcs were flagged.\n"));
927 else
928 gui->log (_("Net not found.\n"));
930 return 1;
934 char buf[50];
935 pcb_snprintf(buf, 50, _("%$m*"), Settings.grid_unit->suffix, length);
936 if (netname)
937 gui->log (_("Net \"%s\" length: %s\n"), netname, buf);
938 else
939 gui->log (_("Net length: %s\n"), buf);
942 return 0;
945 /* ---------------------------------------------------------------------------
946 * reports on an object
947 * syntax:
950 static const char report_syntax[] =
951 N_("Report(Object|DrillReport|FoundPins|NetLength|AllNetLengths|[,name])");
953 static const char report_help[] = N_("Produce various report.");
955 /* %start-doc actions Report
957 @table @code
959 @item Object
960 The object under the crosshair will be reported, describing various
961 aspects of the object.
963 @item DrillReport
964 A report summarizing the number of drill sizes used, and how many of
965 each, will be produced.
967 @item FoundPins
968 A report listing all pins and pads which are marked as ``found'' will
969 be produced.
971 @item NetLength
972 The name and length of the net under the crosshair will be reported to
973 the message log.
975 @item AllNetLengths
976 The name and length of the net under the crosshair will be reported to
977 the message log. An optional parameter specifies mm, mil, pcb, or in
978 units
980 @end table
982 %end-doc */
984 static int
985 Report (int argc, char **argv, Coord x, Coord y)
987 if ((argc < 1) || (argc > 2))
988 AUSAGE (report);
989 else if (strcasecmp (argv[0], "Object") == 0)
991 gui->get_coords (_("Click on an object"), &x, &y);
992 return ReportDialog (argc - 1, argv + 1, x, y);
994 else if (strcasecmp (argv[0], "DrillReport") == 0)
995 return ReportDrills (argc - 1, argv + 1, x, y);
996 else if (strcasecmp (argv[0], "FoundPins") == 0)
997 return ReportFoundPins (argc - 1, argv + 1, x, y);
998 else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 1))
999 return ReportNetLength (argc - 1, argv + 1, x, y);
1000 else if (strcasecmp (argv[0], "AllNetLengths") == 0)
1001 return ReportAllNetLengths (argc - 1, argv + 1, x, y);
1002 else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 2))
1003 return ReportNetLengthByName (argv[1], x, y);
1004 else if (argc == 2)
1005 AUSAGE (report);
1006 else
1007 AFAIL (report);
1008 return 1;
1011 HID_Action report_action_list[] = {
1012 {"ReportObject", N_("Click on an object"), ReportDialog,
1013 reportdialog_help, reportdialog_syntax}
1015 {"Report", 0, Report,
1016 report_help, report_syntax}
1019 REGISTER_ACTIONS (report_action_list)