/*
 *    muiffr.c        © by Martin Steppler
 *
 */

/*
 * $Id: muiffr.c 1.2 1994/04/16 15:58:23 steppler Exp $
 *
 * $Log: muiffr.c $
 * Revision 1.2  1994/04/16  15:58:23  steppler
 * DoOpenFont(): removed a WORD-READ enforcer hit
 *
 * Revision 1.1.1.2  1994/04/10  17:43:11  steppler
 * window title changed
 *
 * Revision 1.1.1.1  1994/04/10  16:38:08  steppler
 * use MUIFFR/Outbound as environment variable for the
 * outbound directory of request files
 * added statistics display to main window
 * added new gadgets to conf window due to introduction of
 * statistics display in the main window
 * removed bug from LoadReqFile(): filenames with passwords
 * including blanks are not split in several lines any longer
 *
 * Revision 1.1  1994/02/26  21:31:44  steppler
 * Initial revision
 *
 */

#include "muiffr.h"
#include "muiffr_locale.h"

#define PROGNAME "MUI Fido File Request"
#define PROGTITLE PROGNAME " 1.2"
#define VERSION "$VER: MUI Fido File Request 1.2 (16.04.94) $"
#define AUTHOR "Martin Steppler"
#define COPYRIGHT "© 1994 " AUTHOR
#define ADDRESS COPYRIGHT "\n\
Internet: steppler@pool.informatik.rwth-aachen.de\n\
ADSP: steppler@cookies.adsp.sub.org\n\
Fido: 2:242/7.12 steppler@mowgli.fido.de\n\
FidoLite: 2:2452/107.12\n"

#define CATALOGNAME "muiffr.catalog"

#define SEARCH_DOWN 0
#define SEARCH_UP 1

#define TO_UPPER(x) ((x >= 'a' && x <= 'z') ? (x - ' ') : (x))

__chip UWORD StopWatch[(1 + 16 + 1) * 2] =
{
    0x0000,0x0000,

    0x0400,0x07C0,
    0x0000,0x07C0,
    0x0100,0x0380,
    0x0000,0x07E0,
    0x07C0,0x1FF8,
    0x1FF0,0x3FEC,
    0x3FF8,0x7FDE,
    0x3FF8,0x7FBE,
    0x7FFC,0xFF7F,
    0x7EFC,0xFFFF,
    0x7FFC,0xFFFF,
    0x3FF8,0x7FFE,
    0x3FF8,0x7FFE,
    0x1FF0,0x3FFC,
    0x07C0,0x1FF8,
    0x0000,0x07E0,

    0x0000,0x0000
};

static int InitAll(void);
static void CleanUp(void);
static int DoOpenLibs(void);
static void DoOpenCatalog(void);
static void DoCloseCatalog(void);
static int DoNewMenu(void);
static void LVListDBClick(void);
static int CreateReqFileName(int quiet);
static void LoadReqFile(int quiet);
static void BoyerMoore(int direction);
static int MisCharSearch(UBYTE *list_str, int list_len, UBYTE *find_str, int len);
static void DoOpenFont(int quiet);
static int AppendPassword(void);
static void SetWaitPointer(APTR *win_obj);
static void ClearWaitPointer(APTR *win_obj);
static void Statistics(LONG bytes, int calc);
static void WorkAroundMuiBug(void);

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct LocaleBase *LocaleBase;
struct DosLibrary *DosBase;
struct Library *MUIMasterBase;
struct Library *DiskfontBase;
struct Library *XpkBase;

struct Application *app;

static enum
{
    ID_PROJECT, ID_OPEN, ID_SAVE, ID_LABEL1, ID_FIND, ID_LABEL2,
    ID_CLIP_CUT, ID_CLIP_COPY, ID_CLIP_PASTE, ID_LABEL3,
    ID_ABOUT, ID_LABEL4, ID_QUIT, ID_END,

    ID_LV_MAIN_LIST_DBCLICK,
    ID_LV_MAIN_REQUESTLIST_DBCLICK,
    ID_ST_MAIN_REQUEST_READY,
    ID_BT_MAIN_ALIASES,
    ID_ST_MAIN_ALIASES_READY,
    ID_BT_MAIN_CONFIGURATION,
    ID_BT_MAIN_PASSWORD,
    ID_ST_MAIN_PASSWORD_READY,
    ID_ST_MAIN_FIND_READY,
    ID_ST_MAIN_FIND_CHANGED,
    ID_BT_MAIN_FIND_NEXT,
    ID_BT_MAIN_FIND_PREV,
    ID_BT_MAIN_LOAD,
    ID_BT_MAIN_INSERT,
    ID_BT_MAIN_DELETE,
    ID_BT_MAIN_SAVE,
    ID_BT_MAIN_DOWNLOAD,

    ID_CONF_QUIT,
    ID_LV_CONF_NODELIST_DBCLICK,
    ID_ST_CONF_ALIAS_READY,
    ID_ST_CONF_NODE_READY,
    ID_ST_CONF_LIST_READY,
    ID_ST_CONF_FONT_READY,
    ID_ST_CONF_PHONE_READY,
    ID_ST_CONF_PASSWORD_READY,
    ID_ST_CONF_SPEED_READY,
    ID_ST_CONF_CHARGES_READY,
    ID_ST_CONF_TIME_READY,
    ID_ST_CONF_VIANUMBER_READY,
    ID_ST_CONF_VIAPHONE_READY,
    ID_ST_CONF_NODELIST_READY,
    ID_BT_CONF_NEW_NODE,
    ID_BT_CONF_DELETE_NODE,
    ID_CYA_CONF_TERM_LF,
    ID_CYA_CONF_TERM_CRLF,
    ID_SL_CONF_FILENAMEPOS,
    ID_SL_CONF_BYTESPOS,
    ID_BT_CONF_LOADCONFIG,
    ID_BT_CONF_SAVECONFIG,

    ID_MENU_CONFIGURATION, ID_MENU_CONF_NODES, ID_MENU_CONF_GLOBAL_SETTINGS,
    ID_MENU_CONF_LABEL1, ID_MENU_CONF_ABOUT, ID_MENU_CONF_LABEL2,
    ID_MENU_CONF_QUIT, ID_MENU_CONF_END,

    ID_ALI_QUIT,
    ID_LV_ALI_NODELIST_DBCLICK

};

static enum
{
    RGP_CONF_NODES, RGP_CONF_GLOBAL_SETTINGS
};

#define TextLabel(label) TextObject, \
    TextFrame, \
    MUIA_Text_PreParse, "\33r", \
    MUIA_Text_Contents, label, \
    MUIA_Weight, 0, \
    MUIA_FramePhantomHoriz, TRUE, \
    End

#define CentText(text) TextObject, \
    TextFrame, \
    MUIA_Text_PreParse, "\33c", \
    MUIA_Text_Contents, text, \
    End

int wbmain(struct WBStartup *wbs)
{
    return (main(0, (char **)wbs));
}

