report_checks -format json

commit d4337917f790d90d7e16d068a19d9d9f8f44b760
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Sep 16 16:42:19 2024 -0700

    report json

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit a2707db7210437e4afc7b6af2c0b1e5cbdc0fa2a
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Sep 16 15:45:08 2024 -0700

    report json

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit f9d511250046a5d3c5105299e42cdc4d75ccdfef
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Sep 15 18:42:39 2024 -0700

    report json

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 82d4d5002e9d134396d199e56d47ea3fdee08a16
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sat Sep 14 20:44:45 2024 -0700

    report json

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2024-09-16 17:04:31 -07:00
parent d2ffbffe2e
commit bae1afcb11
15 changed files with 285 additions and 157 deletions

Binary file not shown.

Binary file not shown.

View File

@ -40,7 +40,7 @@ public:
size_t size() const { return paths_.size(); }
// path(0) is the startpoint.
// path(size()-1) is the endpoint.
PathRef *path(size_t index);
const PathRef *path(size_t index) const;
TimingArc *prevArc(size_t index);
// Returns the path start point.
// Register/Latch Q pin

View File

@ -905,9 +905,9 @@ public:
// Previous path end is used to detect path group changes
// so headers are reported by group.
void reportPathEnd(PathEnd *end,
PathEnd *prev_end);
PathEnd *prev_end,
bool last);
void reportPathEnd(PathEnd *end);
void reportPathEnds(PathEndSeq *ends);
ReportPath *reportPath() { return report_path_; }
void reportPath(Path *path);

View File

@ -169,6 +169,11 @@ void
stringPrint(string &str,
const char *fmt,
...) __attribute__((format (printf, 2, 3)));
// Formated append to std::string.
void
stringAppend(string &str,
const char *fmt,
...) __attribute__((format (printf, 2, 3)));
// Print to a temporary string.
char *

View File

