#define __NOLIBBASE__

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/ttrender.h>
#include <proto/utility.h>
#include <proto/layers.h>
#include <proto/graphics.h>

#define MARGIN_SIZE 2

extern struct Library *SysBase, *DOSBase;

struct Library *IntuitionBase = NULL,
               *UtilityBase = NULL,
               *TTRenderBase = NULL,
               *GfxBase = NULL,
               *LayersBase = NULL;

APTR mempool;

struct Parameters
  {
    STRPTR  file_name;
    STRPTR  font_name;
    ULONG  *font_size;
  };

struct AppData
  {
    struct Window *win;
    ULONG cypos;
    ULONG total_height;
    WORD l, t, r, b;        /* window showbox */
    struct MinList token_list;
    struct MinList line_list;
    struct Parameters prm;
    struct Gadget *last_clicked;
    struct Gadget scroll;
    struct PropInfo pinfo;
    struct Image i1;
    struct Image i2;
    struct Screen *screen_lock;
  };

/*--------------------------------------------------------------------------------------------------*/

void init_minlist(struct MinList *list)
  {
    list->mlh_Head = (struct MinNode*)&list->mlh_Tail;
    list->mlh_Tail = NULL;
    list->mlh_TailPred = (struct MinNode*)&list->mlh_Head;
  }

/*==================================================================================================*/
/* PARSER - generates a list of tokens from input file.                                             */
/*==================================================================================================*/

struct TokenNode
  {
    struct TokenNode *next;
    struct TokenNode *prev;
    ULONG             ssize;
    STRPTR            token;
  };

/* states */

#define WRD 1
#define SPC 2
#define TAB 3
#define NL1 4
#define NL2 5

struct TokenNode *new_token_node(STRPTR token)
  {
    struct TokenNode *tn;

    if (tn = AllocPooled(mempool, sizeof(struct TokenNode)))
      {
        tn->ssize = strlen(token) + 1;
        if (tn->token = AllocPooled(mempool, tn->ssize))
          {
            strcpy(tn->token, token);
            return tn;
          }
        FreePooled(mempool, tn, sizeof(struct TokenNode));
      }
    return 0;
  }

void emit_token(struct AppData *app, UBYTE *tok)
  {
    struct TokenNode *tn;

    if (tn = new_token_node(tok))
      {
        AddTail((struct List *)&app->token_list, (struct Node*)tn);
      }
  }

void emit_space(struct AppData *app)
  {
    struct TokenNode *tn;

    if (tn = new_token_node("<SPC>"))
      {
        AddTail((struct List *)&app->token_list, (struct Node*)tn);
      }
  }

void emit_tab(struct AppData *app)
  {
    struct TokenNode *tn;

    if (tn = new_token_node("<TAB>"))
      {
        AddTail((struct List *)&app->token_list, (struct Node*)tn);
      }
  }

void emit_lf(struct AppData *app)
  {
    struct TokenNode *tn;

    if (tn = new_token_node("<LF>"))
      {
        AddTail((struct List *)&app->token_list, (struct Node*)tn);
      }
  }

UBYTE get_next_char(BPTR in)
  {
    LONG c;

    c = FGetC(in);
    if (c > 0) return (UBYTE)c;
    else return 0;
  }

