muddy/
muddy/CVS/
muddy/area/
muddy/area/CVS/
muddy/clans/CVS/
muddy/classes/CVS/
muddy/doc/
muddy/doc/CVS/
muddy/etc/CVS/
muddy/etc/i3/
muddy/etc/i3/CVS/
muddy/imc/CVS/
muddy/lang/CVS/
muddy/licenses/CVS/
muddy/msgdb/CVS/
muddy/new/CVS/
muddy/notes/
muddy/player/
muddy/races/CVS/
muddy/religions/CVS/
muddy/src/CVS/
muddy/src/comm/CVS/
muddy/src/db/CVS/
muddy/src/intermud/
muddy/src/intermud/CVS/
muddy/src/irc/CVS/
muddy/src/olc/CVS/
/* $Id: note.c,v 1.666 2004/09/20 10:49:51 shrike Exp $ */

/************************************************************************************
 *    Copyright 2004 Astrum Metaphora consortium                                    *
 *                                                                                  *
 *    Licensed under the Apache License, Version 2.0 (the "License");               *
 *    you may not use this file except in compliance with the License.              *
 *    You may obtain a copy of the License at                                       *
 *                                                                                  *
 *    http://www.apache.org/licenses/LICENSE-2.0                                    *
 *                                                                                  *
 *    Unless required by applicable law or agreed to in writing, software           *
 *    distributed under the License is distributed on an "AS IS" BASIS,             *
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.      *
 *    See the License for the specific language governing permissions and           *
 *    limitations under the License.                                                *
 *                                                                                  *
 ************************************************************************************/
/************************************************************************************
 *     ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR           *
 *     ANATOLIA has been brought to you by ANATOLIA consortium                      *
 *       Serdar BULUT {Chronos}         bulut@rorqual.cc.metu.edu.tr                *
 *       Ibrahim Canpunar  {Asena}      canpunar@rorqual.cc.metu.edu.tr             *
 *       Murat BICER  {KIO}             mbicer@rorqual.cc.metu.edu.tr               *
 *       D.Baris ACAR {Powerman}        dbacar@rorqual.cc.metu.edu.tr               *
 *     By using this code, you have agreed to follow the terms of the               *
 *     ANATOLIA license, in the file Anatolia/anatolia.licence                      *
 ***********************************************************************************/

/************************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,                 *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.            *
 *                                                                                  *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael                   *
 *  Chastain, Michael Quan, and Mitchell Tse.                                       *
 *                                                                                  *
 *  In order to use any part of this Merc Diku Mud, you must comply with            *
 *  both the original Diku license in 'license.doc' as well the Merc                *
 *  license in 'license.txt'.  In particular, you may not remove either of          *
 *  these copyright notices.                                                        *
 *                                                                                  *
 *  Much time and thought has gone into this software and you are                   *
 *  benefitting.  We hope that you share your changes too.  What goes               *
 *  around, comes around.                                                           *
 ************************************************************************************/

/************************************************************************************
*       ROM 2.4 is copyright 1993-1995 Russ Taylor                                  *
*       ROM has been brought to you by the ROM consortium                           *
*           Russ Taylor (rtaylor@pacinfo.com)                                       *
*           Gabrielle Taylor (gtaylor@pacinfo.com)                                  *
*           Brian Moore (rom@rom.efn.org)                                           *
*       By using this code, you have agreed to follow the terms of the              *
*       ROM license, in the file Rom24/doc/rom.license                              *
*************************************************************************************/

/************************************************************************************
 * Copyright (c) 1998 fjoe <fjoe@iclub.nsu.ru>                                      *
 * All rights reserved.                                                             *
 *                                                                                  *
 * Redistribution and use in source and binary forms, with or without               *
 * modification, are permitted provided that the following conditions               *
 * are met:                                                                         *
 * 1. Redistributions of source code must retain the above copyright                *
 *    notice, this list of conditions and the following disclaimer.                 *
 * 2. Redistributions in binary form must reproduce the above copyright             *
 *    notice, this list of conditions and the following disclaimer in the           *
 *    documentation and/or other materials provided with the distribution.          *
 *                                                                                  *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND           *
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE            *
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE       *
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE          *
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL       *
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS          *
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)            *
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT       *
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY        *
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF           *
 * SUCH DAMAGE.                                                                     *
 ************************************************************************************/


#include <sys/types.h>
#if	!defined (WIN32)
#include <sys/time.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>

#include "merc.h"
#include "db/db.h"

/* local procedures */
note_t *    new_note    (void);
void        free_note   (note_t *note);

