1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate * 26*7c478bd9Sstevel@tonic-gate * logadm/kw.c -- manage keywords table 27*7c478bd9Sstevel@tonic-gate * 28*7c478bd9Sstevel@tonic-gate * this module expands things like $file.$n in "templates". 29*7c478bd9Sstevel@tonic-gate * calling kw_init() sets the current "filename" used for 30*7c478bd9Sstevel@tonic-gate * $file, $dirname, and $basename. 31*7c478bd9Sstevel@tonic-gate * 32*7c478bd9Sstevel@tonic-gate * any time-based expansions, like $secs, or all the strftime() 33*7c478bd9Sstevel@tonic-gate * percent sequences, are based on the exact same point in time. 34*7c478bd9Sstevel@tonic-gate * so calling kw_expand() on something like "file-%T" will return 35*7c478bd9Sstevel@tonic-gate * the same thing when called multiple times during the same logadm run. 36*7c478bd9Sstevel@tonic-gate */ 37*7c478bd9Sstevel@tonic-gate 38*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 39*7c478bd9Sstevel@tonic-gate 40*7c478bd9Sstevel@tonic-gate #include <stdio.h> 41*7c478bd9Sstevel@tonic-gate #include <libintl.h> 42*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 43*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 44*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 45*7c478bd9Sstevel@tonic-gate #include <sys/utsname.h> 46*7c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h> 47*7c478bd9Sstevel@tonic-gate #include <strings.h> 48*7c478bd9Sstevel@tonic-gate #include <time.h> 49*7c478bd9Sstevel@tonic-gate #include <ctype.h> 50*7c478bd9Sstevel@tonic-gate #include "err.h" 51*7c478bd9Sstevel@tonic-gate #include "lut.h" 52*7c478bd9Sstevel@tonic-gate #include "fn.h" 53*7c478bd9Sstevel@tonic-gate #include "kw.h" 54*7c478bd9Sstevel@tonic-gate 55*7c478bd9Sstevel@tonic-gate /* forward declarations for functions used internally by this module */ 56*7c478bd9Sstevel@tonic-gate static void kw_printer(const char *lhs, void *rhs, void *arg); 57*7c478bd9Sstevel@tonic-gate 58*7c478bd9Sstevel@tonic-gate /* 59*7c478bd9Sstevel@tonic-gate * absurdly long length to hold sprintf of a %d, 60*7c478bd9Sstevel@tonic-gate * or strftime() expansion of a *single* percent sequence 61*7c478bd9Sstevel@tonic-gate */ 62*7c478bd9Sstevel@tonic-gate #define MAXDIGITS 100 63*7c478bd9Sstevel@tonic-gate 64*7c478bd9Sstevel@tonic-gate static struct lut *Keywords; /* lookup table for keywords */ 65*7c478bd9Sstevel@tonic-gate 66*7c478bd9Sstevel@tonic-gate extern time_t Now; /* time used for keyword expansions */ 67*7c478bd9Sstevel@tonic-gate 68*7c478bd9Sstevel@tonic-gate /* 69*7c478bd9Sstevel@tonic-gate * kw_init -- initialize keywords based on given filename 70*7c478bd9Sstevel@tonic-gate */ 71*7c478bd9Sstevel@tonic-gate void 72*7c478bd9Sstevel@tonic-gate kw_init(struct fn *fnp, struct fn *nfnp) 73*7c478bd9Sstevel@tonic-gate { 74*7c478bd9Sstevel@tonic-gate static char *fullpath; 75*7c478bd9Sstevel@tonic-gate static char *nfullpath; 76*7c478bd9Sstevel@tonic-gate static char *splitpath; 77*7c478bd9Sstevel@tonic-gate static char secs[MAXDIGITS]; 78*7c478bd9Sstevel@tonic-gate static struct utsname un; 79*7c478bd9Sstevel@tonic-gate static char platform[SYS_NMLN]; 80*7c478bd9Sstevel@tonic-gate static char isa[SYS_NMLN]; 81*7c478bd9Sstevel@tonic-gate static char domain[256]; 82*7c478bd9Sstevel@tonic-gate static char *home; 83*7c478bd9Sstevel@tonic-gate static char *user; 84*7c478bd9Sstevel@tonic-gate static char *logname; 85*7c478bd9Sstevel@tonic-gate static int initialized; 86*7c478bd9Sstevel@tonic-gate char *ptr; 87*7c478bd9Sstevel@tonic-gate 88*7c478bd9Sstevel@tonic-gate /* make a copy of the string for $file */ 89*7c478bd9Sstevel@tonic-gate if (fullpath) 90*7c478bd9Sstevel@tonic-gate FREE(fullpath); 91*7c478bd9Sstevel@tonic-gate fullpath = STRDUP(fn_s(fnp)); 92*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "file", fullpath); 93*7c478bd9Sstevel@tonic-gate 94*7c478bd9Sstevel@tonic-gate /* make a copy of the string for $nfile */ 95*7c478bd9Sstevel@tonic-gate if (nfullpath) 96*7c478bd9Sstevel@tonic-gate FREE(nfullpath); 97*7c478bd9Sstevel@tonic-gate if (nfnp == NULL) { 98*7c478bd9Sstevel@tonic-gate nfullpath = NULL; 99*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "nfile", ""); 100*7c478bd9Sstevel@tonic-gate } else { 101*7c478bd9Sstevel@tonic-gate nfullpath = STRDUP(fn_s(nfnp)); 102*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "nfile", nfullpath); 103*7c478bd9Sstevel@tonic-gate } 104*7c478bd9Sstevel@tonic-gate 105*7c478bd9Sstevel@tonic-gate /* make a copy of the string for $dirname/$basename */ 106*7c478bd9Sstevel@tonic-gate if (splitpath) 107*7c478bd9Sstevel@tonic-gate FREE(splitpath); 108*7c478bd9Sstevel@tonic-gate splitpath = STRDUP(fn_s(fnp)); 109*7c478bd9Sstevel@tonic-gate 110*7c478bd9Sstevel@tonic-gate if ((ptr = strrchr(splitpath, '/')) == NULL) { 111*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "basename", splitpath); 112*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "dirname", "."); 113*7c478bd9Sstevel@tonic-gate } else { 114*7c478bd9Sstevel@tonic-gate *ptr++ = '\0'; 115*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "basename", ptr); 116*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "dirname", splitpath); 117*7c478bd9Sstevel@tonic-gate } 118*7c478bd9Sstevel@tonic-gate 119*7c478bd9Sstevel@tonic-gate if (initialized) 120*7c478bd9Sstevel@tonic-gate return; /* rest of the keywords don't change */ 121*7c478bd9Sstevel@tonic-gate 122*7c478bd9Sstevel@tonic-gate (void) snprintf(secs, MAXDIGITS, "%d", (int)Now); 123*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "secs", secs); 124*7c478bd9Sstevel@tonic-gate 125*7c478bd9Sstevel@tonic-gate if (uname(&un) < 0) 126*7c478bd9Sstevel@tonic-gate err(EF_SYS, "uname"); 127*7c478bd9Sstevel@tonic-gate 128*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "nodename", un.nodename); 129*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "release", un.release); 130*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "machine", un.machine); 131*7c478bd9Sstevel@tonic-gate 132*7c478bd9Sstevel@tonic-gate if (sysinfo(SI_ARCHITECTURE, isa, sizeof (isa)) == -1) 133*7c478bd9Sstevel@tonic-gate err(EF_WARN|EF_SYS, "sysinfo(SI_ARCHITECTURE) failed."); 134*7c478bd9Sstevel@tonic-gate else 135*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "isa", isa); 136*7c478bd9Sstevel@tonic-gate 137*7c478bd9Sstevel@tonic-gate if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) == -1) 138*7c478bd9Sstevel@tonic-gate err(EF_WARN|EF_SYS, "sysinfo(SI_PLATFORM) failed."); 139*7c478bd9Sstevel@tonic-gate else 140*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "platform", platform); 141*7c478bd9Sstevel@tonic-gate 142*7c478bd9Sstevel@tonic-gate if (sysinfo(SI_SRPC_DOMAIN, domain, sizeof (domain)) == -1) 143*7c478bd9Sstevel@tonic-gate err(EF_WARN|EF_SYS, "sysinfo(SI_SRPC_DOMAIN) failed."); 144*7c478bd9Sstevel@tonic-gate else 145*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "domain", domain); 146*7c478bd9Sstevel@tonic-gate 147*7c478bd9Sstevel@tonic-gate if ((home = getenv("HOME")) != NULL) 148*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "home", STRDUP(home)); 149*7c478bd9Sstevel@tonic-gate 150*7c478bd9Sstevel@tonic-gate if ((user = getenv("USER")) != NULL) 151*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "user", STRDUP(user)); 152*7c478bd9Sstevel@tonic-gate 153*7c478bd9Sstevel@tonic-gate if ((logname = getenv("LOGNAME")) != NULL) 154*7c478bd9Sstevel@tonic-gate Keywords = lut_add(Keywords, "logname", STRDUP(logname)); 155*7c478bd9Sstevel@tonic-gate 156*7c478bd9Sstevel@tonic-gate initialized = 1; 157*7c478bd9Sstevel@tonic-gate } 158*7c478bd9Sstevel@tonic-gate 159*7c478bd9Sstevel@tonic-gate /* helper function for kw_print() */ 160*7c478bd9Sstevel@tonic-gate static void 161*7c478bd9Sstevel@tonic-gate kw_printer(const char *lhs, void *rhs, void *arg) 162*7c478bd9Sstevel@tonic-gate { 163*7c478bd9Sstevel@tonic-gate FILE *stream = (FILE *)arg; 164*7c478bd9Sstevel@tonic-gate 165*7c478bd9Sstevel@tonic-gate (void) fprintf(stream, "%20.20s %s\n", lhs, (char *)rhs); 166*7c478bd9Sstevel@tonic-gate } 167*7c478bd9Sstevel@tonic-gate 168*7c478bd9Sstevel@tonic-gate /* 169*7c478bd9Sstevel@tonic-gate * kw_print -- spew the entire keywords table to stream 170*7c478bd9Sstevel@tonic-gate * 171*7c478bd9Sstevel@tonic-gate * this routine is used to dump the keywords table for debugging. 172*7c478bd9Sstevel@tonic-gate */ 173*7c478bd9Sstevel@tonic-gate void 174*7c478bd9Sstevel@tonic-gate kw_print(FILE *stream) 175*7c478bd9Sstevel@tonic-gate { 176*7c478bd9Sstevel@tonic-gate lut_walk(Keywords, kw_printer, stream); 177*7c478bd9Sstevel@tonic-gate } 178*7c478bd9Sstevel@tonic-gate 179*7c478bd9Sstevel@tonic-gate /* 180*7c478bd9Sstevel@tonic-gate * kw_expand -- expand src into dst with given n value for $n (or $N) 181*7c478bd9Sstevel@tonic-gate * 182*7c478bd9Sstevel@tonic-gate * n == -1 means expand src into a reglob 183*7c478bd9Sstevel@tonic-gate * if gz is true, include ".gz" extension 184*7c478bd9Sstevel@tonic-gate * 185*7c478bd9Sstevel@tonic-gate * returns true if template contains $n or $N (implying rotation of files) 186*7c478bd9Sstevel@tonic-gate */ 187*7c478bd9Sstevel@tonic-gate boolean_t 188*7c478bd9Sstevel@tonic-gate kw_expand(struct fn *src, struct fn *dst, int n, boolean_t gz) 189*7c478bd9Sstevel@tonic-gate { 190*7c478bd9Sstevel@tonic-gate int c; 191*7c478bd9Sstevel@tonic-gate char buf[MAXDIGITS]; 192*7c478bd9Sstevel@tonic-gate boolean_t hasn = B_FALSE; 193*7c478bd9Sstevel@tonic-gate struct fn *kw = fn_new(NULL); 194*7c478bd9Sstevel@tonic-gate char *ptr; 195*7c478bd9Sstevel@tonic-gate struct tm *gmt_tm = gmtime(&Now); 196*7c478bd9Sstevel@tonic-gate 197*7c478bd9Sstevel@tonic-gate while ((c = fn_getc(src)) != '\0') 198*7c478bd9Sstevel@tonic-gate switch (c) { 199*7c478bd9Sstevel@tonic-gate case '.': 200*7c478bd9Sstevel@tonic-gate case '(': 201*7c478bd9Sstevel@tonic-gate case ')': 202*7c478bd9Sstevel@tonic-gate case '^': 203*7c478bd9Sstevel@tonic-gate case '+': 204*7c478bd9Sstevel@tonic-gate case '{': 205*7c478bd9Sstevel@tonic-gate case '}': 206*7c478bd9Sstevel@tonic-gate /* when building an re, escape with a backslash */ 207*7c478bd9Sstevel@tonic-gate if (n < 0) 208*7c478bd9Sstevel@tonic-gate fn_putc(dst, '\\'); 209*7c478bd9Sstevel@tonic-gate fn_putc(dst, c); 210*7c478bd9Sstevel@tonic-gate break; 211*7c478bd9Sstevel@tonic-gate case '?': 212*7c478bd9Sstevel@tonic-gate /* when building an re, change '?' to a single dot */ 213*7c478bd9Sstevel@tonic-gate if (n < 0) 214*7c478bd9Sstevel@tonic-gate fn_putc(dst, '.'); 215*7c478bd9Sstevel@tonic-gate break; 216*7c478bd9Sstevel@tonic-gate case '*': 217*7c478bd9Sstevel@tonic-gate /* when building an re, change '*' to ".*" */ 218*7c478bd9Sstevel@tonic-gate if (n < 0) 219*7c478bd9Sstevel@tonic-gate fn_putc(dst, '.'); 220*7c478bd9Sstevel@tonic-gate fn_putc(dst, '*'); 221*7c478bd9Sstevel@tonic-gate break; 222*7c478bd9Sstevel@tonic-gate case '$': 223*7c478bd9Sstevel@tonic-gate /* '$' marks the start of a keyword */ 224*7c478bd9Sstevel@tonic-gate switch (c = fn_getc(src)) { 225*7c478bd9Sstevel@tonic-gate case '$': 226*7c478bd9Sstevel@tonic-gate /* double '$' stands for a single '$' */ 227*7c478bd9Sstevel@tonic-gate if (n < 0) 228*7c478bd9Sstevel@tonic-gate fn_putc(dst, '\\'); 229*7c478bd9Sstevel@tonic-gate fn_putc(dst, '$'); 230*7c478bd9Sstevel@tonic-gate break; 231*7c478bd9Sstevel@tonic-gate case '#': 232*7c478bd9Sstevel@tonic-gate /* 233*7c478bd9Sstevel@tonic-gate * $# expands to nothing, but forces an end 234*7c478bd9Sstevel@tonic-gate * of keyword, allow juxtaposition of a 235*7c478bd9Sstevel@tonic-gate * keyword with lower-case characters 236*7c478bd9Sstevel@tonic-gate */ 237*7c478bd9Sstevel@tonic-gate break; 238*7c478bd9Sstevel@tonic-gate case 'n': 239*7c478bd9Sstevel@tonic-gate case 'N': 240*7c478bd9Sstevel@tonic-gate if (c == 'N' || !islower(fn_peekc(src))) { 241*7c478bd9Sstevel@tonic-gate /* 242*7c478bd9Sstevel@tonic-gate * we've found $n or $N, if we're 243*7c478bd9Sstevel@tonic-gate * building an re, build one that 244*7c478bd9Sstevel@tonic-gate * matches a number, otherwise 245*7c478bd9Sstevel@tonic-gate * expand the keyword to the n 246*7c478bd9Sstevel@tonic-gate * passed in to this function 247*7c478bd9Sstevel@tonic-gate */ 248*7c478bd9Sstevel@tonic-gate hasn = B_TRUE; 249*7c478bd9Sstevel@tonic-gate if (n < 0) 250*7c478bd9Sstevel@tonic-gate fn_puts(dst, "([0-9]+)$0"); 251*7c478bd9Sstevel@tonic-gate else { 252*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, 253*7c478bd9Sstevel@tonic-gate MAXDIGITS, "%d", 254*7c478bd9Sstevel@tonic-gate (c == 'n') ? n : n + 1); 255*7c478bd9Sstevel@tonic-gate fn_puts(dst, buf); 256*7c478bd9Sstevel@tonic-gate } 257*7c478bd9Sstevel@tonic-gate break; 258*7c478bd9Sstevel@tonic-gate } 259*7c478bd9Sstevel@tonic-gate /*FALLTHROUGH*/ 260*7c478bd9Sstevel@tonic-gate default: 261*7c478bd9Sstevel@tonic-gate /* gather up the keyword name */ 262*7c478bd9Sstevel@tonic-gate fn_renew(kw, NULL); 263*7c478bd9Sstevel@tonic-gate fn_putc(kw, c); 264*7c478bd9Sstevel@tonic-gate while (islower(fn_peekc(src))) 265*7c478bd9Sstevel@tonic-gate fn_putc(kw, fn_getc(src)); 266*7c478bd9Sstevel@tonic-gate 267*7c478bd9Sstevel@tonic-gate /* lookup keyword */ 268*7c478bd9Sstevel@tonic-gate if ((ptr = (char *)lut_lookup(Keywords, 269*7c478bd9Sstevel@tonic-gate fn_s(kw))) == NULL) { 270*7c478bd9Sstevel@tonic-gate /* nope, copy it unexpanded */ 271*7c478bd9Sstevel@tonic-gate if (n < 0) 272*7c478bd9Sstevel@tonic-gate fn_putc(dst, '\\'); 273*7c478bd9Sstevel@tonic-gate fn_putc(dst, '$'); 274*7c478bd9Sstevel@tonic-gate fn_putfn(dst, kw); 275*7c478bd9Sstevel@tonic-gate } else 276*7c478bd9Sstevel@tonic-gate fn_puts(dst, ptr); 277*7c478bd9Sstevel@tonic-gate } 278*7c478bd9Sstevel@tonic-gate break; 279*7c478bd9Sstevel@tonic-gate case '%': 280*7c478bd9Sstevel@tonic-gate /* 281*7c478bd9Sstevel@tonic-gate * % sequence for strftime(), if we're building 282*7c478bd9Sstevel@tonic-gate * an re, we take our best guess at the re for 283*7c478bd9Sstevel@tonic-gate * this sequence, otherwise we pass it to strftime() 284*7c478bd9Sstevel@tonic-gate */ 285*7c478bd9Sstevel@tonic-gate if (n < 0) { 286*7c478bd9Sstevel@tonic-gate /* 287*7c478bd9Sstevel@tonic-gate * the regex for a percent sequence is 288*7c478bd9Sstevel@tonic-gate * usually just ".*" unless it is one 289*7c478bd9Sstevel@tonic-gate * of the common cases we know about 290*7c478bd9Sstevel@tonic-gate * that are numeric. in those cases, we 291*7c478bd9Sstevel@tonic-gate * tighten up the regex to just match digits. 292*7c478bd9Sstevel@tonic-gate * 293*7c478bd9Sstevel@tonic-gate * while it is gross that we embed knowledge 294*7c478bd9Sstevel@tonic-gate * of strftime() sequences here, they are 295*7c478bd9Sstevel@tonic-gate * specified in a standard so aren't 296*7c478bd9Sstevel@tonic-gate * expected to change often, and it *really* 297*7c478bd9Sstevel@tonic-gate * cuts down on the possibility that we'll 298*7c478bd9Sstevel@tonic-gate * expire a file that isn't an old log file. 299*7c478bd9Sstevel@tonic-gate */ 300*7c478bd9Sstevel@tonic-gate if ((c = fn_getc(src)) == 'E' || c == 'O') { 301*7c478bd9Sstevel@tonic-gate c = fn_getc(src); 302*7c478bd9Sstevel@tonic-gate fn_puts(dst, ".*"); 303*7c478bd9Sstevel@tonic-gate } else 304*7c478bd9Sstevel@tonic-gate switch (c) { 305*7c478bd9Sstevel@tonic-gate case 'd': 306*7c478bd9Sstevel@tonic-gate case 'g': 307*7c478bd9Sstevel@tonic-gate case 'G': 308*7c478bd9Sstevel@tonic-gate case 'H': 309*7c478bd9Sstevel@tonic-gate case 'I': 310*7c478bd9Sstevel@tonic-gate case 'j': 311*7c478bd9Sstevel@tonic-gate case 'm': 312*7c478bd9Sstevel@tonic-gate case 'M': 313*7c478bd9Sstevel@tonic-gate case 'S': 314*7c478bd9Sstevel@tonic-gate case 'u': 315*7c478bd9Sstevel@tonic-gate case 'U': 316*7c478bd9Sstevel@tonic-gate case 'V': 317*7c478bd9Sstevel@tonic-gate case 'w': 318*7c478bd9Sstevel@tonic-gate case 'W': 319*7c478bd9Sstevel@tonic-gate case 'y': 320*7c478bd9Sstevel@tonic-gate case 'Y': 321*7c478bd9Sstevel@tonic-gate /* pure numeric cases */ 322*7c478bd9Sstevel@tonic-gate fn_puts(dst, "[0-9]+"); 323*7c478bd9Sstevel@tonic-gate break; 324*7c478bd9Sstevel@tonic-gate case 'e': 325*7c478bd9Sstevel@tonic-gate case 'k': 326*7c478bd9Sstevel@tonic-gate case 'l': 327*7c478bd9Sstevel@tonic-gate /* possible space then num */ 328*7c478bd9Sstevel@tonic-gate fn_puts(dst, " *[0-9]+"); 329*7c478bd9Sstevel@tonic-gate break; 330*7c478bd9Sstevel@tonic-gate case 'D': /* %m/%d/%y */ 331*7c478bd9Sstevel@tonic-gate /* adds slashes! */ 332*7c478bd9Sstevel@tonic-gate fn_puts(dst, 333*7c478bd9Sstevel@tonic-gate "[0-9]+/[0-9]+/[0-9]+"); 334*7c478bd9Sstevel@tonic-gate break; 335*7c478bd9Sstevel@tonic-gate case 'R': /* %H:%M */ 336*7c478bd9Sstevel@tonic-gate fn_puts(dst, "[0-9]+:[0-9]+"); 337*7c478bd9Sstevel@tonic-gate break; 338*7c478bd9Sstevel@tonic-gate case 'T': /* %H:%M:%S */ 339*7c478bd9Sstevel@tonic-gate fn_puts(dst, 340*7c478bd9Sstevel@tonic-gate "[0-9]+:[0-9]+:[0-9]+"); 341*7c478bd9Sstevel@tonic-gate break; 342*7c478bd9Sstevel@tonic-gate default: 343*7c478bd9Sstevel@tonic-gate fn_puts(dst, ".*"); 344*7c478bd9Sstevel@tonic-gate } 345*7c478bd9Sstevel@tonic-gate } else { 346*7c478bd9Sstevel@tonic-gate char tbuf[4]; 347*7c478bd9Sstevel@tonic-gate 348*7c478bd9Sstevel@tonic-gate /* copy % sequence to tbuf */ 349*7c478bd9Sstevel@tonic-gate tbuf[0] = '%'; 350*7c478bd9Sstevel@tonic-gate tbuf[1] = fn_getc(src); 351*7c478bd9Sstevel@tonic-gate if (tbuf[1] == 'E' || tbuf[1] == 'O') { 352*7c478bd9Sstevel@tonic-gate /* "extended" sequence */ 353*7c478bd9Sstevel@tonic-gate tbuf[2] = fn_getc(src); 354*7c478bd9Sstevel@tonic-gate tbuf[3] = '\0'; 355*7c478bd9Sstevel@tonic-gate } else 356*7c478bd9Sstevel@tonic-gate tbuf[2] = '\0'; 357*7c478bd9Sstevel@tonic-gate 358*7c478bd9Sstevel@tonic-gate if (strftime(buf, MAXDIGITS, tbuf, gmt_tm) == 0) 359*7c478bd9Sstevel@tonic-gate /* just copy %x */ 360*7c478bd9Sstevel@tonic-gate fn_puts(dst, tbuf); 361*7c478bd9Sstevel@tonic-gate else 362*7c478bd9Sstevel@tonic-gate fn_puts(dst, buf); 363*7c478bd9Sstevel@tonic-gate } 364*7c478bd9Sstevel@tonic-gate break; 365*7c478bd9Sstevel@tonic-gate default: 366*7c478bd9Sstevel@tonic-gate /* nothing special, just copy it */ 367*7c478bd9Sstevel@tonic-gate fn_putc(dst, c); 368*7c478bd9Sstevel@tonic-gate } 369*7c478bd9Sstevel@tonic-gate 370*7c478bd9Sstevel@tonic-gate if (gz) { 371*7c478bd9Sstevel@tonic-gate if (n < 0) 372*7c478bd9Sstevel@tonic-gate fn_puts(dst, "(\\.gz){0,1}"); 373*7c478bd9Sstevel@tonic-gate else 374*7c478bd9Sstevel@tonic-gate fn_puts(dst, ".gz"); 375*7c478bd9Sstevel@tonic-gate } 376*7c478bd9Sstevel@tonic-gate 377*7c478bd9Sstevel@tonic-gate fn_free(kw); 378*7c478bd9Sstevel@tonic-gate return (hasn); 379*7c478bd9Sstevel@tonic-gate } 380*7c478bd9Sstevel@tonic-gate 381*7c478bd9Sstevel@tonic-gate #ifdef TESTMODULE 382*7c478bd9Sstevel@tonic-gate 383*7c478bd9Sstevel@tonic-gate time_t Now; 384*7c478bd9Sstevel@tonic-gate 385*7c478bd9Sstevel@tonic-gate /* 386*7c478bd9Sstevel@tonic-gate * test main for kw module, usage: a.out fname [template...] 387*7c478bd9Sstevel@tonic-gate */ 388*7c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 389*7c478bd9Sstevel@tonic-gate { 390*7c478bd9Sstevel@tonic-gate int i; 391*7c478bd9Sstevel@tonic-gate struct fn *src = fn_new(NULL); 392*7c478bd9Sstevel@tonic-gate struct fn *dst = fn_new(NULL); 393*7c478bd9Sstevel@tonic-gate 394*7c478bd9Sstevel@tonic-gate err_init(argv[0]); 395*7c478bd9Sstevel@tonic-gate setbuf(stdout, NULL); 396*7c478bd9Sstevel@tonic-gate 397*7c478bd9Sstevel@tonic-gate Now = time(0); 398*7c478bd9Sstevel@tonic-gate 399*7c478bd9Sstevel@tonic-gate if (argc < 2) 400*7c478bd9Sstevel@tonic-gate err(0, "first arg must be fname"); 401*7c478bd9Sstevel@tonic-gate 402*7c478bd9Sstevel@tonic-gate kw_init(fn_new(argv[1]), NULL); 403*7c478bd9Sstevel@tonic-gate 404*7c478bd9Sstevel@tonic-gate kw_print(stdout); 405*7c478bd9Sstevel@tonic-gate 406*7c478bd9Sstevel@tonic-gate for (i = 2; i < argc; i++) { 407*7c478bd9Sstevel@tonic-gate int n; 408*7c478bd9Sstevel@tonic-gate 409*7c478bd9Sstevel@tonic-gate for (n = -1; n < 2; n++) { 410*7c478bd9Sstevel@tonic-gate fn_renew(src, argv[i]); 411*7c478bd9Sstevel@tonic-gate fn_renew(dst, NULL); 412*7c478bd9Sstevel@tonic-gate printf("expand<%s> n %d hasn %d ", 413*7c478bd9Sstevel@tonic-gate argv[i], n, kw_expand(src, dst, n, B_FALSE)); 414*7c478bd9Sstevel@tonic-gate printf("result <%s>\n", fn_s(dst)); 415*7c478bd9Sstevel@tonic-gate } 416*7c478bd9Sstevel@tonic-gate } 417*7c478bd9Sstevel@tonic-gate 418*7c478bd9Sstevel@tonic-gate err_done(0); 419*7c478bd9Sstevel@tonic-gate } 420*7c478bd9Sstevel@tonic-gate 421*7c478bd9Sstevel@tonic-gate #endif /* TESTMODULE */ 422