void parse(struct AppData *app, BPTR in)
  {
    UBYTE c, *ptr, tok[80];
    BYTE s = WRD;

    ptr = tok;

    while (c = get_next_char(in))
      {
        switch (s)
          {
            case WRD:
              switch (c)
                {
                  case 0x09: *ptr++ = 0x00; emit_token(app, tok); ptr = tok; s = TAB; break;
                  case 0x0A: *ptr++ = 0x00; emit_token(app, tok); ptr = tok; s = NL1; break;
                  case 0x0D:
                  case 0x0F: *ptr++ = 0x00; emit_token(app, tok); ptr = tok; s = NL2; break;
                  case 0x20: *ptr++ = 0x00; emit_token(app, tok); ptr = tok; s = SPC; break;
                  default: *ptr++ = c;
                }
              break;

            case SPC:
              emit_space(app);
              switch (c)
                {
                  case 0x09: s = TAB; break;
                  case 0x0A: s = NL1; break;
                  case 0x0D:
                  case 0x0F: s = NL2; break;
                  case 0x20: break;
                  default: *ptr++ = c; s = WRD;
                }
              break;

            case TAB:
              emit_tab(app);
              switch (c)
                {
                  case 0x09: break;
                  case 0x0A: s = NL1; break;
                  case 0x0D:
                  case 0x0F: s = NL2; break;
                  case 0x20: s = SPC; break;
                  default: *ptr++ = c; s = WRD;
                }
              break;

            case NL1:
              switch (c)
                {
                  case 0x09: emit_lf(app); s = TAB; break;
                  case 0x0A: emit_lf(app); s = NL1; break;
                  case 0x0D: s = NL2; break;
                  case 0x0F: emit_lf(app); s = NL2; break;
                  case 0x20: emit_lf(app); s = SPC; break;
                  default: *ptr++ = c; emit_lf(app); s = WRD;
                }
              break;

            case NL2:
              emit_lf(app);
              switch (c)
                {
                  case 0x09: s = TAB; break;
                  case 0x0A: s = NL1; break;
                  case 0x0D:
                  case 0x0F: s = NL2; break;
                  case 0x20: s = SPC; break;
                  default: *ptr++ = c; s = WRD;
                }
              break;
          }
      }
  }

/*==================================================================================================*/
/* LF CLEANER - replaces single <LF> token with <SPC> token, and two adjacent <LF> tokens by single */
/* <LF> token.                                                                                      */
/*==================================================================================================*/

void lf_cleaner(struct MinList *token_list)
  {
    BOOL last_was_lf = FALSE;
    struct TokenNode *tn, *new_tn, *prev_tn;

    tn = (struct TokenNode*)token_list->mlh_Head;

    for (tn = (struct TokenNode*)token_list->mlh_Head; tn->next; tn = tn->next)
      {
        prev_tn = tn->prev;

        if (strcmp(tn->token, "<LF>") == 0)
          {
            if (last_was_lf)
              {
                Remove((struct Node*)prev_tn);
                last_was_lf = FALSE;
              }
            else last_was_lf = TRUE;
          }
        else  /* current token is not <LF> */
          {
            if (last_was_lf)  /* if last was <LF> replace it with <SPC> */
              {
                if (new_tn = new_token_node("<SPC>"))
                  {
                    Insert((struct List*)token_list, (struct Node*)new_tn, (struct Node*)prev_tn);
                    Remove((struct Node*)prev_tn);
                  }
              }
            last_was_lf = FALSE;
          }
      }
  }


/*==================================================================================================*/
/* LINE SPLITTER - generates a list of lines fitted to given width                                  */
/*==================================================================================================*/

#define LINE_ALIGN_LEFT      0
#define LINE_ALIGN_RIGHT     1
#define LINE_ALIGN_CENTER    2
#define LINE_ALIGN_JUSTIFY   3

struct TextLine
  {
    struct TextLine *next;
    struct TextLine *prev;
    ULONG pos_y;
    ULONG tot_w;
    BOOL last_line;             /* TRUE if the last line in paragraph */
    struct MinList tokens;
  };

struct TokenHandle
  {
    struct TokenHandle *next;
    struct TokenHandle *prev;
    struct TokenNode *token;
    ULONG pos_x;
  };

struct SplitterState
  {
    struct TextLine *ctl;             /* current text line */
    ULONG cur_y;                      /* current vertical position in the document */
    ULONG cur_w;                      /* current line width */
    ULONG spc_w;                      /* space token width (cached for speed) */
    ULONG lin_h;                      /* [current] height of current line */
  };

/*--------------------------------------------------------------------------------------------------*/

struct TextLine *new_text_line(void)
  {
    struct TextLine *tl;

    if (tl = AllocPooled(mempool, sizeof(struct TextLine))) init_minlist(&tl->tokens);
    tl->tot_w = 0;
    tl->last_line = FALSE;
    return tl;
  }

/*--------------------------------------------------------------------------------------------------*/

void add_to_line(struct TextLine *line, struct TokenNode *tn, ULONG pos_x)
  {
    struct TokenHandle *th;

    if (th = AllocPooled(mempool, sizeof(struct TokenHandle)))
      {
        th->token = tn;
        th->pos_x = pos_x;
        AddTail((struct List*)&line->tokens, (struct Node*)th);
      }
  }