void load_thread(char *name, note_t **list, int type, time_t free_time);
void parse_note(CHAR_DATA *ch, const char *argument, int type);
bool hide_note(CHAR_DATA *ch, note_t *pnote);
void fwrite_note(FILE *fp, note_t *pnote);

note_t *note_list;
note_t *idea_list;
note_t *penalty_list;
note_t *news_list;
note_t *changes_list;

/* stuff for recyling notes */
note_t *note_free;

note_t *new_note()
{
    note_t *note;

    if (note_free == NULL)
    note = alloc_perm(sizeof(*note));
    else
    { 
    note = note_free;
    note_free = note_free->next;
    }
    return note;
}

void free_note(note_t *note)
{ 
    free_string(note->text  );
    free_string(note->subject);
    free_string(note->to_list);
    free_string(note->date  );
    free_string(note->sender);
    free_string(note->from);

    note->next = note_free;
    note_free   = note;
}

int count_spool(CHAR_DATA *ch, note_t *spool)
{
    int count = 0;
    note_t *pnote;

    for (pnote = spool; pnote != NULL; pnote = pnote->next)
    if (!hide_note(ch,pnote))
        count++;

    return count;
}

DO_FUN(do_unread)
{
    int count;
    bool found = FALSE;
    char arg[MAX_INPUT_LENGTH];
    argument = one_argument(argument, arg, sizeof(arg));

    if (IS_NPC(ch))
    return; 

    if ((count = count_spool(ch,news_list)) > 0)
    {
    found = TRUE;
    char_printf(ch,"There %s %d new news article%s waiting.\n",
        count > 1 ? "are" : "is",count, count > 1 ? "s" : str_empty);
    }
    if ((count = count_spool(ch,changes_list)) > 0)
    {
    found = TRUE;
    char_printf(ch,"There %s %d change%s waiting to be read.\n",
        count > 1 ? "are" : "is", count, count > 1 ? "s" : str_empty);
    }
    if ((count = count_spool(ch,note_list)) > 0)
    {
    found = TRUE;
    char_printf(ch,"You have %d new note%s waiting.\n",
        count, count > 1 ? "s" : str_empty);
    }
    if ((count = count_spool(ch,idea_list)) > 0)
    {
    found = TRUE;
    char_printf(ch,"You have %d unread idea%s to peruse.\n",
        count, count > 1 ? "s" : str_empty);
    }
    if (IS_TRUSTED(ch,ANGEL) && (count = count_spool(ch,penalty_list)) > 0)
    {
    found = TRUE;
    char_printf(ch,"%d %s been added.\n",
        count, count > 1 ? "penalties have" : "penalty has");
    }

    if (!found)
        char_act("You have no unread messages.", ch);

    if (found && !str_prefix(arg, "catchup") && arg[0] != '\0')
    {
        ch->pcdata->last_note = current_time;
        ch->pcdata->last_idea = current_time;
        ch->pcdata->last_penalty = current_time;
        ch->pcdata->last_news = current_time;
        ch->pcdata->last_changes = current_time;
        char_act("You have catchup all unread messages.", ch);
    }
}

void do_note(CHAR_DATA *ch,const char *argument)
{
    parse_note(ch,argument,NOTE_NOTE);
}

void do_idea(CHAR_DATA *ch,const char *argument)
{
    parse_note(ch,argument,NOTE_IDEA);
}

void do_penalty(CHAR_DATA *ch,const char *argument)
{
    parse_note(ch,argument,NOTE_PENALTY);
}

void do_news(CHAR_DATA *ch,const char *argument)
{
    parse_note(ch,argument,NOTE_NEWS);
}

void do_changes(CHAR_DATA *ch,const char *argument)
{
    parse_note(ch,argument,NOTE_CHANGES);
}

void save_notes(int type)
{
    FILE *fp;
    char *name;
    note_t *pnote;

    switch (type) {
    default:
        return;
    case NOTE_NOTE:
        name = NOTE_FILE;
        pnote = note_list;
        break;
    case NOTE_IDEA:
        name = IDEA_FILE;
        pnote = idea_list;
        break;
    case NOTE_PENALTY:
        name = PENALTY_FILE;
        pnote = penalty_list;
        break;
    case NOTE_NEWS:
        name = NEWS_FILE;
        pnote = news_list;
        break;
    case NOTE_CHANGES:
        name = CHANGES_FILE;
        pnote = changes_list;
        break;
    }

    fclose(fpReserve);
    if ((fp = dfopen(NOTES_PATH, name, "w")) == NULL) {
        perror(name);
        return;
    }

    for (; pnote; pnote = pnote->next) 
        fwrite_note(fp, pnote);
    fclose(fp);
    fpReserve = fopen(NULL_FILE, "r");
}

