#include "SCK.H"

int ro = 0;
int y = 0;
int force = 0;
int modified = 0;
int rebuild_apages = 0;
int reset_cc = 0;
int dont_store_cross_links = 0;
char *swapfile = NULL;
unsigned long memory_limit = 0;
char *logfile = NULL;
int async_writes = 0;
__s64 reserve = -1;
#ifdef TESTCODE
int recover_all_files = 0;
int recover_all_directories = 0;
int fragment_recovered_files = 0;
int move_recovered_files = 0;
int dont_sort_recovered_files = 0;
int make_apage_bitmaps = 0;
int always_swap = 0;
int nodirect = 0;
int nocache = 0;
int debug_malloc = 0;
#endif
char *dev = NULL;
int h;
struct stat st;

#ifdef __SPAD__
static __const__ struct __param_table params[] = {
	"", __PARAM_STRING, 1, __MAX_STR_LEN, &dev,
	"RO", __PARAM_BOOL, ~0, 1, &ro,
	"Y", __PARAM_BOOL, ~0, 1, &y,
	"F", __PARAM_BOOL, ~0, 1, &force,
	"REBUILD_APAGES", __PARAM_BOOL, ~0, 1, &rebuild_apages,
	"RESET_CRASH_COUNTS", __PARAM_BOOL, ~0, 1, &reset_cc,
	"DONT_STORE_CROSS_LINKS", __PARAM_BOOL, ~0, 1, &dont_store_cross_links,
	"SWAPFILE", __PARAM_STRING, 1, __MAX_STR_LEN, &swapfile,
	"MEMORY", __PARAM_UNSIGNED_LONG, BUFFER_SIZE, MAXULONG - BUFFER_SIZE, &memory_limit,
	"LOG", __PARAM_STRING, 1, __MAX_STR_LEN, &logfile,
	"ASYNC_WRITES", __PARAM_BOOL, ~0, 1, &async_writes,
	"SET_RESERVE", __PARAM_INT64, 0, 1, &reserve,
#ifdef TESTCODE
	"RECOVER_ALL_FILES", __PARAM_BOOL, ~0, 1, &recover_all_files,
	"RECOVER_ALL_DIRECTORIES", __PARAM_BOOL, ~0, 1, &recover_all_directories,
	"FRAGMENT_RECOVERED_FILES", __PARAM_BOOL, ~0, 1, &fragment_recovered_files,
	"MOVE_RECOVERED_FILES", __PARAM_BOOL, ~0, 1, &move_recovered_files,
	"DONT_SORT_RECOVERED_FILES", __PARAM_BOOL, ~0, 1, &dont_sort_recovered_files,
	"MAKE_APAGE_BITMAPS", __PARAM_BOOL, ~0, 1, &rebuild_apages,
	"MAKE_APAGE_BITMAPS", __PARAM_BOOL, ~0, 1, &make_apage_bitmaps,
	"ALWAYS_SWAP", __PARAM_BOOL, ~0, 1, &always_swap,
	"NODIRECT", __PARAM_BOOL, ~0, 1, &nodirect,
	"NOCACHE", __PARAM_BOOL, ~0, 1, &nocache,
	"DEBUG_MALLOC", __PARAM_BOOL, ~0, 1, &debug_malloc,
#endif
	NULL, 0, 0, 0, NULL,
};
#endif

