/*
* lftp - file transfer program
*
* Copyright (c) 1996-2012 by Alexander V. Lukyanov (lav@yars.free.net)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/* All indexes in this function start at 0; -1 is used contextually to
* indicate the last job or moving to the end of the list. */
#include
#include
#include
#include
#include
#include "QueueFeeder.h"
#include "plural.h"
#include "misc.h"
const char *QueueFeeder::NextCmd(CmdExec *exec, const char *)
{
if(jobs == NULL) return NULL;
/* denext the first job */
QueueJob *job = grab_job(0);
buffer.truncate(0);
if(xstrcmp(cur_pwd, job->pwd)) {
buffer.append("cd ").append_quoted(job->pwd).append("; ");
cur_pwd.set(job->pwd);
}
if(xstrcmp(cur_lpwd, job->lpwd)) {
buffer.append("lcd ").append_quoted(job->lpwd).append("; ");
cur_lpwd.set(job->lpwd);
}
buffer.append(job->cmd.get()).append('\n');
delete job;
return buffer;
}
void QueueFeeder::QueueCmd(const char *cmd, const char *pwd, const char *lpwd, int pos, int v)
{
QueueJob *job = new QueueJob;
job->cmd.set(cmd);
job->pwd.set(pwd);
job->lpwd.set(lpwd);
/* we never want a newline at the end: */
if(last_char(job->cmd) == '\n')
job->cmd.truncate(strlen(job->cmd)-1);
insert_jobs(job, jobs, lastjob, pos != -1? get_job(pos): NULL);
PrintJobs(job, v, _("Added job$|s$"));
}
int QueueFeeder::JobCount(const QueueJob *j)
{
int job_count=0;
for(; j; j=j->next)
job_count++;
return job_count;
}
/* verbose:
* 0, quiet
* 1, interactive
* 2, verbose (print changes of pwd and lpwd)
* PrintRequeue, output to requeue
*/
xstring& QueueFeeder::FormatJobs(xstring& s,const QueueJob *job, int v, const char *plur) const
{
if(v < 1)
return s;
const char *pwd = 0, *lpwd = 0;
if(v == PrintRequeue)
{
for(const QueueJob *j = job; j; j=j->next)
{
if(xstrcmp(pwd, job->pwd))
{
s.append("cd ").append_quoted(job->pwd).append(" &\n");
pwd = job->pwd;
}
if(xstrcmp(lpwd, job->lpwd))
{
s.append("lcd ").append_quoted(job->lpwd).append(" &\n");
lpwd = job->lpwd;
}
s.append("queue ").append_quoted(job->cmd).append('\n');
}
return s;
}
int job_count=JobCount(job);
if(job_count>1)
s.appendf("%s:\n", plural(plur,job_count));
pwd = cur_pwd;
lpwd = cur_lpwd;
int n = 1;
for(const QueueJob *j = job; j; j=j->next)
{
/* Print pwd/lpwd changes when v >= 2. (This only happens when there's
* more than one.) */
if(xstrcmp(pwd, job->pwd))
{
if(v > 2) {
s.append("\tcd ").append_quoted(job->pwd).append('\n');
}
pwd = job->pwd;
}
if(xstrcmp(lpwd, job->lpwd))
{
if(v > 2) {
s.append("\tlcd ").append_quoted(job->lpwd).append('\n');
}
lpwd = job->lpwd;
}
if(job_count==1)
s.appendf("%s: ", plural(plur,job_count));
else
s.appendf("\t%2d. ",n++);
s.append(j->cmd.get()).append('\n');
}
return s;
}
void QueueFeeder::PrintJobs(const QueueJob *job, int v, const char *plur) const
{
xstring buf("");
FormatJobs(buf,job,v,plur);
printf("%s",buf.get());
}
bool QueueFeeder::DelJob(int from, int v)
{
QueueJob *job = grab_job(from);
if(!job)
{
if(v > 0)
{
if(from == -1 || !jobs)
printf(_("No queued jobs.\n"));
else
printf(_("No queued job #%i.\n"), from+1);
}
return false;
}
PrintJobs(job, v, _("Deleted job$|s$"));
FreeList(job);
return true;
}
bool QueueFeeder::DelJob(const char *cmd, int v)
{
QueueJob *job = grab_job(cmd);
if(!job)
{
if(v > 0)
{
if(!jobs)
printf(_("No queued jobs.\n"));
else
printf(_("No queued jobs match \"%s\".\n"), cmd);
}
return false;
}
PrintJobs(job, v, _("Deleted job$|s$"));
FreeList(job);
return true;
}
/* When moving, grab the insertion pointer *before* pulling out things to
* move, since doing so will change offsets. (Note that "to == -1" means
* "move to the end", not "before the last entry".)
*/
bool QueueFeeder::MoveJob(int from, int to, int v)
{
/* Safety: make sure we don't try to move an item before itself. */
if(from == to) return false;
QueueJob *before = to != -1? get_job(to): NULL;
QueueJob *job = grab_job(from);
if(job == NULL) return false;
PrintJobs(job, v, _("Moved job$|s$"));
assert(job != before);
insert_jobs(job, jobs, lastjob, before);
return true;
}
bool QueueFeeder::MoveJob(const char *cmd, int to, int v)
{
QueueJob *before = to != -1? get_job(to): NULL;
/* Mild hack: we need to make sure the "before" job isn't one that's
* going to be moved, so move it upward until it isn't. */
while(before && !fnmatch(cmd, before->cmd,FNM_CASEFOLD)) before=before->next;
QueueJob *job = grab_job(cmd);
if(job == NULL) return false;
PrintJobs(job, v, _("Moved job$|s$"));
insert_jobs(job, jobs, lastjob, before);
return true;
}
/* remove the given job from the list */
void QueueFeeder::unlink_job(QueueJob *job)
{
/* update head/tail */
if(!job->prev) jobs = jobs->next;
if(!job->next) lastjob = lastjob->prev;
/* linked list stuff */
if(job->prev) job->prev->next = job->next;
if(job->next) job->next->prev = job->prev;
job->prev = job->next = 0;
}
QueueFeeder::QueueJob *QueueFeeder::get_job(int n)
{
QueueJob *j;
if(n == -1) {
j = lastjob;
} else {
j = jobs;
while(j && n--) j=j->next;
}
return j;
}
/* get the n'th job, removed from the list; returns NULL (an empty list)
* if there aren't that many jobs: */
QueueFeeder::QueueJob *QueueFeeder::grab_job(int n)
{
QueueJob *j = get_job(n);
if(j)
unlink_job(j);
return j;
}
QueueFeeder::QueueJob *QueueFeeder::grab_job(const char *cmd)
{
QueueJob *j = jobs, *head = NULL, *tail = NULL;
while(j) {
QueueJob *match = get_next_match(cmd, j);
if(!match) break;
j = match->next;
/* matches */
unlink_job(match);
insert_jobs(match, head, tail, NULL);
}
return head;
}
QueueFeeder::QueueJob *QueueFeeder::get_next_match(const char *cmd, QueueJob *j)
{
while(j) {
if(!fnmatch(cmd, j->cmd,FNM_CASEFOLD))
return j;
j = j->next;
}
return 0;
}
/* insert a list of jobs before "before", or at the end if before is NULL.
* If before is not NULL, it must be contained between lst_head and lst_tail. */
void QueueFeeder::insert_jobs(QueueJob *job,
QueueJob *&lst_head,
QueueJob *&lst_tail,
QueueJob *before)
{
assert(!job->prev); /* this should be an independant, clean list head */
/* Find the last entry in the new list. (This is a bit inefficient, as
* we usually know this somewhere else, but passing around both head
* and tail pointers of the new job list is too klugy.) */
QueueJob *tail = job;
while(tail->next) tail=tail->next;
if(!before) {
/* end */
job->prev = lst_tail;
tail->next = 0; /* superfluous; here for clarity */
} else {
tail->next = before;
job->prev = before->prev;
}
if(job->prev) job->prev->next = job;
if(tail->next) tail->next->prev = tail;
if(!tail->next) lst_tail = tail;
if(!job->prev) lst_head = job;
}
/* Free a list of jobs (forward only; j should be a head pointer.) */
void QueueFeeder::FreeList(QueueJob *j)
{
while(j) {
QueueJob *job = j;
j = j->next;
delete job;
}
}
QueueFeeder::~QueueFeeder()
{
FreeList(jobs);
}
xstring& QueueFeeder::FormatStatus(xstring& s,int v,const char *prefix) const
{
if(jobs == NULL)
return s;
if(v == PrintRequeue)
return FormatJobs(s, jobs, v, "");
s.append(prefix).append(_("Commands queued:")).append('\n');
int n = 1;
const char *pwd = cur_pwd, *lpwd = cur_lpwd;
for(const QueueJob *job = jobs; job; job = job->next) {
if(v<2 && n>4 && job->next)
{
s.appendf("%s%2d. ...\n",prefix,n);
break;
}
/* Print pwd/lpwd changes when v >= 2. */
if(v >= 2 && (xstrcmp(pwd, job->pwd)))
s.appendf("%s cd %s\n",prefix,job->pwd.get());
if(v >= 2 && (xstrcmp(lpwd, job->lpwd)))
s.appendf("%s lcd %s\n",prefix,job->lpwd.get());
pwd = job->pwd;
lpwd = job->lpwd;
s.appendf("%s%2d. %s\n",prefix,n++,job->cmd.get());
}
return s;
}