/*
  $NiH: readdir.c,v 1.24 2002/09/16 12:42:41 dillo Exp $

  readdir.c -- read directory listing
  Copyright (C) 1996-2002 Dieter Baron

  This file is part of cftp, a fullscreen ftp client
  The author can be contacted at <dillo@giga.or.at>

  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 2 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, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/



#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "directory.h"
#include "ftp.h"
#include "display.h"
#include "options.h"

static int parse_unix(direntry *de, char *line);
static int parse_ms(direntry *de, char *line);
static void init_parse_time(void);
static time_t parse_time(char *date);

typedef int (*parse_func)(direntry *, char *);

parse_func pfunc[] = {
    parse_unix, parse_ms
};
int npfunc = sizeof(pfunc)/sizeof(pfunc[0]);



#define DIR_STEP	1024

directory *
read_dir(FILE *f)
{
    directory *dir;
    direntry entry;
    int n, pf, ret;
    char *line;
    time_t oldt, newt;

    dir = dir_new();
    n = 0;

    pf = 0;
    oldt = 0;

    init_parse_time();

    while ((line=ftp_gets(f)) != NULL) {
	while ((ret=pfunc[pf](&entry, line)) == -1) {
	    pf++;
	    if (pf >= npfunc) {
		pf = 0;
		ret = 1;
		break;
	    }
	}
	if (ret == 0) {
	    dir_add(dir, &entry);
	    n++;
	}
	free(line);

	if ((newt=time(NULL)) != oldt) {
	    disp_status(DISP_STATUS, "listed %d", n);
	    oldt = newt;
	}
    }

#if 0
    if (n == 0) {
	dir->line = (direntry *)malloc(sizeof(direntry));
	dir->line->line = strdup("");
	dir->line->type = 'x';
	dir->line->name = strdup("");
	dir->line->link = NULL;
	n = 1;
    }
#endif

    return dir;
}



static int
parse_unix(direntry *de, char *line)
{
    char *p, *q;
	
    if (strncmp(line, "total ", 6) == 0)
	return 1;

    if (strcspn(line, " ") < 10
	|| (line[10]!=' ' && !isdigit(line[10])))
	return -1;

    if ((de->line=(char *)malloc(strlen(line)+2)) == NULL)
	return 1;
    
    de->line[0] = ' ';
    strcpy(de->line+1, line);

    switch (line[0]) {
    case 'l':
    case 'd':
	de->type = line[0];
	break;
    case '-':
    case 'p':
	de->type = 'f';
	break;
    default:
	de->type = 'x';
    }

    strtok(line+10, " ");	/* skip perms, links */
    strtok(NULL, " ");	/* owner */
    strtok(NULL, " ");	/* group */
    if ((p=strtok(NULL, " ")) == NULL) {
	free(line);
	return 1;
    }
    de->size = strtol(p, &q, 10);
			/* size  */
    if (p == q)
	de->size = -1;
    else if ((p=strtok(NULL, " ")) == NULL) {
	free(line);
	return 1;
    }

    p[12] = '\0';
    de->mtime = parse_time(p);

    p += 13;
    
    if (de->type == 'l') {
	q = p + strlen(p) - de->size;
	if (strncmp(q-4, " -> ", 4) == 0) {
	    q[-4] = '\0';
	    de->name = strdup(p);
	    de->link = strdup(q);
	}
	else {
	    if ((q=strstr(p, " -> ")) == NULL) {
		/* unknown link format */
		de->name = strdup(p);
		de->link = NULL;
	    }
	    else {
		*q = '\0';
		de->name = strdup(p);
		de->link = strdup(q+4);
	    }
	}
	de->size = -1;
    }
    else {
	de->name = strdup(p);
	de->link = NULL;
    }

    if (de->type == 'd' && (strcmp(de->name, "..")==0 ||
			    strcmp(de->name, ".")==0)) {
	free(de->name);
	return 1;
    }

    return 0;
}



static struct tm now;

static void
init_parse_time(void)
{
    struct tm *p;
    time_t t;
    
    t = time(NULL);
    p = gmtime(&t);
    now = *p;
}

static time_t
parse_time(char *date)
{
    static char *mon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
			   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    struct tm tm;
    int i;

    tm = now;
    tm.tm_sec = tm.tm_wday = tm.tm_yday = 0;

    for (i=0; i<12; i++)
	if (strncasecmp(date, mon[i], 3) == 0) {
	    tm.tm_mon = i;
	    break;
	}

    if (i==12)
	return 0;

    tm.tm_mday = atoi(date+4);
    if (tm.tm_mday == 0)
	return 0;

    if (date[9] == ':') {
	tm.tm_hour = atoi(date+7);
	tm.tm_min = atoi(date+10);

	if (tm.tm_mon < now.tm_mon
	    || (tm.tm_mon == now.tm_mon && tm.tm_mday <= now.tm_mday))
	    tm.tm_year = now.tm_year;
	else
	    tm.tm_year = now.tm_year - 1;
    }
    else {
	tm.tm_year = atoi(date+7);
	if (tm.tm_year == 0)
	    return 0;
	tm.tm_year -= 1900;
	tm.tm_hour = tm.tm_min = 0;
    }

    return mktime(&tm);
}



static int
parse_ms(direntry *de, char *line)
{
    struct tm tm;
	
    if (!(isdigit(line[0]) && isdigit(line[1]) && line[2] == '-'
	  && isdigit(line[3]) && isdigit(line[4]) && line[5] == '-'
	  && isdigit(line[6]) && isdigit(line[7]) && line[8] == ' '))
	return -1;

    if ((de->line=(char *)malloc(strlen(line)+2)) == NULL)
	return 1;
    
    de->line[0] = ' ';
    strcpy(de->line+1, line);

    if (strncmp(line+23, " <DIR>  ", 8) == 0) {
	de->type = 'd';
	de->size = -1;
    }
    else {
	de->type = 'f';
	de->size = strtol(line+17, NULL, 10);
    }

    /* time */
    
    tm.tm_sec = tm.tm_wday = tm.tm_yday = 0;
    tm.tm_mon = atoi(line);
    tm.tm_mday = atoi(line+3);
    tm.tm_year = atoi(line+6);
    if (tm.tm_year < 50) {
	/* y2.05k problem */
	tm.tm_year += 100;
    }
    tm.tm_min = atoi(line+13);
    tm.tm_hour = atoi(line+10) % 12;
    if (line[15] == 'P')	
	tm.tm_hour += 12;
    
    
    de->mtime = mktime(&tm);

    de->name = strdup(line+39);

    return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1