From 33c18cbb0f00540555e433aa21bf2d187ffef73f Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Fri, 23 Dec 1994 02:06:37 +0000 Subject: [PATCH] fixes as per OV suggestions git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@4749 dc483132-0cff-0310-8789-dd5450dbe970 --- src/kadmin.new/client/ChangeLog | 5 + src/kadmin.new/client/Makefile.in | 8 +- src/kadmin.new/client/configure.in | 2 +- src/kadmin.new/client/kadmin.c | 221 +++++++++++--- src/kadmin.new/client/kadmin_ct.ct | 3 + src/kadmin.new/client/ss_wrapper.c | 1 + src/kadmin.new/client/strftime.c | 469 +++++++++++++++++++++++++++++ 7 files changed, 655 insertions(+), 54 deletions(-) create mode 100644 src/kadmin.new/client/ChangeLog create mode 100644 src/kadmin.new/client/strftime.c diff --git a/src/kadmin.new/client/ChangeLog b/src/kadmin.new/client/ChangeLog new file mode 100644 index 000000000..0303a7693 --- /dev/null +++ b/src/kadmin.new/client/ChangeLog @@ -0,0 +1,5 @@ +Thu Dec 22 20:55:05 1994 Tom Yu (tlyu@dragons-lair) + + * kadmin.c: lots of bug fixes, fixing addprinc -randkey, cosmetic + changes to getprinc, etc. + diff --git a/src/kadmin.new/client/Makefile.in b/src/kadmin.new/client/Makefile.in index 160016af9..2a4e41c85 100644 --- a/src/kadmin.new/client/Makefile.in +++ b/src/kadmin.new/client/Makefile.in @@ -1,13 +1,13 @@ CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) $(OVSECINC) -OVSECROOT=/home/tlyu/ovsecure -OVSECSTAGE=/afs/dev.mit.edu/reference/ovsecure/sunos/stage +OVSECROOT=/home/tlyu/ovsecure/install +OVSECSTAGE=/home/tlyu/ovsecure/stage OVSECINC=-I$(OVSECROOT)/include -I$(OVSECSTAGE)/include -OVSECLIB=-L$(OVSECSTAGE)/lib -lclient -lcommon -lrpclib -ldyn +OVSECLIB=-L$(OVSECSTAGE)/lib -ladmclnt -lrpclib -ldyn LDFLAGS = -g LIBOBJS=@LIBOBJS@ ISODELIB=$(OVSECROOT)/lib/libisode.a COMERRLIB=$(OVSECROOT)/lib/libcom_err.a -SSLIB=$(OVSECSTAGE)/lib/libss.a +SSLIB=$(BUILDTOP)/util/ss/libss.a DBMLIB=$(OVSECROOT)/lib/libdb.a KDBLIB=$(OVSECROOT)/lib/libkdb5.a diff --git a/src/kadmin.new/client/configure.in b/src/kadmin.new/client/configure.in index 6528c80e1..3ff3f9646 100644 --- a/src/kadmin.new/client/configure.in +++ b/src/kadmin.new/client/configure.in @@ -8,7 +8,7 @@ AC_HAVE_HEADERS(unistd.h sys/timeb.h alloca.h) AC_HAVE_FUNCS(ftime timezone) AC_CHECK_LIB(ndbm,main) AC_CHECK_LIB(dbm,main) -AC_REPLACE_FUNCS([setenv memmove]) +AC_REPLACE_FUNCS([setenv memmove strftime]) SS_RULES KRB_INCLUDE ISODE_INCLUDE diff --git a/src/kadmin.new/client/kadmin.c b/src/kadmin.new/client/kadmin.c index f2f30b09f..33c9fce34 100644 --- a/src/kadmin.new/client/kadmin.c +++ b/src/kadmin.new/client/kadmin.c @@ -52,7 +52,7 @@ static struct pflag flags[] = { {"allow_dup_skey", 14, KRB5_KDB_DISALLOW_DUP_SKEY, 1}, {"allow_tix", 9, KRB5_KDB_DISALLOW_ALL_TIX, 1}, {"requires_preauth", 16, KRB5_KDB_REQUIRES_PRE_AUTH, 0}, -{"requres_hwauth", 14, KRB5_KDB_REQUIRES_HW_AUTH, 0}, +{"requires_hwauth", 15, KRB5_KDB_REQUIRES_HW_AUTH, 0}, {"needchange", 10, KRB5_KDB_REQUIRES_PWCHANGE, 0}, {"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1}, {"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 } @@ -79,6 +79,9 @@ char *getenv(); struct passwd *getpwuid(); int exit_status = 0; char *def_realm = NULL; +time_t get_date(); + +void *ovsec_hndl = NULL; void usage() { @@ -87,6 +90,35 @@ void usage() exit(1); } +char *strdur(duration) + time_t duration; +{ + static char out[50]; + int days, hours, minutes, seconds; + + days = duration / (24 * 3600); + duration %= 24 * 3600; + hours = duration / 3600; + duration %= 3600; + minutes = duration / 60; + duration %= 60; + seconds = duration; + sprintf(out, "%d %s %02d:%02d:%02d", days, days == 1 ? "day" : "days", + hours, minutes, seconds); + return out; +} + +char *strdate(when) + time_t when; +{ + struct tm *tm; + static char out[30]; + + tm = localtime(&when); + strftime(out, 30, "%a %b %d %H:%M:%S %Z %Y", tm); + return out; +} + /* this is a wrapper to go around krb5_parse_principal so we can set the default realm up properly */ krb5_error_code kadmin_parse_name(name, principal) @@ -226,8 +258,12 @@ char *kadmin_startup(argc, argv) exit(1); } } - retval = ovsec_kadm_init(princstr, NULL, OVSEC_KADM_ADMIN_SERVICE, - def_realm); + retval = ovsec_kadm_init_with_password(princstr, NULL, + OVSEC_KADM_ADMIN_SERVICE, + def_realm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &ovsec_hndl); if (freeprinc) free(princstr); if (retval) { /* assume kadm_init does init_ets() */ @@ -239,7 +275,7 @@ char *kadmin_startup(argc, argv) int quit() { - ovsec_kadm_destroy(); + ovsec_kadm_destroy(ovsec_hndl); /* insert more random cleanup here */ } @@ -283,7 +319,7 @@ void kadmin_delprinc(argc, argv) return; } } - retval = ovsec_kadm_delete_principal(princ); + retval = ovsec_kadm_delete_principal(ovsec_hndl, princ); krb5_free_principal(princ); if (retval) { com_err("delete_principal", retval, @@ -357,7 +393,7 @@ void kadmin_renprinc(argc, argv) return; } } - retval = ovsec_kadm_rename_principal(oldprinc, newprinc); + retval = ovsec_kadm_rename_principal(ovsec_hndl, oldprinc, newprinc); krb5_free_principal(oldprinc); krb5_free_principal(newprinc); if (retval) { @@ -399,7 +435,7 @@ void kadmin_cpw(argc, argv) return; } if ((argc == 4) && (strlen(argv[1]) == 3) && !strcmp("-pw", argv[1])) { - retval = ovsec_kadm_chpass_principal(princ, argv[2]); + retval = ovsec_kadm_chpass_principal(ovsec_hndl, princ, argv[2]); krb5_free_principal(princ); if (retval) { com_err("change_password", retval, @@ -413,7 +449,7 @@ void kadmin_cpw(argc, argv) } else if ((argc == 3) && (strlen(argv[1]) == 8) && !strcmp("-randkey", argv[1])) { krb5_keyblock *newkey = NULL; - retval = ovsec_kadm_randkey_principal(princ, &newkey); + retval = ovsec_kadm_randkey_principal(ovsec_hndl, princ, &newkey); krb5_free_principal(princ); if (retval) { com_err("change_password", retval, @@ -422,6 +458,7 @@ void kadmin_cpw(argc, argv) return; } memset(newkey->contents, 0, newkey->length); + krb5_free_keyblock(newkey); printf("Key for \"%s\" randomized.\n", canon); free(canon); return; @@ -442,7 +479,7 @@ void kadmin_cpw(argc, argv) krb5_free_principal(princ); return; } - retval = ovsec_kadm_chpass_principal(princ, newpw); + retval = ovsec_kadm_chpass_principal(ovsec_hndl, princ, newpw); krb5_free_principal(princ); memset(newpw, 0, sizeof (newpw)); if (retval) { @@ -461,27 +498,33 @@ void kadmin_cpw(argc, argv) return; } -int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, caller) +int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, randkey, caller) int argc; char *argv[]; ovsec_kadm_principal_ent_t oprinc; u_int32 *mask; - char **pass, *caller; + char **pass; + int *randkey; + char *caller; { - int i, j; + int i, j, attrib_set; + time_t date; struct timeb now; krb5_error_code retval; *mask = 0; *pass = NULL; ftime(&now); + *randkey = 0; for (i = 1; i < argc - 1; i++) { + attrib_set = 0; if (strlen(argv[i]) == 7 && !strcmp("-expire", argv[i])) { if (++i > argc - 2) return -1; else { - oprinc->princ_expire_time = get_date(argv[i], now); + date = get_date(argv[i], now); + oprinc->princ_expire_time = date == (time_t)-1 ? 0 : date; *mask |= OVSEC_KADM_PRINC_EXPIRE_TIME; continue; } @@ -491,7 +534,8 @@ int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, caller) if (++i > argc - 2) return -1; else { - oprinc->pw_expiration = get_date(argv[i], now); + date = get_date(argv[i], now); + oprinc->pw_expiration = date == (time_t)-1 ? 0 : date; *mask |= OVSEC_KADM_PW_EXPIRATION; continue; } @@ -541,6 +585,11 @@ int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, caller) continue; } } + if (strlen(argv[i]) == 8 && + !strcmp("-randkey", argv[i])) { + ++*randkey; + continue; + } for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) { if (strlen(argv[i]) == flags[j].flaglen + 1 && !strcmp(flags[j].flagname, @@ -549,18 +598,21 @@ int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, caller) !flags[j].set && argv[i][0] == '+') { oprinc->attributes |= flags[j].theflag; *mask |= OVSEC_KADM_ATTRIBUTES; + attrib_set++; break; } else if (flags[j].set && argv[i][0] == '+' || !flags[j].set && argv[i][0] == '-') { oprinc->attributes &= ~flags[j].theflag; *mask |= OVSEC_KADM_ATTRIBUTES; + attrib_set++; break; } else { return -1; } } } - return -1; + if (!attrib_set) + return -1; /* nothing was parsed */ } if (i != argc - 1) { fprintf(stderr, "%s: parser lost count!\n", caller); @@ -580,6 +632,7 @@ void kadmin_addprinc(argc, argv) { ovsec_kadm_principal_ent_rec princ; u_int32 mask; + int randkey = 0; char *pass, *canon; krb5_error_code retval; static char newpw[1024]; @@ -587,7 +640,8 @@ void kadmin_addprinc(argc, argv) princ.attributes = 0; if (kadmin_parse_princ_args(argc, argv, - &princ, &mask, &pass, "add_principal")) { + &princ, &mask, &pass, &randkey, + "add_principal")) { fprintf(stderr, "add_principal: bad arguments\n"); return; } @@ -598,14 +652,18 @@ void kadmin_addprinc(argc, argv) krb5_free_principal(princ.principal); return; } - if (pass == NULL) { + if (randkey) { /* do special stuff if -randkey specified */ + princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; /* set notix */ + mask |= OVSEC_KADM_ATTRIBUTES; + pass = "dummy"; + } else if (pass == NULL) { int i = sizeof (newpw) - 1; sprintf(prompt1, "Enter password for principal \"%.900s\": ", - argv[1]); + canon); sprintf(prompt2, "Re-enter password for principal \"%.900s\": ", - argv[1]); + canon); retval = krb5_read_password(prompt1, prompt2, newpw, &i); if (retval) { @@ -618,14 +676,39 @@ void kadmin_addprinc(argc, argv) pass = newpw; } mask |= OVSEC_KADM_PRINCIPAL; - retval = ovsec_kadm_create_principal(&princ, mask, pass); - krb5_free_principal(princ.principal); + retval = ovsec_kadm_create_principal(ovsec_hndl, &princ, mask, pass); if (retval) { com_err("add_principal", retval, "while creating \"%s\".", canon); + krb5_free_principal(princ.principal); free(canon); return; } + if (randkey) { /* more special stuff for -randkey */ + krb5_keyblock *newkey = NULL; + retval = ovsec_kadm_randkey_principal(ovsec_hndl, princ.principal, + &newkey); + if (retval) { + com_err("add_principal", retval, + "while randomizing key for \"%s\".", canon); + krb5_free_principal(princ.principal); + free(canon); + return; + } + memset(newkey->contents, 0, newkey->length); + krb5_free_keyblock(newkey); + princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; /* clear notix */ + mask = OVSEC_KADM_ATTRIBUTES; + retval = ovsec_kadm_modify_principal(ovsec_hndl, &princ, mask); + if (retval) { + com_err("add_principal", retval, + "while clearing DISALLOW_ALL_TIX for \"%s\".", canon); + krb5_free_principal(princ.principal); + free(canon); + return; + } + } + krb5_free_principal(princ.principal); printf("Principal \"%s\" created.\n", canon); free(canon); } @@ -635,29 +718,61 @@ void kadmin_modprinc(argc, argv) char *argv[]; { ovsec_kadm_principal_ent_rec princ; + ovsec_kadm_principal_ent_t oldprinc; + krb5_principal kprinc; u_int32 mask; krb5_error_code retval; char *pass, *canon; - - princ.attributes = 0; - if (kadmin_parse_princ_args(argc, argv, - &princ, &mask, &pass, "modify_principal")) { - fprintf(stderr, "modify_principal: bad arguments\n"); + int randkey = 0; + + retval = kadmin_parse_name(argv[argc - 1], &kprinc); + if (retval) { + com_err("modify_principal", retval, "while parsing principal"); return; } - retval = krb5_unparse_name(princ.principal, &canon); + retval = krb5_unparse_name(kprinc, &canon); if (retval) { com_err("modify_principal", retval, "while canonicalizing principal"); + krb5_free_principal(kprinc); + return; + } + retval = ovsec_kadm_get_principal(ovsec_hndl, kprinc, &oldprinc); + krb5_free_principal(kprinc); + if (retval) { + com_err("modify_principal", retval, "while getting \"%s\".", + canon); + free(canon); + return; + } + princ.attributes = oldprinc->attributes; + ovsec_kadm_free_principal_ent(ovsec_hndl, oldprinc); + retval = kadmin_parse_princ_args(argc, argv, + &princ, &mask, + &pass, &randkey, + "modify_principal"); + if (retval) { + fprintf(stderr, "modify_principal: bad arguments\n"); krb5_free_principal(princ.principal); + free(canon); return; } - retval = ovsec_kadm_modify_principal(&princ, mask); + if (randkey) { + fprintf(stderr, "modify_principal: -randkey not allowed\n"); + krb5_free_principal(princ.principal); + free(canon); + return; + } + retval = ovsec_kadm_modify_principal(ovsec_hndl, &princ, mask); + krb5_free_principal(princ.principal); if (retval) { - com_err("modify_principal", retval, "while modifying \"%s\".", - argv[argc - 1]); + com_err("modify_principal", retval, + "while modifying \"%s\".", canon); + free(canon); return; } + printf("Principal \"%s\" modified.\n", canon); + free(canon); } void kadmin_getprinc(argc, argv) @@ -690,7 +805,7 @@ void kadmin_getprinc(argc, argv) krb5_free_principal(princ); return; } - retval = ovsec_kadm_get_principal(princ, &dprinc); + retval = ovsec_kadm_get_principal(ovsec_hndl, princ, &dprinc); krb5_free_principal(princ); if (retval) { com_err("get_principal", retval, "while retrieving \"%s\".", canon); @@ -700,19 +815,22 @@ void kadmin_getprinc(argc, argv) retval = krb5_unparse_name(dprinc->mod_name, &modcanon); if (retval) { com_err("get_principal", retval, "while unparsing modname"); - ovsec_kadm_free_principal_ent(dprinc); + ovsec_kadm_free_principal_ent(ovsec_hndl, dprinc); free(canon); return; } if (argc == 2) { printf("Principal: %s\n", canon); - printf("Expiration date: %d\n", dprinc->princ_expire_time); - printf("Last password change: %d\n", dprinc->last_pwd_change); - printf("Password expiration date: %d\n", dprinc->pw_expiration); - printf("Maximum life: %d\n", dprinc->max_life); - printf("Last modified: by %s\n\ton %d\n", - modcanon, dprinc->mod_date); - printf("Attributes: "); + printf("Expiration date: %s\n", strdate(dprinc->princ_expire_time)); + printf("Last password change: %s\n", + strdate(dprinc->last_pwd_change)); + printf("Password expiration date: %s\n", + dprinc->pw_expiration ? + strdate(dprinc->pw_expiration) : "[none]"); + printf("Maximum life: %s\n", strdur(dprinc->max_life)); + printf("Last modified: by %s\n\ton %s\n", + modcanon, strdate(dprinc->mod_date)); + printf("Attributes:"); for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) { if (dprinc->attributes & (krb5_flags) 1 << i) printf(" %s", prflags[i]); @@ -720,7 +838,7 @@ void kadmin_getprinc(argc, argv) printf("\n"); printf("Key version: %d\n", dprinc->kvno); printf("Master key version: %d\n", dprinc->mkvno); - printf("Policy: %s\n", dprinc->policy); + printf("Policy: %s\n", dprinc->policy ? dprinc->policy : "[none]"); } else { printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\n", canon, dprinc->princ_expire_time, dprinc->last_pwd_change, @@ -729,7 +847,7 @@ void kadmin_getprinc(argc, argv) dprinc->mkvno, dprinc->policy); } free(modcanon); - ovsec_kadm_free_principal_ent(dprinc); + ovsec_kadm_free_principal_ent(ovsec_hndl, dprinc); free(canon); } @@ -742,6 +860,7 @@ int kadmin_parse_policy_args(argc, argv, policy, mask, caller) { int i; struct timeb now; + time_t date; krb5_error_code retval; ftime(&now); @@ -752,7 +871,9 @@ int kadmin_parse_policy_args(argc, argv, policy, mask, caller) if (++i > argc -2) return -1; else { - policy->pw_max_life = get_date(argv[i], now) - now.time; + date = get_date(argv[i], now); + policy->pw_max_life = + (date == (time_t)-1 ? 0 : date) - now.time; *mask |= OVSEC_KADM_PW_MAX_LIFE; continue; } @@ -761,7 +882,9 @@ int kadmin_parse_policy_args(argc, argv, policy, mask, caller) if (++i > argc - 2) return -1; else { - policy->pw_min_life = get_date(argv[i], now) - now.time; + date = get_date(argv[i], now); + policy->pw_min_life = + (date == (time_t)-1 ? 0 : date) - now.time; *mask |= OVSEC_KADM_PW_MIN_LIFE; continue; } @@ -816,7 +939,7 @@ void kadmin_addpol(argc, argv) } else { policy.policy = argv[argc - 1]; mask |= OVSEC_KADM_POLICY; - retval = ovsec_kadm_create_policy(&policy, mask); + retval = ovsec_kadm_create_policy(ovsec_hndl, &policy, mask); if (retval) { com_err("add_policy", retval, "while creating policy \"%s\".", policy.policy); @@ -840,7 +963,7 @@ void kadmin_modpol(argc, argv) return; } else { policy.policy = argv[argc - 1]; - retval = ovsec_kadm_modify_policy(&policy, mask); + retval = ovsec_kadm_modify_policy(ovsec_hndl, &policy, mask); if (retval) { com_err("modify_policy", retval, "while modifying policy \"%s\".", policy.policy); @@ -874,7 +997,7 @@ void kadmin_delpol(argc, argv) return; } } - retval = ovsec_kadm_delete_policy(argv[argc - 1]); + retval = ovsec_kadm_delete_policy(ovsec_hndl, argv[argc - 1]); if (retval) { com_err("delete_policy:", retval, "while deleting policy \"%s\"", argv[argc - 1]); @@ -899,7 +1022,7 @@ void kadmin_getpol(argc, argv) fprintf(stderr, "get_policy: bad arguments\n"); return; } - retval = ovsec_kadm_get_policy(argv[argc - 1], &policy); + retval = ovsec_kadm_get_policy(ovsec_hndl, argv[argc - 1], &policy); if (retval) { com_err("get_policy", retval, "while retrieving policy \"%s\".", argv[argc - 1]); @@ -920,7 +1043,7 @@ void kadmin_getpol(argc, argv) policy->pw_min_length, policy->pw_min_classes, policy->pw_history_num, policy->policy_refcnt); } - ovsec_kadm_free_policy_ent(policy); + ovsec_kadm_free_policy_ent(ovsec_hndl, policy); return; } @@ -937,7 +1060,7 @@ kadmin_getprivs(argc, argv) fprintf(stderr, "get_privs: bad arguments\n"); return; } - retval = ovsec_kadm_get_privs(&plist); + retval = ovsec_kadm_get_privs(ovsec_hndl, &plist); if (retval) { com_err("get_privs", retval, "while retrieving privileges"); return; diff --git a/src/kadmin.new/client/kadmin_ct.ct b/src/kadmin.new/client/kadmin_ct.ct index d28d3e477..f5a67ed53 100644 --- a/src/kadmin.new/client/kadmin_ct.ct +++ b/src/kadmin.new/client/kadmin_ct.ct @@ -62,3 +62,6 @@ request ss_list_requests, "List available requests.", request ss_quit, "Exit program.", quit, exit, q; + +end; + diff --git a/src/kadmin.new/client/ss_wrapper.c b/src/kadmin.new/client/ss_wrapper.c index f7bbda516..3fa3aa9f8 100644 --- a/src/kadmin.new/client/ss_wrapper.c +++ b/src/kadmin.new/client/ss_wrapper.c @@ -28,6 +28,7 @@ extern ss_request_table kadmin_cmds; extern int exit_status; +extern char *kadmin_startup(); int main(argc, argv) int argc; diff --git a/src/kadmin.new/client/strftime.c b/src/kadmin.new/client/strftime.c new file mode 100644 index 000000000..484852a72 --- /dev/null +++ b/src/kadmin.new/client/strftime.c @@ -0,0 +1,469 @@ +/* strftime - custom formatting of date and/or time + Copyright (C) 1989, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Note: this version of strftime lacks locale support, + but it is standalone. + + Performs `%' substitutions similar to those in printf. Except + where noted, substituted fields have a fixed size; numeric fields are + padded if necessary. Padding is with zeros by default; for fields + that display a single number, padding can be changed or inhibited by + following the `%' with one of the modifiers described below. Unknown + field specifiers are copied as normal characters. All other + characters are copied to the output without change. + + Supports a superset of the ANSI C field specifiers. + + Literal character fields: + % % + n newline + t tab + + Numeric modifiers (a nonstandard extension): + - do not pad the field + _ pad the field with spaces + + Time fields: + %H hour (00..23) + %I hour (01..12) + %k hour ( 0..23) + %l hour ( 1..12) + %M minute (00..59) + %p locale's AM or PM + %r time, 12-hour (hh:mm:ss [AP]M) + %R time, 24-hour (hh:mm) + %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension) + %S second (00..61) + %T time, 24-hour (hh:mm:ss) + %X locale's time representation (%H:%M:%S) + %Z time zone (EDT), or nothing if no time zone is determinable + + Date fields: + %a locale's abbreviated weekday name (Sun..Sat) + %A locale's full weekday name, variable length (Sunday..Saturday) + %b locale's abbreviated month name (Jan..Dec) + %B locale's full month name, variable length (January..December) + %c locale's date and time (Sat Nov 04 12:02:33 EST 1989) + %C century (00..99) + %d day of month (01..31) + %e day of month ( 1..31) + %D date (mm/dd/yy) + %h same as %b + %j day of year (001..366) + %m month (01..12) + %U week number of year with Sunday as first day of week (00..53) + %w day of week (0..6) + %W week number of year with Monday as first day of week (00..53) + %x locale's date representation (mm/dd/yy) + %y last two digits of year (00..99) + %Y year (1970...) + + David MacKenzie */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)) +#include +#else +#include +#endif + +#ifndef STDC_HEADERS +time_t mktime (); +#endif + +#if defined(HAVE_TZNAME) +extern char *tzname[2]; +#endif + +/* Types of padding for numbers in date and time. */ +enum padding +{ + none, blank, zero +}; + +static char const* const days[] = +{ + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +static char const * const months[] = +{ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; + +/* Add character C to STRING and increment LENGTH, + unless LENGTH would exceed MAX. */ + +#define add_char(c) \ + do \ + { \ + if (length + 1 <= max) \ + string[length++] = (c); \ + } \ + while (0) + +/* Add a 2 digit number to STRING, padding if specified. + Return the number of characters added, up to MAX. */ + +static int +add_num2 (string, num, max, pad) + char *string; + int num; + int max; + enum padding pad; +{ + int top = num / 10; + int length = 0; + + if (top == 0 && pad == blank) + add_char (' '); + else if (top != 0 || pad == zero) + add_char (top + '0'); + add_char (num % 10 + '0'); + return length; +} + +/* Add a 3 digit number to STRING, padding if specified. + Return the number of characters added, up to MAX. */ + +static int +add_num3 (string, num, max, pad) + char *string; + int num; + int max; + enum padding pad; +{ + int top = num / 100; + int mid = (num - top * 100) / 10; + int length = 0; + + if (top == 0 && pad == blank) + add_char (' '); + else if (top != 0 || pad == zero) + add_char (top + '0'); + if (mid == 0 && top == 0 && pad == blank) + add_char (' '); + else if (mid != 0 || top != 0 || pad == zero) + add_char (mid + '0'); + add_char (num % 10 + '0'); + return length; +} + +/* Like strncpy except return the number of characters copied. */ + +static int +add_str (to, from, max) + char *to; + const char *from; + int max; +{ + int i; + + for (i = 0; from[i] && i <= max; ++i) + to[i] = from[i]; + return i; +} + +static int +add_num_time_t (string, max, num) + char *string; + int max; + time_t num; +{ + /* This buffer is large enough to hold the character representation + (including the trailing NUL) of any unsigned decimal quantity + whose binary representation fits in 128 bits. */ + char buf[40]; + int length; + + if (sizeof (num) > 16) + abort (); + sprintf (buf, "%lu", (unsigned long) num); + length = add_str (string, buf, max); + return length; +} + +/* Return the week in the year of the time in TM, with the weeks + starting on Sundays. */ + +static int +sun_week (tm) + struct tm *tm; +{ + int dl; + + /* Set `dl' to the day in the year of the last day of the week previous + to the one containing the day specified in TM. If the day specified + in TM is in the first week of the year, `dl' will be negative or 0. + Otherwise, calculate the number of complete weeks before our week + (dl / 7) and add any partial week at the start of the year (dl % 7). */ + dl = tm->tm_yday - tm->tm_wday; + return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); +} + +/* Return the week in the year of the time in TM, with the weeks + starting on Mondays. */ + +static int +mon_week (tm) + struct tm *tm; +{ + int dl, wday; + + if (tm->tm_wday == 0) + wday = 6; + else + wday = tm->tm_wday - 1; + dl = tm->tm_yday - wday; + return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); +} + +#if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME) +char * +zone_name (tp) + struct tm *tp; +{ + char *timezone (); + struct timeval tv; + struct timezone tz; + + gettimeofday (&tv, &tz); + return timezone (tz.tz_minuteswest, tp->tm_isdst); +} +#endif + +/* Format the time given in TM according to FORMAT, and put the + results in STRING. + Return the number of characters (not including terminating null) + that were put into STRING, or 0 if the length would have + exceeded MAX. */ + +size_t +strftime (string, max, format, tm) + char *string; + size_t max; + const char *format; + const struct tm *tm; +{ + enum padding pad; /* Type of padding to apply. */ + size_t length = 0; /* Characters put in STRING so far. */ + + for (; *format && length < max; ++format) + { + if (*format != '%') + add_char (*format); + else + { + ++format; + /* Modifiers: */ + if (*format == '-') + { + pad = none; + ++format; + } + else if (*format == '_') + { + pad = blank; + ++format; + } + else + pad = zero; + + switch (*format) + { + /* Literal character fields: */ + case 0: + case '%': + add_char ('%'); + break; + case 'n': + add_char ('\n'); + break; + case 't': + add_char ('\t'); + break; + default: + add_char (*format); + break; + + /* Time fields: */ + case 'H': + case 'k': + length += + add_num2 (&string[length], tm->tm_hour, max - length, + *format == 'H' ? pad : blank); + break; + case 'I': + case 'l': + { + int hour12; + + if (tm->tm_hour == 0) + hour12 = 12; + else if (tm->tm_hour > 12) + hour12 = tm->tm_hour - 12; + else + hour12 = tm->tm_hour; + length += + add_num2 (&string[length], hour12, max - length, + *format == 'I' ? pad : blank); + } + break; + case 'M': + length += + add_num2 (&string[length], tm->tm_min, max - length, pad); + break; + case 'p': + if (tm->tm_hour < 12) + add_char ('A'); + else + add_char ('P'); + add_char ('M'); + break; + case 'r': + length += + strftime (&string[length], max - length, "%I:%M:%S %p", tm); + break; + case 'R': + length += + strftime (&string[length], max - length, "%H:%M", tm); + break; + + case 's': + { + struct tm writable_tm; + writable_tm = *tm; + length += add_num_time_t (&string[length], max - length, + mktime (&writable_tm)); + } + break; + + case 'S': + length += + add_num2 (&string[length], tm->tm_sec, max - length, pad); + break; + case 'T': + length += + strftime (&string[length], max - length, "%H:%M:%S", tm); + break; + case 'X': + length += + strftime (&string[length], max - length, "%H:%M:%S", tm); + break; + case 'Z': +#ifdef HAVE_TM_ZONE + length += add_str (&string[length], tm->tm_zone, max - length); +#else +#ifdef HAVE_TZNAME + if (tm->tm_isdst && tzname[1] && *tzname[1]) + length += add_str (&string[length], tzname[1], max - length); + else + length += add_str (&string[length], tzname[0], max - length); +#else + length += add_str (&string[length], zone_name (tm), max - length); +#endif +#endif + break; + + /* Date fields: */ + case 'a': + add_char (days[tm->tm_wday][0]); + add_char (days[tm->tm_wday][1]); + add_char (days[tm->tm_wday][2]); + break; + case 'A': + length += + add_str (&string[length], days[tm->tm_wday], max - length); + break; + case 'b': + case 'h': + add_char (months[tm->tm_mon][0]); + add_char (months[tm->tm_mon][1]); + add_char (months[tm->tm_mon][2]); + break; + case 'B': + length += + add_str (&string[length], months[tm->tm_mon], max - length); + break; + case 'c': + length += + strftime (&string[length], max - length, + "%a %b %d %H:%M:%S %Z %Y", tm); + break; + case 'C': + length += + add_num2 (&string[length], (tm->tm_year + 1900) / 100, + max - length, pad); + break; + case 'd': + length += + add_num2 (&string[length], tm->tm_mday, max - length, pad); + break; + case 'e': + length += + add_num2 (&string[length], tm->tm_mday, max - length, blank); + break; + case 'D': + length += + strftime (&string[length], max - length, "%m/%d/%y", tm); + break; + case 'j': + length += + add_num3 (&string[length], tm->tm_yday + 1, max - length, pad); + break; + case 'm': + length += + add_num2 (&string[length], tm->tm_mon + 1, max - length, pad); + break; + case 'U': + length += + add_num2 (&string[length], sun_week (tm), max - length, pad); + break; + case 'w': + add_char (tm->tm_wday + '0'); + break; + case 'W': + length += + add_num2 (&string[length], mon_week (tm), max - length, pad); + break; + case 'x': + length += + strftime (&string[length], max - length, "%m/%d/%y", tm); + break; + case 'y': + length += + add_num2 (&string[length], tm->tm_year % 100, + max - length, pad); + break; + case 'Y': + add_char ((tm->tm_year + 1900) / 1000 + '0'); + length += + add_num3 (&string[length], + (1900 + tm->tm_year) % 1000, max - length, zero); + break; + } + } + } + add_char (0); + return length - 1; +} -- 2.26.2