Skip to content

Commit 0116bce

Browse files
Michael Heinsdmoagx
Michael Heins
authored andcommitted
Hex edit for binary columns
Implement the ability to edit binary columns as hex data. Check input string for valid hex values; if invalid input, open alert sheet.
1 parent 43926b0 commit 0116bce

4 files changed

+97
-8
lines changed

Source/SPDataAdditions.h

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ typedef NS_OPTIONS(NSUInteger, SPLineTerminator) {
4343
- (NSData *)dataEncryptedWithKey:(NSData *)aesKey IV:(NSData *)iv;
4444
- (NSData *)dataDecryptedWithPassword:(NSString *)password;
4545
- (NSData *)dataDecryptedWithKey:(NSData *)key;
46+
+ (NSData *)dataWithHexString: (NSString *)hex;
4647

4748
- (NSData *)compress;
4849
- (NSData *)decompress;

Source/SPDataAdditions.m

+68
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,74 @@ - (NSString *)dataToHexString
343343
return hexString;
344344
}
345345

346+
static int hexval( char c)
347+
{
348+
if (c >= '0' && c <= '9')
349+
return c - '0';
350+
if (c >= 'a' && c <= 'f')
351+
return c - 'a' + 10;
352+
if (c >= 'A' && c <= 'F')
353+
return c - 'A' + 10;
354+
return -1;
355+
}
356+
357+
//
358+
// Interpret a string of hex digits in 'hex' as hex data, and return
359+
// an NSData representation of the data. Spaces are permitted within
360+
// the string and an initial '0x' or '0X' will be ignored. If bad input
361+
// is detected, nil is returned.
362+
//
363+
+ (NSData *)dataWithHexString: (NSString *)hex
364+
{
365+
int n = (int)(hex.length + 1);
366+
if (n <= 1)
367+
return nil; // no string or empty string
368+
char c, *str = (char *)malloc( n), *d = str, *e;
369+
const char *s = hex.UTF8String;
370+
//
371+
// Copy input while removing spaces and tabs.
372+
//
373+
do {
374+
c = *s++;
375+
if (c != ' ' && c != '\t')
376+
*d++ = c;
377+
} while (c);
378+
d = str;
379+
if (d[0] == '0' && (d[1] == 'x' || d[1] == 'X')) {
380+
d += 2; // bypass initial 0x or 0X
381+
}
382+
//
383+
// Check for non-hex characters
384+
//
385+
for (e = d; (c = *e); e++) {
386+
if (hexval( c) < 0) {
387+
break;
388+
}
389+
}
390+
n = (int)(e - d); // n = # of hex digits
391+
if (*e) {
392+
//
393+
// Bad hex char at e. Return empty data. Alternative would be to
394+
// convert data up to bad point.
395+
//
396+
free( str);
397+
return nil;
398+
}
399+
int nbytes = (n % 2) ? (n + 1) / 2 : n / 2;
400+
unsigned char *bytes = malloc( nbytes), *b = bytes;
401+
if (n % 2) {
402+
*b++ = hexval( *d++);
403+
}
404+
while (d < e) {
405+
unsigned char v = (hexval( d[0]) << 4) + hexval( d[1]);
406+
*b++ = v;
407+
d += 2;
408+
}
409+
NSData *data = [NSData dataWithBytesNoCopy: bytes length: nbytes freeWhenDone: YES];
410+
free( str);
411+
return data;
412+
}
413+
346414
/**
347415
* Returns the hex representation of the given data.
348416
*/

Source/SPTableContentDataSource.m

+28-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#import "SPDataStorage.h"
3434
#import "SPCopyTable.h"
3535
#import "SPTablesList.h"
36+
#import "SPAlertSheets.h"
3637

3738
#import <pthread.h>
3839
#import <SPMySQL/SPMySQL.h>
@@ -177,6 +178,11 @@ - (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableCol
177178
return;
178179
}
179180

181+
NSInteger columnIndex = [[tableColumn identifier] integerValue];
182+
NSDictionary *columnDefinition = [[(id <SPDatabaseContentViewDelegate>)[tableContentView delegate] dataColumnDefinitions] objectAtIndex:columnIndex];
183+
184+
NSString *columnType = [columnDefinition objectForKey:@"typegrouping"];
185+
180186
// Catch editing events in the row and if the row isn't currently being edited,
181187
// start an edit. This allows edits including enum changes to save correctly.
182188
if (isEditingRow && [tableContentView selectedRow] != currentlyEditingRow) {
@@ -192,7 +198,28 @@ - (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableCol
192198

193199
NSDictionary *column = NSArrayObjectAtIndex(dataColumns, [[tableColumn identifier] integerValue]);
194200

195-
if (object) {
201+
if ([columnType isEqualToString:@"binary"] && [object isKindOfClass: [NSString class]]) {
202+
//
203+
// This is a binary object being edited as a hex string. (Is there a better
204+
// way to detect this case?)
205+
// Convert the string back to binary, checking for errors.
206+
//
207+
NSData *data = [NSData dataWithHexString: object];
208+
if (data) {
209+
object = data;
210+
[tableValues replaceObjectInRow:rowIndex column:[[tableColumn identifier] integerValue] withObject:object];
211+
}
212+
else {
213+
SPOnewayAlertSheet(
214+
NSLocalizedString(@"Error", @"error"),
215+
[tableDocumentInstance parentWindow],
216+
NSLocalizedString(@"Bad hexadecimal data input.", @"Bad hexadecimal data input.")
217+
);
218+
return;
219+
220+
}
221+
}
222+
else if (object) {
196223
// Restore NULLs if necessary
197224
if ([object isEqualToString:[prefs objectForKey:SPNullValue]] && [[column objectForKey:@"null"] boolValue]) {
198225
object = [NSNull null];

Source/SPTableContentDelegate.m

-7
Original file line numberDiff line numberDiff line change
@@ -273,13 +273,6 @@ - (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn
273273
// Retrieve the column definition
274274
NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[tableColumn identifier] integerValue]];
275275

276-
// TODO: Fix editing of "Display as Hex" columns and remove this (also see above)
277-
if ([self cellValueIsDisplayedAsHexForColumn:[[tableColumn identifier] integerValue]]) {
278-
NSBeep();
279-
[SPTooltip showWithObject:NSLocalizedString(@"Disable \"Display Binary Data as Hex\" in the View menu to edit this field.",@"Temporary : Tooltip shown when trying to edit a binary field in table content view while it is displayed using HEX conversion")];
280-
return NO;
281-
}
282-
283276
// Open the editing sheet if required
284277
if ([tableContentView shouldUseFieldEditorForRow:rowIndex column:[[tableColumn identifier] integerValue] checkWithLock:NULL]) {
285278

0 commit comments

Comments
 (0)