Category: Programming


This is something I knocked up a while ago just to keep an eye on a directory structure for permissions changes, excluding upload directories and other things:


#!/bin/bash

ignore[0]="./public_html/blog/wp-content/uploads"
ignore[1]="./bb-attachments"

cd 

unset unsecure i
i=0
while IFS= read -r -d $'\0' file; do
    unsecure[i++]="$file"        # or however you want to process each file
done < <(find . -perm /go+w -print0 2>/dev/null)

for dodgy in ${unsecure[@]}; do
	guilty=1
	for entry in ${ignore[@]}; do
		if [[ "$dodgy" =~ $entry ]]; then
			unset guilty
		fi
	done
	if [ $guilty ]; then
		echo "Unknown unsecure directory : $dodgy"
	fi
done

Application Accepted!

Well that was quite smooth, the application is now in the App Store Right Here!. In other news it appears I’d completely forgotten in my rush to get the site live to include LV partitions for the site and it’s backup. Cue me completely forgetting the LVM commands and having to have a quick refresh on tldp.org. Oh how things have changed so fast! A few weeks ago I stumbled briefly with grant privs in MySQL!

Application Submitted!

Well actually it was submitted last Friday, but with the announcement of the iPad 2 and everything else I’m not expecting it to go live this week. Suffice to say I’ll be posting a store link or page link here when it’s all gone through.

DynDNS.org + Bash + Router == A tiny bit safer.

Pondering how to lock down a few bits to just an IP or two I hacked together this awful bit of bash:

#!/bin/bash
function valid_ip()
{
    local  ip=$1
    local  stat=1

    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        OIFS=$IFS
        IFS='.'
        ip=($ip)
        IFS=$OIFS
        [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
            && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
        stat=$?
    fi
    return $stat
}

HOME_IP=`host dyndnsname.dyndns-home.com|awk '{print $4};'`

if valid_ip $HOME_IP; then
	OLD_IP=`grep HOME_IP /home/user/public_html/blog/wp-admin/.htaccess|awk '{print $3}'`;
	if [ $OLD_IP != $HOME_IP ]; then
		sed -i -e "s/ [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\} #HOME_IP/ $HOME_IP #HOME_IP/" \
			 /home/user/public_html/blog/wp-admin/.htaccess
		echo "DNS Changed to $HOME_IP from $OLD_IP"
	fi
fi

exit

So if you have a locked down .htaccess something like:

Satisfy any
order deny,allow
allow from 12.12.12.12 #HOME_IP
deny from all

The above script will update it. Bung it in cron and hey presto. Most routers have options to update dyndns and other services to ensure your IP is current.

Managed to implement rendering of recipes into multipage PDF documents. They look something like this:

Lasagne Recipe

One thing to note whilst writing the code was the usual inverted co-ordinates. I wanted an image in the top left, title and summary in the top right. After some internal debate I settled on a two column format for the rest. The image is easy, just use the UIImage drawInRect message once you’ve started your PDF context. As for the text the origin of your text box is the bottom left due to the inverted co-ordinates, so it is important to remember when calculating offsets that you need to set the offset.x and offset.y options to the margins of your page and adjust the size.height & size.width settings of the rect to ensure the text renders at the appropriate point. If you need to calculate the size of the rect the text occupies before rendering (not to adjust the rect for rendering but to calculate the location of the next rect) use CTFramesetterSuggestFrameSizeWithConstraints. For example when rendering the summary in the top right I use that function so I can decide whether the image or the summary is the larger and can therefore decide which value to use when calculating the available height for the columns. The following PDF has some debug code turned on that shows a red outline for the rect used to render the text and a small circle at the origin point of the rect. As you can see although the summary text is small the rect was the full height of the page.

Lasagne Recipe with Guidelines

As for rendering the core text itself I settled on a function like this:


+ (CFRange)renderTextRange:(CFRange)currentRange andFrameSetter:(CTFramesetterRef)frameSetter intoRect:(CGRect)frameRect {
	CGMutablePathRef framePath = CGPathCreateMutable();
	CGPathAddRect(framePath, NULL, frameRect);
	CTFrameRef frameRef = CTFramesetterCreateFrame(frameSetter, currentRange, framePath, NULL);
	CGContextRef currentContext = UIGraphicsGetCurrentContext();
	CGContextSaveGState(currentContext);
	CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
	CGContextTranslateCTM(currentContext, 0, 792);
	CGContextScaleCTM(currentContext, 1.0, -1.0);
	CTFrameDraw(frameRef, currentContext);
	/*
	CGContextSetStrokeColorWithColor(currentContext, [[UIColor redColor] CGColor]);
	CGContextAddRect(currentContext, frameRect);
	CGContextStrokePath(currentContext);
	CGContextAddEllipseInRect(currentContext, CGRectMake(frameRect.origin.x-5, frameRect.origin.y-5, 10, 10));
	CGContextStrokePath(currentContext);
	 */
	CGContextRestoreGState(currentContext);
	CGPathRelease(framePath);
	currentRange = CTFrameGetVisibleStringRange(frameRef);
	currentRange.location += currentRange.length;
	currentRange.length = 0;
	CFRelease(frameRef);
	return currentRange;
}

The debug code is the commented out section. The Save/Restore CGState calls are for my own sanity as is the CGContextSetTextMatrix so I can ensure that drawing operations outside of this routine do not affect the drawing process. Although if you remove them keep in mind that the CGContextXXXCTM calls will need undoing somehow or each time the routine is called you’ll apply new and interesting transformations to your text.

If anyone wants me to post a full example block of code comment and I’ll update or make a new post.

As the project nears release my thoughts turn to what will be needed in the near future and how best to solve an intermittent bug that’s present now. The bug was resolving by doing something I should have done ages ago, be consistent with how I use data in table views. When developing the app the most complicated view did not use NSFetchedResultsController as it required 2 of them, one to drive each section.
Implementing this bought another problem to the fore, how to identify recipes uniquely in the DB? The obvious method is to use the objectID that core data uses but this changes until the recipe is finalised by saving it. The problem there is driving the sections from NSFetchResults whilst the recipe is being created. Each addition can change the objectID invalidating a simple predicate such as:

[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"recipe== %@",[recipe objectID]]];

