1 /*
2 * tc_functions.c - various common functions for transcode
3 * Written by Thomas Oestreich, Francesco Romani, Andrew Church, and others
4 *
5 * This file is part of transcode, a video stream processing tool.
6 * transcode is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * transcode is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Make; see the file COPYING. If not, write to
18 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <stdarg.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <ctype.h>
33 #include <sys/stat.h>
34
35 #include "xio.h"
36 #include "libtc.h"
37 #include "tc_defaults.h"
38
39 #include "transcode.h"
40
41 #include "ratiocodes.h"
42
43 /*************************************************************************/
44 #ifdef HAVE_FFMPEG
45
46 pthread_mutex_t tc_libavcodec_mutex = PTHREAD_MUTEX_INITIALIZER;
47
48 #endif
49 /*************************************************************************/
50
51 #define TC_MSG_BUF_SIZE (TC_BUF_MIN * 2)
52
53 /* WARNING: we MUST keep in sync preambles order with TC_LOG* macros */
54 static const char *tc_log_preambles[] = {
55 /* TC_LOG_ERR */
56 "["COL_RED"%s"COL_GRAY"]"COL_RED" critical"COL_GRAY": %s\n",
57 /* TC_LOG_WARN */
58 "["COL_RED"%s"COL_GRAY"]"COL_YELLOW" warning"COL_GRAY": %s\n",
59 /* TC_LOG_INFO */
60 "["COL_BLUE"%s"COL_GRAY"] %s\n",
61 /* TC_LOG_MSG */
62 "[%s] %s\n",
63 /* TC_LOG_EXTRA */
64 "%s%s" /* tag placeholder must be present but tag will be ignored */
65 };
66
67 static int tc_log_use_colors = TC_TRUE;
68 /*
69 * I'm not so proud of doing so. If someone has a better (cleaner)
70 * solution, just speak up. -- fromani 20060919
71 */
72
73
libtc_init(int * argc,char *** argv)74 void libtc_init(int *argc, char ***argv)
75 {
76 int ret = tc_mangle_cmdline(argc, argv, TC_LOG_COLOR_OPTION, NULL);
77
78 if (ret == 0) {
79 tc_log_use_colors = TC_FALSE;
80 } else {
81 const char *envvar = getenv(TC_LOG_COLOR_ENV_VAR);
82 if (envvar != NULL) {
83 tc_log_use_colors = TC_FALSE;
84 }
85 }
86 }
87
88
89
tc_log(TCLogLevel level,const char * tag,const char * fmt,...)90 int tc_log(TCLogLevel level, const char *tag, const char *fmt, ...)
91 {
92 char buf[TC_MSG_BUF_SIZE];
93 char *msg = buf;
94 size_t size = sizeof(buf);
95 int dynbuf = TC_FALSE, truncated = TC_FALSE;
96 /* flag: we must use a dynamic (larger than static) buffer? */
97 va_list ap;
98
99 /* sanity check, avoid {under,over}flow; */
100 level = (level < TC_LOG_ERR) ?TC_LOG_ERR :level;
101 level = (level > TC_LOG_EXTRA) ?TC_LOG_EXTRA :level;
102 /* sanity check, avoid dealing with NULL as much as we can */
103 if (!tc_log_use_colors && level != TC_LOG_EXTRA) {
104 /* TC_LOG_EXTRA and TC_LOG_MSG must not use colors */
105 level = TC_LOG_MSG;
106 }
107
108 tag = (tag != NULL) ?tag :"";
109 fmt = (fmt != NULL) ?fmt :"";
110 /* TC_LOG_EXTRA special handling: force always empty tag */
111 tag = (level == TC_LOG_EXTRA) ?"" :tag;
112
113 size = strlen(tc_log_preambles[level])
114 + strlen(tag) + strlen(fmt) + 1;
115
116 if (size > sizeof(buf)) {
117 /*
118 * we use malloc/fprintf instead of tc_malloc because
119 * we want custom error messages
120 */
121 msg = malloc(size);
122 if (msg != NULL) {
123 dynbuf = TC_TRUE;
124 } else {
125 fprintf(stderr, "(%s) CRITICAL: can't get memory in "
126 "tc_log() output will be truncated.\n",
127 __FILE__);
128 /* force reset to default values */
129 msg = buf;
130 size = sizeof(buf) - 1;
131 truncated = TC_TRUE;
132 }
133 } else {
134 size = sizeof(buf) - 1;
135 }
136
137 /* construct real format string */
138 tc_snprintf(msg, size, tc_log_preambles[level], tag, fmt);
139 // msg[size] = '\0';
140 // /* `size' value was already scaled for the final '\0' */
141 // trusting valgrind, this assignement don't help us and can
142 // lead to troubles. So it's temporary disable until I gain
143 // some time to experiment -- fromani 20060919
144
145 va_start(ap, fmt);
146 vfprintf(stderr, msg, ap);
147 va_end(ap);
148
149 if (dynbuf == 1) {
150 free(msg);
151 }
152
153 /* ensure that all *other* messages are written */
154 fflush(stderr);
155 return (truncated) ?-1 :0;
156 }
157
158 /*************************************************************************/
159
tc_mangle_cmdline(int * argc,char *** argv,const char * opt,const char ** optval)160 int tc_mangle_cmdline(int *argc, char ***argv,
161 const char *opt, const char **optval)
162 {
163 int i = 0, skew = (optval == NULL) ?1 :2, err = -1;
164
165 if (argc == NULL || argv == NULL || opt == NULL) {
166 return err;
167 }
168
169 err = 1;
170 /* first we looking for our option (and it's value) */
171 for (i = 1; i < *argc; i++) {
172 if ((*argv)[i] && strcmp((*argv)[i], opt) == 0) {
173 if (optval == NULL) {
174 err = 0; /* we're set */
175 } else {
176 /* don't peek after the end... */
177 if (i + 1 >= *argc || (*argv)[i + 1][0] == '-') {
178 tc_log_warn(__FILE__, "wrong usage for option '%s'", opt);
179 err = 1; /* no option and/or value found */
180 } else {
181 *optval = (*argv)[i + 1];
182 err = 0;
183 }
184 }
185 break;
186 }
187 }
188
189 /*
190 * if we've found our option, now we must shift back all
191 * the other options after the ours and we must also update argc.
192 */
193 if (!err) {
194 for (; i < (*argc - skew); i++) {
195 (*argv)[i] = (*argv)[i + skew];
196 }
197 (*argc) -= skew;
198 }
199
200 return err;
201 }
202
203
tc_test_program(const char * name)204 int tc_test_program(const char *name)
205 {
206 #ifndef NON_POSIX_PATH
207 const char *path = getenv("PATH");
208 char *tok_path = NULL;
209 char *compl_path = NULL;
210 char *tmp_path;
211 char **strtokbuf;
212 char done;
213 size_t pathlen;
214 long sret;
215 int error = 0;
216
217 if (name == NULL) {
218 tc_warn("ERROR: Searching for a NULL program!\n");
219 return ENOENT;
220 }
221
222 if (path == NULL) {
223 tc_warn("The '%s' program could not be found. \n", name);
224 tc_warn("Because your PATH environment variable is not set.\n");
225 return ENOENT;
226 }
227
228 pathlen = strlen(path) + 1;
229 tmp_path = tc_malloc(pathlen);
230 strtokbuf = tc_malloc(pathlen);
231
232 sret = strlcpy(tmp_path, path, pathlen);
233 tc_test_string(__FILE__, __LINE__, pathlen, sret, errno);
234
235 /* iterate through PATH tokens */
236 for (done = 0, tok_path = strtok_r(tmp_path, ":", strtokbuf);
237 !done && tok_path;
238 tok_path = strtok_r((char *)0, ":", strtokbuf)) {
239 pathlen = strlen(tok_path) + strlen(name) + 2;
240 compl_path = tc_malloc(pathlen * sizeof(char));
241 sret = tc_snprintf(compl_path, pathlen, "%s/%s", tok_path, name);
242
243 if (access(compl_path, X_OK) == 0) {
244 error = 0;
245 done = 1;
246 } else { /* access != 0 */
247 if (errno != ENOENT) {
248 done = 1;
249 error = errno;
250 }
251 }
252
253 tc_free(compl_path);
254 }
255
256 tc_free(tmp_path);
257 tc_free(strtokbuf);
258
259 if (!done) {
260 tc_warn("The '%s' program could not be found. \n", name);
261 tc_warn("Please check your installation.\n");
262 return ENOENT;
263 }
264
265 if (error != 0) {
266 /* access returned an unhandled error */
267 tc_warn("The '%s' program was found, but is not accessible.\n", name);
268 tc_warn("%s\n", strerror(errno));
269 tc_warn("Please check your installation.\n");
270 return error;
271 }
272 #endif
273
274 return 0;
275 }
276
277
tc_test_string(const char * file,int line,int limit,long ret,int errnum)278 int tc_test_string(const char *file, int line, int limit, long ret, int errnum)
279 {
280 if (ret < 0) {
281 fprintf(stderr, "[%s:%d] string error: %s\n",
282 file, line, strerror(errnum));
283 return 1;
284 }
285 if (ret >= limit) {
286 fprintf(stderr, "[%s:%d] truncated %ld characters\n",
287 file, line, (ret - limit) + 1);
288 return 1;
289 }
290 return 0;
291 }
292
293 /*************************************************************************/
294
295 /*
296 * These versions of [v]snprintf() return -1 if the string was truncated,
297 * printing a message to stderr in case of truncation (or other error).
298 */
299
_tc_vsnprintf(const char * file,int line,char * buf,size_t limit,const char * format,va_list args)300 int _tc_vsnprintf(const char *file, int line, char *buf, size_t limit,
301 const char *format, va_list args)
302 {
303 int res = vsnprintf(buf, limit, format, args);
304 return tc_test_string(file, line, limit, res, errno) ? -1 : res;
305 }
306
307
_tc_snprintf(const char * file,int line,char * buf,size_t limit,const char * format,...)308 int _tc_snprintf(const char *file, int line, char *buf, size_t limit,
309 const char *format, ...)
310 {
311 va_list args;
312 int res;
313
314 va_start(args, format);
315 res = _tc_vsnprintf(file, line, buf, limit, format, args);
316 va_end(args);
317 return res;
318 }
319
320 /*************************************************************************/
321
322 /* simple malloc wrapper with failure guard. */
323
_tc_malloc(const char * file,int line,size_t size)324 void *_tc_malloc(const char *file, int line, size_t size)
325 {
326 void *p = malloc(size);
327 if (p == NULL) {
328 fprintf(stderr, "[%s:%d] tc_malloc(): can't allocate %lu bytes\n",
329 file, line, (unsigned long)size);
330 }
331 return p;
332 }
333
334 /* allocate a chunk of memory (like tc_malloc), but zeroes memory before
335 * returning. */
336
_tc_zalloc(const char * file,int line,size_t size)337 void *_tc_zalloc(const char *file, int line, size_t size)
338 {
339 void *p = malloc(size);
340 if (p == NULL) {
341 fprintf(stderr, "[%s:%d] tc_zalloc(): can't allocate %lu bytes\n",
342 file, line, (unsigned long)size);
343 } else {
344 memset(p, 0, size);
345 }
346 return p;
347 }
348
349 /* realloc() wrapper. */
350
_tc_realloc(const char * file,int line,void * p,size_t size)351 void *_tc_realloc(const char *file, int line, void *p, size_t size)
352 {
353 p = realloc(p, size);
354 if (p == NULL && size > 0) {
355 fprintf(stderr, "[%s:%d] tc_realloc(): can't allocate %lu bytes\n",
356 file, line, (unsigned long)size);
357 }
358 return p;
359 }
360
361
362 /*** FIXME ***: find a clean way to refactorize above functions */
363
364 /* Allocate a buffer aligned to the machine's page size, if known. The
365 * buffer must be freed with buffree() (not free()). */
366
_tc_bufalloc(const char * file,int line,size_t size)367 void *_tc_bufalloc(const char *file, int line, size_t size)
368 {
369 #ifdef HAVE_GETPAGESIZE
370 unsigned long pagesize = getpagesize();
371 int8_t *base = malloc(size + sizeof(void *) + pagesize);
372 int8_t *ptr = NULL;
373 unsigned long offset = 0;
374
375 if (base == NULL) {
376 fprintf(stderr, "[%s:%d] tc_bufalloc(): can't allocate %lu bytes\n",
377 file, line, (unsigned long)size);
378 } else {
379 ptr = base + sizeof(void *);
380 offset = (unsigned long)ptr % pagesize;
381
382 if (offset)
383 ptr += (pagesize - offset);
384 ((void **)ptr)[-1] = base; /* save the base pointer for freeing */
385 }
386 return ptr;
387 #else /* !HAVE_GETPAGESIZE */
388 return malloc(size);
389 #endif
390 }
391
_tc_strndup(const char * file,int line,const char * s,size_t n)392 char *_tc_strndup(const char *file, int line, const char *s, size_t n)
393 {
394 char *pc = NULL;
395
396 if (s != NULL) {
397 pc = _tc_malloc(file, line, n + 1);
398 if (pc != NULL) {
399 memcpy(pc, s, n);
400 pc[n] = '\0';
401 }
402 }
403 return pc;
404 }
405
406 /* Free a buffer allocated with tc_bufalloc(). */
tc_buffree(void * ptr)407 void tc_buffree(void *ptr)
408 {
409 #ifdef HAVE_GETPAGESIZE
410 if (ptr)
411 free(((void **)ptr)[-1]);
412 #else
413 free(ptr);
414 #endif
415 }
416
417 /*************************************************************************/
418
tc_pread(int fd,uint8_t * buf,size_t len)419 ssize_t tc_pread(int fd, uint8_t *buf, size_t len)
420 {
421 ssize_t n = 0;
422 ssize_t r = 0;
423
424 while (r < len) {
425 n = xio_read(fd, buf + r, len - r);
426
427 if (n == 0) { /* EOF */
428 break;
429 }
430 if (n < 0) {
431 if (errno == EINTR) {
432 continue;
433 } else {
434 break;
435 }
436 }
437 r += n;
438 }
439 return r;
440 }
441
442
tc_pwrite(int fd,const uint8_t * buf,size_t len)443 ssize_t tc_pwrite(int fd, const uint8_t *buf, size_t len)
444 {
445 ssize_t n = 0;
446 ssize_t r = 0;
447
448 while (r < len) {
449 n = xio_write(fd, buf + r, len - r);
450
451 if (n < 0) {
452 if (errno == EINTR) {
453 continue;
454 } else {
455 break;
456 }
457 }
458 r += n;
459 }
460 return r;
461 }
462
463 #ifdef PIPE_BUF
464 # define BLOCKSIZE PIPE_BUF /* 4096 on linux-x86 */
465 #else
466 # define BLOCKSIZE 4096
467 #endif
468
tc_preadwrite(int fd_in,int fd_out)469 int tc_preadwrite(int fd_in, int fd_out)
470 {
471 uint8_t buffer[BLOCKSIZE];
472 ssize_t bytes;
473 int error = 0;
474
475 do {
476 bytes = tc_pread(fd_in, buffer, BLOCKSIZE);
477
478 /* error on read? */
479 if (bytes < 0) {
480 return -1;
481 }
482
483 /* read stream end? */
484 if (bytes != BLOCKSIZE) {
485 error = 1;
486 }
487
488 if (bytes) {
489 /* write stream problems? */
490 if (tc_pwrite(fd_out, buffer, bytes) != bytes) {
491 error = 1;
492 }
493 }
494 } while (!error);
495
496 return 0;
497 }
498
tc_file_check(const char * name)499 int tc_file_check(const char *name)
500 {
501 struct stat fbuf;
502
503 if(xio_stat(name, &fbuf)) {
504 tc_log_warn(__FILE__, "invalid file \"%s\"", name);
505 return -1;
506 }
507
508 /* file or directory? */
509 if(S_ISDIR(fbuf.st_mode)) {
510 return 1;
511 }
512 return 0;
513 }
514
515 #ifndef major
516 # define major(dev) (((dev) >> 8) & 0xff)
517 #endif
518
tc_probe_path(const char * name)519 int tc_probe_path(const char *name)
520 {
521 struct stat fbuf;
522
523 if(name == NULL) {
524 tc_log_warn(__FILE__, "invalid file \"%s\"", name);
525 return TC_PROBE_PATH_INVALID;
526 }
527
528 if(xio_stat(name, &fbuf) == 0) {
529 /* inode exists */
530
531 /* treat DVD device as absolute directory path */
532 if (S_ISBLK(fbuf.st_mode)) {
533 return TC_PROBE_PATH_ABSPATH;
534 }
535
536 /* char device could be several things, depending on system */
537 /* *BSD DVD device? v4l? bktr? sunau? */
538 if(S_ISCHR(fbuf.st_mode)) {
539 #ifdef __FreeBSD__
540 if (strstr(name, "bktr") || strstr(name, "tuner") || strstr(name, "wbi"))
541 return TC_PROBE_PATH_BKTR;
542 else if (strstr(name, "dsp") || strstr(name, "audio") || strstr(name, "music"))
543 return TC_PROBE_PATH_OSS;
544 else
545 return TC_PROBE_PATH_ABSPATH;
546 #endif
547
548 switch (major(fbuf.st_rdev)) {
549 #ifdef OS_BSD
550 # ifdef __OpenBSD__
551 case 15: /* rcd */
552 return TC_PROBE_PATH_ABSPATH;
553 case 42: /* sunau */
554 return TC_PROBE_PATH_SUNAU;
555 case 49: /* bktr */
556 return TC_PROBE_PATH_BKTR;
557 # endif
558 default: /* libdvdread uses "raw" disk devices here */
559 return TC_PROBE_PATH_ABSPATH;
560 #else
561 case 81: /* v4l (Linux) */
562 return TC_PROBE_PATH_V4L_VIDEO;
563 case 14: /* OSS */
564 return TC_PROBE_PATH_OSS;
565 default:
566 break;
567 #endif
568 }
569 }
570
571 /* file or directory? */
572 if (!S_ISDIR(fbuf.st_mode)) {
573 return TC_PROBE_PATH_FILE;
574 }
575
576 /* directory, check for absolute path */
577 if(name[0] == '/') {
578 return TC_PROBE_PATH_ABSPATH;
579 }
580
581 /* directory mode */
582 return TC_PROBE_PATH_RELDIR;
583 } else {
584 tc_log_warn(__FILE__, "invalid filename \"%s\"", name);
585 return TC_PROBE_PATH_INVALID;
586 }
587
588 return TC_PROBE_PATH_INVALID;
589 }
590
591 /*************************************************************************/
592
593 #define RESIZE_DIV 8
594 #define DIM_IS_OK(dim) ((dim) % RESIZE_DIV == 0)
595
tc_compute_fast_resize_values(void * _vob,int strict)596 int tc_compute_fast_resize_values(void *_vob, int strict)
597 {
598 int ret = -1;
599 int dw = 0, dh = 0; /* delta width, height */
600 vob_t *vob = _vob; /* adjust pointer */
601
602 /* sanity checks first */
603 if (vob == NULL) {
604 return -1;
605 }
606 if (!DIM_IS_OK(vob->ex_v_width) || !DIM_IS_OK(vob->ex_v_width)) {
607 return -1;
608 }
609 if (!DIM_IS_OK(vob->zoom_width) || !DIM_IS_OK(vob->zoom_width)) {
610 return -1;
611 }
612
613 dw = vob->ex_v_width - vob->zoom_width;
614 dh = vob->ex_v_height - vob->zoom_height;
615 /* MORE sanity checks */
616 if (!DIM_IS_OK(dw) || !DIM_IS_OK(dh)) {
617 return -1;
618 }
619 if (dw == 0 && dh == 0) {
620 /* we're already fine */
621 ret = 0;
622 } else if (dw > 0 && dh > 0) {
623 /* smaller destination frame -> -B */
624 vob->resize1_mult = RESIZE_DIV;
625 vob->hori_resize1 = dw / RESIZE_DIV;
626 vob->vert_resize1 = dh / RESIZE_DIV;
627 ret = 0;
628 } else if (dw < 0 && dh < 0) {
629 /* bigger destination frame -> -X */
630 vob->resize2_mult = RESIZE_DIV;
631 vob->hori_resize2 = -dw / RESIZE_DIV;
632 vob->vert_resize2 = -dh / RESIZE_DIV;
633 ret = 0;
634 } else if (strict == 0) {
635 /* always needed in following cases */
636 vob->resize1_mult = RESIZE_DIV;
637 vob->resize2_mult = RESIZE_DIV;
638 ret = 0;
639 if (dw <= 0 && dh >= 0) {
640 vob->hori_resize2 = -dw / RESIZE_DIV;
641 vob->vert_resize1 = dh / RESIZE_DIV;
642 } else if (dw >= 0 && dh <= 0) {
643 vob->hori_resize1 = dw / RESIZE_DIV;
644 vob->vert_resize2 = -dh / RESIZE_DIV;
645 }
646 }
647
648 if (ret == 0) {
649 vob->zoom_width = 0;
650 vob->zoom_height = 0;
651 }
652 return ret;
653 }
654
655 #undef RESIZE_DIV
656 #undef DIM_IS_OK
657
658 /*************************************************************************/
659
660
tc_find_best_aspect_ratio(const void * _vob,int * sar_num,int * sar_den,const char * tag)661 int tc_find_best_aspect_ratio(const void *_vob,
662 int *sar_num, int *sar_den,
663 const char *tag)
664 {
665 const vob_t *vob = _vob; /* adjust pointer */
666 int num, den;
667
668 if (!vob || !sar_num || !sar_den) {
669 return TC_ERROR;
670 }
671
672 /* Aspect Ratio Calculations (modified code from export_ffmpeg.c) */
673 if (vob->export_attributes & TC_EXPORT_ATTRIBUTE_PAR) {
674 if (vob->ex_par > 0) {
675 /*
676 * vob->ex_par MUST be guarantee to be in a sane range
677 * by core (transcode/tcexport
678 */
679 tc_par_code_to_ratio(vob->ex_par, &num, &den);
680 } else {
681 /* same as above */
682 num = vob->ex_par_width;
683 den = vob->ex_par_height;
684 }
685 tc_log_info(tag, "DAR value ratio calculated as %f = %d/%d",
686 (double)num/(double)den, num, den);
687 } else {
688 if (vob->export_attributes & TC_EXPORT_ATTRIBUTE_ASR) {
689 /* same as above for PAR stuff */
690 tc_asr_code_to_ratio(vob->ex_asr, &num, &den);
691 tc_log_info(tag, "display aspect ratio calculated as %f = %d/%d",
692 (double)num/(double)den, num, den);
693
694 /* ffmpeg FIXME:
695 * This original code might lead to rounding/truncating errors
696 * and maybe produces too high values for "den" and
697 * "num" for -y ffmpeg -F mpeg4
698 *
699 * sar = dar * ((double)vob->ex_v_height / (double)vob->ex_v_width);
700 * lavc_venc_context->sample_aspect_ratio.num = (int)(sar * 1000);
701 * lavc_venc_context->sample_aspect_ratio.den = 1000;
702 */
703
704 num *= vob->ex_v_height;
705 den *= vob->ex_v_width;
706 /* I don't need to reduce since x264 does it itself :-) */
707 tc_log_info(tag, "sample aspect ratio calculated as"
708 " %f = %d/%d",
709 (double)num/(double)den, num, den);
710
711 } else { /* user did not specify asr at all, assume no change */
712 tc_log_info(tag, "set display aspect ratio to input");
713 num = 1;
714 den = 1;
715 }
716 }
717
718 *sar_num = num;
719 *sar_den = den;
720 return TC_OK;
721 }
722
723 /*************************************************************************/
724
tc_strstrip(char * s)725 void tc_strstrip(char *s)
726 {
727 char *start;
728
729 if (s == NULL) {
730 return;
731 }
732
733 start = s;
734 while ((*start != 0) && isspace(*start)) {
735 start++;
736 }
737
738 memmove(s, start, strlen(start) + 1);
739 if (strlen(s) == 0) {
740 return;
741 }
742
743 start = &s[strlen(s) - 1];
744 while ((start != s) && isspace(*start)) {
745 *start = 0;
746 start--;
747 }
748 }
749
tc_strsplit(const char * str,char sep,size_t * pieces_num)750 char **tc_strsplit(const char *str, char sep, size_t *pieces_num)
751 {
752 const char *begin = str, *end = NULL;
753 char **pieces = NULL, *pc = NULL;
754 size_t i = 0, n = 2;
755 int failed = TC_FALSE;
756
757 if (!str || !strlen(str)) {
758 return NULL;
759 }
760
761 while (begin != NULL) {
762 begin = strchr(begin, sep);
763 if (begin != NULL) {
764 begin++;
765 n++;
766 }
767 }
768
769 pieces = tc_malloc(n * sizeof(char*));
770 if (!pieces) {
771 return NULL;
772 }
773
774 begin = str;
775 while (begin != NULL) {
776 size_t len;
777
778 end = strchr(begin, sep);
779 if (end != NULL) {
780 len = (end - begin);
781 } else {
782 len = strlen(begin);
783 }
784 if (len > 0) {
785 pc = tc_strndup(begin, len);
786 if (pc == NULL) {
787 failed = TC_TRUE;
788 break;
789 } else {
790 pieces[i] = pc;
791 i++;
792 }
793 }
794 if (end != NULL) {
795 begin = end + 1;
796 } else {
797 break;
798 }
799 }
800
801 if (failed) {
802 /* one or more copy of pieces failed */
803 tc_free(pieces);
804 pieces = NULL;
805 } else { /* i == n - 1 -> all pieces copied */
806 pieces[n - 1] = NULL; /* end marker */
807 if (pieces_num != NULL) {
808 *pieces_num = i;
809 }
810 }
811 return pieces;
812 }
813
tc_strfreev(char ** pieces)814 void tc_strfreev(char **pieces)
815 {
816 if (pieces != NULL) {
817 int i = 0;
818 for (i = 0; pieces[i] != NULL; i++) {
819 tc_free(pieces[i]);
820 }
821 tc_free(pieces);
822 }
823 }
824
825 /*************************************************************************/
826
827 /*
828 * clamp an unsigned value so it can be safely (without any loss) in
829 * an another unsigned integer of <butsize> bits.
830 */
clamp(int32_t value,uint8_t bitsize)831 static int32_t clamp(int32_t value, uint8_t bitsize)
832 {
833 value = (value < 1) ?1 :value;
834 value = (value > (1 << bitsize)) ?(1 << bitsize) :value;
835 return value;
836 }
837
tc_read_matrix(const char * filename,uint8_t * m8,uint16_t * m16)838 int tc_read_matrix(const char *filename, uint8_t *m8, uint16_t *m16)
839 {
840 int i = 0;
841 FILE *input = NULL;
842
843 /* Open the matrix file */
844 input = fopen(filename, "rb");
845 if (!input) {
846 tc_log_warn("read_matrix",
847 "Error opening the matrix file %s",
848 filename);
849 return -1;
850 }
851 if (!m8 && !m16) {
852 tc_log_warn("read_matrix", "bad matrix reference");
853 return -1;
854 }
855
856 /* Read the matrix */
857 for(i = 0; i < TC_MATRIX_SIZE; i++) {
858 int value;
859
860 /* If fscanf fails then get out of the loop */
861 if(fscanf(input, "%d", &value) != 1) {
862 tc_log_warn("read_matrix",
863 "Error reading the matrix file %s",
864 filename);
865 fclose(input);
866 return 1;
867 }
868
869 if (m8 != NULL) {
870 m8[i] = clamp(value, 8);
871 } else {
872 m16[i] = clamp(value, 16);
873 }
874 }
875
876 /* We're done */
877 fclose(input);
878
879 return 0;
880 }
881
tc_print_matrix(uint8_t * m8,uint16_t * m16)882 void tc_print_matrix(uint8_t *m8, uint16_t *m16)
883 {
884 int i;
885
886 if (!m8 && !m16) {
887 tc_log_warn("print_matrix", "bad matrix reference");
888 return;
889 }
890
891 // XXX: magic number
892 for(i = 0; i < TC_MATRIX_SIZE; i += 8) {
893 if (m8 != NULL) {
894 tc_log_info("print_matrix",
895 "%3d %3d %3d %3d "
896 "%3d %3d %3d %3d",
897 (int)m8[i ], (int)m8[i+1],
898 (int)m8[i+2], (int)m8[i+3],
899 (int)m8[i+4], (int)m8[i+5],
900 (int)m8[i+6], (int)m8[i+7]);
901 } else {
902 tc_log_info("print_matrix",
903 "%3d %3d %3d %3d "
904 "%3d %3d %3d %3d",
905 (int)m16[i ], (int)m16[i+1],
906 (int)m16[i+2], (int)m16[i+3],
907 (int)m16[i+4], (int)m16[i+5],
908 (int)m16[i+6], (int)m16[i+7]);
909 }
910 }
911 return;
912 }
913
914 /*************************************************************************/
915
916 /*
917 * Local variables:
918 * c-file-style: "stroustrup"
919 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
920 * indent-tabs-mode: nil
921 * End:
922 *
923 * vim: expandtab shiftwidth=4:
924 */
925