/*
 * Native Windows Program, does not need libc.
 *
 * Compile with:
 * cl -nologo -W3 -O2 -GS- -Zl test_win_native.c \
 *    memcpy64.obj strlen64.obj strcmp64.obj \
 *    -link -release -nodefaultlib -entry:xmain -subsystem:console -map kernel32.lib
 */
#define UNICODE
#define _UNICODE

#include <windows.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define inline __inline static

inline INT fill_buf(HANDLE fh, BYTE *buf, SIZE_T len)
{
    DWORD n;

    if (!ReadFile(fh, buf, (DWORD)len, &n, NULL))
        return -1;
    return n;
}

inline INT write_buf(HANDLE fh, BYTE *buf, SIZE_T len)
{
    DWORD n;

    if (!WriteFile(fh, buf, (DWORD)len, &n, NULL))
        return -1;
    return 0;
}

inline PVOID SysAlloc(SIZE_T size)
{
    return VirtualAllocEx((HANDLE)-1, NULL, size,
            MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
}

#define NELEM(ar) (sizeof(ar)/sizeof(*ar))
#define FIELDLEN (4096)
#define BUFLEN (256*1024)
#define IBUFLEN (BUFLEN + FIELDLEN)

VOID xmain()
{
    SIZE_T i, n, m;
    HANDLE fin, fout;
    PBYTE p, outptr, ptr, end, buf, outbuf, field[64];

    /* open read and write files */
    fin = CreateFile(TEXT("list.txt"), GENERIC_READ, 0, NULL,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (fin == INVALID_HANDLE_VALUE)
E:      ExitProcess(-1);

    fout = CreateFile(TEXT("nodup.txt"), GENERIC_WRITE, 0, NULL,
            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (fout == INVALID_HANDLE_VALUE)
        goto E;

    /* allocate read and write buffer */
    buf = SysAlloc(2*IBUFLEN + BUFLEN);
    if (!buf)
        goto E;

    outbuf = buf + 2*IBUFLEN;
    outptr = outbuf;
    end = ptr = buf;
    i = 1;
    field[0] = buf;

    for (;;) {

        if (ptr != end) {

            switch (*ptr) {
            case ';':
                /* end of field */
                *ptr++ = 0;
                field[i++] = ptr;
                break;

            case '\n':

                if (--i > 3) {
                    /* remove duplicate entries */
                    for (n = 3; n < i - 1; n++) {
                        for (m = n + 1; m < i; m++) {
                            if (strcmp(field[n], field[m]) == 0)
                                field[m][0] = 0;
                        }
                    }
                }

                /* copy fields to output buffer */
                for (n = 0; n < i; n++) {
                    if (!(p = field[n]))
                        continue;
                    m = strlen(p);
                    memcpy(outptr, p, m);
                    outptr += m;
                    *outptr++ = ';';
                }
                *outptr++ = '\n';

                /* write output buffer */
                m = outptr - outbuf;
                if (m > BUFLEN - FIELDLEN) {
                    write_buf(fout, outbuf, m);
                    outptr = outbuf;
                }

                /* reset filed pointer array */
                i = 1;
                field[0] = ++ptr;
                break;

            default:
                /* next char */
                ptr++;
            }
        }
        else {
            /* fill read buffer */

            n = ptr - field[i - 1]; /* number of chars in partial field */
            p = buf + IBUFLEN;

            /* ptr points to first or to second half of buffer */
            if (end <= p)
                ptr = p;
            else
                ptr = buf;

            /* copy partial field to start of new buffer */
            memcpy(ptr, field[i - 1], n);
            field[i - 1] = ptr;
            ptr += n;

            if ((n = fill_buf(fin, ptr, BUFLEN)) <= 0)
                break;
            end = ptr + n;
        }
    }

    /* write outstanding data and return */
    write_buf(fout, outbuf, outptr - outbuf);
    ExitProcess(0);
}
