summaryrefslogtreecommitdiff
path: root/src/toolbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/toolbox.c')
-rw-r--r--src/toolbox.c885
1 files changed, 885 insertions, 0 deletions
diff --git a/src/toolbox.c b/src/toolbox.c
new file mode 100644
index 0000000..35dfd3d
--- /dev/null
+++ b/src/toolbox.c
@@ -0,0 +1,885 @@
+/*
+ * Copyright (C) 2013-2014 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * José Bollo <jose.bollo@open.eurogiciel.org>
+ * Stéphane Desneux <stephane.desneux@open.eurogiciel.org>
+ * Jean-Benoit Martin <jean-benoit.martin@open.eurogiciel.org>
+ *
+ */
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdarg.h>
+
+#include "parser.h"
+#include "heap.h"
+#include "buffer.h"
+#include "foreign.h"
+#include "sha256sum.h"
+
+/*======================================================================*/
+
+#ifndef CONFIGPATH
+# define CONFIGPATH "/etc/tizen-platform.conf"
+#endif
+
+#ifndef TOOLNAME
+# define TOOLNAME "tzplatform-tool"
+#endif
+
+/*== TYPES =============================================================*/
+
+/* for recording read keys */
+struct key {
+ struct key *next; /* link to next */
+ const char *name; /* name of the key */
+ const char *value; /* value of the key */
+ size_t begin; /* positions of begin (used by pretty) */
+ size_t end; /* positions of end (used by pretty) */
+ int dependant; /* is dependant (used by rpm) */
+};
+
+/* for recording used variables */
+struct var {
+ struct var *next; /* link to next */
+ const char *name; /* name of the variable */
+ const char *normal; /* normalized value: "${name}" (for pretty) */
+ int dependant; /* is dependant (used by rpm) */
+};
+
+/*== STATIC DATA =======================================================*/
+
+static char help[] = "\
+\n\
+usage: "TOOLNAME" [command] [--] [file]\n\
+\n\
+You can specify the 'file' to process.\n\
+The default file is "CONFIGPATH"\n\
+Specifying - mean the standard input.\n\
+\n\
+Commands:\n\
+\n\
+help Display this help\n\
+check Check validity of 'file' (this is the default command)\n\
+pretty Pretty print of the 'file' (the normalized format)\n\
+h Produce the C header with the enumeration of the variables\n\
+c Produce the C code to hash the variable names\n\
+rpm Produce the macro file to use with RPM\n\
+signup Produce the signup data for the proxy linked statically\n\
+\n\
+";
+
+static char genh_head[] = "\
+/* I'm generated. Dont edit me! */\n\
+#ifndef TZPLATFORM_VARIABLES_H\n\
+#define TZPLATFORM_VARIABLES_H\n\
+#ifdef __cplusplus\n\
+extern \"C\" {\n\
+#endif\n\
+enum tzplatform_variable {\n\
+\t_TZPLATFORM_VARIABLES_INVALID_ = -1,\n\
+";
+
+static char genh_tail[] = "\
+\t_TZPLATFORM_VARIABLES_COUNT_\n\
+};\n\
+#ifdef __cplusplus\n\
+}\n\
+#endif\n\
+#endif\n\
+";
+
+static char gperf_head[] = "\
+struct varassoc {\n\
+ int offset;\n\
+ int id;\n\
+};\n\
+%%\n\
+";
+
+static char *gperf_command[] = {
+ "/bin/sh",
+ "-c",
+ "gperf -r -m 100 --null-strings -C -P -L ANSI-C -c"
+ " -t -N hashvar -Q varpool -K offset -G -W namassoc"
+ " -F \", _TZPLATFORM_VARIABLES_INVALID_\"",
+ NULL
+};
+
+static char rpm_head[] = "\
+# I'm generated. Dont edit me! \n\
+\n\
+";
+
+static char signup_head[] = "\
+/* I'm generated. Dont edit me! */\n\
+static char tizen_platform_config_signup[33] = {\n\
+ '\\x00',\n\
+";
+
+static char signup_tail[] = "\
+ };\n\
+";
+
+/*== GLOBALS VARIABLES =================================================*/
+
+/* name of the meta file to process */
+static const char * metafilepath = CONFIGPATH;
+
+/* list of the read keys */
+static struct key *keys = NULL;
+
+/* list of the used variables */
+static struct var *vars = NULL;
+
+/* count of errors */
+static int errcount = 0;
+
+/* dependency state */
+static int dependant = 0;
+
+/* action to perform */
+static enum { CHECK, PRETTY, GENC, GENH, RPM, SIGNUP } action = CHECK;
+
+/* output of error */
+static int notstderr = 0;
+
+/*======================================================================*/
+
+/* write the error message */
+static void vwriterror( const char *format, va_list ap)
+{
+ vfprintf(notstderr ? stdout : stderr, format, ap);
+}
+
+/* write the error message */
+static void writerror( const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vwriterror( format, ap);
+ va_end(ap);
+}
+
+/* write error and exit */
+static void fatal( const char *format, ...)
+{
+ va_list ap;
+
+ writerror("Error, ");
+ va_start(ap, format);
+ vwriterror( format, ap);
+ va_end(ap);
+ writerror(".\n");
+
+ /* exit */
+ exit(1);
+}
+
+/* error in the command line */
+static void argerror( const char *format, ...)
+{
+ va_list ap;
+
+ writerror("Error, ");
+ va_start(ap, format);
+ vwriterror( format, ap);
+ va_end(ap);
+ writerror(".\nType '"TOOLNAME" help' to get usage.\n");
+
+ /* exit */
+ exit(1);
+}
+
+/*== MANAGEMENT OF THE LIST OF READ KEYS ===============================*/
+
+/* search a key of 'name' and length 'lname' and return it or NULL */
+static struct key *key_search( const char *name, size_t lname)
+{
+ struct key *result = keys;
+
+ while(result!=NULL && (strncmp( result->name, name, lname)!=0
+ || result->name[lname]!=0))
+ result = result->next;
+
+ return result;
+}
+
+/* append a new key to the list and return it or NULL if allocations failed */
+static struct key *key_add( const char *name, size_t lname,
+ const char *value, size_t lvalue,
+ size_t begin_pos, size_t end_pos)
+{
+ struct key *result, *prev;
+ char *sname, *svalue;
+
+ /* allocations */
+ result = malloc(sizeof *result);
+ sname = strndup( name, lname);
+ svalue = strndup( value, lvalue);
+ if (result == NULL || sname == NULL || svalue == NULL) {
+ /* failure of allocations */
+ free( result);
+ free( sname);
+ free( svalue);
+ result = NULL;
+ }
+ else {
+ /* success: init the structure */
+ result->next = NULL;
+ result->name = sname;
+ result->value = svalue;
+ result->begin = begin_pos;
+ result->end = end_pos;
+ result->dependant = dependant;
+
+ /* link at end of the list */
+ prev = keys;
+ if (prev == NULL)
+ keys = result;
+ else {
+ while(prev!=NULL && prev->next!=NULL)
+ prev = prev->next;
+ prev->next = result;
+ }
+ }
+
+ return result;
+}
+
+/*======================================================================*/
+
+/* search a var of 'name' and length 'lname' and return it or NULL */
+static struct var *var_search( const char *name, size_t lname)
+{
+ struct var *result = vars;
+ while(result!=NULL && (strncmp( result->name, name, lname)!=0
+ || result->name[lname]!=0))
+ result = result->next;
+
+ return result;
+}
+
+/* append a new var to the list and return it or NULL if allocations failed */
+static struct var *var_add( const char *name, size_t lname,
+ int depend, const char *value)
+{
+ struct var *result, *prev;
+ char *sname, *normal;
+ size_t length;
+
+ /* check for the value */
+ if (action == RPM && value != NULL) {
+ length = strlen( value) + 1;
+ }
+ else {
+ value = NULL;
+ length = lname + 4;
+ }
+ /* allocations */
+ result = malloc(sizeof *result);
+ sname = strndup( name, lname);
+ normal = malloc( length);
+ if (result == NULL || sname == NULL || normal == NULL) {
+ /* failure of allocations */
+ free( result);
+ free( sname);
+ free( normal);
+ result = NULL;
+ }
+ else {
+ /* success: init the structure */
+ result->next = NULL;
+ result->name = sname;
+ result->normal = normal;
+ result->dependant = depend;
+ if (value) {
+ memcpy( normal, value, length);
+ }
+ else {
+ *normal++ = '$';
+ *normal++ = '{';
+ memcpy( normal, name, lname);
+ normal += lname;
+ *normal++ = '}';
+ *normal = 0;
+ }
+
+ /* link at end of the list */
+ prev = vars;
+ if (prev == NULL)
+ vars = result;
+ else {
+ while(prev!=NULL && prev->next!=NULL)
+ prev = prev->next;
+ prev->next = result;
+ }
+ }
+
+ return result;
+}
+
+/*== CALLBACK OF PARSING OF THE META FILE ==============================*/
+
+static void conferr( const char *format, ...)
+{
+ va_list ap;
+
+ notstderr = action == CHECK;
+ writerror( "ERROR ");
+ va_start(ap, format);
+ vwriterror( format, ap);
+ va_end(ap);
+ notstderr = 0;
+}
+
+static int errcb( struct parsing *parsing,
+ size_t pos, const char *message)
+{
+ struct parsinfo info;
+
+ /* get the info */
+ parse_utf8_info( parsing, &info, pos);
+
+ /* emit the error */
+ conferr("line %d: %s\n..: %.*s\n..: %*s\n",
+ info.lino, message,
+ (int)info.length, info.begin,
+ info.colno, "^");
+
+ /* count it */
+ errcount++;
+
+ /* continue to parse */
+ return 1;
+}
+
+static int putcb( struct parsing *parsing,
+ const char *name, size_t lname,
+ const char *value, size_t lvalue,
+ size_t begin_pos, size_t end_pos)
+{
+ enum fkey fkey;
+ struct key *key;
+ struct parsinfo here, there;
+
+ /* check that it is not a foreign key */
+ fkey = foreign( name, lname);
+ if (fkey != _FOREIGN_INVALID_) {
+ parse_utf8_info( parsing, &here, begin_pos);
+ conferr("line %d: reserved variable name '%.*s'\n"
+ "..: %.*s\n..: %*s\n",
+ here.lino, (int)lname, name,
+ here.lino, (int)here.length, here.begin, here.colno, "^");
+
+ errcount++;
+ dependant = 0;
+ return 0;
+ }
+
+ /* search if already defined */
+ key = key_search( name, lname);
+ if (key != NULL) {
+
+ /* yes! that's an error */
+ parse_utf8_info( parsing, &here, begin_pos);
+ parse_utf8_info( parsing, &there, key->begin);
+ conferr("line %d: redefinition of '%s'\n"
+ "...was defined line %d\n..: %.*s\n..: %*s\n"
+ "...is redefined line %d\n..: %.*s\n..: %*s\n",
+ here.lino, key->name,
+ there.lino, (int)there.length, there.begin, there.colno, "^",
+ here.lino, (int)here.length, here.begin, here.colno, "^");
+
+ errcount++;
+ dependant = 0;
+ return 0;
+ }
+
+ /* create and record the key */
+ key = key_add( name, lname, value, lvalue, begin_pos, end_pos);
+ dependant = 0;
+ if (key != NULL)
+ return 0;
+
+ /* no can't because of memory! */
+ fatal("out of memory");
+ errcount++;
+ return -1;
+}
+
+static const char *getcb( struct parsing *parsing,
+ const char *name, size_t length,
+ size_t begin_pos, size_t end_pos)
+{
+ struct parsinfo here;
+ struct var *var;
+ enum fkey fkey;
+ struct key *key;
+ int depend;
+
+ /* search if already defined */
+ var = var_search( name, length);
+ if (var != NULL) {
+ /* yes cool, return the normalized form */
+ if (var->dependant)
+ dependant = 1;
+ return var->normal;
+ }
+
+ /* search the variable */
+ fkey = foreign( name, length);
+ key = key_search( name, length);
+
+ if (fkey == _FOREIGN_INVALID_ && key == NULL) {
+
+ /* not a valid variable, emit the error */
+ parse_utf8_info( parsing, &here, begin_pos);
+ conferr("line %d: use of unknown variable '%.*s'\n"
+ "..: %.*s\n..: %*s\n",
+ here.lino, (int)length, name,
+ (int)here.length, here.begin, here.colno, "^");
+ errcount++;
+ dependant = 1; /* kind of invalidity */
+ return "***error***"; /* avoid further error */
+ }
+
+ /* valid variables: those of foreign or the already defined keys */
+
+ /* set dependant state */
+ depend = fkey != _FOREIGN_INVALID_ || key->dependant;
+ if (depend)
+ dependant = 1;
+
+ /* create and record the variable */
+ var = var_add( name, length, depend, key==NULL ? NULL : key->value);
+ if (var != NULL)
+ /* created, return the normalized form */
+ return var->normal;
+
+ /* memory depletion */
+ fatal("out of memory");
+ dependant = 1; /* kind of invalidity */
+ errcount++;
+ return "***error***"; /* avoid further error */
+}
+
+/*======================================================================*/
+
+/* compare two keys */
+static int keycmp(const void *a, const void *b)
+{
+ const struct key *ka = *(const struct key **)a;
+ const struct key *kb = *(const struct key **)b;
+ return strcmp(ka->name, kb->name);
+}
+
+/* sort the keys and return their count */
+static int sortkeys()
+{
+ struct key *key = keys, **array;
+ int count = 0, index;
+
+ while (key) {
+ key = key->next;
+ count++;
+ }
+
+ array = malloc( count * sizeof * array);
+ if (array == NULL)
+ return -1;
+
+ key = keys;
+ index = 0;
+
+ while (key) {
+ array[index++] = key;
+ key = key->next;
+ }
+
+ qsort(array, count, sizeof * array, keycmp);
+
+ while (index) {
+ array[--index]->next = key;
+ key = array[index];
+ }
+ keys = key;
+ free( array);
+
+ return count;
+}
+
+/*======================================================================*/
+
+/* pretty print the read file */
+static int pretty( const char *buffer, size_t length, FILE *output)
+{
+ int status;
+ struct key *key = keys;
+ size_t pos = 0;
+
+ while (pos < length && key != NULL) {
+ if (pos < key->begin) {
+ status = fprintf(output, "%.*s", (int)(key->begin-pos), buffer+pos);
+ if (status < 0)
+ return status;
+ }
+ status = fprintf( output, "%s=%s", key->name, key->value);
+ if (status < 0)
+ return status;
+ pos = key->end;
+ key = key->next;
+ }
+
+ if (pos < length) {
+ status = fprintf( output, "%.*s", (int)(length-pos), buffer+pos);
+ if (status < 0)
+ return status;
+ }
+
+ return 0;
+}
+
+/* generate the header */
+static int genh( FILE *output)
+{
+ struct key *key;
+ int status;
+
+#ifndef NO_SORT_KEYS
+ status = sortkeys();
+ if (status < 0)
+ return status;
+#endif
+
+ status = fprintf( output, "%s", genh_head);
+ if (status < 0)
+ return status;
+ for (key = keys ; key != NULL ; key = key->next) {
+ status = fprintf( output, "\t%s,\n", key->name);
+ if (status < 0)
+ return status;
+ }
+ status = fprintf( output, "%s", genh_tail);
+ if (status < 0)
+ return status;
+ return 0;
+}
+
+/* generate hash code using gperf */
+static int genc(FILE *output)
+{
+ struct key *key;
+ int fds[2];
+ pid_t pid;
+ int result, sts;
+ size_t l;
+
+#ifndef NO_SORT_KEYS
+ sts = sortkeys();
+ if (sts < 0)
+ return sts;
+#endif
+
+ result = pipe(fds);
+ if (result != 0)
+ return result;
+
+ fflush( output);
+ pid = fork();
+ if (pid == -1) {
+ close( fds[0]);
+ close( fds[1]);
+ fatal( "can't fork");
+ result = -1;
+ }
+ else if (pid == 0) {
+ dup2( fds[0], 0);
+ close( fds[0]);
+ close( fds[1]);
+ if (fileno(output) != 1)
+ dup2( fileno(output), 1);
+ result = execve( gperf_command[0], gperf_command, environ);
+ fatal("can't execute gperf");
+ exit(1);
+ }
+ else {
+ close( fds[0]);
+ sts = write( fds[1], gperf_head, sizeof(gperf_head)-1);
+ if (sts < 0)
+ result = sts;
+ for (key = keys ; key != NULL ; key = key->next) {
+ l = strlen( key->name);
+ sts = write( fds[1], key->name, l);
+ if (sts < 0)
+ result = sts;
+ sts = write( fds[1], ", ", 2);
+ if (sts < 0)
+ result = sts;
+ sts = write( fds[1], key->name, l);
+ if (sts < 0)
+ result = sts;
+ sts = write( fds[1], "\n", 1);
+ if (sts < 0)
+ result = sts;
+ }
+ close( fds[1]);
+ wait(&result);
+ sts = WIFEXITED(result) && WEXITSTATUS(result)==0 ? 0 : -1;
+ if (sts < 0)
+ result = sts;
+ }
+ return result;
+}
+
+/* generate the rpm macros */
+static int rpm( FILE *output)
+{
+ struct key *key;
+ int status;
+
+#ifndef NO_SORT_KEYS
+ status = sortkeys();
+ if (status < 0)
+ return status;
+#endif
+
+ status = fprintf( output, "%s", rpm_head);
+ if (status < 0)
+ return status;
+ for (key = keys ; key != NULL ; key = key->next) {
+ if (!key->dependant) {
+ status = fprintf( output, "%%%-40s %s\n", key->name, key->value);
+ if (status < 0)
+ return status;
+ }
+ }
+ return 0;
+}
+
+/* generate the signup */
+static int signup( FILE *output)
+{
+ struct key *key;
+ int status;
+ int i;
+ struct sha256sum *sum;
+ char term;
+ char signup[32];
+
+#ifndef NO_SORT_KEYS
+ status = sortkeys();
+ if (status < 0)
+ return status;
+#endif
+
+ sum = sha256sum_create();
+ if (sum == NULL)
+ return -1;
+
+ term = ';';
+ for (key = keys ; key != NULL ; key = key->next) {
+ status = sha256sum_add_data(sum, key->name, strlen(key->name));
+ if (status < 0) {
+ sha256sum_destroy(sum);
+ return status;
+ }
+ status = sha256sum_add_data(sum, &term, 1);
+ if (status < 0) {
+ sha256sum_destroy(sum);
+ return status;
+ }
+ }
+
+ status = sha256sum_get(sum, signup);
+ sha256sum_destroy(sum);
+ if (status < 0)
+ return status;
+
+ status = fprintf( output, "%s", signup_head);
+ if (status < 0)
+ return status;
+
+ for (i=0 ; i<32 ; i++) {
+ status = fprintf( output, "%s'\\x%02x'%s",
+ (i & 7) ? " " : " ",
+ (int)(unsigned char)signup[i],
+ (i & 7) < 7 ? "," : i == 31 ? "\n" : ",\n");
+ if (status < 0)
+ return status;
+ }
+ status = fprintf( output, "%s", signup_tail);
+ if (status < 0)
+ return status;
+ return 0;
+}
+
+/* main of processing */
+static int process()
+{
+ struct parsing parsing;
+ struct buffer buffer;
+ int result;
+
+ /* read the file */
+ result = buffer_create( &buffer, metafilepath);
+ if (result != 0) {
+ fatal( "can't read file %s", metafilepath);
+ return -1;
+ }
+
+ /* parse the file */
+ parsing.buffer = buffer.buffer;
+ parsing.length = buffer.length;
+ parsing.maximum_data_size = 0;
+ parsing.should_escape = action!=RPM;
+ parsing.data = 0;
+ parsing.get = getcb;
+ parsing.put = putcb;
+ parsing.error = errcb;
+ dependant = 0;
+ result = parse_utf8_config( &parsing);
+ if (result != 0) {
+ buffer_destroy( &buffer);
+ fatal( "while parsing the file %s", metafilepath);
+ return -1;
+ }
+ if (errcount != 0) {
+ buffer_destroy( &buffer);
+ fatal( "%d errors detected %s", errcount, metafilepath);
+ return -1;
+ }
+
+ /* process */
+ switch( action) {
+ case CHECK:
+ break;
+ case PRETTY:
+ pretty( buffer.buffer, buffer.length, stdout);
+ break;
+ case GENH:
+ genh( stdout);
+ break;
+ case GENC:
+ genc( stdout);
+ break;
+ case RPM:
+ rpm( stdout);
+ break;
+ case SIGNUP:
+ signup( stdout);
+ break;
+ }
+
+ buffer_destroy( &buffer);
+ return 0;
+}
+
+/*======================================================================*/
+
+/* very simple argument parsing */
+static int arguments( char **argv)
+{
+ /* skip the program name*/
+ argv++;
+
+ /* scan first arg */
+ if (*argv == NULL) {
+ /* no argument then default is to check */
+ action = CHECK;
+ }
+ else {
+ /* check if first argument is 'check', 'pretty', 'c', 'h', '-h',
+ '--help' or 'help' */
+ if (0 == strcmp( *argv, "check")) {
+ action = CHECK;
+ argv++;
+ }
+ else if (0 == strcmp( *argv, "pretty")) {
+ action = PRETTY;
+ argv++;
+ }
+ else if (0 == strcmp( *argv, "c")) {
+ action = GENC;
+ argv++;
+ }
+ else if (0 == strcmp( *argv, "h")) {
+ action = GENH;
+ argv++;
+ }
+ else if (0 == strcmp( *argv, "rpm")) {
+ action = RPM;
+ argv++;
+ }
+ else if (0 == strcmp( *argv, "signup")) {
+ action = SIGNUP;
+ argv++;
+ }
+ else if (0 == strcmp( *argv, "help") || 0 == strcmp( *argv, "--help")) {
+ printf("%s", help);
+ exit(0);
+ return -1;
+ }
+ else if (**argv == '-') {
+ if (0 == strcmp( *argv, "--") || 0 == strcmp( *argv, "-") ) {
+ action = CHECK;
+ }
+ else {
+ argerror( "unknown option '%s'", *argv);
+ return -1;
+ }
+ }
+ /* skip the -- arg if present */
+ if (*argv != NULL && 0 == strcmp( *argv, "--")) {
+ argv++;
+ }
+ /* get a meta file argument */
+ if (*argv != NULL) {
+ if (0 == strcmp( *argv, "-"))
+ metafilepath = "/dev/stdin";
+ else
+ metafilepath = *argv;
+ argv++;
+ }
+ /* check that there is no extra argument */
+ if (*argv != NULL) {
+ argerror("extra argument found '%s'",*argv);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ if (arguments(argv) == 0)
+ if (process() == 0)
+ return 0;
+
+ return 1;
+}
+
+