int main(int argc, char *argv[])
{
    int return_ok = RETURN_FAIL;
    int running = TRUE;
    APTR vg_Conf;

    if (!InitAll())
        goto abort;

    {
        UBYTE programpath[256], *programname;
        struct WBStartup *wbs;
        BPTR lock;

        if (argc)
            // shell startup
            GetProgramName(programpath, 256);
        else
        {
            // workbench startup
            wbs = (struct WBStartup *)argv;
            programname = wbs->sm_ArgList->wa_Name;
            lock = Lock(programname, ACCESS_READ);
            if (lock)
            {
                NameFromLock(lock, programpath, 256);
                UnLock(lock);
            }
            else
                strcpy(programpath, programname);
        }
        // get diskobj
        app->app_DiskObject = GetDiskObject(programpath);
    }

    app->app_cya_Conf_Termination[CYA_CONF_TERM_LF] = GetCatStr(MSG_GAD_CYA_CONF_TERM_LF);
    app->app_cya_Conf_Termination[CYA_CONF_TERM_CRLF] = GetCatStr(MSG_GAD_CYA_CONF_TERM_CRLF);
    app->app_rgp_Conf[RGP_CONF_NODES] = GetCatStr(MSG_GAD_RGP_CONF_NODES);
    app->app_rgp_Conf[RGP_CONF_GLOBAL_SETTINGS] = GetCatStr(MSG_GAD_RGP_CONF_GLOBAL_SETTINGS);

    app->app_st_Conf_List = KeyString(app->app_Conf_ListBuf, sizeof(app->app_Conf_ListBuf), GetCatHotKey(MSG_GAD_ST_CONF_LIST_KEY));
    app->app_im_Conf_List = PopButton(MUII_PopFile);
    app->app_st_Conf_Font = KeyString(app->app_Conf_FontBuf, sizeof(app->app_Conf_FontBuf), GetCatHotKey(MSG_GAD_ST_CONF_FONT_KEY));
    app->app_im_Conf_Font = PopButton(MUII_PopUp);

    // Configuration Window
    vg_Conf =  VGroup,
        Child, app->app_rg_Conf = RegisterGroup(app->app_rgp_Conf),
            MUIA_Register_Frame, TRUE,
            Child, VGroup,
                Child, app->app_lv_Conf_NodeList = ListviewObject,
                    MUIA_Listview_List , ListObject,
                        InputListFrame,
                        End,
                    End,
                Child, HGroup,
                    Child, ColGroup(6),
                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_ALIAS), GetCatHotKey(MSG_GAD_ST_CONF_ALIAS_KEY)),
                        Child, app->app_st_Conf_Alias = KeyString(app->app_Conf_AliasBuf, sizeof(app->app_Conf_AliasBuf), GetCatHotKey(MSG_GAD_ST_CONF_ALIAS_KEY)),

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_PHONE), GetCatHotKey(MSG_GAD_ST_CONF_PHONE_KEY)),
                        Child, app->app_st_Conf_Phone = StringObject,
                            StringFrame,
                            MUIA_String_Accept, "0123456789- ",
                            MUIA_String_Contents, app->app_Conf_PhoneBuf,
                            MUIA_String_MaxLen, sizeof(app->app_Conf_PhoneBuf),
                            MUIA_ControlChar, GetCatHotKey(MSG_GAD_ST_CONF_PHONE_KEY),
                            End,

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_CHARGES_PER_UNIT), GetCatHotKey(MSG_GAD_ST_CONF_CHARGES_PER_UNIT_KEY)),
                        Child, app->app_st_Conf_Charges = StringObject,
                            StringFrame,
                            MUIA_String_Accept, "0123456789",
                            MUIA_String_Contents, app->app_Conf_ChargesBuf,
                            MUIA_String_MaxLen, sizeof(app->app_Conf_ChargesBuf),
                            MUIA_ControlChar, GetCatHotKey(MSG_GAD_ST_CONF_CHARGES_PER_UNIT_KEY),
                            End,

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_NODE), GetCatHotKey(MSG_GAD_ST_CONF_NODE_KEY)),
                        Child, app->app_st_Conf_Node = StringObject,
                            StringFrame,
                            MUIA_String_Accept, "0123456789:/.",
                            MUIA_String_Contents, app->app_Conf_NodeBuf,
                            MUIA_String_MaxLen, sizeof(app->app_Conf_NodeBuf),
                            MUIA_ControlChar, GetCatHotKey(MSG_GAD_ST_CONF_NODE_KEY),
                            End,

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_BIT_PER_SEC), GetCatHotKey(MSG_GAD_ST_CONF_BIT_PER_SEC_KEY)),
                        Child, app->app_st_Conf_Speed = StringObject,
                            StringFrame,
                            MUIA_String_Accept, "0123456789",
                            MUIA_String_Contents, app->app_Conf_SpeedBuf,
                            MUIA_String_MaxLen, sizeof(app->app_Conf_SpeedBuf),
                            MUIA_ControlChar, GetCatHotKey(MSG_GAD_ST_CONF_BIT_PER_SEC_KEY),
                            End,

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_TIME_PER_UNIT), GetCatHotKey(MSG_GAD_ST_CONF_TIME_PER_UNIT_KEY)),
                        Child, app->app_st_Conf_Time = StringObject,
                            StringFrame,
                            MUIA_String_Accept, "0123456789",
                            MUIA_String_Contents, app->app_Conf_TimeBuf,
                            MUIA_String_MaxLen, sizeof(app->app_Conf_TimeBuf),
                            MUIA_ControlChar, GetCatHotKey(MSG_GAD_ST_CONF_TIME_PER_UNIT_KEY),
                            End,

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_PASSWORD), GetCatHotKey(MSG_GAD_ST_CONF_PASSWORD_KEY)),
                        Child, app->app_st_Conf_Password = StringObject,
                            StringFrame,
                            MUIA_String_Contents, app->app_Conf_PasswordBuf,
                            MUIA_String_MaxLen, sizeof(app->app_Conf_PasswordBuf),
                            MUIA_ControlChar, GetCatHotKey(MSG_GAD_ST_CONF_PASSWORD_KEY),
                            MUIA_String_Secret, TRUE,
                            End,

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_LIST), GetCatHotKey(MSG_GAD_ST_CONF_LIST_KEY)),
                        Child, app->app_pa_Conf_List = PopaslObject,
                            MUIA_Popstring_String, app->app_st_Conf_List,
                            MUIA_Popstring_Button, app->app_im_Conf_List,
                            ASLFR_TitleText, GetCatStr(MSG_INFO_SELECT_LIST),
                            End,

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_FONT), GetCatHotKey(MSG_GAD_ST_CONF_FONT_KEY)),
                        Child, app->app_pa_Conf_Font = PopaslObject,
                            MUIA_Popstring_String, app->app_st_Conf_Font,
                            MUIA_Popstring_Button, app->app_im_Conf_Font,
                            MUIA_Popasl_Type, ASL_FontRequest,
                            ASLFO_TitleText, GetCatStr(MSG_INFO_SELECT_FONT),
                            End,
                        End,
                    End,
                Child, HGroup,
                    GroupSpacing(0),
                    Child, app->app_bt_Conf_NewNode = KeyButton(GetCatStr(MSG_GAD_BT_CONF_NEW_NODE), GetCatHotKey(MSG_GAD_BT_CONF_NEW_NODE_KEY)),
                    Child, app->app_bt_Conf_DeleteNode = KeyButton(GetCatStr(MSG_GAD_BT_CONF_DELETE_NODE), GetCatHotKey(MSG_GAD_BT_CONF_DELETE_NODE_KEY)),
                    End,
                End,

            Child, VGroup,
                Child, VGroup,
                    GroupFrameT(GetCatStr(MSG_GAD_GP_CONF_SETTINGS_DOWNLOAD)),

                    Child, ColGroup(2),
                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_VIANUMBER), GetCatHotKey(MSG_GAD_ST_CONF_VIANUMBER_KEY)),
                        Child, app->app_st_Conf_ViaNumber = KeyString(app->app_Conf_ViaNumberBuf, sizeof(app->app_Conf_ViaNumberBuf), GetCatHotKey(MSG_GAD_ST_CONF_VIANUMBER_KEY)),

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_VIANODE), GetCatHotKey(MSG_GAD_ST_CONF_VIANODE_KEY)),
                        Child, app->app_st_Conf_ViaNode = KeyString(app->app_Conf_ViaNodeBuf, sizeof(app->app_Conf_ViaNodeBuf), GetCatHotKey(MSG_GAD_ST_CONF_VIANODE_KEY)),

                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_NODELIST), GetCatHotKey(MSG_GAD_ST_CONF_NODELIST_KEY)),
                        Child, app->app_pa_Conf_NodeList = PopaslObject,
                            MUIA_Popstring_String, app->app_st_Conf_NodeList = KeyString(app->app_Conf_NodeListBuf, sizeof(app->app_Conf_NodeListBuf), GetCatHotKey(MSG_GAD_ST_CONF_NODELIST_KEY)),
                            MUIA_Popstring_Button, app->app_im_Conf_NodeList = PopButton(MUII_PopDrawer),
                            ASLFR_TitleText, GetCatStr(MSG_INFO_SELECT_DIR),
                            ASLFR_DrawersOnly, TRUE,
                            End,
                        End,
                    End,

                Child, VGroup,
                    GroupFrameT(GetCatStr(MSG_GAD_GP_CONF_SETTINGS_MISC)),

                    Child, ColGroup(2),
                        Child, KeyLabel2(GetCatStr(MSG_GAD_ST_CONF_OUTBOUND), GetCatHotKey(MSG_GAD_ST_CONF_OUTBOUND_KEY)),
                        Child, app->app_pa_Conf_Outbound = PopaslObject,
                            MUIA_Popstring_String, app->app_st_Conf_Outbound = KeyString(app->app_Conf_OutboundBuf, sizeof(app->app_Conf_OutboundBuf), GetCatHotKey(MSG_GAD_ST_CONF_OUTBOUND_KEY)),
                            MUIA_Popstring_Button, app->app_im_Conf_Outbound = PopButton(MUII_PopDrawer),
                            ASLFR_TitleText, GetCatStr(MSG_INFO_SELECT_DIR),
                            ASLFR_DrawersOnly, TRUE,
                            End,

                        Child, KeyLabel1(GetCatStr(MSG_GAD_CY_CONF_TERMINATION), GetCatHotKey(MSG_GAD_CY_CONF_TERMINATION_KEY)),
                        Child, app->app_cy_Conf_Termination = KeyCycle(app->app_cya_Conf_Termination, GetCatHotKey(MSG_GAD_CY_CONF_TERMINATION_KEY)),

                        Child, KeyLabel1(GetCatStr(MSG_GAD_SL_CONF_FILENAMEPOS), GetCatHotKey(MSG_GAD_SL_CONF_FILENAMEPOS_KEY)),
                        Child, app->app_sl_Conf_FileNamePos = KeySlider(1, MAX_FILENAMEPOS, 1, GetCatHotKey(MSG_GAD_SL_CONF_FILENAMEPOS_KEY)),

                        Child, KeyLabel1(GetCatStr(MSG_GAD_SL_CONF_BYTESPOS), GetCatHotKey(MSG_GAD_SL_CONF_BYTESPOS_KEY)),
                        Child, app->app_sl_Conf_BytesPos = KeySlider(1, MAX_BYTESPOS, 2, GetCatHotKey(MSG_GAD_SL_CONF_BYTESPOS_KEY)),
                        End,
                    End,
                End,
            End,

        Child, HGroup,
            GroupSpacing(0),
            Child, app->app_bt_Conf_LoadConfig = KeyButton(GetCatStr(MSG_GAD_BT_CONF_LOADCONFIG), GetCatHotKey(MSG_GAD_BT_CONF_LOADCONFIG_KEY)),
            Child, app->app_bt_Conf_SaveConfig = KeyButton(GetCatStr(MSG_GAD_BT_CONF_SAVECONFIG), GetCatHotKey(MSG_GAD_BT_CONF_SAVECONFIG_KEY)),
            End,
        End;

    app->app_Muiffr = ApplicationObject,
        MUIA_Application_Title         , PROGNAME,
        MUIA_Application_Version       , VERSION,
        MUIA_Application_Copyright     , COPYRIGHT,
        MUIA_Application_Author        , AUTHOR,
        MUIA_Application_Description   , GetCatStr(MSG_ABOUT_DESCRIPTION),
        MUIA_Application_Base          , "MUIFFR",
        MUIA_Application_Menu          , app->app_NewMenu,
        MUIA_Application_DiskObject    , app->app_DiskObject,

        SubWindow,
            app->app_wi_Alias = WindowObject,
            MUIA_Window_Title, GetCatStr(MSG_GAD_GP_ALI_ALIAS),
            MUIA_Window_ID, MAKE_ID('A','L','I','A'),
            MUIA_Window_Menu, MUIV_Window_Menu_NoMenu,
            WindowContents, VGroup,
                Child, VGroup,
                    GroupFrameT(GetCatStr(MSG_GAD_GP_ALI_SELECT)),
                    Child, app->app_lv_Ali_NodeList = ListviewObject,
                        MUIA_Listview_List , ListObject,
                            InputListFrame,
                            End,
                        End,
                    End,
                End,
            End,

        SubWindow,
            app->app_wi_Configuration = WindowObject,
            MUIA_Window_Title, GetCatStr(MSG_GAD_GP_CONF_CONFIGURATION),
            MUIA_Window_ID, MAKE_ID('C','O','N','F'),
            MUIA_Window_Menu, app->app_Conf_NewMenu,
            WindowContents, vg_Conf,
            End,

        SubWindow,
            app->app_wi_Main = WindowObject,
            MUIA_Window_Title, PROGTITLE,
            MUIA_Window_ID, MAKE_ID('F','F','R','Q'),

            WindowContents, VGroup,
                Child, HGroup,
                    GroupFrameT(GetCatStr(MSG_GAD_GP_MAIN_LIST)),
                    Child, app->app_lv_Main_List = ListviewObject,
                        MUIA_Listview_List , ListObject,
                            InputListFrame,
                            End,
                        MUIA_Font, MUIV_Font_Fixed,
                        End,
                    End,

                Child, HGroup,
                    Child, VGroup,
                        GroupFrameT(GetCatStr(MSG_GAD_GP_MAIN_SELECTED)),
                        Child, VGroup,
                            GroupSpacing(0),
                            Child, app->app_lv_Main_RequestList = ListviewObject,
                                MUIA_Listview_List, ListObject,
                                    InputListFrame,
                                    End,
                                MUIA_Font, MUIV_Font_Fixed,
                                MUIA_Listview_MultiSelect, TRUE,
                                End,
                            Child, app->app_st_Main_Request = String(app->app_Main_RequestBuf, sizeof(app->app_Main_RequestBuf)),
                            End,
                        Child, HGroup,
                            Child, TextLabel(GetCatStr(MSG_GAD_TX_MAIN_BYTES)),
                            Child, app->app_tx_Main_Bytes = CentText("0000K"),
                            Child, TextLabel(GetCatStr(MSG_GAD_TX_MAIN_TIME)),
                            Child, app->app_tx_Main_Time = CentText("00:00:00"),
                            Child, TextLabel(GetCatStr(MSG_GAD_TX_MAIN_CHARGES)),
                            Child, app->app_tx_Main_Charges = CentText("0000.00"),
                            End,
                        End,

                    Child, VGroup,
                        Child, VGroup,
                            GroupFrameT(GetCatStr(MSG_GAD_GP_MAIN_CONFIGURATION)),
                            GroupSpacing(1),
                            Child, HGroup,
                                GroupSpacing(0),
                                Child, app->app_bt_Main_Aliases = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_ALIASES), GetCatHotKey(MSG_GAD_BT_MAIN_ALIASES_KEY)),
                                Child, app->app_bt_Main_Configuration = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_CONFIGURATION), GetCatHotKey(MSG_GAD_BT_MAIN_CONFIGURATION_KEY)),
                                End,
                            Child, app->app_st_Main_Aliases = StringObject,
                                StringFrame,
                                MUIA_String_Contents, app->app_Main_AliasesBuf,
                                MUIA_String_MaxLen, sizeof(app->app_Main_AliasesBuf),
                                MUIA_String_Format, MUIV_String_Format_Center,
                                End,
                            End,

                        Child, VGroup,
                            GroupFrame,
                            GroupSpacing(1),
                            Child, HGroup,
                                GroupSpacing(1),
                                Child, app->app_bt_Main_Password = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_PASSWORD), GetCatHotKey(MSG_GAD_BT_MAIN_PASSWORD_KEY)),
                                Child, app->app_st_Main_Password = StringObject,
                                    StringFrame,
                                    MUIA_String_Contents, app->app_Main_PasswordBuf,
                                    MUIA_String_MaxLen, sizeof(app->app_Main_PasswordBuf),
                                    MUIA_String_Secret, TRUE,
                                    End,
                                End,
                            Child, HGroup,
                                Child, KeyLabel2(GetCatStr(MSG_GAD_ST_MAIN_FIND), GetCatHotKey(MSG_GAD_ST_MAIN_FIND_KEY)),
                                Child, app->app_st_Main_Find = KeyString(app->app_Main_FindBuf, sizeof(app->app_Main_FindBuf), GetCatHotKey(MSG_GAD_ST_MAIN_FIND_KEY)),
                                End,

                            Child, VGroup,
                                GroupSpacing(0),
                                Child, ColGroup(2),
                                    GroupSpacing(0),
                                    Child, app->app_bt_Main_FindNext = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_FIND_NEXT), GetCatHotKey(MSG_GAD_BT_MAIN_FIND_NEXT_KEY)),
                                    Child, app->app_bt_Main_FindPrev = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_FIND_PREV), GetCatHotKey(MSG_GAD_BT_MAIN_FIND_PREV_KEY)),

                                    Child, app->app_bt_Main_Insert = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_INSERT), GetCatHotKey(MSG_GAD_BT_MAIN_INSERT_KEY)),
                                    Child, app->app_bt_Main_Delete = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_DELETE), GetCatHotKey(MSG_GAD_BT_MAIN_DELETE_KEY)),
                                    End,

                                Child, HGroup,
                                    GroupSpacing(0),
                                    Child, app->app_bt_Main_Load = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_LOAD), GetCatHotKey(MSG_GAD_BT_MAIN_LOAD_KEY)),
                                    Child, app->app_bt_Main_Save = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_SAVE), GetCatHotKey(MSG_GAD_BT_MAIN_SAVE_KEY)),
                                    Child, app->app_bt_Main_Download = KeyButton(GetCatStr(MSG_GAD_BT_MAIN_DOWNLOAD), GetCatHotKey(MSG_GAD_BT_MAIN_DOWNLOAD_KEY)),
                                    End,
                                End,
                            End,
                        End,
                    End,
                End,
            End,
        End;

    if (!app->app_Muiffr)
    {
        puts("Unable to create application.");
        goto abort;
    }

    // Main Window
    DoMethod(app->app_wi_Main, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_QUIT);
    DoMethod(app->app_lv_Main_List, MUIM_Notify, MUIA_Listview_DoubleClick, TRUE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_LV_MAIN_LIST_DBCLICK);
    DoMethod(app->app_lv_Main_RequestList, MUIM_Notify, MUIA_Listview_DoubleClick, TRUE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_LV_MAIN_REQUESTLIST_DBCLICK);
    DoMethod(app->app_st_Main_Request, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_MAIN_REQUEST_READY);
    DoMethod(app->app_bt_Main_Aliases, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_ALIASES);
    DoMethod(app->app_st_Main_Aliases, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_MAIN_ALIASES_READY);
    DoMethod(app->app_bt_Main_Configuration, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_CONFIGURATION);
    DoMethod(app->app_bt_Main_Password, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_PASSWORD);
    DoMethod(app->app_st_Main_Password, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_MAIN_PASSWORD_READY);
    DoMethod(app->app_st_Main_Find, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_MAIN_FIND_READY);
    DoMethod(app->app_st_Main_Find, MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_MAIN_FIND_CHANGED);
    DoMethod(app->app_bt_Main_FindNext, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_FIND_NEXT);
    DoMethod(app->app_bt_Main_FindPrev, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_FIND_PREV);
    DoMethod(app->app_bt_Main_Load, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_LOAD);
    DoMethod(app->app_bt_Main_Insert, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_INSERT);
    DoMethod(app->app_bt_Main_Delete, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_DELETE);
    DoMethod(app->app_bt_Main_Save, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_SAVE);
    DoMethod(app->app_bt_Main_Download, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_MAIN_DOWNLOAD);

    DoMethod(app->app_wi_Main, MUIM_Window_SetCycleChain,
             app->app_lv_Main_List,
             app->app_lv_Main_RequestList,
             app->app_st_Main_Request,
             app->app_bt_Main_Aliases,
             app->app_bt_Main_Configuration,
             app->app_st_Main_Aliases,
             app->app_bt_Main_Password,
             app->app_st_Main_Password,
             app->app_st_Main_Find,
             app->app_bt_Main_FindNext,
             app->app_bt_Main_FindPrev,
             app->app_bt_Main_Insert,
             app->app_bt_Main_Delete,
             app->app_bt_Main_Load,
             app->app_bt_Main_Save,
             app->app_bt_Main_Download,
             NULL);

    // Configuration Window
    DoMethod(app->app_wi_Configuration, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_CONF_QUIT);
    DoMethod(app->app_lv_Conf_NodeList, MUIM_Notify, MUIA_Listview_DoubleClick, TRUE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_LV_CONF_NODELIST_DBCLICK);
    DoMethod(app->app_st_Conf_Alias, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_CONF_ALIAS_READY);
    DoMethod(app->app_st_Conf_Phone, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_CONF_PHONE_READY);
    DoMethod(app->app_st_Conf_Node, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_CONF_NODE_READY);
    DoMethod(app->app_st_Conf_Password, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_CONF_PASSWORD_READY);
    DoMethod(app->app_st_Conf_Speed, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_CONF_SPEED_READY);
    DoMethod(app->app_st_Conf_Charges, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_CONF_CHARGES_READY);
    DoMethod(app->app_st_Conf_Time, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_CONF_TIME_READY);
    DoMethod(app->app_st_Conf_List, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_CONF_LIST_READY);
    DoMethod(app->app_st_Conf_Font, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ST_CONF_FONT_READY);
    DoMethod(app->app_bt_Conf_NewNode, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_CONF_NEW_NODE);
    DoMethod(app->app_bt_Conf_DeleteNode, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_CONF_DELETE_NODE);
    DoMethod(app->app_cy_Conf_Termination, MUIM_Notify, MUIA_Cycle_Active, CYA_CONF_TERM_LF, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_CYA_CONF_TERM_LF);
    DoMethod(app->app_cy_Conf_Termination, MUIM_Notify, MUIA_Cycle_Active, CYA_CONF_TERM_CRLF, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_CYA_CONF_TERM_CRLF);
    DoMethod(app->app_sl_Conf_FileNamePos, MUIM_Notify, MUIA_Slider_Level, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_SL_CONF_FILENAMEPOS);
    DoMethod(app->app_sl_Conf_BytesPos, MUIM_Notify, MUIA_Slider_Level, MUIV_EveryTime, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_SL_CONF_BYTESPOS);
    DoMethod(app->app_bt_Conf_LoadConfig, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_CONF_LOADCONFIG);
    DoMethod(app->app_bt_Conf_SaveConfig, MUIM_Notify, MUIA_Pressed, FALSE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_BT_CONF_SAVECONFIG);

    DoMethod(app->app_wi_Configuration, MUIM_Window_SetCycleChain,
             app->app_lv_Conf_NodeList,
             app->app_st_Conf_Alias,
             app->app_st_Conf_Node,
             app->app_st_Conf_Phone,
             app->app_st_Conf_Speed,
             app->app_st_Conf_Charges,
             app->app_st_Conf_Time,
             app->app_st_Conf_Password,
             app->app_st_Conf_List,
             app->app_im_Conf_List,
             app->app_st_Conf_Font,
             app->app_im_Conf_Font,
             app->app_bt_Conf_NewNode,
             app->app_bt_Conf_DeleteNode,
             app->app_st_Conf_ViaNumber,
             app->app_st_Conf_ViaNode,
             app->app_st_Conf_NodeList,
             app->app_im_Conf_NodeList,
             app->app_st_Conf_Outbound,
             app->app_im_Conf_Outbound,
             app->app_cy_Conf_Termination,
             app->app_sl_Conf_FileNamePos,
             app->app_sl_Conf_BytesPos,
             app->app_bt_Conf_LoadConfig,
             app->app_bt_Conf_SaveConfig,
             NULL);

    // Alias Window
    DoMethod(app->app_wi_Alias, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_ALI_QUIT);
    DoMethod(app->app_lv_Ali_NodeList, MUIM_Notify, MUIA_Listview_DoubleClick, TRUE, app->app_Muiffr, 2, MUIM_Application_ReturnID, ID_LV_ALI_NODELIST_DBCLICK);

    if (LoadConfig(TRUE))
        LoadReqFile(TRUE);

    // default objects
    set(app->app_wi_Main, MUIA_Window_DefaultObject, app->app_lv_Main_List);
    set(app->app_wi_Configuration, MUIA_Window_DefaultObject, app->app_lv_Conf_NodeList);
    set(app->app_wi_Alias, MUIA_Window_DefaultObject, app->app_lv_Ali_NodeList);

    // active object
    set(app->app_wi_Main, MUIA_Window_ActiveObject, app->app_lv_Main_List);

    // open window
    set(app->app_wi_Main, MUIA_Window_Open, TRUE);

    WorkAroundMuiBug();

    while (running)
    {
        ULONG signal, id;

        switch (id = DoMethod(app->app_Muiffr, MUIM_Application_Input, &signal))
        {
        case ID_CLIP_CUT:
            CutClip();
            break;

        case ID_CLIP_COPY:
            CopyClip();
            break;

        case ID_CLIP_PASTE:
            PasteClip();
            break;

        case ID_ABOUT:
            MUI_Request(app->app_Muiffr, app->app_wi_Main, 0, NULL,
                        GetCatStr(MSG_OKAY),
                        "\33u%s\33n\n\n%s\n\n%s\n%s\n\n%s %s",
                        PROGTITLE,
                        GetCatStr(MSG_ABOUT_DESCRIPTION),
                        ADDRESS,
                        GetCatStr(MSG_ABOUT_COPYRIGHT),
                        GetCatStr(MSG_ABOUT_MUI),
                        "Stefan Stuntz.",
                        TAG_END);
            break;

        case MUIV_Application_ReturnID_Quit :
        case ID_QUIT:
            {
                LONG active;

                get(app->app_pa_Conf_List, MUIA_Popasl_Active, &active);
                if (!active)
                    get(app->app_pa_Conf_Font, MUIA_Popasl_Active, &active);
                if (!active)
                    get(app->app_pa_Conf_Outbound, MUIA_Popasl_Active, &active);

                if (active)
                    DispError(MSG_ERROR_ASL_POPUPS, NULL);
                else
                    running = FALSE;
            }
            break;

        case ID_LV_MAIN_LIST_DBCLICK:
            LVListDBClick();
            break;

        case ID_LV_MAIN_REQUESTLIST_DBCLICK:
            {
                LONG pos;
                UBYTE *line;

                // cursor position
                get(app->app_lv_Main_RequestList, MUIA_List_Active, &pos);

                if (pos >= 0)
                {
                    // get active line
                    DoMethod(app->app_lv_Main_RequestList, MUIM_List_GetEntry, pos, &line);
                    // set string
                    set(app->app_st_Main_Request, MUIA_String_Contents, line);
                }
            }
            break;

        case ID_ST_MAIN_REQUEST_READY:
        case ID_BT_MAIN_INSERT:
            {
                UBYTE *buf;

                get(app->app_st_Main_Request, MUIA_String_Contents, &buf);
                if (*buf)
                {
                    AddListEntry(buf, NULL, FALSE);
                    set(app->app_wi_Main, MUIA_Window_ActiveObject, app->app_st_Main_Request);
                }
            }
            break;

        case ID_BT_MAIN_ALIASES:
            if (!app->app_NodeList)
                DispError(MSG_ERROR_NO_ALIAS, NULL);
            else
            {
                LONG pos = -1;
                struct NodeList *nl = app->app_NodeList;

                set(app->app_wi_Configuration, MUIA_Window_Open, FALSE);
                get(app->app_lv_Conf_NodeList, MUIA_List_Active, &pos);

                for (; nl; nl = nl->nl_Next)
                    DoMethod(app->app_lv_Ali_NodeList, MUIM_List_Insert,
                             &nl->nl_Alias, 1, MUIV_List_Insert_Bottom);
                set(app->app_lv_Ali_NodeList, MUIA_List_Active, pos);
                set(app->app_wi_Alias, MUIA_Window_ActiveObject, app->app_lv_Ali_NodeList);
                set(app->app_wi_Alias, MUIA_Window_Open, TRUE);
                set(app->app_bt_Main_Configuration, MUIA_Disabled, TRUE);
                set(app->app_bt_Main_Aliases, MUIA_Disabled, TRUE);
            }
            break;

        case ID_ST_MAIN_ALIASES_READY:
            {
                LONG pos = 0, count = -1;
                struct NodeList *nl = app->app_NodeList;
                UBYTE *buf;

                if (!nl)
                    DispError(MSG_ERROR_NO_ALIAS, NULL);
                else
                {
                    get(app->app_lv_Conf_NodeList, MUIA_List_Entries, &count);
                    get(app->app_st_Main_Aliases, MUIA_String_Contents, &buf);

                    for (; pos < count; pos++, nl = nl->nl_Next)
                        if (!strcmp(buf, nl->nl_Alias))
                            break;

                    if (pos < count)
                    {
                        // refresh configuration window
                        set(app->app_lv_Conf_NodeList, MUIA_List_Active, pos);
                        NodeListDoubleClick();
                        ReadList(FALSE);
                        LoadReqFile(TRUE);
                    }
                    else
                        DispError(MSG_ERROR_UNKNOWN_ALIAS, buf);
                }
            }
            break;

        case ID_BT_MAIN_CONFIGURATION:
            set(app->app_wi_Configuration, MUIA_Window_Open, TRUE);
            set(app->app_bt_Main_Configuration, MUIA_Disabled, TRUE);
            break;

        case ID_BT_MAIN_PASSWORD:
        case ID_ST_MAIN_PASSWORD_READY:
            AppendPassword();
            break;

        case ID_FIND:
            set(app->app_wi_Main, MUIA_Window_ActiveObject, app->app_st_Main_Find);
            // no break;

        case ID_ST_MAIN_FIND_CHANGED:
            app->app_Flags |= APP_FIND_CHANGED;
            break;

        case ID_ST_MAIN_FIND_READY:
        case ID_BT_MAIN_FIND_NEXT:
            BoyerMoore(SEARCH_DOWN);
            break;

        case ID_BT_MAIN_FIND_PREV:
            BoyerMoore(SEARCH_UP);
            break;

        case ID_BT_MAIN_LOAD:
        case ID_OPEN:
            LoadReqFile(FALSE);
            break;

        case ID_BT_MAIN_DELETE:
            DeleteListEntry();
            break;

        case ID_BT_MAIN_SAVE:
        case ID_SAVE:
            SaveReqFile();
            break;

        case ID_BT_MAIN_DOWNLOAD:
            Download();
            break;

        // Configuration Window
        case ID_CONF_QUIT:
            set(app->app_bt_Main_Configuration, MUIA_Disabled, FALSE);
            set(app->app_wi_Configuration, MUIA_Window_Open, FALSE);
            break;

        case ID_LV_CONF_NODELIST_DBCLICK:
            NodeListDoubleClick();
            break;

        case ID_ST_CONF_ALIAS_READY:
            ConfAliasReady();
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Node);
            break;

        case ID_ST_CONF_NODE_READY:
            UpdateNodelist(NODE_BUF_NUM);
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Phone);
            break;

        case ID_ST_CONF_PHONE_READY:
            UpdateNodelist(PHONE_BUF_NUM);
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Speed);
            break;

        case ID_ST_CONF_SPEED_READY:
            UpdateNodelist(SPEED_BUF_NUM);
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Charges);
            break;

        case ID_ST_CONF_CHARGES_READY:
            UpdateNodelist(CHARGES_BUF_NUM);
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Time);
            break;

        case ID_ST_CONF_TIME_READY:
            UpdateNodelist(TIME_BUF_NUM);
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Password);
            break;

        case ID_ST_CONF_PASSWORD_READY:
            UpdateNodelist(PASSWORD_BUF_NUM);
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_List);
            break;

        case ID_ST_CONF_LIST_READY:
            UpdateNodelist(LIST_BUF_NUM);
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Font);
            break;

        case ID_ST_CONF_FONT_READY:
            UpdateNodelist(FONT_BUF_NUM);
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Alias);
            break;

        case ID_BT_CONF_NEW_NODE:
            AddNodeListEntryInteractively();
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Alias);
            break;

        case ID_BT_CONF_DELETE_NODE:
            DeleteNodeListEntry();
            set(app->app_wi_Configuration, MUIA_Window_ActiveObject, app->app_st_Conf_Alias);
            break;

        case ID_CYA_CONF_TERM_LF:
            app->app_Flags &= ~APP_CYA_CONF_TERM_CRLF;
            break;

        case ID_CYA_CONF_TERM_CRLF:
            app->app_Flags |= APP_CYA_CONF_TERM_CRLF;
            break;

        case ID_SL_CONF_FILENAMEPOS:
            break;

        case ID_SL_CONF_BYTESPOS:
            break;

        case ID_BT_CONF_LOADCONFIG:
            SetWaitPointer(app->app_wi_Configuration);
            SetWaitPointer(app->app_wi_Main);
            LoadConfig(FALSE);
            ClearWaitPointer(app->app_wi_Configuration);
            ClearWaitPointer(app->app_wi_Main);
            break;

        case ID_BT_CONF_SAVECONFIG:
            SaveConfig();
            break;

        case ID_MENU_CONF_NODES:
            set(app->app_rg_Conf, MUIA_Group_ActivePage, 0);
            break;

        case ID_MENU_CONF_GLOBAL_SETTINGS:
            set(app->app_rg_Conf, MUIA_Group_ActivePage, 1);
            break;

        // Alias Window
        case ID_ALI_QUIT:
            set(app->app_bt_Main_Configuration, MUIA_Disabled, FALSE);
            set(app->app_bt_Main_Aliases, MUIA_Disabled, FALSE);
            set(app->app_wi_Alias, MUIA_Window_Open, FALSE);
            DoMethod(app->app_lv_Ali_NodeList, MUIM_List_Clear, TAG_IGNORE);
            break;

        case ID_LV_ALI_NODELIST_DBCLICK:
            {
                LONG pos = -1, pos_bak;
                struct NodeList *nl = app->app_NodeList;

                // close window
                set(app->app_wi_Alias, MUIA_Window_Open, FALSE);
                get(app->app_lv_Ali_NodeList, MUIA_List_Active, &pos);

                SetWaitPointer(app->app_wi_Main);
                pos_bak = pos;
                if (pos >= 0)
                {
                    // jump to selected entry
                    while (pos > 0)
                    {
                        nl = nl->nl_Next;
                        pos--;
                    }
                    // set alias str in main window
                    strcpy(app->app_Main_AliasesBuf, nl->nl_Alias);
                    set(app->app_st_Main_Aliases, MUIA_String_Contents, app->app_Main_AliasesBuf);
                    // refresh configuration window
                    set(app->app_lv_Conf_NodeList, MUIA_List_Active, pos_bak);
                    NodeListDoubleClick();
                    ReadList(FALSE);
                    LoadReqFile(TRUE);
                }
                set(app->app_bt_Main_Configuration, MUIA_Disabled, FALSE);
                set(app->app_bt_Main_Aliases, MUIA_Disabled, FALSE);
                DoMethod(app->app_lv_Ali_NodeList, MUIM_List_Clear, TAG_IGNORE);
                ClearWaitPointer(app->app_wi_Main);
            }
            break;

        default:
            if (id)
                printf("ID: %d = %08lx\n", id, id);
            break;
        }

        if (running && signal)
            Wait(signal);
    }

    return_ok = RETURN_OK;
