cotn/notes/
cotn/src/
/*
 * Original code by Xkilla
 * Cleaned up by Dreimas
 * Converted for AFKMud by Zarius
 * Special thanks to the AFKmud community
 */
#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#endif

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdarg.h>
#include "merc.h"
#include "changes.h"

/*
 * Globals
 */
char     *current_date args((void));
int num_changes args((void));

/*
 * Local Functions
 */

int       maxChanges;

#define  NULLSTR( str )  ( str == NULL || str[0] == '\0' )
CHANGE_DATA *changes_table;
void      do_echo(CHAR_DATA * ch, char *argument);

void load_changes(void)
{
        FILE     *fp;
        int       i;

        if (!(fp = fopen(CHANGES_FILE, "r")))
        {
                bug("Could not open changes.dat file for reading.", 0);
                return;
        }

        fscanf(fp, "%d\n", &maxChanges);

        /*
         * Use malloc so we can realloc later on 
         */
        changes_table =
                (CHANGE_DATA *) malloc(sizeof(CHANGE_DATA) *
                                       (maxChanges + 1));

        for (i = 0; i < maxChanges; i++)
        {
                changes_table[i].change = fread_string(fp);
                changes_table[i].coder = fread_string(fp);
                changes_table[i].date = fread_string(fp);
                changes_table[i].type = fread_string(fp);
                changes_table[i].mudtime = fread_number(fp);
        }
        changes_table[maxChanges].coder = str_dup("");
        fclose(fp);
        return; /* just return */
}

char     *current_date()
{
        static char buf[128];
        struct tm *datetime;

        datetime = localtime(&current_time);
        strftime(buf, sizeof(buf), "%m/%d/%Y", datetime);
        return buf;
}

void save_changes(void)
{
        FILE     *fp;
        int       i;

        if (!(fp = fopen(CHANGES_FILE, "w")))
        {
                perror(CHANGES_FILE);
                return;
        }

        fprintf(fp, "%d\n", maxChanges);
        for (i = 0; i < maxChanges; i++)
        {
                fprintf(fp, "%s~\n", changes_table[i].change);
                fprintf(fp, "%s~\n", changes_table[i].coder);
                fprintf(fp, "%s~\n", changes_table[i].date);
                fprintf(fp, "%s~\n", changes_table[i].type);
                fprintf(fp, "%ld\n", changes_table[i].mudtime);
                fprintf(fp, "\n");
        }

        fclose(fp);
        return;
}

void delete_change(int iChange)
{
        int       i, j;
        CHANGE_DATA *new_table;

        new_table = (CHANGE_DATA *) malloc(sizeof(CHANGE_DATA) * maxChanges);

        if (!new_table)
        {
                return;
        }

        for (i = 0, j = 0; i < maxChanges + 1; i++)
        {
                if (i != iChange - 1)
                {
                        new_table[j] = changes_table[i];
                        j++;
                }
        }

        free(changes_table);
        changes_table = new_table;

        maxChanges--;

        return;
}


void do_addchange(CHAR_DATA * ch, char *argument)
{
        CHANGE_DATA *new_table;
        char      arg1[MAX_INPUT_LENGTH];
        char      buf[MSL];

        argument = one_argument(argument, arg1);

        if (IS_NPC(ch))
                return;

        if (argument[0] == '\0')
        {
                send_to_char("Syntax: Addchange <type> <string>\n\r", ch);
                send_to_char
                        ("#wTypes are: code, area, help, rule, typo.#n\n\r",
                         ch);
                send_to_char("#wType '#Rchanges#w' to view the list.#n\n\r",
                             ch);
                return;
        }

        /*
         * Addchange must have an argument now - Zarius
         */
        if (str_cmp(arg1, "code") && str_cmp(arg1, "area")
            && str_cmp(arg1, "help") && str_cmp(arg1, "rule")
            && str_cmp(arg1, "typo"))
        {
                send_to_char
                        ("Incorrect Type!  Must be code, area, help, rule or typo ONLY\n\r",
                         ch);
                return;
        }

        if (strlen(argument) < 10)
        {
                send_to_char
                        ("The change description must be longer than 10 chars.\n\r",
                         ch);
                return;
        }

        maxChanges++;
        new_table =
                (CHANGE_DATA *) realloc(changes_table,
                                        sizeof(CHANGE_DATA) * (maxChanges +
                                                               1));

        if (!new_table) /* realloc failed */
        {
                send_to_char
                        ("Memory allocation failed. Brace for impact.\n\r",
                         ch);
                return;
        }

        changes_table = new_table;

        changes_table[maxChanges - 1].change = str_dup(argument);
        changes_table[maxChanges - 1].coder = str_dup(ch->name);
        changes_table[maxChanges - 1].date = str_dup(current_date());
        changes_table[maxChanges - 1].type = str_dup(capitalize(arg1));
        changes_table[maxChanges - 1].mudtime = current_time;

        send_to_char("Changes Created.\n\r", ch);
        send_to_char("Type 'changes' to see the changes.\n\r", ch);
        xprintf(buf,
                "#D<#CCHANGE#D> #w%s change #R##%d #wadded, type '#Rchanges#w' to see the details.#D\n\r",
                capitalize(arg1), maxChanges);
        do_echo(ch, buf);
        save_changes();
        return;
}

