/* filesize:  print out the file sizes of the specified files

   Copyright (C) 2004-2026 by Brian Lindholm.  This file is part of the
   littleutils utility set.

   The filesize utility 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, or (at your option) any later
   version.

   The filesize utility 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
   the littleutils.  If not, see <https://www.gnu.org/licenses/>. */

#include <config.h>

#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <limits.h>
#ifdef HAVE_STDIO_H
# include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
# define OPTEND -1
#else
# define OPTEND EOF
#endif
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif

#ifdef __MINGW32__
extern int getopt(int argc, char * const *argv, const char *optstring);
extern char *optarg;
extern int optind;
#endif

#ifdef DJGPP
unsigned short _djstat_flags = 63;  /* speed up stat command for DJGPP */
#endif

#ifndef PATH_MAX
# define PATH_MAX 256
#endif

char *sizefmt_q = (sizeof(off_t) <= sizeof(long) ? "%lu\n" : "%llu\n");
char *sizefmt_v = (sizeof(off_t) <= sizeof(long) ? "%s\t%lu\n" : "%s\t%llu\n");
off_t total_size = 0;

/* help function */
static void help(FILE *where)
{
  fprintf(where, "filesize " PACKAGE_VERSION "\n"
    "usage: filesize [-f file_list] [-h(elp)] [-p(ipe)] [-q(uiet)]\n"
    "         [-t(otalize)] [-v(erbose)] file...\n");
}

/* print filesize function */
static void printsize(char *filename, int totalize, int verbose)
{
  struct stat file_stats;

  if (stat(filename, &file_stats))
    fprintf(stderr, "filesize error: can't determine size of %s\n", filename);
  else
    if (((file_stats.st_mode & S_IFDIR) != S_IFDIR) && ((file_stats.st_mode & S_IFREG) == S_IFREG)) {
      if (totalize == 1)
        total_size += file_stats.st_size;
      else if (verbose == 1)
        fprintf(stdout, sizefmt_v, filename, file_stats.st_size);
      else
        fprintf(stdout, sizefmt_q, file_stats.st_size);
    }
}

/* main program */
int main(int argc, char *argv[])
{
  FILE *infile;
  char filename[PATH_MAX], *listname, *newline, *rc;
  int argn, opt, totalize, use_file, use_pipe, verbose;

  /* parse options */
  listname = "";
  totalize = 0;
  verbose = 0;
  use_file = 0;
  use_pipe = 0;
  while ((opt = getopt(argc, argv, "f:hpqtv")) != OPTEND) switch (opt) {
    case 'f':
      use_file = 1;
      listname = optarg;
      break;
    case 'h':
      help(stdout);
      return(0);
    case 'p':
      use_pipe = 1;
      break;
    case 'q':
      verbose = -1;
      break;
    case 't':
      totalize = 1;
      break;
    case 'v':
      verbose = 1;
      break;
    case '?':
      help(stderr);
      return(1);
  }
  /* finalize options */
  if ((optind == argc) && (use_file == 0) && (use_pipe == 0)) {
    help(stdout);
    return(0);
  }
  if (verbose == 0) {
    if (((argc - optind) != 1) || use_file || use_pipe)
      verbose = 1;
    else
      verbose = -1;
  }
  /* process files in listed in file specified by -f option */
  if (use_file) {
    infile = fopen(listname, "r");
    if (infile == NULL)
      fprintf(stderr, "filesize error: can't open %s!\n", listname);
    else {
      while (!feof(infile)) {
        rc = fgets(filename, PATH_MAX - 1, infile);
        if (rc != NULL) {
          newline = strchr(filename, '\n');
          if (newline != NULL) *newline = '\0';
          if (strlen(filename) != 0) printsize(filename, totalize, verbose);
        }
      }
      (void) fclose(infile);
    }
  }
  /* process files listed on stdin (i.e., the -p option) */
  if (use_pipe) while (!feof(stdin)) {
    rc = fgets(filename, PATH_MAX - 1, stdin);
    if (rc != NULL) {
      newline = strchr(filename, '\n');
      if (newline != NULL) *newline = '\0';
      if (strlen(filename) != 0) printsize(filename, totalize, verbose);
    }
  }
  /* process files given in the argument list */
  for (argn = optind; argn < argc; argn++) printsize(argv[argn], totalize, verbose);
  /* print total size if requested */
  if (totalize == 1) fprintf(stdout, sizefmt_q, total_size);
  /* indicate successful finish */
  return(0);
}
