Tight, fast, and ugly state machine. Handles degenerate cases, like empty input, blank lines at the beginning of the input, long strings of blank lines between paragraphs, and a missing newline marker at the end of the input.
template <typename InputIt, typename OutputIt>
void TextToHTML(InputIt begin, InputIt end, OutputIt target) {
start: if (begin == end) return;
if (*begin == '\n') { ++begin; goto start; }
*target++ = '<'; *target++ = 'p'; *target++ = '>';
para: *target++ = *begin++;
if (begin == end) goto endp;
if (*begin != '\n') goto para;
if (++begin == end) goto endp;
if (*begin == '\n') goto endp;
*target++ = '<'; *target++ = 'b'; *target++ = 'r'; *target++ = ' '; *target++ = '/'; *target++ = '>';
goto para;
endp: *target++ = '<'; *target++ = '/'; *target++ = 'p'; *target++ = '>'; *target++ = '\n';
goto start;
}
int main() {
std::string text = "foo\nbar\n\nbaz";
std::string html;
TextToHTML(text.begin(), text.end(), std::back_inserter(html));
std::cout << html << std::endl;
return 0;
}