The simplest solution is to create the recipe and immediately save it, blank, to the database then present it to the view. I have issues with that, crashes could lead to blank recipes polluting the integrity of the database and all sorts of problems. My minimum requirement is for the recipe to have a title (non unique) at least. So the solution I’ve known was needed has been implemented, UUID’s. For ease of use you want something that:

  • Adds UUIDs as part of the core db migration when the new managed object model is loaded.
  • Automatically creates and adds a UUID to a newly inserted recipe.

To help with this I created a helper class with the function:

+ (NSString*)UUIDString {
    CFUUIDRef theUUID = CFUUIDCreate(NULL);
    CFStringRef string = CFUUIDCreateString(NULL, theUUID);
    CFRelease(theUUID);
    return [NSMakeCollectable(string) autorelease];
}

To ensure the UUID is added to the core data object every time a recipe is created you can add this to the NSManagedObject subclass:

- (void)awakeFromInsert {
	[super awakeFromInsert];
	[self setValue:[KookaDIS UUIDString] forKey:@"uuid"];
}

Now for the method to update the database upon launch. Assuming you have automatic migrations turned on with the NSMigratePersistentStoresAutomaticallyOption option you need to create a NSEntityMigration subclass. I didn’t choose the best name for mine but it looks like this:

#import 

@interface AddUUIDToRecipeEMP : NSEntityMigrationPolicy {

}

@end
#import "AddUUIDToRecipeEMP.h"
#import "KookaDIS.h"

@implementation AddUUIDToRecipeEMP

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject*)src
									  entityMapping:(NSEntityMapping*)map
											manager:(NSMigrationManager*)mgr
											  error:(NSError**)error
{
	NSManagedObjectContext *destMOC = [mgr destinationContext];
	NSString *destEntityName = [map destinationEntityName];

	NSManagedObject *dest = [NSEntityDescription insertNewObjectForEntityForName:destEntityName
														  inManagedObjectContext:destMOC];

	NSArray *keys = [[[src entity] attributesByName] allKeys];
	NSDictionary *values = [src dictionaryWithValuesForKeys:keys];

	[dest setValuesForKeysWithDictionary:values];
	[dest setValue:[KookaDIS UUIDString] forKey:@"uuid"];
	// 511 is binary 111 111 111 or chmod 777.
	[dest setValue:[NSNumber numberWithInt:511] forKey:@"permissions"];

	[mgr associateSourceInstance:src withDestinationInstance:dest forEntityMapping:map];

	return YES;
}

