/* fileown:  print out the owner or group of the specified files

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

   The fileown 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 fileown 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>

#include <grp.h>
#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <limits.h>
#include <pwd.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_SYS_TYPES_H
# include <sys/types.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 *uid_fmt = (sizeof(uid_t) <= sizeof(int) ? "%u" : "%lu");
char *gid_fmt = (sizeof(gid_t) <= sizeof(int) ? "%u" : "%lu");

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

/* print file owner function */
static void printowner(char *filename, int verbose, int print_numbers, int print_gid, int print_uid)
{
  struct stat file_stats;
  struct passwd *file_passwd;
  struct group *file_group;

  if (stat(filename, &file_stats))
    fprintf(stderr, "fileown error: can't determine uid/gid of %s\n", filename);
  else {
    if (verbose == 1) fprintf(stdout, "%s\t", filename);
    if (print_uid == 1) {
      if (print_numbers == 1)
        fprintf(stdout, uid_fmt, file_stats.st_uid);
      else {
        file_passwd = getpwuid(file_stats.st_uid);
        if (file_passwd == NULL)
          fprintf(stdout, uid_fmt, file_stats.st_uid);
        else
          fprintf(stdout, "%s", file_passwd->pw_name);
      }
    }
    if ((print_uid == 1) && (print_gid == 1)) fprintf(stdout, ":");
    if (print_gid == 1) {
      if (print_numbers == 1)
        fprintf(stdout, gid_fmt, file_stats.st_gid);
      else {
        file_group = getgrgid(file_stats.st_gid);
        if (file_group == NULL)
          fprintf(stdout, gid_fmt, file_stats.st_gid);
        else
          fprintf(stdout, "%s", file_group ->gr_name);
      }
    }
    fprintf(stdout, "\n");
  }
}

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

  /* parse options */
  listname = "";
  verbose = 0;
  use_file = 0;
  use_pipe = 0;
  print_numbers = 0;
  print_uid = 1;
  print_gid = 0;
  while ((opt = getopt(argc, argv, "bf:ghnpqv")) != OPTEND) switch (opt) {
    case 'b':
      print_uid = 1;
      print_gid = 1;
      break;
    case 'f':
      use_file = 1;
      listname = optarg;
      break;
    case 'g':
      print_uid = 0;
      print_gid = 1;
      break;
    case 'h':
      help(stdout);
      return(0);
    case 'n':
      print_numbers = 1;
      break;
    case 'p':
      use_pipe = 1;
      break;
    case 'q':
      verbose = -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, "fileown 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) printowner(filename, verbose, print_numbers, print_gid, print_uid);
        }
      }
      (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) printowner(filename, verbose, print_numbers, print_gid, print_uid);
    }
  }
  /* process files given in the argument list */
  for (argn = optind; argn < argc; argn++) printowner(argv[argn], verbose, print_numbers, print_gid, print_uid);
  /* indicate successful finish */
  return(0);
}
