You could do something like
while (s/((^|\>)[^\<]*?)\>/$1\>\;/g) { 1; }
Explained:
Look for the begin of the string or any closing > in place. From here, grab everything which is no opening < until you find the next closing >. Return the grabbed thing but replace the unmatched > by >.
Do this in a while loop, because the first run may not catch all unmatched >'s until no new places are found.
The only 100% working solution would be using an XML/HTML parser. The sample above will mix up HTML-code like <a name=">here">
edit: Sorry, I just saw that my solution is working on the wrong side.
edit2: Here is the correct solution:
while (s/\<([^\>]*?(\<|$))/\<\;$1/g) { 1; }
Explained:
Every < which has no > (
^\> - match everything but >, *? = match 0 to as few as possible chars) until the earliest next < or and end-of-string is replaced.
Has been tested using the string
<<html><x<y</html><
Notice:
The same thing (using a XML/HTML parser for best results) is also true for this one.
Remember using /s if you're processing a multiline string