mirror of
git://projects.qi-hardware.com/fped.git
synced 2024-11-25 00:35:20 +02:00
3488cf80ec
This cannot be set/changed through the GUI yet.
750 lines
20 KiB
Plaintext
750 lines
20 KiB
Plaintext
fped - Footprint editor
|
|
=======================
|
|
|
|
fped is an editor that allows the interactive creation of footprints of
|
|
electronic components. Footprint definitions are stored in a text format
|
|
that resembles a programming language.
|
|
|
|
The language is constrained such that anything that can be expressed in
|
|
the textual definition also has a straightforward equivalent operation
|
|
that can be performed through the GUI.
|
|
|
|
This README describes only the footprint definition language. A
|
|
description of the GUI can be found here:
|
|
|
|
http://downloads.qi-hardware.com/people/werner/fped/gui.html
|
|
|
|
This work is distributed under the terms of the GNU GENERAL PUBLIC
|
|
LICENSE, Version 2:
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
For your convenience, a copy of the complete license has been included
|
|
in the file COPYING.GPLv2.
|
|
|
|
|
|
Building
|
|
--------
|
|
|
|
Prerequisites:
|
|
|
|
- bash
|
|
- flex
|
|
- bison
|
|
- fig2dev (transfig)
|
|
- ImageMagick
|
|
- Netpbm
|
|
- Gtk+ 2.x development package (libgtk2.0-dev or similar)
|
|
- Liberation Fonts (ttf-liberation or similar)
|
|
|
|
Check out the repository:
|
|
|
|
git clone git://projects.qi-hardware.com/fped.git
|
|
cd fped
|
|
|
|
Get updates:
|
|
|
|
git pull
|
|
|
|
Compile:
|
|
|
|
make
|
|
|
|
Run an example:
|
|
|
|
./fped examples/qfn.fpd
|
|
|
|
|
|
Motivation
|
|
----------
|
|
|
|
KiCad already includes a footprint ("module") editor, so why do we need
|
|
a new one ? The issue with footprint generation for KiCad is that the
|
|
built-in module editor is basically a drawing program that only captures
|
|
the result of the module author's interpretation of a footprint drawing,
|
|
but does not include the steps that led to this construction.
|
|
|
|
Furthermore, accurate measuring of dimensions in the drawing can only be
|
|
done manually in the module editor, which makes review difficult and
|
|
time-consuming.
|
|
|
|
In fped, the construction process is made explicit and each step can be
|
|
expressed in terms of the parameters that appear in the vendor's
|
|
drawing. Dimensions can be explicitly measured and the results can be
|
|
included in the graphical output generated by fped.
|
|
|
|
Directly using parameters and construction steps from the reference
|
|
drawing reduces the risk of mistakes. Visualizing the construction
|
|
process and verificative measurements helps efficient and accurate
|
|
review.
|
|
|
|
|
|
Footprint definition file format
|
|
--------------------------------
|
|
|
|
Footprint definitions are stored in text files. The program "fped" reads
|
|
and (soon) writes such files, visualizes their content, and provides a
|
|
graphical editor for them.
|
|
|
|
The syntax is unique and draws from elements of a variety of languages
|
|
commonly found on unix systems. One specialty is that there are no
|
|
reserved words - the language keywords appear only at the beginning of
|
|
a line and can thus be recognized as such without restricting their use
|
|
for identifiers. This reduces the risk of creating incompatibilities
|
|
with existing designs when introduction future language features.
|
|
|
|
fped uses the C preprocessor for comments, conditional compilation,
|
|
and - to a limited extent - also macros. Long lines can be split by
|
|
ending them with a backslash. If multiple items need to be placed in a
|
|
single line, e.g., in a macro, they can be separated with semicolons.
|
|
|
|
The file has the following structure:
|
|
|
|
frame definitions
|
|
...
|
|
package name
|
|
setup
|
|
objects
|
|
...
|
|
|
|
|
|
Geometry model
|
|
--------------
|
|
|
|
The geometry model consists of frames, vectors, and objects. The shape of
|
|
objects is defined by a number of points. These points are produced by
|
|
concatenating vectors.
|
|
|
|
E.g., to draw a line from (1mm, 1mm) to (2mm, 2mm), one would make a
|
|
vector from the origin to (1mm, 1mm) and one either from the origin or
|
|
from the previous vector to (2mm, 2mm), and then make a line connecting
|
|
the two points.
|
|
|
|
|
|
Setup
|
|
- - -
|
|
|
|
The setup section defines settings that affect the entire footprint.
|
|
It is optional and can contain a "unit" directive and an "allow"
|
|
directive.
|
|
|
|
|
|
Units
|
|
- - -
|
|
|
|
fped can calculate in mm and mil. Units are specified by following a
|
|
number with "mm" or "mil", separated by zero or more spaces or tabs.
|
|
|
|
Examples:
|
|
|
|
1mm
|
|
2 mil
|
|
|
|
Units can be mixed in calculations, e.g.,
|
|
|
|
set a = 1mm+20mil
|
|
set b = 10*1mm
|
|
|
|
All values used as dimensions must be either mm or mil.
|
|
|
|
The default unit can be set with one of the following directives:
|
|
|
|
unit mm
|
|
unit mil
|
|
unit auto
|
|
|
|
If the "unit" directive is omitted, fped defaults to millimeters.
|
|
|
|
When saving a footprint definition, the default unit is set to the
|
|
unit set in the GUI.
|
|
|
|
|
|
Allow
|
|
- - -
|
|
|
|
fped normally disallows overlapping pads. This restriction can be
|
|
relaxed with the "allow" directive.
|
|
|
|
allow touch
|
|
|
|
Allows pads touching but not having more than their border in common.
|
|
|
|
allow overlap
|
|
|
|
Do not check for overlaps at all.
|
|
|
|
If the "allow" directive is omitted, fped defaults to allowing
|
|
neigher overlap nor touch.
|
|
|
|
|
|
Vectors
|
|
- - - -
|
|
|
|
Vectors can be anonymous or they can be named for future reference:
|
|
|
|
vec <base> ( <x-expr>, <y-expr> )
|
|
<identifier>: vec <base> ( <x-expr>, <y-expr> )
|
|
|
|
The base can be one of the following items:
|
|
|
|
- @ is the origin of the frame containing the vector
|
|
- . is the end of the previous vector in this frame
|
|
- <identifier> is the name of a previous vector in the same frame
|
|
|
|
The following example would draw the line described in the previous
|
|
section:
|
|
|
|
a: vec @(1mm, 1mm)
|
|
b: vec .(1mm, 1mm)
|
|
line a b
|
|
|
|
|
|
Silk screen objects
|
|
- - - - - - - - - -
|
|
|
|
The output of fped is a footprint definition that contains pads and silk
|
|
screen drawings (we may add more layers in the future). These items are
|
|
called "objects". Their geometry is defined through points obtained with
|
|
vectors.
|
|
|
|
A line connects two points:
|
|
|
|
line <point-a> <point-b> [<width>]
|
|
|
|
The points can be specified with @, ., and an identifier, just like
|
|
a vector base. The option width specifies the thickness of the silk
|
|
screen line. If omitted, a hard-coded default of 15 mil is used.
|
|
|
|
A rectangle has sides parallel to the x and y axis and is defined
|
|
by two diagonally opposite corners:
|
|
|
|
rect <point-a> <point-b> [<width>]
|
|
|
|
A circle is defined by its center and a point on the circle:
|
|
|
|
circ <center> <point> [<width>]
|
|
|
|
This example draws a unit circle:
|
|
|
|
vec @(1mm, 0mm)
|
|
circ @ .
|
|
|
|
An arc is like a circle, but the part of the circle drawn is determined
|
|
by two points. The first point determines the radius and the starting
|
|
angle. The second point only determines the end angle but its distance
|
|
from the center is ignored.
|
|
|
|
arc <center> <radius> <end> [<width>]
|
|
|
|
The arc is drawn in a counter-clockwise direction. The following example
|
|
draws an arc of the unit circle in the x > 0, y > 0 quadrant:
|
|
|
|
from: vec @(1mm, 0mm)
|
|
to: vec @(0mm, 1mm)
|
|
arc @ from to
|
|
|
|
|
|
Pads
|
|
- -
|
|
|
|
Pads are similar to rectangles, but they also have a name.
|
|
|
|
pad "<name>" <point-a> <point-b> [<type>]
|
|
|
|
Variables can be expanded in a pad's name by prefixing their name with
|
|
a dollar sign. The ${name} syntax is also available.
|
|
|
|
Example:
|
|
|
|
vec @(1mm, 1mm)
|
|
pad "1" @ .
|
|
|
|
Pads normally affect the surface copper layer, the solder mask layer,
|
|
and the solder paste layer. This can be modified with the optional
|
|
type argument:
|
|
|
|
Type Layers
|
|
--------- -------------------------------------
|
|
(default) copper, solder mask, and solder paste
|
|
bare copper and solder mask
|
|
trace copper without solder mask opening
|
|
paste solder paste
|
|
mask solder mask
|
|
|
|
Typical uses:
|
|
- "bare": connectors printed directly on the PCB
|
|
- "trace": connections or antennas
|
|
- "paste": sparse solder paste, e.g., for QFN center pads
|
|
- "mask": non-standard mask openings, e.g., for solder mask defined
|
|
pads
|
|
|
|
|
|
Rounded pads
|
|
- - - - - -
|
|
|
|
Rounded pads are like rectangular pads except that they end with a
|
|
semi-circle at each of the smaller sides of the enclosing rectangle.
|
|
If enclosed in a square, rounded pads form a circle.
|
|
|
|
rpad "<name>" <point-a> <point-b> [<type>]
|
|
|
|
|
|
Holes
|
|
- - -
|
|
|
|
Holes can be used for through-hole pins or for mechanical support.
|
|
In the former case, the hole must be placed inside a pad. Only one
|
|
hole per pad is allowed. Mechanical holes must be outside any pads.
|
|
|
|
Through-hole pads are always present on both sides of the board, i.e.,
|
|
when fped generates a KiCad module, the surface layers of a pad
|
|
containing a hole are propagated to the opposite side of the board.
|
|
|
|
Holes have the same shape as a rounded pad and their geometry is
|
|
defined in the same way:
|
|
|
|
hole <point-a> <point-b>
|
|
|
|
|
|
Measurements
|
|
- - - - - -
|
|
|
|
*** This is obsolete - see the section on new-style mesurements at the end. ***
|
|
|
|
Measurements show the distance between two points:
|
|
|
|
meas <point-a> <point-b> <offset>
|
|
|
|
The offset is the distance from the imaginary line connecting points A
|
|
and B the measurement line is draw:
|
|
|
|
- if the offset is 0mm, the line will connect A and B
|
|
- if the offset is positive, the line would be on the left-hand side when
|
|
traveling from A to B
|
|
- if the offset is negative , the line would be on the right-hand side when
|
|
traveling from A to B
|
|
|
|
Example:
|
|
|
|
a: vec @(-1mm, 1mm)
|
|
b: vec @(1mm, 1mm)
|
|
meas a b 0.2 mm
|
|
|
|
|
|
Package name
|
|
- - - - - -
|
|
|
|
The package name is a non-empty string of printable ASCII characters,
|
|
including spaces. If the "package" directive is omitted, fped defaults
|
|
to using the name "_".
|
|
|
|
package "<name>"
|
|
|
|
Examples:
|
|
|
|
package "48-SSOP"
|
|
package "0603"
|
|
|
|
Like in pad names, variables are expanded in package names. This allows
|
|
the generation of multiple packages from a single definition.
|
|
|
|
|
|
Frames
|
|
- - -
|
|
|
|
Frames are used to group things and to reuse them multiple times. Frames
|
|
must be defined before they can be used:
|
|
|
|
frame <name> {
|
|
... items ...
|
|
}
|
|
|
|
Once defined, a frame is placed at a given location with
|
|
|
|
frame <name> <point>
|
|
|
|
The frame definitions must precede all other items in a footprint
|
|
description. Frames cannot be defined inside other frames, but frames
|
|
can invoke each other recursively.
|
|
|
|
For example, this puts two unity squares, one centered at (0 mm, 0 mm),
|
|
the other at (2 mm, 0 mm):
|
|
|
|
frame unit_square {
|
|
a: vec @(-0.5mm, -0.5mm)
|
|
b: vec .(1mm, 1mm)
|
|
rect a b
|
|
}
|
|
|
|
frame unit_square @
|
|
vec @(2mm, 0mm)
|
|
frame unit_square .
|
|
|
|
|
|
Names and variables
|
|
-------------------
|
|
|
|
fped uses several name spaces:
|
|
|
|
- frame names occupy one global name space
|
|
|
|
- vector names occupy name spaces delimited by the frame they're
|
|
contained in. A vector name is only visible inside the frame in which
|
|
it is defined.
|
|
|
|
- variable names occupy name spaces delimited by the frame they're
|
|
contained in. A variable lookup starts in the frame in which the
|
|
corresponding expression appears and propagates to outer frames
|
|
until the variable is found.
|
|
|
|
- pads occupy one global name space (this is currently not enforced)
|
|
|
|
Note that names cannot be redefined. E.g., this does not work:
|
|
|
|
set a = 1
|
|
set a = a+1
|
|
|
|
The names spaces of frames, vectors, variables, and pads are separate
|
|
from each other.
|
|
|
|
|
|
Simple variables
|
|
- - - - - - - -
|
|
|
|
A variable with a single value is defined with the following
|
|
assignment syntax:
|
|
|
|
set <identifier> = <expression>
|
|
|
|
Example:
|
|
|
|
set a = b+2
|
|
|
|
|
|
Loops
|
|
- - -
|
|
|
|
A loop is a variable with a range of values:
|
|
|
|
loop <identifier> = <from>, <to>
|
|
|
|
The variable assumes all the values i for <from> <= i <= <to>, in
|
|
increments of one. E.g.,
|
|
|
|
loop n = 1, 3
|
|
|
|
and
|
|
|
|
loop n = 1, 3.5
|
|
|
|
both assign the values 1, 2, and 3 to the variable "n". The
|
|
following loop would not execute at all:
|
|
|
|
loop n = 1, 0
|
|
|
|
This can be used to implement conditional execution. For example,
|
|
the items in the following frame would be instantiated if the
|
|
variable "enable" is set to 1 but not it is set to 0:
|
|
|
|
frame ... {
|
|
loop dummy = 1, enable
|
|
...
|
|
}
|
|
|
|
When a loop is executed, the objects contained in the body of the
|
|
enclosing frame are generated for each value of the variable. If
|
|
a frame contains multiple loops, all possible combinations of the
|
|
values are generated.
|
|
|
|
The following example draws three concentric circles around the
|
|
origin, with radii 1, 2, and 3:
|
|
|
|
loop x = 1, 3
|
|
vec @(x*1mm, 0mm)
|
|
circ @ .
|
|
|
|
|
|
Tables
|
|
- - -
|
|
|
|
Tables combine values for multiple variables. Like loops, they are
|
|
used to iteratively generate objects. A table begins with a row of
|
|
variable names, followed by one or more rows with values. Rows are
|
|
enclosed in curly braces and their elements are separated by commas.
|
|
|
|
table
|
|
{ <identifier>, ... }
|
|
{ <expression>, ... }
|
|
...
|
|
|
|
Like loops, tables are iterated to generate objects. The following
|
|
example is equivalent to the one in the previous section:
|
|
|
|
table
|
|
{ x }
|
|
{ 1mm }
|
|
{ 2mm }
|
|
{ 3mm }
|
|
vec @(x, 0mm)
|
|
circ @ .
|
|
|
|
Note that we can set the unit of the values directly in this case.
|
|
|
|
Iteration is performed over rows. All variables of the table are set
|
|
to the value in the respective row at the same time. For example, in
|
|
|
|
table
|
|
{ x, y }
|
|
{ 1, 2 }
|
|
{ 3, 4 }
|
|
|
|
(x, y) assume the values (1, 2) and (3, 4).
|
|
|
|
Tables can also be used to provide information that depends on
|
|
other variables. The value of such a variable acts as a key, and a
|
|
row is only selected if all the keys in that row match the
|
|
respective variables. To mark a variable as being used as key, its
|
|
name it prefixed with a question mark.
|
|
|
|
Example:
|
|
|
|
loop n = 1, 2, 3
|
|
table
|
|
{ ?n, name }
|
|
{ 1, "one" }
|
|
{ 2, "two" }
|
|
{ 3, "three" }
|
|
|
|
|
|
Expressions
|
|
-----------
|
|
|
|
Expressions can contain numeric constants (in non-exponential notation),
|
|
variable names, the arithmetic operations +, -, *, /, unary -, and the
|
|
functions sin(), cos(), sqrt(), and floor().
|
|
|
|
Parentheses can be used to change precedence.
|
|
|
|
The argument of sin and cos is a dimensionless number that specifies the
|
|
angle in degrees. E.g., sin(90) yields 1.
|
|
|
|
The argument of sqrt() can be dimensionless or have a dimension with an
|
|
exponent that's a multiple of two. E.g., sqrt(2) and sqrt(2mm*3mm) are
|
|
valid expressions, sqrt(2mm) isn't.
|
|
|
|
The function floor() returns the next integer that is below or equal to
|
|
the argument. If the argument has a dimension, that dimension is
|
|
preserved. E.g., floor(-1.2) returns -2, floor(4.7mm) returns 4mm.
|
|
|
|
|
|
GUI
|
|
---
|
|
|
|
Part of the GUI is described in
|
|
http://downloads.qi-hardware.com/people/werner/fped/gui.html
|
|
|
|
|
|
Keyboard shortcuts
|
|
- - - - - - - - -
|
|
|
|
Space reset user coordinates
|
|
+, = zoom in (like mouse wheel forward)
|
|
- zoom out (like mouse wheel backward)
|
|
. cursor position to screen center (like middle click)
|
|
* zoom and center to extents
|
|
# zoom and center to currently active frame instance
|
|
U undelete the previously deleted object
|
|
/ Switch between variable and item display.
|
|
|
|
|
|
Canvas
|
|
- - -
|
|
|
|
To create a new object, click on the corresponding tool icon, move the
|
|
mouse to the base point of the new object, then drag to the object's
|
|
second point.
|
|
|
|
Frame references are created as follows:
|
|
|
|
- select the frame you want to add
|
|
- click on the frame icon. A black dot should appear on the icon.
|
|
- select the frame on which you want to add the new reference.
|
|
The black dot should change to a green dot. If the current frame
|
|
is a child of the selected frame, the dot remains black.
|
|
- click on the desired base location
|
|
|
|
To change a point of an object, select the object, then drag the point
|
|
to its new location. To edit the object's parameters, select it and
|
|
make the changes in the input area at the bottom.
|
|
|
|
To delete an object, select the delete tool and click on the object.
|
|
Deleted objects can be undeleted by pressing "u". If any other changes
|
|
have been made since deletion, fped may misbehave. If deleting a vector,
|
|
all items that reference it are deleted as well.
|
|
|
|
|
|
Experimental: new-style measurements
|
|
------------------------------------
|
|
|
|
New-style measurements can measure the distance between various pairs
|
|
of points, not only between points in the same instance and the same
|
|
frame. They operate on the set of points produced during instantiation.
|
|
|
|
New-style measurements are placed in the root frame after all other
|
|
items.
|
|
|
|
Known issues:
|
|
- they currently can't be edited through the GUI
|
|
- tie-breaking heuristics don't always do what one expects
|
|
|
|
Syntax:
|
|
|
|
<type> [<label>] <from> <op> <to> [<offset>]
|
|
|
|
Types:
|
|
- meas: measure diagonally
|
|
- measx: measure along the X axis
|
|
- measy: measure along the y axis
|
|
|
|
Note that the type also affects the selection of the points. E.g.,
|
|
measx will select maximum x values.
|
|
|
|
Operators:
|
|
- A -> B: smallest value of A and smallest B greater than A
|
|
- A <- B: like A -> B, but normal (for offset and text) is inverted
|
|
- A >> B: smallest value of A and greatest value of B
|
|
- A << B: like A -> B, but normal (for offset and text) is inverted
|
|
|
|
Operands are qualified vector names. Vectors in the root frame are
|
|
referenced by their name. Vectors in other frames are prefixed with
|
|
the name of the frame followed by a dot.
|
|
|
|
Example:
|
|
|
|
measx pad.sw -> pad.se 1mm
|
|
|
|
The optional label is printed directly before the distance. Example:
|
|
|
|
a: vec @(0mm, 0mm)
|
|
b: vec @(1mm, 0mm)
|
|
measx "width = " a >> b 0mm
|
|
|
|
would print "width = 1mm"
|
|
|
|
|
|
Additional qualifiers
|
|
- - - - - - - - - - -
|
|
|
|
When using frames as reusable building blocks, similar to functions or
|
|
macros in many programming languages, one may need finer control over
|
|
the points that are selected for measurements.
|
|
|
|
For example, let us consider a frame "square" that draws a square
|
|
centered at the frame's origin and with a side length given by the
|
|
variable "size". This variable be set in the frame referencing
|
|
"square".
|
|
|
|
frame square {
|
|
a: vec @(-size/2, -size/2)
|
|
b: vec @(size/2, size/2)
|
|
rect a b
|
|
}
|
|
|
|
frame small {
|
|
set size = 2mm
|
|
frame square @
|
|
}
|
|
|
|
frame big {
|
|
set size = 5mm
|
|
frame square @
|
|
}
|
|
|
|
frame small @
|
|
vec @(5mm, 0mm)
|
|
frame big .
|
|
|
|
If we want to measure the size of each square, we could use
|
|
|
|
measx square.a -> square.b
|
|
|
|
Unfortunately, this only measures the small square. To reach the
|
|
big frame, we need to tell fped to use only those points in "square"
|
|
that have been placed when "square" was invoked from the big frame.
|
|
|
|
This is accomplished by prefixing the points in question with the
|
|
name(s) of the frames that need to be visited. The frame names are
|
|
separated by slashes (/).
|
|
|
|
measx big/square.a -> square.b
|
|
|
|
For clarity, it's better to qualify both points, e.g.,
|
|
|
|
measx big/square.a -> big/square.b
|
|
|
|
If multiple frame names are given, they must be in the order in
|
|
which they are invoked.
|
|
|
|
|
|
Experimental: debugging directives
|
|
----------------------------------
|
|
|
|
For debugging and regression tests, fped supports the following commands,
|
|
most of which mimick the effect of GUI operations:
|
|
|
|
%del <qualified-identifier>
|
|
%move <identifier> [<number>] <identifier>
|
|
%frame <identifier> <qualified-base>
|
|
%print <expression>
|
|
%iprint <expression>
|
|
%meas <identifier>
|
|
%dump
|
|
%exit
|
|
%tsort { -<id> | +<id> | <id-before> <id-after> [<number>] ... }
|
|
|
|
%del removes the specified item. This can be a vector, an object, or
|
|
a frame. If the vector or object is in a different frame than the
|
|
current, its name is qualified with the frame name, e.g., "foo.obj".
|
|
|
|
For this purpose, also objects can be labeled. Object labels behave like
|
|
vector labels and share the same name space. They are not normally
|
|
accessible in the GUI. (You can see them in the code view.)
|
|
|
|
%move take as its first argument the name of the vector or object to
|
|
manipulate. %move sets an anchor point to the vector named as its last
|
|
argument. The anchor point is identified by index as follows:
|
|
|
|
anchor index vec/frame line/rect/pad arc measurement
|
|
-------------- --------- ------------- ------------ -----------
|
|
0 (or omitted) base first point center low point
|
|
1 - second point end of arc high point
|
|
2 - - start of arc -
|
|
|
|
%frame creates a frame reference. Unlike "frame", the destination frame
|
|
can be different from the current frame. E.g., "%frame foo bar.a" would
|
|
add a reference to frame "foo" in frame "bar", rooted at vector "a". The
|
|
parent frame's origin can be references as "@".
|
|
|
|
%dump writes the footprint definition in the fped language to standard
|
|
output. %exit immediately exits fped, without invoking the GUI.
|
|
|
|
%print and %iprint evaluate the expression and print the result to
|
|
standard output. The difference between them is that %print runs only
|
|
once and without explicit instantiation, while %iprint is treated as
|
|
a regular object and is executed as many times as instantiation
|
|
demands.
|
|
|
|
For example, after loop x = 1, 3 we would obtain just 1 with %print
|
|
while %iprint would display, 1, 2, and 3.
|
|
|
|
%meas performs an instantiation and prints the value of the labeled
|
|
measurement.
|
|
|
|
%tsort is used to test-drive the topological sort algorithm. The items
|
|
in the curly braces are declarations of nodes with (-<id>) or without
|
|
(+<id>) decay or edges in the partial order. The optional number is
|
|
the edge's priority. See tsort.c for details, test/tsort for examples.
|