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