Blame src/StatusLine.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net)
Packit 8f70b4
 *
Packit 8f70b4
 * This program is free software; you can redistribute it and/or modify
Packit 8f70b4
 * it under the terms of the GNU General Public License as published by
Packit 8f70b4
 * the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
 * (at your option) any later version.
Packit 8f70b4
 *
Packit 8f70b4
 * This program is distributed in the hope that it will be useful,
Packit 8f70b4
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
 * GNU General Public License for more details.
Packit 8f70b4
 *
Packit 8f70b4
 * You should have received a copy of the GNU General Public License
Packit 8f70b4
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#ifdef HAVE_TERMIOS_H
Packit 8f70b4
#include <termios.h>
Packit 8f70b4
#endif
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <sys/ioctl.h>
Packit 8f70b4
#include "trio.h"
Packit 8f70b4
#include <stdarg.h>
Packit 8f70b4
#include <stdlib.h>
Packit 8f70b4
#include <mbswidth.h>
Packit 8f70b4
#include "xstring.h"
Packit 8f70b4
Packit 8f70b4
#include "ResMgr.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "StatusLine.h"
Packit 8f70b4
Packit 8f70b4
#include "lftp_tinfo.h"
Packit 8f70b4
Packit 8f70b4
ResDecl res_status_interval ("cmd:status-interval", "0.8s", ResMgr::TimeIntervalValidate,ResMgr::NoClosure);
Packit 8f70b4
Packit 8f70b4
int  StatusLine::GetWidth()
Packit 8f70b4
{
Packit 8f70b4
#ifdef TIOCGWINSZ
Packit 8f70b4
   struct winsize sz;
Packit 8f70b4
   sz.ws_col=sz.ws_row=0;
Packit 8f70b4
   ioctl(fd,TIOCGWINSZ,&sz);
Packit 8f70b4
   if(sz.ws_col==0)
Packit 8f70b4
      sz.ws_col=80;
Packit 8f70b4
   if(sz.ws_row==0)
Packit 8f70b4
      sz.ws_row=24;
Packit 8f70b4
   LastHeight=sz.ws_row;
Packit 8f70b4
   return(LastWidth=sz.ws_col);
Packit 8f70b4
#else /* !TIOCGWINSZ */
Packit 8f70b4
   return 80;
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
StatusLine::StatusLine(int new_fd)
Packit 8f70b4
{
Packit 8f70b4
   to_status_line = get_string_term_cap("tsl", "ts");
Packit 8f70b4
   from_status_line = get_string_term_cap("fsl", "fs");
Packit 8f70b4
   prev_line = get_string_term_cap("cuu1","up");
Packit 8f70b4
Packit 8f70b4
   fd=new_fd;
Packit 8f70b4
   update_delayed=false;
Packit 8f70b4
   next_update_title_only=false;
Packit 8f70b4
   strcpy(def_title,"");
Packit 8f70b4
   not_term=!isatty(fd);
Packit 8f70b4
   GetWidth();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
StatusLine::~StatusLine()
Packit 8f70b4
{
Packit 8f70b4
   /* Don't leave a title behind. */
Packit 8f70b4
   WriteTitle("", fd);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void StatusLine::Clear(bool title_also)
Packit 8f70b4
{
Packit 8f70b4
   const char *empty="";
Packit 8f70b4
   update_timer.Stop();
Packit 8f70b4
   ShowN(&empty,1);
Packit 8f70b4
   update_delayed=false;
Packit 8f70b4
   update_timer.SetMilliSeconds(20);
Packit 8f70b4
Packit 8f70b4
   if(title_also)
Packit 8f70b4
      WriteTitle(def_title, fd);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void StatusLine::DefaultTitle(const char *s)
Packit 8f70b4
{
Packit 8f70b4
   strncpy(def_title, s, sizeof(def_title));
Packit 8f70b4
   def_title[sizeof(def_title)-1] = 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void StatusLine::Show(const char *f,...)
Packit 8f70b4
{
Packit 8f70b4
   if(f==0 || f[0]==0)
Packit 8f70b4
   {
Packit 8f70b4
      Clear();
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   char newstr[0x800];
Packit 8f70b4
Packit 8f70b4
   va_list v;
Packit 8f70b4
   va_start(v,f);
Packit 8f70b4
   vsnprintf(newstr,sizeof(newstr),f,v);
Packit 8f70b4
   va_end(v);
Packit 8f70b4
   newstr[sizeof(newstr)-1]=0;
Packit 8f70b4
Packit 8f70b4
   const char *s=newstr;
Packit 8f70b4
   ShowN(&s,1);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void StatusLine::ShowN(const char *const* newstr,int n)
Packit 8f70b4
{
Packit 8f70b4
   if(!update_delayed && shown.IsEqual(newstr,n))
Packit 8f70b4
      return;
Packit 8f70b4
   if(update_delayed && to_be_shown.IsEqual(newstr,n))
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   if(!update_timer.Stopped())
Packit 8f70b4
   {
Packit 8f70b4
      /* not yet */
Packit 8f70b4
      to_be_shown.Assign(newstr,n);
Packit 8f70b4
      update_delayed=true;
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      update(newstr,n);
Packit 8f70b4
      update_delayed=false;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *StatusLine::to_status_line;
Packit 8f70b4
const char *StatusLine::from_status_line;
Packit 8f70b4
const char *StatusLine::prev_line;
Packit 8f70b4
Packit 8f70b4
void StatusLine::WriteTitle(const char *s, int fd) const
Packit 8f70b4
{
Packit 8f70b4
   if(!ResMgr::QueryBool("cmd:set-term-status", getenv("TERM")))
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   subst_t subst[] = {
Packit 8f70b4
      { 'a', "\007" },
Packit 8f70b4
      { 'e', "\033" },
Packit 8f70b4
      { 'n', "\n" },
Packit 8f70b4
      { 's', "lftp" },
Packit 8f70b4
      { 'v', VERSION },
Packit 8f70b4
Packit 8f70b4
      { 'T', s },
Packit 8f70b4
      { 0, "" }
Packit 8f70b4
   };
Packit 8f70b4
Packit 8f70b4
   const char *status_format = ResMgr::Query("cmd:term-status", getenv("TERM"));
Packit 8f70b4
Packit 8f70b4
   xstring &disp=xstring::get_tmp();
Packit 8f70b4
Packit 8f70b4
   if(status_format && *status_format)
Packit 8f70b4
      SubstTo(disp, status_format, subst);
Packit 8f70b4
   else if(to_status_line && from_status_line)
Packit 8f70b4
      /* If we have no format, and we have both tsl and fsl, use them: */
Packit 8f70b4
      disp.vset(to_status_line, s, from_status_line, NULL);
Packit 8f70b4
   else
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   write(fd, disp, disp.length());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void StatusLine::update(const char *const *newstr,int newstr_height)
Packit 8f70b4
{
Packit 8f70b4
   if(not_term)
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   if(!in_foreground_pgrp())
Packit 8f70b4
      return;
Packit 8f70b4
Packit 8f70b4
   /* Don't write blank titles into the title; let Clear() do that. */
Packit 8f70b4
   if(newstr_height>0 && newstr[0][0]) WriteTitle(newstr[0], fd);
Packit 8f70b4
Packit 8f70b4
   if(next_update_title_only)
Packit 8f70b4
   {
Packit 8f70b4
      next_update_title_only=false;
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   int w=GetWidth();
Packit 8f70b4
   int mbflags=0;
Packit 8f70b4
Packit 8f70b4
   if(newstr_height>LastHeight)
Packit 8f70b4
      newstr_height=LastHeight;
Packit 8f70b4
Packit 8f70b4
   // clear old extra lines. Assume we are at beginning of last shown line.
Packit 8f70b4
   int j=shown.Count();
Packit 8f70b4
   if(!prev_line)    // if there is no way to go up, show a single line only.
Packit 8f70b4
      j=newstr_height=1;
Packit 8f70b4
   int i=j-newstr_height;
Packit 8f70b4
   char *spaces=string_alloca(w+1);
Packit 8f70b4
   memset(spaces,' ',w);
Packit 8f70b4
   spaces[w]=0;
Packit 8f70b4
   while(i-->0)
Packit 8f70b4
   {
Packit 8f70b4
      int tw=mbswidth(shown[--j],mbflags);
Packit 8f70b4
      write(fd,"\r",1);
Packit 8f70b4
      write(fd,spaces,tw);
Packit 8f70b4
      write(fd,"\r",1);
Packit 8f70b4
      write(fd,prev_line,strlen(prev_line));
Packit 8f70b4
   }
Packit 8f70b4
   // move to top of shown lines.
Packit 8f70b4
   while(--j>0)
Packit 8f70b4
      write(fd,prev_line,strlen(prev_line));
Packit 8f70b4
Packit 8f70b4
   int curr_line=0;
Packit 8f70b4
   while(curr_line
Packit 8f70b4
   {
Packit 8f70b4
      const char *end=newstr[curr_line];
Packit 8f70b4
      int len=strlen(newstr[curr_line]);
Packit 8f70b4
      int wpos=0;
Packit 8f70b4
      while(len>0)
Packit 8f70b4
      {
Packit 8f70b4
	 int ch_len=mblen(end,len);
Packit 8f70b4
	 if(ch_len<1)
Packit 8f70b4
	    ch_len=1;
Packit 8f70b4
	 int ch_width=mbsnwidth(end,ch_len,mbflags);
Packit 8f70b4
	 if(wpos+ch_width>w-1)
Packit 8f70b4
	    break;
Packit 8f70b4
	 end+=ch_len;
Packit 8f70b4
	 len-=ch_len;
Packit 8f70b4
	 wpos+=ch_width;
Packit 8f70b4
	 if(wpos>=w-1)
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      // FIXME: this assumes that multibyte chars cannot include ' '.
Packit 8f70b4
      while(end>newstr[curr_line] && end[-1]==' ')
Packit 8f70b4
      {
Packit 8f70b4
	 end--;
Packit 8f70b4
	 wpos--;  // FIXME: assumption - space width is 1
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(end-newstr[curr_line]>0)
Packit 8f70b4
	 write(fd,newstr[curr_line],end-newstr[curr_line]);
Packit 8f70b4
Packit 8f70b4
      const char *shown_curr=(curr_line>=shown.Count()?"":shown[curr_line]);
Packit 8f70b4
      int dif=strlen(shown_curr)-(end-newstr[curr_line])+2;
Packit 8f70b4
      if(dif>(w-1)-wpos)
Packit 8f70b4
	 dif=(w-1)-wpos;
Packit 8f70b4
      if(dif>0)
Packit 8f70b4
	 write(fd,spaces,dif);
Packit 8f70b4
      write(fd,"\r",1);
Packit 8f70b4
Packit 8f70b4
      if(++curr_line
Packit 8f70b4
	 write(fd,"\n",1);
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   shown.Assign(newstr,newstr_height);
Packit 8f70b4
Packit 8f70b4
   update_timer.SetResource("cmd:status-interval",0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void StatusLine::WriteLine(const char *f,...)
Packit 8f70b4
{
Packit 8f70b4
   va_list v;
Packit 8f70b4
   va_start(v,f);
Packit 8f70b4
   xstring& newstr=xstring::vformat(f,v).append('\n');
Packit 8f70b4
   va_end(v);
Packit 8f70b4
Packit 8f70b4
   Clear();
Packit 8f70b4
Packit 8f70b4
   write(fd,newstr,newstr.length());
Packit 8f70b4
   update_delayed=false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int StatusLine::Do()
Packit 8f70b4
{
Packit 8f70b4
   if(!update_delayed)
Packit 8f70b4
      return STALL;
Packit 8f70b4
   if(update_timer.Stopped())
Packit 8f70b4
   {
Packit 8f70b4
      update(to_be_shown.Set(),to_be_shown.Count());
Packit 8f70b4
      update_delayed=false;
Packit 8f70b4
      return STALL;
Packit 8f70b4
   }
Packit 8f70b4
   return STALL;
Packit 8f70b4
}