abort:
    CleanUp();
    return (return_ok);
}

static void CleanUp(void)
{
    if (app)
    {
        if (app->app_Muiffr)
            MUI_DisposeObject(app->app_Muiffr);
        if (app->app_DiskObject)
            FreeDiskObject(app->app_DiskObject);
        if (app->app_NewMenu)
            FreeVec(app->app_NewMenu);
        if (app->app_Conf_NewMenu)
            FreeVec(app->app_Conf_NewMenu);
        if (app->app_List)
        {
            UBYTE **list_bak = app->app_List;

            do
            {
                // free strings
                if (*list_bak)
                    FreeVec(*list_bak++);
            }
            while (*list_bak);
            FreeVec(app->app_List);
        }
        if (app->app_RequestList)
        {
            struct RequestList *rl = app->app_RequestList, *rl_next;

            do
            {
                if (rl->rl_Buffer)
                    FreeVec(rl->rl_Buffer);
                rl_next = rl->rl_Next;
                FreeVec(rl);
                rl = rl_next;
            }
            while (rl);
        }
        ClearNodeList();
        if (app->app_Font)
            CloseFont(app->app_Font);
    }
    DoCloseCatalog();
    if (app)
        FreeVec(app);
    if (MUIMasterBase)
        CloseLibrary(MUIMasterBase);
    if (IconBase)
        CloseLibrary(IconBase);
    if (DosBase)
        CloseLibrary((struct Library *)DosBase);
    if (GfxBase)
        CloseLibrary((struct Library *)GfxBase);
    if (IntuitionBase)
        CloseLibrary((struct Library *)IntuitionBase);
}