/*--------------------------------------------------------------------------------------------------*/

void cut_ending_spaces(struct MinList *line_list, ULONG space_width)
  {
    struct TextLine *txl;
    struct TokenHandle *tkh_last;

    for(txl = (struct TextLine*)line_list->mlh_Head; txl->next; txl = txl->next)
      {
        tkh_last = (struct TokenHandle*)txl->tokens.mlh_TailPred;

        if (tkh_last != (struct TokenHandle*)&txl->tokens.mlh_Head)
          {
            if (strcmp(tkh_last->token->token, "<SPC>") == 0)
              {
                Remove((struct Node*)tkh_last);
                FreePooled(mempool, tkh_last, sizeof(struct TokenHandle));
                txl->tot_w -= space_width;
              }
          }
      }
  }

/*--------------------------------------------------------------------------------------------------*/

void init_splitter(struct SplitterState *sps, struct MinList *line_list)
  {
    sps->ctl = NULL;
    sps->cur_y = 0;
    sps->cur_w = 0;
    sps->spc_w = TT_StrWidth(" ");
    sps->ctl = new_text_line();
    AddTail((struct List*)line_list, (struct Node*)sps->ctl);
    sps->ctl->pos_y = sps->cur_y;
  }

/*--------------------------------------------------------------------------------------------------*/

void line_break(struct SplitterState *sps, struct MinList *line_list)
  {
    sps->cur_y += sps->lin_h;
    sps->cur_w = 0;
    sps->ctl = new_text_line();
    AddTail((struct List*)line_list, (struct Node*)sps->ctl);
    sps->ctl->pos_y = sps->cur_y;
  }

/*--------------------------------------------------------------------------------------------------*/

void horiz_layout(struct MinList *line_list, ULONG width, ULONG mode)
  {
    struct TextLine *txl;

    if (mode == LINE_ALIGN_LEFT) return;  /* left layout is default */

    for (txl = (struct TextLine*)line_list->mlh_Head; txl->next; txl = txl->next)
      {
        switch (mode)
          {
            case LINE_ALIGN_RIGHT:
              {
                ULONG freespace;
                struct TokenHandle *tkh;

                freespace = width - txl->tot_w;

                /* move all tokens right */

                for (tkh = (struct TokenHandle*)txl->tokens.mlh_Head; tkh->next; tkh = tkh->next)
                  {
                    tkh->pos_x += freespace;
                  }
              }
            break;

            case LINE_ALIGN_CENTER:
              {
                ULONG freespace;
                struct TokenHandle *tkh;

                freespace = width - txl->tot_w >> 1;

                /* move all tokens right */

                for (tkh = (struct TokenHandle*)txl->tokens.mlh_Head; tkh->next; tkh = tkh->next)
                  {
                    tkh->pos_x += freespace;
                  }
              }
            break;

            case LINE_ALIGN_JUSTIFY:
              {
                ULONG freespace, space_count = 0;
                ULONG spc_int, spc_frac, move_int = 0, move_frac = 0;
                struct TokenHandle *tkh;

                /* the last line of the paragraph should be left (default) aligned */

                if (txl->last_line) continue;

                /* count spaces */

                for (tkh = (struct TokenHandle*)txl->tokens.mlh_Head; tkh->next; tkh = tkh->next)
                  {
                    space_count++;
                  }

                if (space_count > 2)
                  {
                    space_count = space_count - 1 >> 1;
                    freespace = width - txl->tot_w;
                    if (freespace > 0)
                      {
                        spc_int = freespace / space_count;
                        spc_frac = freespace % space_count;

                        /* list contains at least 2 tokens so we safely can start from the second */

                        for (tkh = ((struct TokenHandle*)txl->tokens.mlh_Head)->next; tkh->next; tkh = tkh->next)
                          {
                            if (strcmp(tkh->token->token, "<SPC>") != 0)
                              {
                                move_int += spc_int;
                                move_frac += spc_frac;
                                if (move_frac >= space_count)
                                  {
                                    move_frac -= space_count;
                                    move_int++;
                                  }
                              }
                            tkh->pos_x += move_int;
                          }
                      }
                  }
              }
            break;
          }
      }
  }

/*--------------------------------------------------------------------------------------------------*/

