blob: 6d387c3142bd07f622e0d5f26a13019f1aaa6e81 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013 SGI
* All Rights Reserved.
*/
#include "command.h"
#include "input.h"
#include <sys/types.h>
#include <unistd.h>
#include "init.h"
#include "io.h"
static cmdinfo_t seek_cmd;
static void
seek_help(void)
{
printf(_(
"\n"
" returns the next hole and/or data offset at or after the requested offset\n"
"\n"
" Example:\n"
" 'seek -d 512' - offset of data at or following offset 512\n"
" 'seek -a -r 0' - offsets of all data and hole in entire file\n"
"\n"
" Returns the offset of the next data and/or hole. There is an implied hole\n"
" at the end of file. If the specified offset is past end of file, or there\n"
" is no data past the specified offset, EOF is returned.\n"
" -a -- return the next data and hole starting at the specified offset.\n"
" -d -- return the next data starting at the specified offset.\n"
" -h -- return the next hole starting at the specified offset.\n"
" -r -- return all remaining type(s) starting at the specified offset.\n"
" -s -- also print the starting offset.\n"
"\n"));
}
/* values for flag variable */
#define SEEK_DFLAG (1 << 0)
#define SEEK_HFLAG (1 << 1)
#define SEEK_RFLAG (1 << 2)
/* indexes into the seekinfo array */
#define DATA 0
#define HOLE 1
static struct seekinfo {
char *name; /* display item name */
int seektype; /* data or hole */
int mask; /* compared for print and looping */
} seekinfo[] = {
{"DATA", SEEK_DATA, SEEK_DFLAG},
{"HOLE", SEEK_HOLE, SEEK_HFLAG}
};
/* print item type and offset. catch special cases of eof and error */
static void
seek_output(
int startflag,
char *type,
off_t start,
off_t offset)
{
if (offset == -1) {
if (errno == ENXIO) {
if (startflag)
printf("%s %lld EOF\n", type,
(long long)start);
else
printf("%s EOF\n", type);
} else {
printf("ERR %lld ", (long long)start);
fflush(stdout); /* so the printf preceded the perror */
perror("");
}
} else {
if (startflag)
printf("%s %lld %lld\n", type,
(long long)start, (long long)offset);
else
printf("%s %lld\n", type, (long long)offset);
}
}
static int
seek_f(
int argc,
char **argv)
{
off_t offset, start;
size_t fsblocksize, fssectsize;
int c;
int current; /* specify data or hole */
int flag;
int startflag;
flag = startflag = 0;
init_cvtnum(&fsblocksize, &fssectsize);
while ((c = getopt(argc, argv, "adhrs")) != EOF) {
switch (c) {
case 'a':
flag |= (SEEK_HFLAG | SEEK_DFLAG);
break;
case 'd':
flag |= SEEK_DFLAG;
break;
case 'h':
flag |= SEEK_HFLAG;
break;
case 'r':
flag |= SEEK_RFLAG;
break;
case 's':
startflag = 1;
break;
default:
exitcode = 1;
return command_usage(&seek_cmd);
}
}
if (!(flag & (SEEK_DFLAG | SEEK_HFLAG)) || optind != argc - 1) {
exitcode = 1;
return command_usage(&seek_cmd);
}
start = offset = cvtnum(fsblocksize, fssectsize, argv[optind]);
if (offset < 0) {
exitcode = 1;
return command_usage(&seek_cmd);
}
/*
* check to see if the offset is a data or hole entry and
* decide if we want to display that type of entry.
*/
if (flag & SEEK_HFLAG) {
current = HOLE;
offset = lseek(file->fd, start, SEEK_HOLE);
if (offset != -1 && offset < start)
goto bad_result;
if ((start == offset) || !(flag & SEEK_DFLAG)) {
/*
* this offset is a hole or are only displaying holes.
* if this offset is for data and we are displaying
* data, then we will fall through below to
* initialize the data search.
*/
goto found_hole;
}
}
/* The offset is not a hole, or we are looking just for data */
current = DATA;
offset = lseek(file->fd, start, SEEK_DATA);
if (offset != -1 && offset < start)
goto bad_result;
found_hole:
/*
* At this point we know which type and the offset of the starting
* item. "current" alternates between data / hole entries in
* assending order - this alternation is needed even if only one
* type is to be displayed.
*
* An error or EOF will terminate the display, otherwise "flag"
* determines if there are more items to be displayed.
*/
if (startflag)
printf("Whence Start Result\n");
else
printf("Whence Result\n");
for (c = 0; flag; c++) {
if (offset == -1) {
/* print error or eof if the only entry */
if (errno != ENXIO || c == 0 ) {
seek_output(startflag, seekinfo[current].name,
start, offset);
}
return 0; /* stop on error or EOF */
}
if (flag & seekinfo[current].mask)
seek_output(startflag, seekinfo[current].name, start,
offset);
/*
* When displaying only a single data and/or hole item, mask
* off the item as it is displayed. The loop will end when all
* requested items have been displayed.
*/
if (!(flag & SEEK_RFLAG))
flag &= ~seekinfo[current].mask;
current ^= 1; /* alternate between data and hole */
start = offset;
offset = lseek(file->fd, start, seekinfo[current].seektype);
if (offset != -1 && offset <= start)
goto bad_result;
}
return 0;
bad_result:
fprintf(stderr, "Invalid seek result: lseek(<fd>, %lld, SEEK_%s) = %lld\n",
(long long)start, seekinfo[current].name, (long long)offset);
return 0;
}
void
seek_init(void)
{
seek_cmd.name = "seek";
seek_cmd.cfunc = seek_f;
seek_cmd.argmin = 2;
seek_cmd.argmax = 5;
seek_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
seek_cmd.args = _("-a | -d | -h [-r] off");
seek_cmd.oneline = _("locate the next data and/or hole");
seek_cmd.help = seek_help;
add_command(&seek_cmd);
}