/*{{{  #includes*/
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <dirent.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <local/bool.h>
#include <local/argparse.h>

#include "../common/keys.h"
#include "../common/rcformat.h"
#include "fold_os.h"
/*}}}  */

/*{{{  variables*/
/*{{{  fold-marks*/
char fold_open_str[tag_length]=begin_standard;
char fold_close_str[tag_length]=end_standard;
char fold_file_str[tag_length]=file_standard;
char fold_line_str[tag_length]=line_standard;
/*}}}  */
/*{{{  PATH_SEP*/
#ifndef TOS
#  define PATH_SEP "/"
#else
#  define PATH_SEP "\\"
#endif
/*}}}  */
/*{{{  for commandline-options*/
char keybase[_POSIX_PATH_MAX+1]=".origami";
char mark_mode[mark_name_lg]="";
bool aborted;
bool always=FALSE;
bool mark_no_perm=FALSE;
/*}}}  */
FILE *rcfile;
int depth=255;
int user;
int group;
/*}}}  */
/*{{{  argtab*/
#define argnum 7

ARG argtab[argnum] =
{
  'k', STRING_ARG, keybase,
  'm', STRING_ARG, mark_mode,
  '?', BOOL_ARG, &aborted,
  'h', BOOL_ARG, &aborted,
  'd', INT_ARG, &depth,
  'a', BOOL_ARG, &always,
  'p', BOOL_ARG, &mark_no_perm
};
/*}}}  */

/*{{{  standards*/
/*{{{  completebase*/
#ifdef STD_C
char *completebase(char *s, char *t)
#else
char *completebase(s,t) char *s, *t;
#endif
{
  char *h=getenv("HOME");

  if (h==NULL) *s='\0';
  else
  {
    strcpy(s,h);
    strcat(s,PATH_SEP);
  }
  strcat(s,keybase);
  strcat(s,t);
  return(s);
}
/*}}}  */
/*{{{  parse rcfile*/
#ifdef STD_C
bool rc_file_parser(void)
#else
bool rc_file_parser()
#endif
{
  readtags input;

  while ((input=getc(rcfile))!=RC_ENDE) {
    switch (input) {
      /*{{{  keydef*/
      case RC_DEFKEY: {
        int nodes;
      
        getc(rcfile);
        /*{{{  get size of table*/
        nodes=getw(rcfile);
        /*}}}  */
        /*{{{  read all nodes*/
        while (nodes--) {
          getw(rcfile);
          getc(rcfile);
          getw(rcfile);
          getw(rcfile);
        }
        /*}}}  */
        break;
      }
      /*}}}  */
      /*{{{  macrodef/init*/
      case RC_INITMACRO:
      case RC_DEFMACRO: {
        int lg;
      
        getw(rcfile);
        lg=getw(rcfile);
        while (lg--) getw(rcfile);
        break;
      }
      /*}}}  */
      /*{{{  keyname*/
      case RC_KEYNAME: {
        int i=keyn_lg;
      
        while (i--) getc(rcfile);
        break;
      }
      /*}}}  */
      /*{{{  ints*/
      case RC_INTS:
        getw(rcfile);
        break;
      /*}}}  */
      /*{{{  auto_macro*/
      case RC_AUTOM:
        getc(rcfile);
        break;
      /*}}}  */
      /*{{{  break*/
      case RC_BREAK: {
        getc(rcfile);
        break;
      }
      /*}}}  */
      /*{{{  marks*/
      case RC_MARKS: {
        char name[mark_name_lg];
        int i;
      
        /*{{{  read stored name*/
        for (i=0;i<(mark_name_lg-1);i++)
          name[i]=getc(rcfile);
        name[mark_name_lg-1]='\0';
        /*}}}  */
        if (!strcmp(mark_mode,name)) {
          /*{{{  reset mark-strings*/
          *mark_mode='\0';
          for (i=0;i<(tag_length-1);i++) fold_open_str[i]=getc(rcfile);
          for (i=0;i<(tag_length-1);i++) fold_line_str[i]=getc(rcfile);
          for (i=0;i<(tag_length-1);i++) fold_file_str[i]=getc(rcfile);
          for (i=0;i<(tag_length-1);i++) fold_close_str[i]=getc(rcfile);
          /*}}}  */
        } else
          /*{{{  overread the data*/
          for (i=0;i<(4*(tag_length-1));i++,getc(rcfile));
          /*}}}  */
        break;
      }
      /*}}}  */
      /*{{{  error!*/
      default: return(TRUE);
      /*}}}  */
    }
  }
  /*{{{  if marks needed and not found -> error*/
  if (*mark_mode) {
    printf("no matching marks found.\n");
    return(TRUE);
  }
  /*}}}  */
  return(FALSE);
}
/*}}}  */
/*{{{  set_other_mark*/
#ifdef STD_C
int set_other_mark(void)
#else
int set_other_mark()
#endif /* STD_C */
{
  char keyfile[_POSIX_PATH_MAX+1];

  if (getenv("HOME")==NULL) {
    printf ("no home for keybase\n");
    return (1);
  }
  /*{{{  open rc-file*/
  if ((rcfile = fopen(completebase(keyfile,"rc"),"r")) == 0) {
    printf ("no keybase %s accessable.\n",keyfile);
    return (1);
  }
  /*}}}  */
  if (rc_file_parser()) {
    printf("incorrect rc-data.\n");
    return(1);
  }
  fclose(rcfile);
  return (0);
}
/*}}}  */
/*}}}  */
/*{{{  path_get*/
#ifdef STD_C
char *path_get(char *dir)
#else
char *path_get(dir)
char *dir;
#endif
{
  char *s,*getcwd(), buf[_POSIX_PATH_MAX+1];

  if ((s=getcwd(buf, _POSIX_PATH_MAX))==NULL) {
    perror("pwd");exit(1);
  }
  return(strcpy(dir,s));
}
/*}}}  */
/*{{{  dirhandle*/
/*{{{  dir_ent_cmp*/
#ifdef STD_C
int dir_ent_cmp(char **a,char **b)
#else
int dir_ent_cmp(a,b)
char **a,**b;
#endif
{
  return(strcmp(*a,*b));
}
/*}}}  */
/*{{{  forward for filehandle*/
#ifdef STD_C
void filehandle(char *name, char *dir, int curr_depth);
#endif
/*}}}  */

