pcb 4.1.1
An interactive printed circuit board layout editor.

report.c

Go to the documentation of this file.
00001 
00038 #ifdef HAVE_CONFIG_H
00039 #include "config.h"
00040 #endif
00041 
00042 #include <math.h>
00043 
00044 #include "report.h"
00045 #include "crosshair.h"
00046 #include "data.h"
00047 #include "drill.h"
00048 #include "error.h"
00049 #include "search.h"
00050 #include "misc.h"
00051 #include "mymem.h"
00052 #include "rats.h"
00053 #include "rtree.h"
00054 #include "strflags.h"
00055 #include "macro.h"
00056 #include "undo.h"
00057 #include "find.h"
00058 #include "draw.h"
00059 #include "pcb-printf.h"
00060 #ifdef HAVE_REGEX_H
00061 #include <regex.h>
00062 #endif
00063 
00064 #ifdef HAVE_REGCOMP
00065 #undef HAVE_RE_COMP
00066 #endif
00067 
00068 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
00069 #define USE_RE
00070 #endif
00071 
00072 #ifdef HAVE_LIBDMALLOC
00073 #include <dmalloc.h>
00074 #endif
00075 
00076 #define USER_UNITMASK (Settings.grid_unit->allow)
00077 
00078 static int
00079 ReportDrills (int argc, char **argv, Coord x, Coord y)
00080 {
00081   DrillInfoType *AllDrills;
00082   Cardinal n;
00083   char *stringlist, *thestring;
00084   int total_drills = 0;
00085   size_t size_left;
00086 
00087   AllDrills = GetDrillInfo (PCB->Data);
00088 
00089   for (n = 0; n < AllDrills->DrillN; n++)
00090     {
00091       total_drills += AllDrills->Drill[n].PinCount;
00092       total_drills += AllDrills->Drill[n].ViaCount;
00093     }
00094 
00095   size_left = 512L + AllDrills->DrillN * 64L;
00096   stringlist = (char *)malloc (size_left);
00097 
00098   /* Use tabs for formatting since can't count on a fixed font anymore.
00099      |  And even that probably isn't going to work in all cases.
00100    */
00101   sprintf (stringlist,
00102            _("There are %d different drill sizes used in this layout, %d holes total\n\n"
00103            "Drill Diam. (%s)\t# of Pins\t# of Vias\t# of Elements\t# Unplated\n"),
00104            AllDrills->DrillN, total_drills, Settings.grid_unit->suffix);
00105 
00106   thestring = stringlist;
00107   while (size_left > 0 && *thestring != '\0')
00108     {
00109       thestring++;
00110       size_left--;
00111     }
00112 
00113   for (n = 0; n < AllDrills->DrillN; n++)
00114     {
00115       pcb_snprintf (thestring, size_left,
00116                _("%10m*\t\t%d\t\t%d\t\t%d\t\t%d\n"),
00117                Settings.grid_unit->suffix,
00118                AllDrills->Drill[n].DrillSize,
00119                AllDrills->Drill[n].PinCount, AllDrills->Drill[n].ViaCount,
00120                AllDrills->Drill[n].ElementN,
00121                AllDrills->Drill[n].UnplatedCount);
00122       while (size_left > 0 && *thestring != '\0')
00123         {
00124           thestring++;
00125           size_left--;
00126         }
00127     }
00128   FreeDrillInfo (AllDrills);
00129   /* create dialog box */
00130   gui->report_dialog (_("Drill Report"), stringlist);
00131 
00132   free (stringlist);
00133   return 0;
00134 }
00135 
00136 
00137 static const char reportdialog_syntax[] = N_("ReportDialog()");
00138 
00139 static const char reportdialog_help[] =
00140   N_("Report on the object under the crosshair");
00141 
00142 
00143 static char*
00144 CreateBuriedViaDescription (int from, int to)
00145 {
00146   static char s[32];
00147 
00148   if ((from == 0) && (to == 0))
00149     strcpy (s, "Through-hole");
00150   else
00151     sprintf (s, "Buried (%d:%d)", from, to);
00152 
00153   return s;
00154 }
00155 
00156 /* %start-doc actions ReportDialog
00157 
00158 This is a shortcut for @code{Report(Object)}.
00159 
00160 %end-doc */
00161 
00162 static int
00163 ReportDialog (int argc, char **argv, Coord x, Coord y)
00164 {
00165   void *ptr1, *ptr2, *ptr3;
00166   int type;
00167   char report[2048];
00168 
00169   type = SearchScreen (x, y, REPORT_TYPES, &ptr1, &ptr2, &ptr3);
00170   if (type == NO_TYPE)
00171     type =
00172       SearchScreen (x, y, REPORT_TYPES | LOCKED_TYPE, &ptr1, &ptr2, &ptr3);
00173 
00174   switch (type)
00175     {
00176     case VIA_TYPE:
00177       {
00178         PinType *via;
00179 #ifndef NDEBUG
00180         if (gui->shift_is_pressed ())
00181           {
00182             __r_dump_tree (PCB->Data->via_tree->root, 0);
00183             return 0;
00184           }
00185 #endif
00186         via = (PinType *) ptr2;
00187         if (TEST_FLAG (HOLEFLAG, via))
00188           pcb_snprintf (report, sizeof (report), _("%m+VIA ID# %ld; Flags:%s\n"
00189                    "(X,Y) = %$mD.\n"
00190                    "It is a pure hole of diameter %$mS.\n"
00191                    "Name = \"%s\"."
00192                    "%s"), USER_UNITMASK, via->ID, flags_to_string (via->Flags, VIA_TYPE),
00193                    via->X, via->Y, via->DrillingHole, EMPTY (via->Name),
00194                    TEST_FLAG (LOCKFLAG, via) ? _("It is LOCKED.\n") : "");
00195         else
00196           pcb_snprintf (report, sizeof (report), _("%m+VIA ID# %ld;  Flags:%s\n"
00197                    "(X,Y) = %$mD.\n"
00198                    "Copper width = %$mS. Drill width = %$mS.\n"
00199                    "Clearance width in polygons = %$mS.\n"
00200                    "Annulus = %$mS.\n"
00201                    "Solder mask hole = %$mS (gap = %$mS).\n"
00202                    "Type = %s.\n"
00203                    "Name = \"%s\"."
00204                    "%s"), USER_UNITMASK, via->ID, flags_to_string (via->Flags, VIA_TYPE),
00205                    via->X, via->Y,
00206                    via->Thickness,
00207                    via->DrillingHole,
00208                    via->Clearance / 2,
00209                    (via->Thickness - via->DrillingHole) / 2,
00210                    via->Mask,
00211                    (via->Mask - via->Thickness) / 2,
00212                    CreateBuriedViaDescription (via->BuriedFrom, via->BuriedTo),
00213                    EMPTY (via->Name), TEST_FLAG (LOCKFLAG, via) ?
00214                    _("It is LOCKED.\n") : "");
00215         break;
00216       }
00217     case PIN_TYPE:
00218       {
00219         PinType *Pin;
00220         ElementType *element;
00221 #ifndef NDEBUG
00222         if (gui->shift_is_pressed ())
00223           {
00224             __r_dump_tree (PCB->Data->pin_tree->root, 0);
00225             return 0;
00226           }
00227 #endif
00228         Pin = (PinType *) ptr2;
00229         element = (ElementType *) ptr1;
00230 
00231         PIN_LOOP (element);
00232         {
00233           if (pin == Pin)
00234             break;
00235         }
00236         END_LOOP;
00237         if (TEST_FLAG (HOLEFLAG, Pin))
00238           pcb_snprintf (report, sizeof (report), _("%m+PIN ID# %ld; Flags:%s\n"
00239                    "(X,Y) = %$mD.\n"
00240                    "It is a mounting hole. Drill width = %$mS.\n"
00241                    "It is owned by element %$mS.\n"
00242                    "%s"), USER_UNITMASK, Pin->ID, flags_to_string (Pin->Flags, PIN_TYPE),
00243                    Pin->X, Pin->Y, Pin->DrillingHole,
00244                    EMPTY (element->Name[1].TextString),
00245                    TEST_FLAG (LOCKFLAG, Pin) ? _("It is LOCKED.\n") : "");
00246         else
00247           pcb_snprintf (report, sizeof (report),
00248                    _("%m+PIN ID# %ld;  Flags:%s\n" "(X,Y) = %$mD.\n"
00249                    "Copper width = %$mS. Drill width = %$mS.\n"
00250                    "Clearance width to Polygon = %$mS.\n"
00251                    "Annulus = %$mS.\n"
00252                    "Solder mask hole = %$mS (gap = %$mS).\n"
00253                    "Name = \"%s\".\n"
00254                    "It is owned by element %s\n as pin number %s.\n"
00255                    "%s"), USER_UNITMASK,
00256                    Pin->ID, flags_to_string (Pin->Flags, PIN_TYPE),
00257                    Pin->X, Pin->Y, Pin->Thickness,
00258                    Pin->DrillingHole,
00259                    Pin->Clearance / 2,
00260                    (Pin->Thickness - Pin->DrillingHole) / 2,
00261                    Pin->Mask,
00262                    (Pin->Mask - Pin->Thickness) / 2,
00263                    EMPTY (Pin->Name),
00264                    EMPTY (element->Name[1].TextString), EMPTY (Pin->Number),
00265                    TEST_FLAG (LOCKFLAG, Pin) ? _("It is LOCKED.\n") : "");
00266         break;
00267       }
00268     case LINE_TYPE:
00269       {
00270         LineType *line;
00271 #ifndef NDEBUG
00272         if (gui->shift_is_pressed ())
00273           {
00274             LayerType *layer = (LayerType *) ptr1;
00275             __r_dump_tree (layer->line_tree->root, 0);
00276             return 0;
00277           }
00278 #endif
00279         line = (LineType *) ptr2;
00280         pcb_snprintf (report, sizeof (report), _("%m+LINE ID# %ld;  Flags:%s\n"
00281                  "FirstPoint(X,Y)  = %$mD, ID = %ld.\n"
00282                  "SecondPoint(X,Y) = %$mD, ID = %ld.\n"
00283                  "Width = %$mS.\nClearance width in polygons = %$mS.\n"
00284                  "It is on layer %d\n"
00285                  "and has name \"%s\".\n"
00286                  "%s"), USER_UNITMASK,
00287                  line->ID, flags_to_string (line->Flags, LINE_TYPE),
00288                  line->Point1.X, line->Point1.Y, line->Point1.ID,
00289                  line->Point2.X, line->Point2.Y, line->Point2.ID,
00290                  line->Thickness, line->Clearance / 2,
00291                  GetLayerNumber (PCB->Data, (LayerType *) ptr1),
00292                  UNKNOWN (line->Number),
00293                  TEST_FLAG (LOCKFLAG, line) ? _("It is LOCKED.\n") : "");
00294         break;
00295       }
00296     case RATLINE_TYPE:
00297       {
00298         RatType *line;
00299 #ifndef NDEBUG
00300         if (gui->shift_is_pressed ())
00301           {
00302             __r_dump_tree (PCB->Data->rat_tree->root, 0);
00303             return 0;
00304           }
00305 #endif
00306         line = (RatType *) ptr2;
00307         pcb_snprintf (report, sizeof (report), _("%m+RAT-LINE ID# %ld;  Flags:%s\n"
00308                  "FirstPoint(X,Y)  = %$mD; ID = %ld; "
00309                  "connects to layer group %d.\n"
00310                  "SecondPoint(X,Y) = %$mD; ID = %ld; "
00311                  "connects to layer group %d.\n"),
00312                  USER_UNITMASK, line->ID, flags_to_string (line->Flags, LINE_TYPE),
00313                  line->Point1.X, line->Point1.Y,
00314                  line->Point1.ID, line->group1,
00315                  line->Point2.X, line->Point2.Y,
00316                  line->Point2.ID, line->group2);
00317         break;
00318       }
00319     case ARC_TYPE:
00320       {
00321         ArcType *Arc;
00322         BoxType *box;
00323 #ifndef NDEBUG
00324         if (gui->shift_is_pressed ())
00325           {
00326             LayerType *layer = (LayerType *) ptr1;
00327             __r_dump_tree (layer->arc_tree->root, 0);
00328             return 0;
00329           }
00330 #endif
00331         Arc = (ArcType *) ptr2;
00332         box = GetArcEnds (Arc);
00333 
00334         pcb_snprintf (report, sizeof (report), _("%m+ARC ID# %ld;  Flags:%s\n"
00335                  "CenterPoint(X,Y) = %$mD.\n"
00336                  "Radius = %$mS, Thickness = %$mS.\n"
00337                  "Clearance width in polygons = %$mS.\n"
00338                  "StartAngle = %ma degrees, DeltaAngle = %ma degrees.\n"
00339                  "Bounding Box is %$mD, %$mD.\n"
00340                  "That makes the end points at %$mD and %$mD.\n"
00341                  "It is on layer %d.\n"
00342                  "%s"), USER_UNITMASK, Arc->ID, flags_to_string (Arc->Flags, ARC_TYPE),
00343                  Arc->X, Arc->Y,
00344                  Arc->Width, Arc->Thickness,
00345                  Arc->Clearance / 2, Arc->StartAngle, Arc->Delta,
00346                  Arc->BoundingBox.X1, Arc->BoundingBox.Y1,
00347                  Arc->BoundingBox.X2, Arc->BoundingBox.Y2,
00348                  box->X1, box->Y1,
00349                  box->X2, box->Y2,
00350                  GetLayerNumber (PCB->Data, (LayerType *) ptr1),
00351                  TEST_FLAG (LOCKFLAG, Arc) ? _("It is LOCKED.\n") : "");
00352         break;
00353       }
00354     case POLYGON_TYPE:
00355       {
00356         PolygonType *Polygon;
00357 #ifndef NDEBUG
00358         if (gui->shift_is_pressed ())
00359           {
00360             LayerType *layer = (LayerType *) ptr1;
00361             __r_dump_tree (layer->polygon_tree->root, 0);
00362             return 0;
00363           }
00364 #endif
00365         Polygon = (PolygonType *) ptr2;
00366 
00367         pcb_snprintf (report, sizeof (report), _("%m+POLYGON ID# %ld;  Flags:%s\n"
00368                  "Its bounding box is %$mD %$mD.\n"
00369                  "It has %d points and could store %d more\n"
00370                  "  without using more memory.\n"
00371                  "It has %d holes and resides on layer %d.\n"
00372                  "%s"), USER_UNITMASK, Polygon->ID,
00373                  flags_to_string (Polygon->Flags, POLYGON_TYPE),
00374                  Polygon->BoundingBox.X1, Polygon->BoundingBox.Y1,
00375                  Polygon->BoundingBox.X2, Polygon->BoundingBox.Y2,
00376                  Polygon->PointN, Polygon->PointMax - Polygon->PointN,
00377                  Polygon->HoleIndexN,
00378                  GetLayerNumber (PCB->Data, (LayerType *) ptr1),
00379                  TEST_FLAG (LOCKFLAG, Polygon) ? _("It is LOCKED.\n") : "");
00380         break;
00381       }
00382     case PAD_TYPE:
00383       {
00384         Coord len;
00385         PadType *Pad;
00386         ElementType *element;
00387 #ifndef NDEBUG
00388         if (gui->shift_is_pressed ())
00389           {
00390             __r_dump_tree (PCB->Data->pad_tree->root, 0);
00391             return 0;
00392           }
00393 #endif
00394         Pad = (PadType *) ptr2;
00395         element = (ElementType *) ptr1;
00396 
00397         PAD_LOOP (element);
00398         {
00399           {
00400             if (pad == Pad)
00401               break;
00402           }
00403         }
00404         END_LOOP;
00405         len = Distance (Pad->Point1.X, Pad->Point1.Y, Pad->Point2.X, Pad->Point2.Y);
00406         pcb_snprintf (report, sizeof (report), _("%m+PAD ID# %ld;  Flags:%s\n"
00407                  "FirstPoint(X,Y)  = %$mD; ID = %ld.\n"
00408                  "SecondPoint(X,Y) = %$mD; ID = %ld.\n"
00409                  "Width = %$mS.  Length = %$mS.\n"
00410                  "Clearance width in polygons = %$mS.\n"
00411                  "Solder mask = %$mS x %$mS (gap = %$mS).\n"
00412                  "Name = \"%s\".\n"
00413                  "It is owned by SMD element %s\n"
00414                  "  as pin number %s and is on the %s\n"
00415                  "side of the board.\n"
00416                  "%s"), USER_UNITMASK, Pad->ID,
00417                  flags_to_string (Pad->Flags, PAD_TYPE),
00418                  Pad->Point1.X, Pad->Point1.Y, Pad->Point1.ID,
00419                  Pad->Point2.X, Pad->Point2.Y, Pad->Point2.ID,
00420                  Pad->Thickness, len + Pad->Thickness,
00421                  Pad->Clearance / 2,
00422                  Pad->Mask, len + Pad->Mask,
00423                  (Pad->Mask - Pad->Thickness) / 2,
00424                  EMPTY (Pad->Name),
00425                  EMPTY (element->Name[1].TextString),
00426                  EMPTY (Pad->Number),
00427                  TEST_FLAG (ONSOLDERFLAG,
00428                             Pad) ? _("solder (bottom)") : _("component"),
00429                  TEST_FLAG (LOCKFLAG, Pad) ? _("It is LOCKED.\n") : "");
00430         break;
00431       }
00432     case ELEMENT_TYPE:
00433       {
00434         ElementType *element;
00435 #ifndef NDEBUG
00436         if (gui->shift_is_pressed ())
00437           {
00438             __r_dump_tree (PCB->Data->element_tree->root, 0);
00439             return 0;
00440           }
00441 #endif
00442         element = (ElementType *) ptr2;
00443         pcb_snprintf (report, sizeof (report), _("%m+ELEMENT ID# %ld;  Flags:%s\n"
00444                  "BoundingBox %$mD %$mD.\n"
00445                  "Descriptive Name \"%s\".\n"
00446                  "Name on board \"%s\".\n"
00447                  "Part number name \"%s\".\n"
00448                  "It is %$mS tall and is located at (X,Y) = %$mD %s.\n"
00449                  "Mark located at point (X,Y) = %$mD.\n"
00450                  "It is on the %s side of the board.\n"
00451                  "%s"), USER_UNITMASK,
00452                  element->ID, flags_to_string (element->Flags, ELEMENT_TYPE),
00453                  element->BoundingBox.X1, element->BoundingBox.Y1,
00454                  element->BoundingBox.X2, element->BoundingBox.Y2,
00455                  EMPTY (element->Name[0].TextString),
00456                  EMPTY (element->Name[1].TextString),
00457                  EMPTY (element->Name[2].TextString),
00458                  SCALE_TEXT (FONT_CAPHEIGHT, element->Name[1].Scale),
00459                  element->Name[1].X, element->Name[1].Y,
00460                  TEST_FLAG (HIDENAMEFLAG, element) ? _(",\n  but it's hidden") : "",
00461                  element->MarkX, element->MarkY,
00462                  TEST_FLAG (ONSOLDERFLAG, element) ? _("solder (bottom)") : _("component"),
00463                  TEST_FLAG (LOCKFLAG, element) ? _("It is LOCKED.\n") : "");
00464         break;
00465       }
00466     case TEXT_TYPE:
00467 #ifndef NDEBUG
00468       if (gui->shift_is_pressed ())
00469         {
00470           LayerType *layer = (LayerType *) ptr1;
00471           __r_dump_tree (layer->text_tree->root, 0);
00472           return 0;
00473         }
00474 #endif
00475     case ELEMENTNAME_TYPE:
00476       {
00477         char laynum[32];
00478         TextType *text;
00479 #ifndef NDEBUG
00480         if (gui->shift_is_pressed ())
00481           {
00482             __r_dump_tree (PCB->Data->name_tree[NAME_INDEX (PCB)]->root, 0);
00483             return 0;
00484           }
00485 #endif
00486         text = (TextType *) ptr2;
00487 
00488         if (type == TEXT_TYPE)
00489           sprintf (laynum, _("It is on layer %d."),
00490                    GetLayerNumber (PCB->Data, (LayerType *) ptr1));
00491         pcb_snprintf (report, sizeof (report), _("%m+TEXT ID# %ld;  Flags:%s\n"
00492                  "Located at (X,Y) = %$mD.\n"
00493                  "Characters are %$mS tall.\n"
00494                  "Value is \"%s\".\n"
00495                  "Direction is %d.\n"
00496                  "The bounding box is %$mD %$mD.\n"
00497                  "%s\n"
00498                  "%s"), USER_UNITMASK, text->ID, flags_to_string (text->Flags, TEXT_TYPE),
00499                  text->X, text->Y, SCALE_TEXT (FONT_CAPHEIGHT, text->Scale),
00500                  text->TextString, text->Direction,
00501                  text->BoundingBox.X1, text->BoundingBox.Y1,
00502                  text->BoundingBox.X2, text->BoundingBox.Y2,
00503                  (type == TEXT_TYPE) ? laynum : _("It is an element name."),
00504                  TEST_FLAG (LOCKFLAG, text) ? _("It is LOCKED.\n") : "");
00505         break;
00506       }
00507     case LINEPOINT_TYPE:
00508     case POLYGONPOINT_TYPE:
00509       {
00510         PointType *point = (PointType *) ptr2;
00511         pcb_snprintf (report, sizeof (report), _("%m+POINT ID# %ld.\n"
00512                  "Located at (X,Y) = %$mD.\n"
00513                  "It belongs to a %s on layer %d.\n"), USER_UNITMASK, point->ID,
00514                  point->X, point->Y,
00515                  (type == LINEPOINT_TYPE) ?
00516                      C_("report", "line") : C_("report", "polygon"),
00517                  GetLayerNumber (PCB->Data, (LayerType *) ptr1));
00518         break;
00519       }
00520     case NO_TYPE:
00521       report[0] = '\0';
00522       break;
00523 
00524     default:
00525       sprintf (report, _("Unknown\n"));
00526       break;
00527     }
00528 
00529   if (report[0] == '\0')
00530     {
00531       Message (_("Nothing found to report on\n"));
00532       return 1;
00533     }
00534   /* create dialog box */
00535   gui->report_dialog (_("Report"), report);
00536 
00537   return 0;
00538 }
00539 
00540 static int
00541 ReportFoundPins (int argc, char **argv, Coord x, Coord y)
00542 {
00543   static DynamicStringType list;
00544   char temp[64];
00545   int col = 0;
00546 
00547   DSClearString (&list);
00548   DSAddString (&list, _("The following pins/pads are FOUND:\n"));
00549   ELEMENT_LOOP (PCB->Data);
00550   {
00551     PIN_LOOP (element);
00552     {
00553       if (TEST_FLAG (FOUNDFLAG, pin))
00554         {
00555           sprintf (temp, _("%s-%s,%c"),
00556                    NAMEONPCB_NAME (element),
00557                    pin->Number,
00558                    ((col++ % (COLUMNS + 1)) == COLUMNS) ? '\n' : ' ');
00559           DSAddString (&list, temp);
00560         }
00561     }
00562     END_LOOP;
00563     PAD_LOOP (element);
00564     {
00565       if (TEST_FLAG (FOUNDFLAG, pad))
00566         {
00567           sprintf (temp, _("%s-%s,%c"),
00568                    NAMEONPCB_NAME (element), pad->Number,
00569                    ((col++ % (COLUMNS + 1)) == COLUMNS) ? '\n' : ' ');
00570           DSAddString (&list, temp);
00571         }
00572     }
00573     END_LOOP;
00574   }
00575   END_LOOP;
00576 
00577   gui->report_dialog (_("Report"), list.Data);
00578   return 0;
00579 }
00580 
00587 static double
00588 XYtoNetLength (Coord x, Coord y, int *found)
00589 {
00590   double length;
00591 
00592   length = 0;
00593   *found = 0;
00594 
00595   /* NB: The third argument here, 'false' ensures LookupConnection
00596    *     does not add its changes to the undo system.
00597    */
00598   LookupConnection (x, y, false, PCB->Grid, FOUNDFLAG, true);
00599 
00600   ALLLINE_LOOP (PCB->Data);
00601   {
00602     if (TEST_FLAG (FOUNDFLAG, line))
00603       {
00604         int dx, dy;
00605         dx = line->Point1.X - line->Point2.X;
00606         dy = line->Point1.Y - line->Point2.Y;
00607         length += hypot (dx, dy);
00608         *found = 1;
00609       }
00610   }
00611   ENDALL_LOOP;
00612 
00613   ALLARC_LOOP (PCB->Data);
00614   {
00615     if (TEST_FLAG (FOUNDFLAG, arc))
00616       {
00617         double l;
00618         /* FIXME: we assume width==height here */
00619         l = M_PI * 2*arc->Width * abs(arc->Delta)/360.0;
00620         length += l;
00621         *found = 1;
00622       }
00623   }
00624   ENDALL_LOOP;
00625 
00626   return length;
00627 }
00628 
00629 static int
00630 ReportAllNetLengths (int argc, char **argv, Coord x, Coord y)
00631 {
00632   int ni;
00633   int found;
00634 
00635   /* Reset all connection flags and save an undo-state to get back
00636    * to the state the board was in when we started this function.
00637    *
00638    * After this, we don't add any changes to the undo system, but
00639    * ensure we get back to a point where we can Undo() our changes
00640    * by resetting the connections with ClearFlagOnAllObjects() before
00641    * calling Undo() at the end of the procedure.
00642    */
00643   ClearFlagOnAllObjects (true, FOUNDFLAG);
00644   IncrementUndoSerialNumber ();
00645 
00646   for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
00647     {
00648       char *netname = PCB->NetlistLib.Menu[ni].Name + 2;
00649       char *ename = PCB->NetlistLib.Menu[ni].Entry[0].ListEntry;
00650       char *pname;
00651       bool got_one = 0;
00652 
00653       ename = strdup (ename);
00654       pname = strchr (ename, '-');
00655       if (! pname)
00656         {
00657           free (ename);
00658           continue;
00659         }
00660       *pname++ = 0;
00661 
00662       ELEMENT_LOOP (PCB->Data);
00663       {
00664         char *es = element->Name[NAMEONPCB_INDEX].TextString;
00665         if (es && strcmp (es, ename) == 0)
00666           {
00667             PIN_LOOP (element);
00668             {
00669               if (strcmp (pin->Number, pname) == 0)
00670                 {
00671                   x = pin->X;
00672                   y = pin->Y;
00673                   got_one = 1;
00674                   break;
00675                 }
00676             }
00677             END_LOOP;
00678             PAD_LOOP (element);
00679             {
00680               if (strcmp (pad->Number, pname) == 0)
00681                 {
00682                   x = (pad->Point1.X + pad->Point2.X) / 2;
00683                   y = (pad->Point1.Y + pad->Point2.Y) / 2;
00684                   got_one = 1;
00685                   break;
00686                 }
00687             }
00688             END_LOOP;
00689           }
00690       }
00691       END_LOOP;
00692 
00693       if (got_one)
00694         {
00695           char buf[50];
00696           const char *units_name = argv[0];
00697           Coord length;
00698 
00699           if (argc < 1)
00700             units_name = Settings.grid_unit->suffix;
00701 
00702           length = XYtoNetLength (x, y, &found);
00703 
00704           /* Reset connectors for the next lookup */
00705           ClearFlagOnAllObjects (false, FOUNDFLAG);
00706 
00707           pcb_snprintf(buf, sizeof (buf), _("%$m*"), units_name, length);
00708           gui->log(_("Net \"%s\" length: %s\n"), netname, buf);
00709         }
00710     }
00711 
00712   ClearFlagOnAllObjects (false, FOUNDFLAG);
00713   Undo (true);
00714   return 0;
00715 }
00716 
00717 static int
00718 ReportNetLength (int argc, char **argv, Coord x, Coord y)
00719 {
00720   Coord length = 0;
00721   char *netname = 0;
00722   int found = 0;
00723 
00724   gui->get_coords (_("Click on a connection"), &x, &y);
00725 
00726   /* Reset all connection flags and save an undo-state to get back
00727    * to the state the board was in when we started this function.
00728    *
00729    * After this, we don't add any changes to the undo system, but
00730    * ensure we get back to a point where we can Undo() our changes
00731    * by resetting the connections with ClearFlagOnAllObjects() before
00732    * calling Undo() at the end of the procedure.
00733    */
00734   ClearFlagOnAllObjects (true, FOUNDFLAG);
00735   IncrementUndoSerialNumber ();
00736 
00737   length = XYtoNetLength (x, y, &found);
00738 
00739   if (!found)
00740     {
00741       ClearFlagOnAllObjects (false, FOUNDFLAG);
00742       Undo (true);
00743       gui->log (_("No net under cursor.\n"));
00744       return 1;
00745     }
00746 
00747   ELEMENT_LOOP (PCB->Data);
00748   {
00749     PIN_LOOP (element);
00750     {
00751       if (TEST_FLAG (FOUNDFLAG, pin))
00752         {
00753           int ni, nei;
00754           char *ename = element->Name[NAMEONPCB_INDEX].TextString;
00755           char *pname = pin->Number;
00756           char *n;
00757 
00758           if (ename && pname)
00759             {
00760               n = Concat (ename, _("-"), pname, NULL);
00761               for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
00762                 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
00763                   {
00764                     if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
00765                       {
00766                         netname = PCB->NetlistLib.Menu[ni].Name + 2;
00767                         goto got_net_name; /* four for loops deep */
00768                       }
00769                   }
00770             }
00771         }
00772     }
00773     END_LOOP;
00774     PAD_LOOP (element);
00775     {
00776       if (TEST_FLAG (FOUNDFLAG, pad))
00777         {
00778           int ni, nei;
00779           char *ename = element->Name[NAMEONPCB_INDEX].TextString;
00780           char *pname = pad->Number;
00781           char *n;
00782 
00783           if (ename && pname)
00784             {
00785               n = Concat (ename, _("-"), pname, NULL);
00786               for (ni = 0; ni < PCB->NetlistLib.MenuN; ni++)
00787                 for (nei = 0; nei < PCB->NetlistLib.Menu[ni].EntryN; nei++)
00788                   {
00789                     if (strcmp (PCB->NetlistLib.Menu[ni].Entry[nei].ListEntry, n) == 0)
00790                       {
00791                         netname = PCB->NetlistLib.Menu[ni].Name + 2;
00792                         goto got_net_name; /* four for loops deep */
00793                       }
00794                   }
00795             }
00796         }
00797     }
00798     END_LOOP;
00799   }
00800   END_LOOP;
00801 
00802 got_net_name:
00803   ClearFlagOnAllObjects (false, FOUNDFLAG);
00804   Undo (true);
00805 
00806   {
00807     char buf[50];
00808     pcb_snprintf(buf, sizeof (buf), _("%$m*"), Settings.grid_unit->suffix, length);
00809     if (netname)
00810       gui->log (_("Net \"%s\" length: %s\n"), netname, buf);
00811     else
00812       gui->log (_("Net length: %s\n"), buf);
00813   }
00814 
00815   return 0;
00816 }
00817 
00818 static int
00819 ReportNetLengthByName (char *tofind, int x, int y)
00820 {
00821   int result;
00822   char *netname = 0;
00823   Coord length = 0;
00824   int found = 0;
00825   int i;
00826   LibraryMenuType *net;
00827   ConnectionType conn;
00828   int net_found = 0;
00829 #if defined(USE_RE)
00830   int use_re = 0;
00831 #endif
00832 #if defined(HAVE_REGCOMP)
00833   regex_t elt_pattern;
00834   regmatch_t match;
00835 #endif
00836 #if defined(HAVE_RE_COMP)
00837   char *elt_pattern;
00838 #endif
00839 
00840   if (!PCB)
00841     return 1;
00842 
00843   if (!tofind)
00844     return 1;
00845 
00846 #if defined(USE_RE)
00847       use_re = 1;
00848       for (i = 0; i < PCB->NetlistLib.MenuN; i++)
00849         {
00850           net = PCB->NetlistLib.Menu + i;
00851           if (strcasecmp (tofind, net->Name + 2) == 0)
00852             use_re = 0;
00853         }
00854       if (use_re)
00855         {
00856 #if defined(HAVE_REGCOMP)
00857           result =
00858             regcomp (&elt_pattern, tofind,
00859                      REG_EXTENDED | REG_ICASE | REG_NOSUB);
00860           if (result)
00861             {
00862               char errorstring[128];
00863 
00864               regerror (result, &elt_pattern, errorstring, 128);
00865               Message (_("regexp error: %s\n"), errorstring);
00866               regfree (&elt_pattern);
00867               return (1);
00868             }
00869 #endif
00870 #if defined(HAVE_RE_COMP)
00871           if ((elt_pattern = re_comp (tofind)) != NULL)
00872             {
00873               Message (_("re_comp error: %s\n"), elt_pattern);
00874               return (1);
00875             }
00876 #endif
00877         }
00878 #endif
00879 
00880   for (i = 0; i < PCB->NetlistLib.MenuN; i++)
00881     {
00882       net = PCB->NetlistLib.Menu + i;
00883 
00884 #if defined(USE_RE)
00885           if (use_re)
00886             {
00887 #if defined(HAVE_REGCOMP)
00888               if (regexec (&elt_pattern, net->Name + 2, 1, &match, 0) != 0)
00889                 continue;
00890 #endif
00891 #if defined(HAVE_RE_COMP)
00892               if (re_exec (net->Name + 2) != 1)
00893                 continue;
00894 #endif
00895             }
00896           else
00897 #endif
00898           if (strcasecmp (net->Name + 2, tofind))
00899             continue;
00900 
00901         if (SeekPad (net->Entry, &conn, false))
00902         {
00903           switch (conn.type)
00904           {
00905             case PIN_TYPE:
00906               x = ((PinType *) (conn.ptr2))->X;
00907               y = ((PinType *) (conn.ptr2))->Y;
00908               net_found=1;
00909               break;
00910             case PAD_TYPE:
00911               x = ((PadType *) (conn.ptr2))->Point1.X;
00912               y = ((PadType *) (conn.ptr2))->Point1.Y;
00913               net_found=1;
00914               break;
00915           }
00916           if (net_found)
00917             break;
00918         }
00919     }
00920 
00921   if (!net_found)
00922     {
00923       gui->log (_("No net named \"%s\"\n"), tofind);
00924       return 1;
00925     }
00926 
00927 #ifdef HAVE_REGCOMP
00928   if (use_re)
00929     regfree (&elt_pattern);
00930 #endif
00931 
00932   /* Reset all connection flags and save an undo-state to get back
00933    * to the state the board was in when we started.
00934    *
00935    * After this, we don't add any changes to the undo system, but
00936    * ensure we get back to a point where we can Undo() our changes
00937    * by resetting the connections with ClearFlagOnAllObjects() before
00938    * calling Undo() when we are finished.
00939    */
00940   ClearFlagOnAllObjects (true, FOUNDFLAG);
00941   IncrementUndoSerialNumber ();
00942 
00943   length = XYtoNetLength (x, y, &found);
00944   netname = net->Name + 2;
00945 
00946   ClearFlagOnAllObjects (false, FOUNDFLAG);
00947   Undo (true);
00948 
00949   if (!found)
00950     {
00951       if (net_found)
00952         gui->log (_("Net found, but no lines or arcs were flagged.\n"));
00953       else
00954         gui->log (_("Net not found.\n"));
00955 
00956       return 1;
00957     }
00958 
00959   {
00960     char buf[50];
00961     pcb_snprintf(buf, 50, _("%$m*"), Settings.grid_unit->suffix, length);
00962     if (netname)
00963       gui->log (_("Net \"%s\" length: %s\n"), netname, buf);
00964     else
00965       gui->log (_("Net length: %s\n"), buf);
00966   }
00967 
00968   return 0;
00969 }
00970 
00971 static const char report_syntax[] =
00972   N_("Report(Object|DrillReport|FoundPins|NetLength|AllNetLengths|[,name])");
00973 
00974 static const char report_help[] = N_("Produce various report.");
00975 
00976 /* %start-doc actions Report
00977 
00978 @table @code
00979 
00980 @item Object
00981 The object under the crosshair will be reported, describing various
00982 aspects of the object.
00983 
00984 @item DrillReport
00985 A report summarizing the number of drill sizes used, and how many of
00986 each, will be produced.
00987 
00988 @item FoundPins
00989 A report listing all pins and pads which are marked as ``found'' will
00990 be produced.
00991 
00992 @item NetLength
00993 The name and length of the net under the crosshair will be reported to
00994 the message log.
00995 
00996 @item AllNetLengths
00997 The name and length of the net under the crosshair will be reported to
00998 the message log.  An optional parameter specifies mm, mil, pcb, or in
00999 units
01000 
01001 @end table
01002 
01003 %end-doc */
01007 static int
01008 Report (int argc, char **argv, Coord x, Coord y)
01009 {
01010   if ((argc < 1) || (argc > 2))
01011     AUSAGE (report);
01012   else if (strcasecmp (argv[0], "Object") == 0)
01013     {
01014       gui->get_coords (_("Click on an object"), &x, &y);
01015       return ReportDialog (argc - 1, argv + 1, x, y);
01016     }
01017   else if (strcasecmp (argv[0], "DrillReport") == 0)
01018     return ReportDrills (argc - 1, argv + 1, x, y);
01019   else if (strcasecmp (argv[0], "FoundPins") == 0)
01020     return ReportFoundPins (argc - 1, argv + 1, x, y);
01021   else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 1))
01022     return ReportNetLength (argc - 1, argv + 1, x, y);
01023   else if (strcasecmp (argv[0], "AllNetLengths") == 0)
01024     return ReportAllNetLengths (argc - 1, argv + 1, x, y);
01025   else if ((strcasecmp (argv[0], "NetLength") == 0) && (argc == 2))
01026     return ReportNetLengthByName (argv[1], x, y);
01027   else if (argc == 2)
01028     AUSAGE (report);
01029   else
01030     AFAIL (report);
01031   return 1;
01032 }
01033 
01034 HID_Action report_action_list[] = {
01035   {"ReportObject", N_("Click on an object"), ReportDialog,
01036    reportdialog_help, reportdialog_syntax}
01037   ,
01038   {"Report", 0, Report,
01039    report_help, report_syntax}
01040 };
01041 
01042 REGISTER_ACTIONS (report_action_list)