1 /*
2  * This file is part of the zlog Library.
3  *
4  * Copyright (C) 2011 by Hardy Simpson <HardySimpson1984@gmail.com>
5  *
6  * Licensed under the LGPL v2.1, see the file COPYING in base directory.
7  */
8 
9 #include "fmacros.h"
10 
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <sys/time.h>
16 #include <time.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 
21 #include "conf.h"
22 #include "spec.h"
23 #include "level_list.h"
24 #include "zc_defs.h"
25 
26 
27 #define ZLOG_DEFAULT_TIME_FMT "%F %T"
28 #define	ZLOG_HEX_HEAD  \
29 	"\n             0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F    0123456789ABCDEF"
30 
31 /*******************************************************************************/
zlog_spec_profile(zlog_spec_t * a_spec,int flag)32 void zlog_spec_profile(zlog_spec_t * a_spec, int flag)
33 {
34 	zc_assert(a_spec,);
35 	zc_profile(flag, "----spec[%p][%.*s][%s|%d][%s,%ld,%ld][%s]----",
36 		a_spec,
37 		a_spec->len, a_spec->str,
38 		a_spec->time_fmt,
39 		a_spec->time_cache_index,
40 		a_spec->print_fmt, (long)a_spec->max_width, (long)a_spec->min_width,
41 		a_spec->mdc_key);
42 	return;
43 }
44 
45 /*******************************************************************************/
46 /* implementation of write function */
47 
zlog_spec_write_time(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)48 static int zlog_spec_write_time(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
49 {
50 	zlog_time_cache_t * a_cache = a_thread->event->time_caches + a_spec->time_cache_index;
51 	time_t now_sec = a_thread->event->time_stamp.tv_sec;
52 	struct tm *time_local = &(a_thread->event->time_local);
53 
54 	/* the event meet the 1st time_spec in his life cycle */
55 	if (!now_sec) {
56 		gettimeofday(&(a_thread->event->time_stamp), NULL);
57 		now_sec = a_thread->event->time_stamp.tv_sec;
58 	}
59 
60 	/* When this event's last cached time_local is not now */
61 	if (a_thread->event->time_local_sec != now_sec) {
62 		localtime_r(&(now_sec), time_local);
63 		a_thread->event->time_local_sec = now_sec;
64 	}
65 
66 	/* When this spec's last cache time string is not now */
67 	if (a_cache->sec != now_sec) {
68 		a_cache->len = strftime(a_cache->str, sizeof(a_cache->str), a_spec->time_fmt, time_local);
69 		a_cache->sec = now_sec;
70 	}
71 
72 	return zlog_buf_append(a_buf, a_cache->str, a_cache->len);
73 }
74 
75 #if 0
76 static int zlog_spec_write_time_D(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
77 {
78 	if (!a_thread->event->time_stamp.tv_sec) {
79 		gettimeofday(&(a_thread->event->time_stamp), NULL);
80 	}
81 
82 	/*
83 	 * It is modified when time slips one second.
84 	 * So it is a strong cache, as Default time format is always %F %T.
85 	 * That's why I said %D is faster than %d()
86 	 */
87 	if (a_thread->event->time_stamp.tv_sec != a_thread->event->time_last_D) {
88 
89 		a_thread->event->time_last_D = a_thread->event->time_stamp.tv_sec;
90 		localtime_r(&(a_thread->event->time_stamp.tv_sec),
91 			    &(a_thread->event->time_local));
92 
93 		strftime(a_thread->event->time_cache_D,
94 			sizeof(a_thread->event->time_cache_D),
95 			ZLOG_DEFAULT_TIME_FMT, &(a_thread->event->time_local) );
96 	}
97 	return zlog_buf_append(a_buf, a_thread->event->time_cache_D, sizeof(a_thread->event->time_cache_D) - 1);
98 }
99 #endif
100 
zlog_spec_write_ms(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)101 static int zlog_spec_write_ms(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
102 {
103 	if (!a_thread->event->time_stamp.tv_sec) {
104 		gettimeofday(&(a_thread->event->time_stamp), NULL);
105 	}
106 	return zlog_buf_printf_dec32(a_buf, (a_thread->event->time_stamp.tv_usec / 1000), 3);
107 }
108 
zlog_spec_write_us(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)109 static int zlog_spec_write_us(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
110 {
111 	if (!a_thread->event->time_stamp.tv_sec) {
112 		gettimeofday(&(a_thread->event->time_stamp), NULL);
113 	}
114 	return zlog_buf_printf_dec32(a_buf, a_thread->event->time_stamp.tv_usec, 6);
115 }
116 
zlog_spec_write_mdc(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)117 static int zlog_spec_write_mdc(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
118 {
119 	zlog_mdc_kv_t *a_mdc_kv;
120 
121 	a_mdc_kv = zlog_mdc_get_kv(a_thread->mdc, a_spec->mdc_key);
122 	if (!a_mdc_kv) {
123 		zc_error("zlog_mdc_get_kv key[%s] fail", a_spec->mdc_key);
124 		return 0;
125 	}
126 
127 	return zlog_buf_append(a_buf, a_mdc_kv->value, a_mdc_kv->value_len);
128 }
129 
zlog_spec_write_str(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)130 static int zlog_spec_write_str(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
131 {
132 	return zlog_buf_append(a_buf, a_spec->str, a_spec->len);
133 }
134 
zlog_spec_write_category(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)135 static int zlog_spec_write_category(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
136 {
137 	return zlog_buf_append(a_buf, a_thread->event->category_name, a_thread->event->category_name_len);
138 }
139 
zlog_spec_write_srcfile(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)140 static int zlog_spec_write_srcfile(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
141 {
142 	if (!a_thread->event->file) {
143 		return zlog_buf_append(a_buf, "(file=null)", sizeof("(file=null)") - 1);
144 	} else {
145 		return zlog_buf_append(a_buf, a_thread->event->file, a_thread->event->file_len);
146 	}
147 }
148 
zlog_spec_write_srcfile_neat(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)149 static int zlog_spec_write_srcfile_neat(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
150 {
151 	char *p;
152 
153 	if ((p = strrchr(a_thread->event->file, '/')) != NULL) {
154 		return zlog_buf_append(a_buf, p + 1,
155 			(char*)a_thread->event->file + a_thread->event->file_len - p - 1);
156 	} else {
157 		if (!a_thread->event->file) {
158 			return zlog_buf_append(a_buf, "(file=null)", sizeof("(file=null)") - 1);
159 		} else {
160 			return zlog_buf_append(a_buf, a_thread->event->file, a_thread->event->file_len);
161 		}
162 	}
163 }
164 
zlog_spec_write_srcline(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)165 static int zlog_spec_write_srcline(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
166 {
167 
168 	return zlog_buf_printf_dec64(a_buf, a_thread->event->line, 0);
169 }
170 
zlog_spec_write_srcfunc(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)171 static int zlog_spec_write_srcfunc(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
172 {
173 	if (!a_thread->event->file) {
174 		return zlog_buf_append(a_buf, "(func=null)", sizeof("(func=null)") - 1);
175 	} else {
176 		return zlog_buf_append(a_buf, a_thread->event->func, a_thread->event->func_len);
177 	}
178 }
179 
180 
zlog_spec_write_hostname(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)181 static int zlog_spec_write_hostname(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
182 {
183 	return zlog_buf_append(a_buf, a_thread->event->host_name, a_thread->event->host_name_len);
184 }
185 
zlog_spec_write_newline(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)186 static int zlog_spec_write_newline(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
187 {
188 	return zlog_buf_append(a_buf, FILE_NEWLINE, FILE_NEWLINE_LEN);
189 }
190 
zlog_spec_write_percent(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)191 static int zlog_spec_write_percent(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
192 {
193 	return zlog_buf_append(a_buf, "%", 1);
194 }
195 
zlog_spec_write_pid(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)196 static int zlog_spec_write_pid(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
197 {
198 	/* 1st in event lifecycle */
199 	if (!a_thread->event->pid) {
200 		a_thread->event->pid = getpid();
201 
202 		/* compare with previous event */
203 		if (a_thread->event->pid != a_thread->event->last_pid) {
204 			a_thread->event->last_pid = a_thread->event->pid;
205 			a_thread->event->pid_str_len
206 				= sprintf(a_thread->event->pid_str, "%u", a_thread->event->pid);
207 		}
208 	}
209 
210 	return zlog_buf_append(a_buf, a_thread->event->pid_str, a_thread->event->pid_str_len);
211 }
212 
zlog_spec_write_tid_hex(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)213 static int zlog_spec_write_tid_hex(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
214 {
215 
216 	/* don't need to get tid again, as tmap_new_thread fetch it already */
217 	/* and fork not change tid */
218 	return zlog_buf_append(a_buf, a_thread->event->tid_hex_str, a_thread->event->tid_hex_str_len);
219 }
220 
zlog_spec_write_tid_long(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)221 static int zlog_spec_write_tid_long(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
222 {
223 
224 	/* don't need to get tid again, as tmap_new_thread fetch it already */
225 	/* and fork not change tid */
226 	return zlog_buf_append(a_buf, a_thread->event->tid_str, a_thread->event->tid_str_len);
227 }
228 
zlog_spec_write_level_lowercase(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)229 static int zlog_spec_write_level_lowercase(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
230 {
231 	zlog_level_t *a_level;
232 
233 	a_level = zlog_level_list_get(zlog_env_conf->levels, a_thread->event->level);
234 	return zlog_buf_append(a_buf, a_level->str_lowercase, a_level->str_len);
235 }
236 
zlog_spec_write_level_uppercase(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)237 static int zlog_spec_write_level_uppercase(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
238 {
239 	zlog_level_t *a_level;
240 
241 	a_level = zlog_level_list_get(zlog_env_conf->levels, a_thread->event->level);
242 	return zlog_buf_append(a_buf, a_level->str_uppercase, a_level->str_len);
243 }
244 
zlog_spec_write_usrmsg(zlog_spec_t * a_spec,zlog_thread_t * a_thread,zlog_buf_t * a_buf)245 static int zlog_spec_write_usrmsg(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf)
246 {
247 	if (a_thread->event->generate_cmd == ZLOG_FMT) {
248 		if (a_thread->event->str_format) {
249 			return zlog_buf_vprintf(a_buf,
250 				      a_thread->event->str_format,
251 				      a_thread->event->str_args);
252 		} else {
253 			return zlog_buf_append(a_buf, "format=(null)", sizeof("format=(null)")-1);
254 		}
255 	} else if (a_thread->event->generate_cmd == ZLOG_HEX) {
256 		int rc;
257 		long line_offset;
258 		long byte_offset;
259 
260 		/* thread buf start == null or len <= 0 */
261 		if (a_thread->event->hex_buf == NULL) {
262 			rc = zlog_buf_append(a_buf, "buf=(null)", sizeof("buf=(null)")-1);
263 			goto zlog_hex_exit;
264 		}
265 
266 		rc = zlog_buf_append(a_buf, ZLOG_HEX_HEAD, sizeof(ZLOG_HEX_HEAD)-1);
267 		if (rc) {
268 			goto zlog_hex_exit;
269 		}
270 
271 		line_offset = 0;
272 		byte_offset = 0;
273 
274 		while (1) {
275 			unsigned char c;
276 
277 			rc = zlog_buf_append(a_buf, "\n", 1);
278 			if (rc)  goto zlog_hex_exit;
279 
280 			rc = zlog_buf_printf_dec64(a_buf, line_offset + 1, 10);
281 			if (rc)  goto zlog_hex_exit;
282 			rc = zlog_buf_append(a_buf, "   ", 3);
283 			if (rc)  goto zlog_hex_exit;
284 
285 			for (byte_offset = 0; byte_offset < 16; byte_offset++) {
286 				if (line_offset * 16 + byte_offset < a_thread->event->hex_buf_len) {
287 					c = *((unsigned char *)a_thread->event->hex_buf
288 						+ line_offset * 16 + byte_offset);
289 					rc = zlog_buf_printf_hex(a_buf, c, 2);
290 					if (rc) goto zlog_hex_exit;
291 					rc = zlog_buf_append(a_buf, " ", 1);
292 					if (rc) goto zlog_hex_exit;
293 				} else {
294 					rc = zlog_buf_append(a_buf, "   ", 3);
295 					if (rc)  goto zlog_hex_exit;
296 				}
297 			}
298 
299 			rc = zlog_buf_append(a_buf, "  ", 2);
300 			if (rc) goto zlog_hex_exit;
301 
302 			for (byte_offset = 0; byte_offset < 16; byte_offset++) {
303 				if (line_offset * 16 + byte_offset < a_thread->event->hex_buf_len) {
304 					c = *((unsigned char *)a_thread->event->hex_buf
305 						+ line_offset * 16 + byte_offset);
306 					if (c >= 32 && c <= 126) {
307 						rc = zlog_buf_append(a_buf,(char*)&c, 1);
308 						if (rc)  goto zlog_hex_exit;
309 					} else {
310 						rc = zlog_buf_append(a_buf, ".", 1);
311 						if (rc)  goto zlog_hex_exit;
312 					}
313 				} else {
314 					rc = zlog_buf_append(a_buf, " ", 1);
315 					if (rc)  goto zlog_hex_exit;
316 				}
317 			}
318 
319 			if (line_offset * 16 + byte_offset >= a_thread->event->hex_buf_len) {
320 				break;
321 			}
322 
323 			line_offset++;
324 		}
325 
326 	      zlog_hex_exit:
327 		if (rc < 0) {
328 			zc_error("write hex msg fail");
329 			return -1;
330 		} else if (rc > 0) {
331 			zc_error("write hex msg, buf is full");
332 			return 1;
333 		}
334 
335 		return 0;
336 	}
337 
338 	return 0;
339 }
340 
341 /*******************************************************************************/
342 /* implementation of gen function */
343 
zlog_spec_gen_msg_direct(zlog_spec_t * a_spec,zlog_thread_t * a_thread)344 static int zlog_spec_gen_msg_direct(zlog_spec_t * a_spec, zlog_thread_t * a_thread)
345 {
346 	/* no need to reprint %1.2d here */
347 	return a_spec->write_buf(a_spec, a_thread, a_thread->msg_buf);
348 }
349 
zlog_spec_gen_msg_reformat(zlog_spec_t * a_spec,zlog_thread_t * a_thread)350 static int zlog_spec_gen_msg_reformat(zlog_spec_t * a_spec, zlog_thread_t * a_thread)
351 {
352 	int rc;
353 
354 	zlog_buf_restart(a_thread->pre_msg_buf);
355 
356 	rc = a_spec->write_buf(a_spec, a_thread, a_thread->pre_msg_buf);
357 	if (rc < 0) {
358 		zc_error("a_spec->gen_buf fail");
359 		return -1;
360 	} else if (rc > 0) {
361 		/* buf is full, try printf */
362 	}
363 
364 	return zlog_buf_adjust_append(a_thread->msg_buf,
365 		zlog_buf_str(a_thread->pre_msg_buf), zlog_buf_len(a_thread->pre_msg_buf),
366 		a_spec->left_adjust, a_spec->min_width, a_spec->max_width);
367 }
368 
369 /*******************************************************************************/
zlog_spec_gen_path_direct(zlog_spec_t * a_spec,zlog_thread_t * a_thread)370 static int zlog_spec_gen_path_direct(zlog_spec_t * a_spec, zlog_thread_t * a_thread)
371 {
372 	/* no need to reprint %1.2d here */
373 	return a_spec->write_buf(a_spec, a_thread, a_thread->path_buf);
374 }
375 
zlog_spec_gen_path_reformat(zlog_spec_t * a_spec,zlog_thread_t * a_thread)376 static int zlog_spec_gen_path_reformat(zlog_spec_t * a_spec, zlog_thread_t * a_thread)
377 {
378 	int rc;
379 
380 	zlog_buf_restart(a_thread->pre_path_buf);
381 
382 	rc = a_spec->write_buf(a_spec, a_thread, a_thread->pre_path_buf);
383 	if (rc < 0) {
384 		zc_error("a_spec->gen_buf fail");
385 		return -1;
386 	} else if (rc > 0) {
387 		/* buf is full, try printf */
388 	}
389 
390 	return zlog_buf_adjust_append(a_thread->path_buf,
391 		zlog_buf_str(a_thread->pre_path_buf), zlog_buf_len(a_thread->pre_path_buf),
392 		a_spec->left_adjust, a_spec->min_width, a_spec->max_width);
393 }
394 
395 /*******************************************************************************/
zlog_spec_gen_archive_path_direct(zlog_spec_t * a_spec,zlog_thread_t * a_thread)396 static int zlog_spec_gen_archive_path_direct(zlog_spec_t * a_spec, zlog_thread_t * a_thread)
397 {
398 	/* no need to reprint %1.2d here */
399 	return a_spec->write_buf(a_spec, a_thread, a_thread->archive_path_buf);
400 }
401 
zlog_spec_gen_archive_path_reformat(zlog_spec_t * a_spec,zlog_thread_t * a_thread)402 static int zlog_spec_gen_archive_path_reformat(zlog_spec_t * a_spec, zlog_thread_t * a_thread)
403 {
404 	int rc;
405 
406 	zlog_buf_restart(a_thread->pre_path_buf);
407 
408 	rc = a_spec->write_buf(a_spec, a_thread, a_thread->pre_path_buf);
409 	if (rc < 0) {
410 		zc_error("a_spec->gen_buf fail");
411 		return -1;
412 	} else if (rc > 0) {
413 		/* buf is full, try printf */
414 	}
415 
416 	return zlog_buf_adjust_append(a_thread->archive_path_buf,
417 		zlog_buf_str(a_thread->pre_path_buf), zlog_buf_len(a_thread->pre_path_buf),
418 		a_spec->left_adjust, a_spec->min_width, a_spec->max_width);
419 }
420 
421 /*******************************************************************************/
zlog_spec_parse_print_fmt(zlog_spec_t * a_spec)422 static int zlog_spec_parse_print_fmt(zlog_spec_t * a_spec)
423 {
424 	/* -12.35 12 .35 */
425 	char *p, *q;
426 	long i, j;
427 
428 	p = a_spec->print_fmt;
429 	if (*p == '-') {
430 		a_spec->left_adjust = 1;
431 		p++;
432 	} else {
433 		a_spec->left_adjust = 0;
434 	}
435 
436 	i = j = 0;
437 	sscanf(p, "%ld.", &i);
438 	q = strchr(p, '.');
439 	if (q) sscanf(q, ".%ld", &j);
440 
441 	a_spec->min_width = (size_t) i;
442 	a_spec->max_width = (size_t) j;
443 	return 0;
444 }
445 
446 /*******************************************************************************/
zlog_spec_del(zlog_spec_t * a_spec)447 void zlog_spec_del(zlog_spec_t * a_spec)
448 {
449 	zc_assert(a_spec,);
450 	free(a_spec);
451 	zc_debug("zlog_spec_del[%p]", a_spec);
452 }
453 
454 /* a spec may consist of
455  * a const string: /home/bb
456  * a string begin with %: %12.35d(%F %X,%l)
457  */
zlog_spec_new(char * pattern_start,char ** pattern_next,int * time_cache_count)458 zlog_spec_t *zlog_spec_new(char *pattern_start, char **pattern_next, int *time_cache_count)
459 {
460 	char *p;
461 	int nscan = 0;
462 	int nread = 0;
463 	zlog_spec_t *a_spec;
464 
465 	zc_assert(pattern_start, NULL);
466 	zc_assert(pattern_next, NULL);
467 
468 	a_spec = calloc(1, sizeof(zlog_spec_t));
469 	if (!a_spec) {
470 		zc_error("calloc fail, errno[%d]", errno);
471 		return NULL;
472 	}
473 
474 	a_spec->str = p = pattern_start;
475 
476 	switch (*p) {
477 	case '%':
478 		/* a string begin with %: %12.35d(%F %X) */
479 
480 		/* process width and precision char in %-12.35P */
481 		nread = 0;
482 		nscan = sscanf(p, "%%%[.0-9-]%n", a_spec->print_fmt, &nread);
483 		if (nscan == 1) {
484 			a_spec->gen_msg = zlog_spec_gen_msg_reformat;
485 			a_spec->gen_path = zlog_spec_gen_path_reformat;
486 			a_spec->gen_archive_path = zlog_spec_gen_archive_path_reformat;
487 			if (zlog_spec_parse_print_fmt(a_spec)) {
488 				zc_error("zlog_spec_parse_print_fmt fail");
489 				goto err;
490 			}
491 		} else {
492 			nread = 1; /* skip the % char */
493 			a_spec->gen_msg = zlog_spec_gen_msg_direct;
494 			a_spec->gen_path = zlog_spec_gen_path_direct;
495 			a_spec->gen_archive_path = zlog_spec_gen_archive_path_direct;
496 		}
497 
498 		p += nread;
499 
500 		if (*p == 'd') {
501 			if (*(p+1) != '(') {
502 				/* without '(' , use default */
503 				strcpy(a_spec->time_fmt, ZLOG_DEFAULT_TIME_FMT);
504 				p++;
505 			} else if (STRNCMP(p, ==, "d()", 3)) {
506 				/* with () but without detail time format,
507 				 * keep a_spec->time_fmt=="" */
508 				strcpy(a_spec->time_fmt, ZLOG_DEFAULT_TIME_FMT);
509 				p += 3;
510 			} else {
511 				nread = 0;
512 				nscan = sscanf(p, "d(%[^)])%n", a_spec->time_fmt, &nread);
513 				if (nscan != 1) {
514 					nread = 0;
515 				}
516 				p += nread;
517 				if (*(p - 1) != ')') {
518 					zc_error("in string[%s] can't find match \')\'", a_spec->str);
519 					goto err;
520 				}
521 			}
522 
523 			a_spec->time_cache_index = *time_cache_count;
524 			(*time_cache_count)++;
525 			a_spec->write_buf = zlog_spec_write_time;
526 
527 			*pattern_next = p;
528 			a_spec->len = p - a_spec->str;
529 			break;
530 		}
531 
532 		if (*p == 'M') {
533 			nread = 0;
534 			nscan = sscanf(p, "M(%[^)])%n", a_spec->mdc_key, &nread);
535 			if (nscan != 1) {
536 				nread = 0;
537 				if (STRNCMP(p, ==, "M()", 3)) {
538 					nread = 3;
539 				}
540 			}
541 			p += nread;
542 			if (*(p - 1) != ')') {
543 				zc_error("in string[%s] can't find match \')\'", a_spec->str);
544 				goto err;
545 			}
546 
547 			*pattern_next = p;
548 			a_spec->len = p - a_spec->str;
549 			a_spec->write_buf = zlog_spec_write_mdc;
550 			break;
551 		}
552 
553 		if (STRNCMP(p, ==, "ms", 2)) {
554 			p += 2;
555 			*pattern_next = p;
556 			a_spec->len = p - a_spec->str;
557 			a_spec->write_buf = zlog_spec_write_ms;
558 			break;
559 		} else if (STRNCMP(p, ==, "us", 2)) {
560 			p += 2;
561 			*pattern_next = p;
562 			a_spec->len = p - a_spec->str;
563 			a_spec->write_buf = zlog_spec_write_us;
564 			break;
565 		}
566 
567 		*pattern_next = p + 1;
568 		a_spec->len = p - a_spec->str + 1;
569 
570 		switch (*p) {
571 		case 'c':
572 			a_spec->write_buf = zlog_spec_write_category;
573 			break;
574 		case 'D':
575 			strcpy(a_spec->time_fmt, ZLOG_DEFAULT_TIME_FMT);
576 			a_spec->time_cache_index = *time_cache_count;
577 			(*time_cache_count)++;
578 			a_spec->write_buf = zlog_spec_write_time;
579 			break;
580 		case 'F':
581 			a_spec->write_buf = zlog_spec_write_srcfile;
582 			break;
583 		case 'f':
584 			a_spec->write_buf = zlog_spec_write_srcfile_neat;
585 			break;
586 		case 'H':
587 			a_spec->write_buf = zlog_spec_write_hostname;
588 			break;
589 		case 'L':
590 			a_spec->write_buf = zlog_spec_write_srcline;
591 			break;
592 		case 'm':
593 			a_spec->write_buf = zlog_spec_write_usrmsg;
594 			break;
595 		case 'n':
596 			a_spec->write_buf = zlog_spec_write_newline;
597 			break;
598 		case 'p':
599 			a_spec->write_buf = zlog_spec_write_pid;
600 			break;
601 		case 'U':
602 			a_spec->write_buf = zlog_spec_write_srcfunc;
603 			break;
604 		case 'v':
605 			a_spec->write_buf = zlog_spec_write_level_lowercase;
606 			break;
607 		case 'V':
608 			a_spec->write_buf = zlog_spec_write_level_uppercase;
609 			break;
610 		case 't':
611 			a_spec->write_buf = zlog_spec_write_tid_hex;
612 			break;
613 		case 'T':
614 			a_spec->write_buf = zlog_spec_write_tid_long;
615 			break;
616 		case '%':
617 			a_spec->write_buf = zlog_spec_write_percent;
618 			break;
619 		default:
620 			zc_error("str[%s] in wrong format, p[%c]", a_spec->str, *p);
621 			goto err;
622 		}
623 		break;
624 	default:
625 		/* a const string: /home/bb */
626 		*pattern_next = strchr(p, '%');
627 		if (*pattern_next) {
628 			a_spec->len = *pattern_next - p;
629 		} else {
630 			a_spec->len = strlen(p);
631 			*pattern_next = p + a_spec->len;
632 		}
633 		a_spec->write_buf = zlog_spec_write_str;
634 		a_spec->gen_msg = zlog_spec_gen_msg_direct;
635 		a_spec->gen_path = zlog_spec_gen_path_direct;
636 		a_spec->gen_archive_path = zlog_spec_gen_archive_path_direct;
637 	}
638 
639 	zlog_spec_profile(a_spec, ZC_DEBUG);
640 	return a_spec;
641 err:
642 	zlog_spec_del(a_spec);
643 	return NULL;
644 }
645 
646