static int InitAll(void)
{
    int return_ok = FALSE;

    if (!DoOpenLibs())
        goto abort;

    if (!(app = (struct Application *)AllocVec(sizeof(struct Application), MEMF_CLEAR)))
    {
        puts("Out of memory!");
        goto abort;
    }

    DoOpenCatalog();
    if (!DoNewMenu())
        goto abort;

    return_ok = TRUE;
abort:
    return (return_ok);
}

static int DoOpenLibs(void)
{
    int return_ok = FALSE;

    if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 37)))
    {
        puts("Unable to open intuition.library.");
        goto abort;
    }

    if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 37)))
    {
        puts("Unable to open graphics.library.");
        goto abort;
    }

    if (!(DosBase = (struct DosLibrary *)OpenLibrary("dos.library", 37)))
    {
        puts("Unable to open dos.library.");
        goto abort;
    }

    if (!(IconBase = OpenLibrary("icon.library", 37)))
    {
        puts("Unable to open icon.library.");
        goto abort;
    }

    if (!(MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN)))
    {
        puts("Unable to open muimaster.library.");
        goto abort;
    }

    return_ok = TRUE;
abort:
    return (return_ok);
}

static void DoOpenCatalog(void)
{
    if (LocaleBase = (struct LocaleBase *)OpenLibrary("locale.library", 38))
    {
        app->app_Catalog = OpenCatalog(NULL, (STRPTR)CATALOGNAME,
                                  OC_BuiltInLanguage, (ULONG)"deutsch",
                                  TAG_DONE);
        if (!app->app_Catalog)
        {
            // use builtin language
            CloseLibrary((struct Library *)LocaleBase);
            LocaleBase = NULL;
        }
    }
}