void line_splitter(struct AppData *app)
  {
    struct SplitterState sps;
    struct TokenNode *tn;
    ULONG tok_w, width = app->r - app->l + 1, line_height = *app->prm.font_size;


    init_minlist(&app->line_list);
    init_splitter(&sps, &app->line_list);
    sps.lin_h = line_height;

    for (tn = (struct TokenNode*)app->token_list.mlh_Head; tn->next; tn = tn->next)
      {
        if (strcmp(tn->token, "<SPC>") == 0)
          {
            if (sps.cur_w + sps.spc_w > width) line_break(&sps, &app->line_list);
            else
              {
                add_to_line(sps.ctl, tn, sps.cur_w);
                sps.cur_w += sps.spc_w;
                sps.ctl->tot_w = sps.cur_w;
              }
          }
        else if (strcmp(tn->token, "<LF>") == 0)
          {
            sps.ctl->last_line = TRUE;
            line_break(&sps, &app->line_list);
          }
        else
          {
            tok_w = TT_StrWidth(tn->token);
            if (sps.cur_w == 0 && tok_w > width)
              {
                add_to_line(sps.ctl, tn, sps.cur_w);
                sps.ctl->tot_w = width;
                line_break(&sps, &app->line_list);
              }
            else if (sps.cur_w + tok_w > width)
              {
                line_break(&sps, &app->line_list);
                add_to_line(sps.ctl, tn, sps.cur_w);
                sps.cur_w += tok_w;
                sps.ctl->tot_w = sps.cur_w;
              }
            else
              {
                add_to_line(sps.ctl, tn, sps.cur_w);
                sps.cur_w += tok_w;
                sps.ctl->tot_w = sps.cur_w;
              }
          }
      }
    app->total_height = sps.cur_y + sps.lin_h;
    cut_ending_spaces(&app->line_list, sps.spc_w);
    horiz_layout(&app->line_list, width, LINE_ALIGN_LEFT);
  }

/*==================================================================================================*/

BOOL open_libs(void)
  {
    if (!(GfxBase = OpenLibrary("graphics.library", 39))) return FALSE;
    if (!(LayersBase = OpenLibrary("layers.library", 38))) return FALSE;
    if (!(IntuitionBase = OpenLibrary("intuition.library", 39))) return FALSE;
    if (!(UtilityBase = OpenLibrary("utility.library", 39))) return FALSE;
    if (!(TTRenderBase = OpenLibrary("ttrender.library", 2))) return FALSE;
    return TRUE;
  }

/*--------------------------------------------------------------------------------------------------*/

void close_libs(void)
  {
    if (TTRenderBase) CloseLibrary(TTRenderBase);
    if (UtilityBase) CloseLibrary(UtilityBase);
    if (IntuitionBase) CloseLibrary(IntuitionBase);
    if (LayersBase) CloseLibrary(LayersBase);
    if (GfxBase) CloseLibrary(GfxBase);
  }

/*--------------------------------------------------------------------------------------------------*/

void render_text(struct AppData *app, LONG from, LONG to)
  {
    struct TextLine *txl;
    struct TokenHandle *tkh;
    struct Region *rgn, *old_rgn;
    struct Rectangle rect;
    ULONG spc_w, ascend;

    TT_SetModesTags(
      TTA_Window, (ULONG)app->win,
      TTA_Antialias, FALSE,
      TAG_END);

    TT_GetFontAttrsTags(
      TTFA_Ascender, (ULONG)&ascend,
      TAG_END);

    spc_w = TT_StrWidth("·");

    if (rgn = NewRegion())
      {
        rect.MinX = app->l;
        rect.MinY = from + app->t;
        rect.MaxX = app->r;
        rect.MaxY = to + app->t;
        if (OrRectRegion(rgn, &rect))
          {
            old_rgn = InstallClipRegion(app->win->WLayer, rgn);
            for(txl = (struct TextLine*)app->line_list.mlh_Head; txl->next; txl = txl->next)
              {
                if ((txl->pos_y + *app->prm.font_size > from + app->cypos) && (txl->pos_y <= to + app->cypos))
                  {
                    for(tkh = (struct TokenHandle*)txl->tokens.mlh_Head; tkh->next; tkh = tkh->next)
                      {
                        if (strcmp(tkh->token->token, "<SPC>") == 0);
                        else
                          {
                            Move(app->win->RPort, tkh->pos_x + app->l,
                             txl->pos_y + ascend + app->t - app->cypos);
                            TT_PutStr(tkh->token->token);
                          }
                      }
                  }
              }
            InstallClipRegion(app->win->WLayer, old_rgn);
          }
        DisposeRegion(rgn);
     }
  }