@ -146,8 +146,8 @@ PathExpanded::startIndex() const
return pathsIndex(start_index_);
}
PathRef *
PathExpanded::path(size_t index)
const PathRef *
PathExpanded::path(size_t index) const
{
if (index < paths_.size())
return &paths_[pathsIndex(index)];

View File

@ -1260,7 +1260,7 @@ getProperty(PathEnd *end,
PathExpanded expanded(end->path(), sta);
PathRefSeq paths;
for (auto i = expanded.startIndex(); i < expanded.size(); i++) {
PathRef *path = expanded.path(i);
const PathRef *path = expanded.path(i);
paths.push_back(*path);
}
return PropertyValue(&paths);

View File

@ -269,58 +269,16 @@ ReportPath::setReportSigmas(bool report)
////////////////////////////////////////////////////////////////
void
ReportPath::reportPathEndHeader()
{
switch (format_) {
case ReportPathFormat::full:
case ReportPathFormat::full_clock:
case ReportPathFormat::full_clock_expanded:
case ReportPathFormat::shorter:
case ReportPathFormat::endpoint:
break;
case ReportPathFormat::summary:
reportSummaryHeader();
break;
case ReportPathFormat::slack_only:
reportSlackOnlyHeader();
break;
default:
report_->critical(1470, "unsupported path type");
break;
}
}
void
ReportPath::reportPathEndFooter()
{
string header;
switch (format_) {
case ReportPathFormat::full:
case ReportPathFormat::full_clock:
case ReportPathFormat::full_clock_expanded:
case ReportPathFormat::shorter:
break;
case ReportPathFormat::endpoint:
case ReportPathFormat::summary:
case ReportPathFormat::slack_only:
reportBlankLine();
break;
default:
report_->critical(1471, "unsupported path type");
break;
}
}
void
ReportPath::reportPathEnd(PathEnd *end)
{
reportPathEnd(end, nullptr);
reportPathEnd(end, nullptr, true);
}
void
ReportPath::reportPathEnd(PathEnd *end,
PathEnd *prev_end)
PathEnd *prev_end,
bool last)
{
switch (format_) {
case ReportPathFormat::full:
@ -345,8 +303,8 @@ ReportPath::reportPathEnd(PathEnd *end,
case ReportPathFormat::slack_only:
reportSlackOnly(end);
break;
default:
report_->critical(1473, "unsupported path type");
case ReportPathFormat::json:
reportJson(end, last);
break;
}
}
@ -367,6 +325,49 @@ ReportPath::reportPathEnds(PathEndSeq *ends)
reportPathEndFooter();
}
void
ReportPath::reportPathEndHeader()
{
switch (format_) {
case ReportPathFormat::full:
case ReportPathFormat::full_clock:
case ReportPathFormat::full_clock_expanded:
case ReportPathFormat::shorter:
case ReportPathFormat::endpoint:
break;
case ReportPathFormat::summary:
reportSummaryHeader();
break;
case ReportPathFormat::slack_only:
reportSlackOnlyHeader();
break;
case ReportPathFormat::json:
reportJsonHeader();
break;
}
}
void
ReportPath::reportPathEndFooter()
{
string header;
switch (format_) {
case ReportPathFormat::full:
case ReportPathFormat::full_clock:
case ReportPathFormat::full_clock_expanded:
case ReportPathFormat::shorter:
break;
case ReportPathFormat::endpoint:
case ReportPathFormat::summary:
case ReportPathFormat::slack_only:
reportBlankLine();
break;
case ReportPathFormat::json:
reportJsonFooter();
break;
}
}
void
ReportPath::reportEndpointHeader(PathEnd *end,
PathEnd *prev_end)
@ -1043,6 +1044,146 @@ ReportPath::pathEndpoint(PathEnd *end)
////////////////////////////////////////////////////////////////
void
ReportPath::reportJsonHeader()
{
report_->reportLine("{\"checks\": [");
}
void
ReportPath::reportJsonFooter()
{
report_->reportLine("]");
report_->reportLine("}");
}
void
ReportPath::reportJson(const PathEnd *end,
bool last)
{
string result;
result += "{\n";
stringAppend(result, " \"type\": \"%s\",\n", end->typeName());
stringAppend(result, " \"path_group\": \"%s\",\n",
search_->pathGroup(end)->name());
stringAppend(result, " \"path_type\": \"%s\",\n",
end->minMax(this)->asString());
PathExpanded expanded(end->path(), this);
const Pin *startpoint = expanded.startPath()->vertex(this)->pin();
const Pin *endpoint = expanded.endPath()->vertex(this)->pin();
stringAppend(result, " \"startpoint\": \"%s\",\n",
network_->pathName(startpoint));
stringAppend(result, " \"endpoint\": \"%s\",\n",
network_->pathName(endpoint));
const ClockEdge *src_clk_edge = end->sourceClkEdge(this);
const PathVertex *tgt_clk_path = end->targetClkPath();
if (src_clk_edge) {
stringAppend(result, " \"source_clock\": \"%s\",\n",
src_clk_edge->clock()->name());
stringAppend(result, " \"source_clock_edge\": \"%s\",\n",
src_clk_edge->transition()->name());
}
reportJson(expanded, "source_path", 2, !end->isUnconstrained(), result);
const ClockEdge *tgt_clk_edge = end->targetClkEdge(this);
if (tgt_clk_edge) {
stringAppend(result, " \"target_clock\": \"%s\",\n",
tgt_clk_edge->clock()->name());
stringAppend(result, " \"target_clock_edge\": \"%s\",\n",
tgt_clk_edge->transition()->name());
}
if (tgt_clk_path)
reportJson(end->targetClkPath(), "target_clock_path", 2, true, result);
if (end->checkRole(this)) {
stringAppend(result, " \"data_arrival_time\": %.3e,\n",
end->dataArrivalTimeOffset(this));
const MultiCyclePath *mcp = end->multiCyclePath();
if (mcp)
stringAppend(result, " \"multi_cycle_path\": %d,\n",
mcp->pathMultiplier());
PathDelay *path_delay = end->pathDelay();
if (path_delay)
stringAppend(result, " \"path_delay\": %.3e,\n",
path_delay->delay());
stringAppend(result, " \"crpr\": %.3e,\n", end->checkCrpr(this));
stringAppend(result, " \"margin\": %.3e,\n", end->margin(this));
stringAppend(result, " \"required_time\": %.3e,\n",
end->requiredTimeOffset(this));
stringAppend(result, " \"slack\": %.3e\n", end->slack(this));
}
result += "}";
if (!last)
result += ",";
report_->reportLineString(result);
}
void
ReportPath::reportJson(const Path *path)
{
string result;
result += "{\n";
reportJson(path, "path", 0, false, result);
result += "}\n";
report_->reportLineString(result);
}
void
ReportPath::reportJson(const Path *path,
const char *path_name,
int indent,
bool trailing_comma,
string &result)
{
PathExpanded expanded(path, this);
reportJson(expanded, path_name, indent, trailing_comma, result);
}
void
ReportPath::reportJson(const PathExpanded &expanded,
const char *path_name,
int indent,
bool trailing_comma,
string &result)
{
stringAppend(result, "%*s\"%s\": [\n", indent, "", path_name);
for (size_t i = 0; i < expanded.size(); i++) {
const PathRef *path = expanded.path(i);
const Pin *pin = path->vertex(this)->pin();
stringAppend(result, "%*s {\n", indent, "");
stringAppend(result, "%*s \"pin\": \"%s\",\n",
indent, "",
network_->pathName(pin));
double x, y;
bool exists;
network_->location(pin, x, y, exists);
if (exists) {
stringAppend(result, "%*s \"x\": %.9f,\n", indent, "", x);
stringAppend(result, "%*s \"y\": %.9f,\n", indent, "", y);
}
stringAppend(result, "%*s \"arrival\": %.3e,\n",
indent, "",
delayAsFloat(path->arrival(this)));
stringAppend(result, "%*s \"slew\": %.3e\n",
indent, "",
delayAsFloat(path->slew(this)));
stringAppend(result, "%*s }%s\n",
indent, "",
(i < expanded.size() - 1) ? "," : "");
}
stringAppend(result, "%*s]%s\n",
indent, "",
trailing_comma ? "," : "");
}
////////////////////////////////////////////////////////////////
void
ReportPath::reportSlackOnlyHeader()
{
@ -2365,12 +2506,13 @@ ReportPath::reportPath(const Path *path)
reportPathFull(path);
break;
case ReportPathFormat::json:
reportPathJson(path);
reportJson(path);
break;
case ReportPathFormat::shorter:
case ReportPathFormat::endpoint:
case ReportPathFormat::summary:
case ReportPathFormat::slack_only:
default:
report_->critical(1474, "unsupported path type");
report_->reportLine("Format not supported.");
break;
}
}
@ -2383,54 +2525,7 @@ ReportPath::reportPathFull(const Path *path)
reportSrcClkAndPath(path, expanded, 0.0, delay_zero, delay_zero, false);
}
void
ReportPath::reportPathJson(const Path *path)
{
report_->reportLine("{ \"path\": [");
PathExpanded expanded(path, this);
for (auto i = expanded.startIndex(); i < expanded.size(); i++) {
string line;
PathRef *path = expanded.path(i);
const Pin *pin = path->vertex(this)->pin();
report_->reportLine(" {");
line = " \"pin\": \"";
line += network_->pathName(pin);
line += "\",";
report_->reportLineString(line);
double x, y;
bool exists;
string tmp;
network_->location(pin, x, y, exists);
if (exists) {
line = " \"x\": ";
stringPrint(tmp, "%.9f", x);
line += tmp + ",\n";
line += " \"y\": ";
stringPrint(tmp, "%.9f", y);
line += tmp + ",";
report_->reportLineString(line);
}
line = " \"arrival\": ";
stringPrint(tmp, "%.3e", delayAsFloat(path->arrival(this)));
line += tmp;
line += ",";
report_->reportLineString(line);
line = " \"slew\": ";
stringPrint(tmp, "%.3e", delayAsFloat(path->slew(this)));
line += tmp;
report_->reportLineString(line);
line = " }";
if (i < expanded.size() - 1)
line += ",";
report_->reportLineString(line);
}
report_->reportLine(" ]");
report_->reportLine("}");
}
////////////////////////////////////////////////////////////////
void
ReportPath::reportPath1(const Path *path,
@ -2517,7 +2612,7 @@ ReportPath::reportPath4(const Path *path,
Arrival prev_time(0.0);
if (skip_first_path) {
path_first_index = 1;
PathRef *start = expanded.path(0);
const PathRef *start = expanded.path(0);
prev_time = start->arrival(this) + time_offset;
}
size_t path_last_index = expanded.size() - 1;
@ -2551,7 +2646,7 @@ ReportPath::reportPath5(const Path *path,
expanded.clkPath(clk_path);
Vertex *clk_start = clk_path.vertex(this);
for (size_t i = path_first_index; i <= path_last_index; i++) {
PathRef *path1 = expanded.path(i);
const PathRef *path1 = expanded.path(i);
TimingArc *prev_arc = expanded.prevArc(i);
Vertex *vertex = path1->vertex(this);
Pin *pin = vertex->pin();

View File

@ -61,7 +61,8 @@ public:
// Previous path end is used to detect path group changes
// so headers are reported by group.
void reportPathEnd(PathEnd *end,
PathEnd *prev_end);
PathEnd *prev_end,
bool last);
void reportPathEnds(PathEndSeq *ends);
void reportPath(const Path *path);
@ -81,6 +82,22 @@ public:
void reportFull(const PathEndGatedClock *end);
void reportFull(const PathEndDataCheck *end);
void reportJsonHeader();
void reportJsonFooter();
void reportJson(const PathEnd *end,
bool last);
void reportJson(const Path *path);
void reportJson(const Path *path,
const char *path_name,
int indent,
bool trailing_comma,
string &result);
void reportJson(const PathExpanded &expanded,
const char *path_name,
int indent,
bool trailing_comma,
string &result);
void reportEndHeader();
void reportEndLine(PathEnd *end);
@ -264,7 +281,6 @@ protected:
void reportPath(const PathEnd *end,
PathExpanded &expanded);
void reportPathFull(const Path *path);
void reportPathJson(const Path *path);
void reportPathHeader();
void reportPath1(const Path *path,
PathExpanded &expanded,

View File

@ -480,9 +480,10 @@ report_path_end(PathEnd *end)
void
report_path_end2(PathEnd *end,
PathEnd *prev_end)
PathEnd *prev_end,
bool last)
{
Sta::sta()->reportPathEnd(end, prev_end);
Sta::sta()->reportPathEnd(end, prev_end, last);
}
void

View File

@ -1069,9 +1069,12 @@ proc parse_path_group_arg { group_names } {
proc report_path_ends { path_ends } {
report_path_end_header
set prev_end "NULL"
set end_count [llength $path_ends]
set i 0
foreach path_end $path_ends {
report_path_end2 $path_end $prev_end
report_path_end2 $path_end $prev_end [expr $i == ($end_count - 1)]
set prev_end $path_end
incr i
}
report_path_end_footer
}

View File

@ -2518,12 +2518,6 @@ Sta::setReportPathSigmas(bool report_sigmas)
report_path_->setReportSigmas(report_sigmas);
}
void
Sta::reportPathEnds(PathEndSeq *ends)
{
report_path_->reportPathEnds(ends);
}
void
Sta::reportPathEndHeader()
{
@ -2544,9 +2538,10 @@ Sta::reportPathEnd(PathEnd *end)
void
Sta::reportPathEnd(PathEnd *end,
PathEnd *prev_end)
PathEnd *prev_end,
bool last)
{
report_path_->reportPathEnd(end, prev_end);
report_path_->reportPathEnd(end, prev_end, last);
}
void

View File

@ -84,10 +84,10 @@ private:
float maxTime();
float pathMaxTime();
void writeMeasureDelayStmt(Stage stage,
Path *from_path,
Path *to_path);
const Path *from_path,
const Path *to_path);
void writeMeasureSlewStmt(Stage stage,
Path *path);
const Path *path);
void writeInputWaveform();
void writeClkWaveform();
@ -110,9 +110,9 @@ private:
int stageGateInputPathIndex(Stage stage);
int stageDrvrPathIndex(Stage stage);
int stageLoadPathIndex(Stage stage);
PathRef *stageGateInputPath(Stage stage);
PathRef *stageDrvrPath(Stage stage);
PathRef *stageLoadPath(Stage stage);
const PathRef *stageGateInputPath(Stage stage);
const PathRef *stageDrvrPath(Stage stage);
const PathRef *stageLoadPath(Stage stage);
TimingArc *stageGateArc(Stage stage);
TimingArc *stageWireArc(Stage stage);
Edge *stageGateEdge(Stage stage);
@ -128,8 +128,8 @@ private:
LibertyCell *stageLibertyCell(Stage stage);
Instance *stageInstance(Stage stage);
float findSlew(Path *path);
float findSlew(Path *path,
float findSlew(const Path *path);
float findSlew(const Path *path,
const RiseFall *rf,
TimingArc *next_arc);
Path *path_;
@ -240,7 +240,7 @@ float
WritePathSpice::maxTime()
{
Stage input_stage = stageFirst();
PathRef *input_path = stageDrvrPath(input_stage);
const PathRef *input_path = stageDrvrPath(input_stage);
if (input_path->isClock(this)) {
const Clock *clk = input_path->clock(this);
float period = clk->period();
@ -258,7 +258,7 @@ WritePathSpice::pathMaxTime()
{
float max_time = 0.0;
for (size_t i = 0; i < path_expanded_.size(); i++) {
PathRef *path = path_expanded_.path(i);
const PathRef *path = path_expanded_.path(i);
const RiseFall *rf = path->transition(this);
Vertex *vertex = path->vertex(this);
float path_max_slew = railToRailSlew(findSlew(vertex,rf,nullptr), rf);
@ -315,7 +315,7 @@ WritePathSpice::writeInputSource()
streamPrint(spice_stream_, "**************\n\n");
Stage input_stage = stageFirst();
PathRef *input_path = stageDrvrPath(input_stage);
const PathRef *input_path = stageDrvrPath(input_stage);
if (input_path->isClock(this))
writeClkWaveform();
else
@ -327,7 +327,7 @@ void
WritePathSpice::writeInputWaveform()
{
Stage input_stage = stageFirst();
PathRef *input_path = stageDrvrPath(input_stage);
const PathRef *input_path = stageDrvrPath(input_stage);
const RiseFall *rf = input_path->transition(this);
TimingArc *next_arc = stageGateArc(input_stage + 1);
float slew0 = findSlew(input_path, rf, next_arc);
@ -352,7 +352,7 @@ void
WritePathSpice::writeClkWaveform()
{
Stage input_stage = stageFirst();
PathRef *input_path = stageDrvrPath(input_stage);
const PathRef *input_path = stageDrvrPath(input_stage);
TimingArc *next_arc = stageGateArc(input_stage + 1);
const ClockEdge *clk_edge = input_path->clkEdge(this);
@ -387,7 +387,7 @@ WritePathSpice::writeClkWaveform()
}
float
WritePathSpice::findSlew(Path *path)
WritePathSpice::findSlew(const Path *path)
{
Vertex *vertex = path->vertex(this);
const RiseFall *rf = path->transition(this);
@ -395,9 +395,9 @@ WritePathSpice::findSlew(Path *path)
}
float
WritePathSpice::findSlew(Path *path,
const RiseFall *rf,
TimingArc *next_arc)
WritePathSpice::findSlew(const Path *path,
const RiseFall *rf,
TimingArc *next_arc)
{
Vertex *vertex = path->vertex(this);
return findSlew(vertex, rf, next_arc);
@ -413,9 +413,9 @@ WritePathSpice::writeMeasureStmts()
streamPrint(spice_stream_, "********************\n\n");
for (Stage stage = stageFirst(); stage <= stageLast(); stage++) {
PathRef *gate_input_path = stageGateInputPath(stage);
PathRef *drvr_path = stageDrvrPath(stage);
PathRef *load_path = stageLoadPath(stage);
const PathRef *gate_input_path = stageGateInputPath(stage);
const PathRef *drvr_path = stageDrvrPath(stage);
const PathRef *load_path = stageLoadPath(stage);
if (gate_input_path) {
// gate input -> gate output
writeMeasureSlewStmt(stage, gate_input_path);
@ -432,8 +432,8 @@ WritePathSpice::writeMeasureStmts()
void
WritePathSpice::writeMeasureDelayStmt(Stage stage,
Path *from_path,
Path *to_path)
const Path *from_path,
const Path *to_path)
{
writeMeasureDelayStmt(from_path->pin(this), from_path->transition(this),
to_path->pin(this), to_path->transition(this),
@ -442,7 +442,7 @@ WritePathSpice::writeMeasureDelayStmt(Stage stage,
void
WritePathSpice::writeMeasureSlewStmt(Stage stage,
Path *path)
const Path *path)
{
const Pin *pin = path->pin(this);
const RiseFall *rf = path->transition(this);
@ -514,7 +514,7 @@ WritePathSpice::writeGateStage(Stage stage)
drvr_port->name());
writeSubcktInst(inst);
PathRef *drvr_path = stageDrvrPath(stage);
const PathRef *drvr_path = stageDrvrPath(stage);
const RiseFall *drvr_rf = drvr_path->transition(this);
Edge *gate_edge = stageGateEdge(stage);
@ -544,7 +544,7 @@ WritePathSpice::writeGateStage(Stage stage)
void
WritePathSpice::writeStageParasitics(Stage stage)
{
PathRef *drvr_path = stageDrvrPath(stage);
const PathRef *drvr_path = stageDrvrPath(stage);
DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(this);
ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt();
const Pin *drvr_pin = stageDrvrPin(stage);
@ -637,21 +637,21 @@ WritePathSpice::stageLoadPathIndex(Stage stage)
return stage * 2 - 1;
}
PathRef *
const PathRef *
WritePathSpice::stageGateInputPath(Stage stage)
{
int path_index = stageGateInputPathIndex(stage);
return path_expanded_.path(path_index);
}
PathRef *
const PathRef *
WritePathSpice::stageDrvrPath(Stage stage)
{
int path_index = stageDrvrPathIndex(stage);
return path_expanded_.path(path_index);
}
PathRef *
const PathRef *
WritePathSpice::stageLoadPath(Stage stage)
{
int path_index = stageLoadPathIndex(stage);
@ -678,7 +678,7 @@ WritePathSpice::stageWireArc(Stage stage)
Edge *
WritePathSpice::stageGateEdge(Stage stage)
{
PathRef *path = stageDrvrPath(stage);
const PathRef *path = stageDrvrPath(stage);
TimingArc *arc = stageGateArc(stage);
return path->prevEdge(arc, this);
}
@ -686,7 +686,7 @@ WritePathSpice::stageGateEdge(Stage stage)
Edge *
WritePathSpice::stageWireEdge(Stage stage)
{
PathRef *path = stageLoadPath(stage);
const PathRef *path = stageLoadPath(stage);
TimingArc *arc = stageWireArc(stage);
return path->prevEdge(arc, this);
}
@ -694,7 +694,7 @@ WritePathSpice::stageWireEdge(Stage stage)
Pin *
WritePathSpice::stageGateInputPin(Stage stage)
{
PathRef *path = stageGateInputPath(stage);
const PathRef *path = stageGateInputPath(stage);
return path->pin(this);
}
@ -708,7 +708,7 @@ WritePathSpice::stageGateInputPort(Stage stage)
Pin *
WritePathSpice::stageDrvrPin(Stage stage)
{
PathRef *path = stageDrvrPath(stage);
const PathRef *path = stageDrvrPath(stage);
return path->pin(this);
}
@ -722,7 +722,7 @@ WritePathSpice::stageDrvrPort(Stage stage)
Pin *
WritePathSpice::stageLoadPin(Stage stage)
{
PathRef *path = stageLoadPath(stage);
const PathRef *path = stageLoadPath(stage);
return path->pin(this);
}

View File

@ -341,21 +341,20 @@ proc sta_unknown { args } {
}
if { $ret != 0 } {
return -code $ret -errorcode $errorCode \
"error in unknown while checking if \"$name\" is a unique command abbreviation: $msg"
"Error in unknown while checking if \"$name\" is a unique command abbreviation: $msg."
}
if { [llength $cmds] == 1 } {
return [uplevel 1 [lreplace $args 0 0 $cmds]]
}
if { [llength $cmds] > 1 } {
if {[string equal $name ""]} {
return -code error "empty command name \"\""
return -code error "Empty command name \"\""
} else {
return -code error \
"ambiguous command name \"$name\": [lsort $cmds]"
"Ambiguous command name \"$name\": [lsort $cmds]."
}
}
::unknown {*}$args
return [uplevel 1 [::unknown {*}$args]]
}
proc is_bus_subscript { subscript } {

View File

@ -76,6 +76,20 @@ stringPrint(string &str,
str = tmp;
}
void
stringAppend(string &str,
const char *fmt,
...)
{
va_list args;
va_start(args, fmt);
char *tmp;
size_t tmp_length;
stringPrintTmp(fmt, args, tmp, tmp_length);
va_end(args);
str += tmp;
}
string
stdstrPrint(const char *fmt,
...)