/* * 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(¤t_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; }