#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <vis.h>


static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find ";
static const char ContinueStr[] = "...";
static const char WarnProgSep[] = ": ";
#define DISPLAY_LINE_SIZE 80
#define ENC_SIZE_MAX 5


/*
 * Display a warning about being unable to find a variable with a particular
 * name with safeguards against a pointer to unsafe text.  Limit the display
 * line to a maximum of 79 characters.
 */
static void __safe_display(const char *name, size_t nameLen)
{
	char convStr[ENC_SIZE_MAX];
	char *displayPtr;
	char displayLine[DISPLAY_LINE_SIZE];
	int displayLineSize;
	int nameNdx;

	/*
	 * Calculate size of display line taking the warning prefix "program: "
	 * into account.
	 */
	displayLineSize = sizeof(displayLine) - (sizeof(WarnProgSep) - 1) -
	    (getprogname() != NULL ? strlen(getprogname()) : 0);

	/*
	 * Fill remainder of display line with encoded characters.  Convert each
	 * character individually to maximize available space.
	 */
	strlcpy(displayLine, CorruptEnvFindMsg, sizeof(displayLine));
	for (displayPtr = displayLine + strlen(displayLine), nameNdx = 0;
	    nameNdx < nameLen;
	    displayPtr += strlen(displayPtr), nameNdx++) {
		vis(convStr, name[nameNdx], VIS_TAB | VIS_NL, 0);
		if (strlen(convStr) >= displayLineSize - (displayPtr -
		    displayLine)) {
			nameNdx--;
			break;
		}
		strcat(displayPtr, convStr);
	}

	/*
	 * Show a continuation if the entire string could not be displayed
	 * within the length allowed.  It is possible to cut off part of an
	 * encoded character, so back off conversion till enough room is
	 * available for the continuation string.
	 */
	if (nameNdx >= 0 && nameNdx < nameLen) {
		for (displayPtr = displayLine + strlen(displayLine);
		    nameNdx >= 0 && displayLineSize -
		    (displayPtr - displayLine) <= sizeof(ContinueStr);
		    nameNdx--) {
			vis(convStr, name[nameNdx], VIS_TAB | VIS_NL, 0);
			displayPtr -= strlen(convStr);
			*displayPtr = '\0';
		}

		strcpy(displayPtr, ContinueStr);
	}

	warnx(displayLine);

	return;
}


int main(int argc, char **argv)
{
	char *name = "this_is_a_test               AZ\200ZA";
	size_t nameLen = strlen(name);

	__safe_display(name, nameLen);

	return (0);
}
