xref: /original-bsd/usr.bin/at/atq/atq.c (revision 4e9331e4)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)atq.c	5.6 (Berkeley) 05/11/89";
15 #endif not lint
16 
17 /*
18  *
19  *	Synopsis:  atq [ -c ] [ -n ] [ name ... ]
20  *
21  *
22  *	Print the queue of files waiting to be executed. These files
23  *	were created by using the "at" command and are located in the
24  *	directory "/usr/spool/at".
25  *
26  *
27  *	Author: Steve Wall
28  *		Computer Systems Research Group
29  *		University of California @ Berkeley
30  *
31  */
32 
33 # include <stdio.h>
34 # include <sys/types.h>
35 # include <sys/file.h>
36 # include <sys/dir.h>
37 # include <sys/stat.h>
38 # include <sys/time.h>
39 # include <pwd.h>
40 # include <ctype.h>
41 # include "pathnames.h"
42 
43 /*
44  * Months of the year
45  */
46 static char *mthnames[12] = {
47 	"Jan","Feb","Mar","Apr","May","Jun","Jul",
48 	"Aug","Sep","Oct","Nov","Dec",
49 };
50 
51 char *nullentry = NULL;			/* avoid 'namelist' NULL ptr problems */
52 int numentries;				/* number of entries in spooling area */
53 int namewanted = 0;			/* only print jobs belonging to a
54 					   certain person */
55 struct direct **queue;			/* the queue itself */
56 
57 
58 main(argc,argv)
59 int argc;
60 char **argv;
61 {
62 
63 	int cflag = 0;			/* print in order of creation time */
64 	int nflag = 0;			/* just print the number of jobs in
65 					   queue */
66 	int usage();			/* print usage info and exit */
67 	int creation();			/* sort jobs by date of creation */
68 	int alphasort();		/* sort jobs by date of execution */
69 	int filewanted();		/* should a file be included in queue?*/
70 	int printqueue();		/* print the queue */
71 	int countfiles();		/* count the number of files in queue
72 					   for a given person */
73 	char **namelist = &nullentry;	/* array of specific name(s) requested*/
74 
75 
76 	--argc, ++argv;
77 
78 	/*
79 	 * Interpret command line flags if they exist.
80 	 */
81 	while (argc > 0 && **argv == '-') {
82 		(*argv)++;
83 		while (**argv) switch (*(*argv)++) {
84 
85 			case 'c' :	cflag++;
86 					break;
87 
88 			case 'n' :	nflag++;
89 					break;
90 
91 			default	 :	usage();
92 
93 		}
94 		--argc, ++argv;
95 	}
96 
97 	/*
98 	 * If a certain name (or names) is requested, set a pointer to the
99 	 * beginning of the list.
100 	 */
101 	if (argc > 0) {
102 		++namewanted;
103 		namelist = argv;
104 	}
105 
106 	/*
107 	 * Move to the spooling area and scan the directory, placing the
108 	 * files in the queue structure. The queue comes back sorted by
109 	 * execution time or creation time.
110 	 */
111 	if (chdir(_PATH_ATDIR) == -1) {
112 		perror(_PATH_ATDIR);
113 		exit(1);
114 	}
115 	if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation :
116 				alphasort)) < 0) {
117 		perror(_PATH_ATDIR);
118 		exit(1);
119 	}
120 
121 	/*
122 	 * Either print a message stating:
123 	 *
124 	 *	1) that the spooling area is empty.
125 	 *	2) the number of jobs in the spooling area.
126 	 *	3) the number of jobs in the spooling area belonging to
127 	 *	   a certain person.
128 	 *	4) that the person requested doesn't have any files in the
129 	 *	   spooling area.
130 	 *
131 	 * or send the queue off to "printqueue" for printing.
132 	 *
133 	 * This whole process might seem a bit elaborate, but it's worthwhile
134 	 * to print some informative messages for the user.
135 	 *
136 	 */
137 	if ((numentries == 0) && (!nflag)) {
138 		printf("no files in queue.\n");
139 		exit(0);
140 	}
141 	if (nflag) {
142 		printf("%d\n",(namewanted) ? countfiles(namelist) : numentries);
143 		exit(0);
144 	}
145 	if ((namewanted) && (countfiles(namelist) == 0)) {
146 		printf("no files for %s.\n", (argc == 1) ?
147 					*argv : "specified users");
148 		exit(0);
149 	}
150 	printqueue(namelist);
151 	exit(0);
152 }
153 
154 /*
155  * Count the number of jobs in the spooling area owned by a certain person(s).
156  */
157 countfiles(namelist)
158 char **namelist;
159 {
160 	int i;					/* for loop index */
161 	int entryfound;				/* found file owned by user(s)*/
162 	int numfiles = 0;			/* number of files owned by a
163 						   certain person(s) */
164 	char **ptr;				/* scratch pointer */
165 
166 
167 	/*
168 	 * For each file in the queue, see if the user(s) own the file. We
169 	 * have to use "entryfound" (rather than simply incrementing "numfiles")
170 	 * so that if a person's name appears twice on the command line we
171 	 * don't double the number of files owned by him/her.
172 	 */
173 	for (i = 0; i < numentries ; i++) {
174 		ptr = namelist;
175 		entryfound = 0;
176 
177 		while (*ptr) {
178 			if (isowner(*ptr,queue[i]->d_name))
179 				++entryfound;
180 			++ptr;
181 		}
182 		if (entryfound)
183 			++numfiles;
184 	}
185 	return(numfiles);
186 }
187 
188 /*
189  * Print the queue. If only jobs belonging to a certain person(s) are requested,
190  * only print jobs that belong to that person(s).
191  */
192 printqueue(namelist)
193 char **namelist;
194 {
195 	int i;					/* for loop index */
196 	int rank = 1;				/* rank of a job */
197 	int entryfound;				/* found file owned by user(s)*/
198 	int printrank();			/* print the rank of a job */
199 	int plastrun();				/* print the last time the
200 						   spooling area was updated */
201 	int powner();				/* print the name of the owner
202 						   of the job */
203 	char **ptr;				/* scratch pointer */
204 	struct stat stbuf;			/* buffer for file stats */
205 
206 
207 	/*
208 	 * Print the time the spooling area was last modified and the header
209 	 * for the queue.
210 	 */
211 	plastrun();
212 	printf(" Rank	  Execution Date     Owner     Job #   Job Name\n");
213 
214 	/*
215 	 * Print the queue. If a certain name(s) was requested, print only jobs
216 	 * belonging to that person(s), otherwise print the entire queue.
217 	 * Once again, we have to use "entryfound" (rather than simply
218 	 * comparing each command line argument) so that if a person's name
219 	 * appears twice we don't print each file owned by him/her twice.
220 	 *
221 	 *
222 	 * "printrank", "printdate", and "printjobname" all take existing
223 	 * data and display it in a friendly manner.
224 	 *
225 	 */
226 	for (i = 0; i < numentries; i++) {
227 		if ((stat(queue[i]->d_name, &stbuf)) < 0) {
228 			continue;
229 		}
230 		if (namewanted) {
231 			ptr = namelist;
232 			entryfound = 0;
233 
234 			while (*ptr) {
235 				if (isowner(*ptr,queue[i]->d_name))
236 					++entryfound;
237 				++ptr;
238 			}
239 			if (!entryfound)
240 				continue;
241 		}
242 		printrank(rank++);
243 		printdate(queue[i]->d_name);
244 		powner(queue[i]->d_name);
245 		printf("%5d",stbuf.st_ino);
246 		printjobname(queue[i]->d_name);
247 	}
248 	++ptr;
249 }
250 
251 /*
252  * See if "name" owns "job".
253  */
254 isowner(name,job)
255 char *name;
256 char *job;
257 {
258 	char buf[128];			/* buffer for 1st line of spoolfile
259 					   header */
260 	FILE *infile;			/* I/O stream to spoolfile */
261 
262 	if ((infile = fopen(job,"r")) == NULL) {
263 		fprintf(stderr,"Couldn't open spoolfile ");
264 		perror(job);
265 		return(0);
266 	}
267 
268 	if (fscanf(infile,"# owner: %127s%*[^\n]\n",buf) != 1) {
269 		fclose(infile);
270 		return(0);
271 	}
272 
273 	fclose(infile);
274 	return((strcmp(name,buf) == 0) ? 1 : 0);
275 }
276 
277 /*
278  * Print the owner of the job. This is stored on the first line of the
279  * spoolfile. If we run into trouble getting the name, we'll just print "???".
280  */
281 powner(file)
282 char *file;
283 {
284 	char owner[10];				/* the owner */
285 	FILE *infile;				/* I/O stream to spoolfile */
286 
287 	/*
288 	 * Open the job file and grab the first line.
289 	 */
290 
291 	if ((infile = fopen(file,"r")) == NULL) {
292 		printf("%-10.9s","???");
293 		perror(file);
294 		return;
295 	}
296 
297 	if (fscanf(infile,"# owner: %9s%*[^\n]\n",owner) != 1) {
298 		printf("%-10.9s","???");
299 		fclose(infile);
300 		return;
301 	}
302 
303 	fclose(infile);
304 	printf("%-10.9s",owner);
305 
306 }
307 
308 /*
309  * Print the time the spooling area was updated.
310  */
311 plastrun()
312 {
313 	struct timeval now;			/* time it is right now */
314 	struct timezone zone;			/* NOT USED */
315 	struct tm *loc;				/* detail of time it is right */
316 	u_long lasttime;			/* last update time in seconds
317 						   since 1/1/70 */
318 	FILE *last;				/* file where last update hour
319 						   is stored */
320 
321 
322 	/*
323 	 * Open the file where the last update time is stored, and grab the
324 	 * last update hour. The update time is measured in seconds since
325 	 * 1/1/70.
326 	 */
327 	if ((last = fopen(_PATH_LASTFILE,"r")) == NULL) {
328 		perror(_PATH_LASTFILE);
329 		exit(1);
330 	}
331 	fscanf(last,"%lu",&lasttime);
332 	fclose(last);
333 
334 	/*
335 	 * Get a broken down representation of the last update time.
336 	 */
337 	loc = localtime(&lasttime);
338 
339 	/*
340 	 * Print the time that the spooling area was last updated.
341 	 */
342 	printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]);
343 	printf("%d, 19%d ",loc->tm_mday,loc->tm_year);
344 	printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min);
345 }
346 
347 /*
348  * Print the rank of a job. (I've got to admit it, I stole it from "lpq")
349  */
350 static
351 printrank(n)
352 {
353 	static char *r[] = {
354 		"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
355 	};
356 
357 	if ((n/10) == 1)
358 		 printf("%3d%-5s", n,"th");
359 	else
360 		 printf("%3d%-5s", n, r[n%10]);
361 }
362 
363 /*
364  * Print the date that a job is to be executed. This takes some manipulation
365  * of the file name.
366  */
367 printdate(filename)
368 char *filename;
369 {
370 	int yday  =  0;				/* day of year file will be
371 						   executed */
372 	int min	  =  0;				/* min. file will be executed */
373 	int hour  =  0;				/* hour file will be executed */
374 	int day	  =  0;				/* day file will be executed */
375 	int month =  0;				/* month file will be executed*/
376 	int year  =  0;				/* year file will be executed */
377 	int get_mth_day();			/* convert a day of year to a
378 						   month and day of month */
379 	char date[19];				/* reformatted execution date */
380 
381 	/*
382 	 * Pick off the necessary info from the file name and convert the day
383 	 * of year to a month and day of month.
384 	 */
385 	sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min);
386 	get_mth_day(year,yday,&month,&day);
387 
388 	/*
389 	 * Format the execution date of a job.
390 	 */
391 	sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month],
392 						    day, year,hour,min);
393 
394 	/*
395 	 * Print the date the job will be executed.
396 	 */
397 	printf("%-21.18s",date);
398 }
399 
400 /*
401  * Given a day of the year, calculate the month and day of month.
402  */
403 get_mth_day(year,dayofyear,month,day)
404 int year, dayofyear, *month, *day;
405 
406 {
407 
408 	int i = 1;				/* for loop index */
409 	int leap;				/* are we dealing with a leap
410 						   year? */
411 						/* Table of the number of days
412 						   in each month of the year.
413 
414 						     dofy_tab[1] -- regular year
415 						     dofy_tab[2] -- leap year
416 									      */
417 
418 	static int dofy_tab[2][13] = {
419 		{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
420 		{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
421 	};
422 
423 	/*
424 	 * Are we dealing with a leap year?
425 	 */
426 	leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0);
427 
428 	/*
429 	 * Calculate the month of the year and day of the month.
430 	 */
431 	while (dayofyear >= dofy_tab[leap][i]) {
432 		dayofyear -= dofy_tab[leap][i++];
433 		++(*month);
434 	}
435 	*day = (dayofyear + 1);
436 }
437 
438 /*
439  * Print a job name. If the old "at" has been used to create the spoolfile,
440  * the three line header that the new version of "at" puts in the spoolfile.
441  * Thus, we just print "???".
442  */
443 printjobname(file)
444 char *file;
445 {
446 	char *ptr;				/* scratch pointer */
447 	char jobname[28];			/* the job name */
448 	FILE *filename;				/* job file in spooling area */
449 
450 	/*
451 	 * Open the job file and grab the second line.
452 	 */
453 	printf("   ");
454 
455 	if ((filename = fopen(file,"r")) == NULL) {
456 		printf("%.27s\n", "???");
457 		perror(file);
458 		return;
459 	}
460 	/*
461 	 * Skip over the first line.
462 	 */
463 	fscanf(filename,"%*[^\n]\n");
464 
465 	/*
466 	 * Now get the job name.
467 	 */
468 	if (fscanf(filename,"# jobname: %27s%*[^\n]\n",jobname) != 1) {
469 		printf("%.27s\n", "???");
470 		fclose(filename);
471 		return;
472 	}
473 	fclose(filename);
474 
475 	/*
476 	 * Put a pointer at the begining of the line and remove the basename
477 	 * from the job file.
478 	 */
479 	ptr = jobname;
480 	if ((ptr = (char *)rindex(jobname,'/')) != 0)
481 		++ptr;
482 	else
483 		ptr = jobname;
484 
485 	if (strlen(ptr) > 23)
486 		printf("%.23s ...\n",ptr);
487 	else
488 		printf("%.27s\n",ptr);
489 }
490 
491 /*
492  * Do we want to include a file in the queue? (used by "scandir") We are looking
493  * for files with following syntax: yy.ddd.hhhh. so the test is made to see if
494  * the file name has three dots in it. This test will suffice since the only
495  * other files in /usr/spool/at don't have any dots in their name.
496  */
497 filewanted(direntry)
498 struct direct *direntry;
499 {
500 	int numdot = 0;
501 	char *filename;
502 
503 	filename = direntry->d_name;
504 	while (*filename)
505 		numdot += (*(filename++) == '.');
506 	return(numdot == 3);
507 }
508 
509 /*
510  * Sort files by time of creation. (used by "scandir")
511  */
512 creation(d1, d2)
513 struct direct **d1, **d2;
514 {
515 	struct stat stbuf1, stbuf2;
516 
517 	if (stat((*d1)->d_name,&stbuf1) < 0)
518 		return(1);
519 
520 	if (stat((*d2)->d_name,&stbuf2) < 0)
521 		return(1);
522 
523 	return(stbuf1.st_ctime < stbuf2.st_ctime);
524 }
525 
526 /*
527  * Print usage info and exit.
528  */
529 usage()
530 {
531 	fprintf(stderr,"usage:	atq [-c] [-n] [name ...]\n");
532 	exit(1);
533 }
534