/*--------------------------------------------------------------------------------------------------*/

void move_text(struct AppData *app, LONG newpos)
  {
    LONG delta = newpos - app->cypos;

    app->cypos = newpos;

    if (delta > 0)
      {
        if (delta < app->b - app->t)
          {
            ScrollRasterBF(app->win->RPort, 0, delta, app->l, app->t, app->r, app->b);
            render_text(app, app->b - app->t - delta + 1, app->b - app->t);
          }
        else
          {
            EraseRect(app->win->RPort, app->l, app->t, app->r, app->b);
            render_text(app, 0, app->b - app->t);
          }
      }
    else if (delta < 0)
      {
        if (-delta < app->b - app->t)
          {
            ScrollRasterBF(app->win->RPort, 0, delta, app->l, app->t, app->r, app->b);
            render_text(app, 0, -delta + 1);
          }
        else
          {
            EraseRect(app->win->RPort, app->l, app->t, app->r, app->b);
            render_text(app, 0, app->b - app->t);
          }
      }
  }

/*--------------------------------------------------------------------------------------------------*/

inline void update_box_sizes(struct AppData *app)
  {
    app->l = app->win->BorderLeft + MARGIN_SIZE;
    app->t = app->win->BorderTop + MARGIN_SIZE;
    app->r = app->win->Width - app->win->BorderRight - MARGIN_SIZE - 1;
    app->b = app->win->Height - app->win->BorderBottom - MARGIN_SIZE - 1;
  }

/*--------------------------------------------------------------------------------------------------*/

void update_scroller(struct AppData *app)
  {
    UWORD vbody;
    UWORD vpot;
    UWORD visible;

    visible = app->b - app->t + 1;

    if (app->total_height <= visible)
      {
        vpot = 0;
        vbody = 65535;
      }
    else
      {
        vpot = ((app->cypos << 16) - app->cypos) / (app->total_height - visible);
        vbody = ((visible << 16) - visible) / app->total_height;
      }

    NewModifyProp(&app->scroll, app->win, NULL, AUTOKNOB | FREEVERT | PROPBORDERLESS | PROPNEWLOOK,
     0, vpot, 0, vbody, 1);
  }

/*--------------------------------------------------------------------------------------------------*/

void main_loop(struct AppData *app)
  {
    ULONG signals, portmask;
    BOOL running = TRUE;

    portmask = 1 << app->win->UserPort->mp_SigBit;

    while (running)
      {
        signals = Wait(SIGBREAKF_CTRL_C | portmask);
        if (signals & SIGBREAKF_CTRL_C) running = FALSE;
        if (signals & portmask)
          {
            struct IntuiMessage *imsg;
            ULONG msg_class;
            APTR msg_iaddr;

            while (imsg = (struct IntuiMessage*)GetMsg(app->win->UserPort))
              {
                msg_class = imsg->Class;
                msg_iaddr = imsg->IAddress;
                ReplyMsg((struct Message*)imsg);
                switch (msg_class)
                  {
                    case IDCMP_CLOSEWINDOW:
                      running = FALSE;
                    break;

                    case IDCMP_NEWSIZE:
                      update_box_sizes(app);
                      EraseRect(app->win->RPort, app->win->BorderLeft, app->win->BorderTop,
                       app->win->Width - app->win->BorderRight - 1,
                       app->win->Height - app->win->BorderBottom - 1);
                      line_splitter(app);
                      render_text(app, 0, app->b - app->t);
                      update_scroller(app);
                    break;

                    case IDCMP_GADGETDOWN:
                      app->last_clicked = (struct Gadget*)msg_iaddr;
                    break;

                    case IDCMP_GADGETUP:
                      if (app->last_clicked == &app->scroll)
                        {
                          ULONG newpos;

                          newpos = app->pinfo.VertPot * (app->total_height - (app->b - app->t + 1)) / 65535;
                          if (newpos != app->cypos)
                            {
                              move_text(app, newpos);
                            }
                        }
                      app->last_clicked = NULL;
                    break;

                    case IDCMP_MOUSEMOVE:
                      if (app->last_clicked == &app->scroll)
                        {
                          ULONG newpos;

                          newpos = app->pinfo.VertPot * (app->total_height - (app->b - app->t + 1)) / 65535;
                          if (newpos != app->cypos)
                            {
                              move_text(app, newpos);
                            }
                        }
                    break;
                  }
              }
          }
      }
  }