void do_delchange(CHAR_DATA * ch, char *argument)
{
        char      arg1[MAX_INPUT_LENGTH];
        char      buf[MSL];
        int       num;

        argument = one_argument(argument, arg1);

        if (IS_NPC(ch))
                return;

        if (!ch->desc || NULLSTR(arg1) || !is_number(arg1))
        {
                send_to_char
                        ("#wFor delchange you must provide a change number.#D\n\r",
                         ch);
                send_to_char("Syntax: delchange (change number)\n\r", ch);
                return;
        }

        num = atoi(arg1);
        if (num < 1 || num > maxChanges)
        {
                xprintf(buf, "Valid changes are from 1 to %d.\n\r",
                        maxChanges);
                send_to_char(buf, ch);
                return;
        }
        delete_change(num);
        send_to_char("Change deleted.\n\r", ch);
        return;
}

/*
 * The following format code has been adapted from KaViR's justify
 * snippet -- Dreimas
 */

static void AddSpaces(char **ppszText, int iNumber)
{
        int       iLoop;

        for (iLoop = 0; iLoop < iNumber; iLoop++)
        {
                *(*ppszText)++ = ' ';
        }
}

char     *change_justify(char *pszText, int iAlignment)
{
        static char s_szResult[4096];
        char     *pszResult = &s_szResult[0];
        char      szStore[4096];
        int       iMax;
        int       iLength = iAlignment - 1;
        int       iLoop = 0;

        if (strlen(pszText) < 10)
        {
                strcpy(s_szResult,
                       "BUG: Justified string cannot be less than 10 characters long.");
                return (&s_szResult[0]);
        }

        while (*pszText == ' ')
                pszText++;

        szStore[iLoop++] = *pszText++;

        if (szStore[iLoop - 1] >= 'a' && szStore[iLoop - 1] <= 'z')
                szStore[iLoop - 1] = UPPER(szStore[iLoop - 1]);

        /*
         * The first loop goes through the string, copying it into szStore. The
         * * string is formatted to remove all newlines, capitalise new sentences,
         * * remove excess white spaces and ensure that full stops, commas and
         * * exclaimation marks are all followed by two white spaces.
         */
        while (*pszText)
        {
                switch (*pszText)
                {
                default:
                        szStore[iLoop++] = *pszText++;
                        break;
                case ' ':
                        if (*(pszText + 1) != ' ')
                        {
                                /*
                                 * Store the character 
                                 */
                                szStore[iLoop++] = *pszText;
                        }
                        pszText++;
                        break;
                case '.':
                case '?':
                case '!':
                        szStore[iLoop++] = *pszText++;
                        switch (*pszText)
                        {
                        default:
                                szStore[iLoop++] = ' ';
                                szStore[iLoop++] = ' ';
                                /*
                                 * Discard all leading spaces 
                                 */
                                while (*pszText == ' ')
                                        pszText++;
                                /*
                                 * Store the character 
                                 */
                                szStore[iLoop++] = *pszText++;
                                if (szStore[iLoop - 1] >= 'a'
                                    && szStore[iLoop - 1] <= 'z')
                                        szStore[iLoop - 1] &= ~32;
                                break;
                        case '.':
                        case '?':
                        case '!':
                                break;
                        }
                        break;
                case ',':
                        /*
                         * Store the character 
                         */
                        szStore[iLoop++] = *pszText++;
                        /*
                         * Discard all leading spaces 
                         */
                        while (*pszText == ' ')
                                pszText++;
                        /*
                         * Commas shall be followed by one space 
                         */
                        szStore[iLoop++] = ' ';
                        break;
                case '$':
                        szStore[iLoop++] = *pszText++;
                        while (*pszText == ' ')
                                pszText++;
                        break;
                case '\n':
                case '\r':
                        pszText++;
                        break;
                }
        }

        szStore[iLoop] = '\0';

        /*
         * Initialise iMax to the size of szStore 
         */
        iMax = strlen(szStore);

        /*
         * The second loop goes through the string, inserting newlines at every
         * * appropriate point.
         */
        while (iLength < iMax)
        {
                /*
                 * Go backwards through the current line searching for a space 
                 */
                while (szStore[iLength] != ' ' && iLength > 1)
                        iLength--;

                if (szStore[iLength] == ' ')
                {
                        szStore[iLength] = '\n';

                        iLength += iAlignment;
                }
                else
                        break;
        }


        /*
         * Reset the counter 
         */
        iLoop = 0;

        /*
         * The third and final loop goes through the string, making sure that there
         * * is a \r (return to beginning of line) following every newline, with no
         * * white spaces at the beginning of a particular line of text.
         */
        while (iLoop < iMax)
        {
                /*
                 * Store the character 
                 */
                *pszResult++ = szStore[iLoop];
                switch (szStore[iLoop])
                {
                default:
                        break;
                case '\n':
                        *pszResult++ = '\r';
                        while (szStore[iLoop + 1] == ' ')
                                iLoop++;
                        /*
                         * Add spaces to the front of the line as appropriate 
                         */
                        AddSpaces(&pszResult, 36);
                        break;
                }
                iLoop++;
        }

        *pszResult++ = '\0';

        return (&s_szResult[0]);
}