static void DoCloseCatalog(void)
{
    if (LocaleBase)
    {
        if (app->app_Catalog)
            CloseCatalog(app->app_Catalog);
        app->app_Catalog = NULL;
        CloseLibrary((struct Library *)LocaleBase);
    }
}

// get localized string
UBYTE *GetCatStr(ULONG gad_id)
{
    if (LocaleBase)
        return (GetCatalogStr(app->app_Catalog, gad_id, CatCompArray[gad_id].cca_Str));
    else
        return (CatCompArray[gad_id].cca_Str);
}

// get localized hot key
UBYTE GetCatHotKey(ULONG gad_id)
{
    UBYTE *str;

    if (LocaleBase)
        str = GetCatalogStr(app->app_Catalog, gad_id, CatCompArray[gad_id].cca_Str);
    else
        str = CatCompArray[gad_id].cca_Str;

    return (*str);
}

static int DoNewMenu(void)
{
    int return_ok = FALSE;

    if (!(app->app_NewMenu = (struct NewMenu *)AllocVec((ID_END + 1) * sizeof(struct NewMenu), MEMF_CLEAR)))
    {
        puts("Out of memory!");
        goto abort;
    }

    if (!(app->app_Conf_NewMenu = (struct NewMenu *)AllocVec((ID_MENU_CONF_END - ID_MENU_CONFIGURATION + 1) * sizeof(struct NewMenu), MEMF_CLEAR)))
    {
        puts("Out of memory!");
        goto abort;
    }

    app->app_NewMenu[ID_PROJECT].nm_Type = NM_TITLE;
    app->app_NewMenu[ID_PROJECT].nm_Label = GetCatStr(MSG_MENU_PROJECT);

    app->app_NewMenu[ID_OPEN].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_OPEN].nm_Label = GetCatStr(MSG_MENU_OPEN);
    app->app_NewMenu[ID_OPEN].nm_CommKey = GetCatStr(MSG_MENU_OPEN_KEY);
    app->app_NewMenu[ID_OPEN].nm_UserData = (APTR)ID_OPEN;

    app->app_NewMenu[ID_SAVE].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_SAVE].nm_Label = GetCatStr(MSG_MENU_SAVE);
    app->app_NewMenu[ID_SAVE].nm_CommKey = GetCatStr(MSG_MENU_SAVE_KEY);
    app->app_NewMenu[ID_SAVE].nm_UserData = (APTR)ID_SAVE;

    app->app_NewMenu[ID_LABEL1].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_LABEL1].nm_Label = NM_BARLABEL;

    app->app_NewMenu[ID_FIND].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_FIND].nm_Label = GetCatStr(MSG_MENU_FIND);
    app->app_NewMenu[ID_FIND].nm_CommKey = GetCatStr(MSG_MENU_FIND_KEY);
    app->app_NewMenu[ID_FIND].nm_UserData = (APTR)ID_FIND;

    app->app_NewMenu[ID_LABEL2].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_LABEL2].nm_Label = NM_BARLABEL;

    app->app_NewMenu[ID_CLIP_CUT].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_CLIP_CUT].nm_Label = GetCatStr(MSG_MENU_CLIP_CUT);
    app->app_NewMenu[ID_CLIP_CUT].nm_CommKey = GetCatStr(MSG_MENU_CLIP_CUT_KEY);
    app->app_NewMenu[ID_CLIP_CUT].nm_UserData = (APTR)ID_CLIP_CUT;

    app->app_NewMenu[ID_CLIP_COPY].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_CLIP_COPY].nm_Label = GetCatStr(MSG_MENU_CLIP_COPY);
    app->app_NewMenu[ID_CLIP_COPY].nm_CommKey = GetCatStr(MSG_MENU_CLIP_COPY_KEY);
    app->app_NewMenu[ID_CLIP_COPY].nm_UserData = (APTR)ID_CLIP_COPY;

    app->app_NewMenu[ID_CLIP_PASTE].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_CLIP_PASTE].nm_Label = GetCatStr(MSG_MENU_CLIP_PASTE);
    app->app_NewMenu[ID_CLIP_PASTE].nm_CommKey = GetCatStr(MSG_MENU_CLIP_PASTE_KEY);
    app->app_NewMenu[ID_CLIP_PASTE].nm_UserData = (APTR)ID_CLIP_PASTE;

    app->app_NewMenu[ID_LABEL3].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_LABEL3].nm_Label = NM_BARLABEL;

    app->app_NewMenu[ID_ABOUT].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_ABOUT].nm_Label = GetCatStr(MSG_MENU_ABOUT);
    app->app_NewMenu[ID_ABOUT].nm_CommKey = GetCatStr(MSG_MENU_ABOUT_KEY);
    app->app_NewMenu[ID_ABOUT].nm_UserData = (APTR)ID_ABOUT;

    app->app_NewMenu[ID_LABEL4].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_LABEL4].nm_Label = NM_BARLABEL;

    app->app_NewMenu[ID_QUIT].nm_Type = NM_ITEM;
    app->app_NewMenu[ID_QUIT].nm_Label = GetCatStr(MSG_MENU_QUIT);
    app->app_NewMenu[ID_QUIT].nm_CommKey = GetCatStr(MSG_MENU_QUIT_KEY);
    app->app_NewMenu[ID_QUIT].nm_UserData = (APTR)ID_QUIT;

    app->app_NewMenu[ID_END].nm_Type = NM_END;

    {
        int base = ID_MENU_CONFIGURATION;

        app->app_Conf_NewMenu[ID_MENU_CONFIGURATION - base].nm_Type = NM_TITLE;
        app->app_Conf_NewMenu[ID_MENU_CONFIGURATION - base].nm_Label = GetCatStr(MSG_GAD_GP_CONF_CONFIGURATION);

        app->app_Conf_NewMenu[ID_MENU_CONF_NODES - base].nm_Type = NM_ITEM;
        app->app_Conf_NewMenu[ID_MENU_CONF_NODES - base].nm_Label = GetCatStr(MSG_GAD_RGP_CONF_NODES);
        app->app_Conf_NewMenu[ID_MENU_CONF_NODES - base].nm_CommKey = "1";
        app->app_Conf_NewMenu[ID_MENU_CONF_NODES - base].nm_UserData = (APTR)ID_MENU_CONF_NODES;

        app->app_Conf_NewMenu[ID_MENU_CONF_GLOBAL_SETTINGS - base].nm_Type = NM_ITEM;
        app->app_Conf_NewMenu[ID_MENU_CONF_GLOBAL_SETTINGS - base].nm_Label = GetCatStr(MSG_GAD_RGP_CONF_GLOBAL_SETTINGS);
        app->app_Conf_NewMenu[ID_MENU_CONF_GLOBAL_SETTINGS - base].nm_CommKey = "2";
        app->app_Conf_NewMenu[ID_MENU_CONF_GLOBAL_SETTINGS - base].nm_UserData = (APTR)ID_MENU_CONF_GLOBAL_SETTINGS;

        app->app_Conf_NewMenu[ID_MENU_CONF_LABEL1 - base].nm_Type = NM_ITEM;
        app->app_Conf_NewMenu[ID_MENU_CONF_LABEL1 - base].nm_Label = NM_BARLABEL;

        app->app_Conf_NewMenu[ID_MENU_CONF_ABOUT - base].nm_Type = NM_ITEM;
        app->app_Conf_NewMenu[ID_MENU_CONF_ABOUT - base].nm_Label = GetCatStr(MSG_MENU_ABOUT);
        app->app_Conf_NewMenu[ID_MENU_CONF_ABOUT - base].nm_CommKey = GetCatStr(MSG_MENU_ABOUT_KEY);
        app->app_Conf_NewMenu[ID_MENU_CONF_ABOUT - base].nm_UserData = (APTR)ID_ABOUT;

        app->app_Conf_NewMenu[ID_MENU_CONF_LABEL2 - base].nm_Type = NM_ITEM;
        app->app_Conf_NewMenu[ID_MENU_CONF_LABEL2 - base].nm_Label = NM_BARLABEL;

        app->app_Conf_NewMenu[ID_MENU_CONF_QUIT - base].nm_Type = NM_ITEM;
        app->app_Conf_NewMenu[ID_MENU_CONF_QUIT - base].nm_Label = GetCatStr(MSG_MENU_QUIT);
        app->app_Conf_NewMenu[ID_MENU_CONF_QUIT - base].nm_CommKey = GetCatStr(MSG_MENU_QUIT_KEY);
        app->app_Conf_NewMenu[ID_MENU_CONF_QUIT - base].nm_UserData = (APTR)ID_QUIT;

        app->app_Conf_NewMenu[ID_MENU_CONF_END - base].nm_Type = NM_END;
    }
    return_ok = TRUE;
