src/puller.c: Converted plain comments into doxygen comments.
[geda-pcb/pcjc2.git] / src / report.c
blob1e49d9dd5eed220cf81f4309a53ee50f967bb00c
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 double l;
579 int dx, dy;
580 dx = line->Point1.X - line->Point2.X;
581 dy = line->Point1.Y - line->Point2.Y;
582 l = sqrt ((double)dx*dx + (double)dy*dy);
583 length += l;
584 *found = 1;
587 ENDALL_LOOP;
589 ALLARC_LOOP (PCB->Data);
591 if (TEST_FLAG (FOUNDFLAG, arc))
593 double l;
594 /* FIXME: we assume width==height here */
595 l = M_PI * 2*arc->Width * abs(arc->Delta)/360.0;
596 length += l;
597 *found = 1;
600 ENDALL_LOOP;
602 return length;
605 static int
606 ReportAllNetLengths (int argc, char **argv, Coord x, Coord y)
608 int ni;
609 int found;
611 /* Reset all connection flags and save an undo-state to get back
612 * to the state the board was in when we started this function.
614 * After this, we don't add any changes to the undo system, but
615 * ensure we get back to a point where we can Undo() our changes
616 * by resetting the connections with ClearFlagOnAllObjects() before
617 * calling Undo() at the end of the procedure.
619 ClearFlagOnAllObjects (true, FOUNDFLAG);
620 IncrementUndoSerialNumber ();
622 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
624 char *netname = PCB->NetlistLib.Menu[ni].Name + 2;
625 char *ename = PCB->NetlistLib.Menu[ni].Entry[0].ListEntry;
626 char *pname;
627 bool got_one = 0;
629 ename = strdup (ename);
630 pname = strchr (ename, '-');
631 if (! pname)
633 free (ename);
634 continue;
636 *pname++ = 0;
638 ELEMENT_LOOP (PCB->Data);
640 char *es = element->Name[NAMEONPCB_INDEX].TextString;
641 if (es && strcmp (es, ename) == 0)
643 PIN_LOOP (element);
645 if (strcmp (pin->Number, pname) == 0)
647 x = pin->X;
648 y = pin->Y;
649 got_one = 1;
650 break;
653 END_LOOP;
654 PAD_LOOP (element);
656 if (strcmp (pad->Number, pname) == 0)
658 x = (pad->Point1.X + pad->Point2.X) / 2;
659 y = (pad->Point1.Y + pad->Point2.Y) / 2;
660 got_one = 1;
661 break;
664 END_LOOP;
667 END_LOOP;
669 if (got_one)
671 char buf[50];
672 const char *units_name = argv[0];
673 Coord length;
675 if (argc < 1)
676 units_name = Settings.grid_unit->suffix;
678 length = XYtoNetLength (x, y, &found);
680 /* Reset connectors for the next lookup */
681 ClearFlagOnAllObjects (false, FOUNDFLAG);
683 pcb_snprintf(buf, sizeof (buf), _("%$m*"), units_name, length);
684 gui->log(_("Net %s length %s\n"), netname, buf);
688 ClearFlagOnAllObjects (false, FOUNDFLAG);
689 Undo (true);
690 return 0;
693 static int
694 ReportNetLength (int argc, char **argv, Coord x, Coord y)
696 Coord length = 0;
697 char *netname = 0;
698 int found = 0;
700 gui->get_coords (_("Click on a connection"), &x, &y);
702 /* Reset all connection flags and save an undo-state to get back
703 * to the state the board was in when we started this function.
705 * After this, we don't add any changes to the undo system, but
706 * ensure we get back to a point where we can Undo() our changes
707 * by resetting the connections with ClearFlagOnAllObjects() before
708 * calling Undo() at the end of the procedure.
710 ClearFlagOnAllObjects (true, FOUNDFLAG);
711 IncrementUndoSerialNumber ();
713 length = XYtoNetLength (x, y, &found);
715 if (!found)
717 ClearFlagOnAllObjects (false, FOUNDFLAG);
718 Undo (true);
719 gui->log (_("No net under cursor.\n"));
720 return 1;
723 ELEMENT_LOOP (PCB->Data);
725 PIN_LOOP (element);
727 if (TEST_FLAG (FOUNDFLAG, pin))
729 int ni, nei;
730 char *ename = element->Name[NAMEONPCB_INDEX].TextString;
731 char *pname = pin->Number;
732 char *n;
734 if (ename && pname)
736 n = Concat (ename, _("-"), pname, NULL);
737 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
738 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
740 if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
742 netname = PCB->NetlistLib.Menu[ni].Name + 2;
743 goto got_net_name; /* four for loops deep */
749 END_LOOP;
750 PAD_LOOP (element);
752 if (TEST_FLAG (FOUNDFLAG, pad))
754 int ni, nei;
755 char *ename = element->Name[NAMEONPCB_INDEX].TextString;
756 char *pname = pad->Number;
757 char *n;
759 if (ename && pname)
761 n = Concat (ename, _("-"), pname, NULL);
762 for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
763 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
765 if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
767 netname = PCB->NetlistLib.Menu[ni].Name + 2;
768 goto got_net_name; /* four for loops deep */
774 END_LOOP;
776 END_LOOP;
778 got_net_name:
779 ClearFlagOnAllObjects (false, FOUNDFLAG);
780 Undo (true);
783 char buf[50];
784 pcb_snprintf(buf, sizeof (buf), _("%$m*"), Settings.grid_unit->suffix, length);
785 if (netname)
786 gui->log (_("Net \"%s\" length: %s\n"), netname, buf);
787 else
788 gui->log (_("Net length: %s\n"), buf);
791 return 0;
794 static int
795 ReportNetLengthByName (char *tofind, int x, int y)
797 int result;
798 char *netname = 0;
799 Coord length = 0;
800 int found = 0;
801 int i;
802 LibraryMenuType *net;
803 ConnectionType conn;
804 int net_found = 0;
805 #if defined(USE_RE)
806 int use_re = 0;
807 #endif
808 #if defined(HAVE_REGCOMP)
809 regex_t elt_pattern;
810 regmatch_t match;
811 #endif
812 #if defined(HAVE_RE_COMP)
813 char *elt_pattern;
814 #endif
816 if (!PCB)
817 return 1;
819 if (!tofind)
820 return 1;
822 #if defined(USE_RE)
823 use_re = 1;
824 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
826 net = PCB->NetlistLib.Menu + i;
827 if (strcasecmp (tofind, net->Name + 2) == 0)
828 use_re = 0;
830 if (use_re)
832 #if defined(HAVE_REGCOMP)
833 result =
834 regcomp (&elt_pattern, tofind,
835 REG_EXTENDED | REG_ICASE | REG_NOSUB);
836 if (result)
838 char errorstring[128];
840 regerror (result, &elt_pattern, errorstring, 128);
841 Message (_("regexp error: %s\n"), errorstring);
842 regfree (&elt_pattern);
843 return (1);
845 #endif
846 #if defined(HAVE_RE_COMP)
847 if ((elt_pattern = re_comp (tofind)) != NULL)
849 Message (_("re_comp error: %s\n"), elt_pattern);
850 return (1);
852 #endif
854 #endif
856 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
858 net = PCB->NetlistLib.Menu + i;
860 #if defined(USE_RE)
861 if (use_re)
863 #if defined(HAVE_REGCOMP)
864 if (regexec (&elt_pattern, net->Name + 2, 1, &match, 0) != 0)
865 continue;
866 #endif
867 #if defined(HAVE_RE_COMP)
868 if (re_exec (net->Name + 2) != 1)
869 continue;
870 #endif
872 else
873 #endif
874 if (strcasecmp (net->Name + 2, tofind))
875 continue;
877 if (SeekPad (net->Entry, &conn, false))
879 switch (conn.type)
881 case PIN_TYPE:
882 x = ((PinType *) (conn.ptr2))->X;
883 y = ((PinType *) (conn.ptr2))->Y;
884 net_found=1;
885 break;
886 case PAD_TYPE:
887 x = ((PadType *) (conn.ptr2))->Point1.X;
888 y = ((PadType *) (conn.ptr2))->Point1.Y;
889 net_found=1;
890 break;
892 if (net_found)
893 break;
897 if (!net_found)
899 gui->log (_("No net named %s\n"), tofind);
900 return 1;
903 #ifdef HAVE_REGCOMP
904 if (use_re)
905 regfree (&elt_pattern);
906 #endif
908 /* Reset all connection flags and save an undo-state to get back
909 * to the state the board was in when we started.
911 * After this, we don't add any changes to the undo system, but
912 * ensure we get back to a point where we can Undo() our changes
913 * by resetting the connections with ClearFlagOnAllObjects() before
914 * calling Undo() when we are finished.
916 ClearFlagOnAllObjects (true, FOUNDFLAG);
917 IncrementUndoSerialNumber ();
919 length = XYtoNetLength (x, y, &found);
920 netname = net->Name + 2;
922 ClearFlagOnAllObjects (false, FOUNDFLAG);
923 Undo (true);
925 if (!found)
927 if (net_found)
928 gui->log (_("Net found, but no lines or arcs were flagged.\n"));
929 else
930 gui->log (_("Net not found.\n"));
932 return 1;
936 char buf[50];
937 pcb_snprintf(buf, 50, _("%$m*"), Settings.grid_unit->suffix, length);
938 if (netname)
939 gui->log (_("Net \"%s\" length: %s\n"), netname, buf);
940 else
941 gui->log (_("Net length: %s\n"), buf);
944 return 0;
947 /* ---------------------------------------------------------------------------
948 * reports on an object
949 * syntax:
952 static const char report_syntax[] =
953 N_("Report(Object|DrillReport|FoundPins|NetLength|AllNetLengths|[,name])");
955 static const char report_help[] = N_("Produce various report.");
957 /* %start-doc actions Report
959 @table @code
961 @item Object
962 The object under the crosshair will be reported, describing various
963 aspects of the object.
965 @item DrillReport
966 A report summarizing the number of drill sizes used, and how many of
967 each, will be produced.
969 @item FoundPins
970 A report listing all pins and pads which are marked as ``found'' will
971 be produced.
973 @item NetLength
974 The name and length of the net under the crosshair will be reported to
975 the message log.
977 @item AllNetLengths
978 The name and length of the net under the crosshair will be reported to
979 the message log. An optional parameter specifies mm, mil, pcb, or in
980 units
982 @end table
984 %end-doc */
986 static int
987 Report (int argc, char **argv, Coord x, Coord y)
989 if ((argc < 1) || (argc > 2))
990 AUSAGE (report);
991 else if (strcasecmp (argv[0], "Object") == 0)
993 gui->get_coords (_("Click on an object"), &x, &y);
994 return ReportDialog (argc - 1, argv + 1, x, y);
996 else if (strcasecmp (argv[0], "DrillReport") == 0)
997 return ReportDrills (argc - 1, argv + 1, x, y);
998 else if (strcasecmp (argv[0], "FoundPins") == 0)
999 return ReportFoundPins (argc - 1, argv + 1, x, y);
1000 else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 1))
1001 return ReportNetLength (argc - 1, argv + 1, x, y);
1002 else if (strcasecmp (argv[0], "AllNetLengths") == 0)
1003 return ReportAllNetLengths (argc - 1, argv + 1, x, y);
1004 else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 2))
1005 return ReportNetLengthByName (argv[1], x, y);
1006 else if (argc == 2)
1007 AUSAGE (report);
1008 else
1009 AFAIL (report);
1010 return 1;
1013 HID_Action report_action_list[] = {
1014 {"ReportObject", N_("Click on an object"), ReportDialog,
1015 reportdialog_help, reportdialog_syntax}
1017 {"Report", 0, Report,
1018 report_help, report_syntax}
1021 REGISTER_ACTIONS (report_action_list)