Look at the stat structure. One crude heurestic is that if the size of the file (st_size) is greater than the number of blocks (st_blocks) multipied by the size of a block, then you have a sparse file.
The GNU core utilities which support the --sparse option use this heuristic. From copy.c:
Code:
#if HAVE_STRUCT_STAT_ST_BLOCKS
/* Use a heuristic to determine whether SRC_NAME contains any sparse
blocks. If the file has fewer blocks than would normally be
needed for a file of its size, then at least one of the blocks in
the file is a hole. */
if (x->sparse_mode == SPARSE_AUTO && S_ISREG (src_open_sb.st_mode)
&& ST_NBLOCKS (src_open_sb) < src_open_sb.st_size / ST_NBLOCKSIZE)
make_holes = true;
#endif