/*++ Copyright (c) 2015 Minoca Corp. All Rights Reserved Module Name: readlink.c Abstract: This module implements the readlink command, which returns the destination of a symbolic link. Author: Evan Green 24-Mar-2015 Environment: POSIX --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include #include #include #include #include "swlib.h" // // ---------------------------------------------------------------- Definitions // #define READLINK_VERSION_MAJOR 1 #define READLINK_VERSION_MINOR 0 #define READLINK_USAGE \ "usage: readlink [options] path\n" \ "The readlink utility prints the destination of a symbolic link. \n" \ "Options are:\n" \ " -f, --canonicalize -- Canonicalize the path by following every \n" \ " symbolic link in every component of the path.\n" \ " is still logged in, or another user uses the same home directory.\n"\ " -n, --no-newline -- Do not output a trailing newline.\n" \ " -v, --verbose -- Print error messages.\n" \ " --help -- Displays this help text and exits.\n" \ " --version -- Displays the application version and exits.\n" #define READLINK_OPTIONS_STRING "fnvhV" // // Define application options. // // // Set this option to canonicalize the path. // #define READLINK_OPTION_CANONICALIZE 0x00000001 // // Set this option to omit the newline. // #define READLINK_OPTION_NO_NEWLINE 0x00000002 // // Set this option to be verbose. // #define READLINK_OPTION_VERBOSE 0x00000004 // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // // // -------------------------------------------------------------------- Globals // struct option ReadlinkLongOptions[] = { {"canonicalize", no_argument, 0, 'f'}, {"no-newline", no_argument, 0, 'n'}, {"verbose", required_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {NULL, 0, 0, 0}, }; // // ------------------------------------------------------------------ Functions // INT ReadlinkMain ( INT ArgumentCount, CHAR **Arguments ) /*++ Routine Description: This routine is the main entry point for the readlink utility. Arguments: ArgumentCount - Supplies the number of command line arguments the program was invoked with. Arguments - Supplies a tokenized array of command line arguments. Return Value: Returns an integer exit code. 0 for success, nonzero otherwise. --*/ { ULONG ArgumentIndex; PSTR LinkPath; INT Option; ULONG Options; CHAR ResolvedPath[PATH_MAX + 1]; PSTR Result; int Status; Options = 0; // // Process the control arguments. // while (TRUE) { Option = getopt_long(ArgumentCount, Arguments, READLINK_OPTIONS_STRING, ReadlinkLongOptions, NULL); if (Option == -1) { break; } if ((Option == '?') || (Option == ':')) { Status = 1; goto MainEnd; } switch (Option) { case 'f': Options |= READLINK_OPTION_CANONICALIZE; break; case 'n': Options |= READLINK_OPTION_NO_NEWLINE; break; case 'v': Options |= READLINK_OPTION_VERBOSE; break; case 'V': SwPrintVersion(READLINK_VERSION_MAJOR, READLINK_VERSION_MINOR); return 1; case 'h': printf(READLINK_USAGE); return 1; default: assert(FALSE); Status = 1; goto MainEnd; } } ArgumentIndex = optind; if (ArgumentIndex > ArgumentCount) { ArgumentIndex = ArgumentCount; } if (ArgumentIndex >= ArgumentCount) { SwPrintError(0, NULL, "Argument expected. Try --help for usage"); return 1; } LinkPath = Arguments[ArgumentIndex]; ArgumentIndex += 1; if (ArgumentIndex != ArgumentCount) { SwPrintError(0, Arguments[ArgumentIndex], "Unexpected argument"); Status = 1; goto MainEnd; } if ((Options & READLINK_OPTION_CANONICALIZE) != 0) { Result = realpath(LinkPath, ResolvedPath); if (Result == NULL) { Status = 1; if ((Options & READLINK_OPTION_VERBOSE) != 0) { SwPrintError(errno, LinkPath, "Failed to get real path"); } goto MainEnd; } } else { Status = readlink(LinkPath, ResolvedPath, sizeof(ResolvedPath) - 1); if (Status < 0) { Status = 1; if ((Options & READLINK_OPTION_VERBOSE) != 0) { SwPrintError(errno, LinkPath, "Failed to get link target"); } goto MainEnd; } ResolvedPath[Status] = '\0'; } ResolvedPath[sizeof(ResolvedPath) - 1] = '\0'; printf("%s", ResolvedPath); if ((Options & READLINK_OPTION_NO_NEWLINE) == 0) { printf("\n"); } fflush(NULL); Status = 0; MainEnd: return Status; } // // --------------------------------------------------------- Internal Functions //