void load_notes(void)
{
    load_thread(NOTE_FILE, &note_list, NOTE_NOTE, 14*24*60*60);
    load_thread(IDEA_FILE, &idea_list, NOTE_IDEA, 28*24*60*60);
    load_thread(PENALTY_FILE, &penalty_list, NOTE_PENALTY, 0);
    load_thread(NEWS_FILE, &news_list, NOTE_NEWS, 0);
    load_thread(CHANGES_FILE, &changes_list,NOTE_CHANGES, 0);
}

void load_thread(char *name, note_t **list, int type, time_t free_time)
{
    FILE *fp;
    note_t *pnotelast;
    const char *p;

    if (!dfexist(NOTES_PATH, name))
        return;

    if ((fp = dfopen(NOTES_PATH, name, "r")) == NULL)
    return;

    pnotelast = NULL;
    for (; ;)
    {
    note_t *pnote;
    char letter;
     
    do
    {
        letter = getc(fp);
            if (feof(fp))
            {
                fclose(fp);
                return;
            }
        }
        while (isspace(letter));
        ungetc(letter, fp);
 
        pnote           = alloc_perm(sizeof(*pnote));
 
        if (str_cmp(p = fread_word(fp), "sender"))
            break;
        pnote->sender   = fread_string(fp);
 
        if(!str_cmp(p = fread_word(fp), "from")) {
            pnote->from = fread_string(fp);
            p = fread_word(fp);
        }
        else 
            pnote->from = str_dup(pnote->sender);
 
        if (str_cmp(p, "date"))
            break;
        pnote->date     = fread_string(fp);
 
        if (str_cmp(p = fread_word(fp), "stamp"))
            break;
        pnote->date_stamp = fread_number(fp);
 
        if (str_cmp(p = fread_word(fp), "to"))
            break;
        pnote->to_list  = fread_string(fp);
 
        if (str_cmp(p = fread_word(fp), "subject"))
            break;
        pnote->subject  = fread_string(fp);
 
        if (str_cmp(p = fread_word(fp), "text"))
            break;
        pnote->text     = fread_string(fp);
 
        if (free_time && pnote->date_stamp < current_time - free_time)
        {
        free_note(pnote);
            continue;
        }

    pnote->type = type;
 
        if (*list == NULL)
            *list           = pnote;
        else
            pnotelast->next     = pnote;
 
        pnotelast       = pnote;
    }
 
    strnzcpy(filename, sizeof(filename), NOTES_PATH);
    strnzcat(filename, sizeof(filename), name);
    db_error("load_notes", "bad keyword");
}

void append_note(note_t *pnote)
{
    FILE *fp;
    char *name;
    note_t **list;
    note_t *last;

    switch(pnote->type) {
    default:
        return;
    case NOTE_NOTE:
        name = NOTE_FILE;
        list = &note_list;
        break;
    case NOTE_IDEA:
        name = IDEA_FILE;
        list = &idea_list;
        break;
    case NOTE_PENALTY:
        name = PENALTY_FILE;
        list = &penalty_list;
        break;
    case NOTE_NEWS:
         name = NEWS_FILE;
         list = &news_list;
         break;
    case NOTE_CHANGES:
         name = CHANGES_FILE;
         list = &changes_list;
         break;
    }

    if (*list == NULL)
        *list = pnote;
    else {
        for (last = *list; last->next; last = last->next);
            last->next = pnote;
    }

    fclose(fpReserve);
    if ((fp = dfopen(NOTES_PATH, name, "a")) == NULL) {
            perror(name);
        return;
    }
    fwrite_note(fp, pnote);
        fclose(fp);
    fpReserve = fopen(NULL_FILE, "r");
}

