How to build a custom PIC Programmer?
Yes, what Wouter said.  There are three parts to consider in designing a PIC programmer, the hardware, firmware, and software.  There are a number of choices to make in each, and complexity can be traded off between them in various ways, especially between the firmware and the software.

For simple hardware that only addresses a subset of PICs, see my LProg programmer.  This was optimized for low cost while still using a common PC interface the standard way.  It only works with those PICs that don't require a high voltage on MCLR to enter programming mode, and all its signals are fixed 0-3.3 V.  These two restriction allowed the hardware to be simple and the cost therefore lower.  The schematic is available from the bottom of that page.

At the other end are the USBProg and USBProg2 programmers.  Again, the schematics are available at the bottom of those pages.  These have fully variable Vpp up to 15 V and the digital signals up to 6 V.  They also have more protection.  For example, the digital outputs can be shorted to anywhere from 0-6 V indefinitely without damage to the programmer.  Of course all this complexity comes at a higher parts and manufacturing cost.

The software/firmware tradeoff is mostly a matter of firmware complexity versus speed.  In theory you could make a programmer that only has facilities for the host software to set the lines to particular levels.  The PIC programming protocol is synchronous, so all the clocking could be done in software.  This would make the firmware simple to write, but the result would be a very slow programmer.  Implementing the details of all the different programming algorithms Microchip has dreamed up over the years would take more program memory than is a available in most reasonable control PICs.  The engineers at Microchip's Programming Obfuscation Division have been very busy.  As Wouter said, there can be programming algorithm differences between PICs that otherwise appear to be very similar.  You have to read the programming spec carefully for every PIC you intend to support.  There is no one programming algorithm, not even close.

The host protocol to my programmers is linked to from all of the pages I mentioned above.  This protocol was designed not just for the immediate task I had at hand, but to allow some latitude in programmer design.  As a result, I pushed some complexity to the host software so that a variety of programmers with different native capabilities can be seamlessly supported.  The same host program drives the LProg, USBProg, and some older programmers we have meanwhile discontinued.  It does this not by checking which model it is talking to, but by inquiring its capabilities in a general way as defined by the protocol.

Making your own PIC programmer as a one-off for a specific PIC isn't too hard.  Trying to make a general purpose PIC programmer is more difficult, probably a lot more difficult, than most people realize.  If do end up making your own, I suggest you have it support my host protocol.  If you look at the protocol carefully, you will see much of it is optional.  If your programmer uses my host protocol, then you have my existing host code available for testing and possibly even for regular operation.  Much of my source code is available from http://www.embedinc.com/picprg/sw.htm.