@end

Ignore the permissions setValue for now, that’s for when we’ve covered the new UUID updates. So we now have a custom entity migration class. To use it create a new file in your project choosing “iOS->Resource->Mapping Model”. Within the mapping model click on the pre-made entity mapping that is relevant for you. In the far right hand pain you should see a Custom Policy field, simply type the name of the class you made in there. That’s it. Afaict this turns off any other automatic migration for that record so if you have other alterations you will need to code those changes manually too.

The above changes mean that the predicate for getting the data for the sections is consistent whether the recipe is new or not. It becomes:

[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"recipe.uuid == %@",recipe.uuid]];

The other thing I have considered is the potential inclusion of other recipes with permission and permissions for user uploads to the server. So I implemented a quick bitwise permissions system based on linux simple user/group/world permissions for read/write/execute. I’ve modified it to creater/owner/world. Owner would be someone with explicit permission or (heaven forbid) in app purchase and world would be any other download/import.

I felt that storing the data as an int is the simplest solution but to present that to the rest of the program in such a way would be anachronistic in such a modern language so I needed two routines to convert the info from an int to an NSDictionary and vice versa. Thus these two routines were born and added to the afore mentioned class:

NSString * const kKDISShareEnabled = @"KookaDocumentShareable";
NSString * const kKDISEditEnabled = @"KookaDocumentEditable";
NSString * const kKDISPrintEnabled = @"KookaDocumentPrintable";

NSString * const kKDISCreator = @"KookaDocumentCreator";
NSString * const kKDISOwner = @"KookaDocumentOwner";
NSString * const kKDISWorld = @"KookaDocumentWorld";

+ (NSDictionary *)permissionsDictionaryFromNumber:(NSNumber *)permissions {
	NSMutableDictionary *dictionary = [[[NSMutableDictionary alloc] init] autorelease];
	int value = [permissions intValue];
	int creator = value & 0x7;
	int owner = value >> 3 & 0x7;
	int world = value >> 6 & 0x7;

	NSNumber *cs = [NSNumber numberWithInt:creator & 0x1];
	NSNumber *ce = [NSNumber numberWithInt:creator >> 1 & 0x1];
	NSNumber *cp = [NSNumber numberWithInt:creator >> 2 & 0x1];
	NSNumber *os = [NSNumber numberWithInt:owner & 0x1 ];
	NSNumber *oe = [NSNumber numberWithInt:owner >> 1 & 0x1];
	NSNumber *op = [NSNumber numberWithInt:owner >> 2 & 0x1];
	NSNumber *ws = [NSNumber numberWithInt:world & 0x1];
	NSNumber *we = [NSNumber numberWithInt:world >> 1 & 0x1];
	NSNumber *wp = [NSNumber numberWithInt:world >> 2 & 0x1];

	[dictionary setObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:cs,ce,cp,nil]
													  forKeys:[NSArray arrayWithObjects:kKDISShareEnabled,kKDISEditEnabled,kKDISPrintEnabled,nil]]
				   forKey:kKDISCreator];
	[dictionary setObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:os,oe,op,nil]
													  forKeys:[NSArray arrayWithObjects:kKDISShareEnabled,kKDISEditEnabled,kKDISPrintEnabled,nil]]
				   forKey:kKDISOwner];
	[dictionary setObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:ws,we,wp,nil]
													  forKeys:[NSArray arrayWithObjects:kKDISShareEnabled,kKDISEditEnabled,kKDISPrintEnabled,nil]]
				   forKey:kKDISWorld];

	return dictionary;
}