bool is_note_to(CHAR_DATA *ch, note_t *pnote)
{
    static const char *linked_clans;
    static CHAR_DATA *wch = NULL;
    static bool has_immortal = FALSE;

    char name[MAX_STRING_LENGTH];
    const char *p;
    clan_t *clan;

    if (IS_NPC(ch) || ch->pcdata == NULL)
        return FALSE;

    if (ch != wch)
    {
        char buf[MAX_STRING_LENGTH];
        CHAR_DATA *vch;
        wch = ch;
        has_immortal = FALSE;
        buf[0] = '\0';
        if (IS_NULLSTR(ch->pcdata->linked_list))
            name_add(ch, ch->name, "Links", &ch->pcdata->linked_list);
        p = ch->pcdata->linked_list;
        for(;;)
        {
            bool loaded = FALSE;
            p = one_argument(p, name, sizeof(name));
            if (IS_NULLSTR(name))
                break;
            if ((vch = gq_findchar(name)) == NULL)
            {
                loaded = TRUE;
                vch = char_load_special(name);
            }
            if (vch == NULL)
                continue;
            if (IS_IMMORTAL(vch))
                has_immortal = TRUE;
            if (vch->clan)
            {
                clan = clan_lookup(vch->clan);
                strnzcat(buf, sizeof(buf), clan->name);
                strnzcat(buf, sizeof(buf), " ");
            }
            if (loaded)
                char_nuke(vch);
        }
        free_string(linked_clans);
        linked_clans = str_dup(buf);
    }

    if (!str_cmp(ch->name, pnote->sender))
        return TRUE;

    if (!str_cmp("all", pnote->to_list))
        return TRUE;

    if (has_immortal && is_name("imm", pnote->to_list))
        return TRUE;

    if ((has_immortal || !IS_NULLSTR(linked_clans)) 
                && is_name("clan", pnote->to_list))
        return TRUE;

    // immortals can now see all changes, news, penalties
    // independent on the recipients specified (c) Illinar
    if (has_immortal &&
        (pnote->type == NOTE_PENALTY ||
         pnote->type == NOTE_NEWS    ||
         pnote->type == NOTE_CHANGES)) return TRUE ;

    if (ch == NULL || IS_NPC(ch) || ch->pcdata == NULL)
        return FALSE;

    p =  ch->pcdata->linked_list;
    for(;;)
    {
        p = one_argument(p, name, sizeof(name));
        if (IS_NULLSTR(name))
            break;
        if (is_name_raw(name, pnote->to_list, str_cmp))
            return TRUE;
        if (is_name_raw(name, pnote->sender, str_cmp))
            return TRUE;
    }

    p = linked_clans;
    for(;;)
    {
        p = one_argument(p, name, sizeof(name));
        if (IS_NULLSTR(name))
            break;
        if (is_name_raw(name, pnote->to_list, str_cmp))
            return TRUE;
    }

    return FALSE;
}

bool note_attach(CHAR_DATA *ch, int type)
{
    note_t *pnote;

    if (ch->pnote) {
        if (ch->pnote->type != type) {
            act_puts("You have an unfinished $t in progress.", ch, flag_string(note_types, ch->pnote->type), NULL, TO_CHAR, POS_DEAD);
            return FALSE;
        }
        
        return TRUE;
    }

    if (IS_NPC(ch)){
        bug("[note_attach] NPC be it!", 0);
        return FALSE;
    }

    
    pnote = new_note();
    pnote->next = NULL;
    pnote->sender   = str_dup(ch->name);
    pnote->from     = str_dup(ch->name);
    pnote->date = str_empty;
    pnote->to_list  = str_empty;
    pnote->subject  = str_empty;
    pnote->text = str_empty;
    pnote->type = type;
    ch->pnote   = pnote;
    return TRUE;
}

void note_remove(CHAR_DATA *ch, note_t *pnote, bool delete)
{
    char to_new[MAX_INPUT_LENGTH];
    char to_one[MAX_INPUT_LENGTH];
    note_t *prev;
    note_t **list;
    const char *to_list;

    if (!delete)
    {
    /* make a new list */
        to_new[0]   = '\0';
        to_list = pnote->to_list;
        while (*to_list != '\0')
        {
            to_list = one_argument(to_list, to_one, sizeof(to_one));
            if (to_one[0] != '\0' && str_cmp(ch->name, to_one))
        {
            strnzcat(to_new, sizeof(to_new),  " ");
            strnzcat(to_new, sizeof(to_new), to_one);
        }
        }
        /* Just a simple recipient removal? */
       if (str_cmp(ch->name, pnote->sender) && to_new[0] != '\0')
       {
       free_string(pnote->to_list);
       pnote->to_list = str_dup(to_new + 1);
       return;
       }
    }
    /* nuke the whole note */

    switch(pnote->type)
    {
    default:
        return;
    case NOTE_NOTE:
        list = &note_list;
        break;
    case NOTE_IDEA:
        list = &idea_list;
        break;
    case NOTE_PENALTY:
        list = &penalty_list;
        break;
    case NOTE_NEWS:
        list = &news_list;
        break;
    case NOTE_CHANGES:
        list = &changes_list;
        break;
    }

    /*
     * Remove note from linked list.
     */
    if (pnote == *list)
    {
    *list = pnote->next;
    }
    else
    {
    for (prev = *list; prev != NULL; prev = prev->next)
    {
        if (prev->next == pnote)
        break;
    }

    if (prev == NULL)
    {
        bug("Note_remove: pnote not found.", 0);
        return;
    }

    prev->next = pnote->next;
    }

    save_notes(pnote->type);
    free_note(pnote);
    return;
}

