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[] = "@(#)at.c 5.5 (Berkeley) 01/18/87"; 15 #endif not lint 16 17 /* 18 * Synopsis: at [-s] [-c] [-m] time [filename] 19 * 20 * 21 * 22 * Execute commands at a later date. 23 * 24 * 25 * Modifications by: Steve Wall 26 * Computer Systems Research Group 27 * University of California @ Berkeley 28 * 29 */ 30 #include <stdio.h> 31 #include <ctype.h> 32 #include <signal.h> 33 #include <pwd.h> 34 #include <sys/param.h> 35 #include <sys/time.h> 36 #include <sys/file.h> 37 38 #define HOUR 100 /* 1 hour (using military time) */ 39 #define HALFDAY (12 * HOUR) /* half a day (12 hours) */ 40 #define FULLDAY (24 * HOUR) /* a full day (24 hours) */ 41 42 #define WEEK 1 /* day requested is 'week' */ 43 #define DAY 2 /* day requested is a weekday */ 44 #define MONTH 3 /* day requested is a month */ 45 46 #define BOURNE "/bin/sh" /* run commands with Bourne shell*/ 47 #define CSHELL "/bin/csh" /* run commands with C shell */ 48 49 #define NODATEFOUND -1 /* no date was given on command line */ 50 51 #define ATDIR "/usr/spool/at" /* spooling area */ 52 53 #define LINSIZ 256 /* length of input buffer */ 54 55 /* 56 * A table to identify potential command line values for "time". 57 * 58 * We need this so that we can do some decent error checking on the 59 * command line arguments. (This was inspired by the old "at", which 60 * accepted "at 900 jan 55" as valid input and other small bugs. 61 */ 62 struct datetypes { 63 int type; 64 char *name; 65 } dates_info[22] = { 66 { DAY, "sunday" }, 67 { DAY, "monday" }, 68 { DAY, "tuesday" }, 69 { DAY, "wednesday" }, 70 { DAY, "thursday" }, 71 { DAY, "friday" }, 72 { DAY, "saturday" }, 73 { MONTH, "january" }, 74 { MONTH, "february" }, 75 { MONTH, "march" }, 76 { MONTH, "april" }, 77 { MONTH, "may" }, 78 { MONTH, "june" }, 79 { MONTH, "july" }, 80 { MONTH, "august" }, 81 { MONTH, "september" }, 82 { MONTH, "october" }, 83 { MONTH, "november" }, 84 { MONTH, "december" }, 85 { 0, ""}, 86 }; 87 88 /* 89 * Months of the year. 90 */ 91 char *months[13] = { 92 "jan", "feb", "mar", "apr", "may", "jun", 93 "jul", "aug", "sep", "oct", "nov", "dec", 0, 94 }; 95 96 /* 97 * A table of the number of days in each month of the year. 98 * 99 * yeartable[0] -- normal year 100 * yeartable[1] -- leap year 101 */ 102 static int yeartable[2][13] = { 103 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 104 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 105 }; 106 107 /* 108 * Structure holding the relevant values needed to create a spoolfile. 109 * "attime" will contain the info about when a job is to be run, and 110 * "nowtime" will contain info about what time the "at" command is in- 111 * voked. 112 */ 113 struct times { 114 int year; /* year that job is to be run */ 115 int yday; /* day of year that job is to be run */ 116 int mon; /* month of year that job is to be run*/ 117 int mday; /* day of month that job is to be run */ 118 int wday; /* day of week that job is to be run */ 119 int hour; /* hour of day that job is to be run */ 120 int min; /* min. of hour that job is to be run */ 121 } attime, nowtime; 122 123 char atfile[100]; /* name of spoolfile "yy.ddd.hhhh.??" */ 124 char *getenv(); /* get info on user's environment */ 125 char **environ; /* user's environment */ 126 FILE *spoolfile; /* spool file */ 127 FILE *inputfile; /* input file ("stdin" or "filename") */ 128 char *getwd(); /* used to get current directory info */ 129 130 131 main(argc, argv) 132 int argc; 133 char **argv; 134 { 135 int c; /* scratch variable */ 136 int usage(); /* print usage info and exit */ 137 int cleanup(); /* do cleanup on an interrupt signal */ 138 int dateindex = NODATEFOUND; /* if a day is specified, what option 139 is it? (mon day, week, dayofweek) */ 140 char *shell = BOURNE; /* what shell do we use to run job? */ 141 int shflag = 0; /* override the current shell and run 142 job using the Bourne Shell */ 143 int cshflag = 0; /* override the current shell and run 144 job using the Cshell */ 145 int mailflag = 0; /* send mail after a job has been run?*/ 146 int standardin = 0; /* are we reading from stardard input */ 147 char *tmp; /* scratch pointer */ 148 char line[LINSIZ]; /* a line from input file */ 149 char pwbuf[MAXPATHLEN]; /* the current working directory */ 150 char *jobfile = "stdin"; /* file containing job to be run */ 151 char *getname(); /* get the login name of a user */ 152 int pid; /* For forking for security reasons */ 153 154 155 156 argv++; argc--; 157 158 /* 159 * Interpret command line flags if they exist. 160 */ 161 while (argc > 0 && **argv == '-') { 162 (*argv)++; 163 while (**argv) switch (*(*argv)++) { 164 165 case 'c' : cshflag++; 166 shell = CSHELL; 167 break; 168 169 case 's' : shflag++; 170 shell = BOURNE; 171 break; 172 173 case 'm' : mailflag++; 174 break; 175 176 default : usage(); 177 178 } 179 --argc, ++argv; 180 } 181 if (shflag && cshflag) { 182 fprintf(stderr,"ambiguous shell request.\n"); 183 exit(1); 184 } 185 186 /* 187 * Get the time it is when "at" is invoked. We set both nowtime and 188 * attime to this value so that as we interpret the time the job is to 189 * be run we can compare the two values to determine such things as 190 * whether of not the job should be run the same day the "at" command 191 * is given, whether a job is to be run next year, etc. 192 */ 193 getnowtime(&nowtime, &attime); 194 195 #ifdef DEBUG 196 printit(); 197 #endif 198 199 if (argc <= 0) 200 usage(); 201 202 /* 203 * Interpret argv[1] and create the time of day that the job is to 204 * be run. This is the same function that was used in the old "at" 205 */ 206 maketime(&attime, *argv); 207 --argc; ++argv; 208 209 #ifdef DEBUG 210 printf("\n\nAFTER MAKETIME\n"); 211 printit(); 212 #endif 213 214 /* 215 * If argv[(2)] exists, this is a request to run a job on a certain 216 * day of year or a certain day of week. 217 * 218 * We send argv to the function "getdateindex" which returns the 219 * index value of the requested day in the table "dates_info" 220 * (see line 50 for table). If 'getdateindex" returns a NODATEFOUND, 221 * then the requested day format was not found in the table (usually 222 * this means that the argument is a "filename"). If the requested 223 * day is found, we continue to process command line arguments. 224 */ 225 if (argc > 0) { 226 if ((dateindex = getdateindex(*argv)) != NODATEFOUND) { 227 228 ++argv; --argc; 229 230 /* 231 * Determine the day of year that the job will be run 232 * depending on the value of argv. 233 */ 234 makedayofyear(dateindex, &argv, &argc); 235 } 236 } 237 238 /* 239 * If we get to this point and "dateindex" is set to NODATEFOUND, 240 * then we are dealing with a request with only a "time" specified 241 * (i.e. at 400p) and perhaps 'week' specified (i.e. at 400p week). 242 * If 'week' is specified, we just set excecution for 7 days in the 243 * future. Otherwise, we need to check to see if the requested time 244 * has already passed for the current day. If it has, then we add 245 * one to the day of year that the job will be executed. 246 */ 247 if (dateindex == NODATEFOUND) { 248 int daysinyear; 249 if ((argc > 0) && (strcmp(*argv,"week") == 0)) { 250 attime.yday += 7; 251 ++argv; --argc; 252 } else if (istomorrow()) 253 ++attime.yday; 254 255 daysinyear = isleap(attime.year) ? 366 : 365; 256 if (attime.yday >= daysinyear) { 257 attime.yday -= daysinyear; 258 ++attime.year; 259 } 260 } 261 262 /* 263 * If no more arguments exist, then we are reading 264 * from standard input. Thus, we set the standard 265 * input flag (++standardin). 266 */ 267 if (argc <= 0) 268 ++standardin; 269 270 271 #ifdef DEBUG 272 printf("\n\nAFTER ADDDAYS\n"); 273 printit(); 274 #endif 275 276 /* 277 * Start off assuming we're going to read from standard input, 278 * but if a filename has been given to read from, we will open it 279 * later. 280 */ 281 inputfile = stdin; 282 283 /* 284 * Create the filename for the spoolfile. 285 */ 286 makeatfile(atfile,attime.year,attime.yday,attime.hour,attime.min); 287 288 /* 289 * Open the spoolfile for writing. 290 */ 291 if ((spoolfile = fopen(atfile, "w")) == NULL){ 292 perror(atfile); 293 exit(1); 294 } 295 296 /* 297 * Make the file not world readable. 298 */ 299 fchmod(fileno(spoolfile), 0400); 300 301 /* 302 * The protection mechanism works like this: 303 * We are running ruid=user, euid=spool owner. So far we have been 304 * messing around in the spool directory, so we needed to run 305 * as the owner of the spool directory. 306 * We now need to switch to the user's effective uid 307 * to simplify permission checking. However, we fork first, 308 * so that we can clean up if interrupted. 309 */ 310 signal(SIGINT, SIG_IGN); 311 pid = fork(); 312 if (pid == -1) { 313 perror("fork"); 314 exit(1); 315 } 316 if (pid) { 317 int wpid, status; 318 319 /* 320 * We are the parent. If the kid has problems, 321 * cleanup the spool directory. 322 */ 323 wpid = wait(&status); 324 if (wpid != pid || status) { 325 cleanup(); 326 exit(1); 327 } 328 /* 329 * The kid should have alread flushed the buffers. 330 */ 331 _exit(0); 332 } 333 334 /* 335 * Exit on interrupt. 336 */ 337 signal(SIGINT, SIG_DFL); 338 339 /* 340 * We are the kid, give up special permissions. 341 */ 342 setuid(getuid()); 343 344 /* 345 * Open the input file with the user's permissions. 346 */ 347 if (!standardin) { 348 jobfile = *argv; 349 if ((inputfile = fopen(jobfile, "r")) == NULL) { 350 perror(jobfile); 351 exit(1); 352 } 353 } 354 355 /* 356 * Determine what shell we should use to run the job. If the user 357 * didn't explicitly request that his/her current shell be over- 358 * ridden (shflag of cshflag) then we use the current shell. 359 */ 360 if ((!shflag) && (!cshflag) && (getenv("SHELL") != NULL)) 361 shell = "$SHELL"; 362 363 /* 364 * Put some standard information at the top of the spoolfile. 365 * This info is used by the other "at"-oriented programs (atq, 366 * atrm, atrun). 367 */ 368 fprintf(spoolfile, "# owner: %.127s\n",getname(getuid())); 369 fprintf(spoolfile, "# jobname: %.127s\n",jobfile); 370 fprintf(spoolfile, "# shell: sh\n"); 371 fprintf(spoolfile, "# notify by mail: %s\n",(mailflag) ? "yes" : "no"); 372 fprintf(spoolfile, "\n"); 373 374 /* 375 * Set the modes for any files created by the job being run. 376 */ 377 c = umask(0); 378 umask(c); 379 fprintf(spoolfile, "umask %.1o\n", c); 380 381 /* 382 * Get the current working directory so we know what directory to 383 * run the job from. 384 */ 385 if (getwd(pwbuf) == NULL) { 386 fprintf(stderr, "at: can't get working directory\n"); 387 exit(1); 388 } 389 fprintf(spoolfile, "cd %s\n", pwbuf); 390 391 /* 392 * Copy the user's environment to the spoolfile. 393 */ 394 if (environ) { 395 copyenvironment(&spoolfile); 396 } 397 398 /* 399 * Put in a line to run the proper shell using the rest of 400 * the file as input. Note that 'exec'ing the shell will 401 * cause sh() to leave a /tmp/sh### file around. This line 402 * depends on the shells allowing EOF to end tagged input. The 403 * quotes also guarantee a quoting of the lines before EOF. 404 */ 405 fprintf(spoolfile, "%s << 'QAZWSXEDCRFVTGBYHNUJMIKOLP'\n", shell); 406 407 /* 408 * Now that we have all the files set up, we can start reading in 409 * the job. 410 */ 411 while (fgets(line, LINSIZ, inputfile) != NULL) 412 fputs(line, spoolfile); 413 414 /* 415 * Close all files and change the mode of the spoolfile. 416 */ 417 fclose(inputfile); 418 fclose(spoolfile); 419 420 exit(0); 421 422 } 423 424 /* 425 * Copy the user's environment to the spoolfile in the syntax of the 426 * Bourne shell. After the environment is set up, the proper shell 427 * will be invoked. 428 */ 429 copyenvironment(spoolfile) 430 FILE **spoolfile; 431 { 432 char *tmp; /* scratch pointer */ 433 char **environptr = environ; /* pointer to an environment setting */ 434 435 while(*environptr) { 436 tmp = *environptr; 437 438 /* 439 * We don't want the termcap or terminal entry so skip them. 440 */ 441 if ((strncmp(tmp,"TERM=",5) == 0) || 442 (strncmp(tmp,"TERMCAP=",8) == 0)) { 443 ++environptr; 444 continue; 445 } 446 447 /* 448 * Set up the proper syntax. 449 */ 450 while (*tmp != '=') 451 fputc(*tmp++,*spoolfile); 452 fputc('=', *spoolfile); 453 fputc('\'' , *spoolfile); 454 ++tmp; 455 456 /* 457 * Now copy the entry. 458 */ 459 while (*tmp) { 460 if (*tmp == '\'') 461 fputs("'\\''", *spoolfile); 462 else if (*tmp == '\n') 463 fputs("\\",*spoolfile); 464 else 465 fputc(*tmp, *spoolfile); 466 ++tmp; 467 } 468 fputc('\'' , *spoolfile); 469 470 /* 471 * We need to "export" environment settings. 472 */ 473 fprintf(*spoolfile, "\nexport "); 474 tmp = *environptr; 475 while (*tmp != '=') 476 fputc(*tmp++,*spoolfile); 477 fputc('\n',*spoolfile); 478 ++environptr; 479 } 480 return; 481 } 482 483 /* 484 * Create the filename for the spoolfile. The format is "yy.ddd.mmmm.??" 485 * where "yy" is the year the job will be run, "ddd" the day of year, 486 * "mmmm" the hour and minute, and "??" a scratch value used to dis- 487 * tinguish between two files that are to be run at the same time. 488 */ 489 makeatfile(atfile,year,dayofyear,hour,minute) 490 int year; 491 int hour; 492 int minute; 493 int dayofyear; 494 char *atfile; 495 { 496 int i; /* scratch variable */ 497 498 for (i=0; ; i += 53) { 499 sprintf(atfile, "%s/%02d.%03d.%02d%02d.%02d", ATDIR, year, 500 dayofyear, hour, minute, (getpid() + i) % 100); 501 502 /* 503 * Make sure that the file name that we've created is unique. 504 */ 505 if (access(atfile, F_OK) == -1) 506 return; 507 } 508 } 509 510 /* 511 * Has the requested time already passed for the currrent day? If so, we 512 * will run the job "tomorrow". 513 */ 514 istomorrow() 515 { 516 if (attime.hour < nowtime.hour) 517 return(1); 518 if ((attime.hour == nowtime.hour) && (attime.min < nowtime.min)) 519 return(1); 520 521 return(0); 522 } 523 524 /* 525 * Debugging wreckage. 526 */ 527 printit() 528 { 529 printf("YEAR\tnowtime: %d\tattime: %d\n",nowtime.year,attime.year); 530 printf("YDAY\tnowtime: %d\tattime: %d\n",nowtime.yday,attime.yday); 531 printf("MON\tnowtime: %d\tattime: %d\n",nowtime.mon,attime.mon); 532 printf("MONDAY\tnowtime: %d\tattime: %d\n",nowtime.mday,attime.mday); 533 printf("WDAY\tnowtime: %d\tattime: %d\n",nowtime.wday,attime.wday); 534 printf("HOUR\tnowtime: %d\tattime: %d\n",nowtime.hour,attime.hour); 535 printf("MIN\tnowtime: %d\tattime: %d\n",nowtime.min,attime.min); 536 } 537 538 /* 539 * Calculate the day of year that the job will be executed. 540 * The av,ac arguments are ptrs to argv,argc; updated as necessary. 541 */ 542 makedayofyear(dateindex, av, ac) 543 int dateindex; 544 char ***av; 545 int *ac; 546 { 547 char **argv = *av; /* imitate argc,argv and update args at end */ 548 int argc = *ac; 549 char *ptr; /* scratch pointer */ 550 struct datetypes *daterequested; /* pointer to information about 551 the type of date option 552 we're dealing with */ 553 554 daterequested = &dates_info[dateindex]; 555 556 /* 557 * If we're dealing with a day of week, determine the number of days 558 * in the future the next day of this type will fall on. Add this 559 * value to "attime.yday". 560 */ 561 if (daterequested->type == DAY) { 562 if (attime.wday < dateindex) 563 attime.yday += dateindex - attime.wday; 564 else if(attime.wday > dateindex) 565 attime.yday += (7 - attime.wday) + dateindex; 566 else attime.yday += 7; 567 } 568 569 /* 570 * If we're dealing with a month and day of month, determine the 571 * day of year that this date will fall on. 572 */ 573 if (daterequested->type == MONTH) { 574 575 /* 576 * If a day of month isn't specified, print a message 577 * and exit. 578 */ 579 if (argc <= 0) { 580 fprintf(stderr,"day of month not specified.\n"); 581 exit(1); 582 } 583 584 /* 585 * Scan the day of month value and make sure that it 586 * has no characters in it. If characters are found or 587 * the day requested is zero, print a message and exit. 588 */ 589 ptr = *argv; 590 while (isdigit(*ptr)) 591 ++ptr; 592 if ((*ptr != '\0') || (atoi(*argv) == 0)) { 593 fprintf(stderr,"\"%s\": illegal day of month\n",*argv); 594 exit(1); 595 } 596 597 /* 598 * Set the month of year and day of month values. Since 599 * the first 7 values in our dateinfo table do not deal 600 * with month names, we subtract 7 from the month of year 601 * value. 602 */ 603 attime.mon = (dateindex - 7); 604 attime.mday = (atoi(*argv) - 1); 605 606 /* 607 * Test the day of month value to make sure that the 608 * value is legal. 609 */ 610 if ((attime.mday + 1) > 611 yeartable[isleap(attime.year)][attime.mon + 1]) { 612 fprintf(stderr,"\"%s\": illegal day of month\n",*argv); 613 exit(1); 614 } 615 616 /* 617 * Finally, we determine the day of year. 618 */ 619 attime.yday = (countdays()); 620 ++argv; --argc; 621 } 622 623 /* 624 * If 'week' is specified, add 7 to the day of year. 625 */ 626 if ((argc > 0) && (strcmp(*argv,"week") == 0)) { 627 attime.yday += 7; 628 ++argv; --argc; 629 } 630 631 /* 632 * Now that all that is done, see if the requested execution time 633 * has already passed for this year, and if it has, set execution 634 * for next year. 635 */ 636 if (isnextyear()) 637 ++attime.year; 638 639 /* 640 * Finally, reflect the updated argc,argv to the caller 641 */ 642 *av = argv; 643 *ac = argc; 644 } 645 646 /* 647 * Should the job be run next year? We check for the following situations: 648 * 649 * 1) the requested time has already passed for the current year. 650 * 2) the day of year is greater than the number of days in the year. 651 * 652 * If either of these tests succeed, we increment "attime.year" by 1. 653 * If #2 is true, we also subtract the number of days in the current year 654 * from "attime.yday". #2 can only occur if someone specifies a job to 655 * be run "tomorrow" on Dec. 31 or if they specify a job to be run a 656 * 'week' later and the date is at least Dec. 24. (I think so anyway) 657 */ 658 isnextyear() 659 { register daysinyear; 660 if (attime.yday < nowtime.yday) 661 return(1); 662 663 if ((attime.yday == nowtime.yday) && (attime.hour < nowtime.hour)) 664 return(1); 665 666 daysinyear = isleap(attime.year) ? 366 : 365; 667 if (attime.yday >= daysinyear) { 668 attime.yday -= daysinyear; 669 return(1); 670 } 671 if (attime.yday > (isleap(attime.year) ? 366 : 365)) { 672 attime.yday -= (isleap(attime.year) ? 366 : 365); 673 return(1); 674 } 675 676 return(0); 677 } 678 679 /* 680 * Determine the day of year given a month and day of month value. 681 */ 682 countdays() 683 { 684 int leap; /* are we dealing with a leap year? */ 685 int dayofyear; /* the day of year after conversion */ 686 int monthofyear; /* the month of year that we are 687 dealing with */ 688 689 /* 690 * Are we dealing with a leap year? 691 */ 692 leap = isleap(attime.year); 693 694 monthofyear = attime.mon; 695 dayofyear = attime.mday; 696 697 /* 698 * Determine the day of year. 699 */ 700 while (monthofyear > 0) 701 dayofyear += yeartable[leap][monthofyear--]; 702 703 return(dayofyear); 704 } 705 706 /* 707 * Is a year a leap year? 708 */ 709 isleap(year) 710 int year; 711 712 { 713 return((year%4 == 0 && year%100 != 0) || year%100 == 0); 714 } 715 716 getdateindex(date) 717 char *date; 718 { 719 int i = 0; 720 struct datetypes *ptr; 721 722 ptr = dates_info; 723 724 for (ptr = dates_info; ptr->type != 0; ptr++, i++) { 725 if (isprefix(date, ptr->name)) 726 return(i); 727 } 728 return(NODATEFOUND); 729 } 730 731 isprefix(prefix, fullname) 732 char *prefix, *fullname; 733 { 734 char ch; 735 char *ptr; 736 char *ptr1; 737 738 ptr = prefix; 739 ptr1 = fullname; 740 741 while (*ptr) { 742 ch = *ptr; 743 if (isupper(ch)) 744 ch = tolower(ch); 745 746 if (ch != *ptr1++) 747 return(0); 748 749 ++ptr; 750 } 751 return(1); 752 } 753 754 getnowtime(nowtime, attime) 755 struct times *nowtime; 756 struct times *attime; 757 { 758 struct tm *now; 759 struct timeval time; 760 struct timezone zone; 761 762 if (gettimeofday(&time,&zone) < 0) { 763 perror("gettimeofday"); 764 exit(1); 765 } 766 now = localtime(&time.tv_sec); 767 768 attime->year = nowtime->year = now->tm_year; 769 attime->yday = nowtime->yday = now->tm_yday; 770 attime->mon = nowtime->mon = now->tm_mon; 771 attime->mday = nowtime->mday = now->tm_mday; 772 attime->wday = nowtime->wday = now->tm_wday; 773 attime->hour = nowtime->hour = now->tm_hour; 774 attime->min = nowtime->min = now->tm_min; 775 } 776 777 /* 778 * This is the same routine used in the old "at", so I won't bother 779 * commenting it. It'll give you an idea of what the code looked 780 * like when I got it. 781 */ 782 maketime(attime,ptr) 783 char *ptr; 784 struct times *attime; 785 { 786 int val; 787 char *p; 788 789 p = ptr; 790 val = 0; 791 while(isdigit(*p)) { 792 val = val*10+(*p++ -'0'); 793 } 794 if (p-ptr < 3) 795 val *= HOUR; 796 797 for (;;) { 798 switch(*p) { 799 800 case ':': 801 ++p; 802 if (isdigit(*p)) { 803 if (isdigit(p[1])) { 804 val +=(10* *p + p[1] - 11*'0'); 805 p += 2; 806 continue; 807 } 808 } 809 fprintf(stderr, "bad time format:\n"); 810 exit(1); 811 812 case 'A': 813 case 'a': 814 if (val >= HALFDAY+HOUR) 815 val = FULLDAY+1; /* illegal */ 816 if (val >= HALFDAY && val <(HALFDAY+HOUR)) 817 val -= HALFDAY; 818 break; 819 820 case 'P': 821 case 'p': 822 if (val >= HALFDAY+HOUR) 823 val = FULLDAY+1; /* illegal */ 824 if (val < HALFDAY) 825 val += HALFDAY; 826 break; 827 828 case 'n': 829 case 'N': 830 if ((val == 0) || (val == HALFDAY)) 831 val = HALFDAY; 832 else 833 val = FULLDAY+1; /* illegal */ 834 break; 835 836 case 'M': 837 case 'm': 838 if ((val == 0) || (val == HALFDAY)) 839 val = 0; 840 else 841 val = FULLDAY+1; /* illegal */ 842 break; 843 844 845 case '\0': 846 case ' ': 847 /* 24 hour time */ 848 if (val == FULLDAY) 849 val -= FULLDAY; 850 break; 851 852 default: 853 fprintf(stderr, "bad time format\n"); 854 exit(1); 855 856 } 857 break; 858 } 859 if (val < 0 || val >= FULLDAY) { 860 fprintf(stderr, "time out of range\n"); 861 exit(1); 862 } 863 if (val%HOUR >= 60) { 864 fprintf(stderr, "illegal minute field\n"); 865 exit(1); 866 } 867 attime->hour = val/HOUR; 868 attime->min = val%HOUR; 869 } 870 871 /* 872 * Get the full login name of a person using his/her user id. 873 */ 874 char * 875 getname(uid) 876 int uid; 877 { 878 struct passwd *pwdinfo; /* password info structure */ 879 char *logname, *getlogin(); 880 881 logname = getlogin(); 882 if (logname == NULL || (pwdinfo = getpwnam(logname)) == NULL || 883 pwdinfo->pw_uid != uid) 884 pwdinfo = getpwuid(uid); 885 if (pwdinfo == 0) { 886 fprintf(stderr, "no name for uid %d?\n", uid); 887 exit(1); 888 } 889 return(pwdinfo->pw_name); 890 } 891 892 /* 893 * Do general cleanup. 894 */ 895 cleanup() 896 { 897 if (unlink(atfile) == -1) 898 perror(atfile); 899 exit(1); 900 } 901 902 /* 903 * Print usage info and exit. 904 */ 905 usage() 906 { 907 fprintf(stderr,"usage: at [-csm] time [date] [filename]\n"); 908 exit(1); 909 } 910 911