Frankly, I would probably use Javascript for a solution to your problem; however, let me tell you what I have done in the past for your very issue. I used Mason a lot - which is sort of like a perl version of JSP or ASP. In Mason, you can parse web pages and embed perl code in the HTML with the:
tags. So, let's say you have a form that has three required fields, as you say. Mason would allow you to run checks on submit and, if one or more of these checks fails, you can do what Mason calls an "internal redirect" (which is like an include) in which you still have the STDIN (POST parameters) in memory and you could fill out the form with the correct entries and pass an "error message" back to the original form that appears on the screen next to or above the form. I do this in a login system with Mason, but I can't really show you the code, because it has encryption and a bunch of other internal access control checks (via "handlers" in Mason).
If you want to use CGI, you can do the same thing, but what you will have to do (or what I did in the past) is pass back the parameters to the original form on the URL line (like after a ? for query string), but you probably don't want to do it in clean text (like url.cgi?name=John&email=john@abc.com). Some people do that but I'm a CISSP and that gives me the willies. So, what I have done in the past is encode the query string (with a reason for failure) using something like base-64 encoding (or better a symmetric encryption algorithm plus base-64 encoding) and when you come back to the original page, you can unencode/decrypt the query string and have the parameters back in clean text to pre-fill the form.
Good luck!