+ (NSNumber *)permissionsNumberFromDictionary:(NSDictionary *)dictionary {
	int value = 0;
	int world = [[[dictionary valueForKey:kKDISWorld] valueForKey:kKDISPrintEnabled] intValue] << 2;
	world |= [[[dictionary valueForKey:kKDISWorld] valueForKey:kKDISEditEnabled] intValue] << 1;
	world |= [[[dictionary valueForKey:kKDISWorld] valueForKey:kKDISShareEnabled] intValue];
	int owner = [[[dictionary valueForKey:kKDISOwner] valueForKey:kKDISPrintEnabled] intValue] << 2;
	owner |= [[[dictionary valueForKey:kKDISOwner] valueForKey:kKDISEditEnabled] intValue] << 1;
	owner |= [[[dictionary valueForKey:kKDISOwner] valueForKey:kKDISShareEnabled] intValue];
	int creator = [[[dictionary valueForKey:kKDISCreator] valueForKey:kKDISPrintEnabled] intValue] << 2;
	creator |= [[[dictionary valueForKey:kKDISCreator] valueForKey:kKDISEditEnabled] intValue] << 1;
	creator |= [[[dictionary valueForKey:kKDISCreator] valueForKey:kKDISShareEnabled] intValue];

	value |= world << 6 & 0x1c0;
	value |= owner << 3 & 0x38;
	value |= creator & 0x7;

	return [NSNumber numberWithInt:value];
}

I am not used to coding bitwise operations so my use of odd masks may be a very poor way of doing it but it works.

Want to backup files in windows the linux way?

Download this cwRsync.

Which is rsync packaged up for windows, and create a small script like this to run as administrator:


@ECHO OFF

SETLOCAL

REM ** CUSTOMIZE ** Specify where to find rsync and related files (C:\CWRSYNC)
SET CWRSYNCHOME="c:\Program Files (x86)\cwRsync"

REM Set CYGWIN variable to 'nontsec'. That makes sure that permissions
REM on your windows machine are not updated as a side effect of cygwin
REM operations.
SET CYGWIN=nontsec

REM Set HOME variable to your windows home directory. That makes sure
REM that ssh command creates known_hosts in a directory you have access.
SET HOME=%HOMEDRIVE%%HOMEPATH%

REM Make cwRsync home as a part of system PATH to find required DLLs
SET CWOLDPATH=%PATH%
SET PATH=%CWRSYNCHOME%\BIN;%PATH%

sc stop MsMpSvc
taskkill /f /im msmpeng.exe
rsync --delete -rvP /cygdrive/d/games/steam /cygdrive/f/games
sc start MsMpSvc

Adjust your rsync line as you need to, also note that you don’t need to stop microsoft security essentials but it will slow everything down whilst you’re creating/writing so many files. Of course, if you don’t use it that’s cool but you might want to temporarily shut down whatever tool you do use.

*Post originally contained information on how to backup your comics, redacted at the request of Comixology’s CEO.*

Digital comics are a great thing, and I’m slowly amassing a reasonable library of them through the fantastic Comics.app (Comixology) on the iPhone/iPad. One thing concerns me though and almost holds me back from buying all the issues of Witchblade I want so I have the full collection. Seeing as I’m currently selling all my dead tree copies of the same I want to know that my digital comics are safe & not going to go anywhere. There’s no option on Comixology to view what you’ve purchased in the past and certainly no options to download CBR’s or similar of your purchases. As has just been pointed out to me by David Steinberger (Comixology CEO) they are indeed viewable on the web, here:

Comixology Online Comic Viewer – Account login required.

I swear that was white paging for me last night, which is why I decided to look into scripting an extraction process in the first place.

So the problem with digital distribution is that in a lot of cases you do not really own what you buy. What happens when digital distribution providers vanish? You are normally left with a purchase encumbered by DRM that you cannot read. Your investment wasted. This is a worrying development in consumers rights. If I purchase a comic/book/cd/game in a bricks and mortar store nobody can really take that away from me. I can scan it/copy it and keep it if I so wish and nobody is really going to know unless I start waving it around on the web. I can also loan the item itself to friends. They can enjoy that and go on to collect/purchase the item or recommend it to their friends leading to increased sales. Admittedly Kindle are doing this now and it’s a great thing, bringing some of the functionality of real books into the digital world.