abort:
    return (return_ok);
}

int ReadList(int quiet)
{
    int return_ok = FALSE;
    char *buf;
    FILE *stream = NULL;
    UBYTE buffer[BUF_SIZE], dest[BUF_SIZE + 1];
    int buf_len = 0, buf_pos = 0, end_of_file = FALSE;
    int dest_pos = 0;
    UBYTE *stream_buffer = NULL;
    UBYTE **list = NULL;
    int lines = 0, space = 0;
    UBYTE *xpk_getoutbuf = NULL;
    long xpk_getoutlen, xpk_getoutbuflen, xpk_err = XPKERR_NOTPACKED;
    long xpk_bufpos = 0;

    get(app->app_st_Conf_List, MUIA_String_Contents, &buf);

    // first, try to decompress filelist
    if (XpkBase = OpenLibrary(XPKNAME, 0))
    {
        UBYTE xpk_errbuf[XPKERRMSGSIZE];

        xpk_err = XpkUnpackTags(XPK_InName, buf,
                                XPK_GetOutBuf, &xpk_getoutbuf,
                                XPK_GetOutBufLen, &xpk_getoutbuflen,
                                XPK_GetOutLen, &xpk_getoutlen,
                                XPK_GetError, xpk_errbuf,
                                TAG_DONE);

        CloseLibrary(XpkBase);

        // an error occurred
        if (xpk_err != XPKERR_OK && xpk_err != XPKERR_NOTPACKED)
        {
            if (!quiet)
                DispError(MSG_ERROR_XPK, xpk_errbuf);

            goto abort;
        }
    }

    // load not packed filelist
    if (xpk_err == XPKERR_NOTPACKED)
    {
        if (!(stream_buffer = malloc(STREAM_BUF_SIZE)))
        {
            if (!quiet)
                DispError(MSG_ERROR_OUT_OF_MEMORY, NULL);
            goto abort;
        }

        get(app->app_st_Conf_List, MUIA_String_Contents, &buf);
        if (!(stream = fopen(buf, "r")))
        {
            if (!quiet)
                DispError(MSG_ERROR_CANT_OPEN, buf);
            goto abort;
        }
        // use my own buffer
        setvbuf(stream, stream_buffer, _IOFBF, STREAM_BUF_SIZE);
    }

    for (;;)
    {
        // additional 1 for terminating NULL ptr
        if (lines + 1 >= space)
        {
            // make room
            UBYTE **new_list;

            if (!(new_list = (UBYTE **)AllocVec((space + 50) * sizeof(UBYTE *), MEMF_CLEAR)))
            {
                if (!quiet)
                    DispError(MSG_ERROR_OUT_OF_MEMORY, NULL);
                goto abort;
            }
            space += 50;

            if (lines)
                // copy pointers from the old list to the new one
                memcpy(new_list, list, lines * sizeof(UBYTE *));
            if (list)
                FreeVec(list);
            list = new_list;
        }

        if (buf_pos + 1 >= buf_len)
        {
            if (end_of_file)
            {
                UBYTE *str;

                // flush buffer
                if (dest_pos)
                {
                    dest[dest_pos] = EOS;
                    if (!(str = (UBYTE *)AllocVec(strlen(dest) + 1, MEMF_CLEAR)))
                    {
                        if (!quiet)
                            DispError(MSG_ERROR_OUT_OF_MEMORY, NULL);
                        goto abort;
                    }
                    strcpy(str, dest);
                    list[lines++] = str;
                }
                break;
            }

            if (xpk_err == XPKERR_NOTPACKED)
            {
                if ((buf_len = fread(buffer, 1, BUF_SIZE, stream)) != BUF_SIZE)
                {
                    // reached EOF
                    if (!ferror(stream))
                        end_of_file = TRUE;
                    else
                    {
                        if (!quiet)
                            DispError(MSG_ERROR_WHILE_READING, buf);
                        goto abort;
                    }
                }
            }
            else
            {
                if (xpk_bufpos + BUF_SIZE >= xpk_getoutlen)
                {
                    buf_len = xpk_getoutlen - xpk_bufpos;
                    end_of_file = TRUE;
                }
                else
                    buf_len = BUF_SIZE;

                memcpy(buffer, &xpk_getoutbuf[xpk_bufpos], buf_len);
                xpk_bufpos += buf_len;
            }

            buf_pos = 0;
        }

        while (buf_pos + 1 < buf_len)
        {
            // LF or destination buffer full
            // (no line must be longer than BUF_SIZE)
            if (buffer[buf_pos] == 10 || dest_pos == BUF_SIZE)
            {
                UBYTE *str;

                dest[dest_pos] = EOS;
                if (!(str = (UBYTE *)AllocVec(strlen(dest) + 1, MEMF_CLEAR)))
                {
                    if (!quiet)
                        DispError(MSG_ERROR_OUT_OF_MEMORY, NULL);
                    goto abort;
                }
                strcpy(str, dest);
                list[lines++] = str;
                buf_pos++;
                dest_pos = 0;
                break;
            }
            // tab stop
            else if (buffer[buf_pos] == 9)
            {
                do
                {
                    dest[dest_pos++] = 32;
                }
                while ((dest_pos & 7) && dest_pos < BUF_SIZE);
                buf_pos++;
            }
            // illegal char
            else if (buffer[buf_pos] <= 31 ||
                    (buffer[buf_pos] >= 128 && buffer[buf_pos] <= 159))
            {
                buf_pos++;
            }
            // copy legal char to destination buffer
            else
                dest[dest_pos++] = buffer[buf_pos++];
        }
    }

    // everything worked fine

    // clear list
    DoMethod(app->app_lv_Main_List, MUIM_List_Clear, TAG_IGNORE);

    DoOpenFont(quiet);

    // free old list
    if (app->app_List)
    {
        UBYTE **list_bak = app->app_List;

        do
        {
            // free strings
            if (*list_bak)
                FreeVec(*list_bak++);
        }
        while (*list_bak);
        FreeVec(app->app_List);
    }
    app->app_List = list;
    // set list
    set(app->app_lv_Main_List, MUIA_List_Quiet, TRUE);
    DoMethod(app->app_lv_Main_List, MUIM_List_Insert, app->app_List, -1,
             MUIV_List_Insert_Bottom);
    set(app->app_lv_Main_List, MUIA_List_Quiet, FALSE);
    return_ok = TRUE;
abort:
    if (stream)
        fclose(stream);
    if (stream_buffer)
        free (stream_buffer);
    // free mem allocated by XPK master
    if (xpk_getoutbuf)
        FreeMem(xpk_getoutbuf, xpk_getoutbuflen);
    if (!return_ok)
    {
        if (list)
        {
            UBYTE **list_bak = list;

            do
            {
                // free strings
                if (*list)
                    FreeVec(*list++);
            }
            while (*list);
            FreeVec(list_bak);
        }
    }
    return (return_ok);
}

