The obvious thing to start with is to get rid of MAY_LOOP and arrange things so there's always an infinite loop, and there's always a finished flag, and the finished flag just never gets set to FALSE in the cases where you don't want to loop. Now you've lost several #ifdef...#endif clauses, and there's no #ifdef nesting.

One way of tidying up the inner parts a bit might be by splitting each FD handling code into an init part, and a part that checks for input and a part that checks for error. For example, here's the code for XSMP, whatever that is. This approach is pretty easy, because you can do it with copy and paste. That's exactly how I did it and that's how I can actually present you code:

#ifdef USE_XSMP

#define InitXSMP() \
if (xsmp_icefd != -1) \
{ \
xsmp_idx = nfd; \
fds[nfd].fd = xsmp_icefd; \
fds[nfd].events = POLLIN; \
nfd++; \
}

#define ShouldCheckXSMP() (xsmp_idx >= 0)
#define IsXSMPInput() (xsmp_idx >= 0 && (fds[xsmp_idx].revents & POLLIN))
#define IsXSMPError() (xsmp_idx >= 0 && (fds[xsmp_idx].revents & POLLHUP))

#else

#define InitXSMP() \
if (xsmp_icefd != -1) \
{ \
FD_SET(xsmp_icefd, &rfds); \
FD_SET(xsmp_icefd, &efds); \
if (maxfd < xsmp_icefd) \
maxfd = xsmp_icefd; \
}

#define ShouldCheckXSMP() (xsmp_icefd != -1)
#define IsXSMPInput() (xsmp_icefd != -1 && FD_ISSET(xsmp_icefd, &efds))
#define IsXSMPError() (xsmp_icefd != -1 && FD_ISSET(xsmp_icefd, &rfds))

#endif

#endif

(You could argue about this - for example, should ShouldCheckXSMP() maybe always just be ``(xsmp_icefd!=-1)''? - but the way the code is written, this puts all the details in one place, and the logic in another.)

Then the init code would have this bit:

#ifdef USE_XSMP
InitXSMP();
#endif

And after your poll/select code - which you'd similarly hide in a function or a macro, which I've here assumed sets a flag called `any_events' to say that there were any events that might need looking at - you'd do the business like this:

#ifdef USE_XSMP
if (any_events && ShouldCheckXSMP())
{
if (IsXSMPInput())
{
busy = TRUE;
xsmp_handle_requests();
busy = FALSE;

if (--ret == 0)
finished = FALSE; /* keep going if event was only one */
}
else if (IsXSMPError())
{
if (p_verbose > 0)
verb_msg((char_u *)_("XSMP lost ICE connection"));
xsmp_close();

if (--ret == 0)
finished = FALSE; /* keep going if event was only one */
}
}
#endif

(usual disclaimers for forum post code apply.)

So: the actual logic is handled in one place, whether or not you're using poll and select, which woud be my key criticism of the code as it stands. And I don't mind having code like this in a #ifdef, if it's only one level deep, particularly if it's somewhat formulaic, which this function would end up being if you approached it this way.

Then repeat for all the parts, and do a bit of work to declare the right variables at the top of the function (something I've just completely ignored).

If you'd prefer to be able to step through it in your average debugger - which tends to do a poor job with #defines - you could do the above with functions, but you'd probably need to move all the state into a struct so that you could pass it around more easily.

Perhaps you could have a mini wrapper for poll and select - for this sort of level of use I'd probably write something local to the file, since it's not so much a separate layer, or a library, or what have you, as just some little helper functions to stop the calling code becoming too awful.

You could always have some kind of extensible function pointer-based system whereby a given descriptor has a callback to be invoked if its FD had an error or has input, which would give you the opportunity to have each subsection of the code supply a low-level function in its own file. (For example, say xsmp_icefd is global only because this function needs to use it - now it could be static to the xsmp support file, which would need only expose a function that would be called from here when input was available or there was an error.)

And so on, and so on. I've worked on this sort of thing quite a lot over the years. There's always a way of doing things that doesn't involve a huge gnarly pile of nested #ifdefs and control structures inside #ifdefs. Either of those, let alone both together, are a good sign that you've taken a wrong turning somewhere.

(Some people are doctrinaire about never including any platform-specific #ifdefs anywhere in the first place. I'm not - but those people definitely do have a point.)