93ba33e
diff -up cups-1.7.5/CHANGES.txt.str4396 cups-1.7.5/CHANGES.txt
93ba33e
diff -up cups-1.7.5/scheduler/ipp.c.str4396 cups-1.7.5/scheduler/ipp.c
93ba33e
--- cups-1.7.5/scheduler/ipp.c.str4396	2014-08-20 16:56:29.012022421 +0100
93ba33e
+++ cups-1.7.5/scheduler/ipp.c	2014-08-20 16:56:38.535084011 +0100
93ba33e
@@ -6595,6 +6595,7 @@ get_jobs(cupsd_client_t  *con,		/* I - C
93ba33e
   cupsd_job_t	*job;			/* Current job pointer */
93ba33e
   cupsd_printer_t *printer;		/* Printer */
93ba33e
   cups_array_t	*list;			/* Which job list... */
93ba33e
+  int		delete_list = 0;	/* Delete the list afterwards? */
93ba33e
   cups_array_t	*ra,			/* Requested attributes array */
93ba33e
 		*exclude;		/* Private attributes array */
93ba33e
   cupsd_policy_t *policy;		/* Current policy */
93ba33e
@@ -6694,13 +6695,15 @@ get_jobs(cupsd_client_t  *con,		/* I - C
93ba33e
   {
93ba33e
     job_comparison = 1;
93ba33e
     job_state      = IPP_JOB_CANCELED;
93ba33e
-    list           = Jobs;
93ba33e
+    list           = cupsdGetCompletedJobs(printer);
93ba33e
+    delete_list    = 1;
93ba33e
   }
93ba33e
   else if (!strcmp(attr->values[0].string.text, "aborted"))
93ba33e
   {
93ba33e
     job_comparison = 0;
93ba33e
     job_state      = IPP_JOB_ABORTED;
93ba33e
-    list           = Jobs;
93ba33e
+    list           = cupsdGetCompletedJobs(printer);
93ba33e
+    delete_list    = 1;
93ba33e
   }
93ba33e
   else if (!strcmp(attr->values[0].string.text, "all"))
93ba33e
   {
93ba33e
@@ -6712,7 +6715,8 @@ get_jobs(cupsd_client_t  *con,		/* I - C
93ba33e
   {
93ba33e
     job_comparison = 0;
93ba33e
     job_state      = IPP_JOB_CANCELED;
93ba33e
-    list           = Jobs;
93ba33e
+    list           = cupsdGetCompletedJobs(printer);
93ba33e
+    delete_list    = 1;
93ba33e
   }
93ba33e
   else if (!strcmp(attr->values[0].string.text, "pending"))
93ba33e
   {
93ba33e
@@ -6919,6 +6923,9 @@ get_jobs(cupsd_client_t  *con,		/* I - C
93ba33e
 
93ba33e
   cupsArrayDelete(ra);
93ba33e
 
93ba33e
+  if (delete_list)
93ba33e
+    cupsArrayDelete(list);
93ba33e
+
93ba33e
   con->response->request.status.status_code = IPP_OK;
93ba33e
 }
93ba33e
 
93ba33e
diff -up cups-1.7.5/scheduler/job.c.str4396 cups-1.7.5/scheduler/job.c
93ba33e
--- cups-1.7.5/scheduler/job.c.str4396	2014-08-20 16:56:29.013022427 +0100
93ba33e
+++ cups-1.7.5/scheduler/job.c	2014-08-20 16:56:38.537084024 +0100
93ba33e
@@ -130,6 +130,7 @@ static mime_filter_t	gziptoany_filter =
93ba33e
  */
93ba33e
 
93ba33e
 static int	compare_active_jobs(void *first, void *second, void *data);
93ba33e
+static int	compare_completed_jobs(void *first, void *second, void *data);
93ba33e
 static int	compare_jobs(void *first, void *second, void *data);
93ba33e
 static void	dump_job_history(cupsd_job_t *job);
93ba33e
 static void	finalize_job(cupsd_job_t *job, int set_job_state);
93ba33e
@@ -1568,6 +1569,30 @@ cupsdFindJob(int id)			/* I - Job ID */
93ba33e
 
93ba33e
 
93ba33e
 /*
93ba33e
+ * 'cupsdGetCompletedJobs()'- Generate a completed jobs list.
93ba33e
+ */
93ba33e
+
93ba33e
+cups_array_t *				/* O - Array of jobs */
93ba33e
+cupsdGetCompletedJobs(
93ba33e
+    cupsd_printer_t *p)			/* I - Printer */
93ba33e
+{
93ba33e
+  cups_array_t	*list;			/* Array of jobs */
93ba33e
+  cupsd_job_t	*job;			/* Current job */
93ba33e
+
93ba33e
+
93ba33e
+  list = cupsArrayNew(compare_completed_jobs, NULL);
93ba33e
+
93ba33e
+  for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
93ba33e
+       job;
93ba33e
+       job = (cupsd_job_t *)cupsArrayNext(Jobs))
93ba33e
+    if ((!p || !_cups_strcasecmp(p->name, job->dest)) && job->state_value >= IPP_JOB_STOPPED && job->completed_time)
93ba33e
+      cupsArrayAdd(list, job);
93ba33e
+
93ba33e
+  return (list);
93ba33e
+}
93ba33e
+
93ba33e
+
93ba33e
+/*
93ba33e
  * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
93ba33e
  *                               or held jobs in a printer or class.
93ba33e
  */
93ba33e
@@ -1766,6 +1791,8 @@ cupsdLoadJob(cupsd_job_t *job)		/* I - J
93ba33e
       (attr = ippFindAttribute(job->attrs, "time-at-completed",
93ba33e
 			       IPP_TAG_INTEGER)) != NULL)
93ba33e
   {
93ba33e
+    job->completed_time = attr->values[0].integer;
93ba33e
+
93ba33e
     if (JobHistory < INT_MAX)
93ba33e
       job->history_time = attr->values[0].integer + JobHistory;
93ba33e
     else
93ba33e
@@ -2257,8 +2284,11 @@ cupsdSaveAllJobs(void)
93ba33e
   {
93ba33e
     cupsFilePrintf(fp, "<Job %d>\n", job->id);
93ba33e
     cupsFilePrintf(fp, "State %d\n", job->state_value);
93ba33e
+    if (job->completed_time)
93ba33e
+      cupsFilePrintf(fp, "Completed %ld\n", (long)job->completed_time);
93ba33e
     cupsFilePrintf(fp, "Priority %d\n", job->priority);
93ba33e
-    cupsFilePrintf(fp, "HoldUntil %d\n", (int)job->hold_until);
93ba33e
+    if (job->hold_until)
93ba33e
+      cupsFilePrintf(fp, "HoldUntil %ld\n", (long)job->hold_until);
93ba33e
     cupsFilePrintf(fp, "Username %s\n", job->username);
93ba33e
     cupsFilePrintf(fp, "Destination %s\n", job->dest);
93ba33e
     cupsFilePrintf(fp, "DestType %d\n", job->dtype);
93ba33e
@@ -2993,6 +3023,28 @@ compare_active_jobs(void *first,	/* I -
93ba33e
 
93ba33e
 
93ba33e
 /*
93ba33e
+ * 'compare_completed_jobs()' - Compare the job IDs and completion times of two jobs.
93ba33e
+ */
93ba33e
+
93ba33e
+static int				/* O - Difference */
93ba33e
+compare_completed_jobs(void *first,	/* I - First job */
93ba33e
+                       void *second,	/* I - Second job */
93ba33e
+		       void *data)	/* I - App data (not used) */
93ba33e
+{
93ba33e
+  int	diff;				/* Difference */
93ba33e
+
93ba33e
+
93ba33e
+  (void)data;
93ba33e
+
93ba33e
+  if ((diff = ((cupsd_job_t *)second)->completed_time -
93ba33e
+              ((cupsd_job_t *)first)->completed_time) != 0)
93ba33e
+    return (diff);
93ba33e
+  else
93ba33e
+    return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
93ba33e
+}
93ba33e
+
93ba33e
+
93ba33e
+/*
93ba33e
  * 'compare_jobs()' - Compare the job IDs of two jobs.
93ba33e
  */
93ba33e
 
93ba33e
@@ -4268,6 +4320,14 @@ load_job_cache(const char *filename)	/*
93ba33e
 
93ba33e
       if (job->state_value <= IPP_JOB_STOPPED && cupsdLoadJob(job))
93ba33e
 	cupsArrayAdd(ActiveJobs, job);
93ba33e
+      else if (job->state_value > IPP_JOB_STOPPED)
93ba33e
+      {
93ba33e
+        if (!job->completed_time)
93ba33e
+	{
93ba33e
+	  cupsdLoadJob(job);
93ba33e
+	  unload_job(job);
93ba33e
+	}
93ba33e
+      }
93ba33e
 
93ba33e
       job = NULL;
93ba33e
     }
93ba33e
@@ -4285,9 +4345,13 @@ load_job_cache(const char *filename)	/*
93ba33e
       else if (job->state_value > IPP_JOB_COMPLETED)
93ba33e
         job->state_value = IPP_JOB_COMPLETED;
93ba33e
     }
93ba33e
+    else if (!_cups_strcasecmp(line, "Completed"))
93ba33e
+    {
93ba33e
+      job->completed_time = strtol(value, NULL, 10);
93ba33e
+    }
93ba33e
     else if (!_cups_strcasecmp(line, "HoldUntil"))
93ba33e
     {
93ba33e
-      job->hold_until = atoi(value);
93ba33e
+      job->hold_until = strtol(value, NULL, 10);
93ba33e
     }
93ba33e
     else if (!_cups_strcasecmp(line, "Priority"))
93ba33e
     {
93ba33e
@@ -4625,6 +4689,8 @@ set_time(cupsd_job_t *job,		/* I - Job t
93ba33e
 
93ba33e
   if (!strcmp(name, "time-at-completed"))
93ba33e
   {
93ba33e
+    job->completed_time = curtime;
93ba33e
+
93ba33e
     if (JobHistory < INT_MAX && attr)
93ba33e
       job->history_time = attr->values[0].integer + JobHistory;
93ba33e
     else
93ba33e
diff -up cups-1.7.5/scheduler/job.h.str4396 cups-1.7.5/scheduler/job.h
93ba33e
--- cups-1.7.5/scheduler/job.h.str4396	2014-08-20 16:56:29.013022427 +0100
93ba33e
+++ cups-1.7.5/scheduler/job.h	2014-08-20 16:56:38.538084031 +0100
93ba33e
@@ -54,6 +54,7 @@ struct cupsd_job_s			/**** Job request *
93ba33e
   ipp_attribute_t	*sheets;	/* job-media-sheets-completed */
93ba33e
   time_t		access_time,	/* Last access time */
93ba33e
 			cancel_time,	/* When to cancel/send SIGTERM */
93ba33e
+			completed_time,	/* When job was completed (0 if not) */
93ba33e
 			file_time,	/* Job file retain time */
93ba33e
 			history_time,	/* Job history retain time */
93ba33e
 			hold_until,	/* Hold expiration date/time */
93ba33e
@@ -156,6 +157,7 @@ extern void		cupsdDeleteJob(cupsd_job_t
93ba33e
 			               cupsd_jobaction_t action);
93ba33e
 extern cupsd_job_t	*cupsdFindJob(int id);
93ba33e
 extern void		cupsdFreeAllJobs(void);
93ba33e
+extern cups_array_t	*cupsdGetCompletedJobs(cupsd_printer_t *p);
93ba33e
 extern int		cupsdGetPrinterJobCount(const char *dest);
93ba33e
 extern int		cupsdGetUserJobCount(const char *username);
93ba33e
 extern void		cupsdLoadAllJobs(void);
93ba33e
diff -up cups-1.7.5/systemv/lpstat.c.str4396 cups-1.7.5/systemv/lpstat.c
93ba33e
--- cups-1.7.5/systemv/lpstat.c.str4396	2014-05-22 14:59:21.000000000 +0100
93ba33e
+++ cups-1.7.5/systemv/lpstat.c	2014-08-20 16:56:38.539084037 +0100
93ba33e
@@ -1316,7 +1316,8 @@ show_jobs(const char *dests,		/* I - Des
93ba33e
   const char	*dest,			/* Pointer into job-printer-uri */
93ba33e
 		*username,		/* Pointer to job-originating-user-name */
93ba33e
 		*title,			/* Pointer to job-name */
93ba33e
-		*message;		/* Pointer to job-printer-state-message */
93ba33e
+		*message,		/* Pointer to job-printer-state-message */
93ba33e
+		*time_at;		/* time-at-xxx attribute name to use */
93ba33e
   int		rank,			/* Rank in queue */
93ba33e
 		jobid,			/* job-id */
93ba33e
 		size;			/* job-k-octets */
93ba33e
@@ -1332,7 +1333,8 @@ show_jobs(const char *dests,		/* I - Des
93ba33e
 		  "job-printer-state-message",
93ba33e
 		  "job-printer-uri",
93ba33e
 		  "job-state-reasons",
93ba33e
-		  "time-at-creation"
93ba33e
+		  "time-at-creation",
93ba33e
+		  "time-at-completed"
93ba33e
 		};
93ba33e
 
93ba33e
 
93ba33e
@@ -1398,6 +1400,13 @@ show_jobs(const char *dests,		/* I - Des
93ba33e
     * Loop through the job list and display them...
93ba33e
     */
93ba33e
 
93ba33e
+    if (!strcmp(which, "aborted") ||
93ba33e
+        !strcmp(which, "canceled") ||
93ba33e
+        !strcmp(which, "completed"))
93ba33e
+      time_at = "time-at-completed";
93ba33e
+    else
93ba33e
+      time_at = "time-at-creation";
93ba33e
+
93ba33e
     rank = -1;
93ba33e
 
93ba33e
     for (attr = response->attrs; attr != NULL; attr = attr->next)
93ba33e
@@ -1433,8 +1442,7 @@ show_jobs(const char *dests,		/* I - Des
93ba33e
         else if (!strcmp(attr->name, "job-k-octets") &&
93ba33e
 		 attr->value_tag == IPP_TAG_INTEGER)
93ba33e
 	  size = attr->values[0].integer;
93ba33e
-        else if (!strcmp(attr->name, "time-at-creation") &&
93ba33e
-		 attr->value_tag == IPP_TAG_INTEGER)
93ba33e
+        else if (!strcmp(attr->name, time_at) && attr->value_tag == IPP_TAG_INTEGER)
93ba33e
 	  jobtime = attr->values[0].integer;
93ba33e
         else if (!strcmp(attr->name, "job-printer-state-message") &&
93ba33e
 	         attr->value_tag == IPP_TAG_TEXT)
93ba33e
diff -up cups-1.7.5/test/5.5-lp.sh.str4396 cups-1.7.5/test/5.5-lp.sh
93ba33e
--- cups-1.7.5/test/5.5-lp.sh.str4396	2013-11-06 20:09:03.000000000 +0000
93ba33e
+++ cups-1.7.5/test/5.5-lp.sh	2014-08-20 16:56:38.540084044 +0100
93ba33e
@@ -79,6 +79,19 @@ echo ""
93ba33e
 
93ba33e
 ./waitjobs.sh
93ba33e
 
93ba33e
+echo "LPSTAT Completed Jobs Order Test"
93ba33e
+echo ""
93ba33e
+echo "    lpstat -W completed -o"
93ba33e
+$VALGRIND ../systemv/lpstat -W completed -o | tee $BASE/lpstat-completed.txt
93ba33e
+if test "`uniq -d $BASE/lpstat-completed.txt`" != ""; then
93ba33e
+	echo "    FAILED"
93ba33e
+	exit 1
93ba33e
+else
93ba33e
+	echo "    PASSED"
93ba33e
+fi
93ba33e
+echo ""
93ba33e
+
93ba33e
+
93ba33e
 #
93ba33e
 # End of "$Id: 5.5-lp.sh 11396 2013-11-06 20:09:03Z msweet $".
93ba33e
 #