What if I choose/amforced to abandon the provider… Say I no longer can use an iPhone/Android or have some argument with Amazon and refuse to use their services? I have made those purchases, not rented them. It’s not like deciding to avoid Waterstones or Sainsbury’s.

How do I take them with me? Well in many ways as it happens but with laws such as the DMCA and others it makes it potentially illegal to do so. Like so many strict interpretations of copyright law when handled by corporations they criminalise and therefore marginalise the very people who are the biggest fans. A recent example of this for me was the BBC’s showing of some of Bob Monkhouse archive. He was a huge huge fan of TV/Radio comedy and did everything he could to archive these for his personal use. It got him arrested, although charges were eventually dropped. Some TV shows now recognise this dichotomy, I believe Tru Blood is very lenient towards it’s fans use of it’s imagery. Other’s not so (*cough*Harry Potter*cough*). Another recent example I saw on 4chan’s /co/ board. Somebody had ripped and posted scans of an entire comic for others to read, effectively saying this is ace look look! The author of the comic noticed and joined in the thread, not to condemn but to encourage. Get out there, read my stuff, please buy my books. That’s to be applauded, it clearly worked for him and drove sales. Perhaps that only works for the smaller publishers… would it have worked to promote say, AP Comics Darkham Vale or Com.X’s Bazooka Jules, the first two issues said x/6 the 3rd said no such thing and turned out to be the last. Similar stories exist with Young Liars by David Lapham and other big names as well and that series was beguiling, confusing and excellent. It didn’t work for the piloted and then leaked TV version of Warren Ellis’ Global Frequency, mores the shame.

I for one am happy to get digital comic books to read for less than a 1/3 of the price UK stores tend to charge. It’ the price point digital distribution should be, significantly less than the corporeal version not some 10% tip of the hat. iBooks especially but Kindle and Waterstones etc. take note… your prices are a fucking joke, in some cases 50% plus (amazon) higher than you yourself sell the dead tree version. There is NO way a digital copy of a book should ever cost more than the dead tree version. Are they trying to protect their paper manufacturing arms? Perhaps publishers, in a vertical investment, have shares in managed forests, tree felling, pulping and paper producing industries and they think this protects their alternate revenue streams.

There are a few major differences between the two but the most glaring is that UITextView does not support styles so you cannot create them stylistically the same as a UITextField. You cannot readily apply background images depending on state and you cannot apply styles such as UITextBorderStyleRoundedRect. To get around this in the most simple fashion it appears to me that the best method, and one of the simplest, is to create your own UITextView subclass and use that. For this you need a few images in @2x and normal formats containing the background colours you want. In my case I use these, created from a simple PSD that lets me easily alter the background colour:


For the UITextField components I can then apply the setting like so:

	[name setBackground:textfieldEditBG];
	[name setDisabledBackground:textfieldPlainBG];

Having first loaded the images into those variables with methods like:

[[UIImage imageNamed:@"UITextFieldRoundedRectD"]  stretchableImageWithLeftCapWidth:12 topCapHeight:12];

This means that when I just want to view the text it appears unadorned and when I enable it for editing with:

[desc setEnabled:YES]

I get the rounded rect effect. For the UITextView subclass I use a simple hack as my Plain variants are the same as the background of the view in which they are present:

//
//  UIRoundedTextView.h
//  Cooking Companion
//
//  Created by Paul Downs on 10/12/2010.
//  Copyright 2010 The Lost Souls. All rights reserved.
//

#import 

@interface UIRoundedTextView : UITextView {
	UIImage *borderImage;
}

@property (nonatomic, retain) UIImage *borderImage;

- (void)setBorderImage:(UIImage *)background;

@end
//
//  UIRoundedTextView.m
//  Cooking Companion
//
//  Created by Paul Downs on 10/12/2010.
//  Copyright 2010 The Lost Souls. All rights reserved.
//

#import "UIRoundedTextView.h"

@implementation UIRoundedTextView

@synthesize borderImage;

- (void)setBorderImage:(UIImage *)background {
	if (borderImage != background) {
		borderImage = nil;
		[borderImage release];
		borderImage = [[background stretchableImageWithLeftCapWidth:12 topCapHeight:12] retain];
		[self setClipsToBounds:NO];
	}
}

