Index: res_features.c
===================================================================
--- res_features.c	(revision 7816)
+++ res_features.c	(working copy)
@@ -100,7 +100,8 @@
 /* Last available extension for parking */
 static int parking_stop;
 
-static int parking_offset;
+/* next extension to look for parking */
+static int parking_next;
 
 static int parkfindnext;
 
@@ -153,7 +154,7 @@
 
 static struct parkeduser *parkinglot;
 
-AST_MUTEX_DEFINE_STATIC(parking_lock);
+AST_MUTEX_DEFINE_STATIC(parking_lock);	/* protects all static variables above */
 
 static pthread_t parking_thread;
 
@@ -161,6 +162,9 @@
 
 LOCAL_USER_DECL;
 
+/* we often need to replace an empty string with a default */
+#define S_OR(a, b)	(!ast_strlen_zero(a) ? (a) : (b))
+
 char *ast_parking_ext(void)
 {
 	return parking_ext;
@@ -274,39 +277,54 @@
 	   after these channels too */
 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
 {
-	struct parkeduser *pu, *cur;
-	int i,x,parking_range;
-	char exten[AST_MAX_EXTENSION];
+	struct parkeduser *prev = NULL, *p1 = NULL, *pu, *cur;
+	int i, x;
 	struct ast_context *con;
 
-	pu = malloc(sizeof(struct parkeduser));
+	pu = calloc(1, sizeof(struct parkeduser));
 	if (!pu) {
 		ast_log(LOG_WARNING, "Out of memory\n");
 		return -1;
 	}
-	memset(pu, 0, sizeof(struct parkeduser));
+	/*
+	 * Look for the first free extension starting from parking_next.
+	 * We keep the parking lot sorted by extension, so one pass is enough.
+	 * in the loop, we scan the parking lot, and the first free extension
+	 * (a hole between two consecutive entries) is taken as a candidate.
+	 * If we reach a candidate >= parking_next, then we are done; otherwise,
+	 * we take the previously selected one.
+	 * On exit x is the desired extension (or -1 if not found), and
+	 * p1 is right before the insertion point so we do not need to scan
+	 * the list again for insertions.
+	 */
+	x = -1; /* this means not found */
+	i = parking_start; /* a candidate */
+
 	ast_mutex_lock(&parking_lock);
-	parking_range = parking_stop - parking_start+1;
-	for (i = 0; i < parking_range; i++) {
-		x = (i + parking_offset) % parking_range + parking_start;
-		cur = parkinglot;
-		while(cur) {
-			if (cur->parkingnum == x) 
-				break;
-			cur = cur->next;
-		}
-		if (!cur)
+	for (prev = NULL, cur = parkinglot; cur; i++, prev = cur, cur = cur->next) {
+		if (cur->parkingnum == i) /* candidate is in use, try next */
+			continue;
+		/* cur->parkingnum > i, and extension i is available */
+		if (i >= parking_next) { /* best choice */
+			p1 = prev;
+			x = i;
 			break;
+		}
+		if (x == -1) { /* still unset, tentative choice */
+			p1 = prev;
+			x = i;
+			i = cur->parkingnum;
+		}
 	}
 
-	if (!(i < parking_range)) {
+	if (x == -1) {
 		ast_log(LOG_WARNING, "No more parking spaces\n");
 		free(pu);
 		ast_mutex_unlock(&parking_lock);
 		return -1;
 	}
-	if (parkfindnext) 
-		parking_offset = x - parking_start + 1;
+	if (parkfindnext)	/* update parking_next */
+		parking_next = x >= parking_stop ? parking_start : x+1;
 	chan->appl = "Parked Call";
 	chan->data = NULL; 
 
@@ -318,10 +336,7 @@
 	}
 	pu->start = ast_tvnow();
 	pu->parkingnum = x;
-	if (timeout > 0)
-		pu->parkingtime = timeout;
-	else
-		pu->parkingtime = parkingtime;
+	pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
 	if (extout)
 		*extout = x;
 	if (peer) 
@@ -329,24 +344,22 @@
 
 	/* Remember what had been dialed, so that if the parking
 	   expires, we try to come back to the same place */
-	if (!ast_strlen_zero(chan->macrocontext))
-		ast_copy_string(pu->context, chan->macrocontext, sizeof(pu->context));
-	else
-		ast_copy_string(pu->context, chan->context, sizeof(pu->context));
-	if (!ast_strlen_zero(chan->macroexten))
-		ast_copy_string(pu->exten, chan->macroexten, sizeof(pu->exten));
-	else
-		ast_copy_string(pu->exten, chan->exten, sizeof(pu->exten));
-	if (chan->macropriority)
-		pu->priority = chan->macropriority;
-	else
-		pu->priority = chan->priority;
-	pu->next = parkinglot;
-	parkinglot = pu;
+	ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context));
+	ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten));
+	pu->priority = chan->macropriority ? chan->macropriority : chan->priority;
+
 	/* If parking a channel directly, don't quiet yet get parking running on it */
 	if (peer == chan) 
 		pu->notquiteyet = 1;
+	if (p1) {	 /* insert in the middle */
+		pu->next = p1->next;
+		p1->next = pu;
+	} else {	/* insert at the head */
+		pu->next = parkinglot;
+		parkinglot = pu;
+	}
 	ast_mutex_unlock(&parking_lock);
+
 	/* Wake up the (presumably select()ing) thread */
 	pthread_kill(parking_thread, SIGURG);
 	if (option_verbose > 1) 
@@ -366,23 +379,21 @@
 		);
 
 	if (peer) {
-		if (adsipark && adsi_available(peer)) {
+		if (adsipark && adsi_available(peer))
 			adsi_announce_park(peer, pu->parkingnum);
-		}
-		if (adsipark && adsi_available(peer)) {
+		if (adsipark && adsi_available(peer))
 			adsi_unload_session(peer);
-		}
 	}
 	con = ast_context_find(parking_con);
 	if (!con) {
 		con = ast_context_create(NULL, parking_con, registrar);
-		if (!con) {
+		if (!con)
 			ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
-		}
 	}
 	if (con) {
+		char exten[AST_MAX_EXTENSION];
 		snprintf(exten, sizeof(exten), "%d", x);
		ast_add_extension2(con, 1, exten, 1, NULL, NULL, parkedcall, strdup(exten), FREE, registrar);
 	}
 	if (peer) 
 		ast_say_digits(peer, pu->parkingnum, "", peer->language);
