From 91eb5fc3294d92878119ae1cbd1444a8192fc751 Mon Sep 17 00:00:00 2001 From: gherkin Date: Fri, 30 Oct 2026 13:06:37 +0100 Subject: [PATCH 1/4] The Allegro export escapes \, " and % in net names Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UCbPYsA5LYNX8HZaGGZCpK --- allegro-skills/exportJson.il | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/allegro-skills/exportJson.il b/allegro-skills/exportJson.il index 5bec3fb..b393c68 100644 --- a/allegro-skills/exportJson.il +++ b/allegro-skills/exportJson.il @@ -60,6 +60,15 @@ procedure( addIndent( string @optional ( tabs 1 ) ) ) ) +; Names such as nets can contain " or \, which must be escaped in JSON strings. +; % is doubled because writeJsonFile passes each line to fprintf as the format. +procedure( jsonEscape( text ) + unless( text text = "" ) + text = pcreReplace( pcreCompile( "\\\\" ) text "\\\\\\\\" 0 ) + text = pcreReplace( pcreCompile( "\"" ) text "\\\\\"" 0 ) + pcreReplace( pcreCompile( "%" ) text "%%" 0 ) +) + ; ### segment ; ```js @@ -88,7 +97,7 @@ procedure( addLine( segment width @optional ( isEtch nil ) ( netName nil) ) ) element = strcat( - "{\n\t\"net\": \"" netName "\",\n" + "{\n\t\"net\": \"" jsonEscape( netName ) "\",\n" ) width = segment->width @@ -222,7 +231,7 @@ procedure( addArc( segment width @optional ( isEtch nil ) ( netName nil ) ) netName = segment->net->name ) element = strcat( - "{\n\t\"net\": \"" netName "\",\n" + "{\n\t\"net\": \"" jsonEscape( netName ) "\",\n" "\t\"center\": [" sprintf( nil "%f" xCenter ) ", " sprintf( nil "%f" yCenter ) "],\n" ) @@ -512,7 +521,7 @@ procedure( addVia( via layer renderViaHole ) element = strcat( "{\n" - "\t\"net\": \"" via->net->name "\",\n" + "\t\"net\": \"" jsonEscape( via->net->name ) "\",\n" "\t\"start\": " startEnd ",\n" "\t\"end\": " startEnd ",\n" "\t\"width\": " sprintf( nil "%f" width ) @@ -974,7 +983,7 @@ procedure( createZone( zone ) "{\n" "\t\"svgpath\": \"" buildString( car( svgpath ) " " ) "\",\n" "\t\"fillrule\": \"evenodd\",\n" - "\t\"net\": \"" net "\"\n" + "\t\"net\": \"" jsonEscape( net ) "\"\n" "}" ) ) @@ -1325,7 +1334,7 @@ procedure( addPad( pin ) ;add netname, if present if( ( net != "" ) && ( net != nil ) then - net = strcat( "\"" net "\"" ) + net = strcat( "\"" jsonEscape( net ) "\"" ) else net = "\"\"" ) @@ -2847,9 +2856,9 @@ procedure( addNets() foreach( net nets if( boundp( 'netList ) then - tconc( netList strcat( "\"" net->name "\"" ) ) + tconc( netList strcat( "\"" jsonEscape( net->name ) "\"" ) ) else - netList = tconc( nil strcat( "\"" net->name "\"" ) ) + netList = tconc( nil strcat( "\"" jsonEscape( net->name ) "\"" ) ) ) ) From 56ded7ca5efd92548fc383872dfc2f4964870870 Mon Sep 17 00:00:00 2001 From: gherkin Date: Fri, 30 Oct 2026 13:07:26 +0100 Subject: [PATCH 2/4] The Allegro export falls back to empty rev and company when run without a GUI Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UCbPYsA5LYNX8HZaGGZCpK --- allegro-skills/exportJson.il | 3 +++ 1 file changed, 3 insertions(+) diff --git a/allegro-skills/exportJson.il b/allegro-skills/exportJson.il index b393c68..86321aa 100644 --- a/allegro-skills/exportJson.il +++ b/allegro-skills/exportJson.il @@ -13,10 +13,13 @@ procedure( addMetadata( dsnName rev company ) when( rev == "" rev = axlUIPrompt( strcat( "Enter Revision for " dsnName ":" ) ) + ; axlUIPrompt returns nil when run without a GUI + unless( rev rev = "" ) ) when( company == "" company = axlUIPrompt( "Enter company name:" ) + unless( company company = "" ) ) rev = strcat( "\"" rev "\"" ) From 531be4d1a38770aa5a37a9006ee739707c0880cb Mon Sep 17 00:00:00 2001 From: gherkin Date: Sat, 31 Oct 2026 11:22:05 +0100 Subject: [PATCH 3/4] The Allegro export writes each line with a %s format and escapes every string field fprintf no longer reads output lines as its format, so % needs no doubling. jsonEscape now also covers metadata, board text, pin numbers, and the component table. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UCbPYsA5LYNX8HZaGGZCpK --- allegro-skills/exportJson.il | 40 ++++++++++++------------------------ 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/allegro-skills/exportJson.il b/allegro-skills/exportJson.il index 86321aa..ec9c9e0 100644 --- a/allegro-skills/exportJson.il +++ b/allegro-skills/exportJson.il @@ -22,10 +22,10 @@ procedure( addMetadata( dsnName rev company ) unless( company company = "" ) ) - rev = strcat( "\"" rev "\"" ) - company = strcat( "\"" company "\"" ) + rev = strcat( "\"" jsonEscape( rev ) "\"" ) + company = strcat( "\"" jsonEscape( company ) "\"" ) - dsnName = strcat( "\"" upperCase( dsnName ) "\"" ) + dsnName = strcat( "\"" jsonEscape( upperCase( dsnName ) ) "\"" ) currentTime = timeToTm( stringToTime( getCurrentTime() ) ) day = sprintf( nil "%d" currentTime->tm_mday ) @@ -64,12 +64,10 @@ procedure( addIndent( string @optional ( tabs 1 ) ) ) ; Names such as nets can contain " or \, which must be escaped in JSON strings. -; % is doubled because writeJsonFile passes each line to fprintf as the format. procedure( jsonEscape( text ) unless( text text = "" ) text = pcreReplace( pcreCompile( "\\\\" ) text "\\\\\\\\" 0 ) - text = pcreReplace( pcreCompile( "\"" ) text "\\\\\"" 0 ) - pcreReplace( pcreCompile( "%" ) text "%%" 0 ) + pcreReplace( pcreCompile( "\"" ) text "\\\\\"" 0 ) ) ; ### segment @@ -342,9 +340,7 @@ procedure( addText( object @optional ( textType t ) ) width = textBlock->width thickness = textBlock->photoWidth - ; escape " correctly" - pattern = pcreCompile("[\\\"]") - text = pcreReplace( pattern object->text "\\\\\"" 0 ) + text = jsonEscape( object->text ) element = strcat( "{\n" @@ -1315,7 +1311,7 @@ procedure( addPad( pin ) let( ( ( padLayers 'unbound ) ( drill 'unbound ) ( pads 'unbound ) - xy xPos yPos pos net angle pinOnes pinNumber pattern + xy xPos yPos pos net angle pinOnes pinNumber type padstack startEnd layers pad drillShape drillSize bBox xSize ySize size shape svgpath innerDiameter outerDiameter startInner startOuter ) @@ -1618,10 +1614,8 @@ procedure( addPad( pin ) ; pin number as named in the netlist, on both pads of a through-hole pair when( pinNumber && ( pinNumber != "" ) - ; escape " correctly - pattern = pcreCompile( "[\\\"]" ) pad = strcat( pad ",\n" - "\"name\": \"" pcreReplace( pattern pinNumber "\\\\\"" 0 ) "\"" + "\"name\": \"" jsonEscape( pinNumber ) "\"" ) ) @@ -1661,10 +1655,8 @@ procedure( addPad( pin ) ) procedure( addComponentTable( @optional ( variantSymbolList 'unbound ) ( alternateParts 'unbound ) ) - let( ( pattern dsn symbols refDes layer value ( extraFields 'unbound ) userProperties propertyType extraField component ( components 'unbound ) ) + let( ( dsn symbols refDes layer value ( extraFields 'unbound ) userProperties propertyType extraField component ( components 'unbound ) ) - pattern = pcreCompile( "[\\\"]" ) - percent = pcreCompile( "%" ) dsn = axlDBGetDesign() ; get symbols @@ -1696,9 +1688,7 @@ procedure( addComponentTable( @optional ( variantSymbolList 'unbound ) ( alterna value = "undefined" ) - ; escape " correctly" - value = pcreReplace( percent value "%%" 0 ) - value = pcreReplace( pattern value "\\\\\"" 0 ) + value = jsonEscape( value ) ;dnp when( boundp( 'variantSymbolList ) @@ -1734,10 +1724,7 @@ procedure( addComponentTable( @optional ( variantSymbolList 'unbound ) ( alterna ) ) - extraField = strcat( "\"" sprintf( nil "%L" car( userProperty) ) "\": \"" pcreReplace( pattern userPropertyValue "\\\\\"" 0 ) "\"" ) - - ; escape fields correctly - extraField = pcreReplace( percent extraField "%%" 0 ) + extraField = strcat( "\"" jsonEscape( sprintf( nil "%L" car( userProperty) ) ) "\": \"" jsonEscape( userPropertyValue ) "\"" ) if( boundp( 'extraFields ) then tconc( extraFields extraField ) @@ -1757,9 +1744,9 @@ procedure( addComponentTable( @optional ( variantSymbolList 'unbound ) ( alterna component = strcat( "{\n" addIndent( strcat( "\"attr\": \"\",\n" - "\"footprint\": \"" symbol->component->package "\",\n" + "\"footprint\": \"" jsonEscape( symbol->component->package ) "\",\n" "\"layer\": " layer ",\n" - "\"ref\": \"" refDes "\",\n" + "\"ref\": \"" jsonEscape( refDes ) "\",\n" "\"val\": \"" value "\",\n" "\"extra_fields\": {\n" extraFields @@ -2930,8 +2917,7 @@ procedure( writeJsonFile( @key ( variant nil ) ( symbols 'unbound ) ( alternateP lines = parseString( pcbData "\n" ) foreach( line lines - fprintf( outPort line ) - fprintf( outPort "\n" ) + fprintf( outPort "%s\n" line ) ) close( outPort ) From 67b77c1806b8df71de4bbd0eb6bf337b827dcd1d Mon Sep 17 00:00:00 2001 From: gherkin Date: Sat, 31 Oct 2026 11:35:23 +0100 Subject: [PATCH 4/4] The Allegro export escapes the footprint ref and inner layer names Both were still written raw after jsonEscape was applied to the other string fields. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UCbPYsA5LYNX8HZaGGZCpK --- allegro-skills/exportJson.il | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/allegro-skills/exportJson.il b/allegro-skills/exportJson.il index ec9c9e0..315d070 100644 --- a/allegro-skills/exportJson.il +++ b/allegro-skills/exportJson.il @@ -1913,7 +1913,7 @@ procedure( addFootprint( symbol ) yRelPos = 0.0 general = strcat( - "\"ref\": \"" refdes "\",\n" + "\"ref\": \"" jsonEscape( refdes ) "\",\n" "\"center\": [" sprintf( nil "%f" xCoord( xy ) ) ", " sprintf( nil "%f" -yCoord( xy ) ) "],\n" "\"bbox\": {\n" "\t\"pos\": [" sprintf( nil "%f" xPos ) ", " sprintf( nil "%f" yPos ) "],\n" @@ -2786,7 +2786,7 @@ procedure( addCopper( exportInnerLayers renderViaHoles ) ) ( t - layerName = layer + layerName = jsonEscape( layer ) ) )