- (void)drawRect:(CGRect)rect {
	[super drawRect:rect];
	if (borderImage && [self isEditable]) {
		UIGraphicsGetCurrentContext();
		[borderImage drawInRect:rect];
		UIGraphicsEndImageContext();
	}
}

- (void)dealloc {
	[borderImage release];
	[super dealloc];
}

@end

I can then initialise such a view to display the adorned version in edit mode with:

	[desc setBorderImage:[UIImage imageNamed:@"UITextFieldRoundedRectD"]];
	[desc setClipsToBounds:NO];

Seeing as the drawRect mode has been overridden it does not interfere with the rendering of the text above the image. You could also use a transparent image here if you do not wish to change the background colour away from that of the main view behind it.

It’s important to note that any UITextView components in IB (xib/nib) files will need to have their class set to UIRoundedTextView. If anyone reads this, feel free to use the code above in your own projects.

Why does the following code throw an exception and immediately sig kill on the second iteration at [e nextObject]:

		for (NSManagedObject *current in categories) {
			if ([lowercaseName isEqualToString:[current.name lowercaseString]] && category != current ) {

				// Move all the recipes.
				NSEnumerator *e = [[category valueForKey:@"recipe"] objectEnumerator];
				id object;
				while (object = [e nextObject]) {
					[(NSManagedObject *)object setCategory:current];
				}
				// Delete the category we imported.
				[moc deleteObject:category];

				// Reassign the current category to be returned instead.
				category = current;
				break;
			}
		}

Please note that in the above code, implied but not shown, category is an NSManagedObject and categories is an NSSet of NSManagedObjects. Also category will ALWAYS be present in categories.

The code above is intended to stop duplication of an NSManagedObject by finding out if one already exists and if it does moving it’s objects over to it.

The reason it crashes is because these objects (being NSManagedObjects) are very closely tied to the underlying database. After the first iteration through the while loop we have changed the underlying database, this change is immediately reflected in our NSManagedObject and therefore immediately reflected in the iterator. For example:

1) while (object = [e nextObject]) {

The iterator ‘e’ is attached to the managed object ‘category’, it contains 2 objects. The first of which is assigned to ‘object’.

2) [(NSManagedObject *)object setCategory:current];

The managed object ‘object’ is re-assigned away from the managed object ‘category’ to the managed object ‘current’. The managed object ‘category’ therefore now only has one object.

3) while (object = [e nextObject]) {

An exception is thrown. The NSEnumerator (actually an NSFastEnumerator something or other) can also be written in code as:

for (object in category)

Internally it behaves very very much like a for loop using pointer arithmetic. For this reason on the second loop the pointer is no longer valid as what was object no. 2 is now object no. 1. We have moved the object to another NSManagedObject and the change is reflected immediately. The contents of the while loop can be modified to anything that does not move the objects within ‘category’ and the crash goes away.

What do we do about this? Well the first thought is to work on a copy of the data. Thankfully we are given very simple tools to make copies such as:

NSSet *fishes = [[atlantic fish] copy];
NSMutableSet *fishes = [[pacific fish] mutableCopy];

Right? In this instance yes. Be aware however that this will not always solve your problems. It might give you a copy of the surface data but it does NOT give you a deep copy of the underlying objects. So fishes is not the same as atlantic but it will contain the same ‘fish’. If you follow my rather horrible example. I expect it’s influenced by the tropical fish tank and conversation I was having about Danio’s whilst I write this. The fixed code reads:

		for (NSManagedObject *current in categories) {
			if ([lowercaseName isEqualToString:[current.name lowercaseString]] && category != current ) {

				// Move all the recipes.
				NSSet *workingCopy = [[category item] copy];
				NSEnumerator *e = [[category valueForKey:@"item"] objectEnumerator];
				id object;
				while (object = [e workingCopy]) {
					[(NSManagedObject *)object setCategory:current];
				}
				// Delete the category we imported.
				[workingCopy release];
				[moc deleteObject:category];

				// Reassign the current category to be returned instead.
				category = current;
				break;
			}
		}
Powered by WordPress | Theme: KLG based on Motion by 85ideas.