// rslvlink.c
// Resolve a soft link's actual filename by tracing down the 
// list of links until the non-softlink object is found (up to
// a maximum number of attempts)
// Author: Dennis Lovelady (unixsrc@lovelady.com)
//
// ---Log--------------------------------------------Author--------Date---
// Original version written for AIX                  Lovelady  1996-Sep-18
// Modified to handle multiple files                 Lovelady  1999
// Modified for POSIX                                Lovelady  2003-May-13
// Released to public domain                         Lovelady  2003-May-13



#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
#include <errno.h>

void *PgmName;
#define MAX_LINKS 50
#define SUMMARY_USAGE 0
#define FULL_USAGE 1



void Usage(short PrintAll, void *pgm, FILE *fp)
    {
    fprintf(fp,"\nUsage: %s linkname [linkname...]\n\n", pgm);
    if (! PrintAll) return;
    fprintf(fp,"\t%s will print the actual filename(s) or directory name(s)\n",
            basename(pgm));
    fprintf(fp,"\tassociated with the input linkname(s).  Output will be\n");
    fprintf(fp,"\tactual filename, if only one linkname is passed.  If more\n");
    fprintf(fp,"\tthan one parameter, then output will be in the format of:\n");
    fprintf(fp, "\t\tlinkname1 -> actual_file1\n");
    fprintf(fp, "\t\tlinkname2 -> actual_file2\n");
    fprintf(fp, "\t(one line per linkname passed).\n");
    fprintf(fp, "\tReturns:\n\t\t0 = no error\n\t\tOther = errors enountered\n\n");
    }



int GetStat(void *pfile, struct stat *status)
    {
    // obtain the stat structure for the current link
    // function returns lstat result (0=OK....)

    int rc;
    char *Type;
    if ((rc = lstat(pfile, status)) != 0)
        {
        fprintf(stderr, "%s: %s: %s\n", PgmName, strerror(errno), pfile);
        }

    // We could use exit(rc) here instead of return(rc), but that's
    // generally bad form.  Let the main routine handle the error as
    // it sees fit.

    return(rc);
    }



int HandleFile(void *FileName, void *LinkName)
    {
    int  rc, n=0;
    char pfile[PATH_MAX];
    struct stat status;

    // Point pfile to the name of the file we are seeking, and get
    // status of that file

    strcpy(pfile, FileName);
    strcpy(LinkName, FileName);
    rc=GetStat(pfile, &status);

    // Until the "current file" is not a soft-link, trace it down to 
    // its source.  Because a softlink can unintentionally point to
    // itself, or we can get into a scenario like a -> b -> ... -> a
    // we limit this to 50 attempts via MAX_LINKS.  There is no magic
    // in this number; any reasonable value could be used.

    while ((rc == 0) && (S_ISLNK(status.st_mode)))
        {
        if (n++ > MAX_LINKS)
            {
            fprintf(stderr,"%s: Error: too many links to follow (%d): %s\n",
                    PgmName, n, FileName);
            fprintf(stderr,"Stopping at %s\n", pfile);
            return(255);
            }

        // Since this is a softlink file, we will use readlink to
        // obtain the next filename to use....

        if ((rc = readlink(pfile, LinkName, sizeof(LinkName))) < 0)
            {
            fprintf(stderr,"%s: %s: %s\n", PgmName, strerror(errno), pfile);
            printf("%s\n", FileName);
            return(rc);
            }

        // pfile always contains the name of the file we're working with...

        strcpy(pfile, LinkName);
        rc = GetStat(pfile, &status);

        // Do until pfile is not a softlink, or until error

        }
    return(rc);
    }




int main(int argc, void *argv[])
    {
    int         rc=0, n, end_rc=0;
    char        *pfile, LinkName[PATH_MAX];

    PgmName = argv[0];

    // Ensure that we are called with correct usage

    if (argc < 2)
        {
        Usage(FULL_USAGE, argv[0], stderr);
        exit(1);
        }

    // Populate pfile with the name of the file we are seeking, and get
    // status of that file

    for (n=1; n<argc; n++)
        {
        rc = HandleFile(argv[n], LinkName);
        if (rc == 0)
            {
            if (argc > 2)
                printf("%s -> %s\n", argv[n], LinkName);
            else
                printf("%s\n", LinkName);
            }
        if (rc > 0)
            end_rc = rc ;
        }
    exit(end_rc);
    }
