Index: .gitignore ================================================================== --- .gitignore +++ .gitignore @@ -38,7 +38,9 @@ tests/tests.nds tests/EBOOT.PBP tests/PARAM.SFO tests/objc_sync/objc_sync utils/objfw-config +utils/ofhash/ofhash +utils/ofhash/ofhash.exe utils/ofzip/ofzip utils/ofzip/ofzip.exe Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -691,10 +691,11 @@ AC_ARG_ENABLE(files, AS_HELP_STRING([--disable-files], [disable file support])) AS_IF([test x"$enable_files" != x"no"], [ AC_DEFINE(OF_HAVE_FILES, 1, [Whether we have files]) AC_SUBST(USE_SRCS_FILES, '${SRCS_FILES}') + AC_SUBST(OFHASH, "ofhash") AC_SUBST(OFZIP, "ofzip") AC_CHECK_FUNCS(readdir_r) ]) Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -28,10 +28,11 @@ LOOKUP_ASM_LIB_A = @LOOKUP_ASM_LIB_A@ LOOKUP_ASM_LOOKUP_ASM_A = @LOOKUP_ASM_LOOKUP_ASM_A@ LOOKUP_ASM_LOOKUP_ASM_LIB_A = @LOOKUP_ASM_LOOKUP_ASM_LIB_A@ MAP_LDFLAGS = @MAP_LDFLAGS@ OFBLOCKTESTS_M = @OFBLOCKTESTS_M@ +OFHASH = @OFHASH@ OFHTTPCLIENTTESTS_M = @OFHTTPCLIENTTESTS_M@ OFPROCESS_M = @OFPROCESS_M@ OFKERNELEVENTOBSERVER_KQUEUE_M = @OFKERNELEVENTOBSERVER_KQUEUE_M@ OFKERNELEVENTOBSERVER_POLL_M = @OFKERNELEVENTOBSERVER_POLL_M@ OFKERNELEVENTOBSERVER_SELECT_M = @OFKERNELEVENTOBSERVER_SELECT_M@ Index: utils/Makefile ================================================================== --- utils/Makefile +++ utils/Makefile @@ -1,8 +1,9 @@ include ../extra.mk -SUBDIRS += ${OFZIP} +SUBDIRS += ${OFHASH} \ + ${OFZIP} include ../buildsys.mk DISTCLEAN = objfw-config ADDED utils/ofhash/Makefile Index: utils/ofhash/Makefile ================================================================== --- utils/ofhash/Makefile +++ utils/ofhash/Makefile @@ -0,0 +1,13 @@ +include ../../extra.mk + +PROG = ofhash${PROG_SUFFIX} +SRCS = OFHash.m + +include ../../buildsys.mk + +${PROG}: ${LIBOBJFW_DEP_LVL2} + +CPPFLAGS += -I../../src -I../../src/runtime -I../../src/exceptions -I../.. +LIBS := -L../../src -lobjfw ${LIBS} +LD = ${OBJC} +LDFLAGS += ${LDFLAGS_RPATH} ADDED utils/ofhash/OFHash.m Index: utils/ofhash/OFHash.m ================================================================== --- utils/ofhash/OFHash.m +++ utils/ofhash/OFHash.m @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFApplication.h" +#import "OFArray.h" +#import "OFFile.h" +#import "OFMD5Hash.h" +#import "OFRIPEMD160Hash.h" +#import "OFSHA1Hash.h" +#import "OFSHA224Hash.h" +#import "OFSHA256Hash.h" +#import "OFSHA384Hash.h" +#import "OFSHA512Hash.h" +#import "OFStdIOStream.h" + +#import "OFOpenFileFailedException.h" +#import "OFReadFailedException.h" + +@interface OFHash: OFObject +@end + +OF_APPLICATION_DELEGATE(OFHash) + +static void +help(void) +{ + [of_stderr writeFormat: + @"Usage: %@ [md5|rmd160|sha1|sha224|sha256|sha384|sha512] " + @"file1 [file2 ...]\n", + [OFApplication programName]]; + + [OFApplication terminateWithStatus: 1]; +} + +static Class +hashClassForName(OFString *name) +{ + if ([name isEqual: @"md5"]) + return [OFMD5Hash class]; + else if ([name isEqual: @"rmd160"] || [name isEqual: @"ripemd160"]) + return [OFRIPEMD160Hash class]; + else if ([name isEqual: @"sha1"]) + return [OFSHA1Hash class]; + else if ([name isEqual: @"sha224"]) + return [OFSHA224Hash class]; + else if ([name isEqual: @"sha256"]) + return [OFSHA256Hash class]; + else if ([name isEqual: @"sha384"]) + return [OFSHA384Hash class]; + else if ([name isEqual: @"sha512"]) + return [OFSHA512Hash class]; + + return nil; +} + +@implementation OFHash +- (void)applicationDidFinishLaunching +{ + OFArray *arguments = [OFApplication arguments]; + Class hashClass; + OFEnumerator *enumerator; + OFString *path; + int exitStatus = 0; + + if ([arguments count] < 2) + help(); + + if ((hashClass = hashClassForName([arguments objectAtIndex: 0])) == Nil) + help(); + + enumerator = [[arguments objectsInRange: + of_range(1, [arguments count] - 1)] objectEnumerator]; + while ((path = [enumerator nextObject]) != nil) { + void *pool = objc_autoreleasePoolPush(); + id hash = [hashClass hash]; + OFStream *file; + const uint8_t *digest; + size_t i, digestSize; + + if ([path isEqual: @"-"]) + file = of_stdin; + else { + @try { + file = [OFFile fileWithPath: path + mode: @"rb"]; + } @catch (OFOpenFileFailedException *e) { + [of_stderr writeFormat: + @"Failed to open file %@: %s\n", + [e path], strerror([e errNo])]; + + exitStatus = 1; + goto outer_loop_end; + } + } + + while (![file isAtEndOfStream]) { + uint8_t buffer[1024]; + size_t length; + + @try { + length = [file readIntoBuffer: buffer + length: 1024]; + } @catch (OFReadFailedException *e) { + [of_stderr writeFormat: + @"Failed to read %@: %s\n", + path, strerror([e errNo])]; + + exitStatus = 1; + goto outer_loop_end; + } + + [hash updateWithBuffer: buffer + length: length]; + } + [file close]; + + digest = [hash digest]; + digestSize = [hashClass digestSize]; + for (i = 0; i < digestSize; i++) + [of_stdout writeFormat: @"%02x", digest[i]]; + + [of_stdout writeFormat: @" %@\n", path]; + +outer_loop_end: + objc_autoreleasePoolPop(pool); + } + + [OFApplication terminateWithStatus: exitStatus]; +} +@end