bool hide_note(CHAR_DATA *ch, note_t *pnote)
{
    time_t last_read;

    if (IS_NPC(ch))
    return TRUE;

    switch (pnote->type)
    {
    default:
        return TRUE;
    case NOTE_NOTE:
        last_read = ch->pcdata->last_note;
        break;
    case NOTE_IDEA:
        last_read = ch->pcdata->last_idea;
        break;
    case NOTE_PENALTY:
        last_read = ch->pcdata->last_penalty;
        break;
    case NOTE_NEWS:
        last_read = ch->pcdata->last_news;
        break;
    case NOTE_CHANGES:
        last_read = ch->pcdata->last_changes;
        break;
    }
    
    if (pnote->date_stamp <= last_read)
    return TRUE;

    if (!str_cmp(ch->name,pnote->sender))
    return TRUE;

    if (!is_note_to(ch,pnote))
    return TRUE;

    return FALSE;
}

void update_read(CHAR_DATA *ch, note_t *pnote)
{
    time_t stamp;

    if (IS_NPC(ch))
    return;

    stamp = pnote->date_stamp;

    switch (pnote->type)
    {
        default:
            return;
        case NOTE_NOTE:
        ch->pcdata->last_note = UMAX(ch->pcdata->last_note,stamp);
            break;
        case NOTE_IDEA:
        ch->pcdata->last_idea = UMAX(ch->pcdata->last_idea,stamp);
            break;
        case NOTE_PENALTY:
        ch->pcdata->last_penalty = UMAX(ch->pcdata->last_penalty,stamp);
            break;
        case NOTE_NEWS:
        ch->pcdata->last_news = UMAX(ch->pcdata->last_news,stamp);
            break;
        case NOTE_CHANGES:
        ch->pcdata->last_changes = UMAX(ch->pcdata->last_changes,stamp);
            break;
    }
}

const char *format_from(note_t *pnote, bool fShow)
{
    static char buf[MAX_STRING_LENGTH];
    strnzcpy(buf, sizeof(buf), pnote->from);
    if (fShow && str_cmp(pnote->from, pnote->sender))
    {
        strnzcat(buf, sizeof(buf), "{R({Y");
        strnzcat(buf, sizeof(buf), pnote->sender);
        strnzcat(buf, sizeof(buf), "{R){x");
    }
    return buf;
}

void print_note(BUFFER *buf, note_t *pnote, int vnum, bool fShow)
{
    buf_printf(buf, "{G[{R%3d{G] {W%s{C: {Y%s{x\n"
                       "{w%s{x\n"
                       "{RTo: {W%s{x\n"
"{G-----------------------------------------------------------------------------\n"
                       "{x%s\n"
                       "{x",
                        vnum,
                        format_from(pnote, fShow),
                        pnote->subject,
                        pnote->date,
                        pnote->to_list,
                        pnote->text);
}

const char * quote_note(note_t *pnote, bool fShow)
{
    const char *p;
    char *q;
    char buf[MAX_STRING_LENGTH];
    bool need_quote;

    if (IS_NULLSTR(pnote->text))
        return str_dup(str_empty);

    snprintf(buf, sizeof(buf),
         "On %s %s {xwrote to %s:\n"
         "{x\n",
         pnote->date, format_from(pnote, fShow), pnote->to_list);

    q = strchr(buf, '\0');
    need_quote = TRUE;
    for (p = pnote->text; *p && q-buf < sizeof(buf) - 5; p++) {
        if (need_quote) {
            *q++ = '>';
            *q++ = ' ';
            need_quote = FALSE;
        }

        *q++ = *p;

        if (*p == '\n')
            need_quote = TRUE;
    }
    *q = '\0';

    return str_dup(buf);
}