#ifdef STD_C
void dirhandle(char *name,char *dir,int curr_depth)
#else
dirhandle(name,dir,curr_depth)
char *name;
char *dir;
int curr_depth;
#endif
{
  char new_wd[_POSIX_PATH_MAX+1];
  DIR *dirptr;

  if (curr_depth<=depth) {
    /*{{{  recursion*/
    if (chdir(name))
      printf("%s cannot enter\n",name);
    else {
      /*{{{  directory entered*/
      if ((dirptr=opendir("."))!=NULL) {
        /*{{{  can read the entries*/
        /*{{{  local variables*/
        struct dirent *ls;
        char **s;
#                define bl_size 128
        int count=0;
        int curr;
        int curr_size=bl_size;
        bool marked_point=FALSE;
        /*}}}  */
        
        /*{{{  get current path*/
        if (path_get(new_wd)==NULL) {
          perror("pwd");exit(1);
        }
        /*}}}  */
        /*{{{  get buffer for dir-entries*/
        if ((s=malloc(curr_size*sizeof(char*)))==NULL) {
          fprintf(stderr,"no memory\n");
          exit(1);
        }
        /*}}}  */
        /*{{{  read the contents of the directory*/
        while ((ls=readdir(dirptr))!=NULL) {
          /*{{{  expand entry-list*/
          if (count==curr_size) {
            char **old=s;
            int x=0;
          
            if ((s=malloc((curr_size+bl_size)*sizeof(char*)))==NULL) {
              fprintf(stderr,"no memory\n");
              exit(1);
            }
            while (x<curr_size) {s[x]=old[x];x++;}
            free(old);
            curr_size+=bl_size;
          }
          /*}}}  */
          /*{{{  get memory for entry*/
          if ((s[count]=malloc(strlen(ls->d_name)+1))==NULL) {
            fprintf(stderr,"no memory\n");
            exit(1);
          }
          /*}}}  */
          strcpy(s[count++],ls->d_name);
        }
        /*}}}  */
        qsort(s,count,sizeof(char*),dir_ent_cmp);
        printf("%s  %s\n",fold_open_str,name);
        /*{{{  handle all entries of the directory*/
        curr=0;
        while (curr<count) {
          if (strcmp(".",s[curr]) && strcmp("..",s[curr])) {
            /*{{{  maybe mark .???*/
            if (!marked_point && s[curr][0]=='.') {
              marked_point=TRUE;
              printf("%s  .xxx files\n",fold_open_str);
            }
            /*}}}  */
            /*{{{  maybe endmark .???*/
            if (marked_point && s[curr][0]!='.') {
              marked_point=FALSE;
              printf("%s  \n",fold_close_str);
            }
            /*}}}  */
            filehandle(s[curr],new_wd,curr_depth);
            free(s[curr]);
          }
          curr++;
        }
        /*}}}  */
        if (marked_point) printf("%s  \n",fold_close_str);
        printf("%s  \n",fold_close_str);
        free(s);
        closedir(dirptr);
        /*}}}  */
      } else
        printf("cannot read this directory.\n");
      /*}}}  */
    }
    chdir(dir);
    /*}}}  */
  } else
    printf("maxdepth reached for %s.\n",name);
}
/*}}}  */
/*{{{  filehandle*/
#ifdef STD_C
void filehandle(char *name,char *dir,int curr_depth)
#else
filehandle(name,dir,curr_depth)
char *name;
char *dir;
int curr_depth;
#endif
{
  struct stat buff;
  int stat_ret;

  if (curr_depth<=depth)
    if (stat_ret=stat(name,&buff))
      printf("cannot stat %d: %s.\n",stat_ret,name);
    else {
      if (   ((buff.st_uid==user) && (S_IRUSR&buff.st_mode))
          || ((buff.st_gid==group) && (S_IRGRP&buff.st_mode))
          || (S_IROTH&buff.st_mode)
          || always )
      {
        /*{{{  handle a directory*/
        if (S_ISDIR(buff.st_mode)) dirhandle(name,dir,curr_depth+1);
        /*}}}  */
        /*{{{  handle a file*/
        if (S_ISREG(buff.st_mode)) {
          printf("%sF %s\n",fold_open_str,name);
          printf("%sF %s%s%s\n",fold_file_str,dir,PATH_SEP,name);
          printf("%sF \n",fold_close_str);
        }
        /*}}}  */
      } else if (mark_no_perm)
        printf("no permission for %s\n",name);
    }
}
/*}}}  */

/*{{{  main*/
#ifdef STD_C
int main(argc, argv) int argc; char *argv[];
#else
main(argc, argv) int argc; char *argv[];
#endif
{
  int x=1;
  char call_path[_POSIX_PATH_MAX+1];

  user=getuid();
  group=getgid();
  argc=argparse(argc,argv,argtab,argnum);
  /*{{{  usage in args*/
  if (aborted) {
    fprintf(stderr,"usage: folder [-ahp?] [-k<namebase>] [-m<markname>]\n");
    fprintf(stderr,"              [-d<depth>] [filename] ...\n");
    exit(0);
  }
  /*}}}  */
  /*{{{  depth <=0*/
  if (depth<=0) {
    printf("nothing to do.\n");
    exit(0);
  }
  /*}}}  */
  if (*mark_mode && set_other_mark()) exit(1);
  path_get(call_path);
  if (argc<=1)
    filehandle(".",call_path,0);
  else
    while (x<argc) filehandle(argv[x++],call_path,0);
  exit(0);
}
/*}}}  */
