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.4 (Berkeley) 5/28/86"; 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=daemon. So far we have been 304: * messing around in the spool directory, so we needed the 305: * daemon stuff. Now, we want to read the users file, 306: * so we must give up the daemon protection, but we might 307: * need the daemon's protection if the user interrupts and 308: * we need to remove the spool files. 309: * So, we fork and let the kid set the real and effective 310: * user id's to the user, so he can read everything of his 311: * own, but not his professor's final exam and not stuff 312: * owned by daemon. If the kid exits with non-zero status, 313: * that means that the user typed interrupt, and the parent 314: * (still with daemon permissions) removes the spool file. 315: */ 316: signal(SIGINT, SIG_IGN); 317: pid = fork(); 318: if (pid == -1) { 319: perror("fork"); 320: exit(1); 321: } 322: if (pid) { 323: int wpid, status; 324: 325: /* 326: * We are the parent. If the kid has problems, 327: * cleanup the spool directory. 328: */ 329: wpid = wait(&status); 330: if (wpid != pid || status) { 331: cleanup(); 332: exit(1); 333: } 334: /* 335: * The kid should have alread flushed the buffers. 336: */ 337: _exit(0); 338: } 339: 340: /* 341: * Exit on interrupt. 342: */ 343: signal(SIGINT, SIG_DFL); 344: 345: /* 346: * We are the kid, give up daemon permissions. 347: */ 348: setuid(getuid()); 349: 350: /* 351: * Open the input file with the user's permissions. 352: */ 353: if (!standardin) { 354: jobfile = *argv; 355: if ((inputfile = fopen(jobfile, "r")) == NULL) { 356: perror(jobfile); 357: exit(1); 358: } 359: } 360: 361: /* 362: * If the inputfile is not from a tty then turn off standardin 363: * If the inputfile is a tty, put out a prompt now, instead of 364: * waiting for a lot of file activity to complete. 365: */ 366: if (!(isatty(fileno(inputfile)))) 367: standardin = 0 ; 368: if (standardin) { 369: fputs("at> ", stdout); 370: fflush(stdout); 371: } 372: 373: /* 374: * Determine what shell we should use to run the job. If the user 375: * didn't explicitly request that his/her current shell be over- 376: * ridden (shflag of cshflag) then we use the current shell. 377: */ 378: if ((!shflag) && (!cshflag) && (getenv("SHELL") != NULL)) 379: shell = "$SHELL"; 380: 381: /* 382: * Put some standard information at the top of the spoolfile. 383: * This info is used by the other "at"-oriented programs (atq, 384: * atrm, atrun). 385: */ 386: fprintf(spoolfile, "# owner: %.127s\n",getname(getuid())); 387: fprintf(spoolfile, "# jobname: %.127s\n",jobfile); 388: fprintf(spoolfile, "# shell: sh\n"); 389: fprintf(spoolfile, "# notify by mail: %s\n",(mailflag) ? "yes" : "no"); 390: fprintf(spoolfile, "\n"); 391: 392: /* 393: * Set the modes for any files created by the job being run. 394: */ 395: c = umask(0); 396: umask(c); 397: fprintf(spoolfile, "umask %.1o\n", c); 398: 399: /* 400: * Get the current working directory so we know what directory to 401: * run the job from. 402: */ 403: if (getwd(pwbuf) == NULL) { 404: fprintf(stderr, "at: can't get working directory\n"); 405: exit(1); 406: } 407: fprintf(spoolfile, "cd %s\n", pwbuf); 408: 409: /* 410: * Copy the user's environment to the spoolfile. 411: */ 412: if (environ) { 413: copyenvironment(&spoolfile); 414: } 415: 416: /* 417: * Put in a line to run the proper shell using the rest of 418: * the file as input. Note that 'exec'ing the shell will 419: * cause sh() to leave a /tmp/sh### file around. 420: */ 421: fprintf(spoolfile, 422: "%s << '...the rest of this file is shell input'\n", shell); 423: 424: /* 425: * Now that we have all the files set up, we can start reading in 426: * the job. (I added the prompt "at>" so that the user could tell 427: * when/if he/she was supposed to enter commands from standard 428: * input. The old "at" just sat there and didn't send any kind of 429: * message that said it was waiting for input if it was reading 430: * form standard input). 431: */ 432: while (fgets(line, LINSIZ, inputfile) != NULL) { 433: fputs(line, spoolfile); 434: if (standardin) 435: fputs("at> ", stdout); 436: } 437: if (standardin) 438: fputs("<EOT>\n", stdout); /* clean up the final output */ 439: 440: /* 441: * Close all files and change the mode of the spoolfile. 442: */ 443: fclose(inputfile); 444: fclose(spoolfile); 445: 446: exit(0); 447: 448: } 449: 450: /* 451: * Copy the user's environment to the spoolfile in the syntax of the 452: * Bourne shell. After the environment is set up, the proper shell 453: * will be invoked. 454: */ 455: copyenvironment(spoolfile) 456: FILE **spoolfile; 457: { 458: char *tmp; /* scratch pointer */ 459: char **environptr = environ; /* pointer to an environment setting */ 460: 461: while(*environptr) { 462: tmp = *environptr; 463: 464: /* 465: * We don't want the termcap or terminal entry so skip them. 466: */ 467: if ((strncmp(tmp,"TERM=",5) == 0) || 468: (strncmp(tmp,"TERMCAP=",8) == 0)) { 469: ++environptr; 470: continue; 471: } 472: 473: /* 474: * Set up the proper syntax. 475: */ 476: while (*tmp != '=') 477: fputc(*tmp++,*spoolfile); 478: fputc('=', *spoolfile); 479: fputc('\'' , *spoolfile); 480: ++tmp; 481: 482: /* 483: * Now copy the entry. 484: */ 485: while (*tmp) { 486: if (*tmp == '\'') 487: fputs("'\\''", *spoolfile); 488: else if (*tmp == '\n') 489: fputs("\\",*spoolfile); 490: else 491: fputc(*tmp, *spoolfile); 492: ++tmp; 493: } 494: fputc('\'' , *spoolfile); 495: 496: /* 497: * We need to "export" environment settings. 498: */ 499: fprintf(*spoolfile, "\nexport "); 500: tmp = *environptr; 501: while (*tmp != '=') 502: fputc(*tmp++,*spoolfile); 503: fputc('\n',*spoolfile); 504: ++environptr; 505: } 506: return; 507: } 508: 509: /* 510: * Create the filename for the spoolfile. The format is "yy.ddd.mmmm.??" 511: * where "yy" is the year the job will be run, "ddd" the day of year, 512: * "mmmm" the hour and minute, and "??" a scratch value used to dis- 513: * tinguish between two files that are to be run at the same time. 514: */ 515: makeatfile(atfile,year,dayofyear,hour,minute) 516: int year; 517: int hour; 518: int minute; 519: int dayofyear; 520: char *atfile; 521: { 522: int i; /* scratch variable */ 523: 524: for (i=0; ; i += 53) { 525: sprintf(atfile, "%s/%02d.%03d.%02d%02d.%02d", ATDIR, year, 526: dayofyear, hour, minute, (getpid() + i) % 100); 527: 528: /* 529: * Make sure that the file name that we've created is unique. 530: */ 531: if (access(atfile, F_OK) == -1) 532: return; 533: } 534: } 535: 536: /* 537: * Has the requested time already passed for the currrent day? If so, we 538: * will run the job "tomorrow". 539: */ 540: istomorrow() 541: { 542: if (attime.hour < nowtime.hour) 543: return(1); 544: if ((attime.hour == nowtime.hour) && (attime.min < nowtime.min)) 545: return(1); 546: 547: return(0); 548: } 549: 550: /* 551: * Debugging wreckage. 552: */ 553: printit() 554: { 555: printf("YEAR\tnowtime: %d\tattime: %d\n",nowtime.year,attime.year); 556: printf("YDAY\tnowtime: %d\tattime: %d\n",nowtime.yday,attime.yday); 557: printf("MON\tnowtime: %d\tattime: %d\n",nowtime.mon,attime.mon); 558: printf("MONDAY\tnowtime: %d\tattime: %d\n",nowtime.mday,attime.mday); 559: printf("WDAY\tnowtime: %d\tattime: %d\n",nowtime.wday,attime.wday); 560: printf("HOUR\tnowtime: %d\tattime: %d\n",nowtime.hour,attime.hour); 561: printf("MIN\tnowtime: %d\tattime: %d\n",nowtime.min,attime.min); 562: } 563: 564: /* 565: * Calculate the day of year that the job will be executed. 566: * The av,ac arguments are ptrs to argv,argc; updated as necessary. 567: */ 568: makedayofyear(dateindex, av, ac) 569: int dateindex; 570: char ***av; 571: int *ac; 572: { 573: char **argv = *av; /* imitate argc,argv and update args at end */ 574: int argc = *ac; 575: char *ptr; /* scratch pointer */ 576: struct datetypes *daterequested; /* pointer to information about 577: the type of date option 578: we're dealing with */ 579: 580: daterequested = &dates_info[dateindex]; 581: 582: /* 583: * If we're dealing with a day of week, determine the number of days 584: * in the future the next day of this type will fall on. Add this 585: * value to "attime.yday". 586: */ 587: if (daterequested->type == DAY) { 588: if (attime.wday < dateindex) 589: attime.yday += dateindex - attime.wday; 590: else if(attime.wday > dateindex) 591: attime.yday += (7 - attime.wday) + dateindex; 592: else attime.yday += 7; 593: } 594: 595: /* 596: * If we're dealing with a month and day of month, determine the 597: * day of year that this date will fall on. 598: */ 599: if (daterequested->type == MONTH) { 600: 601: /* 602: * If a day of month isn't specified, print a message 603: * and exit. 604: */ 605: if (argc <= 0) { 606: fprintf(stderr,"day of month not specified.\n"); 607: exit(1); 608: } 609: 610: /* 611: * Scan the day of month value and make sure that it 612: * has no characters in it. If characters are found or 613: * the day requested is zero, print a message and exit. 614: */ 615: ptr = *argv; 616: while (isdigit(*ptr)) 617: ++ptr; 618: if ((*ptr != '\0') || (atoi(*argv) == 0)) { 619: fprintf(stderr,"\"%s\": illegal day of month\n",*argv); 620: exit(1); 621: } 622: 623: /* 624: * Set the month of year and day of month values. Since 625: * the first 7 values in our dateinfo table do not deal 626: * with month names, we subtract 7 from the month of year 627: * value. 628: */ 629: attime.mon = (dateindex - 7); 630: attime.mday = (atoi(*argv) - 1); 631: 632: /* 633: * Test the day of month value to make sure that the 634: * value is legal. 635: */ 636: if ((attime.mday + 1) > 637: yeartable[isleap(attime.year)][attime.mon + 1]) { 638: fprintf(stderr,"\"%s\": illegal day of month\n",*argv); 639: exit(1); 640: } 641: 642: /* 643: * Finally, we determine the day of year. 644: */ 645: attime.yday = (countdays()); 646: ++argv; --argc; 647: } 648: 649: /* 650: * If 'week' is specified, add 7 to the day of year. 651: */ 652: if ((argc > 0) && (strcmp(*argv,"week") == 0)) { 653: attime.yday += 7; 654: ++argv; --argc; 655: } 656: 657: /* 658: * Now that all that is done, see if the requested execution time 659: * has already passed for this year, and if it has, set execution 660: * for next year. 661: */ 662: if (isnextyear()) 663: ++attime.year; 664: 665: /* 666: * Finally, reflect the updated argc,argv to the caller 667: */ 668: *av = argv; 669: *ac = argc; 670: } 671: 672: /* 673: * Should the job be run next year? We check for the following situations: 674: * 675: * 1) the requested time has already passed for the current year. 676: * 2) the day of year is greater than the number of days in the year. 677: * 678: * If either of these tests succeed, we increment "attime.year" by 1. 679: * If #2 is true, we also subtract the number of days in the current year 680: * from "attime.yday". #2 can only occur if someone specifies a job to 681: * be run "tomorrow" on Dec. 31 or if they specify a job to be run a 682: * 'week' later and the date is at least Dec. 24. (I think so anyway) 683: */ 684: isnextyear() 685: { register daysinyear; 686: if (attime.yday < nowtime.yday) 687: return(1); 688: 689: if ((attime.yday == nowtime.yday) && (attime.hour < nowtime.hour)) 690: return(1); 691: 692: daysinyear = isleap(attime.year) ? 366 : 365; 693: if (attime.yday >= daysinyear) { 694: attime.yday -= daysinyear; 695: return(1); 696: } 697: if (attime.yday > (isleap(attime.year) ? 366 : 365)) { 698: attime.yday -= (isleap(attime.year) ? 366 : 365); 699: return(1); 700: } 701: 702: return(0); 703: } 704: 705: /* 706: * Determine the day of year given a month and day of month value. 707: */ 708: countdays() 709: { 710: int leap; /* are we dealing with a leap year? */ 711: int dayofyear; /* the day of year after conversion */ 712: int monthofyear; /* the month of year that we are 713: dealing with */ 714: 715: /* 716: * Are we dealing with a leap year? 717: */ 718: leap = isleap(attime.year); 719: 720: monthofyear = attime.mon; 721: dayofyear = attime.mday; 722: 723: /* 724: * Determine the day of year. 725: */ 726: while (monthofyear > 0) 727: dayofyear += yeartable[leap][monthofyear--]; 728: 729: return(dayofyear); 730: } 731: 732: /* 733: * Is a year a leap year? 734: */ 735: isleap(year) 736: int year; 737: 738: { 739: return((year%4 == 0 && year%100 != 0) || year%100 == 0); 740: } 741: 742: getdateindex(date) 743: char *date; 744: { 745: int i = 0; 746: struct datetypes *ptr; 747: 748: ptr = dates_info; 749: 750: for (ptr = dates_info; ptr->type != 0; ptr++, i++) { 751: if (isprefix(date, ptr->name)) 752: return(i); 753: } 754: return(NODATEFOUND); 755: } 756: 757: isprefix(prefix, fullname) 758: char *prefix, *fullname; 759: { 760: char ch; 761: char *ptr; 762: char *ptr1; 763: 764: ptr = prefix; 765: ptr1 = fullname; 766: 767: while (*ptr) { 768: ch = *ptr; 769: if (isupper(ch)) 770: ch = tolower(ch); 771: 772: if (ch != *ptr1++) 773: return(0); 774: 775: ++ptr; 776: } 777: return(1); 778: } 779: 780: getnowtime(nowtime, attime) 781: struct times *nowtime; 782: struct times *attime; 783: { 784: struct tm *now; 785: struct timeval time; 786: struct timezone zone; 787: 788: if (gettimeofday(&time,&zone) < 0) { 789: perror("gettimeofday"); 790: exit(1); 791: } 792: now = localtime(&time.tv_sec); 793: 794: attime->year = nowtime->year = now->tm_year; 795: attime->yday = nowtime->yday = now->tm_yday; 796: attime->mon = nowtime->mon = now->tm_mon; 797: attime->mday = nowtime->mday = now->tm_mday; 798: attime->wday = nowtime->wday = now->tm_wday; 799: attime->hour = nowtime->hour = now->tm_hour; 800: attime->min = nowtime->min = now->tm_min; 801: } 802: 803: /* 804: * This is the same routine used in the old "at", so I won't bother 805: * commenting it. It'll give you an idea of what the code looked 806: * like when I got it. 807: */ 808: maketime(attime,ptr) 809: char *ptr; 810: struct times *attime; 811: { 812: int val; 813: char *p; 814: 815: p = ptr; 816: val = 0; 817: while(isdigit(*p)) { 818: val = val*10+(*p++ -'0'); 819: } 820: if (p-ptr < 3) 821: val *= HOUR; 822: 823: for (;;) { 824: switch(*p) { 825: 826: case ':': 827: ++p; 828: if (isdigit(*p)) { 829: if (isdigit(p[1])) { 830: val +=(10* *p + p[1] - 11*'0'); 831: p += 2; 832: continue; 833: } 834: } 835: fprintf(stderr, "bad time format:\n"); 836: exit(1); 837: 838: case 'A': 839: case 'a': 840: if (val >= HALFDAY+HOUR) 841: val = FULLDAY+1; /* illegal */ 842: if (val >= HALFDAY && val <(HALFDAY+HOUR)) 843: val -= HALFDAY; 844: break; 845: 846: case 'P': 847: case 'p': 848: if (val >= HALFDAY+HOUR) 849: val = FULLDAY+1; /* illegal */ 850: if (val < HALFDAY) 851: val += HALFDAY; 852: break; 853: 854: case 'n': 855: case 'N': 856: if ((val == 0) || (val == HALFDAY)) 857: val = HALFDAY; 858: else 859: val = FULLDAY+1; /* illegal */ 860: break; 861: 862: case 'M': 863: case 'm': 864: if ((val == 0) || (val == HALFDAY)) 865: val = 0; 866: else 867: val = FULLDAY+1; /* illegal */ 868: break; 869: 870: 871: case '\0': 872: case ' ': 873: /* 24 hour time */ 874: if (val == FULLDAY) 875: val -= FULLDAY; 876: break; 877: 878: default: 879: fprintf(stderr, "bad time format\n"); 880: exit(1); 881: 882: } 883: break; 884: } 885: if (val < 0 || val >= FULLDAY) { 886: fprintf(stderr, "time out of range\n"); 887: exit(1); 888: } 889: if (val%HOUR >= 60) { 890: fprintf(stderr, "illegal minute field\n"); 891: exit(1); 892: } 893: attime->hour = val/HOUR; 894: attime->min = val%HOUR; 895: } 896: 897: /* 898: * Get the full login name of a person using his/her user id. 899: */ 900: char * 901: getname(uid) 902: int uid; 903: { 904: struct passwd *pwdinfo; /* password info structure */ 905: 906: 907: if ((pwdinfo = getpwuid(uid)) == 0) { 908: perror(uid); 909: exit(1); 910: } 911: return(pwdinfo->pw_name); 912: } 913: 914: /* 915: * Do general cleanup. 916: */ 917: cleanup() 918: { 919: if (unlink(atfile) == -1) 920: perror(atfile); 921: exit(1); 922: } 923: 924: /* 925: * Print usage info and exit. 926: */ 927: usage() 928: { 929: fprintf(stderr,"usage: at [-csm] time [date] [filename]\n"); 930: exit(1); 931: }