static void LVListDBClick(void)
{
    LONG pos, filename_pos, bytes_pos, cur_pos = 0;
    UBYTE *line;
    UBYTE buffer[STANDARDSIZE], bytesbuf[STANDARDSIZE], filenamebuf[STANDARDSIZE];

    // cursor position
    get(app->app_lv_Main_List, MUIA_List_Active, &pos);

    if (pos >= 0)
    {
        // get active line
        DoMethod(app->app_lv_Main_List, MUIM_List_GetEntry, pos, &line);
        // get filename_pos
        get(app->app_sl_Conf_FileNamePos, MUIA_Slider_Level, &filename_pos);
        // get bytes_pos
        get(app->app_sl_Conf_BytesPos, MUIA_Slider_Level, &bytes_pos);

        *bytesbuf = EOS;
        *filenamebuf = EOS;

        // non empty line
        while (*line && cur_pos < MAX(filename_pos, bytes_pos))
        {
            // strip blanks
            while (*line && *line == 32)
                line++;
            if (*line)
            {
                pos = 0;
                // extract word until reaching next blank or eol
                while (*line && pos < STANDARDSIZE && *line != 32)
                {
                    buffer[pos++] = *line++;
                }
                buffer[pos] = EOS;

                // reached desired filename position
                if (++cur_pos == filename_pos)
                    strcpy(filenamebuf, buffer);
                // reached desired bytes position
                else if (cur_pos == bytes_pos)
                    strcpy(bytesbuf, buffer);
            }
        }
        if (*filenamebuf)
            AddListEntry(filenamebuf, bytesbuf, FALSE);
    }
}