void parse_note(CHAR_DATA *ch, const char *argument, int type)
{
    char arg[MAX_INPUT_LENGTH];
    BUFFER *buffer;
    note_t *pnote;
    note_t **list;
    char *list_name;
    int vnum, anum;
    DESCRIPTOR_DATA *d;
    bool fShow;

    if (IS_NPC(ch))
        return;
    fShow = IS_TRUSTED(ch, 95);

    switch(type) {
    default:
        return;
    case NOTE_NOTE:
        list = &note_list;
        list_name = "notes";
        break;
    case NOTE_IDEA:
        list = &idea_list;
        list_name = "ideas";
        break;
    case NOTE_PENALTY:
        list = &penalty_list;
        list_name = "penalties";
        break;
    case NOTE_NEWS:
        list = &news_list;
        list_name = "news";
        break;
    case NOTE_CHANGES:
        list = &changes_list;
        list_name = "changes";
        break;
    }

    argument = one_argument(argument, arg, sizeof(arg));

    if (arg[0] == '\0' || !str_prefix(arg, "read")) {
            bool fAll;
        BUFFER *output;

        if (!str_cmp(argument, "all")) {
            fAll = TRUE;
            anum = 0;
        }
        else if (argument[0] == '\0' || !str_prefix(argument, "next")) {
            /* read next unread note */

            vnum = 0;
            for (pnote = *list; pnote != NULL; pnote = pnote->next) {
                if (!hide_note(ch, pnote)) {
                    output = buf_new(-1);
                    print_note(output, pnote, vnum, fShow);
                    page_to_char(buf_string(output), ch);
                    buf_free(output);
                    update_read(ch, pnote);
                    return;
                }
                else if (is_note_to(ch, pnote))
                    vnum++;
            }
            char_act("You have no unread messages.", ch);
            return;
            }
        else if (is_number(argument)) {
            fAll = FALSE;
            anum = atoi(argument);
        }
        else {
            char_act("Read which number?", ch);
            return;
        }
 
        vnum = 0;
        for (pnote = *list; pnote != NULL; pnote = pnote->next) {
            if (is_note_to(ch, pnote) && (vnum++ == anum || fAll)) {
                output = buf_new(-1);
                print_note(output, pnote, vnum-1, fShow);
                page_to_char(buf_string(output), ch);
                buf_free(output);
                if (!fAll)
                    return;
            }
        }
 
        char_printf(ch,"There aren't that many %s.\n",list_name);
        return;
    }

    if (!str_prefix(arg, "list")) {
        char buf[MAX_INPUT_LENGTH];
        char from[MAX_INPUT_LENGTH];
        char to[MAX_INPUT_LENGTH];

#       define CHECK_TO     (A)
#       define CHECK_FROM   (B)

        int flags = 0;

        for (;;) {
            argument = one_argument(argument, buf, sizeof(buf));

            if (!str_cmp(buf, "from")) {
                argument = one_argument(argument,
                            from, sizeof(from));
                if (from[0] == '\0')
                    break;
                SET_BIT(flags, CHECK_FROM);
                continue;
            }

            if (!str_cmp(buf, "to")) {
                argument = one_argument(argument,
                            to, sizeof(to));
                if (to[0] == '\0')
                    break;
                SET_BIT(flags, CHECK_TO);
                continue;
            }

            break;
        }

        vnum = 0;
        for (pnote = *list; pnote != NULL; pnote = pnote->next) {
            if (is_note_to(ch, pnote)) {
                vnum++;

                if (IS_SET(flags, CHECK_TO)
                &&  (!str_cmp("all", pnote->to_list) ||
                     !is_name(to, pnote->to_list)))
                    continue;

                if (IS_SET(flags, CHECK_FROM)
                &&  str_prefix(from, pnote->from) 
                &&  (!fShow || str_prefix(from, pnote->sender)))
                    continue;

                char_printf(ch, "{G[{R%3d{C%c{G] {W%s{C:{w %s\n{x",
                        vnum-1,
                        hide_note(ch,pnote) ? ' ' : '*', 
                        format_from(pnote, fShow), 
                        pnote->subject);
            }
        }
        return;
    }

    if (!str_prefix(arg, "remove")) {
        if (!is_number(argument)) {
            char_act("Note remove which number?", ch);
            return;
        }
 
        anum = atoi(argument);
        vnum = 0;
        for (pnote = *list; pnote != NULL; pnote = pnote->next) {
            if (is_note_to(ch, pnote) && vnum++ == anum) {
                note_remove(ch, pnote, FALSE);
                char_act("Ok.", ch);
                return;
            }
        }
 
        char_printf(ch, "There aren't that many %s.", list_name);
        return;
    }
 
    if (!str_prefix(arg, "delete") && ch->level >= MAX_LEVEL - 5) {
        if (!is_number(argument)) {
            char_act("Note delete which number?", ch);
            return;
        }
 
        anum = atoi(argument);
        vnum = 0;
        for (pnote = *list; pnote != NULL; pnote = pnote->next) {
            if (is_note_to(ch, pnote) && vnum++ == anum) {
                note_remove(ch, pnote, TRUE);
                char_act("Ok.", ch);
                return;
            }
        }

        char_printf(ch,"There aren't that many %s.", list_name);
        return;
    }

    if (!str_prefix(arg, "catchup")) {
        switch(type) {
        case NOTE_NOTE: 
            ch->pcdata->last_note = current_time;
            break;
        case NOTE_IDEA:
            ch->pcdata->last_idea = current_time;
            break;
        case NOTE_PENALTY:
            ch->pcdata->last_penalty = current_time;
            break;
        case NOTE_NEWS:
            ch->pcdata->last_news = current_time;
            break;
        case NOTE_CHANGES:
            ch->pcdata->last_changes = current_time;
            break;
        }
        return;
    }

/* below this point only certain people can edit notes */

    if ((type == NOTE_NEWS && !IS_TRUSTED(ch, ANGEL))
    ||  (type == NOTE_CHANGES && !IS_TRUSTED(ch, ANGEL))) {
        char_printf(ch, "You aren't high enough level to write %s.",
                list_name);
        return;
    }
    
    if ((type == NOTE_NOTE || type == NOTE_IDEA)
    && IS_SET(ch->comm, COMM_NONOTE))
    {
        char_printf(ch, "You are not allowed to write %s.",
                list_name);
        return;
    }

    if (!str_prefix(arg, "edit"))
    {
        if (!note_attach(ch,type))
            return;
        string_append(ch, &ch->pnote->text);
        return;
    }

    if (!str_prefix(arg, "subject"))
    {
        if (!note_attach(ch, type))
            return;
        free_string(ch->pnote->subject);
        ch->pnote->subject = str_dup(argument);
        char_act("Ok.", ch);
        return;
    }

    if (!str_prefix(arg, "+"))
    {
        if (!note_attach(ch, type))
                return;

        buffer = buf_new (-1);

        buf_add (buffer, ch->pnote->text);
        buf_add (buffer, argument);
        buf_add (buffer, "\n\r");
        free_string (ch->pnote->text);
        ch->pnote->text = str_dup (buf_string (buffer));
        buf_free (buffer);
        char_act("Ok.", ch);
        return;
    }

    if (!str_prefix(arg, "from"))
    {
        if (!is_name_raw(argument, ch->pcdata->linked_list, str_cmp) 
        && !IS_TRUSTED(ch, ANGEL))
        {
            char_act("You can't submit only your linked characters as sender.", ch);
            return;
        }
        if (!note_attach(ch, type))
            return;
        free_string(ch->pnote->from);
        ch->pnote->from = str_dup(argument);
        char_act("Ok.", ch);
        return;
    }

    if (!str_prefix(arg, "to")) {
        if (!note_attach(ch, type))
            return;

        free_string(ch->pnote->to_list);
        ch->pnote->to_list = str_dup(argument);
        char_act("Ok.", ch);
        return;
    }

    if (!str_prefix(arg, "forward"))
    {
        char buf[MAX_INPUT_LENGTH];

        argument = one_argument(argument, buf, sizeof(buf));
        if (!is_number(buf))
        {
            char_act("Forward which number?", ch);
            return;
        }

        anum = atoi(buf);
        vnum = 0;
        for (pnote = *list; pnote != NULL; pnote = pnote->next)
            if (is_note_to(ch, pnote) && vnum++ == anum)
                break;

        if (!pnote)
        {
            char_printf(ch, "There aren't that many %s.\n",
                    list_name);
            return;
        }

        if (!note_attach(ch, type))
            return;

        free_string(ch->pnote->text);
        ch->pnote->text = str_printf(
            "* Forwarded by: %s\n"
            "* Originally to: %s\n"
            "* Originally by: %s, %s\n"
            "\n"
            "---------- Forwarded message ----------\n"
            "%s",
            ch->name, 
            pnote->to_list, 
            format_from(pnote, fShow), 
            pnote->date,
            pnote->text);

        free_string(ch->pnote->subject);
        ch->pnote->subject = str_dup(pnote->subject);

        string_append(ch, &ch->pnote->text);
        return;
    }

    if (!str_prefix(arg, "quote")
    ||  !str_prefix(arg, "reply"))
    {
        char buf[MAX_INPUT_LENGTH];

        argument = one_argument(argument, buf, sizeof(buf));
        if (!is_number(buf))
        {
            char_act("Quote which number?", ch);
            return;
        }

        anum = atoi(buf);
        vnum = 0;
        for (pnote = *list; pnote != NULL; pnote = pnote->next)
            if (is_note_to(ch, pnote) && vnum++ == anum)
                break;

        if (!pnote) {
            char_printf(ch, "There aren't that many %s.\n",
                    list_name);
            return;
        }

        if (!note_attach(ch, type))
            return;

        free_string(ch->pnote->text);
        ch->pnote->text = quote_note(pnote, fShow);
        
        free_string(ch->pnote->to_list);
        ch->pnote->to_list = str_dup(pnote->sender);

        free_string(ch->pnote->subject);
        ch->pnote->subject = str_dup(pnote->subject);

        string_append(ch, &ch->pnote->text);
        return;
    }

    if (!str_prefix(arg, "clear") || !str_prefix(arg, "cancel"))
    {
        if (ch->pnote)
        {
            free_note(ch->pnote);
            ch->pnote = NULL;
        }

        char_act("Ok.", ch);
        return;
    }

    if (!str_prefix(arg, "show")) {
        BUFFER *output;

        if (!ch->pnote) {
            char_act("You have no note in progress.", ch);
            return;
        }

        if (ch->pnote->type != type) {
            char_act("You aren't working on that kind of note.", ch);
            return;
        }

        output = buf_new(-1);
        buf_printf(output, "{x%s: %s\n"
                   "{xTo: %s\n"
                   "{x%s\n"
                   "{x",
               format_from(ch->pnote, fShow), 
               ch->pnote->subject,
               ch->pnote->to_list,
               ch->pnote->text);
        page_to_char(buf_string(output), ch);
        buf_free(output);

        return;
    }

    if (!str_prefix(arg, "post") || !str_prefix(arg, "send")) {
        if (ch->pnote == NULL) {
            char_act("You have no note in progress.", ch);
            return;
        }

        if (ch->pnote->type != type) {
            char_act("You aren't working on that kind of note.", ch);
            return;
        }

        if (IS_NULLSTR(ch->pnote->to_list)) {
            char_act("You need to provide a recipient (name, clan name, all, or immortal).", ch);
            return;
        }

        if (IS_NULLSTR(ch->pnote->subject)) {
            char_act("You need to provide a subject.", ch);
            return;
        }

        if (IS_NULLSTR(ch->pnote->text)) {
            char_act("You need to provide a text.", ch);
            return;
        }
        
/*        if (!IS_IMMORTAL(ch) && ch->pcdata->questpoints < 20
            && !str_cmp("all", ch->pnote->to_list))
        {
            act_puts("You haven't enough quest points to write $t", ch, list_name, NULL, TO_CHAR | ACT_TRANS, POS_DEAD);
            return;
        }   
        
        if (!IS_IMMORTAL(ch) && !str_cmp("all", ch->pnote->to_list))
            ch->pcdata->questpoints -= 20;*/

        ch->pnote->next         = NULL;
        ch->pnote->date         = str_dup(strtime(current_time));
        ch->pnote->date_stamp       = current_time;

        append_note(ch->pnote);

        /* Show new note message */
        for (d = descriptor_list; d; d = d->next)
        {
            CHAR_DATA *fch = d->character;
            if (fch != NULL
            &&  fch != ch
            &&  is_note_to(fch, ch->pnote)
            &&  d->connected == CON_PLAYING)
            {
                if (IS_IMMORTAL(fch)
                && IS_SET(fch->comm, COMM_INFO))
                {
                    act_puts("{r[{RINFO{r]{W New $q1{$t} : ", fch, list_name, NULL, TO_CHAR | ACT_TRANS | ACT_NOLF, POS_DEAD);
                    act_puts3("From {Y$t{W to {M$T{W, Subject {C$U{W.{x", 
                                    fch,
                                    format_from(ch->pnote, fShow),
                                    ch->pnote->to_list,
                                    ch->pnote->subject,
                                    TO_CHAR, POS_DEAD);
                } else
                {
                    do_unread(fch, "login");
                }
            }
        }

        ch->pnote = NULL;
        return;
    }

    char_act("You can't do that.", ch);
}

void fwrite_note(FILE *fp, note_t *pnote)
{
    fprintf(fp, "Sender  %s~\n", fix_string(pnote->sender));
    fprintf(fp, "From    %s~\n", fix_string(pnote->from));
    fprintf(fp, "Date    %s~\n", fix_string(pnote->date));
    fprintf(fp, "Stamp   %ld\n", (long) pnote->date_stamp);
    fprintf(fp, "To      %s~\n", fix_string(pnote->to_list));
    fprintf(fp, "Subject %s~\n", fix_string(pnote->subject));
    fprintf(fp, "Text\n%s~\n",   fix_string(pnote->text));
}