#ifndef __SPAD__
static int xmain(int argc, char *argv[]);
int main(int argc, char *argv[])
{
	int r = xmain(argc, argv);
	if (__unlikely(r < 0)) {
		if (__likely(*KERNEL$ERROR_MSG())) fprintf(stderr, "%s\n", KERNEL$ERROR_MSG());
		r = 3;
	}
	return r;
}
static int xmain(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
	int r;
#ifdef __SPAD__
	char **arg = argv;
	int state = 0;
	if (__unlikely(__parse_params(&arg, &state, params, NULL, NULL, NULL)) || __unlikely(!dev)) {
		_snprintf(KERNEL$ERROR_MSG(), __MAX_STR_LEN, "SPADFSCK: SYNTAX ERROR");
		return -EBADSYN;
	}
#else
	int noopt = 0, i;
	if (0) {
		badsyn:
		_snprintf(KERNEL$ERROR_MSG(), __MAX_STR_LEN, "SPADFSCK: SYNTAX ERROR");
		return -EBADSYN;
	}
	for (i = 1; i < argc; i++) {
		/* there used to be getopt, but it sux. On each system in a different way. */
		if (noopt) goto process_dev;
		if (argv[i][0] == '-' && argv[i][1] != '-' && argv[i][1]) {
			long j;
			for (j = 1; argv[i][j]; j++) {
				char c = argv[i][j];
				if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
				switch (c) {
					case 'a':
					case 'p':
					case 'y':
						y = 1;
						break;
					case 'n':
						ro = 1;
						break;
					case 'r':
						break;
					case 'f':
						force = 1;
						break;
					default:
						goto badsyn;
				}
			}
		} else if (!_strcasecmp(argv[i], "--")) noopt = 1;
		else if (!_strcasecmp(argv[i], "--rebuild-apages")) rebuild_apages = 1;
		else if (!_strcasecmp(argv[i], "--reset-crash-counts")) reset_cc = 1;
		else if (!_strcasecmp(argv[i], "--dont-store-cross-links")) dont_store_cross_links = 1;
		else if (!_strcasecmp(argv[i], "--swapfile") && i + 1 < argc) swapfile = argv[++i];
		else if (!_strcasecmp(argv[i], "--memory") && i + 1 < argc) {
			char *ep;
			memory_limit = strtoul(argv[++i], &ep, 10);
			if (*ep || !argv[i][0]) goto badsyn;
			if (memory_limit < 1 << 20 && memory_limit << 20 >> 20 == memory_limit) memory_limit <<= 20;
			if (memory_limit < BUFFER_SIZE || memory_limit >= MAXULONG - BUFFER_SIZE) goto badsyn;
		}
		else if (!_strcasecmp(argv[i], "--log") && i + 1 < argc) logfile = argv[++i];
		else if (!_strcasecmp(argv[i], "--async-writes")) async_writes = 1;
		else if (!_strcasecmp(argv[i], "--set-reserve") && i + 1 < argc) {
			char *ep;
#if !defined(__linux__) || defined(__GLIBC__)
			reserve = strtoll(argv[++i], &ep, 10);
#else
			reserve = strtod(argv[++i], &ep);
#endif
			if (*ep || !argv[i][0]) goto badsyn;
			if (reserve < 0) goto badsyn;
		}
#ifdef TESTCODE
		else if (!_strcasecmp(argv[i], "--recover-all-files")) recover_all_files = 1;
		else if (!_strcasecmp(argv[i], "--recover-all-directories")) recover_all_directories = 1;
		else if (!_strcasecmp(argv[i], "--fragment-recovered-files")) fragment_recovered_files = 1;
		else if (!_strcasecmp(argv[i], "--move-recovered-files")) move_recovered_files = 1;
		else if (!_strcasecmp(argv[i], "--dont-sort-recovered-files")) dont_sort_recovered_files = 1;
		else if (!_strcasecmp(argv[i], "--always-swap")) always_swap = 1;
		/*else if (!_strcasecmp(argv[i], "--nodirect")) nodirect = 1;*/
		else if (!_strcasecmp(argv[i], "--nocache")) nocache = 1;
		else if (!_strcasecmp(argv[i], "--debug-malloc")) debug_malloc = 1;
		else if (argv[i][0] != '-') {
			process_dev:
			if (dev) goto badsyn;
			dev = argv[i];
		} else goto badsyn;
#endif
	}
	if (!dev) goto badsyn;
#endif
	if (ro && __unlikely(rebuild_apages | reset_cc | y)) {
#ifdef __SPAD__
		_snprintf(KERNEL$ERROR_MSG(), __MAX_STR_LEN, "SPADFSCK: YOU CAN'T USE BOTH /RO AND /REBUILD_APAGES, /RESET_CRASH_COUNTS OR /Y");
#else
		_snprintf(KERNEL$ERROR_MSG(), __MAX_STR_LEN, "SPADFSCK: YOU CAN'T USE BOTH -N AND --REBUILD-APAGES, --RESET-CRASH-COUNTS OR -Y");
#endif
		return -EBADSYN;
	}
	if (!memory_limit) {
		__u64 ml = KERNEL$GET_MEMORY_SIZE(VM_TYPE_WIRED_MAPPED) / 2;
		if (ml > MAXULONG - BUFFER_SIZE) ml = MAXULONG - BUFFER_SIZE;
		memory_limit = ml;
		if (memory_limit < BUFFER_SIZE * 2) memory_limit = BUFFER_SIZE * 2;
	}
	h = open(dev, (!ro ? O_RDWR : O_RDONLY) |
#ifndef __SPAD__
	0
#else
#ifndef TESTCODE
	O_DIRECT
#else
	(nodirect ? 0 : O_DIRECT)
#endif
#endif
	);
	if (__unlikely(h == -1)) {
		h = open(dev, (!ro ? O_RDWR : O_RDONLY));
	}
	if (__unlikely(h == -1)) {
		r = -errno;
		_snprintf(KERNEL$ERROR_MSG(), __MAX_STR_LEN, "SPADFSCK: CAN'T OPEN %s: %s", dev, strerror(-r));
		goto realret;
	}
	if (__unlikely(fstat(h, &st))) {
		r = -errno;
		_snprintf(KERNEL$ERROR_MSG(), __MAX_STR_LEN, "SPADFSCK: CAN'T FSTAT %s: %s", dev, strerror(-r));
		close(h);
		goto realret;
	}
	if (__unlikely(init_log())) {
		r = -errno;
		_snprintf(KERNEL$ERROR_MSG(), __MAX_STR_LEN, "SPADFSCK: CAN'T OPEN LOG FILE %s: %s", logfile, strerror(-r));
		close(h);
		goto realret;
	}
	check_again:
	init_buffers();
	init_alloc();
	init_super();
	init_apages();
	init_cross_links();
	init_recovery();
	if (__unlikely(r = check_super())) goto ret;
	if (!force && !tx.txflags) {
		status_printf(0, "filesystem is clean, no checking needed.");
		goto ret0;
	}
	if (__unlikely(r = check_cct())) goto ret;
	if (__unlikely(r = check_apages())) goto ret;
	if (__unlikely(r = check_dir_tree())) goto ret;
	if (__unlikely(r = fix_cct())) goto ret;
	if (__unlikely(r = fix_txblock())) goto ret;
	if (__unlikely(r = fix_apages())) goto ret;
	if (__unlikely(r = fix_errorflag())) goto ret;
	status_printf(0, "done.");

	ret0:
	r = 0;
	ret:
	done_recovery();
	done_cross_links();
	done_apages();
	done_super();
	done_alloc();
	done_buffers();
	if (!r && __unlikely(alloc_over_blocks)) {
		log_printf(0, "ALLOCATED OVER EXISTING DATA - CHECKING AGAIN");
		goto check_again;
	}
	if (__unlikely(modified) && __unlikely(fsync(h))) {
		log_printf(1, "ERROR FLUSHING CACHE: %s", strerror(errno));
		if (!r) r = 1;
	}
	if (__unlikely(close(h))) {
		log_printf(1, "ERROR CLOSING DEVICE: %s", strerror(errno));
		if (!r) r = 1;
	}
	if (__unlikely(done_log())) {
		if (r < 2) r = 2;
	}
	realret:
	done_filename();
	mem_done();
	return r;
}