int num_changes(void)
{
        char     *test;
        int       today;
        int       i;

        i = 0;
        test = current_date();
        today = 0;

        for (i = 0; i < maxChanges; i++)
                if (!str_cmp(test, changes_table[i].date))
                        today++;

        return today;
}


void do_changes(CHAR_DATA * ch, char *argument)
{
        char      arg[MAX_INPUT_LENGTH];
        char      buf[MSL], outbuf[MSL];
        char     *test;
        int       today;
        int       i, xx, amount;
        bool      fAll;
        int       totalpages = 1;

        one_argument(argument, arg);

        if (IS_NPC(ch))
                return;

        if (str_cmp(arg, "code") && str_cmp(arg, "area")
            && str_cmp(arg, "help") && !is_number(arg) && str_cmp(arg, "rule")
            && str_cmp(arg, "typo") && str_cmp(arg, "all") && !NULLSTR(arg))
        {
                send_to_char
                        ("Incorrect Type!  Must be code, area, help, rule, type, page number or ALL\n\r",
                         ch);
                return;
        }

        if (maxChanges < 1)
                return;

        i = 0;
        test = current_date();
        today = 0;


        for (i = 0; i < maxChanges; i++)
                if (!str_cmp(test, changes_table[i].date))
                        today++;

        if (NULLSTR(arg))
                fAll = FALSE;
        else
                fAll = TRUE;

        send_to_char("\n\r", ch);
        outbuf[0] = '\0';

        snprintf(buf, MSL, "#R-=[ #WCoTN 3 Changelog #R]=-");
        amount = 78 - strlen(buf);  /* Determine amount to put in front of line */

        if (amount < 1)
                amount = 1;

        amount = amount / 2;

        for (xx = 0; xx < amount; xx++)
                xcatf(outbuf, " ", MSL);
        xcatf(outbuf, buf, MSL);
        xprintf(buf, "%s\n\r", outbuf);
        send_to_char(buf, ch);

        send_to_char("#R--------------------------------------------------------------------------------#n\n\r", ch);
        send_to_char("#wNo.  Coder        Date        Type    Change#n\n\r", ch);
        send_to_char("#R--------------------------------------------------------------------------------#n\n\r", ch);

        totalpages = 1 + (maxChanges / 10);
        if (totalpages < 1)
                totalpages = 1;



/*        if (is_number(arg))
        {
                int       page = atoi(arg);
                int       number;

                number = page * 10;

                if (page < 0 || page > totalpages)
                {
                        xprintf(buf, "           #RPage must be between 1 and %d!!!\n\r", totalpages);
                        send_to_char(buf, ch);
                        return;
                }

                for (i = (number - 10); (i < number && i < maxChanges); i++)
                {
                        xprintf(buf, "#0[#R%3d#0] %-11s #c*%-6s #P%-5s #w%-45s#D\n\r",
                                (i + 1), changes_table[i].coder,
                                changes_table[i].date, changes_table[i].type,
                                change_justify(changes_table[i].change, 45));
                        send_to_char(buf, ch);
                }
        }
*/
// Akurei's way
        if (is_number(arg) || NULLSTR(arg))
        {
                int       number;

                number = atoi(arg) * 10;
		if( number <= 0 || number > maxChanges ) number = maxChanges;

/*                if (page < 0 || page > totalpages)
                {
                        xprintf(buf, "           #RPage must be between 1 and %d!!!\n\r", totalpages);
                        send_to_char(buf, ch);
                        return;
                }
*/

                for (i = maxChanges >= 10 ? number - 10 : 0; i < number; i++)
                {
                        xprintf(buf, "#0[#R%3d#0] %-11s #c*%-6s #P%-5s #w%-45s#D\n\r",
                                (i + 1), changes_table[i].coder,
                                changes_table[i].date, changes_table[i].type,
                                change_justify(changes_table[i].change, 45));
                        send_to_char(buf, ch);
                }
        }
        else

                for (i = 0; i < maxChanges; i++)
                {
                        if (!fAll
                            && changes_table[i].mudtime + (24L * 3600L) <
                            current_time)
                                continue;

                        if (!str_cmp(arg, "code")
                            && str_cmp(changes_table[i].type, "code"))
                                continue;
                        if (!str_cmp(arg, "area")
                            && str_cmp(changes_table[i].type, "area"))
                                continue;
                        if (!str_cmp(arg, "help")
                            && str_cmp(changes_table[i].type, "help"))
                                continue;
                        if (!str_cmp(arg, "rule")
                            && str_cmp(changes_table[i].type, "rule"))
                                continue;
                        if (!str_cmp(arg, "typo")
                            && str_cmp(changes_table[i].type, "typo"))
                                continue;

                        xprintf(buf,
                                "#0[#R%3d#0] %-11s #c*%-6s #P%-5s #w%-55s#D\n\r",
                                (i + 1), changes_table[i].coder,
                                changes_table[i].date, changes_table[i].type,
                                change_justify(changes_table[i].change, 55));
                        send_to_char(buf, ch);
                }

        send_to_char
                ("#R--------------------------------------------------------------------------------#n\n\r",
                 ch);
        xprintf(buf,
                "#w    There are #D[ #Y%d#D ] #wchanges in the database, #Y%d #wof them were added today.#n\n\r",
                maxChanges, today);
        send_to_char(buf, ch);
        send_to_char
                ("#0       Also see: '#Cchanges all#0' for a list of all the changes.#0\n\r",
                 ch);
        send_to_char
                ("#0             Or: '#Cchanges <type>#0' for a filtered list of all.#n\n\r",
                 ch);
        if (totalpages > 1)
        {
                xprintf(buf,
                        "#0             Or: '#Cchanges <1 to %d>#0' for individual pages.#n\n\r",
                        totalpages);
                send_to_char(buf, ch);
        }
        //send_to_char("#0             Or: '#Cchanges <page number>#0' for individual page.#n\n\r",ch);
        send_to_char
                ("#R--------------------------------------------------------------------------------#n\n\r",
                 ch);
        return;
}