void setup_scroller(struct AppData *app)
  {
    app->scroll.NextGadget = NULL;
    app->scroll.LeftEdge = 5 - app->win->BorderRight;
    app->scroll.TopEdge = app->win->BorderTop + 3;
    app->scroll.Width = app->win->BorderRight - 8;
    app->scroll.Height = -60;
    app->scroll.Flags = GFLG_GADGHNONE | GFLG_RELRIGHT | GFLG_RELHEIGHT;
    app->scroll.Activation = GACT_IMMEDIATE | GACT_RELVERIFY | GACT_RIGHTBORDER | GACT_FOLLOWMOUSE;
    app->scroll.GadgetType = GTYP_PROPGADGET;
    app->scroll.GadgetRender = &app->i1;
    app->scroll.SelectRender = &app->i2;
    app->scroll.GadgetText = NULL;
    app->scroll.SpecialInfo = &app->pinfo;

    app->pinfo.Flags = AUTOKNOB | FREEVERT | PROPBORDERLESS | PROPNEWLOOK;
    app->pinfo.HorizPot = 0;
    app->pinfo.VertPot = 0;
    app->pinfo.HorizBody = 65535;
    app->pinfo.VertBody = 65535;

    AddGadget(app->win, &app->scroll, ~0);
    RefreshGadgets(&app->scroll, app->win, NULL);
  }

LONG Main (void)
  {
    BPTR in;
    Class *rect, *text;
    WORD i;
    struct AppData app;
    struct RDArgs *args;
    ULONG default_size = 14;

    app.cypos = 0;
    app.prm.file_name = "";
    app.prm.font_name = "";
    app.prm.font_size = &default_size;

    if (open_libs())
      {
        if (args = ReadArgs("FILE/A,FONTNAME=-f/K/A,FONTSIZE=-s/K/N", (LONG*)&app.prm, NULL))
          {
            if (mempool = CreatePool(MEMF_ANY, 4096, 4096))
              {
                init_minlist(&app.token_list);

                if (app.screen_lock = LockPubScreen(NULL))
                  {
                    if (app.win = OpenWindowTags(NULL,
                      WA_Width, 600,
                      WA_Height, 350,
                      WA_DragBar, TRUE,
                      WA_DepthGadget, TRUE,
                      WA_CloseGadget, TRUE,
                      WA_SizeGadget, TRUE,
                      WA_MinWidth, 200,
                      WA_MinHeight, 100,
                      WA_MaxWidth, ~0,
                      WA_MaxHeight, ~0,
//                      WA_Gadgets, (ULONG)&app.scroll,
                      WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE | IDCMP_GADGETDOWN | IDCMP_GADGETUP |
                        IDCMP_MOUSEMOVE,
                      WA_Title, (ULONG)"TrueView",
                      TAG_END))
                      {
                        setup_scroller(&app);
                        if (in = Open(app.prm.file_name, MODE_OLDFILE))
                          {
                            parse(&app, in);
                            Close(in);
                            //lf_cleaner(&app.token_list);
                            if (TT_SetFont(app.prm.font_name, *app.prm.font_size))
                              {
                                update_box_sizes(&app);
                                line_splitter(&app);
                                SetAPen(app.win->RPort, 1);
                                SetDrMd(app.win->RPort, JAM1);
                                render_text(&app, 0, app.b - app.t);
                                update_scroller(&app);
                                main_loop(&app);
                              }
                            else PutStr("Failed to open font.\n");
                          }
                        else PrintFault(IoErr(), "TrueView");
                        CloseWindow(app.win);
                      }
                    UnlockPubScreen(NULL, app.screen_lock);
                  }
                DeletePool(mempool);
              }
            FreeArgs(args);
          }
        else PrintFault(IoErr(), "TrueView");
      }
    close_libs();
    return 0;
  }