int AddListEntry(UBYTE *filenamebuf, UBYTE *bytesbuf, int quiet)
{
    int return_ok = FALSE;
    struct RequestList *rl = NULL;
    struct RequestList *rl_walk;
    UBYTE *tail;

    if (!filenamebuf)
        return (return_ok);

    // do not allow double entries
    rl_walk = app->app_RequestList;
    while (rl_walk)
    {
        // entry already exists
        if (!strcmp(rl_walk->rl_Buffer, filenamebuf))
            return (return_ok);
        rl_walk = rl_walk->rl_Next;
    }

    if (!(rl = (struct RequestList *)AllocVec(sizeof(struct RequestList), MEMF_CLEAR)))
        goto abort;
    if (!(rl->rl_Buffer = (UBYTE *)AllocVec(strlen(filenamebuf) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(rl->rl_Buffer, filenamebuf);
    if (bytesbuf && *bytesbuf)
    {
        rl->rl_Bytes = strtol(bytesbuf, &tail, 10);
        if (rl->rl_Bytes < 0)
            rl->rl_Bytes = 0;
    }

    // append entry
    if (rl_walk = app->app_RequestList)
    {
        while (rl_walk->rl_Next)
            rl_walk = rl_walk->rl_Next;
        rl_walk->rl_Next = rl;
    }
    else
        app->app_RequestList = rl;

    // now add entry
    DoMethod(app->app_lv_Main_RequestList, MUIM_List_Insert,
             &rl->rl_Buffer, 1, MUIV_List_Insert_Bottom);
    set(app->app_lv_Main_RequestList, MUIA_List_Active, MUIV_List_Active_Bottom);
    // clear request string
    *app->app_Main_RequestBuf = EOS;
    set(app->app_st_Main_Request, MUIA_String_Contents, app->app_Main_RequestBuf);
    Statistics(rl->rl_Bytes, PLUS);

    return_ok = TRUE;
abort:
    if (!return_ok)
    {
        if (rl)
            FreeVec(rl);
        if (!quiet)
            DispError(MSG_ERROR_OUT_OF_MEMORY, NULL);
    }
    return (return_ok);
}

void DeleteListEntry(void)
{
    // non empty list
    if (app->app_RequestList)
    {
        LONG pos = -1;
        LONG state = MUIV_List_Select_All;
        struct RequestList **rl = &app->app_RequestList, *rl_delete;

        do
        {
            DoMethod(app->app_lv_Main_RequestList, MUIM_List_NextSelected, &pos);

            if (pos != -1)
            {
                // this is the active entry and
                // no further entries are selected?
                DoMethod(app->app_lv_Main_RequestList, MUIM_List_Select,
                         pos, MUIV_List_Select_Ask, &state);

                DoMethod(app->app_lv_Main_RequestList, MUIM_List_Remove, pos);

                // jump to selected entry
                while (pos > 0)
                {
                    rl = &(*rl)->rl_Next;
                    pos--;
                }

                // delete from chain and free mem
                if (*rl)
                {
                    rl_delete = *rl;
                    *rl = (*rl)->rl_Next;
                    if (rl_delete->rl_Buffer)
                        FreeVec(rl_delete->rl_Buffer);
                    Statistics(rl_delete->rl_Bytes, MINUS);
                    FreeVec(rl_delete);
                }
                rl = &app->app_RequestList;
            }
        }
        while (pos != -1 && state != MUIV_List_Select_Off);
    }
}

static int CreateReqFileName(int quiet)
{
    int return_ok = FALSE;
    UBYTE *buf, req_file[PATHSIZE];
    int pos = 0;

    get(app->app_st_Conf_Node, MUIA_String_Contents, &buf);
    strcpy(req_file, buf);

    // replace all colons and slashes by dots
    while (req_file[pos])
    {
        if (req_file[pos] == ':' || req_file[pos] == '/')
            req_file[pos] = '.';
        pos++;
    }
    // append .REQ
    strcat(req_file, ".REQ");

    // create full filename
    // getenv MUIFFR/Outbound
    buf = getenv("MUIFFR/Outbound");
    // no env variable
    if (!buf)
        get(app->app_st_Conf_Outbound, MUIA_String_Contents, &buf);
    strcpy(app->app_ReqFile, buf);
    if (!AddPart(app->app_ReqFile, req_file, PATHSIZE - 1))
        return (FALSE);

    return_ok = TRUE;
abort:
    if (!return_ok && !quiet)
        DispError(MSG_ERROR_INVALID_NODE_ADDRESS, NULL);
    return (return_ok);
}

int SaveReqFile(void)
{
    int return_ok = FALSE;
    FILE *stream = NULL;
    struct RequestList *rl = app->app_RequestList;

    if (!CreateReqFileName(FALSE))
        goto abort;

    if (!rl)
    {
        if (!remove(app->app_ReqFile))
            DispError(MSG_INFO_REQUESTFILE_DELETED, NULL);
        else
            DispError(MSG_ERROR_NO_REQUESTFILE, NULL);
        goto abort;
    }

    if (!(stream = fopen(app->app_ReqFile, "w")))
    {
        DispError(MSG_ERROR_CANT_OPEN, app->app_ReqFile);
        goto abort;
    }

    for (; rl; rl = rl->rl_Next)
    {
        fprintf(stream, "%s%s", rl->rl_Buffer, (app->app_Flags & APP_CYA_CONF_TERM_CRLF) ? "\r\n" : "\n");
        if (ferror(stream))
        {
            DispError(MSG_ERROR_WHILE_WRITING, app->app_ReqFile);
            goto abort;
        }
    }

    return_ok = TRUE;
abort:
    if (stream)
        fclose(stream);
    return (return_ok);
}

void DispError(int msg_id, UBYTE *buf)
{
    UBYTE buffer[BUF_SIZE];

    sprintf(buffer, GetCatStr(msg_id), buf);
    MUI_Request(app->app_Muiffr, app->app_wi_Main, 0, NULL,
                GetCatStr(MSG_OKAY),
                buffer,
                TAG_END);
}

static void LoadReqFile(int quiet)
{
    int return_ok = FALSE;
    FILE *stream = NULL;
    struct RequestList *rl = app->app_RequestList;
    int end_of_file = FALSE;
    UBYTE buffer[BUF_SIZE];

    if (!CreateReqFileName(quiet))
        goto abort;

    if (!(stream = fopen(app->app_ReqFile, "r")))
    {
        if (!quiet)
            DispError(MSG_ERROR_CANT_OPEN, app->app_ReqFile);
        goto abort;
    }

    *app->app_Main_RequestBuf = EOS;
    set(app->app_st_Main_Request, MUIA_String_Contents, app->app_Main_RequestBuf);
    // clear list
    DoMethod(app->app_lv_Main_RequestList, MUIM_List_Clear, TAG_IGNORE);
    // quiet list
    set(app->app_lv_Main_RequestList, MUIA_List_Quiet, TRUE);

    // reset statistics output
    Statistics(0, RESET);

    if (rl)
    {
        struct RequestList *rl_next;

        do
        {
            if (rl->rl_Buffer)
                FreeVec(rl->rl_Buffer);
            rl_next = rl->rl_Next;
            FreeVec(rl);
            rl = rl_next;
        }
        while (rl);
        app->app_RequestList = NULL;
    }

    while (!end_of_file)
    {
        if (!fgets(buffer, BUF_SIZE, stream))
        {
            // reached EOF
            if (!ferror(stream))
                end_of_file = TRUE;
            else
            {
                if (!quiet)
                    DispError(MSG_ERROR_WHILE_READING, app->app_ReqFile);
                goto abort;
            }
        }
        else
        {
            int len = strlen(buffer);

            // remove LF + CR
            if (buffer[len - 1] == '\r' || buffer[len - 1] == '\n')
                buffer[len - 1] = EOS;
            if (len > 1)
                if (buffer[len - 2] == '\r' || buffer[len - 2] == '\n')
                    buffer[len - 2] = EOS;

            AddListEntry(buffer, NULL, quiet);
        }
    }

abort:
    if (stream)
        fclose(stream);
    set(app->app_lv_Main_RequestList, MUIA_List_Quiet, FALSE);
    if (app->app_RequestList)
        set(app->app_lv_Main_RequestList, MUIA_List_Active, MUIV_List_Active_Bottom);
}

static void BoyerMoore(int direction)
{
    UBYTE *find_str;
    int len;
    int pattern_found = FALSE;

    if (!app->app_List)
    {
        DispError(MSG_ERROR_FIRST_LOAD_LIST, NULL);
        return;
    }

    get(app->app_st_Main_Find, MUIA_String_Contents, &find_str);
    if (!*find_str)
    {
        DispError(MSG_ERROR_NO_FIND_STRING, NULL);
        return;
    }
    len = strlen(find_str);

    if (app->app_Flags & APP_FIND_CHANGED)
    {
        int i;

        app->app_Flags &= ~(APP_FIND_CHANGED);

        // init skip table
        for (i = 0; i <= 255; i++)
            app->app_Skip[i] = len;

        for (i = 0; i < len; i++)
            app->app_Skip[find_str[i]] = len - 1 - i;

    }

    {
        UBYTE *list_str;
        long pos, num_lines;
        int list_len;

        get(app->app_lv_Main_List, MUIA_List_Active, &pos);
        get(app->app_lv_Main_List, MUIA_List_Entries, &num_lines);

        for ((direction == SEARCH_DOWN) ? (pos++) : (pos--);
             (direction == SEARCH_DOWN) ? (pos < num_lines) : (pos >= 0);
             (direction == SEARCH_DOWN) ? (pos++) : (pos--))
        {
            // get active line
            DoMethod(app->app_lv_Main_List, MUIM_List_GetEntry, pos, &list_str);
            list_len = strlen(list_str);

            if (MisCharSearch(list_str, list_len, find_str, len) < list_len)
            {
                pattern_found = TRUE;
                set(app->app_lv_Main_List, MUIA_List_Active, pos);
                break;
            }
        }
    }

abort:
    if (!pattern_found)
        DispError(MSG_ERROR_PATTERN_NOT_FOUND, find_str);
    // active object
    set(app->app_wi_Main, MUIA_Window_ActiveObject, app->app_lv_Main_List);
}

static int MisCharSearch(UBYTE *list_str, int list_len, UBYTE *find_str, int len)
{
    int i, j, t;

    if (list_len < len)
        return list_len;

    for (i = len - 1, j = len - 1; j >= 0; i--, j--)
    {
        while (TO_UPPER(list_str[i]) != TO_UPPER(find_str[j]))
        {
            t = app->app_Skip[list_str[i]];
            i += (len - j > t) ? len - j : t;

            if (i >= list_len)
                return list_len;
            j = len - 1;
        }
    }

    return i;
}

static void DoOpenFont(int quiet)
{
    int return_ok = FALSE;
    UBYTE *buf, buffer[PATHSIZE], *size;
    int ysize, force_display_change = FALSE;

    get(app->app_st_Conf_Font, MUIA_String_Contents, &buf);
    strcpy(buffer, buf);

    // same font already open
    if (!strcmp(buf, app->app_LastFontBuf))
        return;

    if (!*buffer)
    {
        quiet = TRUE;
        force_display_change = TRUE;
        goto abort;
    }

    if (size = strchr(buffer, '/'))
    {
        UBYTE *ptr;
        LONG only_digits = TRUE;

        *size++ = EOS;
        ptr = size;

        do
        {
            if (!isdigit(*ptr++))
            {
                only_digits = FALSE;
                break;
            }
        }
        while (*ptr);

        if (only_digits)
            ysize = atoi(size);
        else
            goto abort;
    }
    {
        struct TextFont *font;
        struct TextAttr ta;

        strcat(buffer, ".font");
        ta.ta_Name  = buffer;
        ta.ta_YSize = ysize;
        ta.ta_Style = 0;
        ta.ta_Flags = 0;

        font = OpenFont(&ta);
        if (!font || font->tf_YSize != ta.ta_YSize)
        {
            struct TextFont *font2;

            if (DiskfontBase = OpenLibrary("diskfont.library", 0))
            {
                if (font2 = OpenDiskFont(&ta))
                {
                    if (font)
                        CloseFont(font);
                    if (font2->tf_YSize != ta.ta_YSize)
                    {
                        CloseFont(font2);
                        font2 = NULL;
                    }
                    font = font2;
                }
                CloseLibrary(DiskfontBase);
                DiskfontBase = NULL;
            }
        }
        if (!font)
            goto abort;

        set(app->app_lv_Main_List, MUIA_Font, font);

        if (app->app_Font)
            CloseFont(app->app_Font);
        app->app_Font = font;
        // remember font
        strcpy(app->app_LastFontBuf, buf);
    }

    return_ok = TRUE;
abort:
    if (!return_ok)
    {
        if (!quiet)
            DispError(MSG_ERROR_CANT_OPEN, buf);
        set(app->app_lv_Main_List, MUIA_Font, MUIV_Font_Fixed);
        *app->app_LastFontBuf = EOS;
    }
    if (!quiet || force_display_change)
    {
        set(app->app_wi_Main, MUIA_Window_Open, FALSE);
        set(app->app_wi_Main, MUIA_Window_Open, TRUE);
        set(app->app_wi_Main, MUIA_Window_Activate, TRUE);
    }
}

static int AppendPassword(void)
{
    int return_ok = FALSE;
    struct RequestList *rl = app->app_RequestList;
    UBYTE buffer[BUF_SIZE];
    UBYTE *buf, *old_buf, *new_buf;
    LONG pos = -1, count = 0, pos_bak, buf_len;;
    LONG state = MUIV_List_Select_All;
    LONG active_pos = MUIV_List_Active_Off;

    get(app->app_st_Main_Password, MUIA_String_Contents, &buf);
    buf_len = strlen(buf);
    get(app->app_lv_Main_RequestList, MUIA_List_Active, &active_pos);

    if (*buf && rl)
    {
        // clear request string gadget
        *app->app_Main_RequestBuf = EOS;
        set(app->app_st_Main_Request, MUIA_String_Contents, app->app_Main_RequestBuf);
        // quiet list
        set(app->app_lv_Main_RequestList, MUIA_List_Quiet, TRUE);

        // count entries
        for (; rl; rl = rl->rl_Next)
            count++;

        do
        {
            rl = app->app_RequestList;
            // next selected entry
            DoMethod(app->app_lv_Main_RequestList, MUIM_List_NextSelected, &pos);

            if (pos >= 0)
            {

                // this is the active entry and
                // no further entries are selected?
                DoMethod(app->app_lv_Main_RequestList, MUIM_List_Select,
                         pos, MUIV_List_Select_Ask, &state);

                pos_bak = pos;
                // jump to selected entry
                while (pos_bak > 0)
                {
                    rl = rl->rl_Next;
                    pos_bak--;
                }

                // remember buffer
                old_buf = rl->rl_Buffer;

                // has password already been appended to entry?
                {
                    LONG old_buf_len = strlen(old_buf);

                    if (old_buf_len >= buf_len)
                    {
                        // yes?
                        if (!strcmp(buf, &old_buf[old_buf_len - buf_len]))
                            continue;
                    }
                }

                // create new buffer
                strcpy(buffer, rl->rl_Buffer);
                // append password
                strcat(buffer, buf);

                if (!(new_buf = (UBYTE *)AllocVec(strlen(buffer) + 1, MEMF_CLEAR)))
                {
                    DispError(MSG_ERROR_OUT_OF_MEMORY, NULL);
                    goto abort;
                }

                // remove entry
                DoMethod(app->app_lv_Main_RequestList, MUIM_List_Remove, pos);

                // copy new entry
                strcpy(new_buf, buffer);
                rl->rl_Buffer = new_buf;

                // readd entry
                DoMethod(app->app_lv_Main_RequestList, MUIM_List_Insert,
                         &rl->rl_Buffer, 1,
                         (pos + 1 == count) ? MUIV_List_Insert_Bottom : pos);

                // free old buffer
                FreeVec(old_buf);
            }
        }
        while (pos != -1 && state != MUIV_List_Select_Off);
    }

    // show changes
    set(app->app_lv_Main_RequestList, MUIA_List_Quiet, FALSE);
    // set active entry
    set(app->app_lv_Main_RequestList, MUIA_List_Active, active_pos);

    return_ok = TRUE;
abort:
    return (return_ok);
}

static void SetWaitPointer(APTR *win_obj)
{
    struct Library *SysBase;
    struct Window *window;

    SysBase = *(struct Library **)4;

    get(win_obj, MUIA_Window_Window, &window);
    if (window)
    {
        if (SysBase->lib_Version >= 39)
            SetWindowPointer(window, WA_BusyPointer, TRUE,
                                  WA_PointerDelay, TRUE,
                                  TAG_DONE);
        else
            SetPointer(window, StopWatch, 16, 16, -6, 0);
    }
}

static void ClearWaitPointer(APTR *win_obj)
{
    struct Window *window;

    get(win_obj, MUIA_Window_Window, &window);
    if (window)
        ClearPointer(window);
}

static void Statistics(LONG bytes, int calc)
{
    UBYTE timebuf[STANDARDSIZE], bytesbuf[STANDARDSIZE], chargesbuf[STANDARDSIZE];
    LONG time, charges, speed, tpu, cpu;
    UBYTE *buf;
    char *tail;

    // speed of connection (bit/s)
    get(app->app_st_Conf_Speed, MUIA_String_Contents, &buf);
    speed = strtol(buf, &tail, 10);
    // no conversion could be done (== 0)
    // or non intended result (< 0)
    if (speed <= 0)
        return;

    // time per unit
    get(app->app_st_Conf_Time, MUIA_String_Contents, &buf);
    tpu = strtol(buf, &tail, 10);
    // no conversion could be done (== 0)
    // or non intended result (< 0)
    if (tpu <= 0)
        return;

    // charges per unit
    get(app->app_st_Conf_Charges, MUIA_String_Contents, &buf);
    cpu = strtol(buf, &tail, 10);
    // no conversion could be done (== 0)
    // or non intended result (< 0)
    if (cpu <= 0)
        return;

    // calculate additional time and charges
    time = (bytes * 8) / speed;

    // calculate totals
    if (calc == RESET)
    {
        app->app_Bytes = app->app_Time = app->app_Charges = 0;
    }
    else
    {
        app->app_Bytes += (calc == PLUS) ? bytes : -bytes;
        app->app_Time += (calc == PLUS) ? time : -time;
        app->app_Charges = (app->app_Time / tpu + 1) * cpu;
        if (!app->app_Time)
            app->app_Charges = 0;
    }

    bytes = app->app_Bytes;
    time = app->app_Time;
    charges = app->app_Charges;

    // create strings to display
    sprintf(bytesbuf, "%04ldK", bytes >> 10);
    sprintf(timebuf, "%02ld:%02ld:%02ld", time / 3600, (time % 3600) / 60, (time % 3600) % 60);
    sprintf(chargesbuf, "%04ld.%02ld", charges / 100, charges % 100);

    set(app->app_tx_Main_Bytes, MUIA_Text_Contents, bytesbuf);
    set(app->app_tx_Main_Time, MUIA_Text_Contents, timebuf);
    set(app->app_tx_Main_Charges, MUIA_Text_Contents, chargesbuf);
}

static void WorkAroundMuiBug(void)
{
    UBYTE *buf = " ";

    // in order to prevent a MUI bug
    // (displaying the first element twice in a list at creation time,
    // although there is only one physical entry)
    // add and remove item
    // show changes
    set(app->app_lv_Main_RequestList, MUIA_List_Quiet, TRUE);
    DoMethod(app->app_lv_Main_RequestList, MUIM_List_Insert,
             &buf, 1, MUIV_List_Insert_Bottom);
    DoMethod(app->app_lv_Main_RequestList, MUIM_List_Remove, MUIV_List_Remove_Last);
    set(app->app_lv_Main_RequestList, MUIA_List_Quiet, FALSE);
}
